@ultrst-armada/floks 0.0.0-stage → 1.0.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 (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 +67 -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-2GLR35UM.js +391 -0
  8. package/packages/canvas-ui/dist/chunk-34YRDZPJ.js +189 -0
  9. package/packages/canvas-ui/dist/chunk-6VB74RCH.js +147 -0
  10. package/packages/canvas-ui/dist/chunk-QTTWWBNN.js +114 -0
  11. package/packages/canvas-ui/dist/chunk-QWQ4PCXQ.js +718 -0
  12. package/packages/canvas-ui/dist/chunk-SA3JADUG.js +160 -0
  13. package/packages/canvas-ui/dist/chunk-UEIMM6PV.js +508 -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 +74 -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-C32CB5WO.js +1161 -0
  34. package/packages/ui/dist/chunk-C3L53YN4.js +372 -0
  35. package/packages/ui/dist/chunk-DABYTYOU.js +351 -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-OHISLM3Z.js +703 -0
  42. package/packages/ui/dist/chunk-OR5ZNJI4.js +62 -0
  43. package/packages/ui/dist/chunk-QWXEW7WH.js +238 -0
  44. package/packages/ui/dist/chunk-RLQ2ALJG.js +182 -0
  45. package/packages/ui/dist/chunk-S227WJ5E.js +152 -0
  46. package/packages/ui/dist/chunk-SLJBDDSN.js +0 -0
  47. package/packages/ui/dist/chunk-TXKLROFC.js +370 -0
  48. package/packages/ui/dist/chunk-UOR4P6PO.js +32 -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 +168 -0
  77. package/packages/ui/dist/hierarchy/index.js +36 -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 +282 -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
package/README.md CHANGED
@@ -1,3 +1,2 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # Floks
2
+ This will be the counterpart Kloks but for the frontend components. This will store the canonical frontend elements that we will use in our Skipo projects.
package/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './packages/ui/dist/index.d.ts';
2
+ export * from './packages/canvas-ui/dist/index.d.ts';
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './packages/ui/dist/index.js';
2
+ export * from './packages/canvas-ui/dist/index.js';
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "@ultrst-armada/floks",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "publishConfig": {
6
+ "access": "public",
7
+ "registry": "https://registry.npmjs.org/"
8
+ },
9
+ "description": "Floks — Public Frontend, 2D Graph & UI Package Library (@floks/*)",
10
+ "type": "module",
11
+ "main": "./index.js",
12
+ "module": "./index.js",
13
+ "types": "./index.d.ts",
14
+ "files": [
15
+ "index.js",
16
+ "index.d.ts",
17
+ "packages/*/dist/**",
18
+ "packages/*/src/theme/tokens.css",
19
+ "packages/*/src/styles/**",
20
+ "packages/*/package.json",
21
+ "packages/*/floks.manifest.json"
22
+ ],
23
+ "exports": {
24
+ ".": {
25
+ "types": "./index.d.ts",
26
+ "import": "./index.js",
27
+ "default": "./index.js"
28
+ },
29
+ "./ui": {
30
+ "types": "./packages/ui/dist/index.d.ts",
31
+ "import": "./packages/ui/dist/index.js",
32
+ "default": "./packages/ui/dist/index.js"
33
+ },
34
+ "./ui/theme/tokens.css": "./packages/ui/src/theme/tokens.css",
35
+ "./ui/styles/*": "./packages/ui/src/styles/*",
36
+ "./canvas-ui": {
37
+ "types": "./packages/canvas-ui/dist/index.d.ts",
38
+ "import": "./packages/canvas-ui/dist/index.js",
39
+ "default": "./packages/canvas-ui/dist/index.js"
40
+ }
41
+ },
42
+ "scripts": {
43
+ "build": "pnpm -r run build",
44
+ "postbuild": "node -e \"import('fs').then(fs => { for (const pkg of fs.readdirSync('packages')) { const d = 'packages/' + pkg + '/dist'; if (!fs.existsSync(d)) continue; for (const f of fs.readdirSync(d, { recursive: true })) { const p = d + '/' + f; if (fs.statSync(p).isFile() && (p.endsWith('.js') || p.endsWith('.d.ts'))) { const c = fs.readFileSync(p, 'utf8'); const n = c.replaceAll('@kloks/', '@ultrst-armada/kloks/').replaceAll('@floks/', '@ultrst-armada/floks/').replaceAll('@ultrst/kloks/', '@ultrst-armada/kloks/').replaceAll('@ultrst/floks/', '@ultrst-armada/floks/').replaceAll('\\\"kloks/', '\\\"@ultrst-armada/kloks/').replaceAll('\\'kloks/', '\\'@ultrst-armada/kloks/').replaceAll('\\\"floks/', '\\\"@ultrst-armada/floks/').replaceAll('\\'floks/', '\\'@ultrst-armada/floks/'); if (c !== n) fs.writeFileSync(p, n); } } } })\"",
45
+ "test": "vitest run",
46
+ "typecheck": "pnpm -r run typecheck",
47
+ "clean": "pnpm -r run clean"
48
+ },
49
+ "dependencies": {
50
+ "zod": "^3.24.1"
51
+ },
52
+ "peerDependencies": {
53
+ "lucide-react": ">=0.300.0",
54
+ "react": "^18.0.0 || ^19.0.0",
55
+ "react-dom": "^18.0.0 || ^19.0.0"
56
+ },
57
+ "devDependencies": {
58
+ "@types/node": "^22.0.0",
59
+ "@types/react": "^19.0.0",
60
+ "@types/react-dom": "^19.0.0",
61
+ "lucide-react": "^1.55.0",
62
+ "react": "^19.0.1",
63
+ "react-dom": "^19.0.1",
64
+ "tsup": "^8.4.0",
65
+ "typescript": "^5.7.3",
66
+ "vitest": "^3.1.0"
67
+ },
68
+ "packageManager": "pnpm@9.15.0"
69
+ }
@@ -0,0 +1,54 @@
1
+ import * as React from 'react';
2
+ import React__default from 'react';
3
+
4
+ interface Transform {
5
+ x: number;
6
+ y: number;
7
+ scale: number;
8
+ }
9
+ interface CanvasItem {
10
+ id: string;
11
+ x: number;
12
+ y: number;
13
+ width?: number;
14
+ height?: number;
15
+ content: React.ReactNode;
16
+ }
17
+
18
+ interface CanvasViewportProps {
19
+ children: React__default.ReactNode;
20
+ transform: Transform;
21
+ onChange: (t: Transform) => void;
22
+ minZoom?: number;
23
+ maxZoom?: number;
24
+ }
25
+ declare function CanvasViewport({ children, transform, onChange, minZoom, maxZoom }: CanvasViewportProps): React__default.JSX.Element;
26
+
27
+ interface InfiniteCanvasProps {
28
+ items: CanvasItem[];
29
+ transform: Transform;
30
+ onTransformChange: (t: Transform) => void;
31
+ showGrid?: boolean;
32
+ showMiniMap?: boolean;
33
+ gridSize?: number;
34
+ }
35
+ declare function InfiniteCanvas({ items, transform, onTransformChange, showGrid, showMiniMap, gridSize }: InfiniteCanvasProps): React.JSX.Element;
36
+
37
+ interface MiniMapProps {
38
+ items: CanvasItem[];
39
+ transform: Transform;
40
+ width?: number;
41
+ height?: number;
42
+ }
43
+ declare function MiniMap({ items, transform, width, height }: MiniMapProps): React.JSX.Element;
44
+
45
+ declare function useCanvas(initialTransform?: Transform): {
46
+ transform: Transform;
47
+ setTransform: React.Dispatch<React.SetStateAction<Transform>>;
48
+ items: CanvasItem[];
49
+ setItems: React.Dispatch<React.SetStateAction<CanvasItem[]>>;
50
+ addItem: (item: CanvasItem) => void;
51
+ updateItemPosition: (id: string, x: number, y: number) => void;
52
+ };
53
+
54
+ export { type CanvasItem, CanvasViewport, InfiniteCanvas, MiniMap, type Transform, useCanvas };
@@ -0,0 +1,12 @@
1
+ import {
2
+ CanvasViewport,
3
+ InfiniteCanvas,
4
+ MiniMap,
5
+ useCanvas
6
+ } from "../chunk-34YRDZPJ.js";
7
+ export {
8
+ CanvasViewport,
9
+ InfiniteCanvas,
10
+ MiniMap,
11
+ useCanvas
12
+ };
@@ -0,0 +1,391 @@
1
+ // src/graph-2d/socketCoordinates.ts
2
+ import { getNodeSockets } from "@ultrst-armada/kloks/state-graph";
3
+ import { GraphNodeParent } from "@ultrst-armada/kloks/state-graph";
4
+ function getSocketLocalOffset(node, socketId, isOutput, index, totalSockets) {
5
+ const isCustomBox = node.type === "arrangement" || node.type === "code";
6
+ const clearance = isCustomBox ? 16 : 14;
7
+ return GraphNodeParent.calculateConstrainedSocketOffset(
8
+ node,
9
+ socketId,
10
+ isOutput,
11
+ index,
12
+ totalSockets,
13
+ clearance
14
+ );
15
+ }
16
+ function getSocketScreenPosition(node, socketId, isOutput, worldToScreen, camera) {
17
+ const center = worldToScreen(node.position[0], node.position[2]);
18
+ const { inputs, outputs } = getNodeSockets(node);
19
+ const sockets = isOutput ? outputs : inputs;
20
+ if (sockets.length === 0) {
21
+ return center;
22
+ }
23
+ let idx = -1;
24
+ if (socketId) {
25
+ idx = sockets.findIndex((s) => s.socketId === socketId);
26
+ }
27
+ const validIdx = idx >= 0 ? idx : 0;
28
+ const targetSocket = sockets[validIdx];
29
+ const localOffset = getSocketLocalOffset(
30
+ node,
31
+ targetSocket.socketId,
32
+ isOutput,
33
+ validIdx,
34
+ sockets.length
35
+ );
36
+ return {
37
+ x: center.x + localOffset.x * camera.zoom,
38
+ y: center.y + localOffset.y * camera.zoom
39
+ };
40
+ }
41
+
42
+ // src/graph-2d/Socket2D.tsx
43
+ import { SIGNAL_TYPES } from "@ultrst-armada/kloks/state-graph";
44
+ import { jsx, jsxs } from "react/jsx-runtime";
45
+ var Socket2D = ({
46
+ nodeId,
47
+ socket,
48
+ direction,
49
+ isConnected = false,
50
+ onSocketHover,
51
+ onSocketMouseDown,
52
+ onSocketMouseUp,
53
+ onHover,
54
+ onMouseDown,
55
+ onMouseUp
56
+ }) => {
57
+ const sigDef = SIGNAL_TYPES[socket.signalType] || SIGNAL_TYPES.trigger;
58
+ const colorStr = sigDef.colorStr;
59
+ const resolvedDirection = direction || (socket.polarity === "input" ? "in" : "out");
60
+ const isInput = resolvedDirection === "in";
61
+ const portKey = socket.portId || socket.socketId;
62
+ const hoverCb = onSocketHover || onHover;
63
+ const downCb = onSocketMouseDown || onMouseDown;
64
+ const upCb = onSocketMouseUp || onMouseUp;
65
+ return /* @__PURE__ */ jsxs(
66
+ "div",
67
+ {
68
+ id: `socket-2d-${nodeId}-${resolvedDirection}-${portKey}`,
69
+ className: `flex items-center gap-1.5 cursor-pointer group/sock select-none ${isInput ? "justify-start" : "justify-end ml-auto"}`,
70
+ onMouseEnter: (e) => {
71
+ const rect = e.currentTarget.getBoundingClientRect();
72
+ hoverCb?.({
73
+ nodeId,
74
+ socket,
75
+ screenX: isInput ? rect.left : rect.right,
76
+ screenY: rect.top
77
+ });
78
+ },
79
+ onMouseLeave: () => hoverCb?.(null),
80
+ onMouseDown: (e) => {
81
+ e.stopPropagation();
82
+ downCb?.(nodeId, socket, e);
83
+ },
84
+ onMouseUp: (e) => {
85
+ e.stopPropagation();
86
+ upCb?.(nodeId, socket, e);
87
+ },
88
+ children: [
89
+ isInput && /* @__PURE__ */ jsx(
90
+ "div",
91
+ {
92
+ style: { backgroundColor: colorStr },
93
+ className: `w-2.5 h-2.5 rounded-full border border-black group-hover/sock:scale-130 transition-transform ${isConnected ? "ring-1 ring-white/60" : "opacity-85"}`
94
+ }
95
+ ),
96
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-bold text-[9px] text-neutral-300 group-hover/sock:text-amber-300 tracking-tight", children: socket.label }),
97
+ !isInput && /* @__PURE__ */ jsx(
98
+ "div",
99
+ {
100
+ style: { backgroundColor: colorStr },
101
+ className: `w-2.5 h-2.5 rotate-45 border border-black group-hover/sock:scale-130 transition-transform ${isConnected ? "ring-1 ring-white/60" : "opacity-85"}`
102
+ }
103
+ )
104
+ ]
105
+ }
106
+ );
107
+ };
108
+
109
+ // src/graph-2d/RecorderWaveformDisplay.tsx
110
+ import { useState, useEffect, useRef } from "react";
111
+ import { retrospectiveBufferEngine } from "@ultrst-armada/kloks/audio-midi";
112
+ import { sequencerEngine } from "@ultrst-armada/kloks/audio-midi";
113
+ import { synthEngine } from "@ultrst-armada/kloks/audio-midi";
114
+ import * as GraphConcepts from "@ultrst-armada/kloks/state-graph";
115
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
116
+ function buildWaveformPath(peaks, width, height) {
117
+ if (!peaks || peaks.length === 0) return `M 0 ${height / 2} L ${width} ${height / 2}`;
118
+ const step = width / Math.max(1, peaks.length - 1);
119
+ const midY = height / 2;
120
+ let topPoints = "";
121
+ let bottomPoints = "";
122
+ for (let i = 0; i < peaks.length; i++) {
123
+ const x = Math.round(i * step * 10) / 10;
124
+ const amp = Math.max(0.08, Math.min(1, peaks[i] || 0.08)) * (midY - 2);
125
+ topPoints += `${i === 0 ? "M" : "L"} ${x} ${midY - amp} `;
126
+ bottomPoints = `L ${x} ${midY + amp} ` + bottomPoints;
127
+ }
128
+ return `${topPoints} ${bottomPoints} Z`;
129
+ }
130
+ var RecorderWaveformDisplay = ({
131
+ node,
132
+ connections = [],
133
+ allNodes = [],
134
+ width = 150
135
+ }) => {
136
+ const [currentBeat, setCurrentBeat] = useState(0);
137
+ const [capturedFlash, setCapturedFlash] = useState(false);
138
+ const [livePeaksL, setLivePeaksL] = useState([]);
139
+ const [livePeaksR, setLivePeaksR] = useState([]);
140
+ const lastBeatRef = useRef(0);
141
+ const isArmed = !!node.isArmed;
142
+ const isRetrospective = node.recordingMode === "retrospective";
143
+ const isRecording = !isRetrospective && !!node.isRecording;
144
+ const isStereo = node.recorderType !== "audio_mono";
145
+ useEffect(() => {
146
+ return retrospectiveBufferEngine.onSnapshotCaptured((recId) => {
147
+ if (recId === node.id) {
148
+ setCapturedFlash(true);
149
+ setTimeout(() => setCapturedFlash(false), 1200);
150
+ }
151
+ });
152
+ }, [node.id]);
153
+ useEffect(() => {
154
+ if (!isArmed) return;
155
+ let animId;
156
+ let lastSampleTime = 0;
157
+ const sourceNode = GraphConcepts.findSourceNode?.(node.id, "stream_in", connections, allNodes);
158
+ const incomingWire = connections.find(
159
+ (c) => c.toNodeId === node.id && (c.toPort === "stream_in" || c.toSocketIndex === 0 || !c.toPort)
160
+ );
161
+ const tick = (time) => {
162
+ const beat = sequencerEngine.currentBeat;
163
+ setCurrentBeat(beat);
164
+ if (time - lastSampleTime >= 40) {
165
+ lastSampleTime = time;
166
+ const gainVal = node.gain ?? 1;
167
+ const levels = sourceNode ? synthEngine.getWireSignalLevel(sourceNode, incomingWire?.fromPort) : { left: 0, right: 0 };
168
+ const pL = Math.min(1, levels.left * gainVal);
169
+ const pR = Math.min(1, levels.right * gainVal);
170
+ if (isRecording) {
171
+ setLivePeaksL((prev) => [...prev.slice(-63), pL]);
172
+ setLivePeaksR((prev) => [...prev.slice(-63), pR]);
173
+ } else if (isRetrospective) {
174
+ setLivePeaksL((prev) => [...prev.slice(-39), pL]);
175
+ setLivePeaksR((prev) => [...prev.slice(-39), pR]);
176
+ }
177
+ }
178
+ animId = requestAnimationFrame(tick);
179
+ };
180
+ const currentBeatVal = sequencerEngine.currentBeat;
181
+ if (isRecording && Math.abs(currentBeatVal - lastBeatRef.current) > 0.5) {
182
+ setLivePeaksL([]);
183
+ setLivePeaksR([]);
184
+ }
185
+ lastBeatRef.current = currentBeatVal;
186
+ animId = requestAnimationFrame(tick);
187
+ return () => cancelAnimationFrame(animId);
188
+ }, [isArmed, isRecording, isRetrospective, node.id, node.gain, connections, allNodes]);
189
+ if (!isArmed) return null;
190
+ const elapsedBeats = isRecording ? Math.max(0, currentBeat - (node.recordingStartBeat || 0)) : 0;
191
+ const bars = Math.floor(elapsedBeats / 4);
192
+ const beatsInBar = (elapsedBeats % 4).toFixed(1);
193
+ const timeText = isRecording ? `${bars} BAR${bars === 1 ? "" : "S"} ${beatsInBar} BEATS` : isRetrospective ? `${node.bufferLengthBars ?? 4} BARS BUFFER` : "1 BAR PREVIEW";
194
+ const isRedState = isRecording || isRetrospective && capturedFlash;
195
+ const colorStroke = isRedState ? "#f43f5e" : "#94a3b8";
196
+ const colorFill = isRedState ? "rgba(244, 63, 94, 0.25)" : "rgba(148, 163, 184, 0.15)";
197
+ const glowShadow = isRedState ? "0 0 8px rgba(244, 63, 94, 0.6)" : "none";
198
+ const renderPeaksL = isRecording || isRetrospective ? livePeaksL.length > 0 ? livePeaksL : [0.15, 0.3, 0.2, 0.4, 0.2, 0.15] : [0.15, 0.3, 0.25, 0.5, 0.3, 0.4, 0.2, 0.15];
199
+ const renderPeaksR = isRecording || isRetrospective ? livePeaksR.length > 0 ? livePeaksR : [0.12, 0.28, 0.18, 0.38, 0.22, 0.14] : [0.12, 0.28, 0.22, 0.45, 0.28, 0.35, 0.18, 0.12];
200
+ const trackHeight = isStereo ? 18 : 36;
201
+ return /* @__PURE__ */ jsxs2(
202
+ "div",
203
+ {
204
+ className: "flex flex-col items-center justify-center w-full px-1 py-1 rounded bg-black/60 border border-slate-800 select-none overflow-hidden",
205
+ style: { boxShadow: glowShadow },
206
+ children: [
207
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between w-full px-1 mb-0.5 text-[8px] font-mono font-bold tracking-tight", children: [
208
+ /* @__PURE__ */ jsx2("span", { className: isRedState ? "text-rose-400 animate-pulse" : "text-slate-400", children: isRedState ? "\u25CF REC" : isRetrospective ? "\u25CF BUFFER" : "ARMED" }),
209
+ /* @__PURE__ */ jsx2("span", { className: isRedState ? "text-rose-300 font-extrabold" : "text-slate-400", children: timeText })
210
+ ] }),
211
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-0.5 w-full", children: [
212
+ /* @__PURE__ */ jsxs2("div", { className: `relative w-full ${isStereo ? "h-[18px]" : "h-[36px]"} bg-slate-950/80 rounded border border-slate-800/80 overflow-hidden`, children: [
213
+ isStereo && /* @__PURE__ */ jsx2("span", { className: "absolute top-0.5 left-1 text-[7px] font-mono font-bold text-slate-500 z-10", children: "L" }),
214
+ /* @__PURE__ */ jsx2("svg", { className: "w-full h-full", viewBox: `0 0 ${width} ${trackHeight}`, preserveAspectRatio: "none", children: /* @__PURE__ */ jsx2("path", { d: buildWaveformPath(renderPeaksL, width, trackHeight), fill: colorFill, stroke: colorStroke, strokeWidth: "1.2" }) })
215
+ ] }),
216
+ isStereo && /* @__PURE__ */ jsxs2("div", { className: "relative w-full h-[18px] bg-slate-950/80 rounded border border-slate-800/80 overflow-hidden", children: [
217
+ /* @__PURE__ */ jsx2("span", { className: "absolute top-0.5 left-1 text-[7px] font-mono font-bold text-slate-500 z-10", children: "R" }),
218
+ /* @__PURE__ */ jsx2("svg", { className: "w-full h-full", viewBox: `0 0 ${width} ${trackHeight}`, preserveAspectRatio: "none", children: /* @__PURE__ */ jsx2("path", { d: buildWaveformPath(renderPeaksR, width, trackHeight), fill: colorFill, stroke: colorStroke, strokeWidth: "1.2" }) })
219
+ ] })
220
+ ] })
221
+ ]
222
+ }
223
+ );
224
+ };
225
+
226
+ // src/graph-2d/Node2DBase.tsx
227
+ import {
228
+ Castle,
229
+ Sparkles,
230
+ Flame,
231
+ FileAudio,
232
+ Activity,
233
+ Layers,
234
+ Music,
235
+ Clock,
236
+ RotateCw,
237
+ Volume2,
238
+ Zap,
239
+ Radio,
240
+ LogIn,
241
+ LogOut,
242
+ GitMerge,
243
+ Split,
244
+ Cpu,
245
+ Repeat,
246
+ Type,
247
+ Hash,
248
+ ToggleLeft,
249
+ Braces,
250
+ Folder,
251
+ FileText
252
+ } from "lucide-react";
253
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
254
+ var NODE_CARD_WIDTH = 210;
255
+ var SOCKET_ROW_HEIGHT = 24;
256
+ var renderSpriteIcon = (iconName, color, className = "w-4 h-4 shrink-0") => {
257
+ const props = { className, style: { color } };
258
+ switch (iconName) {
259
+ case "Castle":
260
+ return /* @__PURE__ */ jsx3(Castle, { ...props });
261
+ case "Sparkles":
262
+ return /* @__PURE__ */ jsx3(Sparkles, { ...props });
263
+ case "Flame":
264
+ return /* @__PURE__ */ jsx3(Flame, { ...props });
265
+ case "FileAudio":
266
+ return /* @__PURE__ */ jsx3(FileAudio, { ...props });
267
+ case "Activity":
268
+ return /* @__PURE__ */ jsx3(Activity, { ...props });
269
+ case "Layers":
270
+ return /* @__PURE__ */ jsx3(Layers, { ...props });
271
+ case "Music":
272
+ return /* @__PURE__ */ jsx3(Music, { ...props });
273
+ case "Clock":
274
+ return /* @__PURE__ */ jsx3(Clock, { ...props });
275
+ case "RotateCw":
276
+ return /* @__PURE__ */ jsx3(RotateCw, { ...props });
277
+ case "Volume2":
278
+ return /* @__PURE__ */ jsx3(Volume2, { ...props });
279
+ case "Zap":
280
+ return /* @__PURE__ */ jsx3(Zap, { ...props });
281
+ case "Radio":
282
+ return /* @__PURE__ */ jsx3(Radio, { ...props });
283
+ case "LogIn":
284
+ return /* @__PURE__ */ jsx3(LogIn, { ...props });
285
+ case "LogOut":
286
+ return /* @__PURE__ */ jsx3(LogOut, { ...props });
287
+ case "GitMerge":
288
+ return /* @__PURE__ */ jsx3(GitMerge, { ...props });
289
+ case "Split":
290
+ return /* @__PURE__ */ jsx3(Split, { ...props });
291
+ case "Cpu":
292
+ return /* @__PURE__ */ jsx3(Cpu, { ...props });
293
+ case "Repeat":
294
+ return /* @__PURE__ */ jsx3(Repeat, { ...props });
295
+ case "Type":
296
+ return /* @__PURE__ */ jsx3(Type, { ...props });
297
+ case "Hash":
298
+ return /* @__PURE__ */ jsx3(Hash, { ...props });
299
+ case "ToggleLeft":
300
+ return /* @__PURE__ */ jsx3(ToggleLeft, { ...props });
301
+ case "Braces":
302
+ return /* @__PURE__ */ jsx3(Braces, { ...props });
303
+ case "Folder":
304
+ return /* @__PURE__ */ jsx3(Folder, { ...props });
305
+ case "FileText":
306
+ return /* @__PURE__ */ jsx3(FileText, { ...props });
307
+ default:
308
+ return /* @__PURE__ */ jsx3(Castle, { ...props });
309
+ }
310
+ };
311
+ var Node2DBase = ({
312
+ node,
313
+ sprite,
314
+ sockets,
315
+ screenPos,
316
+ zoom,
317
+ isSelected,
318
+ isHovered,
319
+ children,
320
+ onNodeMouseDown,
321
+ onNodeDoubleClick,
322
+ onNodeMouseEnter,
323
+ onNodeMouseLeave,
324
+ onSocketHover,
325
+ onSocketMouseDown,
326
+ onSocketMouseUp
327
+ }) => {
328
+ const totalRows = Math.max(sockets.inputs.length, sockets.outputs.length, 1);
329
+ const cardHeight = Math.max(70, 36 + totalRows * SOCKET_ROW_HEIGHT);
330
+ const nodeInspectionText = `NODE: ${node.name || sprite.spriteName || node.type} (${(node.type || "NODE").toUpperCase()})`;
331
+ return /* @__PURE__ */ jsxs3(
332
+ "div",
333
+ {
334
+ id: `qwnsr-node-2d-${node.id}`,
335
+ "data-skipo-tip": nodeInspectionText,
336
+ title: nodeInspectionText,
337
+ onMouseEnter: onNodeMouseEnter,
338
+ onMouseLeave: onNodeMouseLeave,
339
+ onMouseDown: onNodeMouseDown,
340
+ onDoubleClick: onNodeDoubleClick,
341
+ style: {
342
+ transform: `translate(${screenPos.x}px, ${screenPos.y}px) translate(-50%, -50%) scale(${zoom})`,
343
+ width: NODE_CARD_WIDTH,
344
+ minHeight: cardHeight,
345
+ boxShadow: isSelected ? `0 0 0 2px #38bdf8, 4px 4px 0px #000000, 0 0 16px ${sprite.glowColor}` : `4px 4px 0px #000000, 0 0 10px ${isHovered ? sprite.glowColor : "rgba(0,0,0,0.5)"}`
346
+ },
347
+ className: `absolute flex flex-col bg-[#0b0e17]/95 border-2 rounded-none transition-transform select-none cursor-move z-20 ${isSelected ? "border-sky-400 bg-[#0f172a]" : isHovered ? "border-amber-400" : "border-[#334155]"}`,
348
+ children: [
349
+ node.type === "recorder" && node.isArmed ? /* @__PURE__ */ jsx3("div", { className: "w-full px-2 py-1 my-auto", children: /* @__PURE__ */ jsx3(RecorderWaveformDisplay, { node, width: 180 }) }) : /* @__PURE__ */ jsx3("div", { className: "flex items-center justify-center p-3 flex-1 my-auto", children: renderSpriteIcon(sprite.iconName, sprite.accentColor, "w-7 h-7 shrink-0") }),
350
+ children,
351
+ /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-1 px-1.5 py-1.5 mt-auto border-t border-[#1e293b] bg-black/40", children: Array.from({ length: totalRows }).map((_, rowIdx) => {
352
+ const inSock = sockets.inputs[rowIdx];
353
+ const outSock = sockets.outputs[rowIdx];
354
+ return /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-1 text-[9px] h-5", children: [
355
+ inSock ? /* @__PURE__ */ jsx3(
356
+ Socket2D,
357
+ {
358
+ nodeId: node.id,
359
+ socket: inSock,
360
+ onHover: (info) => onSocketHover?.(info),
361
+ onMouseDown: (nodeId, sock, e) => onSocketMouseDown?.(nodeId, sock, e),
362
+ onMouseUp: (nodeId, sock, e) => onSocketMouseUp?.(nodeId, sock, e)
363
+ }
364
+ ) : /* @__PURE__ */ jsx3("div", {}),
365
+ outSock ? /* @__PURE__ */ jsx3(
366
+ Socket2D,
367
+ {
368
+ nodeId: node.id,
369
+ socket: outSock,
370
+ onHover: (info) => onSocketHover?.(info),
371
+ onMouseDown: (nodeId, sock, e) => onSocketMouseDown?.(nodeId, sock, e),
372
+ onMouseUp: (nodeId, sock, e) => onSocketMouseUp?.(nodeId, sock, e)
373
+ }
374
+ ) : /* @__PURE__ */ jsx3("div", {})
375
+ ] }, `socket-row-${rowIdx}`);
376
+ }) })
377
+ ]
378
+ }
379
+ );
380
+ };
381
+
382
+ export {
383
+ getSocketLocalOffset,
384
+ getSocketScreenPosition,
385
+ Socket2D,
386
+ RecorderWaveformDisplay,
387
+ NODE_CARD_WIDTH,
388
+ SOCKET_ROW_HEIGHT,
389
+ renderSpriteIcon,
390
+ Node2DBase
391
+ };