@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.
Files changed (119) hide show
  1. package/README.md +37 -2
  2. package/dist/chunk-2GUYATUM.js +643 -0
  3. package/dist/chunk-2GUYATUM.js.map +1 -0
  4. package/dist/{chunk-5S7GSXJG.js → chunk-5QY7HK6G.js} +3145 -1306
  5. package/dist/chunk-5QY7HK6G.js.map +1 -0
  6. package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
  7. package/dist/chunk-OR5FNVUE.js.map +1 -0
  8. package/dist/{chunk-FIAXPNNY.js → chunk-PKK6GLSA.js} +143 -551
  9. package/dist/chunk-PKK6GLSA.js.map +1 -0
  10. package/dist/devtools.js +508 -372
  11. package/dist/devtools.js.map +1 -1
  12. package/dist/dom.js +3 -11
  13. package/dist/ground.js +715 -0
  14. package/dist/ground.js.map +1 -0
  15. package/dist/index.js +80 -2
  16. package/dist/kernel.js +31 -1
  17. package/dist/r3f.js +605 -62
  18. package/dist/r3f.js.map +1 -1
  19. package/dist/react.js +36 -418
  20. package/dist/react.js.map +1 -1
  21. package/dist/types/packages/core/src/catalog/camera-derived.d.ts +65 -3
  22. package/dist/types/packages/core/src/catalog/gesture.d.ts +40 -4
  23. package/dist/types/packages/core/src/catalog/graph.d.ts +7 -6
  24. package/dist/types/packages/core/src/catalog/index.d.ts +1 -0
  25. package/dist/types/packages/core/src/catalog/insert.d.ts +31 -0
  26. package/dist/types/packages/core/src/catalog/pointer.d.ts +11 -0
  27. package/dist/types/packages/core/src/catalog/scene.d.ts +37 -4
  28. package/dist/types/packages/core/src/catalog/selection-presence.d.ts +7 -0
  29. package/dist/types/packages/core/src/catalog/settings-resources.d.ts +21 -0
  30. package/dist/types/packages/core/src/doc/autosave.d.ts +49 -2
  31. package/dist/types/packages/core/src/doc/bootstrap.d.ts +29 -0
  32. package/dist/types/packages/core/src/doc/channels.d.ts +9 -0
  33. package/dist/types/packages/core/src/doc/doc-commit-sink.d.ts +2 -1
  34. package/dist/types/packages/core/src/doc/doc-kit.d.ts +16 -1
  35. package/dist/types/packages/core/src/doc/envelope.d.ts +2 -1
  36. package/dist/types/packages/core/src/doc/migrate.d.ts +8 -6
  37. package/dist/types/packages/core/src/doc/schema-migrate.d.ts +50 -0
  38. package/dist/types/packages/core/src/doc/version-gate.d.ts +3 -0
  39. package/dist/types/packages/core/src/engine/commit-sink.d.ts +24 -2
  40. package/dist/types/packages/core/src/engine/engine.d.ts +11 -0
  41. package/dist/types/packages/core/src/engine/frame-control.d.ts +71 -0
  42. package/dist/types/packages/core/src/engine/frame-info.d.ts +16 -1
  43. package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +79 -6
  44. package/dist/types/packages/core/src/guards/guarded-tx.d.ts +4 -2
  45. package/dist/types/packages/core/src/helpers/churn-guard.d.ts +40 -0
  46. package/dist/types/packages/core/src/index.d.ts +15 -7
  47. package/dist/types/packages/core/src/input/queue.d.ts +11 -0
  48. package/dist/types/packages/core/src/interaction/gesture-status.d.ts +13 -0
  49. package/dist/types/packages/core/src/nav/nested-canvas.d.ts +47 -11
  50. package/dist/types/packages/core/src/ops/arrange.d.ts +15 -0
  51. package/dist/types/packages/core/src/ops/insert.d.ts +62 -0
  52. package/dist/types/packages/core/src/ops/point-pick.d.ts +11 -7
  53. package/dist/types/packages/core/src/ops/sibling-order.d.ts +70 -0
  54. package/dist/types/packages/core/src/schema/meta.d.ts +1 -0
  55. package/dist/types/packages/core/src/settings/defaults.d.ts +45 -0
  56. package/dist/types/packages/core/src/settings/ground-config.d.ts +55 -0
  57. package/dist/types/packages/core/src/systems/chrome.d.ts +6 -2
  58. package/dist/types/packages/core/src/systems/insert-ghost.d.ts +25 -0
  59. package/dist/types/packages/core/src/systems/l1-pick.d.ts +25 -11
  60. package/dist/types/packages/core/src/systems/l2-recognize.d.ts +10 -4
  61. package/dist/types/packages/core/src/systems/l3-behave.d.ts +6 -4
  62. package/dist/types/packages/core/src/systems/l3-claim.d.ts +7 -3
  63. package/dist/types/packages/core/src/systems/l3-drop.d.ts +3 -1
  64. package/dist/types/packages/core/src/systems/l3-resize.d.ts +3 -1
  65. package/dist/types/packages/core/src/systems/l3-snap.d.ts +25 -7
  66. package/dist/types/packages/core/src/systems/nav-flight.d.ts +95 -0
  67. package/dist/types/packages/core/src/widget/define-widget.d.ts +39 -0
  68. package/dist/types/packages/core/src/widget/mount-store.d.ts +11 -3
  69. package/dist/types/packages/core/src/widget/set-props.d.ts +16 -0
  70. package/dist/types/packages/core/src/widget/spawn.d.ts +21 -1
  71. package/dist/types/packages/devtools/src/attach.d.ts +77 -0
  72. package/dist/types/packages/devtools/src/dock.d.ts +38 -0
  73. package/dist/types/packages/devtools/src/gl-panel.d.ts +69 -0
  74. package/dist/types/packages/devtools/src/index.d.ts +8 -6
  75. package/dist/types/packages/dom/src/index.d.ts +1 -3
  76. package/dist/types/packages/dom/src/loop.d.ts +11 -0
  77. package/dist/types/packages/dom/src/measure-wiring.d.ts +2 -2
  78. package/dist/types/packages/dom/src/pointer-adapter.d.ts +21 -1
  79. package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +5 -0
  80. package/dist/types/packages/ground/src/index.d.ts +52 -0
  81. package/dist/types/packages/ground/src/layer.d.ts +40 -0
  82. package/dist/types/packages/ground/src/pass.d.ts +41 -0
  83. package/dist/types/packages/ground/src/passes/grid.d.ts +21 -0
  84. package/dist/types/packages/ground/src/passes/guides-collect.d.ts +14 -0
  85. package/dist/types/packages/ground/src/passes/guides.d.ts +3 -0
  86. package/dist/types/packages/ground/src/passes/soup-collect.d.ts +35 -0
  87. package/dist/types/packages/ground/src/passes/soup-mesh.d.ts +8 -0
  88. package/dist/types/packages/ground/src/passes/wires-collect.d.ts +31 -0
  89. package/dist/types/packages/ground/src/passes/wires.d.ts +10 -0
  90. package/dist/types/packages/ground/src/renderer.d.ts +15 -0
  91. package/dist/types/packages/ice/src/ground.d.ts +1 -0
  92. package/dist/types/packages/kernel/src/bezier.d.ts +15 -0
  93. package/dist/types/packages/kernel/src/coords.d.ts +15 -0
  94. package/dist/types/packages/kernel/src/easing.d.ts +11 -0
  95. package/dist/types/packages/kernel/src/index.d.ts +3 -0
  96. package/dist/types/packages/kernel/src/layout.d.ts +118 -0
  97. package/dist/types/packages/kernel/src/nav-flight.d.ts +93 -0
  98. package/dist/types/packages/r3f/src/bridge.d.ts +73 -12
  99. package/dist/types/packages/r3f/src/composite-material.d.ts +9 -0
  100. package/dist/types/packages/r3f/src/compositor-pass.d.ts +22 -0
  101. package/dist/types/packages/r3f/src/gl-root.d.ts +76 -1
  102. package/dist/types/packages/r3f/src/gl-router.d.ts +10 -0
  103. package/dist/types/packages/r3f/src/index.d.ts +3 -2
  104. package/dist/types/packages/r3f/src/island-state.d.ts +44 -0
  105. package/dist/types/packages/r3f/src/preview-capture.d.ts +19 -0
  106. package/dist/types/packages/r3f/src/self-sustain.d.ts +39 -0
  107. package/dist/types/packages/r3f/src/use-island-frame.d.ts +16 -1
  108. package/dist/types/packages/react/src/engine-context.d.ts +21 -0
  109. package/dist/types/packages/react/src/index.d.ts +4 -2
  110. package/dist/types/packages/react/src/infinite-canvas.d.ts +40 -6
  111. package/dist/types/packages/react/src/preview-snapshots.d.ts +17 -0
  112. package/dist/types/packages/react/src/widget-preview.d.ts +14 -0
  113. package/package.json +32 -61
  114. package/dist/chunk-5S7GSXJG.js.map +0 -1
  115. package/dist/chunk-BCJ47SX7.js.map +0 -1
  116. package/dist/chunk-FIAXPNNY.js.map +0 -1
  117. package/dist/types/packages/devtools/src/panel.d.ts +0 -48
  118. package/dist/types/packages/dom/src/reflectors/grid.d.ts +0 -63
  119. package/dist/types/packages/dom/src/reflectors/wires.d.ts +0 -88
package/dist/ground.js ADDED
@@ -0,0 +1,715 @@
1
+ import {
2
+ Active,
3
+ Camera,
4
+ Culled,
5
+ DEFAULT_GRID_CONFIG,
6
+ DEFAULT_SNAP_GUIDES_CONFIG,
7
+ DEFAULT_WIRES_CONFIG,
8
+ Drag,
9
+ GuideLine,
10
+ MeasuredSize,
11
+ Port,
12
+ PortAnchor,
13
+ Position,
14
+ PrefabId,
15
+ RoutedConnect,
16
+ Selected,
17
+ Size,
18
+ SpacingBar,
19
+ Wire,
20
+ WireFrom,
21
+ WirePorts,
22
+ WireTo,
23
+ defineQuery,
24
+ portSlots,
25
+ widgets
26
+ } from "./chunk-5QY7HK6G.js";
27
+ import {
28
+ portAnchor,
29
+ tessellateCubic,
30
+ wireCubic,
31
+ worldToScreen
32
+ } from "./chunk-OR5FNVUE.js";
33
+
34
+ // ../ground/src/layer.ts
35
+ import { OrthographicCamera, Scene } from "three/webgpu";
36
+ function createLayer(host, world, renderer, passes) {
37
+ const doc = host.container.ownerDocument;
38
+ const canvas = renderer.canvas;
39
+ canvas.style.position = "absolute";
40
+ canvas.style.left = "0";
41
+ canvas.style.top = "0";
42
+ canvas.style.width = "100%";
43
+ canvas.style.height = "100%";
44
+ canvas.style.display = "block";
45
+ canvas.style.pointerEvents = "none";
46
+ host.container.insertBefore(canvas, host.contentPlane);
47
+ const scene = new Scene();
48
+ const camera = new OrthographicCamera(0, 1, 0, 1, -1e3, 1e3);
49
+ for (const pass of passes) scene.add(pass.object);
50
+ const dirty = /* @__PURE__ */ new Map();
51
+ for (const pass of passes) dirty.set(pass, true);
52
+ let anyDirty = true;
53
+ const wakeAll = () => {
54
+ for (const pass of passes) dirty.set(pass, true);
55
+ anyDirty = true;
56
+ };
57
+ const unsubs = [
58
+ // Camera moves re-map every pass's screen-space geometry (2D-canvas parity).
59
+ world.reactive.observeResource(Camera, wakeAll)
60
+ ];
61
+ for (const pass of passes) {
62
+ unsubs.push(
63
+ ...pass.arm(world, () => {
64
+ dirty.set(pass, true);
65
+ anyDirty = true;
66
+ })
67
+ );
68
+ }
69
+ renderer.onReady(wakeAll);
70
+ let cssW = 0;
71
+ let cssH = 0;
72
+ let dpr = 1;
73
+ const applySize = (w, h) => {
74
+ dpr = doc.defaultView?.devicePixelRatio ?? 1;
75
+ cssW = w;
76
+ cssH = h;
77
+ renderer.setSize(w, h, dpr);
78
+ camera.left = 0;
79
+ camera.right = Math.max(1, w);
80
+ camera.top = 0;
81
+ camera.bottom = Math.max(1, h);
82
+ camera.updateProjectionMatrix();
83
+ wakeAll();
84
+ };
85
+ const initialRect = host.container.getBoundingClientRect();
86
+ applySize(initialRect.width, initialRect.height);
87
+ let ro = null;
88
+ try {
89
+ ro = new ResizeObserver((entries) => {
90
+ const entry = entries[0];
91
+ if (entry === void 0) return;
92
+ applySize(entry.contentRect.width, entry.contentRect.height);
93
+ });
94
+ ro.observe(host.container);
95
+ } catch {
96
+ ro = null;
97
+ }
98
+ let redraws = 0;
99
+ const reflector = {
100
+ name: "ground",
101
+ // Self-gated on the dirty union (wires/chrome precedent) — the every-frame
102
+ // cost is one boolean check.
103
+ always: true,
104
+ flush(w) {
105
+ if (!anyDirty || renderer.failed()) return;
106
+ if (!renderer.ready()) return;
107
+ const cam = w.getResource(Camera);
108
+ if (cam === void 0) return;
109
+ anyDirty = false;
110
+ const frame = { width: cssW, height: cssH, dpr, camera: cam };
111
+ for (const pass of passes) {
112
+ if (dirty.get(pass) === true) {
113
+ dirty.set(pass, false);
114
+ pass.collect(w, frame);
115
+ }
116
+ }
117
+ redraws++;
118
+ renderer.render(scene, camera);
119
+ },
120
+ available: () => renderer.ready() && !renderer.failed(),
121
+ redraws: () => redraws
122
+ };
123
+ return {
124
+ reflector,
125
+ invalidateAll: wakeAll,
126
+ dispose() {
127
+ for (const u of unsubs) u();
128
+ unsubs.length = 0;
129
+ ro?.disconnect();
130
+ ro = null;
131
+ for (const pass of passes) pass.dispose();
132
+ renderer.dispose();
133
+ canvas.remove();
134
+ }
135
+ };
136
+ }
137
+
138
+ // ../ground/src/passes/grid.ts
139
+ import {
140
+ clamp,
141
+ float,
142
+ fract,
143
+ length,
144
+ max,
145
+ min,
146
+ mix,
147
+ positionGeometry,
148
+ screenCoordinate,
149
+ smoothstep,
150
+ uniform,
151
+ vec4
152
+ } from "three/tsl";
153
+ import {
154
+ BufferAttribute,
155
+ BufferGeometry,
156
+ Mesh,
157
+ MeshBasicNodeMaterial,
158
+ Vector2,
159
+ Vector3
160
+ } from "three/webgpu";
161
+ var FULLSCREEN_TRIANGLE = new Float32Array([-1, -1, 0, 3, -1, 0, -1, 3, 0]);
162
+ function createGridPass(initial = {}) {
163
+ let cfg = { ...DEFAULT_GRID_CONFIG, ...initial };
164
+ const uCamera = uniform(new Vector2(0, 0));
165
+ const uZoom = uniform(1);
166
+ const uDpr = uniform(1);
167
+ const uSpacings = uniform(new Vector3(...cfg.spacings));
168
+ const uDotColor = uniform(new Vector3(...cfg.dotColor));
169
+ const uDotAlpha = uniform(cfg.dotAlpha);
170
+ const uFadeIn = uniform(new Vector2(...cfg.fadeIn));
171
+ const uFadeOut = uniform(new Vector2(...cfg.fadeOut));
172
+ const uDotRadius = uniform(new Vector2(...cfg.dotRadius));
173
+ const uLevelWeight = uniform(new Vector2(...cfg.levelWeight));
174
+ const effZoom = uZoom.mul(uDpr);
175
+ const worldPos = screenCoordinate.xy.div(effZoom).add(uCamera);
176
+ let total = float(0);
177
+ for (let i = 0; i < 3; i++) {
178
+ const spacing = i === 0 ? uSpacings.x : i === 1 ? uSpacings.y : uSpacings.z;
179
+ const cssSpacing = spacing.mul(uZoom);
180
+ const rise = cssSpacing.sub(uFadeIn.x).div(uFadeIn.y.sub(uFadeIn.x));
181
+ const fall = float(1).sub(cssSpacing.sub(uFadeOut.x).div(uFadeOut.y.sub(uFadeOut.x)));
182
+ const opacity = clamp(min(rise, fall), 0, 1);
183
+ const f = fract(worldPos.div(spacing).add(0.5)).sub(0.5);
184
+ const dist = length(f).mul(spacing).mul(effZoom);
185
+ const t = clamp(cssSpacing.sub(uFadeIn.x).div(40), 0, 1);
186
+ const radius = mix(uDotRadius.x, uDotRadius.y, t).mul(uDpr);
187
+ const dot = smoothstep(radius.sub(0.5), radius.add(0.5), dist).oneMinus();
188
+ const weight = uLevelWeight.x.add(float(i).mul(uLevelWeight.y));
189
+ total = max(total, dot.mul(opacity).mul(weight));
190
+ }
191
+ const material = new MeshBasicNodeMaterial();
192
+ material.vertexNode = vec4(positionGeometry.xy, 0, 1);
193
+ material.fragmentNode = vec4(uDotColor, clamp(total.mul(uDotAlpha), 0, 1));
194
+ material.transparent = true;
195
+ material.depthTest = false;
196
+ material.depthWrite = false;
197
+ const geometry = new BufferGeometry();
198
+ geometry.setAttribute("position", new BufferAttribute(FULLSCREEN_TRIANGLE, 3));
199
+ const mesh = new Mesh(geometry, material);
200
+ mesh.frustumCulled = false;
201
+ mesh.renderOrder = 0;
202
+ return {
203
+ name: "grid",
204
+ object: mesh,
205
+ arm: () => [],
206
+ // camera/resize dirt is layer-owned; config re-tunes call invalidate
207
+ collect(_world, frame) {
208
+ uCamera.value.set(frame.camera.x, frame.camera.y);
209
+ uZoom.value = frame.camera.zoom;
210
+ uDpr.value = frame.dpr;
211
+ },
212
+ configure(next) {
213
+ cfg = { ...cfg, ...next };
214
+ uSpacings.value.set(...cfg.spacings);
215
+ uDotColor.value.set(...cfg.dotColor);
216
+ uDotAlpha.value = cfg.dotAlpha;
217
+ uFadeIn.value.set(...cfg.fadeIn);
218
+ uFadeOut.value.set(...cfg.fadeOut);
219
+ uDotRadius.value.set(...cfg.dotRadius);
220
+ uLevelWeight.value.set(...cfg.levelWeight);
221
+ },
222
+ dispose() {
223
+ geometry.dispose();
224
+ material.dispose();
225
+ }
226
+ };
227
+ }
228
+
229
+ // ../ground/src/passes/soup-collect.ts
230
+ var SoupBuilder = class {
231
+ pos = [];
232
+ col = [];
233
+ vert(x, y, c) {
234
+ this.pos.push(x, y, 0);
235
+ this.col.push(c[0], c[1], c[2], c[3]);
236
+ }
237
+ /** Axis-aligned rect (two triangles). */
238
+ rect(x, y, w, h, c) {
239
+ this.vert(x, y, c);
240
+ this.vert(x + w, y, c);
241
+ this.vert(x + w, y + h, c);
242
+ this.vert(x, y, c);
243
+ this.vert(x + w, y + h, c);
244
+ this.vert(x, y + h, c);
245
+ }
246
+ /** Stroke segment (a→b) of `width`, flat caps — a quad across the segment normal. */
247
+ segment(ax, ay, bx, by, width, c) {
248
+ const dx = bx - ax;
249
+ const dy = by - ay;
250
+ const len = Math.hypot(dx, dy);
251
+ if (len < 1e-6) return;
252
+ const nx = -dy / len * (width / 2);
253
+ const ny = dx / len * (width / 2);
254
+ this.vert(ax + nx, ay + ny, c);
255
+ this.vert(bx + nx, by + ny, c);
256
+ this.vert(bx - nx, by - ny, c);
257
+ this.vert(ax + nx, ay + ny, c);
258
+ this.vert(bx - nx, by - ny, c);
259
+ this.vert(ax - nx, ay - ny, c);
260
+ }
261
+ /** Stroke a polyline (interleaved xy) as per-segment quads (smooth-curve joins). */
262
+ polyline(points, width, c) {
263
+ const n = Math.floor(points.length / 2);
264
+ for (let i = 1; i < n; i++) {
265
+ this.segment(
266
+ points[(i - 1) * 2],
267
+ points[(i - 1) * 2 + 1],
268
+ points[i * 2],
269
+ points[i * 2 + 1],
270
+ width,
271
+ c
272
+ );
273
+ }
274
+ }
275
+ /** Filled disc as a triangle fan (MSAA smooths the rim; 16 segs ≈ round at ≤8 px). */
276
+ disc(cx, cy, r, c, segments = 16) {
277
+ for (let i = 0; i < segments; i++) {
278
+ const a0 = i / segments * Math.PI * 2;
279
+ const a1 = (i + 1) / segments * Math.PI * 2;
280
+ this.vert(cx, cy, c);
281
+ this.vert(cx + Math.cos(a0) * r, cy + Math.sin(a0) * r, c);
282
+ this.vert(cx + Math.cos(a1) * r, cy + Math.sin(a1) * r, c);
283
+ }
284
+ }
285
+ build() {
286
+ return {
287
+ positions: new Float32Array(this.pos),
288
+ colors: new Float32Array(this.col),
289
+ vertexCount: this.pos.length / 3
290
+ };
291
+ }
292
+ };
293
+ function parseCssColor(css) {
294
+ const rgba = /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*(?:,\s*([\d.]+)\s*)?\)$/.exec(css);
295
+ if (rgba !== null) {
296
+ return [
297
+ Number(rgba[1]) / 255,
298
+ Number(rgba[2]) / 255,
299
+ Number(rgba[3]) / 255,
300
+ rgba[4] === void 0 ? 1 : Number(rgba[4])
301
+ ];
302
+ }
303
+ const hex = /^#([0-9a-f]{6})$/i.exec(css);
304
+ if (hex !== null) {
305
+ const v = Number.parseInt(hex[1], 16);
306
+ return [(v >> 16 & 255) / 255, (v >> 8 & 255) / 255, (v & 255) / 255, 1];
307
+ }
308
+ return [0.5, 0.5, 0.5, 1];
309
+ }
310
+
311
+ // ../ground/src/passes/guides-collect.ts
312
+ var guideQ = defineQuery([GuideLine]);
313
+ var barQ = defineQuery([SpacingBar]);
314
+ function collectGuides(world, frame, config = DEFAULT_SNAP_GUIDES_CONFIG) {
315
+ const soup = new SoupBuilder();
316
+ const cam = frame.camera;
317
+ const lw = config.lineWidth;
318
+ const guideColor = [config.color[0], config.color[1], config.color[2], config.guideAlpha];
319
+ const barColor = [config.color[0], config.color[1], config.color[2], config.spacingAlpha];
320
+ world.query(guideQ).each((b) => {
321
+ for (const r of b) {
322
+ const g = world.read(b.entity(r), GuideLine);
323
+ const fullSpan = g.from === g.to;
324
+ if (g.axis === "x") {
325
+ const sx = worldToScreen(g.at, 0, cam).x;
326
+ if (frame.width > 0 && (sx < -1 || sx > frame.width + 1)) continue;
327
+ const y0 = fullSpan ? 0 : worldToScreen(0, g.from, cam).y;
328
+ const y1 = fullSpan ? frame.height : worldToScreen(0, g.to, cam).y;
329
+ soup.rect(sx - lw / 2, Math.min(y0, y1), lw, Math.abs(y1 - y0), guideColor);
330
+ } else {
331
+ const sy = worldToScreen(0, g.at, cam).y;
332
+ if (frame.height > 0 && (sy < -1 || sy > frame.height + 1)) continue;
333
+ const x0 = fullSpan ? 0 : worldToScreen(g.from, 0, cam).x;
334
+ const x1 = fullSpan ? frame.width : worldToScreen(g.to, 0, cam).x;
335
+ soup.rect(Math.min(x0, x1), sy - lw / 2, Math.abs(x1 - x0), lw, guideColor);
336
+ }
337
+ }
338
+ });
339
+ world.query(barQ).each((b) => {
340
+ for (const r of b) {
341
+ const s = world.read(b.entity(r), SpacingBar);
342
+ const t = config.tickPx;
343
+ if (s.axis === "x") {
344
+ const a = worldToScreen(s.from, s.perp, cam);
345
+ const bp = worldToScreen(s.to, s.perp, cam);
346
+ if (frame.width > 0 && (Math.max(a.x, bp.x) < 0 || Math.min(a.x, bp.x) > frame.width)) continue;
347
+ soup.rect(Math.min(a.x, bp.x), a.y - lw / 2, Math.abs(bp.x - a.x), lw, barColor);
348
+ soup.rect(a.x - lw / 2, a.y - t, lw, t * 2, barColor);
349
+ soup.rect(bp.x - lw / 2, bp.y - t, lw, t * 2, barColor);
350
+ } else {
351
+ const a = worldToScreen(s.perp, s.from, cam);
352
+ const bp = worldToScreen(s.perp, s.to, cam);
353
+ if (frame.height > 0 && (Math.max(a.y, bp.y) < 0 || Math.min(a.y, bp.y) > frame.height)) continue;
354
+ soup.rect(a.x - lw / 2, Math.min(a.y, bp.y), lw, Math.abs(bp.y - a.y), barColor);
355
+ soup.rect(a.x - t, a.y - lw / 2, t * 2, lw, barColor);
356
+ soup.rect(bp.x - t, bp.y - lw / 2, t * 2, lw, barColor);
357
+ }
358
+ }
359
+ });
360
+ return soup.build();
361
+ }
362
+
363
+ // ../ground/src/passes/soup-mesh.ts
364
+ import { attribute } from "three/tsl";
365
+ import { BufferAttribute as BufferAttribute2, BufferGeometry as BufferGeometry2, DoubleSide, Mesh as Mesh2, MeshBasicNodeMaterial as MeshBasicNodeMaterial2 } from "three/webgpu";
366
+ function createSoupMesh(renderOrder) {
367
+ const material = new MeshBasicNodeMaterial2();
368
+ material.fragmentNode = attribute("color", "vec4");
369
+ material.transparent = true;
370
+ material.depthTest = false;
371
+ material.depthWrite = false;
372
+ material.side = DoubleSide;
373
+ let geometry = new BufferGeometry2();
374
+ let capacity = 0;
375
+ const mesh = new Mesh2(geometry, material);
376
+ mesh.frustumCulled = false;
377
+ mesh.renderOrder = renderOrder;
378
+ mesh.visible = false;
379
+ return {
380
+ mesh,
381
+ update(soup) {
382
+ if (soup.vertexCount === 0) {
383
+ mesh.visible = false;
384
+ geometry.setDrawRange(0, 0);
385
+ return;
386
+ }
387
+ if (soup.vertexCount > capacity) {
388
+ capacity = Math.max(soup.vertexCount, capacity * 2, 256);
389
+ const old = geometry;
390
+ geometry = new BufferGeometry2();
391
+ const pos = new Float32Array(capacity * 3);
392
+ const col = new Float32Array(capacity * 4);
393
+ pos.set(soup.positions);
394
+ col.set(soup.colors);
395
+ geometry.setAttribute("position", new BufferAttribute2(pos, 3));
396
+ geometry.setAttribute("color", new BufferAttribute2(col, 4));
397
+ mesh.geometry = geometry;
398
+ old.dispose();
399
+ } else {
400
+ const pos = geometry.getAttribute("position");
401
+ const col = geometry.getAttribute("color");
402
+ pos.array.set(soup.positions);
403
+ col.array.set(soup.colors);
404
+ pos.needsUpdate = true;
405
+ col.needsUpdate = true;
406
+ }
407
+ geometry.setDrawRange(0, soup.vertexCount);
408
+ mesh.visible = true;
409
+ },
410
+ dispose() {
411
+ geometry.dispose();
412
+ material.dispose();
413
+ }
414
+ };
415
+ }
416
+
417
+ // ../ground/src/passes/guides.ts
418
+ function createGuidesPass(initial = {}) {
419
+ const config = { ...DEFAULT_SNAP_GUIDES_CONFIG, ...initial };
420
+ const soup = createSoupMesh(2);
421
+ return {
422
+ name: "snap-guides",
423
+ object: soup.mesh,
424
+ arm(world, wake) {
425
+ return [
426
+ world.reactive.observeQuery(guideQ, wake, { cols: [GuideLine] }),
427
+ world.reactive.observeQuery(barQ, wake, { cols: [SpacingBar] })
428
+ ];
429
+ },
430
+ collect(world, frame) {
431
+ soup.update(collectGuides(world, frame, config));
432
+ },
433
+ dispose() {
434
+ soup.dispose();
435
+ }
436
+ };
437
+ }
438
+
439
+ // ../ground/src/passes/wires-collect.ts
440
+ var wireQ = defineQuery([Wire]);
441
+ var geometryQ = defineQuery([Position, Size]);
442
+ var measuredQ = defineQuery([MeasuredSize]);
443
+ var selectedQ = defineQuery([Selected]);
444
+ var portQ = defineQuery([Port, PortAnchor]);
445
+ var routedConnectQ = defineQuery([RoutedConnect]);
446
+ var connectDragQ = defineQuery([RoutedConnect, Drag]);
447
+ var activeQ = defineQuery([Active]);
448
+ function outOfScope(world, e) {
449
+ return world.hasTag(e, Culled) && !world.hasTag(e, Active);
450
+ }
451
+ var TESSELLATION_TOLERANCE_PX = 0.25;
452
+ var DASH_PX = 6;
453
+ var GAP_PX = 4;
454
+ function readRect(world, e) {
455
+ const p = world.get(e, Position);
456
+ if (p === void 0) return void 0;
457
+ const measured = world.get(e, MeasuredSize);
458
+ const s = measured !== void 0 && measured.w > 0 ? measured : world.get(e, Size);
459
+ if (s === void 0) return void 0;
460
+ return { x: p.x, y: p.y, width: s.w, height: s.h };
461
+ }
462
+ var slotCache = /* @__PURE__ */ new Map();
463
+ function slotOf(wt, portId) {
464
+ let slots = slotCache.get(wt.type);
465
+ if (slots === void 0) {
466
+ slots = portSlots(wt.ports);
467
+ slotCache.set(wt.type, slots);
468
+ }
469
+ return slots.get(portId);
470
+ }
471
+ function endpointAnchor(world, widget, portId) {
472
+ if (widget === void 0) return void 0;
473
+ const rect = readRect(world, widget);
474
+ if (rect === void 0) return void 0;
475
+ const type = world.get(widget, PrefabId)?.id;
476
+ if (typeof type !== "string") return void 0;
477
+ const wt = widgets.get(type);
478
+ if (wt === void 0) return void 0;
479
+ const slot = slotOf(wt, portId);
480
+ if (slot === void 0) return void 0;
481
+ return { anchor: rect, slot };
482
+ }
483
+ function cubicToScreen(c, cam) {
484
+ const p0 = worldToScreen(c.x0, c.y0, cam);
485
+ const p1 = worldToScreen(c.x1, c.y1, cam);
486
+ const p2 = worldToScreen(c.x2, c.y2, cam);
487
+ const p3 = worldToScreen(c.x3, c.y3, cam);
488
+ return { x0: p0.x, y0: p0.y, x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y, x3: p3.x, y3: p3.y };
489
+ }
490
+ function pointAtLength(t, d) {
491
+ const arc = t.arcLength;
492
+ const n = arc.length;
493
+ let lo = 0;
494
+ let hi = n - 1;
495
+ while (lo < hi) {
496
+ const mid = lo + hi >> 1;
497
+ if (arc[mid] < d) lo = mid + 1;
498
+ else hi = mid;
499
+ }
500
+ const i = Math.max(1, lo);
501
+ const a = arc[i - 1];
502
+ const b = arc[i];
503
+ const f = b > a ? (d - a) / (b - a) : 0;
504
+ const x0 = t.points[(i - 1) * 2];
505
+ const y0 = t.points[(i - 1) * 2 + 1];
506
+ const x1 = t.points[i * 2];
507
+ const y1 = t.points[i * 2 + 1];
508
+ return { x: x0 + (x1 - x0) * f, y: y0 + (y1 - y0) * f };
509
+ }
510
+ function dashedStroke(soup, t, width, c) {
511
+ const total = t.arcLength[t.arcLength.length - 1];
512
+ for (let d = 0; d < total; d += DASH_PX + GAP_PX) {
513
+ const a = pointAtLength(t, d);
514
+ const b = pointAtLength(t, Math.min(d + DASH_PX, total));
515
+ soup.segment(a.x, a.y, b.x, b.y, width, c);
516
+ }
517
+ }
518
+ function collectWires(world, frame, config = DEFAULT_WIRES_CONFIG, preview) {
519
+ const soup = new SoupBuilder();
520
+ const cam = frame.camera;
521
+ const wireColor = parseCssColor(config.wireColor);
522
+ const selectedColor = parseCssColor(config.selectedColor);
523
+ world.query(wireQ).each((batch) => {
524
+ for (const r of batch) {
525
+ const wire = batch.entity(r);
526
+ const ports = world.get(wire, WirePorts);
527
+ if (ports === void 0) continue;
528
+ const fromWidget = world.getRelation(wire, WireFrom);
529
+ const toWidget = world.getRelation(wire, WireTo);
530
+ if (fromWidget !== void 0 && outOfScope(world, fromWidget)) continue;
531
+ if (toWidget !== void 0 && outOfScope(world, toWidget)) continue;
532
+ const from = endpointAnchor(world, fromWidget, ports.from ?? "");
533
+ const to = endpointAnchor(world, toWidget, ports.to ?? "");
534
+ if (from === void 0 || to === void 0) continue;
535
+ const cubic = wireCubic(
536
+ portAnchor(from.anchor, from.slot),
537
+ from.slot.side,
538
+ portAnchor(to.anchor, to.slot),
539
+ to.slot.side
540
+ );
541
+ const selected = world.hasTag(wire, Selected);
542
+ const t = tessellateCubic(cubicToScreen(cubic, cam), TESSELLATION_TOLERANCE_PX);
543
+ soup.polyline(t.points, selected ? config.selectedWidth : config.wireWidth, selected ? selectedColor : wireColor);
544
+ }
545
+ });
546
+ if (preview?.active === true) {
547
+ const stub = Math.max(24, Math.abs(preview.tx - preview.sx) * 0.5);
548
+ const cubic = {
549
+ x0: preview.sx,
550
+ y0: preview.sy,
551
+ x1: preview.sx + stub,
552
+ y1: preview.sy,
553
+ x2: preview.tx - stub,
554
+ y2: preview.ty,
555
+ x3: preview.tx,
556
+ y3: preview.ty
557
+ };
558
+ const t = tessellateCubic(cubicToScreen(cubic, cam), TESSELLATION_TOLERANCE_PX);
559
+ if (preview.compatible) soup.polyline(t.points, config.wireWidth, selectedColor);
560
+ else dashedStroke(soup, t, config.wireWidth, wireColor);
561
+ }
562
+ const connecting = world.firstOf(routedConnectQ) !== void 0;
563
+ const dotColor = parseCssColor(connecting ? config.portActiveColor : config.portColor);
564
+ world.query(portQ).each((batch) => {
565
+ const ax = batch.col(PortAnchor).x;
566
+ const ay = batch.col(PortAnchor).y;
567
+ for (const r of batch) {
568
+ const s = worldToScreen(ax[r], ay[r], cam);
569
+ soup.disc(s.x, s.y, config.portRadius, dotColor);
570
+ }
571
+ });
572
+ return soup.build();
573
+ }
574
+
575
+ // ../ground/src/passes/wires.ts
576
+ function createWiresPass(initial = {}, readPreview) {
577
+ const config = { ...DEFAULT_WIRES_CONFIG, ...initial };
578
+ const soup = createSoupMesh(1);
579
+ return {
580
+ name: "wires",
581
+ object: soup.mesh,
582
+ arm(world, wake) {
583
+ return [
584
+ world.reactive.observeQuery(wireQ, wake, { cols: [] }),
585
+ world.reactive.observeQuery(geometryQ, wake, { cols: [Position, Size] }),
586
+ // MeasuredSize rides its own observer (readRect prefers it over Size):
587
+ // in geometryQ's cols it would REQUIRE the rider and drop plain widgets.
588
+ world.reactive.observeQuery(measuredQ, wake, { cols: [MeasuredSize] }),
589
+ world.reactive.observeQuery(selectedQ, wake, { cols: [] }),
590
+ // Nav churn: Active membership flips gate the scope filter (a wire
591
+ // whose endpoints left the current frame must disappear this frame).
592
+ world.reactive.observeQuery(activeQ, wake, { cols: [] }),
593
+ world.reactive.observeQuery(portQ, wake, { cols: [PortAnchor] }),
594
+ world.reactive.observeQuery(routedConnectQ, wake, { cols: [] }),
595
+ world.reactive.observeQuery(connectDragQ, wake, { cols: [Drag] })
596
+ ];
597
+ },
598
+ collect(world, frame) {
599
+ soup.update(collectWires(world, frame, config, readPreview?.()));
600
+ },
601
+ dispose() {
602
+ soup.dispose();
603
+ }
604
+ };
605
+ }
606
+
607
+ // ../ground/src/renderer.ts
608
+ import { WebGPURenderer } from "three/webgpu";
609
+ function createGroundRenderer(doc, opts = {}) {
610
+ const canvas = doc.createElement("canvas");
611
+ let renderer = null;
612
+ let isReady = false;
613
+ let isFailed = false;
614
+ const readyCbs = [];
615
+ const gpu = globalThis.navigator?.gpu;
616
+ if (gpu == null || opts.forceWebGL === true) {
617
+ let gl2 = null;
618
+ try {
619
+ gl2 = doc.createElement("canvas").getContext("webgl2");
620
+ } catch {
621
+ gl2 = null;
622
+ }
623
+ if (gl2 === null) isFailed = true;
624
+ }
625
+ if (!isFailed) {
626
+ try {
627
+ renderer = new WebGPURenderer({
628
+ canvas,
629
+ alpha: true,
630
+ // transparent clear — the page background shows through P0
631
+ antialias: true,
632
+ ...opts.forceWebGL === true ? { forceWebGL: true } : {}
633
+ });
634
+ renderer.init().then(() => {
635
+ isReady = true;
636
+ for (const cb of readyCbs.splice(0)) cb();
637
+ }).catch(() => {
638
+ isFailed = true;
639
+ });
640
+ } catch {
641
+ isFailed = true;
642
+ }
643
+ }
644
+ let lastW = 0;
645
+ let lastH = 0;
646
+ let lastDpr = 0;
647
+ return {
648
+ canvas,
649
+ ready: () => isReady,
650
+ failed: () => isFailed,
651
+ onReady(cb) {
652
+ if (isReady) cb();
653
+ else readyCbs.push(cb);
654
+ },
655
+ setSize(cssW, cssH, dpr) {
656
+ if (renderer === null || cssW === lastW && cssH === lastH && dpr === lastDpr) return;
657
+ lastW = cssW;
658
+ lastH = cssH;
659
+ lastDpr = dpr;
660
+ try {
661
+ renderer.setPixelRatio(dpr);
662
+ renderer.setSize(Math.max(1, cssW), Math.max(1, cssH), false);
663
+ } catch {
664
+ isFailed = true;
665
+ }
666
+ },
667
+ render(scene, camera) {
668
+ if (renderer === null || !isReady || isFailed) return;
669
+ try {
670
+ renderer.render(scene, camera);
671
+ } catch {
672
+ isFailed = true;
673
+ }
674
+ },
675
+ dispose() {
676
+ try {
677
+ renderer?.dispose();
678
+ } catch {
679
+ }
680
+ renderer = null;
681
+ }
682
+ };
683
+ }
684
+
685
+ // ../ground/src/index.ts
686
+ function ground(opts = {}) {
687
+ return (ctx) => {
688
+ const doc = ctx.host.container.ownerDocument;
689
+ const renderer = opts.rendererOverride ?? createGroundRenderer(doc, { forceWebGL: opts.forceWebGL === true });
690
+ const grid = createGridPass(opts.grid ?? {});
691
+ const wires = createWiresPass(opts.wires ?? {}, ctx.readWirePreview);
692
+ const guides = createGuidesPass(opts.guides ?? {});
693
+ const passes = [grid, wires, guides, ...opts.passes ?? []];
694
+ const layer = createLayer(ctx.host, ctx.world, renderer, passes);
695
+ return {
696
+ reflector: layer.reflector,
697
+ configureGrid(cfg) {
698
+ grid.configure(cfg);
699
+ layer.invalidateAll();
700
+ },
701
+ dispose: layer.dispose
702
+ };
703
+ };
704
+ }
705
+ export {
706
+ DEFAULT_GRID_CONFIG,
707
+ DEFAULT_SNAP_GUIDES_CONFIG,
708
+ DEFAULT_WIRES_CONFIG,
709
+ SoupBuilder,
710
+ collectGuides,
711
+ collectWires,
712
+ ground,
713
+ parseCssColor
714
+ };
715
+ //# sourceMappingURL=ground.js.map