mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Editor: interactive composition wireframe — drag to reorder and restructure
Drag a view's box (or Alt+up/down) to reorder it within its container; drag onto another view's edge to pair the two in a new row/column, move across containers, or insert. Each leaf shows a glyph of its mark type. Core: spec-restructure.wrapViews (wrap, with flatten-to-insert, collapse, and data-pin invariants) and spec-insert.moveViewTo; the editor applies every drag as one undoable edit via AppStore.composeRequest, keeping the editor the single text source.
This commit is contained in:
@@ -297,13 +297,24 @@ thin app-layer services.
|
||||
- `spec-inline-data` — the rows a specific `data` binding carries for profiling
|
||||
(`rowsForDataBinding`: inline `values`, or a self-defined `datasets` entry).
|
||||
- `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.
|
||||
at an index (`insertView`) and reordering siblings (`moveView` swaps a neighbour, `moveViewTo`
|
||||
slides to any index), plus `elementOffset` to re-find a view after the edit. It also owns the
|
||||
shared `SpecPath` walkers (`valueAtPath`/`arrayAtPath`/`isPrefixPath`) — a module navigating a
|
||||
path reuses these rather than re-inlining the array/object descent.
|
||||
- `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).
|
||||
drive the composition wireframe — a 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`, and on the draft it is **drag-editable** (reorder + restructure).
|
||||
Interaction contract in [arch 10](10-interaction-and-feedback.md).
|
||||
- `spec-restructure` — the path-targeted cross-container moves behind the wireframe's drag.
|
||||
`wrapViews(target, source, axis, side)` pairs the dragged source beside the drop target in a new
|
||||
concat (placed where the target was, the source removed), enforcing three invariants:
|
||||
**flatten** a bare same-orientation concat nested directly in a concat (so a with-axis drop reads
|
||||
as a plain _insert_, not redundant nesting), **collapse** the source's emptied container (unwrap a
|
||||
one-child, drop a zero-child, recursing up the chain), and **data-pin** a source's inherited
|
||||
`data` before it changes ancestor (`dataBindingAtPath`, so it never silently rebinds). Degenerate
|
||||
drops — onto itself, its own ancestor/descendant, or the root — return null.
|
||||
|
||||
**Services (app, store-aware via `getState`):** `spec-transform-actions` (the
|
||||
wrap/simplify/add-view operations and their surfaces), `spec-dataset-hints` (completion,
|
||||
@@ -325,6 +336,13 @@ Decision rules:
|
||||
- **One edit path.** The lightbulb returns a `WorkspaceEdit` (no editor handle); the toolbar
|
||||
and palette use `executeEdits`. Both build the replacement through the same
|
||||
serialize-and-reindent step, bracketed by `pushUndoStop`, so ⌘Z restores the prior text.
|
||||
- **Wireframe restructuring is one core op + the editor's undo.** Every drag resolves to either
|
||||
`moveViewTo` (reorder within one container) or `wrapViews` (everything else — wrap, cross-container
|
||||
move, insert); the wireframe only _requests_ it (`AppStore.requestComposeMove`/`requestComposeWrap`)
|
||||
and `SpecEditor` applies it through the same `executeEdits` + `pushUndoStop` path as the other
|
||||
transforms, so a drag is one ⌘Z and the editor stays the single text source. `wrapViews` covers
|
||||
wrap and insert with one operation because it flattens bare same-orientation nesting afterward; the
|
||||
drag's edge-zone interaction model lives in [arch 10](10-interaction-and-feedback.md).
|
||||
- **Composition CodeLens is cursor-scoped.** It follows the view the cursor sits in —
|
||||
`+ Add view above/below` at the view's edges and `↑/↓ Move` to reorder among its siblings —
|
||||
rather than one fixed button per composition array; an empty composition shows a single `+ Add view`,
|
||||
|
||||
@@ -371,6 +371,14 @@ notifications/validation as they arrive):
|
||||
| Success | `CheckmarkFilled` | `--support-success` | `Toaster` (success) |
|
||||
| Info | `InformationFilled` | `--support-info` | `Toaster` (info) |
|
||||
|
||||
**Mark set** — a custom `mark-*` sub-family, one simplified glyph per Vega-Lite mark
|
||||
(bar, line, area, point, arc, rect, tick, rule, text, plus a `mark-generic` fallback),
|
||||
drawn on the same 32-grid (stroked where a line reads truer than a fill). It labels the
|
||||
leaves of the composition wireframe so views read apart at a glance; mark synonyms
|
||||
(circle/square → point, trail → line, image → rect) collapse onto it via `markIconName`
|
||||
(`mark-icon.ts`), and an unknown or absent mark falls to `mark-generic`. Decorative there
|
||||
(the box's `aria-label` names the view), so these are not in the ⭐ icon-only set.
|
||||
|
||||
**Scoped set** — single-surface, glyph **reserved** in the ledger but **not yet in
|
||||
the `Icon` registry**:
|
||||
|
||||
|
||||
@@ -222,17 +222,38 @@ _(Consulted via `/council` → WAI-ARIA APG `windowsplitter`. This bullet is the
|
||||
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
|
||||
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).)_
|
||||
vertical layout makes spatial arrows ambiguous. Each leaf carries a glyph of its mark type (the
|
||||
`mark-*` icon sub-family, arch 09 §5) so views read apart at a glance. 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).)_
|
||||
|
||||
On the **editable draft** the tree restructures the composition. Every restructure is **applied by
|
||||
the editor** (which owns the one-⌘Z edit) via `AppStore.requestComposeMove`/`requestComposeWrap`,
|
||||
never by writing the draft text directly — so the wireframe and editor share one undo history.
|
||||
|
||||
- **Reorder within a container — APG rearrangeable-listbox.** `Alt+↑`/`Alt+↓` moves the focused
|
||||
view among its siblings: a direct modifier+arrow move, **not** a grab/drop mode. Focus follows
|
||||
the moved box for consecutive moves (so a screen reader re-announces its new position), a
|
||||
**polite** live region states the result, and `aria-keyshortcuts` advertises the keys. _(Council:
|
||||
APG listbox-rearrangeable.)_
|
||||
- **Restructure by drag — edge-zone (dock) model.** The nearest edge of the box under the pointer
|
||||
picks the **axis** (left/right → a row `hconcat`, top/bottom → a column `vconcat`) and **side**.
|
||||
A drop **along** a sibling's own container reorders within it; a drop **across** it — or onto an
|
||||
opaque `layer`/`facet`/`repeat` box — pairs the two in a new concat. The hit-test descends only
|
||||
through `hconcat`/`vconcat` and treats `layer`/`facet`/`repeat`/grid as **opaque** targets (their
|
||||
children overlap or are data-generated, so the unit is the target, never inside it). A 3px accent
|
||||
line marks the landing edge; a `wrap` drop also rings + tints the partner box. The drag is a
|
||||
pointer accelerator over capabilities that stay keyboard-reachable (in-container reorder via
|
||||
`Alt+↑/↓`; cross-container wrap via the editor's wrap actions), so it adds **no keyboard-only
|
||||
gap**. Transform invariants 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
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
> because its risk (compiled-name↔source-path correlation, and an edit-vs-interact pointer
|
||||
> conflict) is real and isolated. First build step: a **read-only Phase A spike** —
|
||||
> `viewTree(spec)` + a static nested-box renderer with click-to-cursor sync.
|
||||
>
|
||||
> **Shipped (2026-06-29):** Phases A–C — the wireframe, mark-type leaf glyphs, in-container
|
||||
> reorder (drag + `Alt+↑/↓`), and cross-container drag-to-restructure (`core/spec-restructure`
|
||||
> `wrapViews`). The live contract is now arch 08 (transforms) + arch 10 (interaction). Phase D
|
||||
> (on-chart overlay) and Phase E (size editing) remain deferred — size deferred by choice.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user