Skip to content

Persistent enable/disable kill-switch + make highlight modes visually distinct#17

Merged
jjpaulino merged 2 commits into
masterfrom
jordan/hover-toggle-and-persistent-disable-7b8a
May 19, 2026
Merged

Persistent enable/disable kill-switch + make highlight modes visually distinct#17
jjpaulino merged 2 commits into
masterfrom
jordan/hover-toggle-and-persistent-disable-7b8a

Conversation

@jjpaulino
Copy link
Copy Markdown
Member

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 distinct

Addresses two related reports:

i am loving the clay devtools, my only feedback so far is that I'd rather it not automatically highlight each component on hover, i would want to have to toggle that mode before it is enabled

oh i guess relatedly, my other feedback would be that 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

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:

  • Off — fully silent on the page. No ambient outlines, no blue hover flash, no selection outline, no name badge, no find-on-page dim/highlight. The panel is still fully usable; pick components from the Tree tab.
  • Selection (default) — pristine page; hover and click paint blue; hold Control to peek at every component.
  • Editable only — corner accents on [data-editable], plus blue hover + click on top.
  • All components — rainbow on every component, 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 extension

Addresses:

Someone asked for 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, add any missing tests for this.

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 (default true) that:

  • Rides on the same chrome.storage.sync key 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 implement storage.sync through the same surface, and the Firefox postbuild only rewrites the background entry shape, not the worker logic.
  • When false, the content-script bootstrap skips installing the highlighter stylesheet, painting components, and mounting the panel. No data-clay-slip-* attributes are ever written to host elements. The toolbar badge clears to zero.
  • The service worker keeps the toolbar popup mounted on every tab (including Clay tabs that normally have it suppressed) when the extension is disabled — so the user always has a one-click UI surface to flip it back on, regardless of which tab they're looking at.
  • A flip from any tab tears down or brings up the panel on every other tab without a reload, via storage.onChanged.

UI

  • Toolbar popup rewritten as a small enable/disable hub:
    • Big toggle at the top with explanatory hint copy.
    • Status sentence that flips between "Visit a Clay page to inspect components" and "The extension is disabled".
    • Quick-link footer to the Options page and the Clay docs.
  • Options page gains a new top-of-page Extension section with the same checkbox.

Tests added (24 new, 200 total)

  • tests/lib/storage.test.tsenabled defaults true, 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 stubbed chrome.* surface:
    • CLAY_DETECTED only suppresses the popup when enabled === true.
    • EXTENSION_ENABLED_CHANGED force-mounts the popup on every tab when disabled and does NOT do so on re-enable.
    • The in-memory enabled cache flips synchronously so the very next CLAY_DETECTED respects it.
    • Cold-start hydration honors the persisted value (enabled: false users who restart the browser don't see the extension boot back up).
    • The storage.onChanged subscription is wired so cross-tab flips propagate.
  • tests/content/index.test.ts (new file) — boots the actual src/content/index.ts in happy-dom against a stubbed chrome.*:
    • Non-Clay page: nothing painted, nothing mounted, but the prefs listener is still installed so re-enables propagate.
    • Clay page + enabled: false: no stylesheet, no panel host, no host-element attributes — but CLAY_DETECTED is still announced so the popup shows the toggle UI.
    • Clay page + enabled: true: full bootstrap.
    • storage.onChanged tear-down: flipping to false removes the panel host live.
    • storage.onChanged bring-up: flipping to true mounts 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

  • A quick smoke test on both browser families would be ideal: sideload either build, flip the new popup toggle, navigate to a Clay page, confirm the FAB disappears and the popup stays available to flip it back on. Then re-enable and confirm the panel reappears without a reload.
  • If you'd like the popup checkbox copy or visual treatment tweaked (e.g. different verbs, different placement of the toggle vs. the status sentence) I'm happy to iterate — current copy errs on the side of explicitness.
Open in Web Open in Cursor 

jordan and others added 2 commits May 18, 2026 21:58
…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 jjpaulino self-assigned this May 18, 2026
@jjpaulino jjpaulino marked this pull request as ready for review May 19, 2026 04:31
@jjpaulino jjpaulino merged commit 4dea68f into master May 19, 2026
1 check passed
@jjpaulino jjpaulino deleted the jordan/hover-toggle-and-persistent-disable-7b8a branch May 19, 2026 04:32
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant