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

+28 -3
View File
@@ -224,10 +224,35 @@ input.cms-map__key { padding-top: 6px; }
.cms-attrchip { position: fixed; pointer-events: auto; transform: translate(-100%, -100%); display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 999px; border: 1px solid #5ee0a0; background: #14161d; color: #5ee0a0; z-index: 1; }
.cms-attrchip:hover { background: #1f3a2e; }
/* left rail: outline + block palette */
.cms-rail { pointer-events: auto; position: fixed; z-index: 2147483001; left: 12px; top: 12px; bottom: 76px; width: 280px; display: flex; flex-direction: column; overflow: hidden; background: var(--cms-panel); border: 1px solid var(--cms-line); border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,.5); }
.cms-rail > header { display: flex; align-items: center; justify-content: space-between; padding: 4px 6px 0 6px; border-bottom: 1px solid var(--cms-line); }
/* left panel: outline + blocks + nodes, one scroll (no tabs) */
.cms-rail { pointer-events: auto; position: fixed; z-index: 2147483001; left: 12px; top: 12px; bottom: 76px; width: 320px; display: flex; flex-direction: column; overflow: hidden; background: var(--cms-panel); border: 1px solid var(--cms-line); border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,.5); }
.cms-rail > header { display: flex; align-items: center; justify-content: space-between; padding: 6px 6px 6px 12px; border-bottom: 1px solid var(--cms-line); font-weight: 600; }
.cms-rail__title { display: inline-flex; align-items: center; gap: 8px; }
.cms-rail__body { overflow-y: auto; flex: 1; min-height: 0; padding-bottom: 12px; }
.cms-rail__body > section + section { border-top: 1px solid var(--cms-line); margin-top: 8px; padding-top: 4px; }
.cms-rail__sec { margin: 10px 12px 4px; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #8b8fa0; }
.cms-rail .cms-tabs { border: 0; } .cms-rail .cms-tabs button { display: inline-flex; align-items: center; gap: 6px; }
.cms-orow { display: flex; align-items: center; gap: 2px; border-radius: 6px; padding-right: 4px; }
.cms-orow:hover { background: #242836; }
.cms-orow.is-sel { background: #242836; color: var(--cms-accent); }
.cms-orow__main { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; background: none; border: 0; border-radius: 6px; padding: 4px 6px 4px 2px; text-align: left; overflow: hidden; white-space: nowrap; }
.cms-orow__grip { cursor: grab; color: #5b6075; display: inline-flex; padding: 4px 2px; touch-action: none; user-select: none; }
.cms-orow__grip:active { cursor: grabbing; }
.cms-orow__chev { flex-shrink: 0; }
.cms-orow__spacer { width: 28px; flex-shrink: 0; }
.cms-outline--nested { padding: 0; overflow: visible; }
.cms-ogap { height: 6px; border-radius: 3px; margin: 1px 6px; transition: background .1s; }
.cms-ogap.is-over { background: rgba(94,224,160,.7); height: 8px; box-shadow: 0 0 0 2px rgba(94,224,160,.25); }
.cms-ogap.is-refused { background: rgba(163,58,74,.35); }
.cms-oslot { list-style: none; }
.cms-oslot__head { display: flex; align-items: center; gap: 6px; padding: 3px 6px 3px 4px; color: #8b8fa0; font-size: 12px; }
.cms-oslot__head .cms-muted { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.cms-oslot-tag { font-style: normal; font-size: 10px; padding: 0 5px; border-radius: 99px; background: #2b3350; color: #9fb2ff; }
.cms-nodefind { display: flex; flex-direction: column; gap: 6px; }
.cms-nodelist { list-style: none; margin: 0; padding: 0 8px; display: flex; flex-direction: column; gap: 2px; }
.cms-noderow { display: flex; align-items: center; gap: 8px; padding: 5px 6px; border: 1px solid var(--cms-line); border-radius: 8px; background: #1a1d26; cursor: grab; touch-action: none; user-select: none; }
.cms-noderow:active { cursor: grabbing; }
.cms-noderow__main { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; white-space: nowrap; }
.cms-pad { padding: 10px 12px 4px; }
.cms-hint { font-size: 11.5px; margin-top: 6px; }
.cms-outline { list-style: none; margin: 0; padding: 4px; overflow: auto; }