Single panel: outline tree with slots, node search with type filter

The outline and blocks are no longer tabs: one panel shows the
outline tree, the new-block palette and a search over existing
nodes. Outline rows drag to reorder (same snap engine, explicit
gap targets), palette blocks and searched nodes drop onto the
outline gaps as well as the page.

Named slots (x-graph.connections, e.g. a pdp's info-section /
3d-accessories / model) render as groups under their block with
allowed types, live counts and a slot-limited insert picker.
Drops that fit no plain-children rule resolve to the first
accepting slot; existing nodes link via graft tree.link.

Adds Api.searchNodes, schema connections helpers, handle-aware
controller ops (linkExisting, insertIntoSlot, dropExisting) and
mock support for /api/nodes listing and graft links.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
mats committed 2026-11-06 20:38:31 +01:00
1 parent a4ed99f13e
commit 33bd5269fd
16 files changed
+852 -110

No files matched your search

+13 -3
View File
@@ -110,9 +110,19 @@ the page URL with `X-CMS-Preview: 1`. Visitors who are not signed in are never t
click a field again to edit. Ambiguous values (same text twice, one value on two properties) are skipped rather than guessed, and
enums, markdown/html fields and read-only fields are never bound as text - those stay in Properties (or need a template marker,
e.g. `data-cms-editor="markdown"` for the WYSIWYG editor).
- **Block palette** (dock ▸ Blocks): every type that fits somewhere on the page, plus blueprints. Drag onto the page - a ghost
"virtual element" and a drop line show where it lands, and containers whose `x-graph.children` does not accept the type refuse it -
or click to add to the selection. Existing blocks drag the same way from the toolbar grip, and **sort by snapping between their siblings** (root nodes included): faint markers show every position, the line snaps to the nearest, and the block is only nested into a container under the pointer while **Alt** is held.
- **Panel** (dock ▸ Page): outline, blocks and nodes in one place, no tabs. Drag palette blocks, searched nodes and
outline rows onto the page - a ghost "virtual element" and a drop line show where it lands, and containers whose
`x-graph.children` does not accept the type refuse it - or drop them onto the outline's gaps and slots instead.
Existing blocks drag the same way from the toolbar grip or the outline grip, and **sort by snapping between their
siblings** (root nodes included): faint markers show every position, the line snaps to the nearest, and the block is
only nested into a container under the pointer while **Alt** is held. Reordering inside the outline uses the same
engine: drop a row onto another row's gap (before / after / into).
- **Nodes**: search *existing* nodes (`GET /api/nodes?type=&q=`) with a type filter, and link a shared block into the page
(`graft` with `tree.link`: no copy, one node in many places) - drag the row onto the page or the outline, or click +
to add it next to the selection.
- **Slots / dropzones** (`x-graph.connections`, e.g. a pdp's `info-section`, `3d-accessories`, `model`): the outline shows
each named slot under its block with its allowed types, a count and an add button (the insert picker is limited to the
slot's types). Drops that fit no plain-children rule resolve to the first accepting slot automatically.
- **Insertion slots**: a selected or hovered container shows "+" between its children (and a big "Add block" when empty).
- **One dialog per thing**: opening Properties/Style/an attribute editor/an insert picker that is already open raises it, pulses it and focuses its first field (unsaved edits are kept) instead of opening a duplicate; clicking a dialog brings it forward and Escape closes the topmost. Custom dialogs opt in with `ctrl.openDialog({ id, ... })`.
- The toolbar keeps the everyday actions; **JSON** and other developer tools live under **…** (`placement: 'menu'` on a