@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.
- package/README.md +2 -3
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +67 -4
- package/packages/canvas-ui/dist/canvas/index.d.ts +54 -0
- package/packages/canvas-ui/dist/canvas/index.js +12 -0
- package/packages/canvas-ui/dist/chunk-2GLR35UM.js +391 -0
- package/packages/canvas-ui/dist/chunk-34YRDZPJ.js +189 -0
- package/packages/canvas-ui/dist/chunk-6VB74RCH.js +147 -0
- package/packages/canvas-ui/dist/chunk-QTTWWBNN.js +114 -0
- package/packages/canvas-ui/dist/chunk-QWQ4PCXQ.js +718 -0
- package/packages/canvas-ui/dist/chunk-SA3JADUG.js +160 -0
- package/packages/canvas-ui/dist/chunk-UEIMM6PV.js +508 -0
- package/packages/canvas-ui/dist/dock/index.d.ts +37 -0
- package/packages/canvas-ui/dist/dock/index.js +10 -0
- package/packages/canvas-ui/dist/editor/index.d.ts +44 -0
- package/packages/canvas-ui/dist/editor/index.js +10 -0
- package/packages/canvas-ui/dist/graph-2d/index.d.ts +270 -0
- package/packages/canvas-ui/dist/graph-2d/index.js +50 -0
- package/packages/canvas-ui/dist/index.d.ts +176 -0
- package/packages/canvas-ui/dist/index.js +1171 -0
- package/packages/canvas-ui/dist/predictive/index.d.ts +74 -0
- package/packages/canvas-ui/dist/predictive/index.js +13 -0
- package/packages/canvas-ui/dist/tile-menu/index.d.ts +45 -0
- package/packages/canvas-ui/dist/tile-menu/index.js +12 -0
- package/packages/canvas-ui/dist/types-D3RRfQUo.d.ts +46 -0
- package/packages/canvas-ui/floks.manifest.json +35 -0
- package/packages/canvas-ui/package.json +82 -0
- package/packages/ui/dist/BottomStrip-DYhAUQR4.d.ts +12 -0
- package/packages/ui/dist/chunk-5ZMXFCD4.js +0 -0
- package/packages/ui/dist/chunk-62ITRDJX.js +141 -0
- package/packages/ui/dist/chunk-A2CDQISJ.js +32 -0
- package/packages/ui/dist/chunk-C32CB5WO.js +1161 -0
- package/packages/ui/dist/chunk-C3L53YN4.js +372 -0
- package/packages/ui/dist/chunk-DABYTYOU.js +351 -0
- package/packages/ui/dist/chunk-DZJ3NSCX.js +665 -0
- package/packages/ui/dist/chunk-ELZOBTWX.js +562 -0
- package/packages/ui/dist/chunk-GKP6KHTI.js +75 -0
- package/packages/ui/dist/chunk-GWW3BGOB.js +238 -0
- package/packages/ui/dist/chunk-KIFQA7L6.js +167 -0
- package/packages/ui/dist/chunk-OHISLM3Z.js +703 -0
- package/packages/ui/dist/chunk-OR5ZNJI4.js +62 -0
- package/packages/ui/dist/chunk-QWXEW7WH.js +238 -0
- package/packages/ui/dist/chunk-RLQ2ALJG.js +182 -0
- package/packages/ui/dist/chunk-S227WJ5E.js +152 -0
- package/packages/ui/dist/chunk-SLJBDDSN.js +0 -0
- package/packages/ui/dist/chunk-TXKLROFC.js +370 -0
- package/packages/ui/dist/chunk-UOR4P6PO.js +32 -0
- package/packages/ui/dist/chunk-W42SJGOJ.js +19 -0
- package/packages/ui/dist/chunk-XXIH2HWI.js +297 -0
- package/packages/ui/dist/chunk-ZQK5I5ZL.js +277 -0
- package/packages/ui/dist/components/auth/index.css +183 -0
- package/packages/ui/dist/components/auth/index.d.ts +10 -0
- package/packages/ui/dist/components/auth/index.js +8 -0
- package/packages/ui/dist/components/base/index.css +183 -0
- package/packages/ui/dist/components/base/index.d.ts +74 -0
- package/packages/ui/dist/components/base/index.js +33 -0
- package/packages/ui/dist/components/bottom-strip/index.css +183 -0
- package/packages/ui/dist/components/bottom-strip/index.d.ts +67 -0
- package/packages/ui/dist/components/bottom-strip/index.js +22 -0
- package/packages/ui/dist/components/composite/index.css +273 -0
- package/packages/ui/dist/components/composite/index.d.ts +104 -0
- package/packages/ui/dist/components/composite/index.js +33 -0
- package/packages/ui/dist/components/containers/index.css +404 -0
- package/packages/ui/dist/components/containers/index.d.ts +93 -0
- package/packages/ui/dist/components/containers/index.js +35 -0
- package/packages/ui/dist/components/layout/index.css +246 -0
- package/packages/ui/dist/components/layout/index.d.ts +53 -0
- package/packages/ui/dist/components/layout/index.js +16 -0
- package/packages/ui/dist/components/settings/index.css +183 -0
- package/packages/ui/dist/components/settings/index.d.ts +14 -0
- package/packages/ui/dist/components/settings/index.js +12 -0
- package/packages/ui/dist/components/workspace/index.d.ts +101 -0
- package/packages/ui/dist/components/workspace/index.js +16 -0
- package/packages/ui/dist/hierarchy/index.css +183 -0
- package/packages/ui/dist/hierarchy/index.d.ts +168 -0
- package/packages/ui/dist/hierarchy/index.js +36 -0
- package/packages/ui/dist/index.css +467 -0
- package/packages/ui/dist/index.d.ts +16 -0
- package/packages/ui/dist/index.js +282 -0
- package/packages/ui/dist/palette/index.d.ts +29 -0
- package/packages/ui/dist/palette/index.js +27 -0
- package/packages/ui/dist/palettes-De5u5iID.d.ts +394 -0
- package/packages/ui/dist/theme/index.d.ts +12063 -0
- package/packages/ui/dist/theme/index.js +96 -0
- package/packages/ui/floks.manifest.json +119 -0
- package/packages/ui/package.json +92 -0
- package/packages/ui/src/styles/base.css +130 -0
- package/packages/ui/src/styles/composite.css +6 -0
- package/packages/ui/src/styles/containers.css +10 -0
- package/packages/ui/src/styles/details-panel.css +147 -0
- package/packages/ui/src/styles/layout.css +58 -0
- package/packages/ui/src/theme/tokens.css +93 -0
package/README.md
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
#
|
|
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
package/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,69 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultrst-armada/floks",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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,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
|
+
};
|