@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.
- package/README.md +37 -2
- package/dist/chunk-5SJYHQI4.js +636 -0
- package/dist/chunk-5SJYHQI4.js.map +1 -0
- package/dist/{chunk-5S7GSXJG.js → chunk-DXU3RZZS.js} +2913 -1180
- package/dist/chunk-DXU3RZZS.js.map +1 -0
- package/dist/{chunk-FIAXPNNY.js → chunk-IASTQMU5.js} +130 -551
- package/dist/chunk-IASTQMU5.js.map +1 -0
- package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
- package/dist/chunk-OR5FNVUE.js.map +1 -0
- package/dist/devtools.js +508 -372
- package/dist/devtools.js.map +1 -1
- package/dist/dom.js +3 -11
- package/dist/ground.js +715 -0
- package/dist/ground.js.map +1 -0
- package/dist/index.js +70 -2
- package/dist/kernel.js +31 -1
- package/dist/r3f.js +599 -62
- package/dist/r3f.js.map +1 -1
- package/dist/react.js +34 -418
- package/dist/react.js.map +1 -1
- package/dist/types/packages/core/src/catalog/camera-derived.d.ts +41 -3
- package/dist/types/packages/core/src/catalog/gesture.d.ts +39 -3
- package/dist/types/packages/core/src/catalog/graph.d.ts +7 -6
- package/dist/types/packages/core/src/catalog/index.d.ts +1 -0
- package/dist/types/packages/core/src/catalog/insert.d.ts +31 -0
- package/dist/types/packages/core/src/catalog/pointer.d.ts +11 -0
- package/dist/types/packages/core/src/catalog/scene.d.ts +37 -4
- package/dist/types/packages/core/src/catalog/selection-presence.d.ts +7 -0
- package/dist/types/packages/core/src/catalog/settings-resources.d.ts +21 -0
- package/dist/types/packages/core/src/doc/autosave.d.ts +15 -2
- package/dist/types/packages/core/src/doc/bootstrap.d.ts +29 -0
- package/dist/types/packages/core/src/doc/channels.d.ts +9 -0
- package/dist/types/packages/core/src/doc/doc-commit-sink.d.ts +2 -1
- package/dist/types/packages/core/src/doc/doc-kit.d.ts +16 -1
- package/dist/types/packages/core/src/doc/envelope.d.ts +2 -1
- package/dist/types/packages/core/src/doc/migrate.d.ts +8 -6
- package/dist/types/packages/core/src/doc/schema-migrate.d.ts +50 -0
- package/dist/types/packages/core/src/doc/version-gate.d.ts +3 -0
- package/dist/types/packages/core/src/engine/commit-sink.d.ts +24 -2
- package/dist/types/packages/core/src/engine/frame-info.d.ts +16 -1
- package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +70 -6
- package/dist/types/packages/core/src/guards/guarded-tx.d.ts +4 -2
- package/dist/types/packages/core/src/helpers/churn-guard.d.ts +40 -0
- package/dist/types/packages/core/src/index.d.ts +13 -7
- package/dist/types/packages/core/src/input/queue.d.ts +11 -0
- package/dist/types/packages/core/src/nav/nested-canvas.d.ts +47 -11
- package/dist/types/packages/core/src/ops/arrange.d.ts +15 -0
- package/dist/types/packages/core/src/ops/insert.d.ts +62 -0
- package/dist/types/packages/core/src/ops/point-pick.d.ts +11 -7
- package/dist/types/packages/core/src/ops/sibling-order.d.ts +70 -0
- package/dist/types/packages/core/src/schema/meta.d.ts +1 -0
- package/dist/types/packages/core/src/settings/defaults.d.ts +45 -0
- package/dist/types/packages/core/src/settings/ground-config.d.ts +55 -0
- package/dist/types/packages/core/src/systems/chrome.d.ts +6 -2
- package/dist/types/packages/core/src/systems/insert-ghost.d.ts +25 -0
- package/dist/types/packages/core/src/systems/l1-pick.d.ts +25 -11
- package/dist/types/packages/core/src/systems/l2-recognize.d.ts +10 -4
- package/dist/types/packages/core/src/systems/l3-behave.d.ts +6 -4
- package/dist/types/packages/core/src/systems/l3-claim.d.ts +7 -3
- package/dist/types/packages/core/src/systems/l3-drop.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-resize.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-snap.d.ts +25 -7
- package/dist/types/packages/core/src/systems/nav-flight.d.ts +95 -0
- package/dist/types/packages/core/src/widget/define-widget.d.ts +39 -0
- package/dist/types/packages/core/src/widget/mount-store.d.ts +11 -3
- package/dist/types/packages/core/src/widget/set-props.d.ts +16 -0
- package/dist/types/packages/core/src/widget/spawn.d.ts +21 -1
- package/dist/types/packages/devtools/src/attach.d.ts +77 -0
- package/dist/types/packages/devtools/src/dock.d.ts +38 -0
- package/dist/types/packages/devtools/src/gl-panel.d.ts +69 -0
- package/dist/types/packages/devtools/src/index.d.ts +8 -6
- package/dist/types/packages/dom/src/index.d.ts +1 -3
- package/dist/types/packages/dom/src/measure-wiring.d.ts +2 -2
- package/dist/types/packages/dom/src/pointer-adapter.d.ts +21 -1
- package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +5 -0
- package/dist/types/packages/ground/src/index.d.ts +52 -0
- package/dist/types/packages/ground/src/layer.d.ts +40 -0
- package/dist/types/packages/ground/src/pass.d.ts +41 -0
- package/dist/types/packages/ground/src/passes/grid.d.ts +21 -0
- package/dist/types/packages/ground/src/passes/guides-collect.d.ts +14 -0
- package/dist/types/packages/ground/src/passes/guides.d.ts +3 -0
- package/dist/types/packages/ground/src/passes/soup-collect.d.ts +35 -0
- package/dist/types/packages/ground/src/passes/soup-mesh.d.ts +8 -0
- package/dist/types/packages/ground/src/passes/wires-collect.d.ts +31 -0
- package/dist/types/packages/ground/src/passes/wires.d.ts +10 -0
- package/dist/types/packages/ground/src/renderer.d.ts +15 -0
- package/dist/types/packages/ice/src/ground.d.ts +1 -0
- package/dist/types/packages/kernel/src/bezier.d.ts +15 -0
- package/dist/types/packages/kernel/src/coords.d.ts +15 -0
- package/dist/types/packages/kernel/src/easing.d.ts +11 -0
- package/dist/types/packages/kernel/src/index.d.ts +3 -0
- package/dist/types/packages/kernel/src/layout.d.ts +118 -0
- package/dist/types/packages/kernel/src/nav-flight.d.ts +93 -0
- package/dist/types/packages/r3f/src/bridge.d.ts +73 -12
- package/dist/types/packages/r3f/src/composite-material.d.ts +9 -0
- package/dist/types/packages/r3f/src/compositor-pass.d.ts +22 -0
- package/dist/types/packages/r3f/src/gl-root.d.ts +76 -1
- package/dist/types/packages/r3f/src/gl-router.d.ts +10 -0
- package/dist/types/packages/r3f/src/index.d.ts +3 -2
- package/dist/types/packages/r3f/src/island-state.d.ts +44 -0
- package/dist/types/packages/r3f/src/preview-capture.d.ts +19 -0
- package/dist/types/packages/r3f/src/self-sustain.d.ts +39 -0
- package/dist/types/packages/r3f/src/use-island-frame.d.ts +16 -1
- package/dist/types/packages/react/src/engine-context.d.ts +8 -0
- package/dist/types/packages/react/src/index.d.ts +4 -2
- package/dist/types/packages/react/src/infinite-canvas.d.ts +40 -6
- package/dist/types/packages/react/src/preview-snapshots.d.ts +17 -0
- package/dist/types/packages/react/src/widget-preview.d.ts +14 -0
- package/package.json +13 -8
- package/dist/chunk-5S7GSXJG.js.map +0 -1
- package/dist/chunk-BCJ47SX7.js.map +0 -1
- package/dist/chunk-FIAXPNNY.js.map +0 -1
- package/dist/types/packages/devtools/src/panel.d.ts +0 -48
- package/dist/types/packages/dom/src/reflectors/grid.d.ts +0 -63
- 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 {
|
|
50
|
+
import type { TickSystem, World } from "@vibecook/strata-ecs";
|
|
47
51
|
export declare function createSelectionChromeSystem(world: World): TickSystem;
|
|
48
|
-
export declare function createBreakpointSystem(
|
|
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
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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
|
|
9
|
-
* the
|
|
10
|
-
*
|
|
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
|
|
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
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
|
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
|
|
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
|
|
10
|
-
*
|
|
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).
|
|
14
|
-
*
|
|
15
|
-
*
|
|
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,
|
|
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>):
|
|
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
|
|
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:
|
|
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;
|