view: the preview is just the site — remove the editor bridge #241

Closed
opened 2026-10-12 22:22:38 +02:00 by dominik.polakovics · 0 comments

Problem

In the View module, clicking an element in the preview jumps into that element's edit form. Editors expect the view to be the site: they want to click around it, open accordions, use buttons, follow links. Today a click inside any marked element is intercepted by the theme's preview bridge (scaffold/themes/deckle/assets/js/ce-bridge.js): it calls preventDefault (so a <summary> toggle, a form button or a lightbox trigger never fires) and posts a deckle:select message the app turns into a navigation to the edit module.

Decision

Remove the editor bridge entirely. The View module is a preview of the site and nothing else, as TYPO3's View module is. Editing starts from the Page module, never from the preview. There is no toggle and no modifier-click.

Consequences decided:

  1. Click-to-edit from the preview goes. No replacement.
  2. Preview → tree sync goes. The bridge's ready reply (carrying location.pathname) was the only way the app learned where the user browsed inside the iframe. The tree selection no longer follows in-preview navigation.
  3. A tree click in the View module always loads the clicked page into the frame, even when it is already the selected page. Without the reverse channel the app only knows the last URL it set. Edge case to cover: tree has page A selected, user browses the preview to B, clicks A in the tree — the frame must go back to A. Note that selection.selectPage (web/src/selection.ts) deliberately no-ops on the same page, and must keep doing so (the selection is derived from the route, issue #33). The "go there" needs its own signal from the tree pane's click to the view module (a click counter / an onActivate next to the selection), not a second selection store. Re-assigning the same iframe src does nothing in Solid, so the load has to go through the iframe ref or a changed value.
  4. The element form's Preview button stays, and lands on the element via the anchor fragment. The view opens the page's address with #c-<uid> appended — the anchor partials/ce/attrs.html already emits in every build and the link picker already uses. The browser scrolls natively. No outline. The view route keeps its element parameter (routing.viewElementRoute, readElement); the resolution behind it (fetching the page, findNodeByUid, childPathToOrdinal, the reveal signal in ViewModule) goes. return navigation back to the form is untouched.
  5. The hover outline from the Page module's element cards goes. It was already unreachable: PreviewPane is mounted only in the View module, the cards only in the Page module.
  6. The theme loses every bridge-only surface. The id="c-<uid>" anchor stays.

An existing site needs no theme update for the fix to take effect: the old bridge intercepts clicks only after the app's deckle:hello handshake, and the new app never sends one, so an old script sits idle. The theme cleanup is hygiene, not a prerequisite.

Scope

App (web/src)

  • Remove bridge.ts (message types, parseBridgeMessage, previewOrigin, ordinalToChildPath, childPathToOrdinal) and its tests test/bridge.test.ts, test/Bridge.test.tsx.
  • PreviewPane.tsx: drop the handshake, the message listener, bridgeLive, iframeShownPath, onBridgeSelect, highlight, reveal, onPreviewNavigate. Keep the viewport toggle and the crash/placeholder states. Rework the forward navigation so a tree activation always loads (decision 3) and the Preview-button arrival loads <address>#c-<uid> (decision 4). Update test/PreviewPane.test.tsx, test/PreviewPaneSync.test.tsx, test/PreviewPaneLang.test.tsx accordingly.
  • App.tsx: remove handleBridgeSelect, bridgeSeq, the highlight signal and its module-switch reset, and the onBridgeSelect / highlight / onHighlight entries of the module context.
  • modules.tsx: ModuleContext loses highlight, onHighlight, onBridgeSelect; ViewModule loses handlePreviewNavigate, the reveal effect and reveal; the PAGE_MODULE comment about the bridge naming it is rewritten. previewmap.ts keeps permalinkForPage/normalizePreviewPath/joinPreviewUrl; buildPermalinkMap and lookupPageByPath lose their only caller — remove them and their tests unless another caller appears.
  • PageContent.tsx / PageScreen.tsx: remove the hover → highlight plumbing (hover, hoverAt, hoverPath, onHighlight, the card hoverStart/hoverEnd callbacks if nothing else uses them).
  • FormScreen.tsx doPreview is unchanged in intent; verify it still builds the view route with the element parameter.
  • i18n: remove any catalog keys that only the removed UI used.

Theme (scaffold/themes/deckle)

  • Delete assets/js/ce-bridge.js and layouts/partials/ce/path.html.
  • layouts/_default/baseof.html: delete the {{ if hugo.IsServer }} block (the .ce-highlight style and the script include).
  • layouts/partials/ce/attrs.html: emit the anchor only; drop the hugo.IsServer branch and rewrite the header comment (the ordinal-semantics paragraph goes with it).
  • layouts/partials/backendlayout/slot.html: drop the comment about inherited content carrying the ancestor's data-ce-page.
  • layouts/partials/l10n/script.html: adjust the note that lists ce-bridge.js among text-free assets, if present.

Go

  • internal/theme/theme_test.go: remove the test over the hugo.IsServer block (~line 666) and the marker-value tests (~line 1034–1070); in the production-hygiene test (~line 242) drop data-ce- and ce-bridge from the forbidden list, keep HAHAHUGOSHORTCODE. internal/theme/news_test.go ~line 1044 expects marker attributes on the news list — update. internal/theme/doc.go describes the leak guarantee in terms of markers and bridge — rewrite.
  • harness check and the drift tests (internal/element/agentsdoc_test.go, scaffold/skills_test.go) must stay green; AGENTS.md mentions none of this and is untouched.

Docs and skills (same change, per CLAUDE.md)

  • scaffold/themes/deckle/README.md: asset list (~107, ~801), the inheritance paragraph (~446: inherited content "keeps the ancestor's markers so clicking selects it on the owning page" — now simply inherits), the l10n note (~647), and the "Editor integration" section (~755–765), which shrinks to: the anchor exists in every build, internal links and the editor's Preview button use it; keep the ce/attrs.html call on every block element's frame.
  • scaffold/site/.claude/skills/redo-theme/SKILL.md: asset inventory (~92) and step 4 (~136–138): no leak grep, no click-to-select check; instead "the element form's Preview button opens the View module scrolled to that element".
  • scaffold/site/.claude/skills/redo-theme/references/theme-contract.md: the attrs/path partial contract (~30–33), the verbatim server-only block (~78–85), and the "bridge" mention (~146).
  • scaffold/site/.claude/skills/add-element/references/element-contract.md (~53): attrs emits the anchor only.
  • scaffold/site/.claude/skills/migrate-site/SKILL.md (~219): drop ce-bridge.js from the carry-over list.
  • scaffold/site/.claude/skills/verify-site/SKILL.md: theme bullet (~91–92) becomes the Preview-button check; drop the leak grep (~109–115) but keep the anchor-presence check.

Rules and owners

  • "Which URL does the preview show for a page" — previewmap.permalinkForPage on the tree's server-sent address, as today. The fragment for an element is "#c-" + uid, spelled once in the view module (the link picker's element targets spell the same anchor server-side; if the client already has a helper for it, call that).
  • "Is this the already-selected page" — selection.selectPage keeps that no-op; the view's reload is a separate activation signal, not a second evaluation of selection.
  • No client or theme re-derivation of element paths remains: the ordinal-path contract (ce/path.html, ordinalToChildPath, childPathToOrdinal) is deleted on both sides at once.

Verification

  • make check (or the repo's equivalent: go test ./..., vitest, harness check on the scaffold) green.
  • Manually in the editor: in the View module, click an accordion summary, a button, a link — the site reacts natively, nothing navigates in the editor. Browse to another page in the preview, click the originally selected page in the tree — the frame returns to it. From an element form, Preview lands scrolled to that element; Close returns to the form.
  • A production hugo build of the scaffold contains id="c-…" anchors and no data-ce- or ce-bridge strings (trivially, since neither exists anymore).
## Problem In the View module, clicking an element in the preview jumps into that element's edit form. Editors expect the view to be the site: they want to click around it, open accordions, use buttons, follow links. Today a click inside any marked element is intercepted by the theme's preview bridge (`scaffold/themes/deckle/assets/js/ce-bridge.js`): it calls `preventDefault` (so a `<summary>` toggle, a form button or a lightbox trigger never fires) and posts a `deckle:select` message the app turns into a navigation to the edit module. ## Decision Remove the editor bridge entirely. The View module is a preview of the site and nothing else, as TYPO3's View module is. Editing starts from the Page module, never from the preview. There is no toggle and no modifier-click. Consequences decided: 1. **Click-to-edit from the preview goes.** No replacement. 2. **Preview → tree sync goes.** The bridge's ready reply (carrying `location.pathname`) was the only way the app learned where the user browsed inside the iframe. The tree selection no longer follows in-preview navigation. 3. **A tree click in the View module always loads the clicked page into the frame, even when it is already the selected page.** Without the reverse channel the app only knows the last URL it set. Edge case to cover: tree has page A selected, user browses the preview to B, clicks A in the tree — the frame must go back to A. Note that `selection.selectPage` (`web/src/selection.ts`) deliberately no-ops on the same page, and must keep doing so (the selection is derived from the route, issue #33). The "go there" needs its own signal from the tree pane's click to the view module (a click counter / an `onActivate` next to the selection), not a second selection store. Re-assigning the same iframe `src` does nothing in Solid, so the load has to go through the iframe ref or a changed value. 4. **The element form's Preview button stays, and lands on the element via the anchor fragment.** The view opens the page's address with `#c-<uid>` appended — the anchor `partials/ce/attrs.html` already emits in every build and the link picker already uses. The browser scrolls natively. No outline. The view route keeps its `element` parameter (`routing.viewElementRoute`, `readElement`); the resolution behind it (fetching the page, `findNodeByUid`, `childPathToOrdinal`, the reveal signal in `ViewModule`) goes. `return` navigation back to the form is untouched. 5. **The hover outline from the Page module's element cards goes.** It was already unreachable: `PreviewPane` is mounted only in the View module, the cards only in the Page module. 6. **The theme loses every bridge-only surface.** The `id="c-<uid>"` anchor stays. An existing site needs no theme update for the fix to take effect: the old bridge intercepts clicks only after the app's `deckle:hello` handshake, and the new app never sends one, so an old script sits idle. The theme cleanup is hygiene, not a prerequisite. ## Scope ### App (`web/src`) - Remove `bridge.ts` (message types, `parseBridgeMessage`, `previewOrigin`, `ordinalToChildPath`, `childPathToOrdinal`) and its tests `test/bridge.test.ts`, `test/Bridge.test.tsx`. - `PreviewPane.tsx`: drop the handshake, the `message` listener, `bridgeLive`, `iframeShownPath`, `onBridgeSelect`, `highlight`, `reveal`, `onPreviewNavigate`. Keep the viewport toggle and the crash/placeholder states. Rework the forward navigation so a tree activation always loads (decision 3) and the Preview-button arrival loads `<address>#c-<uid>` (decision 4). Update `test/PreviewPane.test.tsx`, `test/PreviewPaneSync.test.tsx`, `test/PreviewPaneLang.test.tsx` accordingly. - `App.tsx`: remove `handleBridgeSelect`, `bridgeSeq`, the `highlight` signal and its module-switch reset, and the `onBridgeSelect` / `highlight` / `onHighlight` entries of the module context. - `modules.tsx`: `ModuleContext` loses `highlight`, `onHighlight`, `onBridgeSelect`; `ViewModule` loses `handlePreviewNavigate`, the reveal effect and `reveal`; the `PAGE_MODULE` comment about the bridge naming it is rewritten. `previewmap.ts` keeps `permalinkForPage`/`normalizePreviewPath`/`joinPreviewUrl`; `buildPermalinkMap` and `lookupPageByPath` lose their only caller — remove them and their tests unless another caller appears. - `PageContent.tsx` / `PageScreen.tsx`: remove the hover → highlight plumbing (`hover`, `hoverAt`, `hoverPath`, `onHighlight`, the card `hoverStart`/`hoverEnd` callbacks if nothing else uses them). - `FormScreen.tsx` `doPreview` is unchanged in intent; verify it still builds the view route with the element parameter. - i18n: remove any catalog keys that only the removed UI used. ### Theme (`scaffold/themes/deckle`) - Delete `assets/js/ce-bridge.js` and `layouts/partials/ce/path.html`. - `layouts/_default/baseof.html`: delete the `{{ if hugo.IsServer }}` block (the `.ce-highlight` style and the script include). - `layouts/partials/ce/attrs.html`: emit the anchor only; drop the `hugo.IsServer` branch and rewrite the header comment (the ordinal-semantics paragraph goes with it). - `layouts/partials/backendlayout/slot.html`: drop the comment about inherited content carrying the ancestor's `data-ce-page`. - `layouts/partials/l10n/script.html`: adjust the note that lists `ce-bridge.js` among text-free assets, if present. ### Go - `internal/theme/theme_test.go`: remove the test over the `hugo.IsServer` block (~line 666) and the marker-value tests (~line 1034–1070); in the production-hygiene test (~line 242) drop `data-ce-` and `ce-bridge` from the forbidden list, keep `HAHAHUGOSHORTCODE`. `internal/theme/news_test.go` ~line 1044 expects marker attributes on the news list — update. `internal/theme/doc.go` describes the leak guarantee in terms of markers and bridge — rewrite. - `harness check` and the drift tests (`internal/element/agentsdoc_test.go`, `scaffold/skills_test.go`) must stay green; AGENTS.md mentions none of this and is untouched. ### Docs and skills (same change, per CLAUDE.md) - `scaffold/themes/deckle/README.md`: asset list (~107, ~801), the inheritance paragraph (~446: inherited content "keeps the ancestor's markers so clicking selects it on the owning page" — now simply inherits), the l10n note (~647), and the "Editor integration" section (~755–765), which shrinks to: the anchor exists in every build, internal links and the editor's Preview button use it; keep the `ce/attrs.html` call on every block element's frame. - `scaffold/site/.claude/skills/redo-theme/SKILL.md`: asset inventory (~92) and step 4 (~136–138): no leak grep, no click-to-select check; instead "the element form's Preview button opens the View module scrolled to that element". - `scaffold/site/.claude/skills/redo-theme/references/theme-contract.md`: the attrs/path partial contract (~30–33), the verbatim server-only block (~78–85), and the "bridge" mention (~146). - `scaffold/site/.claude/skills/add-element/references/element-contract.md` (~53): attrs emits the anchor only. - `scaffold/site/.claude/skills/migrate-site/SKILL.md` (~219): drop `ce-bridge.js` from the carry-over list. - `scaffold/site/.claude/skills/verify-site/SKILL.md`: theme bullet (~91–92) becomes the Preview-button check; drop the leak grep (~109–115) but keep the anchor-presence check. ## Rules and owners - "Which URL does the preview show for a page" — `previewmap.permalinkForPage` on the tree's server-sent `address`, as today. The fragment for an element is `"#c-" + uid`, spelled once in the view module (the link picker's element targets spell the same anchor server-side; if the client already has a helper for it, call that). - "Is this the already-selected page" — `selection.selectPage` keeps that no-op; the view's reload is a separate activation signal, not a second evaluation of selection. - No client or theme re-derivation of element paths remains: the ordinal-path contract (`ce/path.html`, `ordinalToChildPath`, `childPathToOrdinal`) is deleted on both sides at once. ## Verification - `make check` (or the repo's equivalent: `go test ./...`, `vitest`, `harness check` on the scaffold) green. - Manually in the editor: in the View module, click an accordion summary, a button, a link — the site reacts natively, nothing navigates in the editor. Browse to another page in the preview, click the originally selected page in the tree — the frame returns to it. From an element form, Preview lands scrolled to that element; Close returns to the form. - A production `hugo` build of the scaffold contains `id="c-…"` anchors and no `data-ce-` or `ce-bridge` strings (trivially, since neither exists anymore).
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#241
No description provided.