@vibecook/ice 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -2
- package/dist/chunk-5SJYHQI4.js +636 -0
- package/dist/chunk-5SJYHQI4.js.map +1 -0
- package/dist/{chunk-5S7GSXJG.js → chunk-DXU3RZZS.js} +2913 -1180
- package/dist/chunk-DXU3RZZS.js.map +1 -0
- package/dist/{chunk-FIAXPNNY.js → chunk-IASTQMU5.js} +130 -551
- package/dist/chunk-IASTQMU5.js.map +1 -0
- package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
- package/dist/chunk-OR5FNVUE.js.map +1 -0
- package/dist/devtools.js +508 -372
- package/dist/devtools.js.map +1 -1
- package/dist/dom.js +3 -11
- package/dist/ground.js +715 -0
- package/dist/ground.js.map +1 -0
- package/dist/index.js +70 -2
- package/dist/kernel.js +31 -1
- package/dist/r3f.js +599 -62
- package/dist/r3f.js.map +1 -1
- package/dist/react.js +34 -418
- package/dist/react.js.map +1 -1
- package/dist/types/packages/core/src/catalog/camera-derived.d.ts +41 -3
- package/dist/types/packages/core/src/catalog/gesture.d.ts +39 -3
- package/dist/types/packages/core/src/catalog/graph.d.ts +7 -6
- package/dist/types/packages/core/src/catalog/index.d.ts +1 -0
- package/dist/types/packages/core/src/catalog/insert.d.ts +31 -0
- package/dist/types/packages/core/src/catalog/pointer.d.ts +11 -0
- package/dist/types/packages/core/src/catalog/scene.d.ts +37 -4
- package/dist/types/packages/core/src/catalog/selection-presence.d.ts +7 -0
- package/dist/types/packages/core/src/catalog/settings-resources.d.ts +21 -0
- package/dist/types/packages/core/src/doc/autosave.d.ts +15 -2
- package/dist/types/packages/core/src/doc/bootstrap.d.ts +29 -0
- package/dist/types/packages/core/src/doc/channels.d.ts +9 -0
- package/dist/types/packages/core/src/doc/doc-commit-sink.d.ts +2 -1
- package/dist/types/packages/core/src/doc/doc-kit.d.ts +16 -1
- package/dist/types/packages/core/src/doc/envelope.d.ts +2 -1
- package/dist/types/packages/core/src/doc/migrate.d.ts +8 -6
- package/dist/types/packages/core/src/doc/schema-migrate.d.ts +50 -0
- package/dist/types/packages/core/src/doc/version-gate.d.ts +3 -0
- package/dist/types/packages/core/src/engine/commit-sink.d.ts +24 -2
- package/dist/types/packages/core/src/engine/frame-info.d.ts +16 -1
- package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +70 -6
- package/dist/types/packages/core/src/guards/guarded-tx.d.ts +4 -2
- package/dist/types/packages/core/src/helpers/churn-guard.d.ts +40 -0
- package/dist/types/packages/core/src/index.d.ts +13 -7
- package/dist/types/packages/core/src/input/queue.d.ts +11 -0
- package/dist/types/packages/core/src/nav/nested-canvas.d.ts +47 -11
- package/dist/types/packages/core/src/ops/arrange.d.ts +15 -0
- package/dist/types/packages/core/src/ops/insert.d.ts +62 -0
- package/dist/types/packages/core/src/ops/point-pick.d.ts +11 -7
- package/dist/types/packages/core/src/ops/sibling-order.d.ts +70 -0
- package/dist/types/packages/core/src/schema/meta.d.ts +1 -0
- package/dist/types/packages/core/src/settings/defaults.d.ts +45 -0
- package/dist/types/packages/core/src/settings/ground-config.d.ts +55 -0
- package/dist/types/packages/core/src/systems/chrome.d.ts +6 -2
- package/dist/types/packages/core/src/systems/insert-ghost.d.ts +25 -0
- package/dist/types/packages/core/src/systems/l1-pick.d.ts +25 -11
- package/dist/types/packages/core/src/systems/l2-recognize.d.ts +10 -4
- package/dist/types/packages/core/src/systems/l3-behave.d.ts +6 -4
- package/dist/types/packages/core/src/systems/l3-claim.d.ts +7 -3
- package/dist/types/packages/core/src/systems/l3-drop.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-resize.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-snap.d.ts +25 -7
- package/dist/types/packages/core/src/systems/nav-flight.d.ts +95 -0
- package/dist/types/packages/core/src/widget/define-widget.d.ts +39 -0
- package/dist/types/packages/core/src/widget/mount-store.d.ts +11 -3
- package/dist/types/packages/core/src/widget/set-props.d.ts +16 -0
- package/dist/types/packages/core/src/widget/spawn.d.ts +21 -1
- package/dist/types/packages/devtools/src/attach.d.ts +77 -0
- package/dist/types/packages/devtools/src/dock.d.ts +38 -0
- package/dist/types/packages/devtools/src/gl-panel.d.ts +69 -0
- package/dist/types/packages/devtools/src/index.d.ts +8 -6
- package/dist/types/packages/dom/src/index.d.ts +1 -3
- package/dist/types/packages/dom/src/measure-wiring.d.ts +2 -2
- package/dist/types/packages/dom/src/pointer-adapter.d.ts +21 -1
- package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +5 -0
- package/dist/types/packages/ground/src/index.d.ts +52 -0
- package/dist/types/packages/ground/src/layer.d.ts +40 -0
- package/dist/types/packages/ground/src/pass.d.ts +41 -0
- package/dist/types/packages/ground/src/passes/grid.d.ts +21 -0
- package/dist/types/packages/ground/src/passes/guides-collect.d.ts +14 -0
- package/dist/types/packages/ground/src/passes/guides.d.ts +3 -0
- package/dist/types/packages/ground/src/passes/soup-collect.d.ts +35 -0
- package/dist/types/packages/ground/src/passes/soup-mesh.d.ts +8 -0
- package/dist/types/packages/ground/src/passes/wires-collect.d.ts +31 -0
- package/dist/types/packages/ground/src/passes/wires.d.ts +10 -0
- package/dist/types/packages/ground/src/renderer.d.ts +15 -0
- package/dist/types/packages/ice/src/ground.d.ts +1 -0
- package/dist/types/packages/kernel/src/bezier.d.ts +15 -0
- package/dist/types/packages/kernel/src/coords.d.ts +15 -0
- package/dist/types/packages/kernel/src/easing.d.ts +11 -0
- package/dist/types/packages/kernel/src/index.d.ts +3 -0
- package/dist/types/packages/kernel/src/layout.d.ts +118 -0
- package/dist/types/packages/kernel/src/nav-flight.d.ts +93 -0
- package/dist/types/packages/r3f/src/bridge.d.ts +73 -12
- package/dist/types/packages/r3f/src/composite-material.d.ts +9 -0
- package/dist/types/packages/r3f/src/compositor-pass.d.ts +22 -0
- package/dist/types/packages/r3f/src/gl-root.d.ts +76 -1
- package/dist/types/packages/r3f/src/gl-router.d.ts +10 -0
- package/dist/types/packages/r3f/src/index.d.ts +3 -2
- package/dist/types/packages/r3f/src/island-state.d.ts +44 -0
- package/dist/types/packages/r3f/src/preview-capture.d.ts +19 -0
- package/dist/types/packages/r3f/src/self-sustain.d.ts +39 -0
- package/dist/types/packages/r3f/src/use-island-frame.d.ts +16 -1
- package/dist/types/packages/react/src/engine-context.d.ts +8 -0
- package/dist/types/packages/react/src/index.d.ts +4 -2
- package/dist/types/packages/react/src/infinite-canvas.d.ts +40 -6
- package/dist/types/packages/react/src/preview-snapshots.d.ts +17 -0
- package/dist/types/packages/react/src/widget-preview.d.ts +14 -0
- package/package.json +13 -8
- package/dist/chunk-5S7GSXJG.js.map +0 -1
- package/dist/chunk-BCJ47SX7.js.map +0 -1
- package/dist/chunk-FIAXPNNY.js.map +0 -1
- package/dist/types/packages/devtools/src/panel.d.ts +0 -48
- package/dist/types/packages/dom/src/reflectors/grid.d.ts +0 -63
- package/dist/types/packages/dom/src/reflectors/wires.d.ts +0 -88
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ function StickyView({ entity, world }) {
|
|
|
42
42
|
|
|
43
43
|
const engine = createCanvasEngine({ widgets: [Sticky] });
|
|
44
44
|
engine.docs.create(); // a local-first document
|
|
45
|
-
engine.ops.spawnWidget("sticky", {
|
|
45
|
+
engine.ops.spawnWidget("sticky", { x: 120, y: 120 });
|
|
46
46
|
attachKeymap(engine); // ⌫ · ⌘Z · ⌘D · ⌘A · Esc · arrows · v/h/c
|
|
47
47
|
|
|
48
48
|
createRoot(document.getElementById("root")).render(
|
|
@@ -66,6 +66,7 @@ per-gesture undo, gesture-aware autosave, and live presence when you
|
|
|
66
66
|
| `@vibecook/ice/react` | `<InfiniteCanvas>`, `<EngineProvider>`, hooks (`useCommit`, `useWidgetProps`, `useSelected`, `useUndoStatus`, `usePresencePeers`, …), `attachKeymap`. |
|
|
67
67
|
| `@vibecook/ice/r3f` | GL widget islands + virtual-texture compositor, `<GLViews>`, `useIslandFrame`, the GL pointer router. Peers: `three`, `@react-three/fiber`. |
|
|
68
68
|
| `@vibecook/ice/dom` | DOM planes, adapters, and reflectors — for custom shells without React. |
|
|
69
|
+
| `@vibecook/ice/ground` | The P0 ground stratum as one WebGPU canvas (grid, wires, snap guides) with automatic WebGL2 fallback. Passed to `<InfiniteCanvas ground={…}>`. Peer: `three`. |
|
|
69
70
|
| `@vibecook/ice/devtools` | `attachDevtools(engine)` — live pointers/gestures, planes, sovereignty, and loop tabs. |
|
|
70
71
|
| `@vibecook/ice/kernel` | Pure math: coordinates, spatial index, snap, wire geometry, zoom bands. Zero dependencies beyond `rbush`. |
|
|
71
72
|
|
|
@@ -73,4 +74,38 @@ React, `react-dom`, `three`, and `@react-three/fiber` are **optional** peer
|
|
|
73
74
|
dependencies — install only what your surfaces use. The core entry is fully
|
|
74
75
|
headless.
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
## Limits
|
|
78
|
+
|
|
79
|
+
Measured, not estimated. The bench source in `packages/core/bench/` is the source
|
|
80
|
+
of truth; [`docs/benchmarks.md`](https://github.com/jamesyong-42/infinite-canvas-engine/blob/main/docs/benchmarks.md)
|
|
81
|
+
records the full output (Apple M1 Max, 2026-07-15).
|
|
82
|
+
|
|
83
|
+
**Collaboration is the tightest ceiling — plan shared boards around ~3,000
|
|
84
|
+
objects.** A local-only document scales to ~100k, but a collaboratively-edited one
|
|
85
|
+
is bounded by how fast a peer applies an incoming edit, which still scales with
|
|
86
|
+
document size: roughly **3,000 objects for an actively-editing peer**, **~40,000
|
|
87
|
+
for a receive-only one**. Partition large shared boards into containers.
|
|
88
|
+
|
|
89
|
+
**Scale through containers, not flat boards.** Idle frames no longer scale with the
|
|
90
|
+
board (872 µs at 100k entities). Camera gestures still can: nested boards stay cheap
|
|
91
|
+
(276–460 µs at 10k, 2.6–4.4 ms at 100k), but a **flat, all-active 100k board costs
|
|
92
|
+
40–64 ms per gesture frame** — the honest O(N) ceiling, since nothing can be skipped
|
|
93
|
+
when everything is active. Containers let active-scoped queries skip whole chunks.
|
|
94
|
+
|
|
95
|
+
**Opening a big document is a one-time cost**: full-document projection at attach is
|
|
96
|
+
~0.3–0.5 s at 10k rows, ~3.2–3.9 s at 100k. Envelope ≈ 100 B/row.
|
|
97
|
+
|
|
98
|
+
**Deliberately out of scope** — each has a named extension seam rather than a hidden
|
|
99
|
+
TODO: a general layout engine (beyond `ops.arrange`), rich text, comments/threads,
|
|
100
|
+
and permissions. strata has no authority model, so multi-user trust is the
|
|
101
|
+
application's to own.
|
|
102
|
+
|
|
103
|
+
**Not yet built**: an engine-level focus model for widget keyboard exclusivity and
|
|
104
|
+
wheel/scroll opt-out. Native form controls already work — the keymap ignores
|
|
105
|
+
keystrokes targeting an `input`, `textarea`, `select`, or `contenteditable` — but a
|
|
106
|
+
widget wanting arrow keys or its own scrolling has no engine contract yet.
|
|
107
|
+
|
|
108
|
+
**The substrate is pre-1.0**: `@vibecook/strata-ecs` minor versions may break APIs.
|
|
109
|
+
|
|
110
|
+
[Changelog](https://github.com/jamesyong-42/infinite-canvas-engine/blob/main/CHANGELOG.md)
|
|
111
|
+
· MIT © James Yong
|
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
import {
|
|
2
|
+
attachPointerAdapter,
|
|
3
|
+
createCanvasHost,
|
|
4
|
+
createChromeReflector,
|
|
5
|
+
createCursorReflector,
|
|
6
|
+
createDomWidgetsReflector,
|
|
7
|
+
createPlaneTransformReflector,
|
|
8
|
+
createPlanes,
|
|
9
|
+
createRemoteCursorsReflector,
|
|
10
|
+
startRafLoop,
|
|
11
|
+
wireMeasurement
|
|
12
|
+
} from "./chunk-IASTQMU5.js";
|
|
13
|
+
import {
|
|
14
|
+
ActiveTool,
|
|
15
|
+
DurableUndoStatus,
|
|
16
|
+
Local,
|
|
17
|
+
Not,
|
|
18
|
+
Position,
|
|
19
|
+
PrefabId,
|
|
20
|
+
PresenceInfo,
|
|
21
|
+
PresencePeer,
|
|
22
|
+
Selected,
|
|
23
|
+
Viewport,
|
|
24
|
+
WidgetBreakpoint,
|
|
25
|
+
createCanvasEngine,
|
|
26
|
+
defineQuery,
|
|
27
|
+
devGuardsEnabled,
|
|
28
|
+
guardedTransaction,
|
|
29
|
+
selectedEntities,
|
|
30
|
+
tools,
|
|
31
|
+
widgets,
|
|
32
|
+
writeRuntimeResource
|
|
33
|
+
} from "./chunk-DXU3RZZS.js";
|
|
34
|
+
|
|
35
|
+
// ../react/src/hooks.ts
|
|
36
|
+
import { createContext, useCallback, useContext, useMemo, useRef, useSyncExternalStore } from "react";
|
|
37
|
+
var WidgetHiddenContext = createContext(false);
|
|
38
|
+
var selectedQ = defineQuery([Selected]);
|
|
39
|
+
function shallowEq(a, b) {
|
|
40
|
+
if (Object.is(a, b)) return true;
|
|
41
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
42
|
+
const ka = Object.keys(a);
|
|
43
|
+
const kb = Object.keys(b);
|
|
44
|
+
if (ka.length !== kb.length) return false;
|
|
45
|
+
for (const k of ka) {
|
|
46
|
+
if (!Object.is(a[k], b[k])) return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
function useWorldComponent(world, entity, component) {
|
|
51
|
+
const hidden = useContext(WidgetHiddenContext);
|
|
52
|
+
const last = useRef(void 0);
|
|
53
|
+
const subscribe = useCallback(
|
|
54
|
+
(onChange) => {
|
|
55
|
+
if (hidden) return () => {
|
|
56
|
+
};
|
|
57
|
+
return world.reactive.observeValue(entity, component, onChange);
|
|
58
|
+
},
|
|
59
|
+
[world, entity, component, hidden]
|
|
60
|
+
);
|
|
61
|
+
const getSnapshot = useCallback(() => {
|
|
62
|
+
if (hidden) return last.current;
|
|
63
|
+
const v = world.isAlive(entity) ? world.get(entity, component) : void 0;
|
|
64
|
+
if (!shallowEq(v, last.current)) last.current = v;
|
|
65
|
+
return last.current;
|
|
66
|
+
}, [world, entity, component, hidden]);
|
|
67
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
68
|
+
}
|
|
69
|
+
function useWidgetProps(world, entity, type, group = "props") {
|
|
70
|
+
const widget = widgets.get(type);
|
|
71
|
+
const g = widget?.groups.find((x) => x.name === group);
|
|
72
|
+
if (widget === void 0 || g === void 0) {
|
|
73
|
+
throw new Error(`ice: useWidgetProps \u2014 unknown widget type/group "${type}"/"${group}".`);
|
|
74
|
+
}
|
|
75
|
+
const raw = useWorldComponent(world, entity, g.component);
|
|
76
|
+
return useMemo(() => {
|
|
77
|
+
if (raw === void 0) return void 0;
|
|
78
|
+
const out = {};
|
|
79
|
+
for (const [name, spec] of Object.entries(g.fields)) {
|
|
80
|
+
const v = raw[name];
|
|
81
|
+
out[name] = spec.kind === "json" && typeof v === "string" ? parseJsonCell(v, spec.default) : v;
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}, [raw, g]);
|
|
85
|
+
}
|
|
86
|
+
function parseJsonCell(cell, serializedDefault) {
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(cell);
|
|
89
|
+
} catch {
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(serializedDefault ?? "null");
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function useSelected(world, entity) {
|
|
98
|
+
const hidden = useContext(WidgetHiddenContext);
|
|
99
|
+
const last = useRef(false);
|
|
100
|
+
const subscribe = useCallback(
|
|
101
|
+
(onChange) => {
|
|
102
|
+
if (hidden) return () => {
|
|
103
|
+
};
|
|
104
|
+
return world.reactive.observeQuery(selectedQ, onChange);
|
|
105
|
+
},
|
|
106
|
+
[world, hidden]
|
|
107
|
+
);
|
|
108
|
+
const getSnapshot = useCallback(() => {
|
|
109
|
+
if (hidden) return last.current;
|
|
110
|
+
last.current = world.isAlive(entity) && world.hasTag(entity, Selected);
|
|
111
|
+
return last.current;
|
|
112
|
+
}, [world, entity, hidden]);
|
|
113
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
114
|
+
}
|
|
115
|
+
function useBreakpoint(world, entity) {
|
|
116
|
+
const bp = useWorldComponent(world, entity, WidgetBreakpoint);
|
|
117
|
+
return bp?.tier ?? "normal";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ../react/src/widget-root.tsx
|
|
121
|
+
import { createElement } from "react";
|
|
122
|
+
import { createPortal } from "react-dom";
|
|
123
|
+
import { useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
124
|
+
function WidgetRoot({ world, store, hosts }) {
|
|
125
|
+
const entries = useSyncExternalStore2(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
126
|
+
const portals = [];
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
if (!world.isAlive(entry.entity)) continue;
|
|
129
|
+
const type = world.get(entry.entity, PrefabId)?.id;
|
|
130
|
+
const widget = typeof type === "string" ? widgets.get(type) : void 0;
|
|
131
|
+
if (widget === void 0) continue;
|
|
132
|
+
const view = widget.surface === "dom" ? widget.component : widget.chrome;
|
|
133
|
+
if (view == null) continue;
|
|
134
|
+
const target = hosts.hostFor(entry.entity);
|
|
135
|
+
if (target === void 0) continue;
|
|
136
|
+
const View = view;
|
|
137
|
+
portals.push(
|
|
138
|
+
createPortal(
|
|
139
|
+
createElement(
|
|
140
|
+
WidgetHiddenContext.Provider,
|
|
141
|
+
{ value: entry.hidden },
|
|
142
|
+
createElement(View, { entity: entry.entity, world })
|
|
143
|
+
),
|
|
144
|
+
target,
|
|
145
|
+
String(entry.entity)
|
|
146
|
+
)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return createElement("div", { "data-ice-widget-root": "" }, portals);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ../react/src/engine-context.tsx
|
|
153
|
+
import { createContext as createContext2, useContext as useContext2, useEffect } from "react";
|
|
154
|
+
import { jsx } from "react/jsx-runtime";
|
|
155
|
+
var EngineContext = createContext2(null);
|
|
156
|
+
function EngineProvider({ engine, children }) {
|
|
157
|
+
return /* @__PURE__ */ jsx(EngineContext.Provider, { value: engine, children });
|
|
158
|
+
}
|
|
159
|
+
function useCanvasEngine() {
|
|
160
|
+
const engine = useContext2(EngineContext);
|
|
161
|
+
if (engine === null) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
"ice: useCanvasEngine \u2014 no <EngineProvider>. Render widgets inside <InfiniteCanvas> (which provides it) or wrap your tree in <EngineProvider engine={...}>."
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return engine;
|
|
167
|
+
}
|
|
168
|
+
function useOps() {
|
|
169
|
+
return useCanvasEngine().ops;
|
|
170
|
+
}
|
|
171
|
+
function useStageHold(engine, active, name) {
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (!active) return void 0;
|
|
174
|
+
return engine.stage.background(name);
|
|
175
|
+
}, [engine, active, name]);
|
|
176
|
+
}
|
|
177
|
+
var warnedUseWorld = false;
|
|
178
|
+
function useWorld() {
|
|
179
|
+
const engine = useCanvasEngine();
|
|
180
|
+
if (devGuardsEnabled() && !warnedUseWorld) {
|
|
181
|
+
warnedUseWorld = true;
|
|
182
|
+
console.warn(
|
|
183
|
+
"ice: useWorld() is a READ/OBSERVE escape hatch \u2014 never write to the world directly. Use useOps()/useCommit() for mutation (design-005 \xA75)."
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return engine.world;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ../react/src/use-commit.ts
|
|
190
|
+
import { useCallback as useCallback2, useRef as useRef2, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
191
|
+
function useCommit() {
|
|
192
|
+
const engine = useCanvasEngine();
|
|
193
|
+
return useCallback2(
|
|
194
|
+
(fn, opts) => {
|
|
195
|
+
const session = engine.docs.current();
|
|
196
|
+
if (session === void 0) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
"ice: useCommit \u2014 no active document. Attach one with engine.docs.create()/open()/join() before committing widget edits."
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
if (session.readOnly) {
|
|
202
|
+
throw new Error("ice: useCommit \u2014 the document is read-only (version gate); widget edits are disabled.");
|
|
203
|
+
}
|
|
204
|
+
guardedTransaction(session.store, engine.world, fn, opts);
|
|
205
|
+
},
|
|
206
|
+
[engine]
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
var NO_UNDO = { canUndo: false, canRedo: false };
|
|
210
|
+
function useUndoStatus() {
|
|
211
|
+
const engine = useCanvasEngine();
|
|
212
|
+
const last = useRef2(NO_UNDO);
|
|
213
|
+
const subscribe = useCallback2(
|
|
214
|
+
(onChange) => engine.world.reactive.observeResource(DurableUndoStatus, onChange),
|
|
215
|
+
[engine]
|
|
216
|
+
);
|
|
217
|
+
const getSnapshot = useCallback2(() => {
|
|
218
|
+
const status = engine.world.getResource(DurableUndoStatus);
|
|
219
|
+
const canUndo = status?.canUndo ?? false;
|
|
220
|
+
const canRedo = status?.canRedo ?? false;
|
|
221
|
+
if (last.current.canUndo !== canUndo || last.current.canRedo !== canRedo) {
|
|
222
|
+
last.current = { canUndo, canRedo };
|
|
223
|
+
}
|
|
224
|
+
return last.current;
|
|
225
|
+
}, [engine]);
|
|
226
|
+
return useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ../react/src/preview-snapshots.ts
|
|
230
|
+
var images = /* @__PURE__ */ new Map();
|
|
231
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
232
|
+
function setPreviewSnapshot(type, image) {
|
|
233
|
+
images.set(type, image);
|
|
234
|
+
for (const l of listeners) l();
|
|
235
|
+
}
|
|
236
|
+
function getPreviewSnapshot(type) {
|
|
237
|
+
return images.get(type);
|
|
238
|
+
}
|
|
239
|
+
function hasPreviewSnapshot(type) {
|
|
240
|
+
return images.has(type);
|
|
241
|
+
}
|
|
242
|
+
function subscribePreviewSnapshots(listener) {
|
|
243
|
+
listeners.add(listener);
|
|
244
|
+
return () => listeners.delete(listener);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ../react/src/widget-preview.tsx
|
|
248
|
+
import {
|
|
249
|
+
Component,
|
|
250
|
+
useEffect as useEffect2,
|
|
251
|
+
useRef as useRef3,
|
|
252
|
+
useSyncExternalStore as useSyncExternalStore4
|
|
253
|
+
} from "react";
|
|
254
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
255
|
+
var sandbox = null;
|
|
256
|
+
function ensurePreviewEntity(def) {
|
|
257
|
+
if (sandbox === null) {
|
|
258
|
+
const ce = createCanvasEngine();
|
|
259
|
+
ce.docs.create();
|
|
260
|
+
ce.world.sync();
|
|
261
|
+
ce.step(16);
|
|
262
|
+
sandbox = { ce, entities: /* @__PURE__ */ new Map() };
|
|
263
|
+
}
|
|
264
|
+
const existing = sandbox.entities.get(def.type);
|
|
265
|
+
if (existing !== void 0) return existing;
|
|
266
|
+
try {
|
|
267
|
+
const e = sandbox.ce.ops.spawnWidget(def.type, {
|
|
268
|
+
x: 0,
|
|
269
|
+
y: 0,
|
|
270
|
+
undoable: false,
|
|
271
|
+
...def.previewProps !== void 0 ? { props: def.previewProps } : {}
|
|
272
|
+
});
|
|
273
|
+
sandbox.ce.world.sync();
|
|
274
|
+
sandbox.ce.step(32);
|
|
275
|
+
sandbox.entities.set(def.type, e);
|
|
276
|
+
return e;
|
|
277
|
+
} catch (err) {
|
|
278
|
+
console.warn(`ice: WidgetPreview("${def.type}") sandbox spawn failed \u2014 falling back.`, err);
|
|
279
|
+
return void 0;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
var PreviewBoundary = class extends Component {
|
|
283
|
+
state = { failed: false };
|
|
284
|
+
static getDerivedStateFromError() {
|
|
285
|
+
return { failed: true };
|
|
286
|
+
}
|
|
287
|
+
componentDidCatch(error) {
|
|
288
|
+
console.warn("ice: a widget preview threw while rendering \u2014 showing the fallback.", error);
|
|
289
|
+
}
|
|
290
|
+
render() {
|
|
291
|
+
return this.state.failed ? this.props.fallback : this.props.children;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
function SnapshotLayer({ image }) {
|
|
295
|
+
const ref = useRef3(null);
|
|
296
|
+
useEffect2(() => {
|
|
297
|
+
const canvas = ref.current;
|
|
298
|
+
if (canvas === null) return;
|
|
299
|
+
canvas.width = image.width;
|
|
300
|
+
canvas.height = image.height;
|
|
301
|
+
try {
|
|
302
|
+
canvas.getContext("2d")?.drawImage(image, 0, 0);
|
|
303
|
+
} catch {
|
|
304
|
+
}
|
|
305
|
+
}, [image]);
|
|
306
|
+
return /* @__PURE__ */ jsx2(
|
|
307
|
+
"canvas",
|
|
308
|
+
{
|
|
309
|
+
ref,
|
|
310
|
+
"data-preview-snapshot": "",
|
|
311
|
+
style: { position: "absolute", inset: 0, width: "100%", height: "100%" }
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
function defaultPlaceholder(label) {
|
|
316
|
+
return /* @__PURE__ */ jsx2(
|
|
317
|
+
"div",
|
|
318
|
+
{
|
|
319
|
+
style: {
|
|
320
|
+
width: "100%",
|
|
321
|
+
height: "100%",
|
|
322
|
+
borderRadius: 12,
|
|
323
|
+
background: "rgba(127, 127, 127, 0.12)",
|
|
324
|
+
boxShadow: "inset 0 0 0 1px rgba(127, 127, 127, 0.25)",
|
|
325
|
+
display: "flex",
|
|
326
|
+
alignItems: "center",
|
|
327
|
+
justifyContent: "center",
|
|
328
|
+
fontSize: 11,
|
|
329
|
+
color: "rgba(127, 127, 127, 0.8)",
|
|
330
|
+
overflow: "hidden"
|
|
331
|
+
},
|
|
332
|
+
children: label
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
function WidgetPreview({ type, width, height, fallback, className, style }) {
|
|
337
|
+
const snapshot = useSyncExternalStore4(subscribePreviewSnapshots, () => getPreviewSnapshot(type));
|
|
338
|
+
const def = widgets.get(type);
|
|
339
|
+
const safeFallback = fallback ?? defaultPlaceholder(type);
|
|
340
|
+
if (def === void 0 || def.defaultSize.w <= 0 || def.defaultSize.h <= 0) {
|
|
341
|
+
return /* @__PURE__ */ jsx2("div", { className, style: { width, height, ...style }, children: safeFallback });
|
|
342
|
+
}
|
|
343
|
+
const s = Math.min(width / def.defaultSize.w, height / def.defaultSize.h);
|
|
344
|
+
const fitW = def.defaultSize.w * s;
|
|
345
|
+
const fitH = def.defaultSize.h * s;
|
|
346
|
+
const stage = (content) => /* @__PURE__ */ jsx2("div", { className, style: { width: fitW, height: fitH, overflow: "hidden", ...style }, children: /* @__PURE__ */ jsx2(
|
|
347
|
+
"div",
|
|
348
|
+
{
|
|
349
|
+
inert: true,
|
|
350
|
+
style: {
|
|
351
|
+
width: def.defaultSize.w,
|
|
352
|
+
height: def.defaultSize.h,
|
|
353
|
+
transform: `scale(${s})`,
|
|
354
|
+
transformOrigin: "0 0",
|
|
355
|
+
pointerEvents: "none",
|
|
356
|
+
userSelect: "none"
|
|
357
|
+
},
|
|
358
|
+
children: /* @__PURE__ */ jsx2(PreviewBoundary, { fallback: safeFallback, children: content })
|
|
359
|
+
}
|
|
360
|
+
) });
|
|
361
|
+
if (def.previewComponent !== null && def.previewComponent !== void 0) {
|
|
362
|
+
const Declared = def.previewComponent;
|
|
363
|
+
return stage(/* @__PURE__ */ jsx2(Declared, {}));
|
|
364
|
+
}
|
|
365
|
+
if (def.surface === "gl" && snapshot !== void 0) {
|
|
366
|
+
let chromeLayer = null;
|
|
367
|
+
if (def.chrome !== null && def.chrome !== void 0) {
|
|
368
|
+
const entity = ensurePreviewEntity(def);
|
|
369
|
+
if (entity !== void 0 && sandbox !== null) {
|
|
370
|
+
const Chrome = def.chrome;
|
|
371
|
+
chromeLayer = /* @__PURE__ */ jsx2(EngineProvider, { engine: sandbox.ce, children: /* @__PURE__ */ jsx2("div", { style: { position: "absolute", inset: 0 }, children: /* @__PURE__ */ jsx2(Chrome, { entity, world: sandbox.ce.world }) }) });
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return stage(
|
|
375
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "relative", width: "100%", height: "100%" }, children: [
|
|
376
|
+
chromeLayer,
|
|
377
|
+
/* @__PURE__ */ jsx2(SnapshotLayer, { image: snapshot })
|
|
378
|
+
] })
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
if (def.surface === "dom" && def.component !== null && def.component !== void 0) {
|
|
382
|
+
const entity = ensurePreviewEntity(def);
|
|
383
|
+
if (entity !== void 0 && sandbox !== null) {
|
|
384
|
+
const View = def.component;
|
|
385
|
+
return stage(
|
|
386
|
+
/* @__PURE__ */ jsx2(EngineProvider, { engine: sandbox.ce, children: /* @__PURE__ */ jsx2(View, { entity, world: sandbox.ce.world }) })
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return /* @__PURE__ */ jsx2("div", { className, style: { width: fitW, height: fitH, ...style }, children: safeFallback });
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ../react/src/use-tool.ts
|
|
394
|
+
import { useCallback as useCallback3, useSyncExternalStore as useSyncExternalStore5 } from "react";
|
|
395
|
+
function useTool() {
|
|
396
|
+
const engine = useCanvasEngine();
|
|
397
|
+
const subscribe = useCallback3(
|
|
398
|
+
(onChange) => engine.world.reactive.observeResource(ActiveTool, onChange),
|
|
399
|
+
[engine]
|
|
400
|
+
);
|
|
401
|
+
const getSnapshot = useCallback3(() => engine.world.getResource(ActiveTool)?.id ?? "select", [engine]);
|
|
402
|
+
const active = useSyncExternalStore5(subscribe, getSnapshot, getSnapshot);
|
|
403
|
+
const setTool = useCallback3((id) => engine.ops.setTool(id), [engine]);
|
|
404
|
+
return [active, setTool];
|
|
405
|
+
}
|
|
406
|
+
function useToolState(id) {
|
|
407
|
+
const [active] = useTool();
|
|
408
|
+
return active === id;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ../react/src/use-presence.ts
|
|
412
|
+
import { useCallback as useCallback4, useRef as useRef4, useSyncExternalStore as useSyncExternalStore6 } from "react";
|
|
413
|
+
var remotePeersQ = defineQuery([PresencePeer, Not(Local)]);
|
|
414
|
+
function usePresencePeers() {
|
|
415
|
+
const engine = useCanvasEngine();
|
|
416
|
+
const cache = useRef4({ key: "\0", value: [] });
|
|
417
|
+
const subscribe = useCallback4(
|
|
418
|
+
(onChange) => engine.world.reactive.observeQuery(remotePeersQ, onChange),
|
|
419
|
+
[engine]
|
|
420
|
+
);
|
|
421
|
+
const getSnapshot = useCallback4(() => {
|
|
422
|
+
const world = engine.world;
|
|
423
|
+
const ids = [];
|
|
424
|
+
world.query(remotePeersQ).each((b) => {
|
|
425
|
+
for (const r of b) ids.push(b.entity(r));
|
|
426
|
+
});
|
|
427
|
+
const key = ids.join(",");
|
|
428
|
+
if (key !== cache.current.key) {
|
|
429
|
+
cache.current = {
|
|
430
|
+
key,
|
|
431
|
+
value: ids.map((entity) => ({ entity, info: world.get(entity, PresenceInfo) }))
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
return cache.current.value;
|
|
435
|
+
}, [engine]);
|
|
436
|
+
return useSyncExternalStore6(subscribe, getSnapshot, getSnapshot);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ../react/src/keymap.ts
|
|
440
|
+
var signature = (key, mod, shift) => `${key.toLowerCase()}|${mod ? 1 : 0}|${shift ? 1 : 0}`;
|
|
441
|
+
function isEditableTarget(target) {
|
|
442
|
+
if (target === null || !(target instanceof HTMLElement)) return false;
|
|
443
|
+
const tag = target.tagName;
|
|
444
|
+
return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || target.isContentEditable;
|
|
445
|
+
}
|
|
446
|
+
function nudgeSelection(engine, dx, dy) {
|
|
447
|
+
const session = engine.docs.current();
|
|
448
|
+
if (session === void 0) return;
|
|
449
|
+
const selection = selectedEntities(engine.world).filter((e) => session.store.keyOf(e) !== void 0);
|
|
450
|
+
if (selection.length === 0) return;
|
|
451
|
+
guardedTransaction(session.store, engine.world, (tx) => {
|
|
452
|
+
for (const e of selection) {
|
|
453
|
+
const p = engine.world.get(e, Position);
|
|
454
|
+
if (p === void 0) continue;
|
|
455
|
+
tx.edit(e).set(Position, { x: p.x + dx, y: p.y + dy });
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
var ARROWS = [
|
|
460
|
+
["ArrowLeft", -1, 0],
|
|
461
|
+
["ArrowRight", 1, 0],
|
|
462
|
+
["ArrowUp", 0, -1],
|
|
463
|
+
["ArrowDown", 0, 1]
|
|
464
|
+
];
|
|
465
|
+
function defaultEntries() {
|
|
466
|
+
const entries = [
|
|
467
|
+
{ key: "Backspace", run: (e) => e.ops.deleteSelection() },
|
|
468
|
+
{ key: "Delete", run: (e) => e.ops.deleteSelection() },
|
|
469
|
+
{ key: "z", mod: true, run: (e) => e.docs.undo() },
|
|
470
|
+
{ key: "z", mod: true, shift: true, run: (e) => e.docs.redo() },
|
|
471
|
+
{ key: "d", mod: true, run: (e) => e.ops.duplicateSelection() },
|
|
472
|
+
{ key: "a", mod: true, run: (e) => e.ops.selectAll() },
|
|
473
|
+
{ key: "Escape", run: (e) => e.ops.cancelActiveGestures() }
|
|
474
|
+
];
|
|
475
|
+
for (const [key, dx, dy] of ARROWS) {
|
|
476
|
+
entries.push({ key, run: (e) => nudgeSelection(e, dx, dy) });
|
|
477
|
+
entries.push({ key, shift: true, run: (e) => nudgeSelection(e, dx * 10, dy * 10) });
|
|
478
|
+
}
|
|
479
|
+
for (const tool of tools.all()) {
|
|
480
|
+
if (tool.shortcut !== void 0) {
|
|
481
|
+
const id = tool.id;
|
|
482
|
+
entries.push({ key: tool.shortcut, run: (e) => e.ops.setTool(id) });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return entries;
|
|
486
|
+
}
|
|
487
|
+
function resolveTarget(target) {
|
|
488
|
+
if (target !== void 0) return target;
|
|
489
|
+
return typeof window !== "undefined" ? window : void 0;
|
|
490
|
+
}
|
|
491
|
+
function attachKeymap(engine, target, overrides = []) {
|
|
492
|
+
const el = resolveTarget(target);
|
|
493
|
+
if (el === void 0) return () => {
|
|
494
|
+
};
|
|
495
|
+
const map = /* @__PURE__ */ new Map();
|
|
496
|
+
for (const entry of defaultEntries()) map.set(signature(entry.key, entry.mod, entry.shift), entry);
|
|
497
|
+
for (const entry of overrides) map.set(signature(entry.key, entry.mod, entry.shift), entry);
|
|
498
|
+
const onKeyDown = (event) => {
|
|
499
|
+
if (isEditableTarget(event.target)) return;
|
|
500
|
+
const mod = event.metaKey || event.ctrlKey;
|
|
501
|
+
const entry = map.get(signature(event.key, mod, event.shiftKey));
|
|
502
|
+
if (entry === void 0) return;
|
|
503
|
+
event.preventDefault();
|
|
504
|
+
entry.run(engine);
|
|
505
|
+
};
|
|
506
|
+
el.addEventListener("keydown", onKeyDown);
|
|
507
|
+
return () => el.removeEventListener("keydown", onKeyDown);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ../react/src/infinite-canvas.tsx
|
|
511
|
+
import { useEffect as useEffect3, useRef as useRef5, useState } from "react";
|
|
512
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
513
|
+
function InfiniteCanvas({
|
|
514
|
+
engine,
|
|
515
|
+
measureQueue,
|
|
516
|
+
onReady,
|
|
517
|
+
ground,
|
|
518
|
+
grid: gridConfig,
|
|
519
|
+
glRoute,
|
|
520
|
+
className,
|
|
521
|
+
style,
|
|
522
|
+
children
|
|
523
|
+
}) {
|
|
524
|
+
const containerRef = useRef5(null);
|
|
525
|
+
const [hosts, setHosts] = useState(void 0);
|
|
526
|
+
const onReadyRef = useRef5(onReady);
|
|
527
|
+
onReadyRef.current = onReady;
|
|
528
|
+
const groundRef = useRef5(null);
|
|
529
|
+
const gridConfigRef = useRef5(gridConfig);
|
|
530
|
+
gridConfigRef.current = gridConfig;
|
|
531
|
+
const glRouteRef = useRef5(glRoute);
|
|
532
|
+
glRouteRef.current = glRoute;
|
|
533
|
+
useEffect3(() => {
|
|
534
|
+
const container = containerRef.current;
|
|
535
|
+
if (container === null) return;
|
|
536
|
+
const { world, engine: core, stack, runtime } = engine;
|
|
537
|
+
const host = createCanvasHost(container);
|
|
538
|
+
const planes = createPlanes(host);
|
|
539
|
+
const planeArgs = { contentPlane: planes.content, liftedPlane: planes.lifted };
|
|
540
|
+
const domWidgets = createDomWidgetsReflector(planeArgs, world, runtime.store);
|
|
541
|
+
const remoteCursors = createRemoteCursorsReflector(host, world);
|
|
542
|
+
const groundLayer = ground?.({ host, world, readWirePreview: () => stack.wirePreview }) ?? null;
|
|
543
|
+
groundRef.current = groundLayer;
|
|
544
|
+
if (groundLayer !== null && gridConfigRef.current !== void 0) {
|
|
545
|
+
groundLayer.configureGrid(gridConfigRef.current);
|
|
546
|
+
}
|
|
547
|
+
const chrome = createChromeReflector(host, world, stack.marqueeBuffer);
|
|
548
|
+
const unregister = [
|
|
549
|
+
core.registerReflector(createPlaneTransformReflector(planeArgs)),
|
|
550
|
+
...groundLayer !== null ? [core.registerReflector(groundLayer.reflector)] : [],
|
|
551
|
+
core.registerReflector(domWidgets),
|
|
552
|
+
core.registerReflector(chrome),
|
|
553
|
+
core.registerReflector(createCursorReflector(host, stack.readCursor)),
|
|
554
|
+
core.registerReflector(remoteCursors.reflector)
|
|
555
|
+
];
|
|
556
|
+
const detachPointer = attachPointerAdapter(
|
|
557
|
+
host,
|
|
558
|
+
stack.queue,
|
|
559
|
+
glRouteRef.current !== void 0 ? (
|
|
560
|
+
// Pass the verdict through UNCOERCED: a rich GLRouteVerdict return
|
|
561
|
+
// (hover-time overInteractive, 2026-07-18) must survive this seam.
|
|
562
|
+
{ glRoute: (kind, x, y, e) => glRouteRef.current?.(kind, x, y, e) ?? false }
|
|
563
|
+
) : {}
|
|
564
|
+
);
|
|
565
|
+
const detachMeasure = measureQueue !== void 0 ? wireMeasurement(runtime.store, domWidgets, measureQueue) : void 0;
|
|
566
|
+
const detachKeymap = attachKeymap(engine);
|
|
567
|
+
const syncViewport = () => {
|
|
568
|
+
const rect = container.getBoundingClientRect();
|
|
569
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1;
|
|
570
|
+
writeRuntimeResource(world, Viewport, { w: rect.width, h: rect.height, dpr });
|
|
571
|
+
};
|
|
572
|
+
syncViewport();
|
|
573
|
+
let resizeObserver;
|
|
574
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
575
|
+
resizeObserver = new ResizeObserver(syncViewport);
|
|
576
|
+
resizeObserver.observe(container);
|
|
577
|
+
}
|
|
578
|
+
const stopLoop = startRafLoop(core);
|
|
579
|
+
setHosts(domWidgets);
|
|
580
|
+
onReadyRef.current?.({ engine, host, planes });
|
|
581
|
+
return () => {
|
|
582
|
+
stopLoop();
|
|
583
|
+
detachKeymap();
|
|
584
|
+
detachMeasure?.();
|
|
585
|
+
detachPointer();
|
|
586
|
+
resizeObserver?.disconnect();
|
|
587
|
+
for (const unreg of unregister) unreg();
|
|
588
|
+
remoteCursors.destroy();
|
|
589
|
+
groundLayer?.dispose();
|
|
590
|
+
groundRef.current = null;
|
|
591
|
+
domWidgets.dispose();
|
|
592
|
+
chrome.dispose();
|
|
593
|
+
planes.dispose();
|
|
594
|
+
host.dispose();
|
|
595
|
+
setHosts(void 0);
|
|
596
|
+
};
|
|
597
|
+
}, [engine, measureQueue, ground]);
|
|
598
|
+
useEffect3(() => {
|
|
599
|
+
if (gridConfig !== void 0) groundRef.current?.configureGrid(gridConfig);
|
|
600
|
+
}, [gridConfig]);
|
|
601
|
+
return /* @__PURE__ */ jsx3(EngineProvider, { engine, children: /* @__PURE__ */ jsxs2("div", { ref: containerRef, className, style: { width: "100%", height: "100%", ...style }, "data-ice-canvas": "", children: [
|
|
602
|
+
hosts !== void 0 ? /* @__PURE__ */ jsx3(WidgetRoot, { world: engine.world, store: engine.runtime.store, hosts }) : null,
|
|
603
|
+
children
|
|
604
|
+
] }) });
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ../react/src/index.ts
|
|
608
|
+
var REACT_VERSION = "0.0.0";
|
|
609
|
+
|
|
610
|
+
export {
|
|
611
|
+
WidgetHiddenContext,
|
|
612
|
+
useWorldComponent,
|
|
613
|
+
useWidgetProps,
|
|
614
|
+
useSelected,
|
|
615
|
+
useBreakpoint,
|
|
616
|
+
WidgetRoot,
|
|
617
|
+
EngineProvider,
|
|
618
|
+
useCanvasEngine,
|
|
619
|
+
useOps,
|
|
620
|
+
useStageHold,
|
|
621
|
+
useWorld,
|
|
622
|
+
useCommit,
|
|
623
|
+
useUndoStatus,
|
|
624
|
+
setPreviewSnapshot,
|
|
625
|
+
getPreviewSnapshot,
|
|
626
|
+
hasPreviewSnapshot,
|
|
627
|
+
subscribePreviewSnapshots,
|
|
628
|
+
WidgetPreview,
|
|
629
|
+
useTool,
|
|
630
|
+
useToolState,
|
|
631
|
+
usePresencePeers,
|
|
632
|
+
attachKeymap,
|
|
633
|
+
InfiniteCanvas,
|
|
634
|
+
REACT_VERSION
|
|
635
|
+
};
|
|
636
|
+
//# sourceMappingURL=chunk-5SJYHQI4.js.map
|