@vibecook/ice 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -2
- package/dist/chunk-2GUYATUM.js +643 -0
- package/dist/chunk-2GUYATUM.js.map +1 -0
- package/dist/{chunk-5S7GSXJG.js → chunk-5QY7HK6G.js} +3145 -1306
- package/dist/chunk-5QY7HK6G.js.map +1 -0
- package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
- package/dist/chunk-OR5FNVUE.js.map +1 -0
- package/dist/{chunk-FIAXPNNY.js → chunk-PKK6GLSA.js} +143 -551
- package/dist/chunk-PKK6GLSA.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 +80 -2
- package/dist/kernel.js +31 -1
- package/dist/r3f.js +605 -62
- package/dist/r3f.js.map +1 -1
- package/dist/react.js +36 -418
- package/dist/react.js.map +1 -1
- package/dist/types/packages/core/src/catalog/camera-derived.d.ts +65 -3
- package/dist/types/packages/core/src/catalog/gesture.d.ts +40 -4
- 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 +49 -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/engine.d.ts +11 -0
- package/dist/types/packages/core/src/engine/frame-control.d.ts +71 -0
- 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 +79 -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 +15 -7
- package/dist/types/packages/core/src/input/queue.d.ts +11 -0
- package/dist/types/packages/core/src/interaction/gesture-status.d.ts +13 -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/loop.d.ts +11 -0
- 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 +21 -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 +32 -61
- 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
|
@@ -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;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The devtools dock — ONE draggable window hosting all three tools
|
|
3
|
+
* (2026-07-13, James: "make the 3 tools into one draggable panel").
|
|
4
|
+
*
|
|
5
|
+
* strata's observer + profiler and our GL panel each mount `position: fixed`
|
|
6
|
+
* into their own corner. The dock does NOT re-implement any of them (standing
|
|
7
|
+
* rule: wrap first-party tools): it is a fixed, draggable shell with three
|
|
8
|
+
* stacked slots; each tool mounts into its slot via its own `container`
|
|
9
|
+
* option, and dock-scoped CSS de-windows them (`position: static`, full
|
|
10
|
+
* width, no border/shadow) so they read as sections of one panel. Their own
|
|
11
|
+
* behavior is untouched — the observer keeps its tabs/collapse/vertical
|
|
12
|
+
* resize (its header drag goes inert: left/top are ignored at static), the
|
|
13
|
+
* profiler + GL strips keep their click-to-expand.
|
|
14
|
+
*
|
|
15
|
+
* Slot order is fixed by the dock, not by mount order: profiler and GL are
|
|
16
|
+
* one-line summary strips, the observer is the deep-dive — strips on top.
|
|
17
|
+
* Layout (x/y/width/open) persists to localStorage, same as the observer:
|
|
18
|
+
* schema edits full-reload the page by design; the dock must stay put.
|
|
19
|
+
*/
|
|
20
|
+
export type DockCorner = "tl" | "tr" | "bl" | "br";
|
|
21
|
+
export type DockSlotId = "profiler" | "gl" | "observer";
|
|
22
|
+
export interface DockOptions {
|
|
23
|
+
/** Where the dock mounts (default: document.body). */
|
|
24
|
+
readonly container?: HTMLElement;
|
|
25
|
+
/** Initial corner when nothing is persisted yet (default "tr"). */
|
|
26
|
+
readonly corner?: DockCorner;
|
|
27
|
+
/** Header title (default "ice devtools"). */
|
|
28
|
+
readonly title?: string;
|
|
29
|
+
/** Initial width in px when nothing is persisted yet (default 420). */
|
|
30
|
+
readonly width?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface Dock {
|
|
33
|
+
readonly el: HTMLElement;
|
|
34
|
+
/** The mount element for one tool. Empty slots are display:none via CSS. */
|
|
35
|
+
slot(id: DockSlotId): HTMLElement;
|
|
36
|
+
dispose(): void;
|
|
37
|
+
}
|
|
38
|
+
export declare function createDock(opts?: DockOptions): Dock;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GL metrics panel — the engine-specific renderer readout strata has no
|
|
3
|
+
* counterpart for (2026-07-13, James: "comprehensive r3f profiler ... and our
|
|
4
|
+
* engine specific optimization metrics ... with nice visuals").
|
|
5
|
+
*
|
|
6
|
+
* Pure DOM, strata's visual language (same palette/typography as the observer
|
|
7
|
+
* + profiler HUD so the three read as one toolset). Fed by pushes of
|
|
8
|
+
* `GlPanelStats` — the STRUCTURAL MIRROR of @ice/r3f's `GlFrameStats`
|
|
9
|
+
* (devtools cannot import r3f under the walls; keep the shapes aligned):
|
|
10
|
+
*
|
|
11
|
+
* head fps · gpu ms + a gpu sparkline against the frame budget
|
|
12
|
+
* frame cpu / gpu bars vs budget, with session maxima
|
|
13
|
+
* render draw calls · triangles · points/lines · programs · geoms · textures
|
|
14
|
+
* virtual live render targets + total resolution, FBO bytes vs budget,
|
|
15
|
+
* textures the island phase census as a segmented bar, repaint/evict counters
|
|
16
|
+
* lod zoom → hysteresis band → effective DPR, live targets per band
|
|
17
|
+
* widgets Visible vs Culled population
|
|
18
|
+
*
|
|
19
|
+
* All values land via `textContent` (never innerHTML with data). Rendering is
|
|
20
|
+
* throttled — pushes arrive per composited frame, the DOM refreshes at ~8 Hz.
|
|
21
|
+
*/
|
|
22
|
+
/** Mirror of @ice/r3f `GlFrameStats` (structural — see the header). */
|
|
23
|
+
export interface GlPanelStats {
|
|
24
|
+
readonly cpuMs: number;
|
|
25
|
+
readonly gpuMs: number;
|
|
26
|
+
readonly fps: number;
|
|
27
|
+
readonly drawCalls: number;
|
|
28
|
+
readonly triangles: number;
|
|
29
|
+
readonly points: number;
|
|
30
|
+
readonly lines: number;
|
|
31
|
+
readonly programs: number;
|
|
32
|
+
readonly geometries: number;
|
|
33
|
+
readonly textures: number;
|
|
34
|
+
readonly renderTargets: number;
|
|
35
|
+
readonly renderMegaPixels: number;
|
|
36
|
+
readonly fboBytes: number;
|
|
37
|
+
readonly fboBudgetBytes: number;
|
|
38
|
+
readonly islands: {
|
|
39
|
+
readonly total: number;
|
|
40
|
+
readonly hot: number;
|
|
41
|
+
readonly warm: number;
|
|
42
|
+
readonly waking: number;
|
|
43
|
+
readonly cold: number;
|
|
44
|
+
readonly dormant: number;
|
|
45
|
+
};
|
|
46
|
+
readonly bandHistogram: Readonly<Record<string, number>>;
|
|
47
|
+
readonly repainted: number;
|
|
48
|
+
readonly pendingPaints: number;
|
|
49
|
+
readonly evicted: number;
|
|
50
|
+
readonly zoom: number;
|
|
51
|
+
readonly band: number;
|
|
52
|
+
readonly effectiveDpr: number;
|
|
53
|
+
readonly visibleWidgets: number;
|
|
54
|
+
readonly culledWidgets: number;
|
|
55
|
+
}
|
|
56
|
+
export type GlPanelCorner = "tl" | "tr" | "bl" | "br";
|
|
57
|
+
export interface GlPanelOptions {
|
|
58
|
+
readonly container?: HTMLElement;
|
|
59
|
+
readonly corner?: GlPanelCorner;
|
|
60
|
+
/** Frame budget in ms — the sparkline's reference line (default 16.7). */
|
|
61
|
+
readonly budgetMs?: number;
|
|
62
|
+
/** Start with the detail body open (default false). */
|
|
63
|
+
readonly expanded?: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface GlPanel {
|
|
66
|
+
push(stats: GlPanelStats): void;
|
|
67
|
+
dispose(): void;
|
|
68
|
+
}
|
|
69
|
+
export declare function createGlPanel(opts?: GlPanelOptions): GlPanel;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ice/devtools —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* @ice/devtools — strata's observer panel + FPS profiler, engine-flavored
|
|
3
|
+
* (describe/durable/ephemeral/reflect-lane glue). Reads the world outside the
|
|
4
|
+
* tick; never a reflector, never writes ECS. Nobody imports us
|
|
5
|
+
* (depcruise-enforced leaf).
|
|
6
6
|
*/
|
|
7
|
-
export declare const DEVTOOLS_VERSION = "0.
|
|
8
|
-
export { attachDevtools, type DevtoolsHandle, type DevtoolsOpts } from "./
|
|
7
|
+
export declare const DEVTOOLS_VERSION = "0.1.0";
|
|
8
|
+
export { attachDevtools, engineDescribe, type DevtoolsEngine, type DevtoolsHandle, type DevtoolsOpts, } from "./attach";
|
|
9
|
+
export { createDock, type Dock, type DockCorner, type DockOptions, type DockSlotId } from "./dock";
|
|
10
|
+
export { createGlPanel, type GlPanel, type GlPanelCorner, type GlPanelOptions, type GlPanelStats } from "./gl-panel";
|
|
@@ -9,11 +9,9 @@ export { createPlaneTransformReflector, type CameraPlanes } from "./reflectors/p
|
|
|
9
9
|
export { createGrayboxReflector } from "./reflectors/graybox";
|
|
10
10
|
export { createDomWidgetsReflector, type DomWidgetsHost, type DomWidgetsReflector, } from "./reflectors/dom-widgets";
|
|
11
11
|
export { startRafLoop } from "./loop";
|
|
12
|
-
export { attachPointerAdapter, type GLRoute, type PointerAdapterOpts } from "./pointer-adapter";
|
|
12
|
+
export { attachPointerAdapter, type GLRoute, type GLRouteVerdict, type PointerAdapterOpts } from "./pointer-adapter";
|
|
13
13
|
export { createCursorReflector } from "./reflectors/cursor";
|
|
14
14
|
export { createRemoteCursorsReflector, type RemoteCursorsReflector } from "./reflectors/remote-cursors";
|
|
15
|
-
export { createGridReflector, DEFAULT_GRID_CONFIG, type GridConfig } from "./reflectors/grid";
|
|
16
|
-
export { createWiresReflector, DEFAULT_WIRES_CONFIG, type WirePreview, type WiresConfig, type WiresReflector, } from "./reflectors/wires";
|
|
17
15
|
export { createChromeReflector } from "./reflectors/chrome";
|
|
18
16
|
export { attachMeasureAdapter, type MeasureAdapter } from "./measure-adapter";
|
|
19
17
|
export { wireMeasurement, type MeasureWiringHosts, type MeasureWiringOpts } from "./measure-wiring";
|
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
* timestamp straight into the frame clock (so FrameInfo.dt is real wall time,
|
|
7
7
|
* clamped in core). The returned stop fn cancels the pending frame AND latches
|
|
8
8
|
* so a frame already in flight cannot re-schedule — calling it is idempotent.
|
|
9
|
+
*
|
|
10
|
+
* THE FREEZE GATE (2026-08-04). Before each frame the loop claims a step from
|
|
11
|
+
* `engine.frame`. While a freeze is held the gate walks its settle and then
|
|
12
|
+
* refuses, at which point the loop PARKS: it stops scheduling rAF at all
|
|
13
|
+
* rather than spinning a callback that does nothing — "the engine is stopped"
|
|
14
|
+
* should mean the browser has no work queued for it either. Thaw arrives
|
|
15
|
+
* through the gate's change subscription, which schedules the next frame.
|
|
16
|
+
*
|
|
17
|
+
* The dt clamp is what makes resume safe: a freeze that lasted an hour hands
|
|
18
|
+
* `step` an enormous `now`, and core clamps the delta (frame-info.ts), so
|
|
19
|
+
* tweens advance by one ordinary frame instead of teleporting to their end.
|
|
9
20
|
*/
|
|
10
21
|
import type { Engine } from "@ice/core";
|
|
11
22
|
export declare function startRafLoop(engine: Engine): () => void;
|
|
@@ -33,8 +33,8 @@ export interface MeasureWiringHosts {
|
|
|
33
33
|
}
|
|
34
34
|
export interface MeasureWiringOpts {
|
|
35
35
|
/**
|
|
36
|
-
* Border-box read for the forced re-measure on show. Defaults to
|
|
37
|
-
* `
|
|
36
|
+
* Border-box read for the forced re-measure on show. Defaults to layout
|
|
37
|
+
* `offsetWidth`/`offsetHeight`; tests inject a deterministic stub.
|
|
38
38
|
*/
|
|
39
39
|
readonly measure?: (el: HTMLElement) => {
|
|
40
40
|
w: number;
|
|
@@ -28,6 +28,15 @@
|
|
|
28
28
|
* through L0). `stopPropagation()` inside widget content is the OTHER boundary:
|
|
29
29
|
* a stopped event never reaches the container listener, so it never becomes a
|
|
30
30
|
* canvas fact at all — that is the design, not a gap.
|
|
31
|
+
*
|
|
32
|
+
* Hover-time amendment (design-002 §8, 2026-07-18): down/move facts ALSO carry
|
|
33
|
+
* `overInteractive` — the same `crossesInteractive` check, run at hover time,
|
|
34
|
+
* OR'd with the GL router's hover verdict — so the world can telegraph the
|
|
35
|
+
* opt-out BEFORE a down (cursor affordances). It never gates gestures:
|
|
36
|
+
* `surfaceHandled` stays down-only and keeps its exact meaning. During a
|
|
37
|
+
* container-captured drag every event retargets to the container, so
|
|
38
|
+
* `overInteractive` reads false mid-gesture — correct: the gesture owns the
|
|
39
|
+
* pointer, not the widget under it.
|
|
31
40
|
*/
|
|
32
41
|
import { type InputQueue } from "@ice/core";
|
|
33
42
|
import type { CanvasHost } from "./host";
|
|
@@ -41,7 +50,18 @@ import type { CanvasHost } from "./host";
|
|
|
41
50
|
* recognizers skip it via `HandledByWidget` — the same contract as the
|
|
42
51
|
* native-interactive opt-out.
|
|
43
52
|
*/
|
|
44
|
-
export type GLRoute = (kind: "down" | "move" | "up" | "cancel", screenX: number, screenY: number, native: PointerEvent) => boolean;
|
|
53
|
+
export type GLRoute = (kind: "down" | "move" | "up" | "cancel", screenX: number, screenY: number, native: PointerEvent) => boolean | GLRouteVerdict;
|
|
54
|
+
/**
|
|
55
|
+
* The richer GL verdict (hover-time amendment, 2026-07-18): `handled` keeps the
|
|
56
|
+
* boolean's exact meaning (island content claimed the event); `overInteractive`
|
|
57
|
+
* adds the hover fact — the pick chain holds claim-capable content (a mesh with
|
|
58
|
+
* a down/click handler), so a down HERE would be the island's. Plain `boolean`
|
|
59
|
+
* returns stay legal (≡ `{ handled }`), so existing routes never break.
|
|
60
|
+
*/
|
|
61
|
+
export interface GLRouteVerdict {
|
|
62
|
+
readonly handled: boolean;
|
|
63
|
+
readonly overInteractive?: boolean;
|
|
64
|
+
}
|
|
45
65
|
export interface PointerAdapterOpts {
|
|
46
66
|
readonly glRoute?: GLRoute;
|
|
47
67
|
}
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
* dedicated observer; the note below is historical:
|
|
25
25
|
* picked up on the next Position/Size stamp or membership change; the ResizeObserver
|
|
26
26
|
* measurement path — a later slice — will arm its own dirt.)
|
|
27
|
+
* - **opacity**: a private observer on `[Opacity]` (attach/detach/value) sets a
|
|
28
|
+
* dirty flag; the change-only pass writes `style.opacity` on the host (absent
|
|
29
|
+
* component = 1 = property cleared). The GL twin is the composite quad's
|
|
30
|
+
* `uOpacity` — a gl widget's host here carries only its DOM chrome, so the
|
|
31
|
+
* two halves fade from the same cell.
|
|
27
32
|
* - **drag-promote** (P3): a private observer on `[Grab]` membership sets a dirty
|
|
28
33
|
* flag; a host whose entity holds `Grab` re-parents content→lifted plane and back
|
|
29
34
|
* on release. Re-parenting MOVES the host node (with its content child), which
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ice/ground — the P0 ground stratum as ONE WebGPU canvas (design-004 §1
|
|
3
|
+
* as-built amendment, 2026-07-16). three's WebGPURenderer + TSL, WebGL2
|
|
4
|
+
* fallback automatic. Off the react chain: react receives the layer as an
|
|
5
|
+
* OPAQUE factory (`<InfiniteCanvas ground={ground(...)}>`); imperative apps
|
|
6
|
+
* register `layer.reflector` themselves. Passes: dot grid, wires, snap
|
|
7
|
+
* guides — future ground shaders/effects join the same pass registry.
|
|
8
|
+
* (Selection chrome moved OFF the ground 2026-07-17: rings live with the
|
|
9
|
+
* cards, the multi-select union box is P4 dom chrome — always on top.)
|
|
10
|
+
*/
|
|
11
|
+
import type { GridConfig, SnapGuidesConfig, WirePreviewBuffer, WiresConfig, World } from "@ice/core";
|
|
12
|
+
import { type GroundLayerHost, type GroundReflector } from "./layer";
|
|
13
|
+
import type { GroundPass } from "./pass";
|
|
14
|
+
import { type GroundRendererLike } from "./renderer";
|
|
15
|
+
export type { GroundFrame, GroundPass } from "./pass";
|
|
16
|
+
export type { GroundReflector } from "./layer";
|
|
17
|
+
export type { GroundRendererLike } from "./renderer";
|
|
18
|
+
export { SoupBuilder, parseCssColor, type Rgba, type TriSoup } from "./passes/soup-collect";
|
|
19
|
+
export { collectGuides } from "./passes/guides-collect";
|
|
20
|
+
export { collectWires } from "./passes/wires-collect";
|
|
21
|
+
export { DEFAULT_GRID_CONFIG, DEFAULT_SNAP_GUIDES_CONFIG, DEFAULT_WIRES_CONFIG, type GridConfig, type SnapGuidesConfig, type WiresConfig, } from "@ice/core";
|
|
22
|
+
export interface GroundContext {
|
|
23
|
+
readonly host: GroundLayerHost;
|
|
24
|
+
readonly world: World;
|
|
25
|
+
/** Connect-drag preview reader (graph boards); omit on boards without wires. */
|
|
26
|
+
readonly readWirePreview?: () => WirePreviewBuffer;
|
|
27
|
+
}
|
|
28
|
+
export interface GroundLayer {
|
|
29
|
+
readonly reflector: GroundReflector;
|
|
30
|
+
/** Live grid re-tune (the react `grid` prop forwards here). */
|
|
31
|
+
configureGrid(cfg: Partial<GridConfig>): void;
|
|
32
|
+
dispose(): void;
|
|
33
|
+
}
|
|
34
|
+
export interface GroundOptions {
|
|
35
|
+
readonly grid?: Partial<GridConfig>;
|
|
36
|
+
readonly wires?: Partial<WiresConfig>;
|
|
37
|
+
readonly guides?: Partial<SnapGuidesConfig>;
|
|
38
|
+
/** Extra app passes, rendered after the built-ins (renderOrder ≥ 3 is yours). */
|
|
39
|
+
readonly passes?: readonly GroundPass[];
|
|
40
|
+
/** Force the WebGL2 backend (debug / e2e A-B runs). */
|
|
41
|
+
readonly forceWebGL?: boolean;
|
|
42
|
+
/** TEST seam: inject a fake renderer (headless orchestration tests). */
|
|
43
|
+
readonly rendererOverride?: GroundRendererLike;
|
|
44
|
+
}
|
|
45
|
+
export type GroundFactory = (ctx: GroundContext) => GroundLayer;
|
|
46
|
+
/**
|
|
47
|
+
* Build the ground layer factory. Usage:
|
|
48
|
+
* react apps: <InfiniteCanvas ground={ground({ grid: {...} })} …>
|
|
49
|
+
* imperative apps: const layer = ground()( { host, world, readWirePreview } );
|
|
50
|
+
* engine.registerReflector(layer.reflector);
|
|
51
|
+
*/
|
|
52
|
+
export declare function ground(opts?: GroundOptions): GroundFactory;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ground LAYER — one reflector, one canvas, one render (design-004 §1
|
|
3
|
+
* as-built 2026-07-16: P0 consolidated from three stacked canvases into a
|
|
4
|
+
* single WebGPU canvas; grid, wires and snap-guides are now PASSES of this
|
|
5
|
+
* layer, and future ground effects join the same registry).
|
|
6
|
+
*
|
|
7
|
+
* Coordinate model: the layer renders in SCREEN CSS-px space — an ortho
|
|
8
|
+
* camera maps [0..w]×[0..h] (y-down) to clip, and passes build their geometry
|
|
9
|
+
* through the kernel `worldToScreen` seam per collect (the proven 2D-canvas
|
|
10
|
+
* semantics: geometry rebuilds on the same dirt that re-stroked the old
|
|
11
|
+
* canvases; the grid stays a per-pixel screen-space shader). The Y-flip
|
|
12
|
+
* question never arises here — `worldToScreen` IS the one conversion (L13).
|
|
13
|
+
*
|
|
14
|
+
* Reflector contract (design-002 §5): post-notify, output-only, never reads
|
|
15
|
+
* layout in flush (the ResizeObserver owns sizing), never writes ECS.
|
|
16
|
+
* `always: true` with a private dirty union — an idle scene renders zero
|
|
17
|
+
* frames; ANY dirty pass re-renders the whole canvas (one draw, same cost
|
|
18
|
+
* class as a pan frame under the old grid). WebGPU init is async: flushes
|
|
19
|
+
* before ready are deferred (flags stay armed), the first ready flush paints.
|
|
20
|
+
*/
|
|
21
|
+
import { type ReflectorDef, type World } from "@ice/core";
|
|
22
|
+
import type { GroundPass } from "./pass";
|
|
23
|
+
import type { GroundRendererLike } from "./renderer";
|
|
24
|
+
export interface GroundLayerHost {
|
|
25
|
+
readonly container: HTMLElement;
|
|
26
|
+
readonly contentPlane: HTMLElement;
|
|
27
|
+
}
|
|
28
|
+
export interface GroundReflector extends ReflectorDef {
|
|
29
|
+
/** False while init is pending or after a device/context failure. */
|
|
30
|
+
available(): boolean;
|
|
31
|
+
/** Whole-canvas renders so far (the churn instrument — 0 on an idle scene). */
|
|
32
|
+
redraws(): number;
|
|
33
|
+
}
|
|
34
|
+
export interface GroundLayerInternals {
|
|
35
|
+
reflector: GroundReflector;
|
|
36
|
+
/** Wake every pass + render (config changes, external invalidation). */
|
|
37
|
+
invalidateAll(): void;
|
|
38
|
+
dispose(): void;
|
|
39
|
+
}
|
|
40
|
+
export declare function createLayer(host: GroundLayerHost, world: World, renderer: GroundRendererLike, passes: readonly GroundPass[]): GroundLayerInternals;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ground-pass contract (design-004 §1, as-built 2026-07-16: P0 is ONE
|
|
3
|
+
* WebGPU canvas; passes are its draw layers).
|
|
4
|
+
*
|
|
5
|
+
* A pass owns a scene `object` (mesh/group with a TSL node material), arms its
|
|
6
|
+
* own dirty observers, and rebuilds its geometry/uniforms in `collect` — PURE
|
|
7
|
+
* world reads in the layer's SCREEN-px coordinate space (the ortho camera maps
|
|
8
|
+
* CSS px → clip; world→screen goes through the kernel `worldToScreen` seam,
|
|
9
|
+
* Law 13). Heavy per-pass math (tessellation, triangle-soup emission) lives in
|
|
10
|
+
* sibling `*-collect.ts` modules that import ONLY core/kernel, so collectors
|
|
11
|
+
* unit-test under happy-dom with no GPU and no three import.
|
|
12
|
+
*/
|
|
13
|
+
import type { World } from "@ice/core";
|
|
14
|
+
import type { Object3D } from "three/webgpu";
|
|
15
|
+
/** Per-flush frame facts every pass collects against. */
|
|
16
|
+
export interface GroundFrame {
|
|
17
|
+
/** CSS-px viewport size. */
|
|
18
|
+
readonly width: number;
|
|
19
|
+
readonly height: number;
|
|
20
|
+
readonly dpr: number;
|
|
21
|
+
/** Engine camera: world top-left + zoom (grid.ts P0 contract). */
|
|
22
|
+
readonly camera: {
|
|
23
|
+
readonly x: number;
|
|
24
|
+
readonly y: number;
|
|
25
|
+
readonly zoom: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface GroundPass {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
/** The pass's scene object; the layer adds it once and disposes with the pass. */
|
|
31
|
+
readonly object: Object3D;
|
|
32
|
+
/**
|
|
33
|
+
* Arm dirty observers on the world; `wake()` marks this pass for a
|
|
34
|
+
* re-collect on the next flush. Camera/resize dirt is LAYER-owned (it wakes
|
|
35
|
+
* every pass) — arm only pass-specific sources here. Returns unsubscribers.
|
|
36
|
+
*/
|
|
37
|
+
arm(world: World, wake: () => void): Array<() => void>;
|
|
38
|
+
/** Rebuild geometry/uniforms from the world (read-only; screen-px space). */
|
|
39
|
+
collect(world: World, frame: GroundFrame): void;
|
|
40
|
+
dispose(): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dot-grid pass — TSL port of the raw-WebGL grid (itself verbatim from v1
|
|
3
|
+
* GridRenderer.ts; the fragment math below is that shader re-expressed as a
|
|
4
|
+
* node graph, level loop unrolled in JS). Screen-space: a fullscreen triangle
|
|
5
|
+
* whose vertex stage passes clip coords through; the fragment reconstructs
|
|
6
|
+
* world position from `screenCoordinate` (top-left device px on BOTH backends
|
|
7
|
+
* — three normalizes, so the old manual y-flip is gone) + camera uniforms.
|
|
8
|
+
*
|
|
9
|
+
* One deliberate re-expression: the GLSL if/else fade ladder became the
|
|
10
|
+
* closed form `clamp(min(rise, fall), 0, 1)` — identical piecewise output
|
|
11
|
+
* (rise 0→1 across fadeIn, plateau 1, fall 1→0 across fadeOut) with no
|
|
12
|
+
* branching. Everything else (fract-distance dots, 0.5-device-px AA,
|
|
13
|
+
* max-composited levels) matches term for term.
|
|
14
|
+
*/
|
|
15
|
+
import { type GridConfig } from "@ice/core";
|
|
16
|
+
import type { GroundPass } from "../pass";
|
|
17
|
+
export interface GridPass extends GroundPass {
|
|
18
|
+
/** Live re-tune (the react `grid` prop seam) — partial merge over current values. */
|
|
19
|
+
configure(cfg: Partial<GridConfig>): void;
|
|
20
|
+
}
|
|
21
|
+
export declare function createGridPass(initial?: Partial<GridConfig>): GridPass;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snap-guides collector — the pure ECS→triangles half of the guides pass
|
|
3
|
+
* (v1 SnapGuideRenderer look, carried over from the 2D-canvas reflector it
|
|
4
|
+
* replaces): full-viewport magenta alignment lines (`GuideLine.from == to` ⇒
|
|
5
|
+
* full span — design-003 §6), bounded spans, and equal-spacing bars with 4px
|
|
6
|
+
* end ticks. Screen-px output via the kernel `worldToScreen` seam. NO guide
|
|
7
|
+
* count caps (the pool grows; so does the soup).
|
|
8
|
+
*/
|
|
9
|
+
import { type SnapGuidesConfig, type World } from "@ice/core";
|
|
10
|
+
import type { GroundFrame } from "../pass";
|
|
11
|
+
import { type TriSoup } from "./soup-collect";
|
|
12
|
+
export declare const guideQ: import("@vibecook/strata-ecs").Query;
|
|
13
|
+
export declare const barQ: import("@vibecook/strata-ecs").Query;
|
|
14
|
+
export declare function collectGuides(world: World, frame: GroundFrame, config?: SnapGuidesConfig): TriSoup;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triangle-soup builders — the PURE half of the geometry passes (no three
|
|
3
|
+
* import; unit-tested under happy-dom). Collectors emit screen-px triangles
|
|
4
|
+
* with per-vertex rgba into a growable soup; the three-side `soup-mesh`
|
|
5
|
+
* uploads them. MSAA (renderer antialias) smooths edges, matching the old 2D
|
|
6
|
+
* canvases' stroke AA closely enough at 1–2 px widths.
|
|
7
|
+
*/
|
|
8
|
+
export interface TriSoup {
|
|
9
|
+
/** xyz per vertex (z = 0 — the layer renders flat in screen space). */
|
|
10
|
+
positions: Float32Array;
|
|
11
|
+
/** rgba per vertex, 0-1. */
|
|
12
|
+
colors: Float32Array;
|
|
13
|
+
vertexCount: number;
|
|
14
|
+
}
|
|
15
|
+
export type Rgba = readonly [number, number, number, number];
|
|
16
|
+
export declare class SoupBuilder {
|
|
17
|
+
private pos;
|
|
18
|
+
private col;
|
|
19
|
+
private vert;
|
|
20
|
+
/** Axis-aligned rect (two triangles). */
|
|
21
|
+
rect(x: number, y: number, w: number, h: number, c: Rgba): void;
|
|
22
|
+
/** Stroke segment (a→b) of `width`, flat caps — a quad across the segment normal. */
|
|
23
|
+
segment(ax: number, ay: number, bx: number, by: number, width: number, c: Rgba): void;
|
|
24
|
+
/** Stroke a polyline (interleaved xy) as per-segment quads (smooth-curve joins). */
|
|
25
|
+
polyline(points: ArrayLike<number>, width: number, c: Rgba): void;
|
|
26
|
+
/** Filled disc as a triangle fan (MSAA smooths the rim; 16 segs ≈ round at ≤8 px). */
|
|
27
|
+
disc(cx: number, cy: number, r: number, c: Rgba, segments?: number): void;
|
|
28
|
+
build(): TriSoup;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `rgba(r, g, b, a)` / `rgb(…)` / `#rrggbb` → [r,g,b,a] 0-1 (the WiresConfig
|
|
32
|
+
* colors are CSS strings — v1/2D-canvas heritage). Unknown formats fall back
|
|
33
|
+
* to opaque mid-gray rather than throwing in a reflector path.
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseCssColor(css: string): Rgba;
|