@ultrst-armada/floks 0.0.0-stage → 1.0.1

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 (93) hide show
  1. package/README.md +2 -3
  2. package/index.d.ts +2 -0
  3. package/index.js +2 -0
  4. package/package.json +69 -4
  5. package/packages/canvas-ui/dist/canvas/index.d.ts +54 -0
  6. package/packages/canvas-ui/dist/canvas/index.js +12 -0
  7. package/packages/canvas-ui/dist/chunk-34YRDZPJ.js +189 -0
  8. package/packages/canvas-ui/dist/chunk-6VB74RCH.js +147 -0
  9. package/packages/canvas-ui/dist/chunk-OOTTFWXA.js +508 -0
  10. package/packages/canvas-ui/dist/chunk-QTTWWBNN.js +114 -0
  11. package/packages/canvas-ui/dist/chunk-SA3JADUG.js +160 -0
  12. package/packages/canvas-ui/dist/chunk-U2SC3RMJ.js +391 -0
  13. package/packages/canvas-ui/dist/chunk-XVAHT5WE.js +718 -0
  14. package/packages/canvas-ui/dist/dock/index.d.ts +37 -0
  15. package/packages/canvas-ui/dist/dock/index.js +10 -0
  16. package/packages/canvas-ui/dist/editor/index.d.ts +44 -0
  17. package/packages/canvas-ui/dist/editor/index.js +10 -0
  18. package/packages/canvas-ui/dist/graph-2d/index.d.ts +270 -0
  19. package/packages/canvas-ui/dist/graph-2d/index.js +50 -0
  20. package/packages/canvas-ui/dist/index.d.ts +176 -0
  21. package/packages/canvas-ui/dist/index.js +1171 -0
  22. package/packages/canvas-ui/dist/predictive/index.d.ts +75 -0
  23. package/packages/canvas-ui/dist/predictive/index.js +13 -0
  24. package/packages/canvas-ui/dist/tile-menu/index.d.ts +45 -0
  25. package/packages/canvas-ui/dist/tile-menu/index.js +12 -0
  26. package/packages/canvas-ui/dist/types-D3RRfQUo.d.ts +46 -0
  27. package/packages/canvas-ui/floks.manifest.json +35 -0
  28. package/packages/canvas-ui/package.json +82 -0
  29. package/packages/ui/dist/BottomStrip-DYhAUQR4.d.ts +12 -0
  30. package/packages/ui/dist/chunk-5ZMXFCD4.js +0 -0
  31. package/packages/ui/dist/chunk-62ITRDJX.js +141 -0
  32. package/packages/ui/dist/chunk-A2CDQISJ.js +32 -0
  33. package/packages/ui/dist/chunk-ATQPZLJX.js +238 -0
  34. package/packages/ui/dist/chunk-C32CB5WO.js +1161 -0
  35. package/packages/ui/dist/chunk-C3L53YN4.js +372 -0
  36. package/packages/ui/dist/chunk-DZJ3NSCX.js +665 -0
  37. package/packages/ui/dist/chunk-ELZOBTWX.js +562 -0
  38. package/packages/ui/dist/chunk-GKP6KHTI.js +75 -0
  39. package/packages/ui/dist/chunk-GWW3BGOB.js +238 -0
  40. package/packages/ui/dist/chunk-KIFQA7L6.js +167 -0
  41. package/packages/ui/dist/chunk-KYFSUQVT.js +769 -0
  42. package/packages/ui/dist/chunk-OR5ZNJI4.js +62 -0
  43. package/packages/ui/dist/chunk-RLQ2ALJG.js +182 -0
  44. package/packages/ui/dist/chunk-S227WJ5E.js +152 -0
  45. package/packages/ui/dist/chunk-SLJBDDSN.js +0 -0
  46. package/packages/ui/dist/chunk-TXKLROFC.js +370 -0
  47. package/packages/ui/dist/chunk-UOR4P6PO.js +32 -0
  48. package/packages/ui/dist/chunk-VEWPTZGO.js +351 -0
  49. package/packages/ui/dist/chunk-W42SJGOJ.js +19 -0
  50. package/packages/ui/dist/chunk-XXIH2HWI.js +297 -0
  51. package/packages/ui/dist/chunk-ZQK5I5ZL.js +277 -0
  52. package/packages/ui/dist/components/auth/index.css +183 -0
  53. package/packages/ui/dist/components/auth/index.d.ts +10 -0
  54. package/packages/ui/dist/components/auth/index.js +8 -0
  55. package/packages/ui/dist/components/base/index.css +183 -0
  56. package/packages/ui/dist/components/base/index.d.ts +74 -0
  57. package/packages/ui/dist/components/base/index.js +33 -0
  58. package/packages/ui/dist/components/bottom-strip/index.css +183 -0
  59. package/packages/ui/dist/components/bottom-strip/index.d.ts +67 -0
  60. package/packages/ui/dist/components/bottom-strip/index.js +22 -0
  61. package/packages/ui/dist/components/composite/index.css +273 -0
  62. package/packages/ui/dist/components/composite/index.d.ts +104 -0
  63. package/packages/ui/dist/components/composite/index.js +33 -0
  64. package/packages/ui/dist/components/containers/index.css +404 -0
  65. package/packages/ui/dist/components/containers/index.d.ts +93 -0
  66. package/packages/ui/dist/components/containers/index.js +35 -0
  67. package/packages/ui/dist/components/layout/index.css +246 -0
  68. package/packages/ui/dist/components/layout/index.d.ts +53 -0
  69. package/packages/ui/dist/components/layout/index.js +16 -0
  70. package/packages/ui/dist/components/settings/index.css +183 -0
  71. package/packages/ui/dist/components/settings/index.d.ts +14 -0
  72. package/packages/ui/dist/components/settings/index.js +12 -0
  73. package/packages/ui/dist/components/workspace/index.d.ts +101 -0
  74. package/packages/ui/dist/components/workspace/index.js +16 -0
  75. package/packages/ui/dist/hierarchy/index.css +183 -0
  76. package/packages/ui/dist/hierarchy/index.d.ts +233 -0
  77. package/packages/ui/dist/hierarchy/index.js +74 -0
  78. package/packages/ui/dist/index.css +467 -0
  79. package/packages/ui/dist/index.d.ts +16 -0
  80. package/packages/ui/dist/index.js +320 -0
  81. package/packages/ui/dist/palette/index.d.ts +29 -0
  82. package/packages/ui/dist/palette/index.js +27 -0
  83. package/packages/ui/dist/palettes-De5u5iID.d.ts +394 -0
  84. package/packages/ui/dist/theme/index.d.ts +12063 -0
  85. package/packages/ui/dist/theme/index.js +96 -0
  86. package/packages/ui/floks.manifest.json +119 -0
  87. package/packages/ui/package.json +92 -0
  88. package/packages/ui/src/styles/base.css +130 -0
  89. package/packages/ui/src/styles/composite.css +6 -0
  90. package/packages/ui/src/styles/containers.css +10 -0
  91. package/packages/ui/src/styles/details-panel.css +147 -0
  92. package/packages/ui/src/styles/layout.css +58 -0
  93. package/packages/ui/src/theme/tokens.css +93 -0
@@ -0,0 +1,718 @@
1
+ import {
2
+ Node2DBase,
3
+ getSocketScreenPosition
4
+ } from "./chunk-U2SC3RMJ.js";
5
+
6
+ // src/graph-2d/types.ts
7
+ var COORD_SCALE = 40;
8
+ var BIOME_CONFIGS = {
9
+ zenith: { bg: "#030712", gridColor: "rgba(56, 189, 248, 0.08)", aoeColor: "rgba(56, 189, 248, 0.05)" },
10
+ nadir: { bg: "#0a0503", gridColor: "rgba(251, 146, 60, 0.08)", aoeColor: "rgba(251, 146, 60, 0.05)" },
11
+ abyss: { bg: "#020617", gridColor: "rgba(99, 102, 241, 0.08)", aoeColor: "rgba(99, 102, 241, 0.05)" },
12
+ default: { bg: "#050508", gridColor: "rgba(255, 255, 255, 0.06)", aoeColor: "rgba(255, 255, 255, 0.04)" }
13
+ };
14
+
15
+ // src/graph-2d/useCanvas2DCoordinates.ts
16
+ import { useState, useRef, useEffect, useCallback } from "react";
17
+ function useCanvas2DCoordinates() {
18
+ const [camera, setCamera] = useState({ x: 0, y: 0, zoom: 1 });
19
+ const [containerSize, setContainerSize] = useState({ width: 800, height: 600 });
20
+ const containerRef = useRef(null);
21
+ useEffect(() => {
22
+ if (!containerRef.current) return;
23
+ const observer = new ResizeObserver((entries) => {
24
+ for (const entry of entries) {
25
+ setContainerSize({
26
+ width: entry.contentRect.width,
27
+ height: entry.contentRect.height
28
+ });
29
+ }
30
+ });
31
+ observer.observe(containerRef.current);
32
+ return () => observer.disconnect();
33
+ }, []);
34
+ const worldToScreen = useCallback(
35
+ (wx, wz) => {
36
+ const cx = containerSize.width / 2;
37
+ const cy = containerSize.height / 2;
38
+ return {
39
+ x: cx + (wx - camera.x) * COORD_SCALE * camera.zoom,
40
+ y: cy + (wz - camera.y) * COORD_SCALE * camera.zoom
41
+ };
42
+ },
43
+ [camera, containerSize]
44
+ );
45
+ const screenToWorld = useCallback(
46
+ (sx, sy) => {
47
+ const rect = containerRef.current?.getBoundingClientRect() || { left: 0, top: 0 };
48
+ const localX = sx - rect.left;
49
+ const localY = sy - rect.top;
50
+ const cx = containerSize.width / 2;
51
+ const cy = containerSize.height / 2;
52
+ const wx = (localX - cx) / (camera.zoom * COORD_SCALE) + camera.x;
53
+ const wz = (localY - cy) / (camera.zoom * COORD_SCALE) + camera.y;
54
+ return [wx, 0, wz];
55
+ },
56
+ [camera, containerSize]
57
+ );
58
+ const setCameraPosition = useCallback((x, y, zoom) => {
59
+ setCamera((prev) => ({
60
+ x,
61
+ y,
62
+ zoom: zoom !== void 0 ? zoom : prev.zoom
63
+ }));
64
+ }, []);
65
+ return {
66
+ camera,
67
+ setCamera,
68
+ setCameraPosition,
69
+ containerSize,
70
+ containerRef,
71
+ worldToScreen,
72
+ screenToWorld
73
+ };
74
+ }
75
+
76
+ // src/graph-2d/useCanvas2DMagnetic.ts
77
+ import { useState as useState2, useMemo, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
78
+ import { MagneticGraphManager } from "@ultrst-armada/kloks/state-graph";
79
+ function useCanvas2DMagnetic({
80
+ nodes,
81
+ connections,
82
+ camera,
83
+ setCameraPosition,
84
+ onMoveNode,
85
+ onConnectRequest
86
+ }) {
87
+ const manager = useMemo(() => new MagneticGraphManager(320), []);
88
+ const [magneticEnabled, setMagneticEnabled] = useState2(true);
89
+ const [autoSolidifyEnabled, setAutoSolidifyEnabled] = useState2(false);
90
+ const [activeTraces, setActiveTraces] = useState2([]);
91
+ const [landmarks, setLandmarks] = useState2([]);
92
+ const onConnectRequestRef = useRef2(onConnectRequest);
93
+ onConnectRequestRef.current = onConnectRequest;
94
+ useEffect2(() => {
95
+ if (!magneticEnabled) {
96
+ setActiveTraces((prev) => prev.length === 0 ? prev : []);
97
+ return;
98
+ }
99
+ const traces = manager.computeTraces(nodes);
100
+ setActiveTraces((prev) => {
101
+ if (prev.length === 0 && traces.length === 0) return prev;
102
+ if (prev.length === traces.length && prev.every(
103
+ (t, i) => t.sourceNodeId === traces[i].sourceNodeId && t.targetNodeId === traces[i].targetNodeId && t.sourceHandleId === traces[i].sourceHandleId && t.targetHandleId === traces[i].targetHandleId
104
+ )) {
105
+ return prev;
106
+ }
107
+ return traces;
108
+ });
109
+ if (autoSolidifyEnabled && traces.length > 0) {
110
+ const resolved = manager.resolveAutoSolidification(nodes, traces, connections);
111
+ for (const res of resolved) {
112
+ const alreadyExists = connections.some(
113
+ (c) => c.fromNodeId === res.sourceNodeId && c.toNodeId === res.targetNodeId && (c.fromSocketId === res.sourceHandleId || c.fromPort === res.sourceHandleId) && (c.toSocketId === res.targetHandleId || c.toPort === res.targetHandleId)
114
+ );
115
+ if (!alreadyExists) {
116
+ onConnectRequestRef.current(res.sourceNodeId, res.targetNodeId, res.sourceHandleId, res.targetHandleId);
117
+ }
118
+ }
119
+ }
120
+ }, [nodes, connections, magneticEnabled, autoSolidifyEnabled, manager]);
121
+ const solidifyTrace = useCallback2(
122
+ (trace) => {
123
+ onConnectRequestRef.current(trace.sourceNodeId, trace.targetNodeId, trace.sourceHandleId, trace.targetHandleId);
124
+ },
125
+ []
126
+ );
127
+ const saveLandmark = useCallback2(
128
+ (name) => {
129
+ const lm = manager.saveLandmark(name, camera);
130
+ setLandmarks(manager.getLandmarks());
131
+ return lm;
132
+ },
133
+ [manager, camera]
134
+ );
135
+ const jumpToLandmark = useCallback2(
136
+ (landmark) => {
137
+ setCameraPosition(landmark.camera.x, landmark.camera.y, landmark.camera.zoom);
138
+ },
139
+ [setCameraPosition]
140
+ );
141
+ return {
142
+ manager,
143
+ magneticEnabled,
144
+ setMagneticEnabled,
145
+ autoSolidifyEnabled,
146
+ setAutoSolidifyEnabled,
147
+ activeTraces,
148
+ solidifyTrace,
149
+ landmarks,
150
+ saveLandmark,
151
+ jumpToLandmark
152
+ };
153
+ }
154
+
155
+ // src/graph-2d/NodeRenderer2D.tsx
156
+ import { jsx, jsxs } from "react/jsx-runtime";
157
+ var TriggerNode2D = (props) => {
158
+ const trigNode = props.node;
159
+ const interval = trigNode.intervalBeats || 1;
160
+ return /* @__PURE__ */ jsx(
161
+ Node2DBase,
162
+ {
163
+ ...props,
164
+ extraHeaderBadge: /* @__PURE__ */ jsxs("span", { className: "text-[8px] bg-amber-950 border border-amber-500/50 text-amber-300 px-1 py-0.2 rounded font-mono font-bold", children: [
165
+ interval,
166
+ " BEATS"
167
+ ] })
168
+ }
169
+ );
170
+ };
171
+ var ContainerNode2D = (props) => {
172
+ const containerNode = props.node;
173
+ const cType = containerNode.containerType || "midi";
174
+ let badge = `${cType.toUpperCase()}`;
175
+ if (cType === "audio") {
176
+ badge = containerNode.audioFormat === "stereo" ? "STEREO" : "MONO";
177
+ }
178
+ return /* @__PURE__ */ jsx(
179
+ Node2DBase,
180
+ {
181
+ ...props,
182
+ extraHeaderBadge: /* @__PURE__ */ jsx("span", { className: "text-[8px] bg-sky-950 border border-sky-500/50 text-sky-300 px-1 py-0.2 rounded font-mono font-bold", children: badge })
183
+ }
184
+ );
185
+ };
186
+ var ModuleNode2D = (props) => {
187
+ const modNode = props.node;
188
+ return /* @__PURE__ */ jsx(
189
+ Node2DBase,
190
+ {
191
+ ...props,
192
+ extraHeaderBadge: /* @__PURE__ */ jsxs("span", { className: "text-[8px] bg-purple-950 border border-purple-600/60 text-purple-300 px-1 py-0.2 rounded font-bold", children: [
193
+ modNode.innerNodes?.length || 0,
194
+ " NODES"
195
+ ] })
196
+ }
197
+ );
198
+ };
199
+ var EffectNode2D = (props) => {
200
+ const effectNode = props.node;
201
+ let badgeText = `R: ${effectNode.radius || 6}m`;
202
+ let badgeColor = "bg-emerald-950 border-emerald-500/50 text-emerald-300";
203
+ if (effectNode.effectType === "compressor") {
204
+ const sc = effectNode.compressorConfig?.sidechainEnabled ? " \u2022 SC" : "";
205
+ badgeText = `${effectNode.compressorConfig?.ratio ?? 4}:1${sc}`;
206
+ badgeColor = "bg-indigo-950 border-indigo-500/50 text-indigo-300";
207
+ } else if (effectNode.effectType === "multiband_compressor") {
208
+ const sc = effectNode.multibandCompressorConfig?.sidechainEnabled ? " \u2022 SC" : "";
209
+ badgeText = `3-BAND${sc}`;
210
+ badgeColor = "bg-violet-950 border-violet-500/50 text-violet-300";
211
+ } else if (effectNode.effectType === "eq") {
212
+ badgeText = "4-BAND EQ";
213
+ badgeColor = "bg-teal-950 border-teal-500/50 text-teal-300";
214
+ } else if (effectNode.effectType === "limiter") {
215
+ badgeText = `CEIL: ${effectNode.limiterConfig?.ceiling ?? -0.1}dB`;
216
+ badgeColor = "bg-amber-950 border-amber-500/50 text-amber-300";
217
+ } else if (effectNode.effectType === "reverb") {
218
+ badgeText = `${effectNode.reverbConfig?.decayTime ?? 2.5}s`;
219
+ badgeColor = "bg-purple-950 border-purple-500/50 text-purple-300";
220
+ } else if (effectNode.effectType === "delay") {
221
+ badgeText = `${effectNode.delayConfig?.delayBeats ?? 0.5} B`;
222
+ badgeColor = "bg-blue-950 border-blue-500/50 text-blue-300";
223
+ } else if (effectNode.effectType === "saturation") {
224
+ badgeText = (effectNode.saturationConfig?.saturationType || "tape").toUpperCase();
225
+ badgeColor = "bg-rose-950 border-rose-500/50 text-rose-300";
226
+ }
227
+ return /* @__PURE__ */ jsx(
228
+ Node2DBase,
229
+ {
230
+ ...props,
231
+ extraHeaderBadge: /* @__PURE__ */ jsx("span", { className: `text-[8px] ${badgeColor} border px-1 py-0.2 rounded font-mono font-bold uppercase`, children: badgeText })
232
+ }
233
+ );
234
+ };
235
+ var NodeRenderer2D = (props) => {
236
+ switch (props.node.type) {
237
+ case "trigger":
238
+ return /* @__PURE__ */ jsx(TriggerNode2D, { ...props });
239
+ case "container":
240
+ return /* @__PURE__ */ jsx(ContainerNode2D, { ...props });
241
+ case "module":
242
+ return /* @__PURE__ */ jsx(ModuleNode2D, { ...props });
243
+ case "effect":
244
+ return /* @__PURE__ */ jsx(EffectNode2D, { ...props });
245
+ default:
246
+ return /* @__PURE__ */ jsx(Node2DBase, { ...props });
247
+ }
248
+ };
249
+
250
+ // src/graph-2d/Wire2D.tsx
251
+ import { SIGNAL_TYPES } from "@ultrst-armada/kloks/state-graph";
252
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
253
+ var Wire2D = ({
254
+ wire,
255
+ p1,
256
+ p2,
257
+ isSelected = false,
258
+ isEnveloped = false,
259
+ isPlaying = false,
260
+ bpm = 120,
261
+ pulseAnimTick = 0,
262
+ zoom = 1,
263
+ onSelect
264
+ }) => {
265
+ const { pathD, cp1, cp2 } = wire.calculateCubicBezier2D(p1, p2);
266
+ const sigDef = SIGNAL_TYPES[wire.signalType] || SIGNAL_TYPES.trigger;
267
+ const wireColor = isSelected ? "#38bdf8" : sigDef.colorStr;
268
+ const speed = bpm / 60 * 0.4 * wire.flowSpeedMultiplier;
269
+ const pulseT = pulseAnimTick * 1e-3 * speed % 1;
270
+ const pulsePoint = wire.calculatePulsePoint2D(pulseT, p1, p2, cp1, cp2);
271
+ return /* @__PURE__ */ jsxs2(
272
+ "g",
273
+ {
274
+ id: `wire-2d-${wire.id}`,
275
+ className: "cursor-pointer group select-none",
276
+ onClick: (e) => {
277
+ e.stopPropagation();
278
+ onSelect?.(wire.id);
279
+ },
280
+ children: [
281
+ isEnveloped && /* @__PURE__ */ jsx2(
282
+ "path",
283
+ {
284
+ d: pathD,
285
+ fill: "none",
286
+ stroke: "#fbbf24",
287
+ strokeWidth: 6 * zoom,
288
+ strokeOpacity: "0.4",
289
+ strokeLinecap: "round"
290
+ }
291
+ ),
292
+ /* @__PURE__ */ jsx2(
293
+ "path",
294
+ {
295
+ d: pathD,
296
+ fill: "none",
297
+ stroke: "transparent",
298
+ strokeWidth: 16 * zoom
299
+ }
300
+ ),
301
+ /* @__PURE__ */ jsx2(
302
+ "path",
303
+ {
304
+ d: pathD,
305
+ fill: "none",
306
+ stroke: wireColor,
307
+ strokeWidth: (isSelected ? 3.5 : 2) * zoom,
308
+ strokeLinecap: "round",
309
+ strokeDasharray: isSelected ? "6,3" : void 0,
310
+ className: "transition-all duration-150 group-hover:stroke-sky-300"
311
+ }
312
+ ),
313
+ isPlaying && /* @__PURE__ */ jsx2(
314
+ "circle",
315
+ {
316
+ cx: pulsePoint.x,
317
+ cy: pulsePoint.y,
318
+ r: 3.5 * zoom,
319
+ fill: wireColor,
320
+ filter: "drop-shadow(0 0 6px white)"
321
+ }
322
+ )
323
+ ]
324
+ }
325
+ );
326
+ };
327
+
328
+ // src/graph-2d/CanvasBackground2D.tsx
329
+ import { useMemo as useMemo2 } from "react";
330
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
331
+ var CanvasBackground2D = ({
332
+ currentBiome = "default",
333
+ camera,
334
+ containerSize,
335
+ nodes,
336
+ worldToScreen
337
+ }) => {
338
+ const biome = BIOME_CONFIGS[currentBiome] || BIOME_CONFIGS.default;
339
+ const gridLines = useMemo2(() => {
340
+ const lines = [];
341
+ const gridSize = 100 * camera.zoom;
342
+ if (gridSize < 15) return "";
343
+ const cx = containerSize.width / 2;
344
+ const cy = containerSize.height / 2;
345
+ const offsetX = (cx - camera.x * COORD_SCALE * camera.zoom) % gridSize;
346
+ const offsetY = (cy - camera.y * COORD_SCALE * camera.zoom) % gridSize;
347
+ for (let x = offsetX - gridSize; x < containerSize.width + gridSize; x += gridSize) {
348
+ lines.push(`M ${x} 0 V ${containerSize.height}`);
349
+ }
350
+ for (let y = offsetY - gridSize; y < containerSize.height + gridSize; y += gridSize) {
351
+ lines.push(`M 0 ${y} H ${containerSize.width}`);
352
+ }
353
+ return lines.join(" ");
354
+ }, [camera, containerSize]);
355
+ const uniqueNodes = useMemo2(() => {
356
+ const seen = /* @__PURE__ */ new Set();
357
+ return nodes.filter((node) => {
358
+ if (!node.id) return false;
359
+ if (seen.has(node.id)) return false;
360
+ seen.add(node.id);
361
+ return true;
362
+ });
363
+ }, [nodes]);
364
+ return /* @__PURE__ */ jsxs3(
365
+ "svg",
366
+ {
367
+ className: "absolute inset-0 w-full h-full pointer-events-none",
368
+ style: { backgroundColor: biome.bg },
369
+ children: [
370
+ /* @__PURE__ */ jsx3("path", { d: gridLines, stroke: biome.gridColor, strokeWidth: 1, fill: "none" }),
371
+ uniqueNodes.map((node) => {
372
+ const screenPos = worldToScreen(node.position[0], node.position[2]);
373
+ const nodeRadius = node.aoeRadius || node.radius;
374
+ const radius = (nodeRadius !== void 0 ? nodeRadius * COORD_SCALE : 120) * camera.zoom;
375
+ if (radius <= 5) return null;
376
+ return /* @__PURE__ */ jsx3("g", { opacity: 0.6, children: /* @__PURE__ */ jsx3(
377
+ "circle",
378
+ {
379
+ cx: screenPos.x,
380
+ cy: screenPos.y,
381
+ r: radius,
382
+ fill: biome.aoeColor,
383
+ stroke: biome.gridColor,
384
+ strokeWidth: 1,
385
+ strokeDasharray: "4,4"
386
+ }
387
+ ) }, `aoe-${node.id}`);
388
+ })
389
+ ]
390
+ }
391
+ );
392
+ };
393
+
394
+ // src/graph-2d/CanvasErrorBoundary.tsx
395
+ import { Component } from "react";
396
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
397
+ var CanvasErrorBoundary = class extends Component {
398
+ props;
399
+ state = { hasError: false, errorMessage: "" };
400
+ constructor(props) {
401
+ super(props);
402
+ this.props = props;
403
+ }
404
+ static getDerivedStateFromError(error) {
405
+ return { hasError: true, errorMessage: error?.message || "Unknown render error" };
406
+ }
407
+ componentDidCatch(error, errorInfo) {
408
+ console.error("2D Node Graph Viewer encountered an error:", error, errorInfo);
409
+ }
410
+ render() {
411
+ if (this.state.hasError) {
412
+ return /* @__PURE__ */ jsx4("div", { className: "w-full h-full flex flex-col items-center justify-center bg-slate-950 p-6 text-center text-slate-300", children: /* @__PURE__ */ jsxs4("div", { className: "max-w-md p-5 rounded-xl border border-red-500/30 bg-red-950/20 backdrop-blur-md", children: [
413
+ /* @__PURE__ */ jsx4("h3", { className: "text-sm font-semibold text-red-400 mb-2", children: "2D Graph View Recovered" }),
414
+ /* @__PURE__ */ jsx4("p", { className: "text-xs text-slate-400 mb-4 font-mono", children: this.state.errorMessage }),
415
+ /* @__PURE__ */ jsx4(
416
+ "button",
417
+ {
418
+ onClick: () => this.setState({ hasError: false, errorMessage: "" }),
419
+ className: "px-4 py-1.5 text-xs rounded bg-slate-800 hover:bg-slate-700 text-slate-100 border border-slate-600 transition-colors",
420
+ children: "Reload 2D View"
421
+ }
422
+ )
423
+ ] }) });
424
+ }
425
+ return this.props.children;
426
+ }
427
+ };
428
+
429
+ // src/graph-2d/MagneticTraces2D.tsx
430
+ import { Link2 } from "lucide-react";
431
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
432
+ var TRACE_COLORS = {
433
+ AUDIO_STEREO: "#00ffff",
434
+ AUDIO_MONO: "#38bdf8",
435
+ AUDIO_PACKET: "#818cf8",
436
+ TRIGGER: "#eab308",
437
+ MIDI_STREAM: "#ec4899",
438
+ MIDI_PACKET: "#f43f5e",
439
+ CURVE_STREAM: "#10b981",
440
+ CURVE_PACKET: "#34d399",
441
+ COMPONENT: "#a855f7",
442
+ DEFAULT: "#60a5fa"
443
+ };
444
+ var MagneticTraces2D = ({
445
+ nodes,
446
+ connections = [],
447
+ traces,
448
+ worldToScreen,
449
+ camera,
450
+ onSolidify
451
+ }) => {
452
+ const nodeMap = new Map(nodes.map((n) => [n.id, n]));
453
+ const connectedPairSet = /* @__PURE__ */ new Set();
454
+ connections.forEach((c) => {
455
+ const fromSock = c.fromSocketId || c.fromPort || "";
456
+ const toSock = c.toSocketId || c.toPort || "";
457
+ connectedPairSet.add(`${c.fromNodeId}:${fromSock}->${c.toNodeId}:${toSock}`);
458
+ connectedPairSet.add(`${c.fromNodeId}->${c.toNodeId}`);
459
+ });
460
+ const seenKeys = /* @__PURE__ */ new Set();
461
+ const visibleTraces = traces.filter((t) => {
462
+ const keyWithSockets = `${t.sourceNodeId}:${t.sourceHandleId}->${t.targetNodeId}:${t.targetHandleId}`;
463
+ const keyNodeOnly = `${t.sourceNodeId}->${t.targetNodeId}`;
464
+ if (connectedPairSet.has(keyWithSockets) || connectedPairSet.has(keyNodeOnly)) {
465
+ return false;
466
+ }
467
+ const traceUniqueId = t.id || keyWithSockets;
468
+ if (seenKeys.has(traceUniqueId)) {
469
+ return false;
470
+ }
471
+ seenKeys.add(traceUniqueId);
472
+ return true;
473
+ });
474
+ if (visibleTraces.length === 0) return null;
475
+ return /* @__PURE__ */ jsxs5("div", { className: "absolute inset-0 pointer-events-none", children: [
476
+ /* @__PURE__ */ jsx5("svg", { className: "w-full h-full", children: visibleTraces.map((trace) => {
477
+ const source = nodeMap.get(trace.sourceNodeId);
478
+ const target = nodeMap.get(trace.targetNodeId);
479
+ if (!source || !target) return null;
480
+ const p1 = getSocketScreenPosition(source, trace.sourceHandleId, true, worldToScreen, camera);
481
+ const p2 = getSocketScreenPosition(target, trace.targetHandleId, false, worldToScreen, camera);
482
+ const color = TRACE_COLORS[trace.wireType] || TRACE_COLORS.DEFAULT;
483
+ const proximityOpacity = Math.max(0.35, Math.min(0.9, 1 - trace.spatialScore / 360));
484
+ return /* @__PURE__ */ jsx5("g", { children: /* @__PURE__ */ jsx5(
485
+ "line",
486
+ {
487
+ x1: p1.x,
488
+ y1: p1.y,
489
+ x2: p2.x,
490
+ y2: p2.y,
491
+ stroke: color,
492
+ strokeWidth: 1.5,
493
+ opacity: proximityOpacity
494
+ }
495
+ ) }, trace.id);
496
+ }) }),
497
+ visibleTraces.map((trace) => {
498
+ const source = nodeMap.get(trace.sourceNodeId);
499
+ const target = nodeMap.get(trace.targetNodeId);
500
+ if (!source || !target) return null;
501
+ const p1 = getSocketScreenPosition(source, trace.sourceHandleId, true, worldToScreen, camera);
502
+ const p2 = getSocketScreenPosition(target, trace.targetHandleId, false, worldToScreen, camera);
503
+ const midX = (p1.x + p2.x) / 2;
504
+ const midY = (p1.y + p2.y) / 2;
505
+ return /* @__PURE__ */ jsx5(
506
+ "div",
507
+ {
508
+ style: {
509
+ position: "absolute",
510
+ left: `${midX}px`,
511
+ top: `${midY}px`,
512
+ transform: "translate(-50%, -50%)"
513
+ },
514
+ className: "pointer-events-auto z-20 group",
515
+ children: /* @__PURE__ */ jsx5(
516
+ "button",
517
+ {
518
+ onMouseDown: (e) => {
519
+ e.stopPropagation();
520
+ e.preventDefault();
521
+ },
522
+ onPointerDown: (e) => {
523
+ e.stopPropagation();
524
+ },
525
+ onClick: (e) => {
526
+ e.stopPropagation();
527
+ e.preventDefault();
528
+ onSolidify(trace);
529
+ },
530
+ className: "w-7 h-7 rounded-full bg-slate-950 border-2 border-cyan-400/80 text-cyan-300 hover:text-white hover:bg-cyan-950 hover:border-cyan-200 hover:scale-125 shadow-[0_0_12px_rgba(6,182,212,0.6)] flex items-center justify-center transition-all cursor-pointer group/btn",
531
+ title: `Solidify magnetic connection: ${trace.sourceHandleId} -> ${trace.targetHandleId}`,
532
+ children: /* @__PURE__ */ jsx5(Link2, { className: "w-3.5 h-3.5 text-cyan-400 group-hover/btn:scale-110" })
533
+ }
534
+ )
535
+ },
536
+ `widget-${trace.id}`
537
+ );
538
+ })
539
+ ] });
540
+ };
541
+
542
+ // src/graph-2d/RotaryKnob.tsx
543
+ import { useRef as useRef3, useCallback as useCallback3, useState as useState3 } from "react";
544
+ import { describeSvgArc } from "@ultrst-armada/kloks/state-graph";
545
+ import { usePalette } from "@ultrst-armada/floks/ui";
546
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
547
+ function RotaryKnob({
548
+ value,
549
+ min = 0,
550
+ max = 1,
551
+ step,
552
+ defaultValue,
553
+ onChange,
554
+ size = 38,
555
+ color,
556
+ trackColor,
557
+ label,
558
+ unit = "",
559
+ formatValue,
560
+ title,
561
+ disabled = false,
562
+ className = ""
563
+ }) {
564
+ const palette = usePalette();
565
+ const effectiveColor = color || palette.accentPrimary;
566
+ const effectiveTrackColor = trackColor || palette.borderSubtle;
567
+ const knobRef = useRef3(null);
568
+ const isDraggingRef = useRef3(false);
569
+ const startYRef = useRef3(0);
570
+ const startValRef = useRef3(value);
571
+ const [isHovered, setIsHovered] = useState3(false);
572
+ const [isEngaged, setIsEngaged] = useState3(false);
573
+ const clampedVal = Math.min(max, Math.max(min, value));
574
+ const range = max - min || 1;
575
+ const normalized = (clampedVal - min) / range;
576
+ const minAngle = -135;
577
+ const maxAngle = 135;
578
+ const currentAngle = minAngle + normalized * (maxAngle - minAngle);
579
+ const strokeWidth = Math.max(2.5, size * 0.08);
580
+ const radius = (size - strokeWidth * 2) / 2;
581
+ const cx = size / 2;
582
+ const cy = size / 2;
583
+ const bgArc = describeSvgArc(cx, cy, radius, minAngle, maxAngle);
584
+ const valArc = normalized > 5e-3 ? describeSvgArc(cx, cy, radius, minAngle, currentAngle) : "";
585
+ const snapValue = useCallback3((raw) => {
586
+ let v = Math.min(max, Math.max(min, raw));
587
+ if (step && step > 0) v = Math.round((v - min) / step) * step + min;
588
+ return parseFloat(v.toFixed(4));
589
+ }, [min, max, step]);
590
+ const handlePointerDown = (e) => {
591
+ if (disabled) return;
592
+ e.preventDefault();
593
+ e.stopPropagation();
594
+ isDraggingRef.current = true;
595
+ startYRef.current = e.clientY;
596
+ startValRef.current = clampedVal;
597
+ setIsEngaged(true);
598
+ knobRef.current?.setPointerCapture(e.pointerId);
599
+ };
600
+ const handlePointerMove = (e) => {
601
+ if (!isDraggingRef.current || disabled) return;
602
+ e.preventDefault();
603
+ e.stopPropagation();
604
+ const sensitivity = e.shiftKey ? 400 : 150;
605
+ const deltaVal = (startYRef.current - e.clientY) / sensitivity * range;
606
+ const newVal = snapValue(startValRef.current + deltaVal);
607
+ if (newVal !== clampedVal) onChange(newVal);
608
+ };
609
+ const handlePointerUp = (e) => {
610
+ if (isDraggingRef.current) {
611
+ isDraggingRef.current = false;
612
+ setIsEngaged(false);
613
+ try {
614
+ knobRef.current?.releasePointerCapture(e.pointerId);
615
+ } catch {
616
+ }
617
+ }
618
+ };
619
+ const handleWheel = (e) => {
620
+ if (disabled) return;
621
+ e.preventDefault();
622
+ e.stopPropagation();
623
+ const newVal = snapValue(clampedVal - Math.sign(e.deltaY) * (step || range * 0.02));
624
+ if (newVal !== clampedVal) onChange(newVal);
625
+ };
626
+ const formattedDisplay = formatValue ? formatValue(clampedVal) : unit === "%" ? `${Math.round(clampedVal * 100)}%` : unit ? `${clampedVal.toFixed(step && step < 0.1 ? 2 : 1)}${unit}` : typeof clampedVal === "number" && Number.isInteger(clampedVal) ? `${clampedVal}` : `${clampedVal.toFixed(2)}`;
627
+ return /* @__PURE__ */ jsxs6(
628
+ "div",
629
+ {
630
+ className: `inline-flex flex-col items-center select-none font-mono ${disabled ? "opacity-40 cursor-not-allowed" : "cursor-ns-resize"} ${className}`,
631
+ title: title || (label ? `${label}: ${formattedDisplay}` : formattedDisplay),
632
+ onMouseEnter: () => setIsHovered(true),
633
+ onMouseLeave: () => setIsHovered(false),
634
+ children: [
635
+ label && /* @__PURE__ */ jsx6("span", { style: { color: palette.textMuted }, className: "text-[9px] uppercase font-bold tracking-wider mb-1 text-center truncate max-w-[80px]", children: label }),
636
+ /* @__PURE__ */ jsxs6(
637
+ "div",
638
+ {
639
+ ref: knobRef,
640
+ onPointerDown: handlePointerDown,
641
+ onPointerMove: handlePointerMove,
642
+ onPointerUp: handlePointerUp,
643
+ onPointerCancel: handlePointerUp,
644
+ onWheel: handleWheel,
645
+ onDoubleClick: (e) => {
646
+ if (!disabled && defaultValue !== void 0) {
647
+ e.stopPropagation();
648
+ onChange(defaultValue);
649
+ }
650
+ },
651
+ style: { width: size, height: size },
652
+ className: "relative flex items-center justify-center touch-none group",
653
+ children: [
654
+ /* @__PURE__ */ jsxs6("svg", { width: size, height: size, className: "overflow-visible", children: [
655
+ /* @__PURE__ */ jsx6("path", { d: bgArc, fill: "none", stroke: effectiveTrackColor, strokeWidth, strokeLinecap: "round" }),
656
+ normalized > 5e-3 && /* @__PURE__ */ jsx6(
657
+ "path",
658
+ {
659
+ d: valArc,
660
+ fill: "none",
661
+ stroke: effectiveColor,
662
+ strokeWidth,
663
+ strokeLinecap: "round",
664
+ className: "transition-all duration-75",
665
+ style: { filter: isEngaged || isHovered ? `drop-shadow(0 0 4px ${effectiveColor})` : void 0 }
666
+ }
667
+ )
668
+ ] }),
669
+ /* @__PURE__ */ jsx6(
670
+ "div",
671
+ {
672
+ style: {
673
+ width: size - strokeWidth * 3.2,
674
+ height: size - strokeWidth * 3.2,
675
+ transform: `rotate(${currentAngle}deg)`,
676
+ borderColor: isEngaged ? effectiveColor : isHovered ? palette.borderBold : palette.borderMedium,
677
+ backgroundColor: palette.bgSurface
678
+ },
679
+ className: "absolute rounded-full border flex items-start justify-center transition-colors duration-150",
680
+ children: /* @__PURE__ */ jsx6(
681
+ "div",
682
+ {
683
+ style: {
684
+ backgroundColor: effectiveColor,
685
+ width: Math.max(2, size * 0.06),
686
+ height: size * 0.24,
687
+ boxShadow: `0 0 5px ${effectiveColor}`
688
+ },
689
+ className: "mt-0.5 rounded-full"
690
+ }
691
+ )
692
+ }
693
+ )
694
+ ]
695
+ }
696
+ ),
697
+ /* @__PURE__ */ jsx6("span", { style: { color: isEngaged ? effectiveColor : palette.textSecondary }, className: "text-[10px] font-bold mt-1 text-center transition-colors", children: formattedDisplay })
698
+ ]
699
+ }
700
+ );
701
+ }
702
+
703
+ export {
704
+ COORD_SCALE,
705
+ BIOME_CONFIGS,
706
+ useCanvas2DCoordinates,
707
+ useCanvas2DMagnetic,
708
+ TriggerNode2D,
709
+ ContainerNode2D,
710
+ ModuleNode2D,
711
+ EffectNode2D,
712
+ NodeRenderer2D,
713
+ Wire2D,
714
+ CanvasBackground2D,
715
+ CanvasErrorBoundary,
716
+ MagneticTraces2D,
717
+ RotaryKnob
718
+ };