Click-to-select bridge and viewport toggle in the preview pane #16

Closed
opened 2026-07-11 13:02:06 +02:00 by dominik.polakovics · 2 comments

What to build

The cheap 80% of frontend editing (docs/DECISIONS.md D7, D11): in preview (hugo server mode), block elements carry data-ce-* markers via the theme's ce/attrs partial; a small script injected into the preview reports clicks to the SPA, which selects the corresponding element and scrolls to its form. Hovering an element form highlights the element in the preview. Plus a mobile/desktop viewport toggle for the preview pane.

Acceptance criteria

  • Clicking an element in the preview selects it in the element list and opens its form; nested elements select the innermost target with a way up to the container
  • Selecting/hovering a form highlights the rendered element in the preview
  • Mapping survives LiveReload refreshes without desync
  • Production builds contain neither markers nor bridge script
  • Viewport toggle switches the iframe between mobile/desktop widths

Blocked by

## What to build The cheap 80% of frontend editing (docs/DECISIONS.md D7, D11): in preview (hugo server mode), block elements carry data-ce-* markers via the theme's `ce/attrs` partial; a small script injected into the preview reports clicks to the SPA, which selects the corresponding element and scrolls to its form. Hovering an element form highlights the element in the preview. Plus a mobile/desktop viewport toggle for the preview pane. ## Acceptance criteria - [ ] Clicking an element in the preview selects it in the element list and opens its form; nested elements select the innermost target with a way up to the container - [ ] Selecting/hovering a form highlights the rendered element in the preview - [ ] Mapping survives LiveReload refreshes without desync - [ ] Production builds contain neither markers nor bridge script - [ ] Viewport toggle switches the iframe between mobile/desktop widths ## Blocked by - #3 - #15
Author
Owner

This was generated by AI during triage.

Agent Brief

Category: enhancement
Summary: The cheap 80% of frontend editing: a small bridge script, present only in server-mode preview builds, reports clicks on data-ce-*-marked elements in the preview iframe to the SPA, which selects the corresponding element and opens its form; hovering/selecting a form highlights the rendered element in the preview. Plus a mobile/desktop viewport toggle for the preview pane.

Dependency note: this issue is hard-blocked by #3 and #15. #3 provides element selection and the schema-generated forms the bridge must open; #15 provides the theme's ce/attrs partial that stamps data-ce-* markers on every block element's root tag under hugo.IsServer — without the markers there is nothing to click, without the forms nothing to open. The scheduler handles ordering. The issue body's docs/DECISIONS.md D7, D11 references point at a file that is not committed — this brief is self-contained.

Current behavior:
The SPA's preview pane renders the Hugo dev server in an iframe pointed at Hugo's own URL — a different origin (port) than the harness. Selection flows one way only: the user picks an element from the SPA's element list and edits its form; clicking inside the preview does nothing editor-related, there is no visual link between a form and its rendered element, and the iframe is always the full pane width. After #15, preview builds carry data-ce-* markers on block elements' root tags, but nothing consumes them.

Desired behavior:

Bridge script. A small script rides along in server-mode preview builds only (the natural delivery is the theme including it behind hugo.IsServer, alongside the ce/attrs markers). It attaches click handling for marked elements and talks to the parent SPA. Because Hugo's dev server is a different origin than the harness, the channel is postMessage (or an equivalent cross-origin-safe mechanism), and both sides validate the peer's origin — the SPA accepts bridge messages only from the known preview URL's origin, the bridge accepts commands only from its embedding parent.

Click-to-select. Clicking a marked element in the preview selects that element in the SPA's element list and opens its schema-generated form, scrolling it into view. This drives the same selection state the element list uses — one source of truth, not a parallel path. For nested elements (grid columns, accordion items), the innermost marked element under the click wins, and the UI offers an obvious way up to the containing element (e.g. a parent/breadcrumb affordance on the selection). Clicks on unmarked regions do nothing editor-related; normal in-preview navigation (following links) keeps working.

Reverse highlight. Selecting an element's form, or hovering over it, visually highlights the corresponding rendered element in the preview (e.g. an outline); the highlight clears when the hover ends or the selection changes. This flows over the same bridge channel in the other direction.

Survives LiveReload. Hugo LiveReload replaces the preview document on every rebuild — including right after each save from #3. The mapping must not desync: element identity comes from the data-ce-* markers freshly emitted on each build, never from cached DOM references, and the bridge re-establishes itself after every reload. After a save-triggered reload, clicking and highlighting still target the correct elements.

Production hygiene. A production build (hugo without server) contains neither data-ce-* markers (already #15's contract) nor the bridge script — extend the guarantee to the script and cover both with a test.

Viewport toggle. The preview pane gains a mobile/desktop toggle that switches the iframe between a representative mobile width (narrow, centered) and full desktop width, so editors can check the mobile-first theme both ways. The toggle is independent of the bridge and works even when nothing is selected.

Key interfaces:

  • The data-ce-* marker semantics documented by #15's ce/attrs partial are the bridge's input: page identity plus the element's address within the page's parsed tree. If the markers as emitted lack something the bridge genuinely needs, extend the partial's output in this issue — keeping emission strictly behind hugo.IsServer.
  • The bridge message protocol (both directions): element identity in click reports, highlight/clear commands from the SPA, with origin validation on both ends. Document the message shapes — later preview features build on this channel.
  • The SPA selection state from #3/#4: bridge clicks must land in the same mechanism, so list-driven and preview-driven selection are indistinguishable downstream.
  • The element address carried by markers must correspond to the tree the harness API serves for that page, so the SPA can map a click to a form without heuristics.

Acceptance criteria:

  • Clicking a rendered block element in the preview selects it in the element list and opens its form, scrolled into view
  • For nested elements the innermost marked target is selected, and the UI offers a way up to the containing element
  • Selecting or hovering an element's form highlights the corresponding rendered element in the preview, and the highlight clears afterwards
  • After a LiveReload refresh (e.g. following a save), click-to-select and highlighting still map to the correct elements — no desync
  • The SPA validates the origin of bridge messages; the bridge only honors commands from its parent
  • A production build of the sample site contains neither data-ce-* attributes nor the bridge script, covered by a test
  • A viewport toggle switches the preview iframe between mobile and desktop widths
  • The full flow works end-to-end against the sample site

Out of scope:

  • Editing inside the preview — inline text editing, drag-and-drop, in-preview toolbars; this slice is select-and-highlight only
  • Element CRUD — add/remove/reorder/nest (#6)
  • Rich-text bodies and inline chips (#7, #8)
  • The element list and page tree UI themselves (#4) — this issue drives their existing selection
  • The theme and ce/attrs partial (#15) beyond extending the marker payload if the bridge needs it
  • Arbitrary custom viewport widths, device frames, or rotation — two widths suffice
> *This was generated by AI during triage.* ## Agent Brief **Category:** enhancement **Summary:** The cheap 80% of frontend editing: a small bridge script, present only in server-mode preview builds, reports clicks on `data-ce-*`-marked elements in the preview iframe to the SPA, which selects the corresponding element and opens its form; hovering/selecting a form highlights the rendered element in the preview. Plus a mobile/desktop viewport toggle for the preview pane. **Dependency note:** this issue is **hard-blocked by #3 and #15**. #3 provides element selection and the schema-generated forms the bridge must open; #15 provides the theme's `ce/attrs` partial that stamps `data-ce-*` markers on every block element's root tag under `hugo.IsServer` — without the markers there is nothing to click, without the forms nothing to open. The scheduler handles ordering. The issue body's `docs/DECISIONS.md D7, D11` references point at a file that is not committed — this brief is self-contained. **Current behavior:** The SPA's preview pane renders the Hugo dev server in an iframe pointed at Hugo's own URL — a different origin (port) than the harness. Selection flows one way only: the user picks an element from the SPA's element list and edits its form; clicking inside the preview does nothing editor-related, there is no visual link between a form and its rendered element, and the iframe is always the full pane width. After #15, preview builds carry `data-ce-*` markers on block elements' root tags, but nothing consumes them. **Desired behavior:** *Bridge script.* A small script rides along in server-mode preview builds only (the natural delivery is the theme including it behind `hugo.IsServer`, alongside the `ce/attrs` markers). It attaches click handling for marked elements and talks to the parent SPA. Because Hugo's dev server is a different origin than the harness, the channel is `postMessage` (or an equivalent cross-origin-safe mechanism), and both sides validate the peer's origin — the SPA accepts bridge messages only from the known preview URL's origin, the bridge accepts commands only from its embedding parent. *Click-to-select.* Clicking a marked element in the preview selects that element in the SPA's element list and opens its schema-generated form, scrolling it into view. This drives the same selection state the element list uses — one source of truth, not a parallel path. For nested elements (grid columns, accordion items), the innermost marked element under the click wins, and the UI offers an obvious way up to the containing element (e.g. a parent/breadcrumb affordance on the selection). Clicks on unmarked regions do nothing editor-related; normal in-preview navigation (following links) keeps working. *Reverse highlight.* Selecting an element's form, or hovering over it, visually highlights the corresponding rendered element in the preview (e.g. an outline); the highlight clears when the hover ends or the selection changes. This flows over the same bridge channel in the other direction. *Survives LiveReload.* Hugo LiveReload replaces the preview document on every rebuild — including right after each save from #3. The mapping must not desync: element identity comes from the `data-ce-*` markers freshly emitted on each build, never from cached DOM references, and the bridge re-establishes itself after every reload. After a save-triggered reload, clicking and highlighting still target the correct elements. *Production hygiene.* A production build (`hugo` without server) contains neither `data-ce-*` markers (already #15's contract) nor the bridge script — extend the guarantee to the script and cover both with a test. *Viewport toggle.* The preview pane gains a mobile/desktop toggle that switches the iframe between a representative mobile width (narrow, centered) and full desktop width, so editors can check the mobile-first theme both ways. The toggle is independent of the bridge and works even when nothing is selected. **Key interfaces:** - The `data-ce-*` marker semantics documented by #15's `ce/attrs` partial are the bridge's input: page identity plus the element's address within the page's parsed tree. If the markers as emitted lack something the bridge genuinely needs, extend the partial's output in this issue — keeping emission strictly behind `hugo.IsServer`. - The bridge message protocol (both directions): element identity in click reports, highlight/clear commands from the SPA, with origin validation on both ends. Document the message shapes — later preview features build on this channel. - The SPA selection state from #3/#4: bridge clicks must land in the same mechanism, so list-driven and preview-driven selection are indistinguishable downstream. - The element address carried by markers must correspond to the tree the harness API serves for that page, so the SPA can map a click to a form without heuristics. **Acceptance criteria:** - [ ] Clicking a rendered block element in the preview selects it in the element list and opens its form, scrolled into view - [ ] For nested elements the innermost marked target is selected, and the UI offers a way up to the containing element - [ ] Selecting or hovering an element's form highlights the corresponding rendered element in the preview, and the highlight clears afterwards - [ ] After a LiveReload refresh (e.g. following a save), click-to-select and highlighting still map to the correct elements — no desync - [ ] The SPA validates the origin of bridge messages; the bridge only honors commands from its parent - [ ] A production build of the sample site contains neither `data-ce-*` attributes nor the bridge script, covered by a test - [ ] A viewport toggle switches the preview iframe between mobile and desktop widths - [ ] The full flow works end-to-end against the sample site **Out of scope:** - Editing inside the preview — inline text editing, drag-and-drop, in-preview toolbars; this slice is select-and-highlight only - Element CRUD — add/remove/reorder/nest (#6) - Rich-text bodies and inline chips (#7, #8) - The element list and page tree UI themselves (#4) — this issue drives their existing selection - The theme and `ce/attrs` partial (#15) beyond extending the marker payload if the bridge needs it - Arbitrary custom viewport widths, device frames, or rotation — two widths suffice
Author
Owner

Closing as superseded: the maintainer has decided on a clean-slate reset of the project (issue #29, PR #30). The v1 implementation this issue builds on is being removed and the plan will be re-drawn from scratch; this spec is retired rather than actioned.

Closing as superseded: the maintainer has decided on a clean-slate reset of the project (issue #29, PR #30). The v1 implementation this issue builds on is being removed and the plan will be re-drawn from scratch; this spec is retired rather than actioned.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle#16
No description provided.