@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
|
@@ -39,14 +39,58 @@ export interface IslandRenderState {
|
|
|
39
39
|
* every pass (5 Hot vs cap 4 froze the shapes card, 2026-07-12).
|
|
40
40
|
*/
|
|
41
41
|
lastPaintSeq: number;
|
|
42
|
+
/**
|
|
43
|
+
* Animation time OWED to this island's `useIslandFrame` callbacks: every
|
|
44
|
+
* pass a Hot island is paint-eligible adds the pass dtMs; the paint that
|
|
45
|
+
* finally runs delivers the whole balance and zeroes it. Without this, a
|
|
46
|
+
* stagger-deferred island simply LOST the deferred passes' time — 5 Hot vs
|
|
47
|
+
* cap 4 ran every animation at 4/5 speed (2026-07-20 rate probe), violating
|
|
48
|
+
* design-004's "the cap changes cadence, never speed". Freeze semantics are
|
|
49
|
+
* preserved: frozen islands are not paint-eligible, so a stage hold still
|
|
50
|
+
* PAUSES animation rather than banking it.
|
|
51
|
+
*/
|
|
52
|
+
pendingDtMs: number;
|
|
42
53
|
/**
|
|
43
54
|
* Composite-quad scale (lift-on-hold): scales the CARD on the canvas —
|
|
44
55
|
* texture, rounded alpha corners and all — instead of the scene content
|
|
45
56
|
* (which the card-sized frustum would crop into hard corners, 2026-07-12
|
|
46
57
|
* field report). ≠1 also pops render order over neighbors.
|
|
58
|
+
*
|
|
59
|
+
* This is the DRAWN value: the compositor pass eases it toward
|
|
60
|
+
* `liftTarget` (dtMs-driven spring). GL widgets have no CSS transitions, so
|
|
61
|
+
* the DOM-lift parity (2026-07-13 field report: "GL lift snaps") lives in
|
|
62
|
+
* the pass, not in userland.
|
|
47
63
|
*/
|
|
48
64
|
compositeScale: number;
|
|
65
|
+
/** Where the lift is headed; `setCompositeScale` retargets from the drawn value. */
|
|
66
|
+
liftTarget: number;
|
|
67
|
+
/** Drawn value at (re)target time — the ease runs liftFrom → liftTarget. */
|
|
68
|
+
liftFrom: number;
|
|
69
|
+
/** Accumulated pass dtMs since the last retarget; ≥ liftMs ⇒ settled. */
|
|
70
|
+
liftElapsedMs: number;
|
|
71
|
+
/** Transition duration in ms; 0 = snap (the pre-ease behavior). */
|
|
72
|
+
liftMs: number;
|
|
73
|
+
/**
|
|
74
|
+
* Composite-quad opacity channel (the lift's fade twin): the DRAWN value the
|
|
75
|
+
* pass eases toward `opacityTarget` and MULTIPLIES with the widget's durable
|
|
76
|
+
* `Opacity` cell — presentation-transient (chrome-grade, e.g. the drag-lift
|
|
77
|
+
* fade) stacked over the doc-level fact, never written to ECS.
|
|
78
|
+
*/
|
|
79
|
+
compositeOpacity: number;
|
|
80
|
+
/** Where the fade is headed; `setCompositeOpacity` retargets from the drawn value. */
|
|
81
|
+
opacityTarget: number;
|
|
82
|
+
/** Drawn value at (re)target time — the ease runs opacityFrom → opacityTarget. */
|
|
83
|
+
opacityFrom: number;
|
|
84
|
+
/** Accumulated pass dtMs since the last retarget; ≥ opacityMs ⇒ settled. */
|
|
85
|
+
opacityElapsedMs: number;
|
|
86
|
+
/** Transition duration in ms; 0 = snap. */
|
|
87
|
+
opacityMs: number;
|
|
49
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Default lift transition — the GL twin of the DOM card-lift idiom (180ms
|
|
91
|
+
* springy pop; the curve itself lives in the compositor pass).
|
|
92
|
+
*/
|
|
93
|
+
export declare const LIFT_DEFAULT_MS = 180;
|
|
50
94
|
export interface IslandStateStore {
|
|
51
95
|
/** Get-or-create (Cold, unpainted). */
|
|
52
96
|
ensure(key: number): IslandRenderState;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Texture, WebGLRenderer } from "three";
|
|
2
|
+
export interface CapturePreviewOpts {
|
|
3
|
+
/** Types to capture (default: every registered gl-surface widget). */
|
|
4
|
+
readonly types?: readonly string[];
|
|
5
|
+
/**
|
|
6
|
+
* Shared IBL. Prefer the FACTORY form: PMREM textures live on the GPU of
|
|
7
|
+
* the renderer that generated them (no CPU image), so the app's main-canvas
|
|
8
|
+
* env is EMPTY on the capture renderer (field bug 2026-07-19 — black
|
|
9
|
+
* metals). The factory receives the capture renderer and builds the env
|
|
10
|
+
* there (e.g. `(gl) => new PMREMGenerator(gl).fromScene(new RoomEnvironment(), 0.04).texture`).
|
|
11
|
+
* A plain Texture is accepted for same-renderer/CPU-backed cases.
|
|
12
|
+
*/
|
|
13
|
+
readonly environment?: Texture | ((gl: WebGLRenderer) => Texture | null) | null;
|
|
14
|
+
/** Pixel density of the capture (default: fit 512px on the long side, ≤2). */
|
|
15
|
+
readonly scale?: number;
|
|
16
|
+
/** Frames advanced before the capture (animations reach a live pose). */
|
|
17
|
+
readonly settleFrames?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function captureWidgetPreviews(opts?: CapturePreviewOpts): Promise<string[]>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-sustain scheduling for the demand loop (design-004 §3 animation-rate
|
|
3
|
+
* amendment, 2026-07-14).
|
|
4
|
+
*
|
|
5
|
+
* When the ONLY reason to run another frame is ambient animation (Hot islands
|
|
6
|
+
* or a stagger backlog), the loop no longer re-invalidates at native refresh:
|
|
7
|
+
* on a 120 Hz panel that was 5 always-Hot islands × 120 paints/sec ≈ 96 % GPU
|
|
8
|
+
* duty at idle — the fan. Instead the next pass is scheduled `1000/maxFps` ms
|
|
9
|
+
* after the last pass STARTED, so ambient animation runs at ~`maxFps` and the
|
|
10
|
+
* GPU idles between frames.
|
|
11
|
+
*
|
|
12
|
+
* What stays at native refresh:
|
|
13
|
+
* - lift eases (`liftAnimating`) — 180 ms of interaction feedback;
|
|
14
|
+
* - every EXTERNALLY invalidated frame (camera, drag, props dirt): the
|
|
15
|
+
* bridge's `requestFrame` path is untouched, so interaction still runs at
|
|
16
|
+
* display rate and Hot islands ride along uncapped while it lasts.
|
|
17
|
+
*
|
|
18
|
+
* The cap is approximate: the wake-up is a timeout and the actual frame lands
|
|
19
|
+
* on the next vsync after it. {@link RATE_CAP_SLACK_MS} pulls the timeout half
|
|
20
|
+
* a 120 Hz vsync early so the following vsync is the intended one, not one
|
|
21
|
+
* past it (naive `interval` scheduling on a 120 Hz grid yields 40 fps, not 60).
|
|
22
|
+
*/
|
|
23
|
+
/** Timeout lead so the post-timeout vsync lands on the target boundary. */
|
|
24
|
+
export declare const RATE_CAP_SLACK_MS = 4;
|
|
25
|
+
export interface SelfSustainStats {
|
|
26
|
+
readonly anyHot: boolean;
|
|
27
|
+
readonly pendingPaints: number;
|
|
28
|
+
readonly liftAnimating: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Decide how the demand loop continues after a pass.
|
|
32
|
+
*
|
|
33
|
+
* @param stats The pass's self-sustain signals.
|
|
34
|
+
* @param elapsedMs ms since the pass started (schedule anchor).
|
|
35
|
+
* @param maxFps Animation rate cap; `Infinity` restores uncapped.
|
|
36
|
+
* @returns `"none"` (park), `"now"` (invalidate immediately), or the timeout
|
|
37
|
+
* delay in ms before invalidating.
|
|
38
|
+
*/
|
|
39
|
+
export declare function selfSustainPlan(stats: SelfSustainStats, elapsedMs: number, maxFps: number): "none" | "now" | number;
|
|
@@ -20,5 +20,20 @@ export declare function useIslandInvalidate(): () => void;
|
|
|
20
20
|
* corners together) and pops it over neighbors while ≠1. Scaling scene content
|
|
21
21
|
* instead crops the corners at the card-sized frustum — the 2026-07-12
|
|
22
22
|
* hard-corner field report. Resets to 1 on unmount.
|
|
23
|
+
*
|
|
24
|
+
* The scale EASES to each new value (default 180ms, the DOM card-lift
|
|
25
|
+
* spring — GL has no CSS transitions, so the compositor runs the curve;
|
|
26
|
+
* 2026-07-13 field report: "GL lift snaps while DOM lifts smooth").
|
|
27
|
+
* `durationMs: 0` snaps.
|
|
23
28
|
*/
|
|
24
|
-
export declare function useIslandLift(scale: number): void;
|
|
29
|
+
export declare function useIslandLift(scale: number, durationMs?: number): void;
|
|
30
|
+
/**
|
|
31
|
+
* Composite-quad fade — the lift's opacity twin (e.g. the drag-lift 75 %
|
|
32
|
+
* fade): eases the quad's drawn opacity to `opacity` (default 180ms; 0
|
|
33
|
+
* snaps) and MULTIPLIES the widget's durable `Opacity` cell, so a transient
|
|
34
|
+
* fade stacks over a doc-level opacity instead of fighting it. The DOM
|
|
35
|
+
* chrome runs its own CSS `opacity … ease` transition — same duration/curve
|
|
36
|
+
* family, so card body and GL content fade in lockstep. Resets to 1 on
|
|
37
|
+
* unmount.
|
|
38
|
+
*/
|
|
39
|
+
export declare function useIslandOpacity(opacity: number, durationMs?: number): void;
|
|
@@ -22,6 +22,27 @@ export declare function EngineProvider({ engine, children }: EngineProviderProps
|
|
|
22
22
|
export declare function useCanvasEngine(): CanvasEngine;
|
|
23
23
|
/** The engine-owned write paths (design-005 §4): setTool, deleteSelection, etc. */
|
|
24
24
|
export declare function useOps(): CanvasOps;
|
|
25
|
+
/**
|
|
26
|
+
* Take a stage background hold while `active` (ce.stage, 2026-07-19): the
|
|
27
|
+
* overlay-lifecycle sugar — the disposer releases on deactivate AND unmount,
|
|
28
|
+
* so a crashed/unmounted overlay can never wedge the canvas backgrounded.
|
|
29
|
+
* `engine` is an explicit param (not context): overlays typically render as
|
|
30
|
+
* SIBLINGS of <InfiniteCanvas>, outside the provider.
|
|
31
|
+
*/
|
|
32
|
+
export declare function useStageHold(engine: CanvasEngine, active: boolean, name: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Freeze the engine while `active` (ce.frame, 2026-08-04) — {@link useStageHold}'s
|
|
35
|
+
* stricter sibling, same lifecycle sugar: the effect cleanup IS the thaw, so an
|
|
36
|
+
* overlay that unmounts (or crashes) can never wedge the engine parked.
|
|
37
|
+
*
|
|
38
|
+
* WHICH ONE. A stage hold quiets discretionary rendering over a world that
|
|
39
|
+
* keeps stepping — right for chrome that RECEDES the canvas, where undo,
|
|
40
|
+
* collab and tweens must still land visibly behind a partial overlay. A freeze
|
|
41
|
+
* stops the loop outright — right for chrome that COVERS the canvas, where
|
|
42
|
+
* every frame behind it is work nobody can see. Reaching for a freeze under a
|
|
43
|
+
* partial overlay will look broken: the canvas is a photograph until thaw.
|
|
44
|
+
*/
|
|
45
|
+
export declare function useFrameFreeze(engine: CanvasEngine, active: boolean, name: string): void;
|
|
25
46
|
/**
|
|
26
47
|
* Escape hatch — the raw {@link World} for READS and Tier-3 OBSERVATION only.
|
|
27
48
|
* Mutation goes through {@link useOps}/{@link useCommit} (a gesture-claimed,
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
export declare const REACT_VERSION = "0.0.0";
|
|
7
7
|
export { WidgetHiddenContext, useBreakpoint, useSelected, useWidgetProps, useWorldComponent, } from "./hooks";
|
|
8
8
|
export { WidgetRoot, type WidgetComponentProps, type WidgetHosts, type WidgetRootProps } from "./widget-root";
|
|
9
|
-
export { EngineProvider, useCanvasEngine, useOps, useWorld, type EngineProviderProps, } from "./engine-context";
|
|
9
|
+
export { EngineProvider, useCanvasEngine, useFrameFreeze, useOps, useStageHold, useWorld, type EngineProviderProps, } from "./engine-context";
|
|
10
10
|
export { useCommit, useUndoStatus, type Commit, type UndoStatus } from "./use-commit";
|
|
11
|
+
export { WidgetPreview, type WidgetPreviewProps } from "./widget-preview";
|
|
12
|
+
export { getPreviewSnapshot, hasPreviewSnapshot, setPreviewSnapshot, subscribePreviewSnapshots, type PreviewImage, } from "./preview-snapshots";
|
|
11
13
|
export { useTool, useToolState } from "./use-tool";
|
|
12
14
|
export { usePresencePeers, type PresencePeerView } from "./use-presence";
|
|
13
15
|
export { attachKeymap, type KeymapEntry } from "./keymap";
|
|
14
|
-
export { InfiniteCanvas, type InfiniteCanvasHandle, type InfiniteCanvasProps, } from "./infinite-canvas";
|
|
16
|
+
export { InfiniteCanvas, type GroundLayerFactory, type GroundLayerHandle, type InfiniteCanvasHandle, type InfiniteCanvasProps, } from "./infinite-canvas";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* and the viewport sync — everything that must live next to a real container.
|
|
8
8
|
*
|
|
9
9
|
* Boot order (registration order = reflector flush order, mirrored exactly):
|
|
10
|
-
* host → planes → reflectors[ planeTransform ·
|
|
10
|
+
* host → planes → reflectors[ planeTransform · ground(P0, app-injected) ·
|
|
11
11
|
* domWidgets(P1/P3) · chrome(P4) · cursor · remoteCursors(P5) ] →
|
|
12
12
|
* pointer adapter → measurement (optional) → keymap → rAF loop → viewport RO.
|
|
13
13
|
*
|
|
@@ -24,14 +24,19 @@
|
|
|
24
24
|
* - GL / R3F: the `@ice/r3f` wall forbids `@ice/react` importing three, so a
|
|
25
25
|
* GL layer (GLViews) mounts APP-SIDE. Use {@link onReady} to receive the host
|
|
26
26
|
* + planes and mount it yourself (like apps/glboard).
|
|
27
|
+
* - The GROUND layer (P0: dot grid, wires, snap guides — @ice/ground, three's
|
|
28
|
+
* WebGPURenderer) rides the same wall: pass its factory through the OPAQUE
|
|
29
|
+
* {@link InfiniteCanvasProps.ground} prop (`ground={ground({...})}`); this
|
|
30
|
+
* component types it structurally and never imports the package. No factory
|
|
31
|
+
* ⇒ no ground layer (headless/test boots).
|
|
27
32
|
* - Devtools: the import wall forbids `@ice/react` importing `@ice/devtools`.
|
|
28
33
|
* Wire the panel app-side against `engine.engine` (also via {@link onReady}).
|
|
29
34
|
* - Measurement: auto-sized widgets need a `MeasureQueue` passed to BOTH
|
|
30
35
|
* `createCanvasEngine({ measureQueue })` (ingest side) and this component's
|
|
31
36
|
* `measureQueue` prop (ResizeObserver side). Absent ⇒ measurement is skipped.
|
|
32
37
|
*/
|
|
33
|
-
import { type CanvasEngine, type MeasureQueue } from "@ice/core";
|
|
34
|
-
import { type CanvasHost, type GLRoute, type
|
|
38
|
+
import { type CanvasEngine, type GridConfig, type MeasureQueue, type ReflectorDef, type WirePreviewBuffer, type World } from "@ice/core";
|
|
39
|
+
import { type CanvasHost, type GLRoute, type Planes } from "@ice/dom";
|
|
35
40
|
import { type CSSProperties, type ReactElement, type ReactNode } from "react";
|
|
36
41
|
/** Handed to {@link InfiniteCanvasProps.onReady} for app-side GL/devtools wiring. */
|
|
37
42
|
export interface InfiniteCanvasHandle {
|
|
@@ -39,6 +44,27 @@ export interface InfiniteCanvasHandle {
|
|
|
39
44
|
readonly host: CanvasHost;
|
|
40
45
|
readonly planes: Planes;
|
|
41
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* STRUCTURAL mirror of `@ice/ground`'s GroundLayer/GroundFactory (the
|
|
49
|
+
* `WidgetDef.component`-style opaque seam: react never imports the package —
|
|
50
|
+
* the wall forbids three here). `@ice/ground.ground(...)` returns a function
|
|
51
|
+
* assignable to this type.
|
|
52
|
+
*/
|
|
53
|
+
export interface GroundLayerHandle {
|
|
54
|
+
readonly reflector: ReflectorDef & {
|
|
55
|
+
available(): boolean;
|
|
56
|
+
};
|
|
57
|
+
configureGrid(cfg: Partial<GridConfig>): void;
|
|
58
|
+
dispose(): void;
|
|
59
|
+
}
|
|
60
|
+
export type GroundLayerFactory = (ctx: {
|
|
61
|
+
readonly host: {
|
|
62
|
+
readonly container: HTMLElement;
|
|
63
|
+
readonly contentPlane: HTMLElement;
|
|
64
|
+
};
|
|
65
|
+
readonly world: World;
|
|
66
|
+
readonly readWirePreview: () => WirePreviewBuffer;
|
|
67
|
+
}) => GroundLayerHandle;
|
|
42
68
|
export interface InfiniteCanvasProps {
|
|
43
69
|
/** A constructed engine (`createCanvasEngine(...)`); NOT disposed on unmount. */
|
|
44
70
|
readonly engine: CanvasEngine;
|
|
@@ -49,10 +75,18 @@ export interface InfiniteCanvasProps {
|
|
|
49
75
|
readonly measureQueue?: MeasureQueue;
|
|
50
76
|
/** Called once after the host/reflectors/loop are live (app-side GL/devtools). */
|
|
51
77
|
readonly onReady?: (handle: InfiniteCanvasHandle) => void;
|
|
78
|
+
/**
|
|
79
|
+
* The P0 ground layer (dot grid, wires, snap guides — one WebGPU canvas).
|
|
80
|
+
* Pass `ground(opts)` from `@ice/ground`; received opaquely (see
|
|
81
|
+
* {@link GroundLayerFactory}). Memoize in the caller — a new identity
|
|
82
|
+
* re-boots the canvas mount effect. Absent ⇒ no ground layer renders.
|
|
83
|
+
*/
|
|
84
|
+
readonly ground?: GroundLayerFactory;
|
|
52
85
|
/**
|
|
53
86
|
* Dot-grid tuning (theme dot color, spacing, fades). Applied live via the
|
|
54
|
-
*
|
|
55
|
-
* (memoize in the caller to avoid redundant same-value redraws).
|
|
87
|
+
* ground layer's `configureGrid` — changing it never re-boots the canvas
|
|
88
|
+
* (memoize in the caller to avoid redundant same-value redraws). No-op
|
|
89
|
+
* when {@link ground} is absent.
|
|
56
90
|
*/
|
|
57
91
|
readonly grid?: Partial<GridConfig>;
|
|
58
92
|
/**
|
|
@@ -68,4 +102,4 @@ export interface InfiniteCanvasProps {
|
|
|
68
102
|
/** Overlays inside the viewport (toolbars, HUD) — rendered under the EngineProvider. */
|
|
69
103
|
readonly children?: ReactNode;
|
|
70
104
|
}
|
|
71
|
-
export declare function InfiniteCanvas({ engine, measureQueue, onReady, grid: gridConfig, glRoute, className, style, children, }: InfiniteCanvasProps): ReactElement;
|
|
105
|
+
export declare function InfiniteCanvas({ engine, measureQueue, onReady, ground, grid: gridConfig, glRoute, className, style, children, }: InfiniteCanvasProps): ReactElement;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GL preview snapshot store (design-005 §2 preview contract, P2 —
|
|
3
|
+
* 2026-07-19). react stays three-free by LAW, so the capture pipeline lives
|
|
4
|
+
* in @ice/r3f and meets `<WidgetPreview>` HERE, at a dumb data seam: a
|
|
5
|
+
* module-level map of widget type → captured image, with subscription so an
|
|
6
|
+
* open palette re-renders the moment a capture lands.
|
|
7
|
+
*
|
|
8
|
+
* `PreviewImage` accepts a canvas alongside ImageBitmap so tests (and any
|
|
9
|
+
* app-side prebuilt-cache policy — the P3 seam) can inject without a GPU.
|
|
10
|
+
*/
|
|
11
|
+
export type PreviewImage = ImageBitmap | HTMLCanvasElement;
|
|
12
|
+
export declare function setPreviewSnapshot(type: string, image: PreviewImage): void;
|
|
13
|
+
export declare function getPreviewSnapshot(type: string): PreviewImage | undefined;
|
|
14
|
+
export declare function hasPreviewSnapshot(type: string): boolean;
|
|
15
|
+
export declare function subscribePreviewSnapshots(listener: () => void): () => void;
|
|
16
|
+
/** TEST-ONLY wipe (not on the barrel). */
|
|
17
|
+
export declare function __resetPreviewSnapshotsForTests(): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactElement, type ReactNode } from "react";
|
|
2
|
+
/** TEST-ONLY sandbox wipe (mirrors __resetWidgetsForTests; not on the barrel). */
|
|
3
|
+
export declare function __resetPreviewSandboxForTests(): void;
|
|
4
|
+
export interface WidgetPreviewProps {
|
|
5
|
+
readonly type: string;
|
|
6
|
+
/** The available box; the preview renders aspect-FITTED inside it. */
|
|
7
|
+
readonly width: number;
|
|
8
|
+
readonly height: number;
|
|
9
|
+
/** App silhouette shown when no preview path applies (or one throws). */
|
|
10
|
+
readonly fallback?: ReactNode;
|
|
11
|
+
readonly className?: string;
|
|
12
|
+
readonly style?: CSSProperties;
|
|
13
|
+
}
|
|
14
|
+
export declare function WidgetPreview({ type, width, height, fallback, className, style }: WidgetPreviewProps): ReactElement;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibecook/ice",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "ICE — infinite canvas engine. Figma-grade infinite-canvas UX as a framework: defineWidget turns React/R3F components into canvas citizens — selectable, movable, resizable, wired into node graphs, CRDT-synced, undoable per gesture.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "James Yong <me@jamesyong42.com>",
|
|
@@ -26,70 +26,51 @@
|
|
|
26
26
|
"three",
|
|
27
27
|
"gestures"
|
|
28
28
|
],
|
|
29
|
-
"files": [
|
|
30
|
-
"dist"
|
|
31
|
-
],
|
|
29
|
+
"files": ["dist"],
|
|
32
30
|
"main": "./dist/index.js",
|
|
33
31
|
"types": "./dist/types/packages/ice/src/index.d.ts",
|
|
34
32
|
"exports": {
|
|
35
|
-
".": {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
"./
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
},
|
|
43
|
-
"./dom": {
|
|
44
|
-
"types": "./dist/types/packages/ice/src/dom.d.ts",
|
|
45
|
-
"default": "./dist/dom.js"
|
|
46
|
-
},
|
|
47
|
-
"./react": {
|
|
48
|
-
"types": "./dist/types/packages/ice/src/react.d.ts",
|
|
49
|
-
"default": "./dist/react.js"
|
|
50
|
-
},
|
|
51
|
-
"./r3f": {
|
|
52
|
-
"types": "./dist/types/packages/ice/src/r3f.d.ts",
|
|
53
|
-
"default": "./dist/r3f.js"
|
|
54
|
-
},
|
|
55
|
-
"./devtools": {
|
|
56
|
-
"types": "./dist/types/packages/ice/src/devtools.d.ts",
|
|
57
|
-
"default": "./dist/devtools.js"
|
|
58
|
-
},
|
|
33
|
+
".": { "types": "./dist/types/packages/ice/src/index.d.ts", "default": "./dist/index.js" },
|
|
34
|
+
"./kernel": { "types": "./dist/types/packages/ice/src/kernel.d.ts", "default": "./dist/kernel.js" },
|
|
35
|
+
"./dom": { "types": "./dist/types/packages/ice/src/dom.d.ts", "default": "./dist/dom.js" },
|
|
36
|
+
"./react": { "types": "./dist/types/packages/ice/src/react.d.ts", "default": "./dist/react.js" },
|
|
37
|
+
"./r3f": { "types": "./dist/types/packages/ice/src/r3f.d.ts", "default": "./dist/r3f.js" },
|
|
38
|
+
"./ground": { "types": "./dist/types/packages/ice/src/ground.d.ts", "default": "./dist/ground.js" },
|
|
39
|
+
"./devtools": { "types": "./dist/types/packages/ice/src/devtools.d.ts", "default": "./dist/devtools.js" },
|
|
59
40
|
"./package.json": "./package.json"
|
|
60
41
|
},
|
|
61
|
-
"engines": {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
42
|
+
"engines": { "node": ">=20" },
|
|
43
|
+
"publishConfig": { "access": "public" },
|
|
44
|
+
"scripts": {
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"build": "tsup && tsc -p tsconfig.dts.json",
|
|
47
|
+
"prepack": "pnpm run build"
|
|
66
48
|
},
|
|
67
49
|
"dependencies": {
|
|
68
|
-
"@vibecook/strata-ecs": "0.
|
|
69
|
-
"loro-crdt": "^1.13.
|
|
50
|
+
"@vibecook/strata-ecs": "0.11.0",
|
|
51
|
+
"loro-crdt": "^1.13.8",
|
|
52
|
+
"stats-gl": "^4.2.3",
|
|
70
53
|
"rbush": "^3.0.1"
|
|
71
54
|
},
|
|
72
55
|
"peerDependencies": {
|
|
73
56
|
"@react-three/fiber": ">=9",
|
|
74
|
-
"react": ">=
|
|
75
|
-
"react-dom": ">=
|
|
57
|
+
"react": ">=19",
|
|
58
|
+
"react-dom": ">=19",
|
|
76
59
|
"three": ">=0.160.0"
|
|
77
60
|
},
|
|
78
61
|
"peerDependenciesMeta": {
|
|
79
|
-
"@react-three/fiber": {
|
|
80
|
-
|
|
81
|
-
},
|
|
82
|
-
"
|
|
83
|
-
"optional": true
|
|
84
|
-
},
|
|
85
|
-
"react-dom": {
|
|
86
|
-
"optional": true
|
|
87
|
-
},
|
|
88
|
-
"three": {
|
|
89
|
-
"optional": true
|
|
90
|
-
}
|
|
62
|
+
"@react-three/fiber": { "optional": true },
|
|
63
|
+
"react": { "optional": true },
|
|
64
|
+
"react-dom": { "optional": true },
|
|
65
|
+
"three": { "optional": true }
|
|
91
66
|
},
|
|
92
67
|
"devDependencies": {
|
|
68
|
+
"@ice/core": "workspace:*",
|
|
69
|
+
"@ice/devtools": "workspace:*",
|
|
70
|
+
"@ice/dom": "workspace:*",
|
|
71
|
+
"@ice/kernel": "workspace:*",
|
|
72
|
+
"@ice/r3f": "workspace:*",
|
|
73
|
+
"@ice/react": "workspace:*",
|
|
93
74
|
"@react-three/fiber": "^9",
|
|
94
75
|
"@types/rbush": "^3.0.3",
|
|
95
76
|
"@types/react": "^19",
|
|
@@ -97,16 +78,6 @@
|
|
|
97
78
|
"@types/three": "^0.185.0",
|
|
98
79
|
"react": "^19",
|
|
99
80
|
"react-dom": "^19",
|
|
100
|
-
"three": "^0.185.0"
|
|
101
|
-
"@ice/core": "0.0.0",
|
|
102
|
-
"@ice/dom": "0.0.0",
|
|
103
|
-
"@ice/kernel": "0.0.0",
|
|
104
|
-
"@ice/r3f": "0.0.0",
|
|
105
|
-
"@ice/devtools": "0.0.0",
|
|
106
|
-
"@ice/react": "0.0.0"
|
|
107
|
-
},
|
|
108
|
-
"scripts": {
|
|
109
|
-
"typecheck": "tsc --noEmit",
|
|
110
|
-
"build": "tsup && tsc -p tsconfig.dts.json"
|
|
81
|
+
"three": "^0.185.0"
|
|
111
82
|
}
|
|
112
|
-
}
|
|
83
|
+
}
|