Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ docs/
│ ├── site-transfer.md ← export / import CMS bundles
│ ├── site-import.md ← Super Import (static sites → CMS)
│ ├── html-import.md ← paste / import HTML into the page tree
│ ├── fragments.md ← portable sections/pages/components: extract, plan, insert
│ ├── editor-preferences.md ← catalog-driven editor prefs
│ └── canvas-iframe-per-frame.md ← per-breakpoint iframe rendering
│
Expand Down Expand Up @@ -160,6 +161,7 @@ Three categories, three voices:
| [features/site-transfer.md](features/site-transfer.md) | Export / import CMS bundle (JSON round-trip between instances) |
| [features/site-import.md](features/site-import.md) | Super Import — static-site files / ZIP → pages, style rules, media |
| [features/html-import.md](features/html-import.md) | HTML string → `PageNode` fragment (paste HTML, AI `insertHtml` tool) |
| [features/fragments.md](features/fragments.md) | Site fragments — a section, page or component with its styles, tokens, components, tables and media; extract, plan and insert |
| [features/editor-preferences.md](features/editor-preferences.md) | Catalog-driven local UI preferences for the editor |
| [features/canvas-iframe-per-frame.md](features/canvas-iframe-per-frame.md) | Per-breakpoint iframe rendering in the visual editor canvas |

Expand Down
3 changes: 2 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ The repo is organized by responsibility, not by feature. Every file has one reas
| Framework schemas (leaf) | `src/core/framework-schema/*` | Pure TypeBox schemas + derived types for persisted framework settings (`FrameworkSettings`, `GeneratedClassMetadata`, etc.); no dependency on the engine or page-tree |
| CSS value sanitiser (leaf) | `src/core/css-sanitize/*` | Single canonical `sanitiseCssValue` — dependency-free leaf shared by `@core/publisher` and `@core/framework`; blocks `expression()` / `javascript:` / `{}` / `</` injection at the CSS value level |
| Visual components | `src/core/visualComponents/*` | VC tree shape, slot synchronization, recursion checks |
| Site fragments | `src/core/fragments/*` | Portable section/page/component format: extract a closure, plan an insert (the site wins), commit it in one undo step; headless and importable by build scripts |
| Persistence (client-side) | `src/core/persistence/*` | HTTP envelopes, response schemas, site validation |
| Validation utilities | `src/core/utils/*` | TypeBox helpers, JSON boundary helpers, sanitization |
| Admin shell | `src/admin/*` (excluding `pages/site/`)| Auth, routing, workspaces, plugin host UI, modals |
Expand Down Expand Up @@ -391,7 +392,7 @@ Architectural rules live as tests in `src/__tests__/architecture/*.test.ts` and
| All provider SDKs banned repo-wide (no exceptions); drivers talk directly to each provider's REST API | `ai-driver-isolation.test.ts` |
| UI primitives live in `src/ui/components/` | `ui-primitives-location.test.ts` |

See [docs/reference/architecture-tests.md](reference/architecture-tests.md) for the complete catalog (81 gate files).
See [docs/reference/architecture-tests.md](reference/architecture-tests.md) for the complete catalog (104 gate files).

---

Expand Down
8 changes: 6 additions & 2 deletions docs/editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ Organization is persisted in `site.explorer` on the site shell. Folders are deco

**Undo/redo** uses patch-based history: every undoable mutation captures Mutative `[next, forward, inverse]` patch pairs scoped to the `SiteDocument`. Undo applies `entry.inverse`, redo applies `entry.forward` — O(change) in both time and memory, not O(site). A 50-deep history holds kilobytes of patches instead of hundreds of megabytes of full-site clones. See [`docs/reference/editor-history.md`](reference/editor-history.md).

The store is composed of **12 slices**, each created by a factory in `store/slices/`:
The store is composed of **14 slices**, each created by a factory in `store/slices/`:

| Slice | Owns |
|------------------------|----------------------------------------------------------------------------|
Expand All @@ -345,6 +345,8 @@ The store is composed of **12 slices**, each created by a factory in `store/slic
| `sitePanelSlice` | Dependency manifest + site runtime settings |
| `clipboardSlice` | Copy / cut / paste of layer subtrees, persisted editor-wide |
| `inlineEditSlice` | `activeInlineEdit` — the canvas inline text-edit session (double-click to edit) |
| `layoutsSlice` | Saved layouts: save / insert / rename / delete |
| `fragmentsSlice` | Insert fragment dialog flag + `insertFragment` (one undo step) — see [`features/fragments.md`](features/fragments.md) |

The combined `EditorStore` type lives at `store/types.ts` so each slice can import it without going through `store.ts` (this eliminates the historical store ↔ slice cycles).

Expand Down Expand Up @@ -648,7 +650,7 @@ The modal uses the tile-card pattern from `docs/design.md`: `--bg-surface` paren

### Saved layouts

Right-clicking a layer (DOM panel or canvas) offers **Save as layout…** — page mode only, disabled with an inline reason on the page root. The action opens `LayoutNameDialog` (`src/admin/pages/site/dialogs/`), then `saveNodeAsLayout` captures the node + its whole subtree **and every referenced style rule** into a `SavedLayout` (`@core/layouts`) on `site.layouts`. The snapshot shape deliberately mirrors the clipboard payload, and both flows share one engine (`@site/store/subtreeSnapshot`): collecting a subtree + its classes, and restoring a snapshot with fresh node ids, scoped classes cloned with remapped `scope.nodeId`, framework classes re-matched by name, and regular classes reused-or-reimported. Inserting a saved layout therefore reproduces the original selection exactly, the same way paste would.
Right-clicking a layer (DOM panel or canvas) offers **Save as layout…** — page mode only, disabled with an inline reason on the page root. The action opens `LayoutNameDialog` (`src/admin/pages/site/dialogs/`), then `saveNodeAsLayout` captures the node + its whole subtree **and every referenced style rule** into a `SavedLayout` (`@core/layouts`) on `site.layouts`. The snapshot shape deliberately mirrors the clipboard payload, and both flows share one engine (`@site/store/subtreeSnapshot`): collecting a subtree + its classes, and restoring a snapshot with fresh node ids, scoped classes cloned with remapped `scope.nodeId`, framework classes re-matched by name, and regular classes reused-or-reimported. Inserting a saved layout therefore reproduces the original selection exactly, the same way paste would. Layouts are same-site snapshots that match regular classes by id; to move a section between sites use a fragment ([`features/fragments.md`](features/fragments.md)), which matches classes, components and tokens by name and brings the rules, tokens, tables and media they need.

Saved layouts persist as rows in the `layouts` system table (`savedLayoutFromRow` / `savedLayoutToCells` in `@core/data/layoutFromRow`) through the same transactional site-document save as pages and components (`PUT /admin/api/cms/site-document`, dirty-tracked per layout id with explicit deleted-layout ids). Plugins can ship layouts too — `definePack({ layouts: [{ id, name, html, css? }] })` entries are authored as clean HTML (+ CSS) and compiled to snapshot form at plugin build time (`compilePackLayout`, using the same HTML-import pipeline as "Paste HTML here…"); they install into the same table with ids namespaced `<pluginId>/<id>` and are replaced on pack re-sync (see [`docs/features/plugin-system.md`](features/plugin-system.md)). The inserter's Layouts section groups them accordingly: the user's **Saved** layouts first, then one group per plugin labelled with the plugin's display name (`composeLayoutsSection` + `pluginRuntime.getPluginName`). In the inserter, snapshot-borne hazards disable items inline instead of failing on click: a snapshot carrying a `base.outlet` follows the outlet module's own placement rules, and in VC mode a snapshot whose component refs would create a dependency cycle is disabled. Refs to since-deleted Visual Components are stripped at insertion time. Right-clicking a saved layout in the inserter offers Rename… (closes the inserter and reopens `LayoutNameDialog`) and Delete (immediate — it's an undoable site mutation — confirmed via toast).

Expand All @@ -668,6 +670,8 @@ Architecture:
- **`keybindings.ts`** — declarative keybinding registry.
- **`state.ts`, `stateHandlers.ts`** — palette state machine.

**Insert fragment from file…** (`editor.insertFragment`, `commands/fragments.ts`) opens `InsertFragmentModal` (`src/admin/modals/InsertFragment/`), lazily mounted by `AdminCanvasEditorBody` while `insertFragmentModalOpen`; see [`features/fragments.md`](features/fragments.md).

The palette is wired so that **plugin-registered commands work the same as built-in ones**. Spotlight is the editor's keyboard surface.

---
Expand Down
Loading
Loading