@vibecook/ice 0.1.0 → 0.2.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 (115) hide show
  1. package/README.md +37 -2
  2. package/dist/chunk-5SJYHQI4.js +636 -0
  3. package/dist/chunk-5SJYHQI4.js.map +1 -0
  4. package/dist/{chunk-5S7GSXJG.js → chunk-DXU3RZZS.js} +2913 -1180
  5. package/dist/chunk-DXU3RZZS.js.map +1 -0
  6. package/dist/{chunk-FIAXPNNY.js → chunk-IASTQMU5.js} +130 -551
  7. package/dist/chunk-IASTQMU5.js.map +1 -0
  8. package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
  9. package/dist/chunk-OR5FNVUE.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 +70 -2
  16. package/dist/kernel.js +31 -1
  17. package/dist/r3f.js +599 -62
  18. package/dist/r3f.js.map +1 -1
  19. package/dist/react.js +34 -418
  20. package/dist/react.js.map +1 -1
  21. package/dist/types/packages/core/src/catalog/camera-derived.d.ts +41 -3
  22. package/dist/types/packages/core/src/catalog/gesture.d.ts +39 -3
  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 +15 -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/frame-info.d.ts +16 -1
  41. package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +70 -6
  42. package/dist/types/packages/core/src/guards/guarded-tx.d.ts +4 -2
  43. package/dist/types/packages/core/src/helpers/churn-guard.d.ts +40 -0
  44. package/dist/types/packages/core/src/index.d.ts +13 -7
  45. package/dist/types/packages/core/src/input/queue.d.ts +11 -0
  46. package/dist/types/packages/core/src/nav/nested-canvas.d.ts +47 -11
  47. package/dist/types/packages/core/src/ops/arrange.d.ts +15 -0
  48. package/dist/types/packages/core/src/ops/insert.d.ts +62 -0
  49. package/dist/types/packages/core/src/ops/point-pick.d.ts +11 -7
  50. package/dist/types/packages/core/src/ops/sibling-order.d.ts +70 -0
  51. package/dist/types/packages/core/src/schema/meta.d.ts +1 -0
  52. package/dist/types/packages/core/src/settings/defaults.d.ts +45 -0
  53. package/dist/types/packages/core/src/settings/ground-config.d.ts +55 -0
  54. package/dist/types/packages/core/src/systems/chrome.d.ts +6 -2
  55. package/dist/types/packages/core/src/systems/insert-ghost.d.ts +25 -0
  56. package/dist/types/packages/core/src/systems/l1-pick.d.ts +25 -11
  57. package/dist/types/packages/core/src/systems/l2-recognize.d.ts +10 -4
  58. package/dist/types/packages/core/src/systems/l3-behave.d.ts +6 -4
  59. package/dist/types/packages/core/src/systems/l3-claim.d.ts +7 -3
  60. package/dist/types/packages/core/src/systems/l3-drop.d.ts +3 -1
  61. package/dist/types/packages/core/src/systems/l3-resize.d.ts +3 -1
  62. package/dist/types/packages/core/src/systems/l3-snap.d.ts +25 -7
  63. package/dist/types/packages/core/src/systems/nav-flight.d.ts +95 -0
  64. package/dist/types/packages/core/src/widget/define-widget.d.ts +39 -0
  65. package/dist/types/packages/core/src/widget/mount-store.d.ts +11 -3
  66. package/dist/types/packages/core/src/widget/set-props.d.ts +16 -0
  67. package/dist/types/packages/core/src/widget/spawn.d.ts +21 -1
  68. package/dist/types/packages/devtools/src/attach.d.ts +77 -0
  69. package/dist/types/packages/devtools/src/dock.d.ts +38 -0
  70. package/dist/types/packages/devtools/src/gl-panel.d.ts +69 -0
  71. package/dist/types/packages/devtools/src/index.d.ts +8 -6
  72. package/dist/types/packages/dom/src/index.d.ts +1 -3
  73. package/dist/types/packages/dom/src/measure-wiring.d.ts +2 -2
  74. package/dist/types/packages/dom/src/pointer-adapter.d.ts +21 -1
  75. package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +5 -0
  76. package/dist/types/packages/ground/src/index.d.ts +52 -0
  77. package/dist/types/packages/ground/src/layer.d.ts +40 -0
  78. package/dist/types/packages/ground/src/pass.d.ts +41 -0
  79. package/dist/types/packages/ground/src/passes/grid.d.ts +21 -0
  80. package/dist/types/packages/ground/src/passes/guides-collect.d.ts +14 -0
  81. package/dist/types/packages/ground/src/passes/guides.d.ts +3 -0
  82. package/dist/types/packages/ground/src/passes/soup-collect.d.ts +35 -0
  83. package/dist/types/packages/ground/src/passes/soup-mesh.d.ts +8 -0
  84. package/dist/types/packages/ground/src/passes/wires-collect.d.ts +31 -0
  85. package/dist/types/packages/ground/src/passes/wires.d.ts +10 -0
  86. package/dist/types/packages/ground/src/renderer.d.ts +15 -0
  87. package/dist/types/packages/ice/src/ground.d.ts +1 -0
  88. package/dist/types/packages/kernel/src/bezier.d.ts +15 -0
  89. package/dist/types/packages/kernel/src/coords.d.ts +15 -0
  90. package/dist/types/packages/kernel/src/easing.d.ts +11 -0
  91. package/dist/types/packages/kernel/src/index.d.ts +3 -0
  92. package/dist/types/packages/kernel/src/layout.d.ts +118 -0
  93. package/dist/types/packages/kernel/src/nav-flight.d.ts +93 -0
  94. package/dist/types/packages/r3f/src/bridge.d.ts +73 -12
  95. package/dist/types/packages/r3f/src/composite-material.d.ts +9 -0
  96. package/dist/types/packages/r3f/src/compositor-pass.d.ts +22 -0
  97. package/dist/types/packages/r3f/src/gl-root.d.ts +76 -1
  98. package/dist/types/packages/r3f/src/gl-router.d.ts +10 -0
  99. package/dist/types/packages/r3f/src/index.d.ts +3 -2
  100. package/dist/types/packages/r3f/src/island-state.d.ts +44 -0
  101. package/dist/types/packages/r3f/src/preview-capture.d.ts +19 -0
  102. package/dist/types/packages/r3f/src/self-sustain.d.ts +39 -0
  103. package/dist/types/packages/r3f/src/use-island-frame.d.ts +16 -1
  104. package/dist/types/packages/react/src/engine-context.d.ts +8 -0
  105. package/dist/types/packages/react/src/index.d.ts +4 -2
  106. package/dist/types/packages/react/src/infinite-canvas.d.ts +40 -6
  107. package/dist/types/packages/react/src/preview-snapshots.d.ts +17 -0
  108. package/dist/types/packages/react/src/widget-preview.d.ts +14 -0
  109. package/package.json +13 -8
  110. package/dist/chunk-5S7GSXJG.js.map +0 -1
  111. package/dist/chunk-BCJ47SX7.js.map +0 -1
  112. package/dist/chunk-FIAXPNNY.js.map +0 -1
  113. package/dist/types/packages/devtools/src/panel.d.ts +0 -48
  114. package/dist/types/packages/dom/src/reflectors/grid.d.ts +0 -63
  115. package/dist/types/packages/dom/src/reflectors/wires.d.ts +0 -88
@@ -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;
@@ -4,19 +4,27 @@
4
4
  *
5
5
  * - `cullSystem` (derive): viewport test over equipped widgets' EFFECTIVE size
6
6
  * (MeasuredSize where present, else Size), overscan scaled by zoom; flips
7
- * Visible/Culled CHANGE-ONLY.
7
+ * Visible/Culled CHANGE-ONLY. GATED (2026-07-15, the activeMembership
8
+ * playbook): a real `runIf` — camera/viewport window compare as the `extra`
9
+ * trigger (full pass; the window moved under everyone) ∨ a petition-7
10
+ * collector on Position/Size/MeasuredSize + Active flips (delta pass —
11
+ * drags/spawns/measures re-test only the journaled entities). Idle frames
12
+ * skip; camera frames stay O(active) by the widgetQ Active scope.
8
13
  * - `mountSystem` (derive, after cull): maintains the mount list — every
9
14
  * Visible widget is mounted; Culled widgets stay mounted-but-hidden
10
15
  * (`display:none` at the host; React state preserved) within a count LRU
11
16
  * budget (default RUNTIME_BUDGETS.keepMountedWidgets); beyond it the
12
17
  * least-recently-visible unmount for real. Decisions are ENGINE-side; the
13
18
  * dom host reflector and the React portal hook both consume the snapshot.
19
+ * GATED the same way on Visible/Culled flips (+ destroys of tagged
20
+ * widgets); LRU recency is stamped at the flip (identical eviction order —
21
+ * "last tick seen visible" = the tick it stopped being visible).
14
22
  * - The store implements the `useSyncExternalStore` contract: `subscribe` /
15
23
  * `getSnapshot` with snapshot identity changing IFF membership or a hidden
16
24
  * flag changed. Listener notification is deferred to post-notify (the
17
25
  * engine flush reflector) — never from inside the tick.
18
26
  */
19
- import { type Entity, type System, type TickSystem, type World } from "@vibecook/strata-ecs";
27
+ import { type Entity, type TickSystem, type World } from "@vibecook/strata-ecs";
20
28
  import type { Engine } from "../engine/engine";
21
29
  import type { MeasureQueue } from "../input/measure-queue";
22
30
  export interface MountEntry {
@@ -30,7 +38,7 @@ export interface WidgetMountStore {
30
38
  }
31
39
  export interface WidgetRuntime {
32
40
  readonly store: WidgetMountStore;
33
- readonly cullSystem: System;
41
+ readonly cullSystem: TickSystem;
34
42
  readonly mountSystem: TickSystem;
35
43
  /** Post-notify listener flush — install registers it as a reflector. */
36
44
  flush(): void;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * setWidgetProps — the paved-road widget prop UPDATE op (2026-07-13 review:
3
+ * spawn validated through each prop's Standard Schema, but the update path —
4
+ * raw `useCommit` writes — bypassed bounds/enum/json-shape checks entirely).
5
+ *
6
+ * Mirrors `widgetSpawnInits` semantics exactly: unknown prop names throw,
7
+ * each given value runs `spec["~standard"].validate` (an invalid value throws
8
+ * BEFORE anything is written), `p.json` values are validated as parsed shapes
9
+ * then serialized into their string cell. Writes are whole-group absolute
10
+ * values (the current cell merged with the validated updates — design-001
11
+ * §5.2 conflict-group granularity), one guardedTransaction = one undo step.
12
+ */
13
+ import type { Entity, World } from "@vibecook/strata-ecs";
14
+ import type { DurableStore } from "@vibecook/strata-ecs/durable";
15
+ import { type GuardedTxOpts } from "../guards/guarded-tx";
16
+ export declare function setWidgetProps(store: DurableStore, world: World, entity: Entity, props: Readonly<Record<string, unknown>>, opts?: GuardedTxOpts): void;
@@ -6,7 +6,7 @@
6
6
  * design-001 §5.2's conflict-group granularity). Validation runs through each
7
7
  * prop's Standard Schema before the write.
8
8
  */
9
- import type { Entity, World } from "@vibecook/strata-ecs";
9
+ import type { Entity, OrderPlace, Relation, World } from "@vibecook/strata-ecs";
10
10
  import type { DurableStore } from "@vibecook/strata-ecs/durable";
11
11
  import { type ComponentInit } from "../schema/prefab";
12
12
  export interface SpawnWidgetOpts {
@@ -14,10 +14,30 @@ export interface SpawnWidgetOpts {
14
14
  readonly y: number;
15
15
  readonly w?: number;
16
16
  readonly h?: number;
17
+ /**
18
+ * Sibling placement in the spawn parent's `ChildOf` sequence (petition 8);
19
+ * default "last" = painted last = on top. "first" spawns UNDER everything in
20
+ * the frame (e.g. a comment box spawning behind its members).
21
+ */
22
+ readonly order?: OrderPlace;
23
+ /** Explicit `ChildOf` parent; default = the frame parent (nav frame ?? board root). */
24
+ readonly parent?: Entity;
17
25
  readonly props?: Readonly<Record<string, unknown>>;
18
26
  /** false ⇒ the spawn never enters the local undo stack (batch seeds). */
19
27
  readonly undoable?: boolean;
20
28
  }
29
+ /**
30
+ * Hang the spawn's `ChildOf` edge (petition 8) — shared by `spawnWidget` and
31
+ * the doc sink's create-intent execution so frame placement can never diverge
32
+ * between the two spawn paths. A legacy world (no BoardRoot, no nav frame,
33
+ * no explicit parent) gets NO edge: the entity stays legacy-fallback-ordered.
34
+ */
35
+ export declare function attachSpawnParent(tx: {
36
+ setRelation(e: Entity, r: Relation, target: Entity, place?: OrderPlace): void;
37
+ }, world: World, spawned: Entity, opts?: {
38
+ readonly parent?: Entity;
39
+ readonly order?: OrderPlace;
40
+ }): void;
21
41
  /**
22
42
  * The prefab-override list for a widget spawn (Position/Size + validated
23
43
  * whole-group prop values). Shared by `spawnWidget` and the doc sink's
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @ice/devtools — strata's first-party tools, engine-flavored (rebuilt
3
+ * 2026-07-13, superseding the M10 hand-rolled 4-tab panel: the standing rule
4
+ * is observers/inspectors WRAP `@vibecook/strata-ecs/tools`, never
5
+ * re-implement them).
6
+ *
7
+ * `attachDevtools(engine)` = strata's observer panel + FPS profiler, plus the
8
+ * engine glue strata cannot know:
9
+ *
10
+ * DESCRIBE PrefabId → widget type; recognizers → kind + live phase;
11
+ * pointers/ports/wires/peers/chrome labeled as such.
12
+ * DURABLE TAB fed from the facade's CURRENT DocSession (docId, converged
13
+ * snapshot, attachment baseline) — the getter re-reads per
14
+ * poll, so it tracks docs.create/open/join/close live.
15
+ * EPHEMERAL TAB fed from an app-provided PresenceSession getter (the
16
+ * facade keeps presence private; collab demos pass theirs).
17
+ * "reflect" LANE the engine's post-notify reflector flush cost — the one
18
+ * frame slice strata's world hooks cannot see — reported
19
+ * into the profiler via an onPublish hook reading
20
+ * `lastFrame()` telemetry (one frame late by construction:
21
+ * publish runs before this frame's reflect).
22
+ *
23
+ * By default all three tools mount into ONE draggable dock (dock.ts —
24
+ * 2026-07-13, James: "make the 3 tools into one draggable panel"); pass
25
+ * `dock: false` for the classic scattered corners.
26
+ *
27
+ * Reads only, outside the tick (verified-unrestricted); never a reflector,
28
+ * never writes ECS. Nobody imports this package (depcruise-enforced leaf).
29
+ */
30
+ import type { Entity, World } from "@vibecook/strata-ecs";
31
+ import { type DescribeFn, type EntityDescription, type ObserverHandle, type ObserverOptions, type ProfilerHandle, type ProfilerOptions } from "@vibecook/strata-ecs/tools";
32
+ import { type DockOptions } from "./dock";
33
+ import { type GlPanelOptions, type GlPanelStats } from "./gl-panel";
34
+ import { type CanvasEngine, type Engine, type PresenceSession } from "@ice/core";
35
+ export interface DevtoolsOpts {
36
+ /** Where the dock (or, with `dock: false`, each panel) mounts (default: document.body). */
37
+ readonly container?: HTMLElement;
38
+ /**
39
+ * ONE draggable dock hosting all three tools (the default). `false` scatters
40
+ * them to their classic fixed corners; an object sets initial placement.
41
+ * The dock mounts lazily with the first tool that needs it.
42
+ */
43
+ readonly dock?: boolean | Pick<DockOptions, "corner" | "title" | "width">;
44
+ /** Observer panel: `false` to omit, or strata passthrough options. */
45
+ readonly observer?: boolean | Pick<ObserverOptions, "defaultTab" | "tab" | "recorderCap">;
46
+ /** Profiler HUD: `false` to omit, or strata passthrough options. */
47
+ readonly profiler?: boolean | Pick<ProfilerOptions, "budgetMs" | "windowSize" | "corner" | "expanded" | "lanes">;
48
+ /** GL metrics panel: `false` to omit, or placement/budget options. Mounts lazily on the first `glStats` push. */
49
+ readonly glPanel?: boolean | Pick<GlPanelOptions, "corner" | "budgetMs" | "expanded">;
50
+ /** Collab demos: the presence session whose store feeds the ephemeral tab. */
51
+ readonly presence?: () => PresenceSession | null | undefined;
52
+ /** Override entity labeling (default: the engine describe below). */
53
+ readonly describe?: DescribeFn;
54
+ }
55
+ export interface DevtoolsHandle {
56
+ readonly observer: ObserverHandle | null;
57
+ readonly profiler: ProfilerHandle | null;
58
+ /** Host-cost lane passthrough (paint, decode…); no-op when the profiler is off. */
59
+ lane(name: string, ms: number): void;
60
+ /**
61
+ * Feed one GL frame's metrics (wire `<GLViews onFrameStats>` here). The GL
62
+ * panel mounts on the first push — renderer counts, virtual-texture census,
63
+ * LOD banding, cull population. No-op when opts.glPanel === false.
64
+ */
65
+ glStats(stats: GlPanelStats): void;
66
+ /** Tear all panels + the telemetry hook down. Idempotent. */
67
+ detach(): void;
68
+ }
69
+ /**
70
+ * Engine-aware entity labeling for the observer's lists + timeline: identity
71
+ * off ROLE (widget type, recognizer kind + phase, pointer device), not off
72
+ * raw component-name composition. Falls back to strata's defaultDescribe.
73
+ */
74
+ export declare function engineDescribe(world: World, e: Entity): EntityDescription;
75
+ /** Accepts the facade (durable tab included) or a bare core engine. */
76
+ export type DevtoolsEngine = CanvasEngine | Engine;
77
+ export declare function attachDevtools(engine: DevtoolsEngine, opts?: DevtoolsOpts): DevtoolsHandle;