From a2464fcf04c1658820f64099b60ce02e880d6e3a Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 1 Mar 2026 11:42:19 -0800 Subject: [PATCH 1/5] 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 --- Dockerfile | 5 ++++ Makefile | 5 +++- build.js | 50 +++++++++++++++++++++++++++++++ src/popup/index.html | 48 +++++++++++++++++++++++++++++ src/popup/views/settings.js | 60 ++++++++++++++++++++++++++++++++++++- src/shared/buildInfo.js | 35 ++++++++++++++++++++++ src/shared/log.js | 21 +++++++++++-- src/shared/state.js | 4 +++ 8 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 src/shared/buildInfo.js diff --git a/Dockerfile b/Dockerfile index d9e1ac2..b2cf0b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,5 +11,10 @@ RUN yarn install --frozen-lockfile COPY . . +ARG GIT_COMMIT_SHORT=unknown +ARG GIT_COMMIT_FULL=unknown +ENV GIT_COMMIT_SHORT=${GIT_COMMIT_SHORT} +ENV GIT_COMMIT_FULL=${GIT_COMMIT_FULL} + RUN make check RUN make build diff --git a/Makefile b/Makefile index ff9ffe8..4d596a1 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,10 @@ dev: @yarn run build --watch 2>&1 docker: - @docker build -t autistmask . + @docker build \ + --build-arg GIT_COMMIT_SHORT=$$(git rev-parse --short HEAD 2>/dev/null || echo unknown) \ + --build-arg GIT_COMMIT_FULL=$$(git rev-parse HEAD 2>/dev/null || echo unknown) \ + -t autistmask . hooks: @echo "Installing pre-commit hook..." diff --git a/build.js b/build.js index fdc9a24..4fd43d3 100644 --- a/build.js +++ b/build.js @@ -11,9 +11,55 @@ function ensureDir(dir) { fs.mkdirSync(dir, { recursive: true }); } +function getBuildInfo() { + const pkg = JSON.parse( + fs.readFileSync(path.join(__dirname, "package.json"), "utf8"), + ); + let commitHash = process.env.GIT_COMMIT_SHORT || "unknown"; + if (commitHash === "unknown") { + try { + commitHash = execSync("git rev-parse --short HEAD", { + encoding: "utf8", + }).trim(); + } catch (_) { + // not a git repo or git not available + } + } + let commitHashFull = process.env.GIT_COMMIT_FULL || "unknown"; + if (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 +84,7 @@ async function build() { platform: "browser", target: ["chrome110", "firefox110"], minify: true, + define, }); // bundle background script @@ -49,6 +96,7 @@ async function build() { platform: "browser", target: ["chrome110", "firefox110"], minify: true, + define, }); // bundle content script @@ -60,6 +108,7 @@ async function build() { platform: "browser", target: ["chrome110", "firefox110"], minify: true, + define, }); // bundle inpage script (injected into page context, separate file) @@ -71,6 +120,7 @@ async function build() { platform: "browser", target: ["chrome110", "firefox110"], minify: true, + define, }); // copy popup HTML diff --git a/src/popup/index.html b/src/popup/index.html index 90fb615..53c9113 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -1002,6 +1002,54 @@

+ +
+

About

+
+
+ License: + +
+
+ Author: + +
+
+ Version: + +
+
+ Build date: + +
+
+ Commit: + +
+
+
+ + diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index aa42a7a..8ba6060 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -10,12 +10,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 +153,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-build-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 +322,31 @@ function init(ctx) { ctx.showSettingsAddTokenView, ); + // Easter egg: click version 10 times to reveal the debug well + $("about-version").addEventListener("click", () => { + versionClickCount++; + clearTimeout(versionClickTimer); + // Reset counter if user stops clicking for 3 seconds + versionClickTimer = setTimeout(() => { + versionClickCount = 0; + }, 3000); + 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(); }); diff --git a/src/shared/buildInfo.js b/src/shared/buildInfo.js new file mode 100644 index 0000000..63c4b1a --- /dev/null +++ b/src/shared/buildInfo.js @@ -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 "; +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, +}; diff --git a/src/shared/log.js b/src/shared/log.js index b4a63f9..551fb20 100644 --- a/src/shared/log.js +++ b/src/shared/log.js @@ -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 }; diff --git a/src/shared/state.js b/src/shared/state.js index e8ee1b1..b0192d8 100644 --- a/src/shared/state.js +++ b/src/shared/state.js @@ -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; -- 2.49.1 From 1a749a978e363108d87a23a6e797eab26f87b628 Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 1 Mar 2026 12:28:32 -0800 Subject: [PATCH 2/5] 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. --- src/popup/styles/main.css | 2 +- src/popup/views/settings.js | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/popup/styles/main.css b/src/popup/styles/main.css index f0b3a24..30532d5 100644 --- a/src/popup/styles/main.css +++ b/src/popup/styles/main.css @@ -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; } diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index 8ba6060..cc36a85 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -3,6 +3,7 @@ const { showView, showFlash, escapeHtml, + flashCopyFeedback, goBack, pushCurrentView, } = require("./helpers"); @@ -322,7 +323,18 @@ function init(ctx) { ctx.showSettingsAddTokenView, ); - // Easter egg: click version 10 times to reveal the debug well + // Bright saturated colors for easter egg flashes (clicks 6–10) + 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); @@ -330,6 +342,29 @@ function init(ctx) { versionClickTimer = setTimeout(() => { versionClickCount = 0; }, 3000); + + const el = $("about-version"); + + if (versionClickCount > 5) { + // Colored flash for clicks 6–10 + 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 1–5 + flashCopyFeedback(el); + } + if (versionClickCount >= 10) { versionClickCount = 0; clearTimeout(versionClickTimer); -- 2.49.1 From 004cb4186897b8c15c76d9b2f02bc8f687d62628 Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 1 Mar 2026 12:32:01 -0800 Subject: [PATCH 3/5] 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 --- src/popup/index.html | 14 ++++++++++++-- src/popup/views/settings.js | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/popup/index.html b/src/popup/index.html index 53c9113..a38e901 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -1005,6 +1005,16 @@

About

+

+ AutistMask + — Minimal Ethereum wallet browser extension. +

License: @@ -1022,8 +1032,8 @@ >
- Build date: - + Release date: +
Commit: diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index cc36a85..db5b824 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -160,7 +160,7 @@ function show() { const authorName = BUILD_AUTHOR.replace(/\s*<[^>]+>/, ""); $("about-author").textContent = authorName; $("about-version").textContent = BUILD_VERSION; - $("about-build-date").textContent = BUILD_DATE; + $("about-release-date").textContent = BUILD_DATE; $("about-commit-link").textContent = BUILD_COMMIT; $("about-commit-link").href = GITEA_COMMIT_URL; -- 2.49.1 From be38ce081e15de8432d12acc9e378d3db12c6ab0 Mon Sep 17 00:00:00 2001 From: user Date: Sun, 1 Mar 2026 13:05:55 -0800 Subject: [PATCH 4/5] refactor: derive git info inside Docker instead of --build-arg - 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. --- .dockerignore | 1 - Dockerfile | 7 +------ Makefile | 5 +---- build.js | 32 ++++++++++++++------------------ 4 files changed, 16 insertions(+), 29 deletions(-) diff --git a/.dockerignore b/.dockerignore index 7452cbb..da592f8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,3 @@ -.git node_modules .DS_Store dist diff --git a/Dockerfile b/Dockerfile index b2cf0b6..bb7d041 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 @@ -11,10 +11,5 @@ RUN yarn install --frozen-lockfile COPY . . -ARG GIT_COMMIT_SHORT=unknown -ARG GIT_COMMIT_FULL=unknown -ENV GIT_COMMIT_SHORT=${GIT_COMMIT_SHORT} -ENV GIT_COMMIT_FULL=${GIT_COMMIT_FULL} - RUN make check RUN make build diff --git a/Makefile b/Makefile index 4d596a1..ff9ffe8 100644 --- a/Makefile +++ b/Makefile @@ -33,10 +33,7 @@ dev: @yarn run build --watch 2>&1 docker: - @docker build \ - --build-arg GIT_COMMIT_SHORT=$$(git rev-parse --short HEAD 2>/dev/null || echo unknown) \ - --build-arg GIT_COMMIT_FULL=$$(git rev-parse HEAD 2>/dev/null || echo unknown) \ - -t autistmask . + @docker build -t autistmask . hooks: @echo "Installing pre-commit hook..." diff --git a/build.js b/build.js index 4fd43d3..54c1179 100644 --- a/build.js +++ b/build.js @@ -15,25 +15,21 @@ function getBuildInfo() { const pkg = JSON.parse( fs.readFileSync(path.join(__dirname, "package.json"), "utf8"), ); - let commitHash = process.env.GIT_COMMIT_SHORT || "unknown"; - if (commitHash === "unknown") { - try { - commitHash = execSync("git rev-parse --short HEAD", { - encoding: "utf8", - }).trim(); - } catch (_) { - // not a git repo or git not available - } + 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 = process.env.GIT_COMMIT_FULL || "unknown"; - if (commitHashFull === "unknown") { - try { - commitHashFull = execSync("git rev-parse 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, -- 2.49.1 From d6a6e24c4ee968b8c5bd0544385ef8250b34e165 Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 1 Mar 2026 15:21:51 -0800 Subject: [PATCH 5/5] fix: make debug mode toggle work at runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The debug/insecure warning banner was only controlled by the compile-time DEBUG constant. The settings easter egg checkbox toggled state.debugMode and the runtime log flag, but neither index.js nor helpers.js checked these runtime values — the banner was created/updated based solely on the compile-time constant. Changes: - Extract banner logic into updateDebugBanner() in helpers.js that checks isDebug() (combines compile-time DEBUG and runtime debugMode) - Banner is dynamically created/removed: appears when debug is enabled, removed when disabled (no stale banners) - index.js init() syncs runtime debug flag from persisted state before first render, then delegates to updateDebugBanner() - settings.js calls updateDebugBanner() after the checkbox change so the banner immediately reflects the new state - Debug mode persists across popup close/reopen via state.debugMode Fixes the bug where the settings debug toggle had no visible effect. --- src/popup/index.js | 23 +++++++------------- src/popup/views/helpers.js | 42 +++++++++++++++++++++++++++---------- src/popup/views/settings.js | 4 +++- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/popup/index.js b/src/popup/index.js index 6f8f6d2..8b72d05 100644 --- a/src/popup/index.js +++ b/src/popup/index.js @@ -1,18 +1,19 @@ // AutistMask popup entry point. // Loads state, initializes views, triggers first render. -const { DEBUG } = require("../shared/constants"); const { state, saveState, loadState, currentNetwork, } = require("../shared/state"); +const { isDebug, setRuntimeDebug } = require("../shared/log"); const { refreshPrices } = require("../shared/prices"); const { refreshBalances } = require("../shared/balances"); const { $, showView, + updateDebugBanner, setRenderMain, pushCurrentView, goBack, @@ -209,21 +210,11 @@ async function init() { await loadState(); applyTheme(state.theme); - const net = currentNetwork(); - if (DEBUG || net.isTestnet) { - const banner = document.createElement("div"); - banner.id = "debug-banner"; - if (DEBUG && net.isTestnet) { - banner.textContent = "DEBUG / INSECURE [TESTNET]"; - } else if (net.isTestnet) { - banner.textContent = "[TESTNET]"; - } else { - banner.textContent = "DEBUG / INSECURE"; - } - banner.style.cssText = - "background:#c00;color:#fff;text-align:center;font-size:10px;padding:1px 0;font-family:monospace;position:sticky;top:0;z-index:9999;"; - document.body.prepend(banner); - } + // Sync runtime debug flag from persisted state before first render + setRuntimeDebug(state.debugMode); + + // Create the debug/testnet banner if needed (uses runtime debug state) + updateDebugBanner(); // Auto-default active address if ( diff --git a/src/popup/views/helpers.js b/src/popup/views/helpers.js index e4744ef..fc9f2f9 100644 --- a/src/popup/views/helpers.js +++ b/src/popup/views/helpers.js @@ -1,6 +1,7 @@ // Shared DOM helpers used by all views. const { DEBUG } = require("../../shared/constants"); +const { isDebug } = require("../../shared/log"); const { formatUsd, getPrice, @@ -59,19 +60,37 @@ function showView(name) { clearFlash(); state.currentView = name; saveState(); + updateDebugBanner(name); +} + +// Create or update the debug/insecure warning banner. +// Called on every view switch and after the settings debug toggle changes. +// The banner is shown when the compile-time DEBUG constant is true OR when +// the user has enabled runtime debug mode via the settings easter egg, OR +// when the active network is a testnet. +function updateDebugBanner(viewName) { + const debug = isDebug(); const net = currentNetwork(); - if (DEBUG || net.isTestnet) { - const banner = document.getElementById("debug-banner"); - if (banner) { - if (DEBUG && net.isTestnet) { - banner.textContent = - "DEBUG / INSECURE [TESTNET] (" + name + ")"; - } else if (net.isTestnet) { - banner.textContent = "[TESTNET]"; - } else { - banner.textContent = "DEBUG / INSECURE (" + name + ")"; - } + const show = debug || net.isTestnet; + let banner = document.getElementById("debug-banner"); + if (show) { + if (!banner) { + banner = document.createElement("div"); + banner.id = "debug-banner"; + banner.style.cssText = + "background:#c00;color:#fff;text-align:center;font-size:10px;padding:1px 0;font-family:monospace;position:sticky;top:0;z-index:9999;"; + document.body.prepend(banner); } + const suffix = viewName ? " (" + viewName + ")" : ""; + if (debug && net.isTestnet) { + banner.textContent = "DEBUG / INSECURE [TESTNET]" + suffix; + } else if (net.isTestnet) { + banner.textContent = "[TESTNET]" + suffix; + } else { + banner.textContent = "DEBUG / INSECURE" + suffix; + } + } else if (banner) { + banner.remove(); } } @@ -417,6 +436,7 @@ module.exports = { showError, hideError, showView, + updateDebugBanner, setRenderMain, pushCurrentView, goBack, diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index db5b824..576c251 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -1,6 +1,7 @@ const { $, showView, + updateDebugBanner, showFlash, escapeHtml, flashCopyFeedback, @@ -372,11 +373,12 @@ function init(ctx) { } }); - // Debug mode toggle + // Debug mode toggle — update runtime flag, persist, and re-render banner $("settings-debug-mode").addEventListener("change", async () => { state.debugMode = $("settings-debug-mode").checked; setRuntimeDebug(state.debugMode); await saveState(); + updateDebugBanner(state.currentView); }); // Sync runtime debug flag on init -- 2.49.1