Skip to content

πŸ• Reshape highlight modes (rainbow, click feedback, βŒ₯-peek) + drop env config + passive edit mode#15

Merged
jjpaulino merged 4 commits into
masterfrom
feat/selection-peek-and-edit-bypass
May 14, 2026
Merged

πŸ• Reshape highlight modes (rainbow, click feedback, βŒ₯-peek) + drop env config + passive edit mode#15
jjpaulino merged 4 commits into
masterfrom
feat/selection-peek-and-edit-bypass

Conversation

@jjpaulino
Copy link
Copy Markdown
Member

@jjpaulino jjpaulino commented May 14, 2026

Summary

Three rounds of polish that together rewire how the extension renders highlights, how it learns about cross-environment hostnames, and how it behaves on Clay edit-mode pages.

1. Highlight modes β€” final shape

Mode Ambient layer Hover Selected Modifier
Off Nothing Nothing Nothing β€”
Selection (default) Pristine page; hold βŒ₯ Alt/Option to flash a six-color rainbow over every component Blue 2px outline + label badge Blue 2px outline + inset blue tint + label badge βŒ₯ reveals the rainbow
Editable Always-on subtle corner accents on [data-editable] only Same as Selection Same as Selection β€”
All components Always-on six-color rainbow continuous outlines on every component Same as Selection Same as Selection β€”

The rainbow ambient layer in all and the βŒ₯-peek matches the look the original Clay devtools shipped with β€” easier to read at a glance as a structural map of the page than the corner accents we tried in the interim. Hover and selected always paint in the single blue accent on top of either ambient layer, so the click-feedback signal stays consistent across every mode.

2. Drop the global environments / defaultEnvironment config

Per-instance env knowledge now lives entirely in the Site host mappings section of the options page β€” there's no longer a parallel "Environments" config that duplicated the same hostnames in a different shape. The Site host mappings already hold per-brand hostnames for prod / staging / qa, which is everything the panel needs:

  • The View on: pill row reads from it.
  • The Share dropdown reads from it.
  • The Diff tab's Compare: dropdown derives its cross-env options from it: findMappingForHost(location.hostname) β†’ list every other env in that mapping that has a host configured β†’ one option per env. The right-side fetch uses the mapping's host for the chosen env. When the current host isn't in any mapping, only Published-vs-Draft is offered (with an inline help nudge).

useEnvHost() is gone. All callers now pass no host override, so buildUrl etc. fall back to the URI's embedded host β€” the right default for same-page operations. The EnvironmentSwitcher pill is gone too.

3. Passive mode on ?edit=true pages

The previous "no-op the entire extension on edit pages" behavior was too aggressive β€” users lost the panel, the tree, the JSON tab, the diff, the SEO tab, every copy button. Reworked so:

  • The panel still mounts on edit-mode pages.
  • Component detection still runs (the tree, page info, JSON, diff, SEO, notes β€” everything that depends only on the panel store keeps working).
  • Cross-env links + every copy button still work (URI, cURL, fetch, CSS selector, share, View on…).
  • What's skipped (so we don't compete with Clay's own editor chrome):
    • The highlighter stylesheet is never installed β†’ no outlines, no corner accents, no rainbow, no labels.
    • The host-page click + mouseover listeners are never attached β†’ page clicks/hovers stay 100% with Clay.
    • The Alt-reveal keyboard listener isn't installed.

Mechanism: every host-element-writing function in highlighter.ts now no-ops until installHighlightStyles() has been called. The presence of the <style id="clay-slip-highlight-styles"> tag is the source of truth (isHighlighterInstalled()). On edit-mode pages we simply skip the install call and every downstream helper silently no-ops β€” no per-call-site editMode flag plumbing required. The empty state in the Inspect tab adapts on edit pages: "Page is in Clay edit mode β€” pick a component from the Tree tab to inspect it."

Files touched

  • src/content/highlighter.ts β€” isHighlighterInstalled() gate, every element-writing helper checks it (applyHighlights, clearHighlights, setSelected, setHovered, setAnnotatedUris, setFindMatches); PALETTE constant + data-clay-slip-color-idx attribute drive the rainbow; six per-color CSS rules per active mode; editable keeps corner ticks
  • src/content/index.ts β€” split into syncComponents() (read-only) + paintAndSync() (full); bootstrap branches on isEditMode(); PANEL_TOGGLE handler also keeps passive mode on re-mount
  • src/content/panel/hooks/useElementSelection.ts β€” early-return in edit mode; no document-level click/mouseover listeners attached
  • src/content/panel/components/ComponentDetails.tsx β€” empty-state copy adapts in edit mode
  • src/lib/clay-uri.ts β€” isEditMode() doc comment rewritten to describe the passive-mode contract
  • src/lib/types.ts β€” dropped Environment, EnvironmentHosts, EnvironmentConfig, ENVIRONMENT_*, DEFAULT_ENVIRONMENT_HOSTS, plus environments / defaultEnvironment from UserPreferences
  • src/options/Options.tsx β€” Environments section gone; Site host mappings copy updated
  • src/content/panel/store.ts β€” useEnvHost() removed
  • src/content/panel/components/DiffView.tsx β€” env-compare options derive from siteHosts
  • src/content/panel/components/EnvironmentSwitcher.tsx β€” deleted
  • src/content/panel/components/{PageInfo,JsonPreview,ComponentDetails}.tsx + hooks/useKeyboardShortcuts.ts β€” drop useEnvHost, pass no host override
  • src/content/panel/App.tsx β€” drop EnvironmentSwitcher mount
  • src/content/panel/styles.css β€” drop .cs-env-pill rules
  • tests/content/highlighter.test.ts β€” 5 new passive-mode tests, rainbow contract tests, color-index cycling test, per-describe beforeEach(installHighlightStyles) for active-mode tests
  • README.md + PRIVACY.md β€” updated for the new mode shapes, the dropped env switcher, and the passive edit-mode behavior
  • package.json β€” 2.1.0 β†’ 2.2.0

Test plan

  • npm run typecheck
  • npm run lint
  • npm run format:check
  • npm test -- --run (167 / 167 passing)
  • npm run build β€” clean build
  • Load the unpacked dist/ in Chrome on a Clay page (non-edit):
    • Default mode is selection and the page is pristine
    • Hover any component β†’ blue outline + name label badge
    • Click any component β†’ blue outline + inset tint + label
    • Hold βŒ₯ β†’ rainbow over every component, release β†’ pristine again
    • Switch to all mode β†’ rainbow always-on; hover/click still blue on top
    • Switch to editable mode β†’ only [data-editable] shows subtle corner accents
    • Switch to off β†’ nothing painted
  • On a Clay page with ?edit=true:
    • Floating Clay button still appears in the corner
    • Click it β†’ panel opens
    • Inspect tab shows "Page is in Clay edit mode β€” pick a component from the Tree tab to inspect it."
    • Tree tab β†’ click a component β†’ panel updates and host scrolls to it, but NO outlines/dimming on the page
    • JSON, Diff, SEO, Notes tabs work
    • Copy URI / Copy as cURL etc. work and produce protocol-prefixed URLs
    • Hovering a component on the page does NOT show a label badge
    • Clicking a component on the page does NOT capture/intercept the click β€” Clay's own editor handles it
  • On a page whose hostname IS in a configured site mapping, open Diff tab β†’ confirm Compare: dropdown lists each other env in that mapping
  • On a page whose hostname is NOT in any mapping, open Diff tab β†’ confirm only Published-vs-Draft is offered with a help nudge
  • chrome://extensions β†’ Clay Slip β†’ Options β†’ confirm there is no "Environments" section, only "Site host mappings"

Compatibility note

environments and defaultEnvironment already-stored in chrome.storage.sync are silently ignored β€” they're inert leftovers that no code reads. No migration is required.

jjpaulino and others added 2 commits May 14, 2026 14:45
Reshapes the highlight modes around the daily-driver workflow and adds
two distinct visual signals (hover vs. clicked).

Mode mental model
-----------------
  selection (default)
    Pristine page. Hover and click highlight the targeted component.
    HOLD βŒ₯ (Alt/Option) β†’ corner accents appear on every component
    for the duration of the press, so you can quickly scan for
    structure without making the highlight permanent.

  editable
    Always-on corner accents on [data-editable]. Unchanged.

  all
    Always-on corner accents on every component. (Restored β€” was
    pristine-by-default in the previous iteration; that was the wrong
    default for an "all" mode and the peek behavior moved up.)

  off
    Nothing painted. Hover/select still highlight individually.

Why it works
------------
'selection' is now the smart default that covers the 90% case: the
page reads as untouched, hover and click work normally for inspection,
and βŒ₯ is a power-user gesture for the occasional "where are all the
components?" peek. Users who want the always-on overview pick 'all';
users who want zero noise pick 'off'.

Click feedback (selected β‰  hover)
---------------------------------
Hover and selected used to be visually similar β€” both 2px solid blue,
just 70% vs 100% opacity. After clicking a component you'd often
think "did anything happen?" The new selected rule adds an inset
box-shadow at 8% blue that fills the box, so selected = outline +
filled background. This reads as "this is the active item" the way
macOS Finder / GitHub file browser highlight rows. Implemented as
box-shadow inset (no extra pseudo, no layout impact, doesn't touch
the host's actual background).

Hover surfaces the component name
---------------------------------
The label badge ::before now renders for both `[hover][label]` and
`[selected][label]`, so mousing over a component shows what it is
without you having to click. Both rules paint identically when an
element is both hovered and selected, no flicker.

Edit-mode bypass
----------------
isEditMode() in clay-uri.ts: returns true for `?edit=true` exactly
(documented Clay convention; loose matching would silently disable
the extension on URLs that mention an edit param for unrelated
reasons). Bootstrap and the PANEL_TOGGLE handler both bail out, so
on edit pages we don't paint anything, don't mount the panel, and
don't send CLAY_DETECTED β€” the toolbar icon stays at its default
("not active here") popup.

Files
-----
- src/lib/clay-uri.ts β€” new isEditMode() helper.
- src/content/index.ts β€” bootstrap and PANEL_TOGGLE skip on edit mode.
- src/content/highlighter.ts:
  * Corner-tick selectors gated by selection+reveal | all | editable.
  * Selected gets inset 9999px box-shadow at TOKENS.selected.fillAlpha
    (0.08) for click feedback.
  * Hover gets position:relative + label-badge selector extended to
    [hover][label]::before, so the component name surfaces on hover.
  * installAltRevealListener() now triggers on mode='selection' (was
    'all').
- src/lib/types.ts β€” labels: selection 'Selection' / all 'All
  components'; descriptions updated to match.
- src/content/panel/components/HighlightModeMenu.tsx β€” compactLabel
  shows 'Sel βŒ₯' (was on 'All').
- README.md β€” Highlights blurb + keyboard table updated; new bullet
  for the edit-mode bypass.

Tests (160 total, +10 new vs the previous PR baseline)
------------------------------------------------------
- isEditMode: ?edit=true / mixed-with-other-params (true), and the
  five common false cases (false/1/no-value/empty/hash).
- Stylesheet contract:
  * mode='selection' rules MUST include [data-clay-slip-reveal].
  * mode='all' rules MUST NOT include the reveal attr.
  * mode='editable' rules MUST NOT include the reveal attr.
  * No bare 'mode="selection" [' selectors leak through.
  * Hover + selected label badge selectors both present.
  * Selected rule includes 'box-shadow: inset' (click feedback).
  * Hover rule does NOT include the inset fill.
  * Hover rule includes 'position: relative' (label badge anchor).
- installAltRevealListener: fires on 'selection' (was 'all'), no-op
  on every other mode.

Co-authored-by: Cursor <cursoragent@cursor.com>
Two requested adjustments on top of selection-peek + edit-mode bypass:

**1. Rainbow ambient outlines for `all` mode (and the βŒ₯-peek)**

`all` mode and the selection-mode βŒ₯-peek now paint every component with
a continuous full-perimeter outline cycled from a six-color palette,
matching the look the original Clay devtools shipped with β€” easier to
read at a glance as a structural map of the page than the corner
accents.

- `applyHighlights` now stamps `data-clay-slip-color-idx="0..5"` on
  every component, cycled by document order.
- New `PALETTE` constant (six Tailwind 500-shade colors picked for
  mutual distinguishability and for not clashing with the inspection
  blue).
- Six per-color CSS rules per active mode key off
  `[data-clay-slip-color-idx="N"]`.
- `editable` mode keeps the corner-tick rendering β€” different purpose
  ("show me what's editable" vs. structural overview).
- Hover and selected still paint in the single blue accent (outline +
  inset tint) so the click-feedback signal stays consistent across
  every mode, on top of either the rainbow or the corner-accent
  ambient layer.

**2. Drop the global `environments` / `defaultEnvironment` config**

Per-instance env knowledge now lives entirely in the **Site host
mappings** section. There's no longer a parallel "Environments" config
that duplicated the same hostnames in a different shape.

- Removed `Environment`, `EnvironmentHosts`, `EnvironmentConfig`,
  `ENVIRONMENT_ORDER`, `ENVIRONMENT_LABELS`,
  `DEFAULT_ENVIRONMENT_HOSTS` from `lib/types`.
- Removed `environments` and `defaultEnvironment` from
  `UserPreferences` and `DEFAULT_PREFERENCES`.
- Deleted `EnvironmentSwitcher` component and its `.cs-env-pill`
  styles.
- Removed `useEnvHost()` from the panel store. All callers now pass
  no host override, so `buildUrl` etc. fall back to the URI's embedded
  host (which is the page's host) β€” the right default for same-page
  operations.
- `DiffView` rewired: the `Compare:` dropdown now derives its
  cross-env options from `findMappingForHost(location.hostname,
  siteHosts)` β€” one option per other env in that mapping that has a
  host configured. The right-side fetch uses the mapping's host for
  the chosen env. When the current host isn't in any mapping, only
  Published-vs-Draft is offered (with an inline help nudge to add
  the mapping).
- `Options` page: dropped the **Environments** section. Updated the
  **Site host mappings** copy to call out that it's now the single
  source of truth for cross-env behavior.

**Test + doc updates**

- `tests/content/highlighter.test.ts`: replaced the corner-tick
  contract block with a rainbow-contract block (six per-color
  outline rules per active mode, reveal-gating contract, hover/selected
  exclusion, editable-stays-corner-tick contract). Added an
  `applyHighlights` test for the cycling color index.
- `README` + `PRIVACY`: updated highlight-mode description, dropped
  the "Environment switcher" bullet, rewrote the **Site host
  mappings** section to call out the Diff-tab integration.
- Bumped to `2.2.0` (breaking pref-shape change for forks reading
  `preferences.environments`; user-stored values are silently
  ignored β€” they're inert leftovers in `chrome.storage.sync` that
  no code reads).

Co-authored-by: Cursor <cursoragent@cursor.com>
@jjpaulino jjpaulino changed the title πŸ• Reshape highlight modes: βŒ₯-peek in selection, click feedback, edit-mode bypass πŸ• Reshape highlight modes (rainbow ambient, click feedback, βŒ₯-peek, edit-mode bypass) + drop global env config May 14, 2026
jjpaulino and others added 2 commits May 14, 2026 15:58
Pivot the docs to reflect the actual distribution channel: the .zip
attached to each GitHub Release. There's no Chrome Web Store listing
to point people at, so the install instructions need to be the first
thing a non-contributor sees and they need to walk through "Load
unpacked" step by step.

README:

- New top-level **Install** section. First-time install (download zip
  β†’ unzip to a stable folder β†’ enable Developer mode β†’ Load unpacked
  β†’ pin), update flow (download new zip β†’ unzip over the old folder
  β†’ ↻ Reload), and removal flow. Includes a "Don't move/delete the
  folder" warning (a common foot-gun with sideloaded extensions),
  the Chromium-browser variants of the extensions URL (Chrome / Edge
  / Brave / Arc / Vivaldi / Opera), the "Developer mode" yellow-banner
  reassurance, and a Troubleshooting table for the five most common
  symptoms.
- Demoted the previous "Install (development)" section to **Build
  from source** and moved it down past Configuration. Most readers
  shouldn't need it.
- Stripped the **"Upload the zip to the Chrome Web Store dashboard"**
  step from the Releasing section. Publishing the GitHub draft IS
  the release; users grab the zip from there.
- Updated the Privacy summary line to drop the CWS reference.
- Tweaked the Architecture sidebar (env hosts β†’ site host mappings)
  and bumped the test count in the Migration notes from 84 β†’ 162.

PRIVACY:

- "Linked from the Chrome Web Store listing" β†’ "Distributed alongside
  the GitHub releases β€” the only place Clay Slip is published".
- Remote-code section now points at GitHub Releases for the .zip and
  notes that anyone can verify by checking out the matching tag and
  running the build.
- Storage-table preferences row updated to drop "environment hosts"
  (we removed that config) and add highlight mode/intensity.

Release workflow + zip script:

- Comments updated to call out that GitHub Releases is the primary
  destination (CWS layout is mentioned only as the secondary "if we
  ever publish there" reason for keeping manifest.json at the zip
  root).
- Zip script's "Next steps" now prints a sideload smoke-test recipe
  (unzip β†’ enable Developer mode β†’ Load unpacked) and a pointer at
  the README β†’ Releasing flow for publishing, instead of the dead
  "upload to the CWS dashboard" instructions.

No code changes β€” `npm run validate` passes (162/162). Smoke-tested
`npm run release:dry` end-to-end, zip layout verified.

Co-authored-by: Cursor <cursoragent@cursor.com>
Switch the `?edit=true` behavior from "no-op the entire extension" to
"go passive": the panel still mounts and every read-only feature stays
fully usable, but we don't paint outlines on the host page or capture
clicks/hovers there. Clay's own in-page editor chrome owns the host
DOM β€” we stay out of its way without sacrificing the panel's
inspection/diff/SEO/notes/copy features.

**Mechanism: gate every host-element write on stylesheet presence**

Added `isHighlighterInstalled()` (presence of the `<style>` tag is the
source of truth) and gated every element-writing helper on it:
applyHighlights, clearHighlights, setSelected, setHovered,
setAnnotatedUris, setFindMatches. The global `<html>` writes
(setHighlightMode/Opacity/Reveal) are NOT gated since they're
low-impact and useful for keeping prefs in sync.

This means we don't have to plumb an `editMode` flag into every call
site β€” bootstrap simply skips `installHighlightStyles()` in edit mode
and every downstream helper silently no-ops. Symmetric: any future
"unmount panel" path that removes the style tag would also flip the
helpers back to no-op.

**Bootstrap behavior**

- ALL Clay pages now send `CLAY_DETECTED` (including edit-mode ones)
  so the toolbar popup shows the panel-toggle UI on edit pages too.
- On edit-mode pages: `syncComponents()` (read-only) β†’ mount panel β†’
  badge. Skipped: `installHighlightStyles`, `applyHighlights`,
  `installAltRevealListener`.
- On normal pages: same as before β€” full `paintAndSync` + alt-reveal
  + deep-link auto-select.
- `PANEL_TOGGLE` handler refactored: re-mounting on an edit page
  also stays in passive mode.

**Panel-side**

- `useElementSelection` bails early in edit mode β€” no document-level
  click/mouseover listeners installed. Tree-click selection still
  works because it goes through the store directly.
- `ComponentDetails` empty state shows a contextual hint on edit-mode
  pages: "Page is in Clay edit mode β€” pick a component from the Tree
  tab to inspect it." instead of the misleading "click any component
  on the page" copy.

**Tests**

- 5 new tests for the no-op-until-installed contract:
  applyHighlights, setSelected/setHovered, setAnnotatedUris,
  clearHighlights, and an install-then-uninstall round-trip
  (locking in the symmetry).
- Per-describe `beforeEach(installHighlightStyles)` added to the
  active-mode tests that were previously implicitly relying on the
  helpers writing without install.
- 167/167 tests pass (was 162; added 5).

**Docs**

- README "Disabled in Clay edit mode" β†’ "Passive in Clay edit mode"
  bullet rewritten with the full list of what does/doesn't work.
- README troubleshooting row swapped: instead of "Floating button
  doesn't appear" (no longer true β€” it appears), now "Clicking on
  the page doesn't select any component β†’ use the Tree tab".
- `clay-uri.ts` `isEditMode` doc comment updated to describe the
  passive-mode contract.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jjpaulino jjpaulino changed the title πŸ• Reshape highlight modes (rainbow ambient, click feedback, βŒ₯-peek, edit-mode bypass) + drop global env config πŸ• Reshape highlight modes (rainbow, click feedback, βŒ₯-peek) + drop env config + passive edit mode May 14, 2026
@jjpaulino jjpaulino self-assigned this May 14, 2026
@jjpaulino jjpaulino merged commit 1380924 into master May 14, 2026
1 check passed
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