diff --git a/docs/e2e/feature-matrix.md b/docs/e2e/feature-matrix.md index 067659035..cbdb0e37f 100644 --- a/docs/e2e/feature-matrix.md +++ b/docs/e2e/feature-matrix.md @@ -169,6 +169,7 @@ VERSION-001 note: `tests/e2e/version-history.e2e.ts` creates and opens a page of | SITE-018 | P1 | ✅ | Templates | Author a post template with data bindings | Posts collection exists | Site panel → New template → binding picker → publish post | Canvas previews currentEntry title/body and the public post route renders the published row through the custom template | seeded-template priority, unresolved tokens, missing outlet, save/publish ordering | | SITE-019 | P1 | ✅ | Saved Layouts | Save and reuse a styled layout subtree | Page with authored section | Layer context menu → Save as layout → Module inserter → Layouts → manage menu → publish | Layout validation is inline, saved layout insertion preserves content/styles, rename/delete manage the saved item only, and already-inserted content persists and publishes | stale page selection, mobile category labels, context-menu layering, duplicate names | | SITE-017 | P1 | ✅ | Visual Components | Convert authored content into a reusable component with editable slot fills | Page with editable node | Componentize, add slot outlet, fill slot, save, publish | Public route includes component body and slot fill without editor slot labels | component/page save ordering, slot lock friction, stale refs | +| SITE-027 | P1 | ✅ | Canvas fidelity | Site custom properties that share an admin token name (`--radius`, `--text`) render with the site's values in the canvas | Page with a site stylesheet setting those properties on `:root` | Code panel → New stylesheet → insert Container → publish | Canvas `:root`, body, and published page agree on the site values; the empty-Container placeholder chrome keeps the editor radius | editor tokens shadowing site tokens, chrome losing its admin styling, canvas/published mismatch | Visual builder note: `visual-builder.e2e.ts` inserts notch and picker modules, searches the full module picker and inserts by keyboard while verifying Recent/list-view persistence, drags a picker module into a canvas Container target, selects layers, edits text/button props, reorders layers from the DOM panel and canvas drag handle, exercises undo/redo buttons and shortcuts with redo-branch clearing and reload reset, validates locked slot-instance structure while allowing slot content insertion, applies spacing/color/typography controls, verifies tablet-width insertion, authors a post template with currentEntry bindings, validates and manages saved layouts, and publishes a freshly componentized page with filled slot content. @@ -186,6 +187,8 @@ SITE-018 note: `visual-builder.e2e.ts` first publishes a disposable post, create SITE-019 note: `visual-builder.e2e.ts` saves a styled Container subtree as a layout, verifies blank and duplicate-name validation, confirms the Layouts category remains reachable at 390px, inserts the saved layout into another page with its captured class styling, renames and deletes the saved layout from the inserter manage menu, saves/reloads, publishes, and verifies anonymous public output. DEF-20260623-SITE019-001 fixed stale node selection on `addPage`; DEF-20260623-SITE019-002 fixed count-only mobile category buttons; DEF-20260623-SITE019-003 fixed the saved-layout manage menu z-index under the spotlight inserter. +SITE-027 note: `canvas-chrome-tokens.e2e.ts` creates a site stylesheet that sets `--radius` and `--text` on `:root` and applies them to `body`, inserts an empty Container so the module placeholder chrome renders, and reads computed values in real Chromium: the canvas iframe `:root` and `body` use the site values, the placeholder still uses the editor's `--radius`, and an anonymous visitor of the published route sees the same body values as the canvas. Bun-level `editorChromeInjector.test.tsx` separately fails if the chrome stylesheet declares or reads any custom property outside `--chrome-*`; the unit DOM cannot model `@layer`, which is why the browser spec exists. Run against `main`'s injector on 2026-10-13, it failed with canvas `--radius` 6px and text `#ededed`. + ## Site Runtime And Code | ID | Priority | Auto | Area | User Goal | Setup | Path | Expected Outcome | Watch For | diff --git a/docs/e2e/feature-validation.tsv b/docs/e2e/feature-validation.tsv index c161a921b..3870e4d83 100644 --- a/docs/e2e/feature-validation.tsv +++ b/docs/e2e/feature-validation.tsv @@ -54,6 +54,7 @@ SITE-023 Site AI agent panel As a site editor, I want an AI assistant in the sit SITE-024 Canvas contexts and conditional layers As a site editor, I want to author conditional visibility and preview request contexts so pages can vary by state without duplicating structures. CanvasContextSelector switches request/condition contexts, ConditionBuilder edits node conditions, conditional layer actions update node condition state, and dynamic detection/publisher respects request-dependent output. Missing context; invalid condition expression; conditions on locked/slot nodes; context switch with selected node; public cache mismatch. Condition schemas and store actions validate condition trees; dynamic detection determines request-dependent holes; style/content/structure caps govern edits. src/admin/pages/site/canvas/CanvasContextSelector.tsx; src/admin/pages/site/canvas/ConditionBuilder.tsx; src/admin/pages/site/store/slices/site/conditionalLayerActions.ts; src/core/publisher/dynamicDetection.ts Some context behaviours surface through public holes rather than static HTML. Happy: add condition and preview alternate context. Error: invalid condition rejected. Boundary: default context. Invalid: stale condition field. Permission: structure/content edit as implemented. Performance: context switch quick. Mobile: selector usable. Automated baseline, build, lint, bundle, and Playwright E2E passed; manual exploratory pending 0 None 2026-06-21 SITE-025 Clipboard, multi-select, inline edit, and rename workflows As a site editor, I want to copy/paste, multi-select, inline edit text, and rename layers/pages so repetitive edits are fast. Editor store supports clipboard subtree clone/remap, multi-selection inspector/actions, inline text editing wiring for modules with inlineTextEdit, and canvas/explorer rename dialogs. Copy locked/slot nodes; paste into invalid parent; multi-select across trees; inline edit loses focus; duplicate scoped class IDs. Clipboard and clone helpers remap node ids/scoped classes; inline edit updates declared prop only; rename routes through centralized mutations/dialog validation. src/admin/pages/site/store/slices/clipboard; src/admin/pages/site/panels/PropertiesPanel/MultiSelectionInspector.tsx; src/admin/pages/site/store/slices/inlineEditSlice.ts; src/admin/pages/site/canvas/CanvasRenameDialog.tsx Some behaviours have unit coverage and need browser focus checks. Happy: copy/paste and inline edit text. Error: invalid paste ignored. Boundary: multi-select zero/one/many. Invalid: stale clipboard node. Permission: structure/content caps by action. Performance: paste large subtree acceptable. Mobile: inline editing usable. Automated baseline, build, lint, bundle, and Playwright E2E passed; manual exploratory pending 0 None 2026-06-21 SITE-026 Plugin editor extensions As a site editor, I want enabled plugins to contribute editor panels, canvas overlays, toolbar actions, and modules so extension UI works inside the editor. PanelRail shows plugin editor panels, PluginEditorPanel renders plugin content through host hooks, PluginCanvasOverlayLayer renders overlays, plugin module factory renders sandboxed module packs, and toolbar/runtime hooks execute through plugin host. Plugin disabled mid-session; activation error; missing runtime asset; overlay measuring wrong iframe; module dependency missing. Plugin host imports respect boundaries; grantedPermissions control SDK surfaces; module pack VM validates payloads and dependencies. src/admin/pages/site/panels/PluginEditorPanel; src/admin/pages/site/canvas/PluginCanvasOverlayLayer; src/admin/pages/site/canvas/pluginModuleComponentFactory.tsx; server/plugins/modulePackVm.ts Requires fixture plugin with editor contributions. Happy: fixture plugin panel/overlay/module renders. Error: activation error shown. Boundary: disabled plugin removes contribution. Invalid: malformed module pack. Permission: plugin granted permissions. Performance: plugin failure does not crash editor. Mobile: panel/overlay does not block controls. Automated baseline, build, lint, bundle, and Playwright E2E passed; manual exploratory pending 0 None 2026-06-21 +SITE-027 Canvas renders site custom properties that share admin token names As a site editor, I want my site's own custom properties to render in the canvas exactly as they publish, even when they share a name with an editor token such as --radius or --text. The canvas iframe's editor-chrome stylesheet forwards admin tokens only under the --chrome-* namespace, so a site :root rule setting --radius or --text in @layer user-authored resolves to the site's value in the canvas and on the published page, while editor chrome (module placeholder, slot instance, unknown-module fallback) keeps the admin values. Site property named like any forwarded admin token (--text-subtle, --bg-surface, --border, --danger); site Framework tokens --text-* and --space-*; chrome rules reading an un-namespaced var; placeholder chrome inside user content. EditorChromeInjector declares and reads only --chrome-* custom properties on the iframe :root; the forwarded token set is derived from the var(--chrome-*) references in its rules; author CSS stays in @layer user-authored. src/admin/pages/site/canvas/EditorChromeInjector.tsx; src/admin/pages/site/canvas/IframeFrameSurface.tsx; src/admin/pages/site/canvas/ClassStyleInjector.tsx; src/admin/pages/site/canvas/UserStylesheetInjector.tsx; tests/e2e/canvas-chrome-tokens.e2e.ts; src/__tests__/canvas/editorChromeInjector.test.tsx happy-dom does not implement cascade layers, so the cascade itself is only provable in the Playwright browser spec; the Bun test guards the namespace invariant. Happy: site stylesheet sets --radius/--text and canvas plus published body match the site values. Error: editor value shadows site value (fails on pre-fix main). Boundary: empty Container placeholder chrome keeps the editor radius. Invalid: chrome CSS declaring or reading a non --chrome-* property fails the Bun gate. Permission: Site edit and publish as owner. Performance: n/a. Mobile: n/a. canvas-chrome-tokens.e2e.ts and editorChromeInjector.test.tsx passed 2026-10-13; both fail against the pre-fix injector 1 P2 fixed Defect found 2026-10-13 while verifying fragment inserts: canvas computed --radius was 6px although the site :root set 12px. Fixed by namespacing every forwarded chrome token as --chrome-*. 2026-10-13 PUBLISH-001 Publish now As a site publisher, I want to publish draft changes so visitors see the current site. Publish button uses step-up, POST /publish builds site CSS/runtime assets/static artifacts, swaps current slot, bumps publish version, and returns status. Concurrent publish lock; disk write failure with DB fallback; no pages; unpublished drafts; long publish. Publish route requires pages.publish and step-up; publish pipeline validates/sanitizes page tree and assets; cache version bumps on success. src/admin/pages/site/toolbar/PublishButton.tsx; server/handlers/cms/publish.ts; server/publish/publishSite.ts; server/publish/staticArtefact.ts; server/publish/publishState.ts Use disposable uploads dir; public assertions must use fresh visitor context. Happy: publish homepage and public route updates. Error: publish failure shows message. Boundary: no-op publish. Invalid: invalid site doc blocks. Permission: publisher cap + step-up. Performance: progress prevents duplicate clicks. Mobile: publish action reachable. Manual core lifecycle passed 2026-06-22; automated/build/lint/bundle/Playwright E2E passed; broader exploratory pending 0 None Manual run 2026-06-22 core-owner-lifecycle passed; see docs/e2e/runs/2026-06-22-core-owner-lifecycle.md. 2026-06-22 PUBLISH-002 Scheduled publish As a publisher, I want to schedule the active page or post row for future publication so content can go live later without an immediate publish click. Site toolbar scheduling opens SchedulePublishDialog for the active page or post row, captures a local future datetime, posts the ISO time to the row schedule endpoint, marks the row scheduled, surfaces scheduled pages in the dashboard Pages count and Publish lineup, and tickPublishScheduler publishes due rows as a system actor. Past datetime; local timezone conversion; close/cancel without mutation; multiple scheduled rows; server restart; advisory lock duplicate prevention; due row already changed before tick. Dialog requires a date/time value and blocks past times; RowScheduleBodySchema requires scheduledPublishAt; handler requires publisher permission and canPublishDataRow; repository lists due scheduled rows; scheduler uses advisory locking and a system publisher. src/admin/modals/SchedulePublishDialog/SchedulePublishDialog.tsx; src/admin/pages/site/toolbar/PublishButton.tsx; src/admin/pages/dashboard/widgets/PagesWidget.tsx; src/admin/pages/dashboard/widgets/PublishQueueWidget.tsx; server/handlers/cms/data/rows.ts; server/repositories/data/rows/schedule.ts; server/publish/publishScheduler.ts; src/__tests__/server/publishScheduler.test.ts; tests/e2e/page-management.e2e.ts Site scheduling targets the active page row rather than a whole-site static publish; the editor Page shape does not currently prefill scheduledPublishAt in the dialog; dashboard widgets are the user-visible scheduled status surface. Happy: schedule active page from Site toolbar and verify dashboard scheduled count plus Publish lineup route. Error: past time is rejected by dialog/server. Boundary: local datetime serializes to ISO near timezone boundaries. Invalid: missing datetime disables scheduling. Permission/security: publisher permission and data-row publish gate. Performance: dashboard lineup refreshes from summary endpoint. Mobile: dialog and publish menu remain reachable on narrow viewport. Focused PUBLISH-002 page scheduling Playwright regression and scheduler tick Bun regression passed 2026-06-23 0 None Commands: bun run test:e2e -- --project=e2e tests/e2e/page-management.e2e.ts -g "PUBLISH-002"; bun test src/__tests__/server/publishScheduler.test.ts. Run log: docs/e2e/runs/2026-06-23-publish002-page-scheduling.md. 2026-06-23 PUBLISH-003 Publish cache, artifacts, and status As an operator, I want publish state and cache invalidation to be correct so visitors do not see stale or mixed pages. Static artifacts serve disk-first from uploads/published/current, render cache keys include publishVersion, version bump evicts dynamic cache, and status endpoint reports current publish state. Partial artifact slot; stale CSS hash; dynamic route cache after publish; unpublish artifact removal. Two-slot symlink swap; namespace routes 404 unknown assets; renderCache keyed by url, query, version. server/publish/staticArtefact.ts; server/publish/renderCache.ts; server/publish/publishState.ts; server/handlers/cms/publish.ts Requires filesystem permissions in uploads dir. Happy: publish then cache updates. Error: stale asset hash 404. Boundary: simultaneous visitor during swap. Invalid: bad status method. Permission: status read as configured. Performance: static route disk-first. Mobile: N/A. Manual core lifecycle partially covered 2026-06-22; automated/build/lint/bundle/Playwright E2E passed; broader exploratory pending 0 None Manual run 2026-06-22 core-owner-lifecycle passed; see docs/e2e/runs/2026-06-22-core-owner-lifecycle.md. 2026-06-22 diff --git a/docs/editor.md b/docs/editor.md index 69a8b69f8..36b858988 100644 --- a/docs/editor.md +++ b/docs/editor.md @@ -433,7 +433,9 @@ Each iframe `
` receives five `