From d49838048f98ae1ca1048233858e75265f7fb286 Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Tue, 18 Aug 2026 11:07:19 -0400 Subject: [PATCH] feat: add Re-scan and Drop cache to the dev debug menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-scan is the work the header's Refresh button does, reachable from a menu that is always there: Refresh lives inside the publication section, so it is hidden on exactly the pages a re-scan is most wanted for — the ones detection found nothing on. Drop cache empties every cache instead of bypassing one, so the next read of anything runs the miss path a warm profile never shows. It only drops; re-scanning on the way out would refill, and watching a cold read happen is the reason to empty them. Both write their result into the menu, which stays open to hold it and clears it the next time it opens. Co-Authored-By: Claude Opus 5 (1M context) --- popup.html | 3 +++ src/popup/popup.css | 10 +++++++++ src/popup/popup.ts | 49 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/popup.html b/popup.html index 353ddc8..0d12463 100644 --- a/popup.html +++ b/popup.html @@ -139,6 +139,9 @@ diff --git a/src/popup/popup.css b/src/popup/popup.css index 98d449d..1730edf 100644 --- a/src/popup/popup.css +++ b/src/popup/popup.css @@ -696,6 +696,16 @@ actor-typeahead { position: relative; display: flex; } +/* What the last debug action did, said in the menu that started it: the two + that fetch have no other place to report, and the popup's status pills are + redrawn from page state rather than written to. */ +.menu-note[hidden] { display: none; } +.menu-note { + margin: 4px 8px 2px; + color: var(--muted); + font-size: 12px; +} + /* Nothing sits below the footer for a menu to drop into, so this one rises. */ .menu.up { top: auto; diff --git a/src/popup/popup.ts b/src/popup/popup.ts index fcbfa8c..6c40604 100644 --- a/src/popup/popup.ts +++ b/src/popup/popup.ts @@ -720,12 +720,30 @@ $('feedback-cancel').addEventListener('click', () => setFeedbackPanel(false)) // --- debug menu (dev channel only) --- // -// Where dev-only tools go. Today it holds one: the welcome page, which Chrome -// otherwise shows exactly once per install (src/background.ts) and which is -// therefore impossible to look at again without reinstalling the extension. +// Where dev-only tools go, all of them reaching something a released build +// deliberately makes unreachable: +// +// - the welcome page, which Chrome otherwise shows exactly once per install +// (src/background.ts) and which is therefore impossible to look at again +// without reinstalling the extension; +// - a re-scan, which the header's Refresh button also does but which lives +// inside the publication section, so it is hidden on exactly the pages a +// re-scan is most wanted for: the ones detection found nothing on; +// - dropping every cached read, which nothing else does at all. Refresh +// bypasses the caches on the path it walks; this empties them, so the +// next read of anything — on any tab, from a cold worker — runs the miss +// path a user with a warm cache would never see. function setDebugMenu(open: boolean) { $('debug-menu').hidden = !open $('debug-open').setAttribute('aria-expanded', String(open)) + // A result belongs to the action that produced it, not to the next opening. + if (open) debugNote('') +} + +/** What the last debug action did. Cleared whenever the menu opens. */ +function debugNote(text: string) { + $('debug-note').textContent = text + $('debug-note').hidden = !text } if (isDevChannel()) $('debug-area').hidden = false @@ -737,6 +755,31 @@ $('debug-open').addEventListener('click', (e) => { $('debug-welcome').addEventListener('click', () => openUrl(welcomeUrl())) +// Both of the below stop the click reaching the document-level close-all +// handler: the menu is where the answer is written, so it has to still be +// open when the answer arrives. +$('debug-rescan').addEventListener('click', async (e) => { + e.stopPropagation() + debugNote('Re-scanning…') + await loadState(true) + debugNote(requestFailed ? 'Re-scan failed; see the console.' : 'Re-scanned.') +}) + +// Only drops. Re-scanning from here would refill on the way out, and watching +// a cold read happen is the reason to empty the caches in the first place — +// so which read runs next is left to whoever asked. +$('debug-drop-cache').addEventListener('click', async (e) => { + e.stopPropagation() + debugNote('Dropping…') + try { + const { dropped } = await send<{ dropped: number }>({ type: 'drop-caches' }) + debugNote(`Dropped ${dropped} ${dropped === 1 ? 'entry' : 'entries'}.`) + } catch (err) { + console.debug('[substandard] drop-caches failed', err) + debugNote(err instanceof Error ? err.message : String(err)) + } +}) + $('feedback-form').addEventListener('submit', async (e) => { e.preventDefault() const note = $('feedback-note') -- 2.51.2