Compare commits

..

4 Commits

Author SHA1 Message Date
user
be38ce081e refactor: derive git info inside Docker instead of --build-arg
All checks were successful
check / check (push) Successful in 45s
- Remove .git from .dockerignore so it's available in Docker build context
- Install git in Docker image (apt-get)
- Remove ARG/ENV GIT_COMMIT_SHORT/FULL from Dockerfile
- Remove --build-arg from Makefile docker target
- Simplify build.js to use git CLI directly (no env var indirection)

build.js already had fallback logic to shell out to git; now that .git
is present in the build context, it works directly without needing
values passed in from outside Docker.
2026-03-01 15:16:16 -08:00
clawbot
004cb41868 fix: add app name/repo link, rename build date to release date per issue #144
- Add AutistMask name with link to repo at top of About well
- Rename 'Build date' label to 'Release date' to match issue requirements
- Update element ID from about-build-date to about-release-date
2026-03-01 15:16:16 -08:00
clawbot
1a749a978e feat: add version click flash animation with colored easter egg, darken light mode wells
- Version number clicks now trigger copy-flash animation
- After 5 clicks, each additional click flashes a different bright
  saturated color (hot pink, vivid green, electric blue, orange, purple)
- 10th click reveals debug well as before
- Wells in light mode darkened from #f5f5f5 to #e8e8e8 for better
  contrast with white background

Addresses additional requirements from issue #144 comments.
2026-03-01 15:16:16 -08:00
clawbot
a2464fcf04 feat: add About well to settings with build info and debug easter egg
Add a new well at the bottom of the settings view that displays:
- License (GPL-3.0)
- Author (sneak)
- Version (from package.json)
- Build date (injected at build time)
- Git commit short hash (linked to Gitea commit URL)

Build-time injection: build.js now reads the git commit hash and version
from package.json, injecting them via esbuild define constants. The
Dockerfile and Makefile pass commit hashes as build args so the info is
available even when .git is excluded from the Docker context.

Easter egg: clicking the version number 10 times reveals a hidden debug
well below the About well, containing a toggle for debug mode. The debug
mode flag is persisted in state and enables verbose console logging via
the runtime debug flag in the logger.

closes #144
2026-03-01 15:15:44 -08:00
9 changed files with 257 additions and 7 deletions

View File

@@ -1,4 +1,3 @@
.git
node_modules
.DS_Store
dist

View File

@@ -1,7 +1,7 @@
# node:22-slim (22.x LTS), 2026-02-24
FROM node@sha256:5373f1906319b3a1f291da5d102f4ce5c77ccbe29eb637f072b6c7b70443fc36
RUN apt-get update && apt-get install -y --no-install-recommends make && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y --no-install-recommends make git && rm -rf /var/lib/apt/lists/*
RUN corepack enable && corepack prepare yarn@1.22.22 --activate
WORKDIR /app

View File

@@ -11,9 +11,51 @@ function ensureDir(dir) {
fs.mkdirSync(dir, { recursive: true });
}
function getBuildInfo() {
const pkg = JSON.parse(
fs.readFileSync(path.join(__dirname, "package.json"), "utf8"),
);
let commitHash = "unknown";
try {
commitHash = execSync("git rev-parse --short HEAD", {
encoding: "utf8",
}).trim();
} catch (_) {
// not a git repo or git not available
}
let commitHashFull = "unknown";
try {
commitHashFull = execSync("git rev-parse HEAD", {
encoding: "utf8",
}).trim();
} catch (_) {
// not a git repo or git not available
}
return {
version: pkg.version,
license: pkg.license,
author: pkg.author,
commitHash,
commitHashFull,
buildDate: new Date().toISOString().slice(0, 10),
};
}
async function build() {
console.log("Building AutistMask extension...");
const buildInfo = getBuildInfo();
console.log("Build info:", buildInfo);
const define = {
__BUILD_VERSION__: JSON.stringify(buildInfo.version),
__BUILD_LICENSE__: JSON.stringify(buildInfo.license),
__BUILD_AUTHOR__: JSON.stringify(buildInfo.author),
__BUILD_COMMIT__: JSON.stringify(buildInfo.commitHash),
__BUILD_COMMIT_FULL__: JSON.stringify(buildInfo.commitHashFull),
__BUILD_DATE__: JSON.stringify(buildInfo.buildDate),
};
// compile tailwind CSS
console.log("Compiling Tailwind CSS...");
const tailwindInput = path.join(SRC, "popup", "styles", "main.css");
@@ -38,6 +80,7 @@ async function build() {
platform: "browser",
target: ["chrome110", "firefox110"],
minify: true,
define,
});
// bundle background script
@@ -49,6 +92,7 @@ async function build() {
platform: "browser",
target: ["chrome110", "firefox110"],
minify: true,
define,
});
// bundle content script
@@ -60,6 +104,7 @@ async function build() {
platform: "browser",
target: ["chrome110", "firefox110"],
minify: true,
define,
});
// bundle inpage script (injected into page context, separate file)
@@ -71,6 +116,7 @@ async function build() {
platform: "browser",
target: ["chrome110", "firefox110"],
minify: true,
define,
});
// copy popup HTML

View File

@@ -1002,6 +1002,64 @@
</p>
<div id="settings-denied-sites"></div>
</div>
<div class="bg-well p-3 mx-1 mb-3">
<h3 class="font-bold mb-1">About</h3>
<p class="text-xs mb-2">
<a
href="https://git.eeqj.de/sneak/AutistMask"
class="underline decoration-dashed"
target="_blank"
rel="noopener noreferrer"
>AutistMask</a
>
— Minimal Ethereum wallet browser extension.
</p>
<div class="text-xs">
<div class="mb-1">
<span class="text-muted">License:</span>
<span id="about-license"></span>
</div>
<div class="mb-1">
<span class="text-muted">Author:</span>
<span id="about-author"></span>
</div>
<div class="mb-1">
<span class="text-muted">Version:</span>
<span
id="about-version"
class="cursor-pointer select-none"
></span>
</div>
<div class="mb-1">
<span class="text-muted">Release date:</span>
<span id="about-release-date"></span>
</div>
<div>
<span class="text-muted">Commit:</span>
<a
id="about-commit-link"
class="underline decoration-dashed"
target="_blank"
rel="noopener noreferrer"
></a>
</div>
</div>
</div>
<div
id="settings-debug-well"
class="bg-well p-3 mx-1 mb-3"
style="display: none"
>
<h3 class="font-bold mb-1">Debug</h3>
<label
class="text-xs flex items-center gap-1 cursor-pointer"
>
<input type="checkbox" id="settings-debug-mode" />
Enable debug mode
</label>
</div>
</div>
<!-- ============ DELETE WALLET CONFIRM ============ -->

View File

@@ -10,7 +10,7 @@
--color-border: #000000;
--color-border-light: #cccccc;
--color-hover: #eeeeee;
--color-well: #f5f5f5;
--color-well: #e8e8e8;
--color-danger-well: #fef2f2;
--color-section: #dddddd;
}

View File

@@ -3,6 +3,7 @@ const {
showView,
showFlash,
escapeHtml,
flashCopyFeedback,
goBack,
pushCurrentView,
} = require("./helpers");
@@ -10,12 +11,23 @@ const { applyTheme } = require("../theme");
const { state, saveState, currentNetwork } = require("../../shared/state");
const { NETWORKS, SUPPORTED_CHAIN_IDS } = require("../../shared/networks");
const { onChainSwitch } = require("../../shared/chainSwitch");
const { log, debugFetch } = require("../../shared/log");
const { log, debugFetch, setRuntimeDebug } = require("../../shared/log");
const deleteWallet = require("./deleteWallet");
const {
BUILD_VERSION,
BUILD_LICENSE,
BUILD_AUTHOR,
BUILD_COMMIT,
BUILD_DATE,
GITEA_COMMIT_URL,
} = require("../../shared/buildInfo");
const runtime =
typeof browser !== "undefined" ? browser.runtime : chrome.runtime;
let versionClickCount = 0;
let versionClickTimer = null;
function renderSiteList(containerId, siteMap, stateKey) {
const container = $(containerId);
const hostnames = [...new Set(Object.values(siteMap).flat())];
@@ -142,6 +154,28 @@ function show() {
renderSiteLists();
renderWalletListSettings();
// Populate About well
$("about-license").textContent = BUILD_LICENSE;
// Show only the name part of the author field (strip email)
const authorName = BUILD_AUTHOR.replace(/\s*<[^>]+>/, "");
$("about-author").textContent = authorName;
$("about-version").textContent = BUILD_VERSION;
$("about-release-date").textContent = BUILD_DATE;
$("about-commit-link").textContent = BUILD_COMMIT;
$("about-commit-link").href = GITEA_COMMIT_URL;
// Reset version click counter each time settings opens
versionClickCount = 0;
// Show debug well if debug mode is already enabled
const debugWell = $("settings-debug-well");
if (state.debugMode) {
debugWell.style.display = "";
} else {
debugWell.style.display = "none";
}
$("settings-debug-mode").checked = state.debugMode;
showView("settings");
}
@@ -289,6 +323,65 @@ function init(ctx) {
ctx.showSettingsAddTokenView,
);
// Bright saturated colors for easter egg flashes (clicks 610)
const easterEggColors = [
"#ff0055", // hot pink
"#00cc44", // vivid green
"#3366ff", // electric blue
"#ff9900", // bright orange
"#aa00ff", // vivid purple
];
// Easter egg: click version 10 times to reveal the debug well.
// Each click does a copy-flash animation. After 5 clicks, each
// additional click flashes a different bright saturated color.
$("about-version").addEventListener("click", () => {
versionClickCount++;
clearTimeout(versionClickTimer);
// Reset counter if user stops clicking for 3 seconds
versionClickTimer = setTimeout(() => {
versionClickCount = 0;
}, 3000);
const el = $("about-version");
if (versionClickCount > 5) {
// Colored flash for clicks 610
const colorIdx = versionClickCount - 6;
const color = easterEggColors[colorIdx % easterEggColors.length];
el.classList.remove("copy-flash-fade");
el.style.backgroundColor = color;
el.style.color = "#ffffff";
setTimeout(() => {
el.style.backgroundColor = "";
el.style.color = "";
el.classList.add("copy-flash-fade");
setTimeout(() => {
el.classList.remove("copy-flash-fade");
}, 275);
}, 75);
} else {
// Standard copy-flash for clicks 15
flashCopyFeedback(el);
}
if (versionClickCount >= 10) {
versionClickCount = 0;
clearTimeout(versionClickTimer);
$("settings-debug-well").style.display = "";
}
});
// Debug mode toggle
$("settings-debug-mode").addEventListener("change", async () => {
state.debugMode = $("settings-debug-mode").checked;
setRuntimeDebug(state.debugMode);
await saveState();
});
// Sync runtime debug flag on init
setRuntimeDebug(state.debugMode);
$("btn-settings-back").addEventListener("click", () => {
goBack();
});

35
src/shared/buildInfo.js Normal file
View File

@@ -0,0 +1,35 @@
// Build-time constants injected by esbuild define in build.js.
// These globals are replaced at bundle time with string literals.
/* global __BUILD_VERSION__, __BUILD_LICENSE__, __BUILD_AUTHOR__,
__BUILD_COMMIT__, __BUILD_COMMIT_FULL__, __BUILD_DATE__ */
const BUILD_VERSION =
typeof __BUILD_VERSION__ !== "undefined" ? __BUILD_VERSION__ : "dev";
const BUILD_LICENSE =
typeof __BUILD_LICENSE__ !== "undefined" ? __BUILD_LICENSE__ : "GPL-3.0";
const BUILD_AUTHOR =
typeof __BUILD_AUTHOR__ !== "undefined"
? __BUILD_AUTHOR__
: "sneak <sneak@sneak.berlin>";
const BUILD_COMMIT =
typeof __BUILD_COMMIT__ !== "undefined" ? __BUILD_COMMIT__ : "unknown";
const BUILD_COMMIT_FULL =
typeof __BUILD_COMMIT_FULL__ !== "undefined"
? __BUILD_COMMIT_FULL__
: "unknown";
const BUILD_DATE =
typeof __BUILD_DATE__ !== "undefined" ? __BUILD_DATE__ : "unknown";
const GITEA_COMMIT_URL =
"https://git.eeqj.de/sneak/AutistMask/commit/" + BUILD_COMMIT_FULL;
module.exports = {
BUILD_VERSION,
BUILD_LICENSE,
BUILD_AUTHOR,
BUILD_COMMIT,
BUILD_COMMIT_FULL,
BUILD_DATE,
GITEA_COMMIT_URL,
};

View File

@@ -1,12 +1,27 @@
// Leveled logger. Outputs to console with [AutistMask] prefix.
// Level is DEBUG when the DEBUG constant is true, INFO otherwise.
// Level is DEBUG when the compile-time DEBUG constant is true or the runtime
// debugMode state flag is enabled. The runtime flag is checked lazily so it
// responds immediately when toggled in settings.
const { DEBUG } = require("./constants");
const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
const threshold = DEBUG ? LEVELS.debug : LEVELS.info;
// Runtime debug mode flag — set by settings.js when the user toggles debug
// mode via the easter egg. Kept here as a simple mutable reference so it can
// be updated without circular dependency issues with state.js.
let _runtimeDebug = false;
function setRuntimeDebug(enabled) {
_runtimeDebug = enabled;
}
function isDebug() {
return DEBUG || _runtimeDebug;
}
function emit(level, method, args) {
const threshold = isDebug() ? LEVELS.debug : LEVELS.info;
if (LEVELS[level] >= threshold) {
console[method]("[AutistMask]", ...args);
}
@@ -37,4 +52,4 @@ async function debugFetch(url, opts) {
return resp;
}
module.exports = { log, debugFetch };
module.exports = { log, debugFetch, setRuntimeDebug, isDebug };

View File

@@ -29,6 +29,7 @@ const DEFAULT_STATE = {
fraudContracts: [],
tokenHolderCache: {},
theme: "system",
debugMode: false,
};
const state = {
@@ -68,6 +69,7 @@ async function saveState() {
fraudContracts: state.fraudContracts,
tokenHolderCache: state.tokenHolderCache,
theme: state.theme,
debugMode: state.debugMode,
currentView: state.currentView,
selectedWallet: state.selectedWallet,
selectedAddress: state.selectedAddress,
@@ -128,6 +130,8 @@ async function loadState() {
state.fraudContracts = saved.fraudContracts || [];
state.tokenHolderCache = saved.tokenHolderCache || {};
state.theme = saved.theme || "system";
state.debugMode =
saved.debugMode !== undefined ? saved.debugMode : false;
state.currentView = saved.currentView || null;
state.selectedWallet =
saved.selectedWallet !== undefined ? saved.selectedWallet : null;