Editor: composition structure wireframe (read-only)

This commit is contained in:
2026-06-29 12:39:59 +03:00
parent 345ecef5a3
commit 57604a80a6
13 changed files with 774 additions and 17 deletions
@@ -299,6 +299,11 @@ thin app-layer services.
- `spec-insert` — the composition the cursor is in (`compositionTargetAt`), inserting a view
at an index (`insertView`) and reordering siblings (`moveView`), plus `elementOffset` to
re-find a view after the edit.
- `spec-view-tree` — the whole composition as a recursive tree (`viewTree`), each node carrying
its operator, orientation and byte range. Read at once (vs. `spec-insert`'s one-array edits) to
drive the composition wireframe — a read-only schematic of the multi-view structure in a preview-
toolbar disclosure (`CompositionWireframe`); clicking a box reveals that view's range in the
editor via `AppStore.requestRevealView`. Interaction contract in [arch 10](10-interaction-and-feedback.md).
**Services (app, store-aware via `getState`):** `spec-transform-actions` (the
wrap/simplify/add-view operations and their surfaces), `spec-dataset-hints` (completion,
+18 -16
View File
@@ -328,21 +328,22 @@ Carbon and drawn `fill: currentColor`.
**Core set** — recurring, cross-surface:
| Meaning | Carbon glyph | Form | Surfaces |
| -------------------- | --------------- | ------------------------ | -------------------------------------------------------------------------------------------- |
| Close / dismiss | `Close` (✕) | icon-only ⭐ | `ModalShell`, `Toaster` |
| Theme → dark | `Asleep` (moon) | icon-only ⭐ | `ThemeToggle` (shown when light) |
| Theme → light | `Light` (sun) | icon-only ⭐ | `ThemeToggle` (shown when dark) |
| References a dataset | `DataTable` | icon + text | Library row marker, Linked-datasets list, header **Datasets**, editor **Extract**⁴ |
| Add / create-new | `Add` | icon + text → icon-only⁴ | Library "Create New Snippet" (collapses to "+" when the pane is narrow), Datasets "New …" |
| Delete | `TrashCan` | icon-only ⭐ (danger) | Library row delete¹ — text "Delete" in the panel² |
| Import workspace | `Upload` | icon + text | Header **Import** (a file is brought into the app) |
| Export workspace | `Download` | icon + text | Header **Export** (the workspace is written out) |
| About / information | `Information` | icon + text | Header **About** |
| Revert draft | `Reset` | icon + text → icon-only⁴ | Editor toolbar **Revert** (restore last published) |
| Live search | `Search` | icon-in-field⁵ | Library search box (leading magnifier; the input's `aria-label`/placeholder names the field) |
| Settings (gear) | `Settings` | icon-only ⭐ | Per-pane settings disclosures (Editor, Preview, Library dates) |
| Unpublished draft | (CSS dot) | status-glyph | Library row (paired with a hidden label) |
| Meaning | Carbon glyph | Form | Surfaces |
| --------------------- | --------------- | ------------------------ | -------------------------------------------------------------------------------------------- |
| Close / dismiss | `Close` (✕) | icon-only ⭐ | `ModalShell`, `Toaster` |
| Theme → dark | `Asleep` (moon) | icon-only ⭐ | `ThemeToggle` (shown when light) |
| Theme → light | `Light` (sun) | icon-only ⭐ | `ThemeToggle` (shown when dark) |
| References a dataset | `DataTable` | icon + text | Library row marker, Linked-datasets list, header **Datasets**, editor **Extract**⁴ |
| Add / create-new | `Add` | icon + text → icon-only⁴ | Library "Create New Snippet" (collapses to "+" when the pane is narrow), Datasets "New …" |
| Delete | `TrashCan` | icon-only ⭐ (danger) | Library row delete¹ — text "Delete" in the panel² |
| Import workspace | `Upload` | icon + text | Header **Import** (a file is brought into the app) |
| Export workspace | `Download` | icon + text | Header **Export** (the workspace is written out) |
| About / information | `Information` | icon + text | Header **About** |
| Revert draft | `Reset` | icon + text → icon-only⁴ | Editor toolbar **Revert** (restore last published) |
| Live search | `Search` | icon-in-field⁵ | Library search box (leading magnifier; the input's `aria-label`/placeholder names the field) |
| Settings (gear) | `Settings` | icon-only ⭐ | Per-pane settings disclosures (Editor, Preview, Library dates) |
| Unpublished draft | (CSS dot) | status-glyph | Library row (paired with a hidden label) |
| Composition structure | (custom frame) | icon-only ⭐ | Preview toolbar — composition-wireframe disclosure (a frame holding nested view blocks) |
**Pane-toggle set** — the one **custom** sub-family (not single Carbon glyphs): a
panel frame with one of three regions filled, where the filled bar's _position_
@@ -379,7 +380,8 @@ the `Icon` registry**:
⭐ = **icon-only set** (the glyph alone names the control, via `aria-label`): the
**universal** glyphs `close` + `theme`; the conventional disclosure/affordance
glyphs `settings` (gear) and the **pane-toggle** trio (position is the meaning);
glyphs `settings` (gear) and `structure` (the composition-wireframe frame) and the
**pane-toggle** trio (position is the meaning);
and **delete** as a deliberate destructive-row exception — a dense, repeated list
action where a label would cost more than it gives. `search` is _not_ ⭐: its
magnifier is a decorative lead-in to a labelled input (footnote ⁵), not a control
@@ -221,6 +221,19 @@ below) like `PaneSplitHandle`, with the gesture in `useRowResizeDrag` (the row t
_(Consulted via `/council` → WAI-ARIA APG `windowsplitter`. This bullet is the contract;
cite it, not the APG file.)_
**Resolved — composition structure wireframe.** The preview toolbar's structure disclosure (a
read-only schematic of the spec's multi-view composition — `CompositionWireframe`, arch 08) is a
**WAI-ARIA APG `tree`** inside a disclosure popover (`usePopover`): bare nested boxes are
`tree``treeitem``group`, single-select via `aria-selected`, **one tab stop with a roving
tabindex**, arrow keys in **logical (document) order** — Up/Down between nodes, Left → parent,
Right → first child, Home/End, Enter/Space activate — not spatial, since a mixed horizontal/
vertical layout makes spatial arrows ambiguous. Selecting a box reveals + selects that view's
source range in the editor (`AppStore.requestRevealView`) but **does not steal focus**, so the
wireframe stays the active surface while the editor scrolls to follow; the editor selection is the
single source of truth. The toolbar glyph appears **only for a composed spec** — a single-view
spec hides the affordance rather than disclosing an empty tree. _(Council: APG treeview; the
cursor-scoping reachability rationale is in [arch 08](08-vega-editor-techniques.md).)_
**Resolved — pane toggle strip.** The persistent show/hide strip (spec §01A) is a **WAI-ARIA
APG `toolbar`** (`role="toolbar"`, `aria-orientation="vertical"`, an `aria-label` such as
"Workspace panes") — **not** a row of independently-tabbable buttons. Grouping into a toolbar