Skip to content

fix(sections-editor): make CMS hover overlay track scroll, click, and label correctly#4136

Merged
aka-sacci-ccr merged 5 commits into
mainfrom
aka-sacci-ccr/fix-editor-section-hover
Jun 25, 2026
Merged

fix(sections-editor): make CMS hover overlay track scroll, click, and label correctly#4136
aka-sacci-ccr merged 5 commits into
mainfrom
aka-sacci-ccr/fix-editor-section-hover

Conversation

@aka-sacci-ccr

@aka-sacci-ccr aka-sacci-ccr commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes several papercuts in the Sections editor (CMS) hover overlay in the site editor. All changes are confined to the injected iframe script (cms-editor-script.ts) plus the editor-side wiring that feeds it labels/kinds (preview.tsx).

Bugs fixed

  1. Highlight didn't follow the section on scroll. It used position:fixed with viewport-relative coords recomputed only on mousemove, so scrolling without moving the mouse left the highlight floating in the old spot. Now uses position:absolute with document coordinates (rect + scroll), so the browser scrolls it together with the page natively — no lag. A capture-phase scroll/resize listener covers nested scroll containers.

  2. Clicking opened the wrong section. getAllSections() matched every [data-manifest-key] element — including nested islands and framework-injected sections — so the DOM index didn't line up with the decofile sections array the panel indexes into. Confirmed via /.decofile: the editable array runs Header … Footer; deco injects framework sections (SeoV2, Theme, htmx, Analytics, Session, CartAlert) around it that are not in that array. Now scopes to top-level section[data-manifest-key] and skips framework keys via IGNORED_MANIFEST_KEYS.

  3. Badge label correctness.

    • Global sections (incl. globals inside async Lazy rendering) show their block name — resolved editor-side since the DOM only carries manifest paths.
    • Normal sections show their full resolve (data-manifest-key).
    • Lazy non-globals show the inner section's manifest key instead of Rendering/Lazy.tsx.
  4. Color-coded highlight by kind: global = light purple, variant = green, normal = blue.

Implementation notes

  • Labels + kinds are derived from the decofile in preview.tsx (parseSections + getPageVariantSectionsAt) and sent into the iframe via postMessage (cms-editor::set-labels) on every (re)injection — mode switch, page navigation, save-reload — so they track the current page. No useEffect.
  • The injected script keeps the click contract ({ sectionIndex, manifestKey }) unchanged; only DOM enumeration/positioning/labeling changed.

Testing

  • bun run fmt, bun run lint (0 errors), tsc --noEmit (mesh) all clean.
  • Manual verification needed (script is injected into the preview iframe — reload the preview to re-inject): in CMS mode, hover + scroll without moving the mouse (highlight stays glued); click sections after nested/lazy content (opens the correct one); check badge names for globals and colors per kind.

🤖 Generated with Claude Code


Summary by cubic

Fixes the CMS hover overlay in the Sections editor so the highlight tracks scroll, clicks open the correct section across runtimes, and labels/colors reflect the section type. Aligns editable sections to the DOM via subsequence matching with per-section key candidates (including Lazy wrapper vs inner and multivariate variant keys), so framework-injected and runtime-specific differences are ignored.

  • Bug Fixes
    • Highlight follows scroll via absolute positioning with document coordinates; capture-phase scroll/resize covers nested scrollers.
    • Click mapping aligns the editable sequence to top-level section[data-manifest-key] using subsequence matching and per-section candidate keys (e.g., Lazy wrapper or inner key; multivariate collects each variant’s resolved keys), so indices match the decofile even with interleaved framework sections (TanStack) or wrappers; recomputes from the live DOM to self-heal as content mounts.
    • Labels are accurate: globals show saved block names (incl. inside Lazy); non-global Lazy shows the inner section key; others show the full manifest key. Labels/kinds/keys are provided via cms-editor::set-labels.
    • Color-coded highlight by kind: global (purple), variant (green), normal (blue).

Written for commit 8db50b8. Summary will update on new commits.

Review in cubic

decobot and others added 5 commits June 24, 2026 17:41
… label correctly

The CMS-mode section hover overlay had several issues:

- Highlight didn't follow the section on scroll: it used position:fixed
  with viewport-relative coords recomputed only on mousemove. Switched to
  position:absolute with document coords so the browser scrolls it
  natively (no lag); a capture scroll/resize listener handles nested
  scroll containers.
- Clicking opened the wrong section: getAllSections() matched every
  [data-manifest-key] element (nested islands, framework-injected
  sections), so the DOM index didn't line up with the decofile sections
  array. Now it scopes to top-level section[data-manifest-key] and skips
  framework sections (IGNORED_MANIFEST_KEYS) that aren't in the page's
  editable array.
- Badge label: globals (incl. globals inside async Lazy rendering) now
  show their block name (resolved editor-side, since the DOM can't carry
  it); non-global sections show their full resolve; Lazy non-globals show
  the inner section's manifest key.
- Highlight is now color-coded by kind: global = light purple,
  variant = green, normal = blue.

Labels/kinds are computed from the decofile in preview.tsx and sent into
the iframe via postMessage on each (re)injection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
knip flagged it — the const is only used within cms-editor-script.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hardcoded framework list

Replace the hardcoded IGNORED_MANIFEST_KEYS denylist with sequence
alignment: the editor now sends the expected manifest-key sequence of the
editable run (saved-block refs resolved to their component; multivariate
flags as "" wildcards since they render their active variant), and the
iframe finds the offset that best matches that run within the DOM's
top-level sections. Leading/trailing framework sections deco injects drop
out automatically, on any site, without enumerating them.

findSection now returns null for anything outside the aligned window, so
hover and click ignore framework sections cleanly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… candidates

The contiguous-window alignment broke on the TanStack runtime, where deco
interleaves zero-height framework sections (Theme, Session, ButtonHelp)
*between* editable ones rather than only around them, and where a Lazy
renders its inner section directly at top level instead of keeping a
Lazy.tsx wrapper. Symptoms: hover on the header did nothing, hover on the
next section highlighted as if it were the header, and clicks opened the
wrong (or no) section.

Replace the contiguous offset search with an LCS-style DP that assigns
each editable section to a DOM section in order, skipping non-matching
(framework) sections wherever they fall. Each editable section now sends
an array of candidate keys instead of one: a Lazy lists both its loader
key and the inner section key (covers classic wrapper vs TanStack inline),
multivariate stays a wildcard ([]). getAllSections() recomputes from the
live DOM each call so it self-heals as client-rendered/lazy sections mount.

Verified end-to-end with Playwright on both a classic SSR site (14/14
contiguous, offset 6) and the TanStack site (5/5, interleaved framework
skipped; correct highlight + click index for header and the section after).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…card

A multivariate (A/B) section resolved to [] (wildcard), which the
alignment treats as "matches anything". On the home page the first
editable section is an A/B-tested header, so its wildcard greedily
matched the leading zero-height framework section (Theme) instead of the
real Header — leaving the header un-hoverable and shifting nothing else
(only that entry mis-mapped). Now collect each variant's resolved
key(s) as candidates, so it matches whichever variant actually rendered.

Verified: home now aligns 8/8 with editable[0] -> Header (was Theme).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aka-sacci-ccr aka-sacci-ccr merged commit 4bb6fdc into main Jun 25, 2026
14 checks passed
@aka-sacci-ccr aka-sacci-ccr deleted the aka-sacci-ccr/fix-editor-section-hover branch June 25, 2026 13:24
decocms Bot pushed a commit that referenced this pull request Jun 25, 2026
PR: #4136 fix(sections-editor): make CMS hover overlay track scroll, click, and label correctly
Bump type: patch

- decocms (apps/mesh/package.json): 3.57.0 -> 3.57.1

Deploy-Scope: web
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.

2 participants