Persistent enable/disable kill-switch + make highlight modes visually distinct#17
Merged
jjpaulino merged 2 commits intoMay 19, 2026
Merged
Conversation
…stinct Earlier iterations only mode-gated the ambient layer. Hover and click always flashed blue regardless of the chosen mode, which surfaced as two user reports: > i'd rather it not automatically highlight each component on hover, > i would want to have to toggle that mode before it is enabled > the options to edit the highlight mode don't seem to do anything — > i'm seeing the same blue outline on hover regardless of which i > select Now the mode owns every paint on the host page. Off mode produces a truly pristine page (no blue flash, no selection outline, no name badge, no find-on-page dim/highlight) while still leaving the panel fully usable — pick components from the Tree tab. The other three modes paint blue hover/select on top of their ambient layer, so each mode is visibly different from the others. Implementation is a single :not([data-clay-slip-mode='off']) gate on the hover / selected / label / match / filtering rules, with a CSS contract test that locks the gate in place so a future edit can't silently regress. Co-authored-by: Jordan Paulino <jjpaulino@users.noreply.github.com>
Adds a top-level 'enabled' preference (default true) that fully turns
Clay Slip off on every page until the user turns it back on. Addresses
the request:
> a way to turn it off entirely and persist the change until they
> turn it back on. Make sure this applies for both chromium and
> firefox.
Behavior when disabled:
- Content script bootstrap detects the false value and skips
installing the highlighter stylesheet, painting components, and
mounting the panel. No data-clay-slip-* attributes are ever
written to host elements.
- The service worker keeps the toolbar popup mounted on every tab
(including Clay tabs that normally have it suppressed), so the
user always has a UI surface to flip the extension back on from
whatever tab they're looking at.
- Storage.sync propagates the flip to every tab; each tab tears
down or brings up its panel without a reload.
UI:
- New checkbox at the top of the toolbar popup (now always visible
on Clay pages when disabled, in addition to the existing
non-Clay-page behavior). When the toggle flips, the popup also
notifies the service worker so popup-state changes are immediate
without waiting for the storage listener.
- New 'Extension' section at the top of the Options page with the
same checkbox.
Tests:
- tests/lib/storage.test.ts: enabled defaults true, persists, merges
with other prefs, round-trips false<->true.
- tests/background/service-worker.test.ts (new): CLAY_DETECTED popup
suppression is gated on the enabled cache; EXTENSION_ENABLED_CHANGED
force-mounts the popup on every tab when disabled and not when
re-enabled; cache flips on every state change; storage hydration
on cold start honors the persisted value; storage.onChanged
propagation from another tab is wired.
- tests/content/index.test.ts (new): bootstrap on a non-Clay page
still subscribes to prefs; bootstrap on a Clay page with disabled
paints nothing, mounts nothing, but announces CLAY_DETECTED;
bootstrap on a Clay page with enabled mounts everything; live
flips through storage.onChanged tear down / bring up without a
reload.
The whole code path is the same on Chromium and Firefox — the
chrome.storage.sync API, the action.setPopup API, and the
webextension-polyfill all behave identically across both targets, and
the Firefox postbuild only rewrites the background entry shape, not
the worker logic. Verified by running both production builds.
Co-authored-by: Jordan Paulino <jjpaulino@users.noreply.github.com>
jjpaulino
added a commit
that referenced
this pull request
May 19, 2026
…on + PRIVACY PR #17 added the persistent on/off toggle and updated the Highlights bullets, but missed three follow-on places users actually look: README.md: - Usage table: two new rows. One for disabling (toolbar popup → uncheck "Enable Clay Slip") and one for re-enabling (popup stays reachable on every tab while disabled, including Clay pages where it's normally suppressed). Both call out that the choice persists across browser restarts. - New Configuration → "Extension on/off" subsection. Spells out the dormant-vs-running distinction: * disabled → no painting, no panel mount, no DOM touches, badge cleared, popup force-shown on every tab as the escape hatch * cross-tab propagation via storage.onChanged (flip from any tab takes effect everywhere with no reload) * persisted on chrome.storage.sync (same surface on Chromium and Firefox) Plus an explicit contrast with the Highlight mode → Off setting, which trips users up otherwise: * Highlight mode → Off = "extension is running, panel works, just don't paint on the page" * Enable Clay Slip → off = "extension is dormant, full stop, until I say otherwise" PRIVACY.md: - Bumped Last-updated to 2026-05-19. - Storage table: storage.sync now lists the master enable/disable toggle alongside the existing preferences. No code changes, no test changes. Prettier sweep clean, validate (typecheck + lint + format:check + 200 tests) green. Setting up the v2.4.0 release commit next. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses three pieces of user feedback in two focused commits. Each commit stands on its own and can be reviewed (or reverted) independently.
Commit 1:
Mode-gate hover/select painting so each highlight mode is visually distinctAddresses two related reports:
Both are caused by the same root issue: earlier iterations only mode-gated the ambient layer (the rainbow / corner-tick outlines that paint on every component). Hover and click always flashed the blue inspection outline regardless of which mode was selected, which made the dropdown feel inert and gave users who picked Off no way to silence the page.
This commit pushes the gate down to the hover / selected / label / find-on-page rules too, via a single
:not([data-clay-slip-mode="off"])join on<html>. The four modes now behave like the labels promise:[data-editable], plus blue hover + click on top.The CSS contract is locked in by a new test (
gates hover/selected/label on :not([mode='off']) so 'Off' produces a pristine page) so a future stylesheet edit can't silently regress.Commit 2:
Add persistent enable/disable kill-switch for the whole extensionAddresses:
Distinct from the
highlightMode: 'off'knob in commit 1 — that's "the extension is running and the panel is available, but don't paint anything on the page"; this is "the extension is dormant, full stop, until I say otherwise".Adds a new top-level preference
enabled: boolean(defaulttrue) that:chrome.storage.synckey as every other preference, so the choice persists across browser restarts and follows the user to any browser profile signed into the same account. Identical behavior on Chromium and Firefox — both implementstorage.syncthrough the same surface, and the Firefox postbuild only rewrites the background entry shape, not the worker logic.false, the content-script bootstrap skips installing the highlighter stylesheet, painting components, and mounting the panel. Nodata-clay-slip-*attributes are ever written to host elements. The toolbar badge clears to zero.storage.onChanged.UI
Tests added (24 new, 200 total)
tests/lib/storage.test.ts—enableddefaultstrue, round-trips through storage, merges cleanly with other prefs, and the off→on→off→on cycle is symmetric.tests/background/service-worker.test.ts(new file) — drives the worker's message listener directly with a stubbedchrome.*surface:CLAY_DETECTEDonly suppresses the popup whenenabled === true.EXTENSION_ENABLED_CHANGEDforce-mounts the popup on every tab when disabled and does NOT do so on re-enable.enabledcache flips synchronously so the very nextCLAY_DETECTEDrespects it.enabled: falseusers who restart the browser don't see the extension boot back up).storage.onChangedsubscription is wired so cross-tab flips propagate.tests/content/index.test.ts(new file) — boots the actualsrc/content/index.tsin happy-dom against a stubbedchrome.*:enabled: false: no stylesheet, no panel host, no host-element attributes — butCLAY_DETECTEDis still announced so the popup shows the toggle UI.enabled: true: full bootstrap.storage.onChangedtear-down: flipping tofalseremoves the panel host live.storage.onChangedbring-up: flipping totruemounts the panel live, no reload.Verification
npm run validate(typecheck + lint + format check + 200 tests) — green.npm run build— green (Chromium production bundle).npm run build:firefox— green (Firefox production bundle + postbuild rewrite).What I'd want from you to wrap this up