@vibecook/ice 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +37 -2
  2. package/dist/chunk-2GUYATUM.js +643 -0
  3. package/dist/chunk-2GUYATUM.js.map +1 -0
  4. package/dist/{chunk-5S7GSXJG.js → chunk-5QY7HK6G.js} +3145 -1306
  5. package/dist/chunk-5QY7HK6G.js.map +1 -0
  6. package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
  7. package/dist/chunk-OR5FNVUE.js.map +1 -0
  8. package/dist/{chunk-FIAXPNNY.js → chunk-PKK6GLSA.js} +143 -551
  9. package/dist/chunk-PKK6GLSA.js.map +1 -0
  10. package/dist/devtools.js +508 -372
  11. package/dist/devtools.js.map +1 -1
  12. package/dist/dom.js +3 -11
  13. package/dist/ground.js +715 -0
  14. package/dist/ground.js.map +1 -0
  15. package/dist/index.js +80 -2
  16. package/dist/kernel.js +31 -1
  17. package/dist/r3f.js +605 -62
  18. package/dist/r3f.js.map +1 -1
  19. package/dist/react.js +36 -418
  20. package/dist/react.js.map +1 -1
  21. package/dist/types/packages/core/src/catalog/camera-derived.d.ts +65 -3
  22. package/dist/types/packages/core/src/catalog/gesture.d.ts +40 -4
  23. package/dist/types/packages/core/src/catalog/graph.d.ts +7 -6
  24. package/dist/types/packages/core/src/catalog/index.d.ts +1 -0
  25. package/dist/types/packages/core/src/catalog/insert.d.ts +31 -0
  26. package/dist/types/packages/core/src/catalog/pointer.d.ts +11 -0
  27. package/dist/types/packages/core/src/catalog/scene.d.ts +37 -4
  28. package/dist/types/packages/core/src/catalog/selection-presence.d.ts +7 -0
  29. package/dist/types/packages/core/src/catalog/settings-resources.d.ts +21 -0
  30. package/dist/types/packages/core/src/doc/autosave.d.ts +49 -2
  31. package/dist/types/packages/core/src/doc/bootstrap.d.ts +29 -0
  32. package/dist/types/packages/core/src/doc/channels.d.ts +9 -0
  33. package/dist/types/packages/core/src/doc/doc-commit-sink.d.ts +2 -1
  34. package/dist/types/packages/core/src/doc/doc-kit.d.ts +16 -1
  35. package/dist/types/packages/core/src/doc/envelope.d.ts +2 -1
  36. package/dist/types/packages/core/src/doc/migrate.d.ts +8 -6
  37. package/dist/types/packages/core/src/doc/schema-migrate.d.ts +50 -0
  38. package/dist/types/packages/core/src/doc/version-gate.d.ts +3 -0
  39. package/dist/types/packages/core/src/engine/commit-sink.d.ts +24 -2
  40. package/dist/types/packages/core/src/engine/engine.d.ts +11 -0
  41. package/dist/types/packages/core/src/engine/frame-control.d.ts +71 -0
  42. package/dist/types/packages/core/src/engine/frame-info.d.ts +16 -1
  43. package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +79 -6
  44. package/dist/types/packages/core/src/guards/guarded-tx.d.ts +4 -2
  45. package/dist/types/packages/core/src/helpers/churn-guard.d.ts +40 -0
  46. package/dist/types/packages/core/src/index.d.ts +15 -7
  47. package/dist/types/packages/core/src/input/queue.d.ts +11 -0
  48. package/dist/types/packages/core/src/interaction/gesture-status.d.ts +13 -0
  49. package/dist/types/packages/core/src/nav/nested-canvas.d.ts +47 -11
  50. package/dist/types/packages/core/src/ops/arrange.d.ts +15 -0
  51. package/dist/types/packages/core/src/ops/insert.d.ts +62 -0
  52. package/dist/types/packages/core/src/ops/point-pick.d.ts +11 -7
  53. package/dist/types/packages/core/src/ops/sibling-order.d.ts +70 -0
  54. package/dist/types/packages/core/src/schema/meta.d.ts +1 -0
  55. package/dist/types/packages/core/src/settings/defaults.d.ts +45 -0
  56. package/dist/types/packages/core/src/settings/ground-config.d.ts +55 -0
  57. package/dist/types/packages/core/src/systems/chrome.d.ts +6 -2
  58. package/dist/types/packages/core/src/systems/insert-ghost.d.ts +25 -0
  59. package/dist/types/packages/core/src/systems/l1-pick.d.ts +25 -11
  60. package/dist/types/packages/core/src/systems/l2-recognize.d.ts +10 -4
  61. package/dist/types/packages/core/src/systems/l3-behave.d.ts +6 -4
  62. package/dist/types/packages/core/src/systems/l3-claim.d.ts +7 -3
  63. package/dist/types/packages/core/src/systems/l3-drop.d.ts +3 -1
  64. package/dist/types/packages/core/src/systems/l3-resize.d.ts +3 -1
  65. package/dist/types/packages/core/src/systems/l3-snap.d.ts +25 -7
  66. package/dist/types/packages/core/src/systems/nav-flight.d.ts +95 -0
  67. package/dist/types/packages/core/src/widget/define-widget.d.ts +39 -0
  68. package/dist/types/packages/core/src/widget/mount-store.d.ts +11 -3
  69. package/dist/types/packages/core/src/widget/set-props.d.ts +16 -0
  70. package/dist/types/packages/core/src/widget/spawn.d.ts +21 -1
  71. package/dist/types/packages/devtools/src/attach.d.ts +77 -0
  72. package/dist/types/packages/devtools/src/dock.d.ts +38 -0
  73. package/dist/types/packages/devtools/src/gl-panel.d.ts +69 -0
  74. package/dist/types/packages/devtools/src/index.d.ts +8 -6
  75. package/dist/types/packages/dom/src/index.d.ts +1 -3
  76. package/dist/types/packages/dom/src/loop.d.ts +11 -0
  77. package/dist/types/packages/dom/src/measure-wiring.d.ts +2 -2
  78. package/dist/types/packages/dom/src/pointer-adapter.d.ts +21 -1
  79. package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +5 -0
  80. package/dist/types/packages/ground/src/index.d.ts +52 -0
  81. package/dist/types/packages/ground/src/layer.d.ts +40 -0
  82. package/dist/types/packages/ground/src/pass.d.ts +41 -0
  83. package/dist/types/packages/ground/src/passes/grid.d.ts +21 -0
  84. package/dist/types/packages/ground/src/passes/guides-collect.d.ts +14 -0
  85. package/dist/types/packages/ground/src/passes/guides.d.ts +3 -0
  86. package/dist/types/packages/ground/src/passes/soup-collect.d.ts +35 -0
  87. package/dist/types/packages/ground/src/passes/soup-mesh.d.ts +8 -0
  88. package/dist/types/packages/ground/src/passes/wires-collect.d.ts +31 -0
  89. package/dist/types/packages/ground/src/passes/wires.d.ts +10 -0
  90. package/dist/types/packages/ground/src/renderer.d.ts +15 -0
  91. package/dist/types/packages/ice/src/ground.d.ts +1 -0
  92. package/dist/types/packages/kernel/src/bezier.d.ts +15 -0
  93. package/dist/types/packages/kernel/src/coords.d.ts +15 -0
  94. package/dist/types/packages/kernel/src/easing.d.ts +11 -0
  95. package/dist/types/packages/kernel/src/index.d.ts +3 -0
  96. package/dist/types/packages/kernel/src/layout.d.ts +118 -0
  97. package/dist/types/packages/kernel/src/nav-flight.d.ts +93 -0
  98. package/dist/types/packages/r3f/src/bridge.d.ts +73 -12
  99. package/dist/types/packages/r3f/src/composite-material.d.ts +9 -0
  100. package/dist/types/packages/r3f/src/compositor-pass.d.ts +22 -0
  101. package/dist/types/packages/r3f/src/gl-root.d.ts +76 -1
  102. package/dist/types/packages/r3f/src/gl-router.d.ts +10 -0
  103. package/dist/types/packages/r3f/src/index.d.ts +3 -2
  104. package/dist/types/packages/r3f/src/island-state.d.ts +44 -0
  105. package/dist/types/packages/r3f/src/preview-capture.d.ts +19 -0
  106. package/dist/types/packages/r3f/src/self-sustain.d.ts +39 -0
  107. package/dist/types/packages/r3f/src/use-island-frame.d.ts +16 -1
  108. package/dist/types/packages/react/src/engine-context.d.ts +21 -0
  109. package/dist/types/packages/react/src/index.d.ts +4 -2
  110. package/dist/types/packages/react/src/infinite-canvas.d.ts +40 -6
  111. package/dist/types/packages/react/src/preview-snapshots.d.ts +17 -0
  112. package/dist/types/packages/react/src/widget-preview.d.ts +14 -0
  113. package/package.json +32 -61
  114. package/dist/chunk-5S7GSXJG.js.map +0 -1
  115. package/dist/chunk-BCJ47SX7.js.map +0 -1
  116. package/dist/chunk-FIAXPNNY.js.map +0 -1
  117. package/dist/types/packages/devtools/src/panel.d.ts +0 -48
  118. package/dist/types/packages/dom/src/reflectors/grid.d.ts +0 -63
  119. package/dist/types/packages/dom/src/reflectors/wires.d.ts +0 -88
@@ -6,11 +6,13 @@
6
6
  * the tick are verified-unrestricted). One implementation so the two can
7
7
  * never disagree on ordering.
8
8
  *
9
- * Priority: HandleSpec chrome first (nearest edge), then widgets by StackZ
10
- * descending. Deliberately NOT plane-stratified: cross-plane z is stratified
11
- * visually (P2 GL composites above P1 DOM), but pick order is StackZ across
12
- * all surfaces — design-004 §1 names the mixed-overlap consequence and
13
- * accepts it; do not "fix" it here.
9
+ * Priority: HandleSpec chrome first (nearest edge), then widgets topmost by
10
+ * SIBLING ORDER (petition 8: the frame parent's ChildOf sequence, legacy
11
+ * (StackZ, entity) fallback for edge-less entities — compareStackOrder is the
12
+ * ONE comparator paint and pick share). Deliberately NOT plane-stratified:
13
+ * cross-plane z is stratified visually (P2 GL composites above P1 DOM), but
14
+ * pick order is sibling order across all surfaces — design-004 §1 names the
15
+ * mixed-overlap consequence and accepts it; do not "fix" it here.
14
16
  */
15
17
  import type { Component, Entity } from "@vibecook/strata-ecs";
16
18
  import type { Cubic, SpatialIndex } from "@ice/kernel";
@@ -33,10 +35,12 @@ export declare function distPointToBox(px: number, py: number, minX: number, min
33
35
  * Top pick under a world point within `rWorld` (point-in-box when 0).
34
36
  * Tier order (design-003 §3, amended for M8): HandleSpec chrome → PORT
35
37
  * entities (interaction affordances sit atop their widget edge) → widgets by
36
- * StackZ → WIRES (render in P0 under content; pick below widgets, above
38
+ * sibling order (topmost = compareStackOrder max; `ordinals` is the caller's
39
+ * frame ordinal map — SiblingOrderIndex — so paint and pick share one order)
40
+ * → WIRES (render in P0 under content; pick below widgets, above
37
41
  * canvas — render order and pick order agree). The canvas fallback stays the
38
42
  * caller's. Wires narrow-phase against their cached cubic (`wires` source;
39
43
  * without one, wire entries are skipped — coarse AABB hits would make fat
40
44
  * bounding boxes steal canvas taps).
41
45
  */
42
- export declare function pickTopAt(reader: PickReader, index: SpatialIndex<Entity>, wx: number, wy: number, rWorld: number, wires?: WirePickSource): Entity | undefined;
46
+ export declare function pickTopAt(reader: PickReader, index: SpatialIndex<Entity>, wx: number, wy: number, rWorld: number, wires?: WirePickSource, ordinals?: ReadonlyMap<Entity, number>): Entity | undefined;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Sibling order — THE per-frame stacking source (petition 8; design-004 §1
3
+ * amendment). Within a frame, paint/pick/drop order IS the frame parent's
4
+ * ordered `ChildOf` sibling sequence: index 0 paints first (bottom), the tail
5
+ * paints last (top). The frame parent is `currentNavFrame(world) ?? BoardRoot`
6
+ * — every renderer/pick consumer shares this module so the orders can never
7
+ * diverge (the point-pick lesson, extended to the comparator).
8
+ *
9
+ * LEGACY FALLBACK (must keep working): when the `BoardRoot` resource is absent
10
+ * (a pre-schema-2 doc opened read-only, or a doc-less runtime world) OR an
11
+ * entity carries no `ChildOf` edge (bare runtime spawns), stacking falls back
12
+ * to the legacy `(StackZ.z asc, entity asc)` comparator. This module is the
13
+ * ONLY sanctioned StackZ reader outside the schema migration.
14
+ *
15
+ * Mixed frames — some entities ordinal-mapped, some fallback — are nominally
16
+ * impossible (the 1→2 migration hangs an edge on every durable widget, and a
17
+ * pre-schema-2 read-only doc has no BoardRoot at all, so it is all-fallback).
18
+ * The comparator still totals them: ordinal-mapped entities sort BELOW the
19
+ * fallback set (ordinals first in paint order), each group internally ordered
20
+ * by its own rule — documented so a stray bare spawn lands somewhere stable
21
+ * rather than somewhere random.
22
+ *
23
+ * Invalidation is PULL-ONLY, the strata 0.8.0 contract: `world.orderStamp
24
+ * (parent, ChildOf)` is a monotonic per-parent version consumers poll after a
25
+ * reactive wake; the wake itself is an `observeQuery` whose grammar names the
26
+ * relation (`Related(ChildOf)` — pure reorders turn it over).
27
+ */
28
+ import type { Component, Entity, World } from "@vibecook/strata-ecs";
29
+ /** Structural read both `World` and `SystemCtx` provide — all the comparator needs. */
30
+ export interface StackOrderReader {
31
+ get<S>(e: Entity, c: Component<S>): S | undefined;
32
+ }
33
+ /**
34
+ * The frame parent — the entity whose `ChildOf` sibling sequence is the
35
+ * current frame's stacking order: the nav frame when inside a container, else
36
+ * the document's board root. `undefined` = a legacy world (pre-schema-2
37
+ * read-only doc / doc-less runtime) — everything is fallback-ordered.
38
+ */
39
+ export declare function frameParent(world: World): Entity | undefined;
40
+ /** `parent`'s sibling sequence as entity → position (the renderers' sort key). */
41
+ export declare function buildOrdinals(world: World, parent: Entity): Map<Entity, number>;
42
+ /**
43
+ * THE stacking comparator (ascending = paint order; the pick tier takes the
44
+ * max). Ordinal-mapped entities order by sibling position; entities missing
45
+ * from the map use the legacy `(StackZ asc, entity asc)` fallback among
46
+ * themselves and sort ABOVE the ordinal-mapped set (see the header note on
47
+ * nominally-impossible mixed frames).
48
+ */
49
+ export declare function compareStackOrder(reader: StackOrderReader, ordinals: ReadonlyMap<Entity, number>, a: Entity, b: Entity): number;
50
+ export interface SiblingOrderIndex {
51
+ /** The frame parent backing the current ordinal map (undefined = legacy world). */
52
+ parent(): Entity | undefined;
53
+ /** The stamp-checked ordinal map — rebuilt only when the frame parent or its `orderStamp` moved. */
54
+ ordinals(): ReadonlyMap<Entity, number>;
55
+ /** True when the next `ordinals()` would rebuild (cheap pull; no rebuild). */
56
+ stale(): boolean;
57
+ /**
58
+ * Arm the reorder wake: an `observeQuery` naming `ChildOf` (`Related`
59
+ * grammar — pure reorders and edge turnover both fire it). Callers pair it
60
+ * with `stale()`/`ordinals()` so other-frame churn stays a no-op.
61
+ */
62
+ observe(cb: () => void): () => void;
63
+ }
64
+ /**
65
+ * A pull-based ordinal cache over the CURRENT frame parent's sibling sequence.
66
+ * Consumers each hold their own instance (the cache is a cheap `getReverse`
67
+ * walk); correctness is shared through {@link compareStackOrder} and the
68
+ * stamp, never through instance identity.
69
+ */
70
+ export declare function createSiblingOrderIndex(world: World): SiblingOrderIndex;
@@ -31,6 +31,7 @@ export declare function defineComponent<const Sch extends RawSchema>(name: strin
31
31
  export declare function defineTag(name: string): Tag;
32
32
  export declare function defineRelation(name: string, opts?: {
33
33
  arity?: "one" | "many";
34
+ ordered?: boolean;
34
35
  }): Relation;
35
36
  export declare function defineResource<const Sch extends RawSchema>(name: string, schema: Sch, opts?: {
36
37
  durable?: boolean;
@@ -15,6 +15,8 @@ export declare const GESTURE_DEFAULTS: {
15
15
  readonly inertiaDecayMs: 325;
16
16
  readonly multiTapWindowMs: 280;
17
17
  readonly multiTapSlopPx: 20;
18
+ readonly wheelZoomSensitivity: 0.01;
19
+ readonly wheelZoomMaxStep: 10;
18
20
  };
19
21
  /** Pick radii + retarget dead-band (design-003 §3 L1 targeting). */
20
22
  export declare const POINTER_DEFAULTS: {
@@ -39,12 +41,55 @@ export declare const SNAP_DEFAULTS: {
39
41
  readonly enabled: true;
40
42
  readonly thresholdPx: 5;
41
43
  };
44
+ /**
45
+ * Selection-chrome knobs (2026-07-17). `liftScale` mirrors the app's visual
46
+ * drag-lift scale (widgetlab CardShell: 1.05) so the union box can inflate a
47
+ * grabbed member's rect and keep WRAPPING the card it sees; 1 = no lift
48
+ * visual, no inflation.
49
+ */
50
+ export declare const CHROME_DEFAULTS: {
51
+ readonly liftScale: 1;
52
+ };
42
53
  /** Zoom clamp (design-003 §5 item 9 cameraControl) and frame dt clamp (design-002 §1). */
43
54
  export declare const CAMERA_DEFAULTS: {
44
55
  readonly minZoom: 0.1;
45
56
  readonly maxZoom: 5;
46
57
  readonly dtClampMs: 64;
47
58
  };
59
+ /**
60
+ * The natural auto-framing band (2026-07-18, James: "not super zoomed in
61
+ * nor super zoomed out, should feel natural"): every DEFAULT framing —
62
+ * `ops.frameContent` at boot, the folder arrival camera, and the folder
63
+ * minis' portal affine (widgetlab keeps its preview in lockstep) — fits
64
+ * content via kernel `fitCamera` but never lands outside
65
+ * [fitMinZoom, fitMaxZoom], intersected with the session's hard
66
+ * CameraLimits. `ops.zoomToFit` stays UNCAPPED: an explicit "show me
67
+ * everything" beats natural.
68
+ */
69
+ export declare const FIT_DEFAULTS: {
70
+ readonly pad: 80;
71
+ readonly minZoom: 0.5;
72
+ readonly maxZoom: 1;
73
+ };
74
+ /**
75
+ * Nav-transition motion constants (design-006 §4/§5, tuned in the approved
76
+ * 2026-07-15 mock). `responseMs` is the critically-damped spring response for
77
+ * ENTER; exit runs at `exitResponseFactor ×` (returning is lighter). Response
78
+ * scales `durationPerOctave` per zoom-octave beyond `baseOctaves` (Apple's
79
+ * duration-with-distance). Beyond `freezeOctaves` a flight cannot be presented
80
+ * geometrically (Chromium raster-scale swing — design-006 §5): the start depth
81
+ * caps at `capFactor ×` arrival and the transition presents as a crossfade.
82
+ */
83
+ export declare const NAV_TRANSITION_DEFAULTS: {
84
+ readonly responseMs: 420;
85
+ readonly exitResponseFactor: 0.8;
86
+ readonly durationPerOctave: 0.12;
87
+ readonly baseOctaves: 3.5;
88
+ readonly freezeOctaves: 4.2;
89
+ readonly capFactor: 10;
90
+ readonly settleP: 0.999;
91
+ readonly settleV: 0.02;
92
+ };
48
93
  /** Widget/FBO/port budgets (design-004 §2 host pipeline, §3 FBO pool, §6 port materialization; ported into design-005 §4 engine facade). */
49
94
  export declare const RUNTIME_BUDGETS: {
50
95
  readonly keepMountedWidgets: 256;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Ground-layer (P0) pass configs — CANONICAL home (2026-07-16, the @ice/ground
3
+ * extraction): plain data shared by the ground renderer, the react facade's
4
+ * `grid` prop, and apps. They live in core because core is the vocabulary
5
+ * package every layer may import (ground cannot import react; react cannot
6
+ * import ground — design-002 §6). Values are verbatim from the original
7
+ * renderers: GridConfig from v1 GridRenderer.ts:5-35 (tuned to Apple
8
+ * Freeform / FigJam), wires + snap-guides from their 2026 dom reflectors.
9
+ */
10
+ export interface GridConfig {
11
+ /** World-unit spacings for up to 3 grid levels [fine, medium, coarse]. */
12
+ spacings: [number, number, number];
13
+ /** Dot RGB color as [r, g, b] in 0-1 range. */
14
+ dotColor: [number, number, number];
15
+ /** Base dot opacity multiplier (0-1). */
16
+ dotAlpha: number;
17
+ /** CSS-pixel range where a grid level fades in: [start, end]. */
18
+ fadeIn: [number, number];
19
+ /** CSS-pixel range where a grid level fades out: [start, end]. */
20
+ fadeOut: [number, number];
21
+ /** Dot radius range in CSS pixels [min, max]. Scaled by DPR internally. */
22
+ dotRadius: [number, number];
23
+ /** Per-level opacity weight: level i gets (base + i * step). */
24
+ levelWeight: [number, number];
25
+ }
26
+ /** Verbatim from v1 GridRenderer.ts:27-35 (Apple Freeform / FigJam feel). */
27
+ export declare const DEFAULT_GRID_CONFIG: GridConfig;
28
+ /** Minimal-neutral wire styling (design-004 §6: chrome is subdued; wires defer to content). */
29
+ export interface WiresConfig {
30
+ /** Default wire stroke (CSS color). */
31
+ wireColor: string;
32
+ /** Accent stroke for a Selected wire. */
33
+ selectedColor: string;
34
+ wireWidth: number;
35
+ selectedWidth: number;
36
+ /** Materialized-port dot radius (CSS px) + colors (idle vs during a connect drag). */
37
+ portRadius: number;
38
+ portColor: string;
39
+ portActiveColor: string;
40
+ }
41
+ export declare const DEFAULT_WIRES_CONFIG: WiresConfig;
42
+ /** v1 `DEFAULT_SNAP_GUIDE_CONFIG` (SnapGuideRenderer.ts), field for field. */
43
+ export interface SnapGuidesConfig {
44
+ /** Guide line + spacing bar color as [r, g, b] in 0-1 range. */
45
+ color: [number, number, number];
46
+ /** Stroke width in screen px (constant across zoom). */
47
+ lineWidth: number;
48
+ /** Alignment-line alpha. */
49
+ guideAlpha: number;
50
+ /** Equal-spacing bar alpha. */
51
+ spacingAlpha: number;
52
+ /** End-tick half-length in screen px (v1 shader barHeight). */
53
+ tickPx: number;
54
+ }
55
+ export declare const DEFAULT_SNAP_GUIDES_CONFIG: SnapGuidesConfig;
@@ -7,6 +7,10 @@
7
7
  * `Selected` set: ONE selection-box entity + 8 resize-handle entities. The box
8
8
  * entity carries ONLY the `SelectionBox` value (x,y,w,h = the union bbox); the
9
9
  * handles carry Position + Size + `HandleSpec{anchor}` + `VisualOf → box`.
10
+ * Box policy (amended 2026-07-17): all-resizable selections get box + grips
11
+ * (v1 parity); a MULTI-selection gets the box (group bbox) even when
12
+ * non-resizable; a single non-resizable widget gets no engine chrome (the
13
+ * app owns its selection look).
10
14
  *
11
15
  * HANDLE PICKING (the point of the design): handles carry a world AABB, so the
12
16
  * `react`-phase `spatialSync` indexes them automatically and `picking` resolves
@@ -43,6 +47,6 @@
43
47
  * measure loop converges (§2). The stored w,h are the effective size at the
44
48
  * tier transition (what the tier "was computed from").
45
49
  */
46
- import type { System, TickSystem, World } from "@vibecook/strata-ecs";
50
+ import type { TickSystem, World } from "@vibecook/strata-ecs";
47
51
  export declare function createSelectionChromeSystem(world: World): TickSystem;
48
- export declare function createBreakpointSystem(_world: World): System;
52
+ export declare function createBreakpointSystem(world: World): TickSystem;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The insert-ghost reap (cleanup phase; catalog/insert.ts, 2026-07-19).
3
+ *
4
+ * Three exits for a tray-insert ghost, all owned here so nothing transient
5
+ * crosses frames unaccounted (design-002 §2 cleanup law):
6
+ *
7
+ * - PROMOTED (`GhostCommitted`, set by moveBehavior at the create commit):
8
+ * despawned ONE tick after the commit — the projected twin lands at the
9
+ * NEXT sync, so waiting one tick makes the swap happen inside a single
10
+ * reflect flush (twin mounts, ghost unmounts, same frame — no blink).
11
+ * Selection rides the same beat: the doc sink parked the twin ids in its
12
+ * out-of-ECS hand-off (`drainCreatedSelections`); they are drained on the
13
+ * commit tick and applied HERE one tick later, when the projection is
14
+ * alive — the select-on-grab the ghost held transfers to the real widget.
15
+ *
16
+ * - RETIRING (`GhostRetiring`, cancel / rejected drop): despawned when the
17
+ * fly-back `TransformTween` lands (camera-sim removes it on arrival).
18
+ *
19
+ * - STRANDED (no Grab, no tween, no live recognizer still capturing it): a
20
+ * clean click on a tray tile (the drag never formed), or an integrity
21
+ * cancel that raced the claim. Despawned silently — it never left the
22
+ * press point under the tray.
23
+ */
24
+ import type { System, World } from "@vibecook/strata-ecs";
25
+ export declare function createInsertGhostReap(world: World): System;
@@ -5,13 +5,26 @@
5
5
  *
6
6
  * `spatialSync` maintains the kernel `SpatialIndex` over every Position+Size
7
7
  * entity (widgets AND HandleSpec chrome — both carry a world AABB). It is a
8
- * TICK system (strata 0.5.0) so the body runs EVERY frame — it is the writer
9
- * that bumps
10
- * `SpatialVersion`, and its own writes have no version stamp to gate on
11
- * (design-002 §4), so it must run eagerly and stays cheap via a private
12
- * last-known cache: an entity is re-`upsert`ed only when its AABB actually
13
- * changed, and a cache-sweep removes entries no longer matched (despawns, or a
14
- * lost Position/Size). Materialized ports are M8 — skipped.
8
+ * TICK system so the body runs every frame — it is the writer that bumps
9
+ * `SpatialVersion` — but as of strata 0.7.0 (petition 7 LANDED) the body is
10
+ * O(delta), not O(N): a `world.changes` collector journals exactly which
11
+ * entities were touched (Position/Size writes, Active/WidgetEquipped flips,
12
+ * spawns, destroys — same-frame, drained INSIDE the pipeline), and each is
13
+ * re-checked against `isIndexable` → upsert or remove. The 2026-07-13 walk
14
+ * machinery (last-known AABB cache, compare-and-skip, generation sweep) is
15
+ * RETIRED — the collector knows; the system no longer has to discover.
16
+ *
17
+ * The FULL WALK survives as exactly what petition 7 said it should be:
18
+ * - the SEED (a collector only journals writes made after its creation);
19
+ * - the `reset` route (world.reset / replace import / nav `clearCaches`);
20
+ * - the `coarse` fallback (some declared-raw-write system touched
21
+ * Position/Size rows the journal cannot attribute — rescan, never miss).
22
+ * Nav gating rides the walk QUERIES at archetype level: widgets match
23
+ * `[Position, Size, WidgetEquipped, Active]`, chrome matches
24
+ * `[Position, Size, Not(WidgetEquipped)]` — the union is exactly
25
+ * `Position+Size ∧ (¬WidgetEquipped ∨ Active)`, and `isIndexable` is the
26
+ * same predicate entity-wise for the delta path. Materialized ports are M8 —
27
+ * skipped.
15
28
  *
16
29
  * `picking` runs the dual pick (design-003 §3), gated by a version guard over
17
30
  * `PointerVersion ∨ SpatialVersion`. `CameraVersion` does not exist — camera
@@ -24,10 +37,11 @@
24
37
  * dead-band (hover is forgiving; hold the current target
25
38
  * while the pointer stays within its expanded bounds).
26
39
  * - `TouchesExact` = r=0 point pick (grab is precise; no dead-band).
27
- * Pick priority is plane priority: HandleSpec chrome first, then widgets by
28
- * StackZ descending, then the CanvasSurface entity as the guaranteed fallback
29
- * (design-001 §4). Relations are written change-only (design-002 §4 hygiene).
30
- * Wires/ports are M8 — skipped.
40
+ * Pick priority is plane priority: HandleSpec chrome first, then widgets
41
+ * topmost by sibling order (petition 8 — the shared SiblingOrderIndex feeds
42
+ * pickTopAt, so pick can never diverge from paint), then the CanvasSurface
43
+ * entity as the guaranteed fallback (design-001 §4). Relations are written
44
+ * change-only (design-002 §4 hygiene). Wires/ports are M8 — skipped.
31
45
  */
32
46
  import type { Entity, System, TickSystem, World } from "@vibecook/strata-ecs";
33
47
  import { SpatialIndex } from "@ice/kernel";
@@ -13,10 +13,16 @@
13
13
  * the same frame); field updates are immediate `edit().set`
14
14
  * under declared `access.write`.
15
15
  *
16
- * All timing reads FrameInfo.now (the engine clock); all thresholds read
17
- * the GestureSettings RESOURCE (live-tunable, design-005 §4) with the
18
- * GESTURE_DEFAULTS constants as the unset fallback. Gesture math is screen-space totals + zoom-at-claim — never
19
- * live PointerWorld (design-002 §2 staleness rule).
16
+ * All timing reads FrameInfo.CLOCK — the accumulated CLAMPED-dt gesture clock
17
+ * (2026-07-17, supersedes both raw FrameInfo.now and the 2026-07-12
18
+ * event-timestamp anchor): rAF `now` lags wall time by up to SECONDS under
19
+ * throttling/headless/software rendering while event.timeStamp does not, so
20
+ * any window mixing the two evaluates garbage; `clock` is monotonic, stalls
21
+ * contribute ≤ dtClampMs (the stall-protection intent), and no event
22
+ * timestamp ever enters window math. All thresholds read the GestureSettings
23
+ * RESOURCE (live-tunable, design-005 §4) with the GESTURE_DEFAULTS constants
24
+ * as the unset fallback. Gesture math is screen-space totals + zoom-at-claim
25
+ * — never live PointerWorld (design-002 §2 staleness rule).
20
26
  */
21
27
  import type { World } from "@vibecook/strata-ecs";
22
28
  import { type System } from "@vibecook/strata-ecs";
@@ -5,15 +5,17 @@
5
5
  * ctl:claim; per frame (persistent `Active`) apply ABSOLUTE live writes from
6
6
  * rider origins + gesture totals; on `JustEnded` emit ONE commit (through the
7
7
  * CommitSink seam — M5 swaps in the doc transaction); on `JustCancelled`
8
- * restore from riders — Position AND StackZ (a forgotten StackZ restore leaves
9
- * the cell diverged forever); remove riders and gesture-scoped edges on every
10
- * terminal path. Discrete actions edge-trigger on `Just*` markers only.
8
+ * restore from riders — Position AND sibling order (petition 8: a forgotten
9
+ * order restore leaves the sequence diverged forever, the old StackZ law);
10
+ * remove riders and gesture-scoped edges on every terminal path. Discrete
11
+ * actions edge-trigger on `Just*` markers only.
11
12
  *
12
13
  * Move outcome tree (design-003 §5.5, three-way via dropSystem's signals):
13
14
  * DropTarget + OverlapCandidate on the container → CONSUME (reparent intent);
14
15
  * DropTarget without OverlapCandidate → FLY-BACK (no commit; TransformTween
15
16
  * rider per widget — the tween HOLDS the claim, design-001 §3 amended);
16
- * no DropTarget → COMMIT (final Position + StackZ per live Drags edge).
17
+ * no DropTarget → COMMIT (final Position per live Drags edge + the sibling
18
+ * placement as ORDER ops, so one gesture stays one transaction).
17
19
  * Without dropSystem installed no DropTarget ever exists ⇒ plain commits.
18
20
  */
19
21
  import type { System, World } from "@vibecook/strata-ecs";
@@ -7,9 +7,13 @@
7
7
  * claim-frame `read(w, Grab)` throws (design-003 review C1).
8
8
  *
9
9
  * `moveClaim` (JustActive × RoutedMove): select-on-grab, `Drags` edges over the
10
- * dragged set, `Grab` per widget with the CURRENT Position/Size/StackZ embedded
11
- * in the command payload, and the StackZ elevate (an immediate value write —
12
- * part of the gesture divergence, restored from `Grab.z` on cancel).
10
+ * dragged set, `Grab` per widget with the CURRENT Position/Size + the ORDER
11
+ * MEMO embedded (petition 8: ChildOf parent, nearest non-dragged predecessor
12
+ * sibling, original sibling index), and the sibling elevate — `moveRelation(w,
13
+ * ChildOf, "last")`, frame-scoped, equivalent to the old global-top within the
14
+ * frame; part of the gesture divergence, restored from the memo on cancel.
15
+ * Edge-less widgets (legacy/runtime world) skip both memo and elevate — the
16
+ * move would be a strata no-op and there is no sequence to restore.
13
17
  *
14
18
  * `resizeClaim` (JustActive × RoutedResize): `Drags` + `Grab` over the Selected
15
19
  * ∧ Resizable set; the anchor lives on the captured handle's `HandleSpec` — no
@@ -3,7 +3,9 @@
3
3
  * AFTER moveBehavior so it tests POST-move bounds).
4
4
  *
5
5
  * For Active `RoutedMove` drags: spatial-query the dragged union bounds for
6
- * `Container` widgets (∉dragged), pick the topmost by StackZ, and publish two
6
+ * `Container` widgets (∉dragged), pick the topmost by sibling order (petition
7
+ * 8 — compareStackOrder, the SAME comparator pick/paint use, so the drop
8
+ * candidate can never diverge from what the user sees on top), and publish two
7
9
  * signals the move-outcome tree consumes (l3-behave §5.5):
8
10
  * - `DropTarget(recognizer → container)` — set for the container under the
9
11
  * bounds REGARDLESS of an accepts-match (so the outcome tree can distinguish
@@ -7,7 +7,9 @@
7
7
  * zoomAtClaim`, resized about the FIXED opposite edge/corner of the grabbed
8
8
  * handle (`n`/`s` height-only, `e`/`w` width-only, corners both). Modifiers ride
9
9
  * the watched pointer's `PointerMods`: `alt` = resize about center, `shift` =
10
- * preserve aspect (corner handles). Min size clamps to 1×1. `JustEnded` emits ONE
10
+ * preserve aspect (corner handles). Min size clamps to the target widget's
11
+ * `defineWidget` `minSize` (via `PrefabId`), or 1×1 for non-widget resizables.
12
+ * `JustEnded` emits ONE
11
13
  * `resize` commit (Position+Size per live target); `JustCancelled`/`JustFailed`
12
14
  * restore from `Grab`. Every terminal path removes `Grab` and the `Drags` edges
13
15
  * (mirrors l3-behave's clearGestureState; resize has no DropTarget).
@@ -6,14 +6,32 @@
6
6
  * over the dragged widgets — NEVER last-applied `Position` (one frame stale ⇒
7
7
  * guide-chasing oscillation, design-003 §5.2 red-team; `Grab` is readable here
8
8
  * thanks to the ctl:claim settle point, §4.5). Candidates come from the SHARED
9
- * spatial index (rect query = intended bounds expanded by threshold, filtered to
10
- * `SnapTarget ∧ ∉dragged`; Visible filtering waits for cull). Threshold is
9
+ * spatial index (rect query = intended bounds ∪ VIEWPORT, expanded by
10
+ * threshold — design-003 §5.2: a distant same-axis edge IN VIEW must
11
+ * contribute a guide, the Figma behavior; the widgetlab field check 2026-07-16
12
+ * caught the intended-rect-only query never seeing a card 115px above the
13
+ * drag). Filtered to `SnapTarget ∧ ¬Culled ∧ ∉dragged` — ¬Culled is the
14
+ * design's "Visible" scoping in cull-less rigs too (no cull system ⇒ nothing
15
+ * is Culled ⇒ all candidates pass). Threshold is
11
16
  * `thresholdPx / zoomAtClaim` (screen-constant). The kernel `computeSnapGuides`
12
17
  * does the Figma math; we write `SnapState{dx,dy}` on the recognizer (moveBehavior
13
- * adds it to the absolute write). Guide CHROME entities (`GuideLine`) are deferred
14
- * to the chrome-reflector milestone — SnapState only here. SnapState clears to 0,0
15
- * when snap is off / no source / no candidate.
18
+ * adds it to the absolute write). SnapState clears to 0,0 when snap is off / no
19
+ * source / no candidate.
20
+ *
21
+ * GUIDE CHROME (design-003 §6, as-built 2026-07-16 — the once-deferred half):
22
+ * the kernel result's `guides`/`spacings` become pooled runtime `GuideLine` /
23
+ * `SpacingBar` entities the P0 snap-guides reflector draws (design-004 §1
24
+ * amendment: ground stratum, under content — James's call). Pool mechanics
25
+ * mirror selectionChrome: a TICK system (exactly one pass per frame — spawn/
26
+ * destroy are not idempotent, and the pool must reap on the frame the last
27
+ * snapping drag ENDS, when the recognizer query is empty), `ctx.spawn` on
28
+ * grow, `ctx.destroy` on shrink, change-only `edit().set` in steady state
29
+ * (same guide held across frames ⇒ zero writes). Alignment guides write
30
+ * `from = to = 0` — the full-viewport sentinel (v1's full-canvas lines);
31
+ * spacing indicators flatten one entity per segment (v1 `u_spacings` parity).
32
+ * No silent caps: the pool grows to whatever the kernel returns (v1's
33
+ * MAX_GUIDES=16 truncation is banned).
16
34
  */
17
- import type { Entity, System, World } from "@vibecook/strata-ecs";
35
+ import type { Entity, TickSystem, World } from "@vibecook/strata-ecs";
18
36
  import { type SpatialIndex } from "@ice/kernel";
19
- export declare function createSnapSystem(world: World, index: SpatialIndex<Entity>): System;
37
+ export declare function createSnapSystem(world: World, index: SpatialIndex<Entity>): TickSystem;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Nav-flight — the T1 camera-flight half of design-006 (portal zoom).
3
+ *
4
+ * CUT-FIRST, THEN FLY (§3): the nav ops perform the ECS cut synchronously
5
+ * (frame switch, membership resweep, index rebuild) and then hand presentation
6
+ * to this slice — `startNavFlight` snaps the camera to the continuity-solved
7
+ * start `c0` and arms the `NavTransition` resource; the `navFlight` tick
8
+ * system (simulate, beside cameraInertia) advances a closed-form critically-
9
+ * damped progress spring each frame and writes the camera along the log-zoom
10
+ * path until rest. The transition is PURE PRESENTATION: killing it at any
11
+ * moment leaves a fully consistent world in the destination frame.
12
+ *
13
+ * - `NavTransition` is a RESOURCE, not closure state (the CameraInertia
14
+ * pattern: devtools sees it, no hidden singleton — design-003 §9). Resources
15
+ * cannot be removed, so `active` is the liveness flag; `epoch` bumps on
16
+ * every (re)start so the T2 reflector can distinguish retarget from resume.
17
+ * - Camera writes are UNCLAMPED (design-006 §3.1): flight paths transit far
18
+ * outside the user-gesture zoom band; only the ARRIVAL camera (clamped by
19
+ * the nav op) bounds where the flight rests.
20
+ * - Gestures always win (§4): any active camera gesture — `Camera.gesturing`,
21
+ * stamped by cameraControl in ctl:behave, i.e. EARLIER this same frame —
22
+ * deactivates the flight without touching the camera.
23
+ * - The portal affine (`as/aox/aoy`, departed-frame → destination-frame) and
24
+ * `frozen` ride the resource for T2's departing-plane reflector; T1's camera
25
+ * math only needs c0/c1.
26
+ */
27
+ import type { TickSystem, World } from "@vibecook/strata-ecs";
28
+ import type { CameraState, PortalAffine } from "@ice/kernel";
29
+ /** The one in-flight nav transition (design-006 §3.1). Inactive = at rest. */
30
+ export declare const NavTransition: import("@vibecook/strata-ecs").Resource<import("@vibecook/strata-ecs").ValueOf<{
31
+ readonly active: import("@vibecook/strata-ecs").FieldSpec<"bool", true>;
32
+ readonly kind: import("@vibecook/strata-ecs").FieldSpec<import("@vibecook/strata-ecs").EnumType<"enter" | "exit">, true>;
33
+ /** Progress spring state (closed-form critically damped, kernel springStep). */
34
+ readonly p: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
35
+ readonly v: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
36
+ /** Depth-capped flight — T2 presents it as a crossfade, not geometry (§5). */
37
+ readonly frozen: import("@vibecook/strata-ecs").FieldSpec<"bool", true>;
38
+ /** Bumps on every flight start; T2 distinguishes retarget from resume. */
39
+ readonly epoch: import("@vibecook/strata-ecs").FieldSpec<"u32", true>;
40
+ /** Response multiplier from octave distance (locked at start; §4). */
41
+ readonly durMul: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
42
+ readonly c0x: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
43
+ readonly c0y: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
44
+ readonly c0z: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
45
+ readonly c1x: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
46
+ readonly c1y: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
47
+ readonly c1z: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
48
+ /** Portal affine A: departed-frame coords → destination-frame coords (T2). */
49
+ readonly as: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
50
+ readonly aox: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
51
+ readonly aoy: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
52
+ }>, {
53
+ readonly active: import("@vibecook/strata-ecs").FieldSpec<"bool", true>;
54
+ readonly kind: import("@vibecook/strata-ecs").FieldSpec<import("@vibecook/strata-ecs").EnumType<"enter" | "exit">, true>;
55
+ /** Progress spring state (closed-form critically damped, kernel springStep). */
56
+ readonly p: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
57
+ readonly v: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
58
+ /** Depth-capped flight — T2 presents it as a crossfade, not geometry (§5). */
59
+ readonly frozen: import("@vibecook/strata-ecs").FieldSpec<"bool", true>;
60
+ /** Bumps on every flight start; T2 distinguishes retarget from resume. */
61
+ readonly epoch: import("@vibecook/strata-ecs").FieldSpec<"u32", true>;
62
+ /** Response multiplier from octave distance (locked at start; §4). */
63
+ readonly durMul: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
64
+ readonly c0x: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
65
+ readonly c0y: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
66
+ readonly c0z: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
67
+ readonly c1x: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
68
+ readonly c1y: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
69
+ readonly c1z: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
70
+ /** Portal affine A: departed-frame coords → destination-frame coords (T2). */
71
+ readonly as: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
72
+ readonly aox: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
73
+ readonly aoy: import("@vibecook/strata-ecs").FieldSpec<"f64", true>;
74
+ }>;
75
+ /**
76
+ * Arm a flight from the already-cut world: continuity-solve was done by the
77
+ * caller (`exact` = c0 in destination-frame coords), the arrival `c1` is
78
+ * already clamped. Applies the depth cap (§5), snaps the camera to c0, and
79
+ * activates the resource. Callers guarantee a live Viewport — headless/no-
80
+ * viewport paths snap to the arrival instead and never reach here.
81
+ */
82
+ export declare function startNavFlight(world: World, kind: "enter" | "exit", A: PortalAffine, exact: CameraState, c1: CameraState): void;
83
+ /** Deactivate without touching the camera (integrity pops, gesture yields). */
84
+ export declare function abortNavFlight(world: World): void;
85
+ /**
86
+ * Is a nav flight driving the camera right now? THE gate for flight-time
87
+ * GPU/paint duty (design-006 §8.2, answered by field testing 2026-07-16):
88
+ * heavy-motion consumers treat `Camera.gesturing ∨ navFlightActive` as one
89
+ * "camera in transient motion" signal — composite DPR drop, band-repaint
90
+ * suppression, Hot-island freeze, breakpoint retier deferral. `gesturing`
91
+ * itself stays a pure user-gesture fact (the inertia precedent): flights
92
+ * never stamp it, consumers OR the two.
93
+ */
94
+ export declare function navFlightActive(world: World): boolean;
95
+ export declare function createNavFlight(world: World): TickSystem;
@@ -20,6 +20,12 @@ export interface WidgetInteraction {
20
20
  readonly solid?: boolean;
21
21
  /** "longPress": hold-to-lift dragging (iOS home-screen model); default "press". */
22
22
  readonly dragOn?: "press" | "longPress";
23
+ /**
24
+ * UE-Blueprint comment-box drag (2026-07-18): a move claim on this widget
25
+ * also claims every widget FULLY INSIDE its bounds at claim time (spatial
26
+ * membership — never reparenting). Default false.
27
+ */
28
+ readonly sweepContained?: boolean;
23
29
  }
24
30
  export interface WidgetDef {
25
31
  readonly type: string;
@@ -34,6 +40,16 @@ export interface WidgetDef {
34
40
  readonly surface: WidgetSurface;
35
41
  /** Framework component (opaque to core — the react package narrows it). */
36
42
  readonly component: unknown;
43
+ /**
44
+ * GL only: a DOM chrome component portaled into the widget's CONTENT-plane
45
+ * host (P1), which stacks UNDER the GL canvas (P2) — v1's proven
46
+ * CardChrome-beneath-the-canvas sandwich. The island renders ONLY the 3D
47
+ * content; the card body (gradient, radius, ring, shadow, lift spring,
48
+ * hover/overlap glow) is DOM and shares the app's CSS with DOM widgets.
49
+ * Opaque to core — the react package narrows it. Ignored for dom-surface
50
+ * widgets (their component IS the chrome).
51
+ */
52
+ readonly chrome?: unknown;
37
53
  readonly sizeMode?: SizeMode;
38
54
  readonly defaultSize?: {
39
55
  readonly w: number;
@@ -57,6 +73,23 @@ export interface WidgetDef {
57
73
  * the compatibility key list the connect gesture checks.
58
74
  */
59
75
  readonly ports?: readonly WidgetPortDecl[];
76
+ /**
77
+ * Palette/tray preview (design-005 §2 amendment, 2026-07-19). ENGINE-FREE
78
+ * BY CONTRACT: a preview renders with no world, no entity, no ops — a pure
79
+ * picture the host mounts inert (that is what makes it portable: trays,
80
+ * docs sites, a widget store). Authored at `defaultSize`; hosts scale.
81
+ * Three tiers, least effort first:
82
+ * - absent → the REAL component mounts with default props in
83
+ * the framework's preview sandbox (dom surfaces;
84
+ * truthful by construction);
85
+ * - `{ props: {…} }` → same sandbox mount with curated props;
86
+ * - a component, or `{ component }` → author-owned preview (a static
87
+ * mockup, a live self-ticking clock — the author's call). In P1 all
88
+ * declared previews are DOM components — the sanctioned escape hatch
89
+ * for GL widgets too (P2 adds the r3f snapshot pipeline).
90
+ * Opaque to core — the react package narrows it (`component` precedent).
91
+ */
92
+ readonly preview?: unknown;
60
93
  readonly container?: {
61
94
  readonly accepts: readonly string[];
62
95
  readonly provides?: readonly string[];
@@ -96,6 +129,8 @@ export interface WidgetType {
96
129
  readonly propToGroup: Readonly<Record<string, string>>;
97
130
  readonly surface: WidgetSurface;
98
131
  readonly component: unknown;
132
+ /** GL widgets: DOM chrome under the canvas (see WidgetDef.chrome). */
133
+ readonly chrome: unknown;
99
134
  readonly sizeMode: SizeMode;
100
135
  readonly defaultSize: {
101
136
  readonly w: number;
@@ -112,6 +147,10 @@ export interface WidgetType {
112
147
  /** Runtime capability tags the equip system stamps at projection. */
113
148
  readonly capabilityTags: readonly Tag[];
114
149
  readonly migrate: Readonly<Record<number, (prev: Record<string, unknown>) => Record<string, unknown>>>;
150
+ /** Normalized author preview component (opaque; react narrows). null = none declared. */
151
+ readonly previewComponent: unknown;
152
+ /** Prop overrides for the default real-component preview mount (validated names). */
153
+ readonly previewProps?: Readonly<Record<string, unknown>>;
115
154
  }
116
155
  /** Stamped by the equip system once a projected widget carries its capability tags. */
117
156
  export declare const WidgetEquipped: Tag;