@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.
- package/README.md +2 -3
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +69 -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-34YRDZPJ.js +189 -0
- package/packages/canvas-ui/dist/chunk-6VB74RCH.js +147 -0
- package/packages/canvas-ui/dist/chunk-OOTTFWXA.js +508 -0
- package/packages/canvas-ui/dist/chunk-QTTWWBNN.js +114 -0
- package/packages/canvas-ui/dist/chunk-SA3JADUG.js +160 -0
- package/packages/canvas-ui/dist/chunk-U2SC3RMJ.js +391 -0
- package/packages/canvas-ui/dist/chunk-XVAHT5WE.js +718 -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 +75 -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-ATQPZLJX.js +238 -0
- package/packages/ui/dist/chunk-C32CB5WO.js +1161 -0
- package/packages/ui/dist/chunk-C3L53YN4.js +372 -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-KYFSUQVT.js +769 -0
- package/packages/ui/dist/chunk-OR5ZNJI4.js +62 -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-VEWPTZGO.js +351 -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 +233 -0
- package/packages/ui/dist/hierarchy/index.js +74 -0
- package/packages/ui/dist/index.css +467 -0
- package/packages/ui/dist/index.d.ts +16 -0
- package/packages/ui/dist/index.js +320 -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
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// src/tile-menu/Tile.tsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function Tile({ tile, onClick, onContextMenu, dragHandleProps, isDragging }) {
|
|
4
|
+
return /* @__PURE__ */ jsxs(
|
|
5
|
+
"div",
|
|
6
|
+
{
|
|
7
|
+
style: {
|
|
8
|
+
backgroundColor: "rgba(30, 30, 30, 0.7)",
|
|
9
|
+
backdropFilter: "blur(12px)",
|
|
10
|
+
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
11
|
+
borderRadius: "12px",
|
|
12
|
+
padding: "16px",
|
|
13
|
+
display: "flex",
|
|
14
|
+
flexDirection: "column",
|
|
15
|
+
gap: "8px",
|
|
16
|
+
cursor: "pointer",
|
|
17
|
+
opacity: isDragging ? 0.5 : 1,
|
|
18
|
+
boxShadow: isDragging ? "0 8px 16px rgba(0,0,0,0.5)" : "0 2px 4px rgba(0,0,0,0.2)",
|
|
19
|
+
transition: "transform 0.2s, box-shadow 0.2s"
|
|
20
|
+
},
|
|
21
|
+
onClick: () => onClick?.(tile.id),
|
|
22
|
+
onContextMenu: (e) => onContextMenu?.(e, tile.id),
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
25
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
26
|
+
tile.icon && /* @__PURE__ */ jsx("span", { style: { fontSize: "24px" }, children: tile.icon }),
|
|
27
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column" }, children: [
|
|
28
|
+
/* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 600, fontSize: "14px" }, children: tile.title }),
|
|
29
|
+
tile.subtitle && /* @__PURE__ */ jsx("span", { style: { color: "#aaa", fontSize: "12px" }, children: tile.subtitle })
|
|
30
|
+
] })
|
|
31
|
+
] }),
|
|
32
|
+
/* @__PURE__ */ jsx("div", { ...dragHandleProps, style: { cursor: "grab", padding: "4px", color: "#666" }, children: "\u283F" })
|
|
33
|
+
] }),
|
|
34
|
+
tile.badges && tile.badges.length > 0 && /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "4px", flexWrap: "wrap", marginTop: "8px" }, children: tile.badges.map((badge) => /* @__PURE__ */ jsx("span", { style: {
|
|
35
|
+
fontSize: "10px",
|
|
36
|
+
backgroundColor: "rgba(255,255,255,0.1)",
|
|
37
|
+
padding: "2px 6px",
|
|
38
|
+
borderRadius: "12px",
|
|
39
|
+
color: "#ccc"
|
|
40
|
+
}, children: badge }, badge)) })
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/tile-menu/TileGrid.tsx
|
|
47
|
+
import { useState, useRef, useEffect } from "react";
|
|
48
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
49
|
+
function TileGrid({
|
|
50
|
+
tiles,
|
|
51
|
+
onReorder: _onReorder,
|
|
52
|
+
onTileClick
|
|
53
|
+
}) {
|
|
54
|
+
const containerRef = useRef(null);
|
|
55
|
+
const [columns, setColumns] = useState(3);
|
|
56
|
+
const [tileMinHeight, setTileMinHeight] = useState(110);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!containerRef.current) return;
|
|
59
|
+
const observer = new ResizeObserver((entries) => {
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
const { width, height } = entry.contentRect;
|
|
62
|
+
if (width <= 0) return;
|
|
63
|
+
let cols = 1;
|
|
64
|
+
if (width >= 1200) cols = 4;
|
|
65
|
+
else if (width >= 860) cols = 3;
|
|
66
|
+
else if (width >= 540) cols = 2;
|
|
67
|
+
else cols = 1;
|
|
68
|
+
const estimatedRows = Math.ceil(tiles.length / cols);
|
|
69
|
+
let dynamicHeight = 110;
|
|
70
|
+
if (height > 0 && estimatedRows > 0) {
|
|
71
|
+
const availableRowHeight = (height - (estimatedRows + 1) * 16) / estimatedRows;
|
|
72
|
+
dynamicHeight = Math.max(90, Math.min(180, Math.floor(availableRowHeight)));
|
|
73
|
+
}
|
|
74
|
+
setColumns(cols);
|
|
75
|
+
setTileMinHeight(dynamicHeight);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
observer.observe(containerRef.current);
|
|
79
|
+
return () => observer.disconnect();
|
|
80
|
+
}, [tiles.length]);
|
|
81
|
+
return /* @__PURE__ */ jsx2(
|
|
82
|
+
"div",
|
|
83
|
+
{
|
|
84
|
+
ref: containerRef,
|
|
85
|
+
className: "skipo-tile-grid-container",
|
|
86
|
+
style: {
|
|
87
|
+
display: "grid",
|
|
88
|
+
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
|
89
|
+
gridAutoRows: `minmax(${tileMinHeight}px, auto)`,
|
|
90
|
+
gap: "14px",
|
|
91
|
+
padding: "14px",
|
|
92
|
+
width: "100%",
|
|
93
|
+
height: "100%",
|
|
94
|
+
boxSizing: "border-box",
|
|
95
|
+
overflowY: "auto"
|
|
96
|
+
},
|
|
97
|
+
children: tiles.map((tile) => /* @__PURE__ */ jsx2(Tile, { tile, onClick: onTileClick }, tile.id))
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/tile-menu/TileCreateButton.tsx
|
|
103
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
104
|
+
function TileCreateButton({ onClick }) {
|
|
105
|
+
return /* @__PURE__ */ jsx3(
|
|
106
|
+
"div",
|
|
107
|
+
{
|
|
108
|
+
onClick,
|
|
109
|
+
style: {
|
|
110
|
+
border: "2px dashed rgba(255, 255, 255, 0.2)",
|
|
111
|
+
borderRadius: "12px",
|
|
112
|
+
padding: "16px",
|
|
113
|
+
display: "flex",
|
|
114
|
+
justifyContent: "center",
|
|
115
|
+
alignItems: "center",
|
|
116
|
+
cursor: "pointer",
|
|
117
|
+
minHeight: "100px",
|
|
118
|
+
color: "rgba(255, 255, 255, 0.5)",
|
|
119
|
+
transition: "all 0.2s"
|
|
120
|
+
},
|
|
121
|
+
onMouseEnter: (e) => {
|
|
122
|
+
e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.5)";
|
|
123
|
+
e.currentTarget.style.color = "#fff";
|
|
124
|
+
},
|
|
125
|
+
onMouseLeave: (e) => {
|
|
126
|
+
e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.2)";
|
|
127
|
+
e.currentTarget.style.color = "rgba(255, 255, 255, 0.5)";
|
|
128
|
+
},
|
|
129
|
+
children: /* @__PURE__ */ jsx3("span", { style: { fontSize: "32px" }, children: "+" })
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/tile-menu/useTileMenu.ts
|
|
135
|
+
import { useState as useState2, useCallback } from "react";
|
|
136
|
+
function useTileMenu(initialTiles = []) {
|
|
137
|
+
const [tiles, setTiles] = useState2(initialTiles);
|
|
138
|
+
const addTile = useCallback((tile) => {
|
|
139
|
+
setTiles((prev) => [...prev, tile]);
|
|
140
|
+
}, []);
|
|
141
|
+
const removeTile = useCallback((id) => {
|
|
142
|
+
setTiles((prev) => prev.filter((t) => t.id !== id));
|
|
143
|
+
}, []);
|
|
144
|
+
const reorderTiles = useCallback((newOrder) => {
|
|
145
|
+
setTiles(newOrder);
|
|
146
|
+
}, []);
|
|
147
|
+
return {
|
|
148
|
+
tiles,
|
|
149
|
+
addTile,
|
|
150
|
+
removeTile,
|
|
151
|
+
reorderTiles
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export {
|
|
156
|
+
Tile,
|
|
157
|
+
TileGrid,
|
|
158
|
+
TileCreateButton,
|
|
159
|
+
useTileMenu
|
|
160
|
+
};
|
|
@@ -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: `floks-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
|
+
};
|