@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
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires collector — the pure ECS→triangles half of the wires pass. Port of
|
|
3
|
+
* the 2D-canvas wires reflector's drawing, kernel-tessellated:
|
|
4
|
+
*
|
|
5
|
+
* - Wire geometry NEVER reads a port ENTITY (design-001 §5.3): endpoints
|
|
6
|
+
* resolve from the wire's `WireFrom`/`WireTo` widgets + `WirePorts` ids
|
|
7
|
+
* against the type registry's static port schema (core `portSlots` — the
|
|
8
|
+
* old local resolver's TODO, retired), so a CULLED endpoint still draws.
|
|
9
|
+
* - Cubics build in WORLD space then map control points through the kernel
|
|
10
|
+
* `worldToScreen` seam — bezier curves are affine-invariant, so the mapped
|
|
11
|
+
* cubic IS the screen curve — and `tessellateCubic` (0.25 px tolerance)
|
|
12
|
+
* flattens with density that tracks zoom for free.
|
|
13
|
+
* - Connect preview: solid when snapped-compatible, dashed [6,4] otherwise
|
|
14
|
+
* (dash placement from the tessellation's arc-length prefix).
|
|
15
|
+
* - Materialized port dots: triangle-fan discs, active color while any
|
|
16
|
+
* connect drag is routed.
|
|
17
|
+
*/
|
|
18
|
+
import { type WirePreviewBuffer, type WiresConfig, type World } from "@ice/core";
|
|
19
|
+
import type { GroundFrame } from "../pass";
|
|
20
|
+
import { type TriSoup } from "./soup-collect";
|
|
21
|
+
export declare const wireQ: import("@vibecook/strata-ecs").Query;
|
|
22
|
+
export declare const geometryQ: import("@vibecook/strata-ecs").Query;
|
|
23
|
+
export declare const measuredQ: import("@vibecook/strata-ecs").Query;
|
|
24
|
+
export declare const selectedQ: import("@vibecook/strata-ecs").Query;
|
|
25
|
+
export declare const portQ: import("@vibecook/strata-ecs").Query;
|
|
26
|
+
export declare const routedConnectQ: import("@vibecook/strata-ecs").Query;
|
|
27
|
+
/** A live connect drag moves the preview every frame — Drag value dirt covers it. */
|
|
28
|
+
export declare const connectDragQ: import("@vibecook/strata-ecs").Query;
|
|
29
|
+
/** Nav churn signal: Active membership flips re-collect (scope filter below). */
|
|
30
|
+
export declare const activeQ: import("@vibecook/strata-ecs").Query;
|
|
31
|
+
export declare function collectWires(world: World, frame: GroundFrame, config?: WiresConfig, preview?: WirePreviewBuffer): TriSoup;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires pass — the three-side wrapper over the pure `collectWires` (renders
|
|
3
|
+
* above the grid, below the guides, matching the old P0 canvas order).
|
|
4
|
+
* Dirt mirrors the old 2D reflector's observers; the connect preview rides
|
|
5
|
+
* `[RoutedConnect, Drag]` value dirt (the buffer updates exactly when the
|
|
6
|
+
* connect recognizer's Drag advances) instead of a per-frame value diff.
|
|
7
|
+
*/
|
|
8
|
+
import { type WirePreviewBuffer, type WiresConfig } from "@ice/core";
|
|
9
|
+
import type { GroundPass } from "../pass";
|
|
10
|
+
export declare function createWiresPass(initial?: Partial<WiresConfig>, readPreview?: () => WirePreviewBuffer): GroundPass;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Camera, Scene } from "three/webgpu";
|
|
2
|
+
/** The layer's renderer seam — tests inject a fake (compositor `GlLike` precedent). */
|
|
3
|
+
export interface GroundRendererLike {
|
|
4
|
+
readonly canvas: HTMLCanvasElement;
|
|
5
|
+
ready(): boolean;
|
|
6
|
+
failed(): boolean;
|
|
7
|
+
/** Register a one-shot "became ready" callback (fires immediately if already ready). */
|
|
8
|
+
onReady(cb: () => void): void;
|
|
9
|
+
setSize(cssW: number, cssH: number, dpr: number): void;
|
|
10
|
+
render(scene: Scene, camera: Camera): void;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
13
|
+
export declare function createGroundRenderer(doc: Document, opts?: {
|
|
14
|
+
forceWebGL?: boolean;
|
|
15
|
+
}): GroundRendererLike;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../../ground/src/index";
|
|
@@ -29,6 +29,21 @@ export declare function cubicPoint(c: Cubic, t: number): Vec2;
|
|
|
29
29
|
* culling/picking prefilters.
|
|
30
30
|
*/
|
|
31
31
|
export declare function cubicAABB(c: Cubic): AABB;
|
|
32
|
+
export interface Tessellation {
|
|
33
|
+
/** Flattened polyline, interleaved [x0,y0, x1,y1, …] in the cubic's own space. */
|
|
34
|
+
points: Float32Array;
|
|
35
|
+
/** Cumulative arc length at each polyline point (same unit; [0] = 0). */
|
|
36
|
+
arcLength: Float32Array;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Adaptive cubic flattening + prefix arc length (2026-07-16, the @ice/ground
|
|
40
|
+
* wires pass: stroke tessellation and dash placement both ride this). Emit
|
|
41
|
+
* density follows curvature — flat spans emit one segment regardless of
|
|
42
|
+
* length; tolerance is in the cubic's OWN space (the ground renderer passes
|
|
43
|
+
* screen-px cubics with ~0.25 px tolerance, so density tracks zoom for free).
|
|
44
|
+
* `maxDepth` bounds pathological inputs (2^depth segments worst case).
|
|
45
|
+
*/
|
|
46
|
+
export declare function tessellateCubic(c: Cubic, tolerance?: number, maxDepth?: number): Tessellation;
|
|
32
47
|
/** Distance from a point to a line segment. */
|
|
33
48
|
export declare function distanceToSegment(px: number, py: number, ax: number, ay: number, bx: number, by: number): number;
|
|
34
49
|
/**
|
|
@@ -26,6 +26,21 @@ export declare function worldToScreen(worldX: number, worldY: number, camera: Ca
|
|
|
26
26
|
* Returns the new camera (does not mutate). Clamping is the caller's policy.
|
|
27
27
|
*/
|
|
28
28
|
export declare function zoomAtPoint(camera: CameraState, screenX: number, screenY: number, newZoom: number): CameraState;
|
|
29
|
+
/**
|
|
30
|
+
* The DEFAULT framing camera (2026-07-18, James: "do zoom to fit, but with a
|
|
31
|
+
* upper and bottom cap … should feel natural"): zoom-to-fit `content` (a
|
|
32
|
+
* world-space bbox) with `pad` px of breathing room on every side, zoom
|
|
33
|
+
* clamped into [minZoom, maxZoom] — the natural band: never so far in that
|
|
34
|
+
* one small card fills the screen, never so far out that the board turns
|
|
35
|
+
* into an ant farm. The content CENTER stays centered whether or not a cap
|
|
36
|
+
* engages. Callers own the band policy (engine default: FIT_DEFAULTS ∩ the
|
|
37
|
+
* session's hard CameraLimits) and must pass a real viewport.
|
|
38
|
+
*/
|
|
39
|
+
export declare function fitCamera(content: Rect, vpW: number, vpH: number, opts: {
|
|
40
|
+
pad: number;
|
|
41
|
+
minZoom: number;
|
|
42
|
+
maxZoom: number;
|
|
43
|
+
}): CameraState;
|
|
29
44
|
/**
|
|
30
45
|
* Camera → the ONE per-plane CSS transform (design-002 §5 `planeTransform`).
|
|
31
46
|
* Children are laid out in WORLD units inside the plane (left/top = world x/y),
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS-style cubic-bezier timing functions (pure math — kernel).
|
|
3
|
+
*
|
|
4
|
+
* `cubicBezierEase(x1, y1, x2, y2)` builds f(t): the exact curve CSS
|
|
5
|
+
* `transition-timing-function: cubic-bezier(…)` runs, so GL surfaces can
|
|
6
|
+
* match DOM transitions beat-for-beat (the composite-quad lift mirrors the
|
|
7
|
+
* DOM card lift's spring, cubic-bezier(0.2, 0.9, 0.3, 1.2)). y outside [0,1]
|
|
8
|
+
* (overshoot springs) is legal and expected; x1/x2 are clamped to [0,1] per
|
|
9
|
+
* spec so x(u) stays monotone and invertible.
|
|
10
|
+
*/
|
|
11
|
+
export declare function cubicBezierEase(x1: number, y1: number, x2: number, y2: number): (t: number) => number;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure auto-layout math (2026-07-17, James: cards dropped into a folder "just
|
|
3
|
+
* pile up on each other" — and, later, an explicit desktop-style Clean Up).
|
|
4
|
+
*
|
|
5
|
+
* Two primitives, two products, one law each:
|
|
6
|
+
* - `insertSlot` — place ONE newcomer near a drop hint without moving
|
|
7
|
+
* anything: newcomers get a free slot, incumbents never move. Consumed by
|
|
8
|
+
* the drop-consume path (l3-behave). Candidates are edge-aligned to the
|
|
9
|
+
* incumbents (flush right/below/left/above with a gutter), so successive
|
|
10
|
+
* drops grow aligned rows and columns instead of a global imposed grid.
|
|
11
|
+
* - `packLayout` — bottom-left candidate packing for the explicit
|
|
12
|
+
* "Clean Up" command (v2 2026-07-17, James: shelves were "not really
|
|
13
|
+
* efficient" and selection packs "overlay on top of unselected widgets"):
|
|
14
|
+
* items flow in reading order (current y, then x, then index) and each
|
|
15
|
+
* takes the lowest-then-leftmost free spot, so small cards fill the air
|
|
16
|
+
* beside tall ones instead of opening a new shelf. `obstacles` rects are
|
|
17
|
+
* honored as immovable — a scoped pack flows AROUND bystanders. Still
|
|
18
|
+
* "my mess, straightened": deterministic, anchored at the cluster's own
|
|
19
|
+
* top-left, idempotent on packed input (pinned by test).
|
|
20
|
+
* - `layerGraph` — the Unreal-Blueprint-style arrange for WIRED widgets:
|
|
21
|
+
* Sugiyama-lite layered layout (longest-path ranks along edge direction,
|
|
22
|
+
* barycenter ordering, columns left→right, vertically centered). Callers
|
|
23
|
+
* lay out each connected component with this and pack the resulting
|
|
24
|
+
* blocks with `packLayout`.
|
|
25
|
+
*
|
|
26
|
+
* Plain structs in, plain structs out (Law 13): no ECS, no DOM, no camera.
|
|
27
|
+
* All coordinates live in ONE frame — callers convert (container-local for
|
|
28
|
+
* folder drops, world for canvas clean-up) before and after.
|
|
29
|
+
*/
|
|
30
|
+
export interface LayoutRect {
|
|
31
|
+
readonly x: number;
|
|
32
|
+
readonly y: number;
|
|
33
|
+
readonly w: number;
|
|
34
|
+
readonly h: number;
|
|
35
|
+
}
|
|
36
|
+
export interface LayoutSize {
|
|
37
|
+
readonly w: number;
|
|
38
|
+
readonly h: number;
|
|
39
|
+
}
|
|
40
|
+
export interface LayoutPoint {
|
|
41
|
+
readonly x: number;
|
|
42
|
+
readonly y: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Nearest free slot to `hint` for a `size` newcomer among `incumbents`.
|
|
46
|
+
*
|
|
47
|
+
* The hint wins verbatim when it is already free (a deliberate drop into
|
|
48
|
+
* empty space is user intent — don't second-guess it). Otherwise candidates
|
|
49
|
+
* flush against each incumbent's edges (top- and far-aligned variants) are
|
|
50
|
+
* ranked by squared distance to the hint; ties resolve to the first-seen
|
|
51
|
+
* candidate (stable in incumbent order — deterministic across runs). The
|
|
52
|
+
* fallback — a spot gutter-right of EVERYTHING — cannot crowd anyone, so a
|
|
53
|
+
* slot always exists.
|
|
54
|
+
*/
|
|
55
|
+
export declare function insertSlot(incumbents: readonly LayoutRect[], size: LayoutSize, hint: LayoutPoint, gutter: number): {
|
|
56
|
+
x: number;
|
|
57
|
+
y: number;
|
|
58
|
+
};
|
|
59
|
+
export interface PackOpts {
|
|
60
|
+
readonly gutter: number;
|
|
61
|
+
/** Pack origin; defaults to the items' current bbox top-left (the cluster stays put). */
|
|
62
|
+
readonly origin?: LayoutPoint;
|
|
63
|
+
/**
|
|
64
|
+
* Band wrap width; defaults to a near-square derivation (√(total area × 1.6),
|
|
65
|
+
* never narrower than the widest item). Soft: an item that fits nowhere
|
|
66
|
+
* inside the band still places (lowest spot, band ignored) rather than
|
|
67
|
+
* failing.
|
|
68
|
+
*/
|
|
69
|
+
readonly maxWidth?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Immovable rects the pack must flow AROUND (e.g. unselected widgets when
|
|
72
|
+
* arranging a selection). Never returned, never moved, never crowded.
|
|
73
|
+
*/
|
|
74
|
+
readonly obstacles?: readonly LayoutRect[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Bottom-left candidate packing: items in reading order of their CURRENT
|
|
78
|
+
* positions (strict y, then x, then input index) each take the
|
|
79
|
+
* lowest-then-leftmost free spot from the candidate set (the origin, plus
|
|
80
|
+
* gutter-flush right-of and below-of every placed/obstacle rect, plus the
|
|
81
|
+
* band-left row start under each). Dense with mixed sizes — small cards fill
|
|
82
|
+
* the air beside tall ones — and obstacle-aware. Returns placements parallel
|
|
83
|
+
* to the input array.
|
|
84
|
+
*/
|
|
85
|
+
export declare function packLayout(items: readonly LayoutRect[], opts: PackOpts): Array<{
|
|
86
|
+
x: number;
|
|
87
|
+
y: number;
|
|
88
|
+
}>;
|
|
89
|
+
export interface LayerGraphOpts {
|
|
90
|
+
/** Horizontal gap between columns (leave room for wires to breathe). */
|
|
91
|
+
readonly gapX: number;
|
|
92
|
+
/** Vertical gap between nodes inside a column. */
|
|
93
|
+
readonly gapY: number;
|
|
94
|
+
/**
|
|
95
|
+
* Wrap the column run into stacked BANDS when the unwrapped width would
|
|
96
|
+
* exceed this (carriage-return style: each band flows left→right, bands
|
|
97
|
+
* stack top→down with a 2×gapY separator). Omit for a single band — a
|
|
98
|
+
* graph is inherently wide and only wraps when the caller says the canvas
|
|
99
|
+
* band demands it.
|
|
100
|
+
*/
|
|
101
|
+
readonly maxWidth?: number;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Layered graph layout (Sugiyama-lite) — the Unreal-Blueprint-style arrange
|
|
105
|
+
* for wired widgets. Rank = longest path from the sources along edge
|
|
106
|
+
* direction (a DFS drops back edges, so cycles are safe); columns flow
|
|
107
|
+
* left→right; nodes stack top→down within a column. Ordering = alternating
|
|
108
|
+
* barycenter sweeps with the BEST ordering kept (counted wire crossings),
|
|
109
|
+
* then the classic transpose refinement (adjacent swaps that strictly
|
|
110
|
+
* reduce crossings) — not provably minimal, but the standard practical
|
|
111
|
+
* pair. Columns center vertically on their band's tallest. Placements are
|
|
112
|
+
* relative to (0,0) — callers anchor the block. Deterministic: all
|
|
113
|
+
* orderings tie-break on node index.
|
|
114
|
+
*/
|
|
115
|
+
export declare function layerGraph(nodes: readonly LayoutSize[], edges: readonly (readonly [number, number])[], opts: LayerGraphOpts): Array<{
|
|
116
|
+
x: number;
|
|
117
|
+
y: number;
|
|
118
|
+
}>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nav-flight math (design-006 §2/§4 — the portal zoom, T1 camera-flight half).
|
|
3
|
+
*
|
|
4
|
+
* Pure math, no ECS/DOM. Conventions are coords.ts's (Law 13): camera {x,y} =
|
|
5
|
+
* world point at the viewport top-left, zoom = screen px per world unit,
|
|
6
|
+
* `screen = (world − cam)·zoom`.
|
|
7
|
+
*
|
|
8
|
+
* The portal embedding: a container's content coordinates are FRAME-LOCAL
|
|
9
|
+
* (design-001 §5.1) — the transition DEFINES the embedding by mapping the
|
|
10
|
+
* arrival camera's visible rect onto the container's body rect (`portalAffine`,
|
|
11
|
+
* aspect-fit, centers aligned). With that affine fixed, both transition layers
|
|
12
|
+
* ride ONE camera: the destination frame renders under `c(t)` directly; the
|
|
13
|
+
* departed frame renders under `c(t) ∘ A` where A maps departed-frame coords
|
|
14
|
+
* into destination-frame coords. Continuity at the cut is exact by
|
|
15
|
+
* construction (`solveFlightStart`), and no drift is possible mid-flight —
|
|
16
|
+
* there is no second clock.
|
|
17
|
+
*
|
|
18
|
+
* Numerics locked by the mock (2026-07-15, three rounds of field testing):
|
|
19
|
+
* - the progress spring uses the CLOSED-FORM critically-damped solution —
|
|
20
|
+
* semi-implicit Euler diverges once ω·dt nears 1 (a fast exit response on
|
|
21
|
+
* one dropped frame sent the mock's camera to zoom 1501);
|
|
22
|
+
* - the path interpolates zoom in LOG space (constant perceived zoom
|
|
23
|
+
* velocity — van Wijk & Nuij) with the view center linear in 1/zoom, which
|
|
24
|
+
* is exactly an anchored zoom whenever a fixed screen point exists;
|
|
25
|
+
* - flights beyond `maxOctaves` must be depth-capped (`capFlightStart`):
|
|
26
|
+
* sweeping a DOM plane's raster scale ~30× in one flight outruns
|
|
27
|
+
* Chromium's async tile re-raster (stale/missing tiles, persisting at
|
|
28
|
+
* rest) — the capped flight is presented as a crossfade-through-zoom.
|
|
29
|
+
*/
|
|
30
|
+
import type { CameraState } from "./coords";
|
|
31
|
+
import type { Rect } from "./shapes";
|
|
32
|
+
/** Uniform scale + translate: `A(q) = o + q·s`. */
|
|
33
|
+
export interface PortalAffine {
|
|
34
|
+
readonly s: number;
|
|
35
|
+
readonly ox: number;
|
|
36
|
+
readonly oy: number;
|
|
37
|
+
}
|
|
38
|
+
/** The world rect a camera shows through a viewport. */
|
|
39
|
+
export declare function visibleRect(cam: CameraState, vpW: number, vpH: number): Rect;
|
|
40
|
+
/**
|
|
41
|
+
* The portal embedding: map `arrival` (the world rect the arrival camera will
|
|
42
|
+
* show, in the CHILD frame's coords) onto `portal` (the container's body rect,
|
|
43
|
+
* in the PARENT frame's coords). Aspect-FIT with centers aligned (design-006
|
|
44
|
+
* §8.3 — the card chrome absorbs the letterbox).
|
|
45
|
+
*/
|
|
46
|
+
export declare function portalAffine(arrival: Rect, portal: Rect): PortalAffine;
|
|
47
|
+
export declare function invertAffine(a: PortalAffine): PortalAffine;
|
|
48
|
+
/** `(outer ∘ inner)(q) = outer(inner(q))` — for multi-level exit portals. */
|
|
49
|
+
export declare function composeAffine(outer: PortalAffine, inner: PortalAffine): PortalAffine;
|
|
50
|
+
/**
|
|
51
|
+
* Continuity solve: the camera `c0` (in the DESTINATION frame) under which
|
|
52
|
+
* departed-frame content mapped through `A` (departed → destination coords)
|
|
53
|
+
* renders EXACTLY as it did under `camPre` (in the departed frame) the instant
|
|
54
|
+
* before the cut. Derivation: a departed point q renders at
|
|
55
|
+
* `(A(q) − c0)·z0 = q·(A.s·z0) + (A.o − c0)·z0`; matching `(q − camPre)·zPre`
|
|
56
|
+
* coefficient-wise gives z0 and the origin below. Zoom limits deliberately do
|
|
57
|
+
* NOT apply here — flight paths transit far outside the user-gesture band
|
|
58
|
+
* (design-006 §3.1); only the ARRIVAL camera is clamped by the caller.
|
|
59
|
+
*/
|
|
60
|
+
export declare function solveFlightStart(A: PortalAffine, camPre: CameraState): CameraState;
|
|
61
|
+
/** Zoom distance in octaves — the flight-length measure (design-006 §4/§5). */
|
|
62
|
+
export declare function flightOctaves(from: CameraState, to: CameraState): number;
|
|
63
|
+
/**
|
|
64
|
+
* Depth-cap a flight start (design-006 §5): beyond `maxOctaves`, clamp the
|
|
65
|
+
* start zoom to `capFactor ×` the arrival zoom (dive direction preserved),
|
|
66
|
+
* keeping the exact start's view CENTER — the portal anchor — so the motion
|
|
67
|
+
* still comes from/goes to the right place. `capped: true` means the caller
|
|
68
|
+
* must present the flight as a crossfade (frozen departing cover + incoming
|
|
69
|
+
* fade-in): geometric continuity is unpresentable at that raster-scale swing.
|
|
70
|
+
*/
|
|
71
|
+
export declare function capFlightStart(exact: CameraState, arrival: CameraState, vpW: number, vpH: number, maxOctaves: number, capFactor: number): {
|
|
72
|
+
c0: CameraState;
|
|
73
|
+
capped: boolean;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* The flight path `c(p)`, p ∈ [0,1]: zoom log-lerped (constant perceived zoom
|
|
77
|
+
* velocity); view CENTER linear in 1/zoom — exactly an anchored zoom whenever
|
|
78
|
+
* a fixed screen point exists between the endpoints, a graceful blend
|
|
79
|
+
* otherwise. Endpoints are exact: c(0) = c0, c(1) = c1.
|
|
80
|
+
*/
|
|
81
|
+
export declare function flightCamera(c0: CameraState, c1: CameraState, p: number, vpW: number, vpH: number): CameraState;
|
|
82
|
+
/**
|
|
83
|
+
* One step of the critically-damped progress spring toward 1, in CLOSED FORM:
|
|
84
|
+
* with u = 1 − p, the exact solution is `u(t) = (u₀ + Bt)·e^(−ωt)`,
|
|
85
|
+
* `B = −v₀ + ω·u₀`. Unconditionally stable at ANY dt — the discretized form
|
|
86
|
+
* (semi-implicit Euler) diverges once ω·dt approaches 1, which one dropped
|
|
87
|
+
* frame at a fast response reaches easily (mock-measured, 2026-07-15).
|
|
88
|
+
* From rest (v=0) the approach is monotonic: p never overshoots 1.
|
|
89
|
+
*/
|
|
90
|
+
export declare function springStep(p: number, v: number, omega: number, dtSec: number): {
|
|
91
|
+
p: number;
|
|
92
|
+
v: number;
|
|
93
|
+
};
|
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Owns everything ENGINE-facing: the module-side island state, the island
|
|
5
5
|
* registry (scene+camera handles the frame pass iterates), per-island dirt
|
|
6
|
-
* observers, the `r3fInvalidator`
|
|
7
|
-
* render write trap, and entity-death cleanup. Everything
|
|
8
|
-
* pool, quads, the actual passes) lives in `GLViews`, whose
|
|
9
|
-
* Canvas/context's — the bridge outlives Canvas remounts
|
|
10
|
-
* the
|
|
6
|
+
* observers, the `r3fInvalidator`/`r3fAdvance` reflectors, `useIslandFrame`
|
|
7
|
+
* callbacks, the render write trap, and entity-death cleanup. Everything
|
|
8
|
+
* GL-facing (FBO pool, quads, the actual passes) lives in `GLViews`, whose
|
|
9
|
+
* lifetime is the Canvas/context's — the bridge outlives Canvas remounts
|
|
10
|
+
* (HMR) and carries the frame wiring across them.
|
|
11
11
|
*
|
|
12
12
|
* Two-level invalidation, mechanized:
|
|
13
13
|
* - COMPOSITE dirt (cheap — re-run the frame pass, sample cached textures):
|
|
14
|
-
* camera/viewport via the reflector; per-island Position
|
|
14
|
+
* camera/viewport via the reflector; per-island Position Tier-3; the frame
|
|
15
|
+
* ChildOf reorder wake (petition 8 — quad renderOrder is sibling order);
|
|
15
16
|
* island enter/exit via register/unregister. All call `requestFrame()`.
|
|
16
17
|
* - ISLAND dirt (expensive — repaint the FBO): per-island props-group and
|
|
17
18
|
* Size Tier-3 → `bumpPaint()` + `requestFrame()`; the animation signal
|
|
@@ -20,11 +21,28 @@
|
|
|
20
21
|
* `Camera.gesturing`; the gesture-end camera write re-fires the reflector,
|
|
21
22
|
* so the once-at-gesture-end repaint needs no extra machinery).
|
|
22
23
|
*
|
|
24
|
+
* REFLECT-PHASE ADVANCE (design-004 §3 amendment, 2026-07-20 — the pan-lag
|
|
25
|
+
* fix): the composite render is driven FROM the engine's reflect phase, not
|
|
26
|
+
* from R3F's demand loop. `requestFrame()` only latches `wantsFrame`; the
|
|
27
|
+
* `r3fAdvance` reflector (`always: true` — registered right after the
|
|
28
|
+
* observed invalidator so same-flush dirt is consumed) calls the wired
|
|
29
|
+
* `renderNow` (R3F `advance`) synchronously. DOM plane transforms and the GL
|
|
30
|
+
* composite therefore commit in the SAME task and present in the same frame.
|
|
31
|
+
* The prior wiring (`invalidate()` → R3F's own rAF) could only arm R3F for
|
|
32
|
+
* the NEXT frame, and rAF registration order between the two loops is sticky
|
|
33
|
+
* luck — field-measured on 2026-07-20: R3F rendering the PREVIOUS frame's
|
|
34
|
+
* camera while the DOM plane showed the current one, a permanent one-frame
|
|
35
|
+
* smear under fast pans. Ambient animation rides the same seam: the pass
|
|
36
|
+
* reports its self-sustain plan via `schedulePass`, and the due-time check
|
|
37
|
+
* replaces the old wake-up timer (the engine loop runs every frame anyway).
|
|
38
|
+
* The Canvas must mount `frameloop="never"` — a self-scheduling loop would
|
|
39
|
+
* re-render out of phase again.
|
|
40
|
+
*
|
|
23
41
|
* Runtime-three-free (type-only imports): unit tests drive the bridge with a
|
|
24
42
|
* real world and fake scene/camera handles, no GL context.
|
|
25
43
|
*/
|
|
26
44
|
import type { OrthographicCamera, Scene } from "three";
|
|
27
|
-
import { type Engine, type Entity } from "@ice/core";
|
|
45
|
+
import { type Engine, type Entity, type SiblingOrderIndex } from "@ice/core";
|
|
28
46
|
import { type IslandStateStore } from "./island-state";
|
|
29
47
|
import { type RenderWriteTrap } from "./dev-write-trap";
|
|
30
48
|
export interface IslandHandle {
|
|
@@ -36,11 +54,22 @@ export type IslandFrameCallback = (dtMs: number) => void;
|
|
|
36
54
|
export interface GLBridgeOpts {
|
|
37
55
|
/** Arm the zero-render→ECS-writes trap (DEV builds / traces). Default false. */
|
|
38
56
|
readonly devAssertRenderWrites?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Injected monotonic clock for the ambient-animation due-time (tests pass a
|
|
59
|
+
* stub — the pool's injected-`now` precedent). Default `performance.now`.
|
|
60
|
+
*/
|
|
61
|
+
readonly now?: () => number;
|
|
39
62
|
}
|
|
40
63
|
export interface GLBridge {
|
|
41
64
|
readonly engine: Engine;
|
|
42
65
|
readonly state: IslandStateStore;
|
|
43
66
|
readonly renderAssert: RenderWriteTrap;
|
|
67
|
+
/**
|
|
68
|
+
* The frame ordinal cache (petition 8): the compositor pass reads quad
|
|
69
|
+
* renderOrder from it, and its `ordinals()` read resets the staleness the
|
|
70
|
+
* bridge's reorder wake filters on — bridge and pass MUST share it.
|
|
71
|
+
*/
|
|
72
|
+
readonly order: SiblingOrderIndex;
|
|
44
73
|
/**
|
|
45
74
|
* Island mount (the `<Island>` component): registers the scene/camera pair,
|
|
46
75
|
* stamps `animatedDecl` from the widget type, and arms the per-island dirt
|
|
@@ -54,14 +83,46 @@ export interface GLBridge {
|
|
|
54
83
|
/** Subscribe a paint-time callback; holds an animation ref while live. */
|
|
55
84
|
addFrameCallback(entity: Entity, cb: IslandFrameCallback): () => void;
|
|
56
85
|
frameCallbacksFor(entity: Entity): readonly IslandFrameCallback[];
|
|
57
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Composite invalidation — latch a frame pass (coalesced). Consumed by the
|
|
88
|
+
* `r3fAdvance` reflector on the NEXT engine flush: dirt raised during a
|
|
89
|
+
* step (observers, the invalidator reflector) renders the SAME frame; dirt
|
|
90
|
+
* raised outside a step (React effects) renders on the next tick.
|
|
91
|
+
*/
|
|
58
92
|
requestFrame(): void;
|
|
59
93
|
/** Island invalidation — this island must repaint (props/size/manual dirt). */
|
|
60
94
|
bumpPaint(entity: Entity): void;
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Composite-quad lift scale (1 = rest). Scales the CARD on the canvas,
|
|
97
|
+
* corners intact. Sets the TARGET: the compositor pass eases the drawn
|
|
98
|
+
* value there over `durationMs` (default 180 — the DOM card-lift spring;
|
|
99
|
+
* 0 snaps). Mid-flight retargets restart from the current drawn value.
|
|
100
|
+
*/
|
|
101
|
+
setCompositeScale(entity: Entity, scale: number, durationMs?: number): void;
|
|
102
|
+
/**
|
|
103
|
+
* Composite-quad opacity (1 = rest) — the lift's fade twin. MULTIPLIES the
|
|
104
|
+
* widget's durable `Opacity` cell in the pass (presentation-transient over
|
|
105
|
+
* the doc fact). Sets the TARGET: the pass eases the drawn value there over
|
|
106
|
+
* `durationMs` (default 180; 0 snaps); mid-flight retargets restart from the
|
|
107
|
+
* current drawn value.
|
|
108
|
+
*/
|
|
109
|
+
setCompositeOpacity(entity: Entity, opacity: number, durationMs?: number): void;
|
|
110
|
+
/**
|
|
111
|
+
* GLViews wires R3F's `advance` here (null on unmount). Called ONLY by the
|
|
112
|
+
* `r3fAdvance` reflector, synchronously inside the engine's reflect phase —
|
|
113
|
+
* the same task the DOM reflectors write in, so both surfaces present the
|
|
114
|
+
* same camera. While unwired, latched dirt survives and renders on the
|
|
115
|
+
* first flush after (re)mount.
|
|
116
|
+
*/
|
|
117
|
+
setRenderNow(fn: ((nowMs: number) => void) | null): void;
|
|
118
|
+
/**
|
|
119
|
+
* The pass reports how the frame loop continues (`selfSustainPlan`):
|
|
120
|
+
* `"now"` latches an immediate next frame (lift eases, stagger backlogs —
|
|
121
|
+
* native refresh); a number arms the ambient-animation due-time (the
|
|
122
|
+
* `maxAnimationFps` cap — replaces the old wake-up timer); `"none"` parks.
|
|
123
|
+
* Each pass re-reports, so the due-time is always the LAST pass's plan.
|
|
124
|
+
*/
|
|
125
|
+
schedulePass(plan: "none" | "now" | number): void;
|
|
65
126
|
/** Tear down reflector + observers + state (doc close / app teardown). */
|
|
66
127
|
uninstall(): void;
|
|
67
128
|
}
|
|
@@ -9,9 +9,18 @@ import type { Texture } from "three";
|
|
|
9
9
|
export declare class CompositeMaterial extends ShaderMaterial {
|
|
10
10
|
private readonly mapUniform;
|
|
11
11
|
private readonly opacityUniform;
|
|
12
|
+
private readonly draggedRectUniform;
|
|
13
|
+
private readonly isDraggedUniform;
|
|
12
14
|
constructor();
|
|
13
15
|
/** Bind the island's FBO texture (null clears). */
|
|
14
16
|
setMap(map: Texture | null): void;
|
|
15
17
|
/** Composite opacity. Pass-through — no clamping; callers pass valid 0..1. */
|
|
16
18
|
setOpacity(opacity: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* Drag clip, composite-space min/max (v1's uDraggedRect re-cut from screen
|
|
21
|
+
* space). (0,0,0,0) = inactive. Mutates the vec4 in place — no allocation.
|
|
22
|
+
*/
|
|
23
|
+
setDraggedRect(minX: number, minY: number, maxX: number, maxY: number): void;
|
|
24
|
+
/** True on the dragged card's own quad — exempt from the clip. */
|
|
25
|
+
setIsDragged(isDragged: boolean): void;
|
|
17
26
|
}
|
|
@@ -21,6 +21,17 @@ export interface QuadLike {
|
|
|
21
21
|
setTexture(t: unknown): void;
|
|
22
22
|
setVisible(v: boolean): void;
|
|
23
23
|
setRenderOrder(n: number): void;
|
|
24
|
+
/**
|
|
25
|
+
* Composite opacity, 0..1 (design-004 §3: the neutral composite's whole
|
|
26
|
+
* per-widget fact) — the pass passes `Opacity` cell × eased fade channel.
|
|
27
|
+
*/
|
|
28
|
+
setOpacity(o: number): void;
|
|
29
|
+
/**
|
|
30
|
+
* Drag clip (v1 RFC-003, composite-space): non-exempt quads discard
|
|
31
|
+
* fragments inside the rect so a dragged card's P1 DOM chrome shows
|
|
32
|
+
* through. (0,0,0,0, false) = inactive.
|
|
33
|
+
*/
|
|
34
|
+
setDragClip(minX: number, minY: number, maxX: number, maxY: number, exempt: boolean): void;
|
|
24
35
|
}
|
|
25
36
|
export interface QuadsLike {
|
|
26
37
|
ensure(key: number): QuadLike;
|
|
@@ -70,6 +81,15 @@ export interface PassContext {
|
|
|
70
81
|
readonly compositeScene: object;
|
|
71
82
|
readonly maxFboBytes: number;
|
|
72
83
|
readonly maxRepaintsPerFrame: number;
|
|
84
|
+
/**
|
|
85
|
+
* Idle ceiling on island paint resolution (design-004 §3 amendment,
|
|
86
|
+
* 2026-07-14): new paints allocate at `min(pixelRatio, maxPaintDpr) × band`.
|
|
87
|
+
* On a dpr-2 display the composite's bilinear upscale from a 1.5× texture is
|
|
88
|
+
* visually indistinguishable (A/B knot crops) while Hot repaint cost drops
|
|
89
|
+
* ~22 %. Composes with the gesture drop (pixelRatio is already 1 while
|
|
90
|
+
* gesturing) and with zoom bands. `Infinity` = uncapped.
|
|
91
|
+
*/
|
|
92
|
+
readonly maxPaintDpr: number;
|
|
73
93
|
readonly dtMs: number;
|
|
74
94
|
}
|
|
75
95
|
export interface PassStats {
|
|
@@ -79,5 +99,7 @@ export interface PassStats {
|
|
|
79
99
|
evicted: number;
|
|
80
100
|
fboBytes: number;
|
|
81
101
|
anyHot: boolean;
|
|
102
|
+
/** A composite ease (lift scale or fade) is mid-flight — caller re-invalidates (composite-only frames). */
|
|
103
|
+
liftAnimating: boolean;
|
|
82
104
|
}
|
|
83
105
|
export declare function runCompositorPass(ctx: PassContext): PassStats;
|
|
@@ -10,6 +10,22 @@ export interface GLViewsProps {
|
|
|
10
10
|
readonly maxFboBytes?: number;
|
|
11
11
|
/** Repaint stagger per composited frame (v1 default 4). */
|
|
12
12
|
readonly maxRepaintsPerFrame?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Ambient-animation rate cap (default 60; `Infinity` = uncapped). Applies
|
|
15
|
+
* ONLY to self-sustained frames — Hot islands and stagger backlogs park the
|
|
16
|
+
* loop between paints instead of spinning at native refresh (5 Hot islands
|
|
17
|
+
* at 120 Hz dpr-2 measured 96 % GPU duty at idle, 2026-07-14). Lift eases
|
|
18
|
+
* and externally invalidated frames (camera, drags, props dirt) still run
|
|
19
|
+
* at display rate. Approximate: frames land on the vsync grid.
|
|
20
|
+
*/
|
|
21
|
+
readonly maxAnimationFps?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Idle paint-DPR ceiling (default 1.5; `Infinity` = uncapped): island FBOs
|
|
24
|
+
* allocate at `min(pixelRatio, maxPaintDpr) × band`. On dpr-2 displays the
|
|
25
|
+
* composite's bilinear upscale from 1.5× is visually indistinguishable
|
|
26
|
+
* (2026-07-14 A/B) and cuts Hot repaint cost ~22 %.
|
|
27
|
+
*/
|
|
28
|
+
readonly maxPaintDpr?: number;
|
|
13
29
|
/**
|
|
14
30
|
* Shared IBL: stamped as `scene.environment` on EVERY island's private
|
|
15
31
|
* scene (arrival/change repaints all islands). Load it app-side (e.g.
|
|
@@ -17,5 +33,64 @@ export interface GLViewsProps {
|
|
|
17
33
|
* fine; islands paint unlit-by-IBL and repaint when it lands.
|
|
18
34
|
*/
|
|
19
35
|
readonly environment?: Texture | null;
|
|
36
|
+
/**
|
|
37
|
+
* GL frame profiling (2026-07-13): fires once per composited frame with the
|
|
38
|
+
* pass's real costs. CPU/counts/pass-shape are engine-measured every
|
|
39
|
+
* profiled frame; GPU ms rides stats-gl's headless `StatsProfiler`
|
|
40
|
+
* (EXT_disjoint_timer_query / WebGPU timestamps — 0 where unsupported, e.g.
|
|
41
|
+
* Safari without its flag), DYNAMICALLY imported on first use so the
|
|
42
|
+
* dependency costs nothing when profiling is off. Feed the numbers to the
|
|
43
|
+
* devtools profiler HUD as lanes (`devtools.lane("gpu", s.gpuMs)`).
|
|
44
|
+
*/
|
|
45
|
+
readonly onFrameStats?: (stats: GlFrameStats) => void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* One profiled composite frame — see {@link GLViewsProps.onFrameStats}.
|
|
49
|
+
* MIRRORED structurally as `GlPanelStats` in @ice/devtools (which cannot
|
|
50
|
+
* import r3f under the walls) — keep the shapes aligned.
|
|
51
|
+
*/
|
|
52
|
+
export interface GlFrameStats {
|
|
53
|
+
/** Whole-pass main-thread ms (reconcile + island paints + composite). */
|
|
54
|
+
readonly cpuMs: number;
|
|
55
|
+
/** GPU ms summed across the pass's render calls (0 until queries resolve / unsupported). */
|
|
56
|
+
readonly gpuMs: number;
|
|
57
|
+
/** stats-gl smoothed fps (0 until the profiler is live). */
|
|
58
|
+
readonly fps: number;
|
|
59
|
+
/** `renderer.info` aggregated across ALL of this pass's render calls. */
|
|
60
|
+
readonly drawCalls: number;
|
|
61
|
+
readonly triangles: number;
|
|
62
|
+
readonly points: number;
|
|
63
|
+
readonly lines: number;
|
|
64
|
+
/** Live compiled shader programs (renderer-lifetime, not per-frame). */
|
|
65
|
+
readonly programs: number;
|
|
66
|
+
readonly geometries: number;
|
|
67
|
+
readonly textures: number;
|
|
68
|
+
/** Virtual texturing: live island render targets + their summed resolution. */
|
|
69
|
+
readonly renderTargets: number;
|
|
70
|
+
readonly renderMegaPixels: number;
|
|
71
|
+
readonly fboBytes: number;
|
|
72
|
+
readonly fboBudgetBytes: number;
|
|
73
|
+
/** Island phase census (the demand/retention state machine, design-004 §3/§7). */
|
|
74
|
+
readonly islands: {
|
|
75
|
+
readonly total: number;
|
|
76
|
+
readonly hot: number;
|
|
77
|
+
readonly warm: number;
|
|
78
|
+
readonly waking: number;
|
|
79
|
+
readonly cold: number;
|
|
80
|
+
readonly dormant: number;
|
|
81
|
+
};
|
|
82
|
+
/** Live targets per painted zoom band ("×1", "×0.5", …) — the LOD downgrade census. */
|
|
83
|
+
readonly bandHistogram: Readonly<Record<string, number>>;
|
|
84
|
+
/** This pass's shape (PassStats). */
|
|
85
|
+
readonly repainted: number;
|
|
86
|
+
readonly pendingPaints: number;
|
|
87
|
+
readonly evicted: number;
|
|
88
|
+
/** LOD context: camera zoom → hysteresis band → the DPR new paints get. */
|
|
89
|
+
readonly zoom: number;
|
|
90
|
+
readonly band: number;
|
|
91
|
+
readonly effectiveDpr: number;
|
|
92
|
+
/** Scene population: Visible vs Culled widgets (all surfaces). */
|
|
93
|
+
readonly visibleWidgets: number;
|
|
94
|
+
readonly culledWidgets: number;
|
|
20
95
|
}
|
|
21
|
-
export declare function GLViews({ engine, bridge, store, maxFboBytes, maxRepaintsPerFrame, environment, }: GLViewsProps): ReactElement;
|
|
96
|
+
export declare function GLViews({ engine, bridge, store, maxFboBytes, maxRepaintsPerFrame, maxAnimationFps, maxPaintDpr, environment, onFrameStats, }: GLViewsProps): ReactElement;
|
|
@@ -56,5 +56,15 @@ export interface GLPointerRouterDeps {
|
|
|
56
56
|
export interface GLPointerRouter {
|
|
57
57
|
/** The adapter's `glRoute` seam. True = island content claimed the event. */
|
|
58
58
|
route(kind: "down" | "move" | "up" | "cancel", screenX: number, screenY: number, native: PointerEvent): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Hover-time verdict as of the LAST routed `move` (design-002 §8 amendment,
|
|
61
|
+
* 2026-07-18): the pick chain under the pointer holds claim-capable island
|
|
62
|
+
* content — a mesh whose handlers include `onPointerDown`/`onClick` (the
|
|
63
|
+
* claim/click paths; hover-only handlers like the shapes swarm's move reader
|
|
64
|
+
* do NOT count — they never opt a down out). True for the whole life of an
|
|
65
|
+
* island capture. Feed it into the adapter's `GLRouteVerdict.overInteractive`
|
|
66
|
+
* on moves; presentation truth only, never a routing input.
|
|
67
|
+
*/
|
|
68
|
+
overInteractive(): boolean;
|
|
59
69
|
}
|
|
60
70
|
export declare function createGLPointerRouter({ world, bridge, index }: GLPointerRouterDeps): GLPointerRouter;
|
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
export { createGLBridge, type GLBridge, type GLBridgeOpts, type IslandHandle, type IslandFrameCallback } from "./bridge";
|
|
7
7
|
export { createIslandStateStore, type IslandRenderState, type IslandStateStore, type PaintedAt } from "./island-state";
|
|
8
8
|
export { runCompositorPass, type CompCameraLike, type GlLike, type IslandCameraLike, type PassContext, type PassStats, type PoolLike, type QuadLike, type QuadsLike, type TargetLike, } from "./compositor-pass";
|
|
9
|
-
export { GLViews, type GLViewsProps } from "./gl-root";
|
|
9
|
+
export { GLViews, type GLViewsProps, type GlFrameStats } from "./gl-root";
|
|
10
10
|
export { Island, type IslandProps } from "./island";
|
|
11
|
-
export { IslandContext, useIslandContext, useIslandFrame, useIslandInvalidate, useIslandLift, type IslandContextValue, } from "./use-island-frame";
|
|
11
|
+
export { IslandContext, useIslandContext, useIslandFrame, useIslandInvalidate, useIslandLift, useIslandOpacity, type IslandContextValue, } from "./use-island-frame";
|
|
12
12
|
export { RenderTargetPool, type PoolEntryInfo } from "./pool";
|
|
13
13
|
export { ResourceRegistry } from "./resource-registry";
|
|
14
14
|
export { CompositeMaterial } from "./composite-material";
|
|
15
15
|
export { createRenderWriteTrap, type RenderWriteTrap } from "./dev-write-trap";
|
|
16
16
|
export { createGLPointerRouter, type GLPointerRouter, type GLPointerRouterDeps, type IslandPointerEvent, } from "./gl-router";
|
|
17
|
+
export { captureWidgetPreviews, type CapturePreviewOpts } from "./preview-capture";
|