fix(sections-editor): make CMS hover overlay track scroll, click, and label correctly#4136
Merged
Merged
Conversation
… 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>
guitavano
approved these changes
Jun 25, 2026
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
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.
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
Highlight didn't follow the section on scroll. It used
position:fixedwith viewport-relative coords recomputed only onmousemove, so scrolling without moving the mouse left the highlight floating in the old spot. Now usesposition:absolutewith document coordinates (rect + scroll), so the browser scrolls it together with the page natively — no lag. A capture-phasescroll/resizelistener covers nested scroll containers.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 decofilesectionsarray the panel indexes into. Confirmed via/.decofile: the editable array runsHeader … Footer; deco injects framework sections (SeoV2,Theme,htmx,Analytics,Session,CartAlert) around it that are not in that array. Now scopes to top-levelsection[data-manifest-key]and skips framework keys viaIGNORED_MANIFEST_KEYS.Badge label correctness.
Lazyrendering) show their block name — resolved editor-side since the DOM only carries manifest paths.data-manifest-key).Rendering/Lazy.tsx.Color-coded highlight by kind: global = light purple, variant = green, normal = blue.
Implementation notes
preview.tsx(parseSections+getPageVariantSectionsAt) and sent into the iframe viapostMessage(cms-editor::set-labels) on every (re)injection — mode switch, page navigation, save-reload — so they track the current page. NouseEffect.{ sectionIndex, manifestKey }) unchanged; only DOM enumeration/positioning/labeling changed.Testing
bun run fmt,bun run lint(0 errors),tsc --noEmit(mesh) all clean.🤖 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
Lazywrapper vs inner and multivariate variant keys), so framework-injected and runtime-specific differences are ignored.section[data-manifest-key]using subsequence matching and per-section candidate keys (e.g.,Lazywrapper 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.Lazy); non-globalLazyshows the inner section key; others show the full manifest key. Labels/kinds/keys are provided viacms-editor::set-labels.Written for commit 8db50b8. Summary will update on new commits.