@signalsandsorcery/plugin-sdk 2.36.2 → 2.38.2
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/dist/index.d.mts +101 -4
- package/dist/index.d.ts +101 -4
- package/dist/index.js +880 -710
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +783 -615
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -82,6 +82,7 @@ __export(index_exports, {
|
|
|
82
82
|
TRANSITION_DESIGNER_DRAFT_KEY: () => TRANSITION_DESIGNER_DRAFT_KEY,
|
|
83
83
|
TrackDrawer: () => TrackDrawer,
|
|
84
84
|
TrackExternalFxSection: () => TrackExternalFxSection,
|
|
85
|
+
TrackFreezeSection: () => TrackFreezeSection,
|
|
85
86
|
TrackMeterStrip: () => TrackMeterStrip,
|
|
86
87
|
TrackRow: () => TrackRow,
|
|
87
88
|
TransitionDesigner: () => TransitionDesigner,
|
|
@@ -152,6 +153,7 @@ __export(index_exports, {
|
|
|
152
153
|
useSceneState: () => useSceneState,
|
|
153
154
|
useSoundHistory: () => useSoundHistory,
|
|
154
155
|
useTrackExternalFx: () => useTrackExternalFx,
|
|
156
|
+
useTrackFreeze: () => useTrackFreeze,
|
|
155
157
|
useTrackLevel: () => useTrackLevel,
|
|
156
158
|
useTrackLevels: () => useTrackLevels,
|
|
157
159
|
useTrackMeter: () => useTrackMeter,
|
|
@@ -228,11 +230,11 @@ var EMPTY_FX_DETAIL_STATE = {
|
|
|
228
230
|
};
|
|
229
231
|
|
|
230
232
|
// src/components/TrackRow.tsx
|
|
231
|
-
var
|
|
233
|
+
var import_react12 = __toESM(require("react"));
|
|
232
234
|
var import_lucide_react = require("lucide-react");
|
|
233
235
|
|
|
234
236
|
// src/components/TrackDrawer.tsx
|
|
235
|
-
var
|
|
237
|
+
var import_react5 = require("react");
|
|
236
238
|
|
|
237
239
|
// src/constants/fx-presets.ts
|
|
238
240
|
var EQ_PRESETS = {
|
|
@@ -1214,14 +1216,132 @@ function TrackExternalFxSection({
|
|
|
1214
1216
|
);
|
|
1215
1217
|
}
|
|
1216
1218
|
|
|
1217
|
-
// src/components/
|
|
1219
|
+
// src/components/TrackFreezeSection.tsx
|
|
1218
1220
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1221
|
+
var STALE_LABELS = {
|
|
1222
|
+
midi: "MIDI changed",
|
|
1223
|
+
preset: "preset changed",
|
|
1224
|
+
instrument: "instrument changed",
|
|
1225
|
+
"external-fx": "FX changed",
|
|
1226
|
+
role: "role changed"
|
|
1227
|
+
};
|
|
1228
|
+
function TrackFreezeSection({
|
|
1229
|
+
state,
|
|
1230
|
+
busy,
|
|
1231
|
+
error,
|
|
1232
|
+
onFreeze,
|
|
1233
|
+
onUnfreeze
|
|
1234
|
+
}) {
|
|
1235
|
+
const frozen = state?.frozen === true;
|
|
1236
|
+
const stale = frozen && state?.stale === true;
|
|
1237
|
+
const missing = state?.missingDeps ?? [];
|
|
1238
|
+
const unfreezeBlocked = frozen && missing.length > 0;
|
|
1239
|
+
const statusLine = !state ? "Reading freeze state\u2026" : !frozen ? state.latentFreshFreeze ? "Live \u2014 a fresh stem is cached, freezing is instant." : "Live \u2014 playing through the instrument and FX chain." : stale ? `\u26A0\uFE0F\u2744 Frozen (stale) \u2014 ${state.staleReasons.map((r) => STALE_LABELS[r] ?? r).join(", ")} since the stem rendered. Playback keeps the old stem until you re-freeze.` : "\u2744 Frozen \u2014 playing the rendered stem. Mixer (volume \xB7 pan \xB7 mute \xB7 solo) stays live.";
|
|
1240
|
+
const primaryLabel = busy === "freeze" ? "Rendering stem\u2026" : busy === "unfreeze" ? "Restoring chain\u2026" : !frozen ? "\u2744 Freeze track" : stale ? "\u2744 Re-freeze (render new stem)" : "Unfreeze track";
|
|
1241
|
+
const primaryAction = !frozen || stale ? onFreeze : onUnfreeze;
|
|
1242
|
+
const primaryDisabled = busy !== null || !state || frozen && !stale && unfreezeBlocked;
|
|
1243
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-freeze-section", children: [
|
|
1244
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[11px] text-sas-muted/80 leading-snug", "data-testid": "sdk-freeze-status", children: statusLine }),
|
|
1245
|
+
frozen && missing.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1246
|
+
"div",
|
|
1247
|
+
{
|
|
1248
|
+
className: "text-[11px] leading-snug rounded-sm border border-sas-border bg-sas-panel-alt px-2 py-1.5 text-sas-muted",
|
|
1249
|
+
"data-testid": "sdk-freeze-missing-deps",
|
|
1250
|
+
children: [
|
|
1251
|
+
"Missing on this machine:",
|
|
1252
|
+
" ",
|
|
1253
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-sas-accent", children: missing.map((d) => d.name).join(", ") }),
|
|
1254
|
+
". ",
|
|
1255
|
+
"Install (or rescan plugins if blacklisted) to unfreeze \u2014 the frozen sound keeps playing meanwhile."
|
|
1256
|
+
]
|
|
1257
|
+
}
|
|
1258
|
+
),
|
|
1259
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1260
|
+
"button",
|
|
1261
|
+
{
|
|
1262
|
+
type: "button",
|
|
1263
|
+
"data-testid": "sdk-freeze-primary",
|
|
1264
|
+
disabled: primaryDisabled,
|
|
1265
|
+
onClick: primaryAction,
|
|
1266
|
+
className: `w-full py-2 text-xs font-medium rounded-sm border transition-colors ${primaryDisabled ? "border-sas-border text-sas-muted/40 cursor-not-allowed" : "border-sas-accent bg-sas-accent/20 text-sas-accent hover:bg-sas-accent/40"}`,
|
|
1267
|
+
title: !frozen ? "Render this track to audio and disable its plugins (mix controls stay live)" : stale ? "Sound inputs changed \u2014 render a fresh stem" : unfreezeBlocked ? "Unfreeze needs the missing plugins installed" : "Remove the stem and re-enable the instrument + FX chain",
|
|
1268
|
+
children: primaryLabel
|
|
1269
|
+
}
|
|
1270
|
+
),
|
|
1271
|
+
frozen && stale && !unfreezeBlocked && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1272
|
+
"button",
|
|
1273
|
+
{
|
|
1274
|
+
type: "button",
|
|
1275
|
+
"data-testid": "sdk-freeze-unfreeze-secondary",
|
|
1276
|
+
disabled: busy !== null,
|
|
1277
|
+
onClick: onUnfreeze,
|
|
1278
|
+
className: "w-full py-1.5 text-[11px] rounded-sm border border-sas-border text-sas-muted hover:border-sas-accent hover:text-sas-accent transition-colors",
|
|
1279
|
+
children: "Unfreeze instead (back to live editing)"
|
|
1280
|
+
}
|
|
1281
|
+
),
|
|
1282
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[11px] text-red-400 leading-snug", "data-testid": "sdk-freeze-error", children: error }),
|
|
1283
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[10px] text-sas-muted/50 leading-snug", children: "Frozen tracks survive missing plugins and travel inside project backups. The stem is kept after unfreezing, so re-freezing an unchanged track is instant." })
|
|
1284
|
+
] });
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
// src/hooks/useTrackFreeze.ts
|
|
1288
|
+
var import_react4 = require("react");
|
|
1289
|
+
function useTrackFreeze(host, trackId) {
|
|
1290
|
+
const enabled = !!host && typeof host.getTrackFreezeState === "function";
|
|
1291
|
+
const [state, setState] = (0, import_react4.useState)(null);
|
|
1292
|
+
const [busy, setBusy] = (0, import_react4.useState)(null);
|
|
1293
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
1294
|
+
const refresh = (0, import_react4.useCallback)(async () => {
|
|
1295
|
+
if (!enabled || !host?.getTrackFreezeState) return;
|
|
1296
|
+
try {
|
|
1297
|
+
setState(await host.getTrackFreezeState(trackId));
|
|
1298
|
+
} catch {
|
|
1299
|
+
setState(null);
|
|
1300
|
+
}
|
|
1301
|
+
}, [enabled, host, trackId]);
|
|
1302
|
+
(0, import_react4.useEffect)(() => {
|
|
1303
|
+
void refresh();
|
|
1304
|
+
}, [refresh]);
|
|
1305
|
+
(0, import_react4.useEffect)(() => {
|
|
1306
|
+
if (!enabled) return void 0;
|
|
1307
|
+
const onFreezeChanged = () => {
|
|
1308
|
+
void refresh();
|
|
1309
|
+
};
|
|
1310
|
+
window.addEventListener("sas:freeze-changed", onFreezeChanged);
|
|
1311
|
+
return () => window.removeEventListener("sas:freeze-changed", onFreezeChanged);
|
|
1312
|
+
}, [enabled, refresh]);
|
|
1313
|
+
const run = (0, import_react4.useCallback)(
|
|
1314
|
+
async (action) => {
|
|
1315
|
+
if (!host) return;
|
|
1316
|
+
const method = action === "freeze" ? host.freezeTrack : host.unfreezeTrack;
|
|
1317
|
+
if (typeof method !== "function") return;
|
|
1318
|
+
setBusy(action);
|
|
1319
|
+
setError(null);
|
|
1320
|
+
try {
|
|
1321
|
+
setState(await method.call(host, trackId));
|
|
1322
|
+
} catch (e) {
|
|
1323
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1324
|
+
void refresh();
|
|
1325
|
+
} finally {
|
|
1326
|
+
setBusy(null);
|
|
1327
|
+
}
|
|
1328
|
+
},
|
|
1329
|
+
[host, trackId, refresh]
|
|
1330
|
+
);
|
|
1331
|
+
const freeze = (0, import_react4.useCallback)(() => run("freeze"), [run]);
|
|
1332
|
+
const unfreeze = (0, import_react4.useCallback)(() => run("unfreeze"), [run]);
|
|
1333
|
+
return { enabled, state, busy, error, refresh, freeze, unfreeze };
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// src/components/TrackDrawer.tsx
|
|
1337
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1219
1338
|
var TAB_LABELS = {
|
|
1220
1339
|
fx: "FX",
|
|
1221
1340
|
pick: "Pick",
|
|
1222
1341
|
history: "History",
|
|
1223
1342
|
import: "Import",
|
|
1224
|
-
edit: "Edit"
|
|
1343
|
+
edit: "Edit",
|
|
1344
|
+
freeze: "\u2744 Freeze"
|
|
1225
1345
|
};
|
|
1226
1346
|
function TrackDrawer({
|
|
1227
1347
|
activeTab,
|
|
@@ -1233,6 +1353,7 @@ function TrackDrawer({
|
|
|
1233
1353
|
onFxDryWetChange,
|
|
1234
1354
|
fxDisabled = false,
|
|
1235
1355
|
externalFxHost,
|
|
1356
|
+
freeze,
|
|
1236
1357
|
instruments = [],
|
|
1237
1358
|
currentPluginId = null,
|
|
1238
1359
|
isLoading = false,
|
|
@@ -1255,23 +1376,27 @@ function TrackDrawer({
|
|
|
1255
1376
|
editSnap,
|
|
1256
1377
|
onAuditionNote
|
|
1257
1378
|
}) {
|
|
1258
|
-
const [search, setSearch] = (0,
|
|
1379
|
+
const [search, setSearch] = (0, import_react5.useState)("");
|
|
1259
1380
|
const fxEnabled = !!onFxToggle;
|
|
1260
1381
|
const pickEnabled = !!onSelect;
|
|
1261
1382
|
const historyEnabled = !!onRestoreSound;
|
|
1262
1383
|
const importEnabled = !!onImportSound;
|
|
1263
1384
|
const editEnabled = !!onNotesChange;
|
|
1264
|
-
const
|
|
1385
|
+
const internalFreeze = useTrackFreeze(freeze ? void 0 : externalFxHost, trackId);
|
|
1386
|
+
const fz = freeze ?? internalFreeze;
|
|
1387
|
+
const freezeEnabled = fz.enabled;
|
|
1388
|
+
const enabledTabs = (0, import_react5.useMemo)(() => {
|
|
1265
1389
|
const tabs = [];
|
|
1266
1390
|
if (fxEnabled) tabs.push("fx");
|
|
1267
1391
|
if (pickEnabled) tabs.push("pick");
|
|
1268
1392
|
if (historyEnabled) tabs.push("history");
|
|
1269
1393
|
if (importEnabled) tabs.push("import");
|
|
1270
1394
|
if (editEnabled) tabs.push("edit");
|
|
1395
|
+
if (freezeEnabled) tabs.push("freeze");
|
|
1271
1396
|
return tabs;
|
|
1272
|
-
}, [fxEnabled, pickEnabled, historyEnabled, importEnabled, editEnabled]);
|
|
1397
|
+
}, [fxEnabled, pickEnabled, historyEnabled, importEnabled, editEnabled, freezeEnabled]);
|
|
1273
1398
|
const SURGE_XT_DEFAULT_ID = "Surge XT";
|
|
1274
|
-
const filtered = (0,
|
|
1399
|
+
const filtered = (0, import_react5.useMemo)(() => {
|
|
1275
1400
|
let all = instruments.filter((i) => i.name !== "Surge XT");
|
|
1276
1401
|
if (search.trim()) {
|
|
1277
1402
|
const q = search.toLowerCase();
|
|
@@ -1291,12 +1416,12 @@ function TrackDrawer({
|
|
|
1291
1416
|
const history = soundHistory ?? [];
|
|
1292
1417
|
const effectiveTab = enabledTabs.includes(activeTab) ? activeTab : enabledTabs[0] ?? "fx";
|
|
1293
1418
|
const tabClass = (active) => `px-2 py-0.5 text-xs rounded-sm transition-colors ${active ? "bg-sas-accent/20 text-sas-accent font-medium" : "text-sas-muted hover:text-sas-accent"}`;
|
|
1294
|
-
const strip = enabledTabs.length > 1 ? /* @__PURE__ */ (0,
|
|
1419
|
+
const strip = enabledTabs.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1295
1420
|
"div",
|
|
1296
1421
|
{
|
|
1297
1422
|
className: "flex items-center gap-1 border-b border-sas-border pb-1",
|
|
1298
1423
|
"data-testid": "sdk-drawer-tabs",
|
|
1299
|
-
children: enabledTabs.map((tab) => /* @__PURE__ */ (0,
|
|
1424
|
+
children: enabledTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1300
1425
|
"button",
|
|
1301
1426
|
{
|
|
1302
1427
|
type: "button",
|
|
@@ -1310,9 +1435,9 @@ function TrackDrawer({
|
|
|
1310
1435
|
}
|
|
1311
1436
|
) : null;
|
|
1312
1437
|
const currentSound = soundHistoryCursor >= 0 && soundHistoryCursor < history.length ? history[soundHistoryCursor].label : null;
|
|
1313
|
-
const header = strip || currentSound ? /* @__PURE__ */ (0,
|
|
1438
|
+
const header = strip || currentSound ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-1", "data-testid": "sdk-drawer-header", children: [
|
|
1314
1439
|
strip,
|
|
1315
|
-
currentSound && /* @__PURE__ */ (0,
|
|
1440
|
+
currentSound && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1316
1441
|
"span",
|
|
1317
1442
|
{
|
|
1318
1443
|
className: "text-[10px] text-sas-muted/60 truncate px-0.5",
|
|
@@ -1321,10 +1446,41 @@ function TrackDrawer({
|
|
|
1321
1446
|
}
|
|
1322
1447
|
)
|
|
1323
1448
|
] }) : null;
|
|
1449
|
+
if (effectiveTab === "freeze") {
|
|
1450
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-freeze", children: [
|
|
1451
|
+
header,
|
|
1452
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1453
|
+
TrackFreezeSection,
|
|
1454
|
+
{
|
|
1455
|
+
state: fz.state,
|
|
1456
|
+
busy: fz.busy,
|
|
1457
|
+
error: fz.error,
|
|
1458
|
+
onFreeze: () => void fz.freeze(),
|
|
1459
|
+
onUnfreeze: () => void fz.unfreeze()
|
|
1460
|
+
}
|
|
1461
|
+
)
|
|
1462
|
+
] });
|
|
1463
|
+
}
|
|
1464
|
+
if (fz.state?.frozen === true) {
|
|
1465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-frozen-lock", children: [
|
|
1466
|
+
header,
|
|
1467
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-[11px] text-sas-muted/80 leading-snug rounded-sm border border-sas-border bg-sas-panel-alt px-2 py-2", children: "\u2744 This track is frozen \u2014 it plays its rendered stem, so sound editing here would be inaudible. Volume, pan, mute and solo stay live." }),
|
|
1468
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1469
|
+
"button",
|
|
1470
|
+
{
|
|
1471
|
+
type: "button",
|
|
1472
|
+
"data-testid": "sdk-drawer-frozen-goto",
|
|
1473
|
+
onClick: () => onTabChange?.("freeze"),
|
|
1474
|
+
className: "w-full py-2 text-xs font-medium rounded-sm border border-sas-accent bg-sas-accent/20 text-sas-accent hover:bg-sas-accent/40 transition-colors",
|
|
1475
|
+
children: "Open the Freeze tab to unfreeze"
|
|
1476
|
+
}
|
|
1477
|
+
)
|
|
1478
|
+
] });
|
|
1479
|
+
}
|
|
1324
1480
|
if (effectiveTab === "edit") {
|
|
1325
|
-
return /* @__PURE__ */ (0,
|
|
1481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-edit", children: [
|
|
1326
1482
|
header,
|
|
1327
|
-
/* @__PURE__ */ (0,
|
|
1483
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1328
1484
|
PianoRollEditor,
|
|
1329
1485
|
{
|
|
1330
1486
|
notes: editNotes ?? [],
|
|
@@ -1339,9 +1495,9 @@ function TrackDrawer({
|
|
|
1339
1495
|
] });
|
|
1340
1496
|
}
|
|
1341
1497
|
if (effectiveTab === "fx") {
|
|
1342
|
-
return /* @__PURE__ */ (0,
|
|
1498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-fx", children: [
|
|
1343
1499
|
header,
|
|
1344
|
-
/* @__PURE__ */ (0,
|
|
1500
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1345
1501
|
FxToggleBar,
|
|
1346
1502
|
{
|
|
1347
1503
|
trackId,
|
|
@@ -1352,20 +1508,20 @@ function TrackDrawer({
|
|
|
1352
1508
|
disabled: fxDisabled
|
|
1353
1509
|
}
|
|
1354
1510
|
),
|
|
1355
|
-
externalFxHost && /* @__PURE__ */ (0,
|
|
1511
|
+
externalFxHost && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TrackExternalFxSection, { host: externalFxHost, trackId, disabled: fxDisabled })
|
|
1356
1512
|
] });
|
|
1357
1513
|
}
|
|
1358
1514
|
if (effectiveTab === "import") {
|
|
1359
1515
|
const soundNoun = /preset/i.test(importSoundLabel ?? "") ? "preset" : /sample/i.test(importSoundLabel ?? "") ? "sample" : "sound";
|
|
1360
|
-
return /* @__PURE__ */ (0,
|
|
1516
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-import", children: [
|
|
1361
1517
|
header,
|
|
1362
|
-
/* @__PURE__ */ (0,
|
|
1518
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "text-[11px] text-sas-muted/70 leading-snug", children: [
|
|
1363
1519
|
"Copy the sound from a matching track in another scene \u2014 your MIDI stays, only the",
|
|
1364
1520
|
" ",
|
|
1365
1521
|
soundNoun,
|
|
1366
1522
|
" changes."
|
|
1367
1523
|
] }),
|
|
1368
|
-
/* @__PURE__ */ (0,
|
|
1524
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1369
1525
|
"button",
|
|
1370
1526
|
{
|
|
1371
1527
|
type: "button",
|
|
@@ -1383,16 +1539,16 @@ function TrackDrawer({
|
|
|
1383
1539
|
}
|
|
1384
1540
|
if (effectiveTab === "history") {
|
|
1385
1541
|
const order = history.map((_, i) => i).reverse();
|
|
1386
|
-
return /* @__PURE__ */ (0,
|
|
1542
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1387
1543
|
header,
|
|
1388
|
-
history.length === 0 ? /* @__PURE__ */ (0,
|
|
1544
|
+
history.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1389
1545
|
"div",
|
|
1390
1546
|
{
|
|
1391
1547
|
className: "text-xs text-sas-muted/60 text-center py-3",
|
|
1392
1548
|
"data-testid": "sdk-history-empty",
|
|
1393
1549
|
children: "No sounds yet \u2014 shuffle to build history."
|
|
1394
1550
|
}
|
|
1395
|
-
) : /* @__PURE__ */ (0,
|
|
1551
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1396
1552
|
"ul",
|
|
1397
1553
|
{
|
|
1398
1554
|
className: "flex flex-col gap-1 max-h-[160px] overflow-y-auto",
|
|
@@ -1400,8 +1556,8 @@ function TrackDrawer({
|
|
|
1400
1556
|
children: order.map((i) => {
|
|
1401
1557
|
const entry = history[i];
|
|
1402
1558
|
const isCurrent = i === soundHistoryCursor;
|
|
1403
|
-
return /* @__PURE__ */ (0,
|
|
1404
|
-
/* @__PURE__ */ (0,
|
|
1559
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { className: "flex items-center gap-1", children: [
|
|
1560
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1405
1561
|
"button",
|
|
1406
1562
|
{
|
|
1407
1563
|
type: "button",
|
|
@@ -1411,12 +1567,12 @@ function TrackDrawer({
|
|
|
1411
1567
|
className: `flex-1 min-w-0 flex items-center justify-between px-2 py-1.5 rounded-sm border text-left text-xs transition-colors ${isCurrent ? "border-sas-accent bg-sas-accent/20 text-sas-accent cursor-default" : "border-sas-border bg-sas-panel-alt text-sas-muted hover:border-sas-accent hover:text-sas-accent"}`,
|
|
1412
1568
|
title: isCurrent ? "Current sound" : `Restore: ${entry.label}`,
|
|
1413
1569
|
children: [
|
|
1414
|
-
/* @__PURE__ */ (0,
|
|
1415
|
-
/* @__PURE__ */ (0,
|
|
1570
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate", children: entry.label }),
|
|
1571
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[10px] text-sas-muted/60 flex-shrink-0 ml-2", children: isCurrent ? "\u25CF current" : "restore" })
|
|
1416
1572
|
]
|
|
1417
1573
|
}
|
|
1418
1574
|
),
|
|
1419
|
-
onToggleFavorite && /* @__PURE__ */ (0,
|
|
1575
|
+
onToggleFavorite && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1420
1576
|
"button",
|
|
1421
1577
|
{
|
|
1422
1578
|
type: "button",
|
|
@@ -1434,10 +1590,10 @@ function TrackDrawer({
|
|
|
1434
1590
|
] });
|
|
1435
1591
|
}
|
|
1436
1592
|
if (effectiveTab === "pick" && editorStage) {
|
|
1437
|
-
return /* @__PURE__ */ (0,
|
|
1593
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1438
1594
|
header,
|
|
1439
|
-
/* @__PURE__ */ (0,
|
|
1440
|
-
/* @__PURE__ */ (0,
|
|
1595
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1596
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1441
1597
|
"button",
|
|
1442
1598
|
{
|
|
1443
1599
|
onClick: () => onBackToInstruments?.(),
|
|
@@ -1445,9 +1601,9 @@ function TrackDrawer({
|
|
|
1445
1601
|
children: "\u2190 Back"
|
|
1446
1602
|
}
|
|
1447
1603
|
),
|
|
1448
|
-
/* @__PURE__ */ (0,
|
|
1604
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs text-sas-muted font-medium truncate flex-1", children: selectedInstrumentName ?? "Plugin" })
|
|
1449
1605
|
] }),
|
|
1450
|
-
/* @__PURE__ */ (0,
|
|
1606
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1451
1607
|
"button",
|
|
1452
1608
|
{
|
|
1453
1609
|
onClick: () => onShowEditor?.(),
|
|
@@ -1459,10 +1615,10 @@ function TrackDrawer({
|
|
|
1459
1615
|
}
|
|
1460
1616
|
const isDefaultSelected = currentPluginId === null;
|
|
1461
1617
|
const isSelected = (pluginId) => pluginId === currentPluginId;
|
|
1462
|
-
return /* @__PURE__ */ (0,
|
|
1618
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1463
1619
|
header,
|
|
1464
|
-
/* @__PURE__ */ (0,
|
|
1465
|
-
/* @__PURE__ */ (0,
|
|
1620
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1621
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1466
1622
|
"input",
|
|
1467
1623
|
{
|
|
1468
1624
|
type: "text",
|
|
@@ -1472,7 +1628,7 @@ function TrackDrawer({
|
|
|
1472
1628
|
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
1473
1629
|
}
|
|
1474
1630
|
),
|
|
1475
|
-
/* @__PURE__ */ (0,
|
|
1631
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1476
1632
|
"button",
|
|
1477
1633
|
{
|
|
1478
1634
|
onClick: () => onRefresh?.(),
|
|
@@ -1483,54 +1639,54 @@ function TrackDrawer({
|
|
|
1483
1639
|
}
|
|
1484
1640
|
)
|
|
1485
1641
|
] }),
|
|
1486
|
-
isLoading && instruments.length === 0 ? /* @__PURE__ */ (0,
|
|
1487
|
-
/* @__PURE__ */ (0,
|
|
1642
|
+
isLoading && instruments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
1643
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1488
1644
|
"button",
|
|
1489
1645
|
{
|
|
1490
1646
|
onClick: () => onSelect?.(SURGE_XT_DEFAULT_ID),
|
|
1491
1647
|
className: `flex flex-col items-start px-2 py-1.5 rounded-sm border text-left transition-colors ${isDefaultSelected ? "border-sas-accent bg-sas-accent/20 text-sas-accent" : "border-sas-border bg-sas-panel-alt text-sas-muted hover:border-sas-accent hover:text-sas-accent"}`,
|
|
1492
1648
|
title: "Surge XT \u2014 Default instrument",
|
|
1493
1649
|
children: [
|
|
1494
|
-
/* @__PURE__ */ (0,
|
|
1650
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1495
1651
|
isDefaultSelected && "\u2713 ",
|
|
1496
1652
|
"Surge XT"
|
|
1497
1653
|
] }),
|
|
1498
|
-
/* @__PURE__ */ (0,
|
|
1654
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: "Default" })
|
|
1499
1655
|
]
|
|
1500
1656
|
},
|
|
1501
1657
|
"__surge-xt-default__"
|
|
1502
1658
|
),
|
|
1503
1659
|
filtered.map((inst) => {
|
|
1504
1660
|
const selected = isSelected(inst.pluginId);
|
|
1505
|
-
return /* @__PURE__ */ (0,
|
|
1661
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1506
1662
|
"button",
|
|
1507
1663
|
{
|
|
1508
1664
|
onClick: () => onSelect?.(inst.pluginId),
|
|
1509
1665
|
className: `flex flex-col items-start px-2 py-1.5 rounded-sm border text-left transition-colors ${selected ? "border-sas-accent bg-sas-accent/20 text-sas-accent" : inst.missing ? "border-amber-500/50 bg-amber-500/10 text-amber-400 hover:border-amber-500" : "border-sas-border bg-sas-panel-alt text-sas-muted hover:border-sas-accent hover:text-sas-accent"}`,
|
|
1510
1666
|
title: `${inst.name} by ${inst.manufacturer} (${inst.type.toUpperCase()})${inst.missing ? " \u2014 MISSING" : ""}`,
|
|
1511
1667
|
children: [
|
|
1512
|
-
/* @__PURE__ */ (0,
|
|
1668
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1513
1669
|
selected && "\u2713 ",
|
|
1514
1670
|
inst.name
|
|
1515
1671
|
] }),
|
|
1516
|
-
/* @__PURE__ */ (0,
|
|
1672
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: inst.manufacturer || inst.type.toUpperCase() })
|
|
1517
1673
|
]
|
|
1518
1674
|
},
|
|
1519
1675
|
inst.pluginId
|
|
1520
1676
|
);
|
|
1521
1677
|
}),
|
|
1522
|
-
filtered.length === 0 && /* @__PURE__ */ (0,
|
|
1678
|
+
filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "col-span-2 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No other plugins found" })
|
|
1523
1679
|
] })
|
|
1524
1680
|
] });
|
|
1525
1681
|
}
|
|
1526
1682
|
|
|
1527
1683
|
// src/components/ConfirmDialog.tsx
|
|
1528
|
-
var
|
|
1684
|
+
var import_react7 = require("react");
|
|
1529
1685
|
|
|
1530
1686
|
// src/components/Modal.tsx
|
|
1531
|
-
var
|
|
1687
|
+
var import_react6 = require("react");
|
|
1532
1688
|
var import_react_dom = require("react-dom");
|
|
1533
|
-
var
|
|
1689
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1534
1690
|
function Modal({
|
|
1535
1691
|
open,
|
|
1536
1692
|
onClose,
|
|
@@ -1540,7 +1696,7 @@ function Modal({
|
|
|
1540
1696
|
closeOnEscape = true,
|
|
1541
1697
|
initialFocusRef
|
|
1542
1698
|
}) {
|
|
1543
|
-
(0,
|
|
1699
|
+
(0, import_react6.useEffect)(() => {
|
|
1544
1700
|
if (!open) return void 0;
|
|
1545
1701
|
const onKey = (e) => {
|
|
1546
1702
|
if (closeOnEscape && e.key === "Escape") {
|
|
@@ -1554,7 +1710,7 @@ function Modal({
|
|
|
1554
1710
|
}, [open, onClose, closeOnEscape, initialFocusRef]);
|
|
1555
1711
|
if (!open) return null;
|
|
1556
1712
|
return (0, import_react_dom.createPortal)(
|
|
1557
|
-
/* @__PURE__ */ (0,
|
|
1713
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1558
1714
|
"div",
|
|
1559
1715
|
{
|
|
1560
1716
|
className: "fixed inset-0 z-[1000] flex items-center justify-center bg-black/60",
|
|
@@ -1568,7 +1724,7 @@ function Modal({
|
|
|
1568
1724
|
}
|
|
1569
1725
|
|
|
1570
1726
|
// src/components/ConfirmDialog.tsx
|
|
1571
|
-
var
|
|
1727
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1572
1728
|
function ConfirmDialog({
|
|
1573
1729
|
open,
|
|
1574
1730
|
title,
|
|
@@ -1580,8 +1736,8 @@ function ConfirmDialog({
|
|
|
1580
1736
|
onCancel,
|
|
1581
1737
|
testIdPrefix = "confirm-dialog"
|
|
1582
1738
|
}) {
|
|
1583
|
-
const cancelRef = (0,
|
|
1584
|
-
return /* @__PURE__ */ (0,
|
|
1739
|
+
const cancelRef = (0, import_react7.useRef)(null);
|
|
1740
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Modal, { open, onClose: onCancel, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1585
1741
|
"div",
|
|
1586
1742
|
{
|
|
1587
1743
|
className: "w-[360px] max-w-[90vw] flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
@@ -1591,8 +1747,8 @@ function ConfirmDialog({
|
|
|
1591
1747
|
"aria-label": title,
|
|
1592
1748
|
"data-testid": `${testIdPrefix}-modal`,
|
|
1593
1749
|
children: [
|
|
1594
|
-
/* @__PURE__ */ (0,
|
|
1595
|
-
/* @__PURE__ */ (0,
|
|
1750
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "px-4 py-3 border-b border-sas-border", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-sm font-medium text-sas-text", "data-testid": `${testIdPrefix}-title`, children: title }) }),
|
|
1751
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1596
1752
|
"div",
|
|
1597
1753
|
{
|
|
1598
1754
|
className: "px-4 py-3 text-xs text-sas-muted leading-relaxed break-words",
|
|
@@ -1600,8 +1756,8 @@ function ConfirmDialog({
|
|
|
1600
1756
|
children: message
|
|
1601
1757
|
}
|
|
1602
1758
|
),
|
|
1603
|
-
/* @__PURE__ */ (0,
|
|
1604
|
-
/* @__PURE__ */ (0,
|
|
1759
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-end gap-2 px-4 py-3 border-t border-sas-border", children: [
|
|
1760
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1605
1761
|
"button",
|
|
1606
1762
|
{
|
|
1607
1763
|
ref: cancelRef,
|
|
@@ -1612,7 +1768,7 @@ function ConfirmDialog({
|
|
|
1612
1768
|
children: cancelLabel
|
|
1613
1769
|
}
|
|
1614
1770
|
),
|
|
1615
|
-
/* @__PURE__ */ (0,
|
|
1771
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1616
1772
|
"button",
|
|
1617
1773
|
{
|
|
1618
1774
|
type: "button",
|
|
@@ -1629,7 +1785,7 @@ function ConfirmDialog({
|
|
|
1629
1785
|
}
|
|
1630
1786
|
|
|
1631
1787
|
// src/components/LevelMeter.tsx
|
|
1632
|
-
var
|
|
1788
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1633
1789
|
var COLOR_GREEN = "#2BD576";
|
|
1634
1790
|
var COLOR_ORANGE = "#F5A623";
|
|
1635
1791
|
var COLOR_RED = "#FF4D5E";
|
|
@@ -1657,7 +1813,7 @@ var LevelMeter = ({
|
|
|
1657
1813
|
const widthPct = active ? dbToPct(peakDb) : 0;
|
|
1658
1814
|
const showPeak = peakHoldDb != null && active && peakHoldDb > -60;
|
|
1659
1815
|
const peakHoldPct = showPeak ? dbToPct(peakHoldDb) : 0;
|
|
1660
|
-
return /* @__PURE__ */ (0,
|
|
1816
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1661
1817
|
"div",
|
|
1662
1818
|
{
|
|
1663
1819
|
className: `sas-level-meter ${className ?? ""}`,
|
|
@@ -1668,7 +1824,7 @@ var LevelMeter = ({
|
|
|
1668
1824
|
gap: compact ? 0 : 6
|
|
1669
1825
|
},
|
|
1670
1826
|
children: [
|
|
1671
|
-
/* @__PURE__ */ (0,
|
|
1827
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1672
1828
|
"div",
|
|
1673
1829
|
{
|
|
1674
1830
|
style: {
|
|
@@ -1682,8 +1838,8 @@ var LevelMeter = ({
|
|
|
1682
1838
|
minWidth: compact ? 0 : 60
|
|
1683
1839
|
},
|
|
1684
1840
|
children: [
|
|
1685
|
-
/* @__PURE__ */ (0,
|
|
1686
|
-
/* @__PURE__ */ (0,
|
|
1841
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { position: "absolute", inset: 0, background: METER_GRADIENT } }),
|
|
1842
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1687
1843
|
"div",
|
|
1688
1844
|
{
|
|
1689
1845
|
style: {
|
|
@@ -1697,7 +1853,7 @@ var LevelMeter = ({
|
|
|
1697
1853
|
}
|
|
1698
1854
|
}
|
|
1699
1855
|
),
|
|
1700
|
-
/* @__PURE__ */ (0,
|
|
1856
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1701
1857
|
"div",
|
|
1702
1858
|
{
|
|
1703
1859
|
"data-testid": `${id}-segments`,
|
|
@@ -1710,7 +1866,7 @@ var LevelMeter = ({
|
|
|
1710
1866
|
}
|
|
1711
1867
|
}
|
|
1712
1868
|
),
|
|
1713
|
-
showPeak && /* @__PURE__ */ (0,
|
|
1869
|
+
showPeak && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1714
1870
|
"div",
|
|
1715
1871
|
{
|
|
1716
1872
|
"data-testid": `${id}-peak`,
|
|
@@ -1731,7 +1887,7 @@ var LevelMeter = ({
|
|
|
1731
1887
|
]
|
|
1732
1888
|
}
|
|
1733
1889
|
),
|
|
1734
|
-
!compact && /* @__PURE__ */ (0,
|
|
1890
|
+
!compact && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1735
1891
|
"span",
|
|
1736
1892
|
{
|
|
1737
1893
|
style: {
|
|
@@ -1744,7 +1900,7 @@ var LevelMeter = ({
|
|
|
1744
1900
|
children: active && peakDb > -120 ? `${peakDb.toFixed(0)} dB` : "\u2014"
|
|
1745
1901
|
}
|
|
1746
1902
|
),
|
|
1747
|
-
clipped && /* @__PURE__ */ (0,
|
|
1903
|
+
clipped && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1748
1904
|
"span",
|
|
1749
1905
|
{
|
|
1750
1906
|
"data-testid": `${id}-clip`,
|
|
@@ -1769,7 +1925,7 @@ var LevelMeter = ({
|
|
|
1769
1925
|
};
|
|
1770
1926
|
|
|
1771
1927
|
// src/hooks/useTrackLevels.ts
|
|
1772
|
-
var
|
|
1928
|
+
var import_react8 = require("react");
|
|
1773
1929
|
var meterDiagRLast = /* @__PURE__ */ new Map();
|
|
1774
1930
|
var POLL_INTERVAL_MS = 33;
|
|
1775
1931
|
var HIDDEN_RECHECK_MS = 250;
|
|
@@ -1780,9 +1936,9 @@ function isHidden() {
|
|
|
1780
1936
|
return typeof document !== "undefined" && document.hidden === true;
|
|
1781
1937
|
}
|
|
1782
1938
|
function useTrackLevels(host, enabled = true) {
|
|
1783
|
-
const mapRef = (0,
|
|
1784
|
-
const listenersRef = (0,
|
|
1785
|
-
const handleRef = (0,
|
|
1939
|
+
const mapRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
|
|
1940
|
+
const listenersRef = (0, import_react8.useRef)(/* @__PURE__ */ new Set());
|
|
1941
|
+
const handleRef = (0, import_react8.useRef)(null);
|
|
1786
1942
|
if (handleRef.current === null) {
|
|
1787
1943
|
handleRef.current = {
|
|
1788
1944
|
getLevel: (trackId) => mapRef.current.get(trackId) ?? null,
|
|
@@ -1794,7 +1950,7 @@ function useTrackLevels(host, enabled = true) {
|
|
|
1794
1950
|
}
|
|
1795
1951
|
};
|
|
1796
1952
|
}
|
|
1797
|
-
(0,
|
|
1953
|
+
(0, import_react8.useEffect)(() => {
|
|
1798
1954
|
const notify = () => {
|
|
1799
1955
|
listenersRef.current.forEach((l) => l());
|
|
1800
1956
|
};
|
|
@@ -1864,8 +2020,8 @@ function sameLevel(a, b) {
|
|
|
1864
2020
|
return a.peakDb === b.peakDb && a.clipped === b.clipped;
|
|
1865
2021
|
}
|
|
1866
2022
|
function useTrackLevel(handle, trackId) {
|
|
1867
|
-
const [level, setLevel] = (0,
|
|
1868
|
-
(0,
|
|
2023
|
+
const [level, setLevel] = (0, import_react8.useState)(null);
|
|
2024
|
+
(0, import_react8.useEffect)(() => {
|
|
1869
2025
|
if (!handle) {
|
|
1870
2026
|
setLevel(null);
|
|
1871
2027
|
return;
|
|
@@ -1889,11 +2045,11 @@ function sameMeter(a, b) {
|
|
|
1889
2045
|
return a.active === b.active && a.clipped === b.clipped && a.peakDb === b.peakDb && Math.round(a.peakHoldDb * 2) === Math.round(b.peakHoldDb * 2);
|
|
1890
2046
|
}
|
|
1891
2047
|
function useTrackMeter(handle, trackId) {
|
|
1892
|
-
const [view, setView] = (0,
|
|
1893
|
-
const heldDbRef = (0,
|
|
1894
|
-
const heldAtRef = (0,
|
|
1895
|
-
const lastTickRef = (0,
|
|
1896
|
-
(0,
|
|
2048
|
+
const [view, setView] = (0, import_react8.useState)(IDLE_METER_VIEW);
|
|
2049
|
+
const heldDbRef = (0, import_react8.useRef)(METER_FLOOR_DB);
|
|
2050
|
+
const heldAtRef = (0, import_react8.useRef)(0);
|
|
2051
|
+
const lastTickRef = (0, import_react8.useRef)(0);
|
|
2052
|
+
(0, import_react8.useEffect)(() => {
|
|
1897
2053
|
if (!handle) {
|
|
1898
2054
|
heldDbRef.current = METER_FLOOR_DB;
|
|
1899
2055
|
lastTickRef.current = 0;
|
|
@@ -1936,8 +2092,8 @@ function useTrackMeter(handle, trackId) {
|
|
|
1936
2092
|
return view;
|
|
1937
2093
|
}
|
|
1938
2094
|
function useTransportPlaying(host) {
|
|
1939
|
-
const [playing, setPlaying] = (0,
|
|
1940
|
-
(0,
|
|
2095
|
+
const [playing, setPlaying] = (0, import_react8.useState)(false);
|
|
2096
|
+
(0, import_react8.useEffect)(() => {
|
|
1941
2097
|
if (!host) {
|
|
1942
2098
|
setPlaying(false);
|
|
1943
2099
|
return;
|
|
@@ -1965,7 +2121,7 @@ function useTransportPlaying(host) {
|
|
|
1965
2121
|
}
|
|
1966
2122
|
|
|
1967
2123
|
// src/components/TrackMeterStrip.tsx
|
|
1968
|
-
var
|
|
2124
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1969
2125
|
var TrackMeterStrip = ({
|
|
1970
2126
|
levels,
|
|
1971
2127
|
trackId,
|
|
@@ -1973,12 +2129,12 @@ var TrackMeterStrip = ({
|
|
|
1973
2129
|
className
|
|
1974
2130
|
}) => {
|
|
1975
2131
|
const meter = useTrackMeter(levels, trackId);
|
|
1976
|
-
return /* @__PURE__ */ (0,
|
|
2132
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1977
2133
|
"div",
|
|
1978
2134
|
{
|
|
1979
2135
|
"data-testid": "sdk-track-meter",
|
|
1980
2136
|
className: `w-full px-2 py-1 bg-sas-panel-alt border border-t-0 border-sas-border ${roundBottom ? "rounded-b-sm" : ""} ${className ?? ""}`,
|
|
1981
|
-
children: /* @__PURE__ */ (0,
|
|
2137
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1982
2138
|
LevelMeter,
|
|
1983
2139
|
{
|
|
1984
2140
|
compact: true,
|
|
@@ -1994,7 +2150,7 @@ var TrackMeterStrip = ({
|
|
|
1994
2150
|
};
|
|
1995
2151
|
|
|
1996
2152
|
// src/components/VolumeSlider.tsx
|
|
1997
|
-
var
|
|
2153
|
+
var import_react9 = require("react");
|
|
1998
2154
|
|
|
1999
2155
|
// src/utils/volume-conversion.ts
|
|
2000
2156
|
var SLIDER_UNITY = 0.75;
|
|
@@ -2016,7 +2172,7 @@ function dbToSlider(db) {
|
|
|
2016
2172
|
}
|
|
2017
2173
|
|
|
2018
2174
|
// src/components/VolumeSlider.tsx
|
|
2019
|
-
var
|
|
2175
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2020
2176
|
function formatDb(value) {
|
|
2021
2177
|
const db = sliderToDb(value);
|
|
2022
2178
|
if (db <= -60) return "-\u221E dB";
|
|
@@ -2024,12 +2180,12 @@ function formatDb(value) {
|
|
|
2024
2180
|
return `${sign}${db.toFixed(1)} dB`;
|
|
2025
2181
|
}
|
|
2026
2182
|
function useDebouncedCallback(callback, delay) {
|
|
2027
|
-
const timeoutRef = (0,
|
|
2028
|
-
const callbackRef = (0,
|
|
2029
|
-
(0,
|
|
2183
|
+
const timeoutRef = (0, import_react9.useRef)(null);
|
|
2184
|
+
const callbackRef = (0, import_react9.useRef)(callback);
|
|
2185
|
+
(0, import_react9.useEffect)(() => {
|
|
2030
2186
|
callbackRef.current = callback;
|
|
2031
2187
|
}, [callback]);
|
|
2032
|
-
const debouncedCallback = (0,
|
|
2188
|
+
const debouncedCallback = (0, import_react9.useCallback)(
|
|
2033
2189
|
(...args) => {
|
|
2034
2190
|
if (timeoutRef.current) {
|
|
2035
2191
|
clearTimeout(timeoutRef.current);
|
|
@@ -2040,7 +2196,7 @@ function useDebouncedCallback(callback, delay) {
|
|
|
2040
2196
|
},
|
|
2041
2197
|
[delay]
|
|
2042
2198
|
);
|
|
2043
|
-
(0,
|
|
2199
|
+
(0, import_react9.useEffect)(() => {
|
|
2044
2200
|
return () => {
|
|
2045
2201
|
if (timeoutRef.current) {
|
|
2046
2202
|
clearTimeout(timeoutRef.current);
|
|
@@ -2055,15 +2211,15 @@ var VolumeSlider = ({
|
|
|
2055
2211
|
disabled = false,
|
|
2056
2212
|
className = ""
|
|
2057
2213
|
}) => {
|
|
2058
|
-
const [localValue, setLocalValue] = (0,
|
|
2059
|
-
const [isDragging, setIsDragging] = (0,
|
|
2060
|
-
(0,
|
|
2214
|
+
const [localValue, setLocalValue] = (0, import_react9.useState)(value);
|
|
2215
|
+
const [isDragging, setIsDragging] = (0, import_react9.useState)(false);
|
|
2216
|
+
(0, import_react9.useEffect)(() => {
|
|
2061
2217
|
if (!isDragging) {
|
|
2062
2218
|
setLocalValue(value);
|
|
2063
2219
|
}
|
|
2064
2220
|
}, [value, isDragging]);
|
|
2065
2221
|
const debouncedOnChange = useDebouncedCallback(onChange, 50);
|
|
2066
|
-
const handleChange = (0,
|
|
2222
|
+
const handleChange = (0, import_react9.useCallback)(
|
|
2067
2223
|
(e) => {
|
|
2068
2224
|
const newValue = parseFloat(e.target.value);
|
|
2069
2225
|
setLocalValue(newValue);
|
|
@@ -2071,19 +2227,19 @@ var VolumeSlider = ({
|
|
|
2071
2227
|
},
|
|
2072
2228
|
[debouncedOnChange]
|
|
2073
2229
|
);
|
|
2074
|
-
const handleMouseDown = (0,
|
|
2230
|
+
const handleMouseDown = (0, import_react9.useCallback)(() => {
|
|
2075
2231
|
setIsDragging(true);
|
|
2076
2232
|
}, []);
|
|
2077
|
-
const handleMouseUp = (0,
|
|
2233
|
+
const handleMouseUp = (0, import_react9.useCallback)(() => {
|
|
2078
2234
|
setIsDragging(false);
|
|
2079
2235
|
onChange(localValue);
|
|
2080
2236
|
}, [localValue, onChange]);
|
|
2081
|
-
return /* @__PURE__ */ (0,
|
|
2237
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2082
2238
|
"div",
|
|
2083
2239
|
{
|
|
2084
2240
|
className: `flex items-center ${className}`,
|
|
2085
2241
|
title: `Volume: ${formatDb(localValue)}`,
|
|
2086
|
-
children: /* @__PURE__ */ (0,
|
|
2242
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2087
2243
|
"input",
|
|
2088
2244
|
{
|
|
2089
2245
|
type: "range",
|
|
@@ -2123,8 +2279,8 @@ var VolumeSlider = ({
|
|
|
2123
2279
|
};
|
|
2124
2280
|
|
|
2125
2281
|
// src/components/PanSlider.tsx
|
|
2126
|
-
var
|
|
2127
|
-
var
|
|
2282
|
+
var import_react10 = require("react");
|
|
2283
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2128
2284
|
function toPanDisplay(value) {
|
|
2129
2285
|
if (Math.abs(value) < 0.02) {
|
|
2130
2286
|
return "Center";
|
|
@@ -2133,12 +2289,12 @@ function toPanDisplay(value) {
|
|
|
2133
2289
|
return value < 0 ? `L${percent}` : `R${percent}`;
|
|
2134
2290
|
}
|
|
2135
2291
|
function useDebouncedCallback2(callback, delay) {
|
|
2136
|
-
const timeoutRef = (0,
|
|
2137
|
-
const callbackRef = (0,
|
|
2138
|
-
(0,
|
|
2292
|
+
const timeoutRef = (0, import_react10.useRef)(null);
|
|
2293
|
+
const callbackRef = (0, import_react10.useRef)(callback);
|
|
2294
|
+
(0, import_react10.useEffect)(() => {
|
|
2139
2295
|
callbackRef.current = callback;
|
|
2140
2296
|
}, [callback]);
|
|
2141
|
-
const debouncedCallback = (0,
|
|
2297
|
+
const debouncedCallback = (0, import_react10.useCallback)(
|
|
2142
2298
|
(...args) => {
|
|
2143
2299
|
if (timeoutRef.current) {
|
|
2144
2300
|
clearTimeout(timeoutRef.current);
|
|
@@ -2149,7 +2305,7 @@ function useDebouncedCallback2(callback, delay) {
|
|
|
2149
2305
|
},
|
|
2150
2306
|
[delay]
|
|
2151
2307
|
);
|
|
2152
|
-
(0,
|
|
2308
|
+
(0, import_react10.useEffect)(() => {
|
|
2153
2309
|
return () => {
|
|
2154
2310
|
if (timeoutRef.current) {
|
|
2155
2311
|
clearTimeout(timeoutRef.current);
|
|
@@ -2164,15 +2320,15 @@ var PanSlider = ({
|
|
|
2164
2320
|
disabled = false,
|
|
2165
2321
|
className = ""
|
|
2166
2322
|
}) => {
|
|
2167
|
-
const [localValue, setLocalValue] = (0,
|
|
2168
|
-
const [isDragging, setIsDragging] = (0,
|
|
2169
|
-
(0,
|
|
2323
|
+
const [localValue, setLocalValue] = (0, import_react10.useState)(value);
|
|
2324
|
+
const [isDragging, setIsDragging] = (0, import_react10.useState)(false);
|
|
2325
|
+
(0, import_react10.useEffect)(() => {
|
|
2170
2326
|
if (!isDragging) {
|
|
2171
2327
|
setLocalValue(value);
|
|
2172
2328
|
}
|
|
2173
2329
|
}, [value, isDragging]);
|
|
2174
2330
|
const debouncedOnChange = useDebouncedCallback2(onChange, 50);
|
|
2175
|
-
const handleChange = (0,
|
|
2331
|
+
const handleChange = (0, import_react10.useCallback)(
|
|
2176
2332
|
(e) => {
|
|
2177
2333
|
const newValue = parseFloat(e.target.value);
|
|
2178
2334
|
setLocalValue(newValue);
|
|
@@ -2180,23 +2336,23 @@ var PanSlider = ({
|
|
|
2180
2336
|
},
|
|
2181
2337
|
[debouncedOnChange]
|
|
2182
2338
|
);
|
|
2183
|
-
const handleMouseDown = (0,
|
|
2339
|
+
const handleMouseDown = (0, import_react10.useCallback)(() => {
|
|
2184
2340
|
setIsDragging(true);
|
|
2185
2341
|
}, []);
|
|
2186
|
-
const handleMouseUp = (0,
|
|
2342
|
+
const handleMouseUp = (0, import_react10.useCallback)(() => {
|
|
2187
2343
|
setIsDragging(false);
|
|
2188
2344
|
onChange(localValue);
|
|
2189
2345
|
}, [localValue, onChange]);
|
|
2190
|
-
const handleDoubleClick = (0,
|
|
2346
|
+
const handleDoubleClick = (0, import_react10.useCallback)(() => {
|
|
2191
2347
|
setLocalValue(0);
|
|
2192
2348
|
onChange(0);
|
|
2193
2349
|
}, [onChange]);
|
|
2194
|
-
return /* @__PURE__ */ (0,
|
|
2350
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2195
2351
|
"div",
|
|
2196
2352
|
{
|
|
2197
2353
|
className: `flex items-center ${className}`,
|
|
2198
2354
|
title: `Pan: ${toPanDisplay(localValue)}`,
|
|
2199
|
-
children: /* @__PURE__ */ (0,
|
|
2355
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2200
2356
|
"input",
|
|
2201
2357
|
{
|
|
2202
2358
|
type: "range",
|
|
@@ -2237,8 +2393,8 @@ var PanSlider = ({
|
|
|
2237
2393
|
};
|
|
2238
2394
|
|
|
2239
2395
|
// src/components/SorceryProgressBar.tsx
|
|
2240
|
-
var
|
|
2241
|
-
var
|
|
2396
|
+
var import_react11 = require("react");
|
|
2397
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2242
2398
|
function calculateTimeBasedTarget(elapsedMs, estimatedDurationMs) {
|
|
2243
2399
|
const t = elapsedMs / estimatedDurationMs;
|
|
2244
2400
|
if (t <= 0) return 0;
|
|
@@ -2283,20 +2439,20 @@ function SorceryProgressBar({
|
|
|
2283
2439
|
onProgressChange,
|
|
2284
2440
|
estimatedDurationMs
|
|
2285
2441
|
}) {
|
|
2286
|
-
const [progress, setProgress] = (0,
|
|
2287
|
-
const timerRef = (0,
|
|
2288
|
-
const isLoadingRef = (0,
|
|
2289
|
-
const hasStartedRef = (0,
|
|
2290
|
-
const startTimeRef = (0,
|
|
2291
|
-
const onProgressChangeRef = (0,
|
|
2292
|
-
const onCompleteRef = (0,
|
|
2442
|
+
const [progress, setProgress] = (0, import_react11.useState)(initialProgress);
|
|
2443
|
+
const timerRef = (0, import_react11.useRef)(null);
|
|
2444
|
+
const isLoadingRef = (0, import_react11.useRef)(false);
|
|
2445
|
+
const hasStartedRef = (0, import_react11.useRef)(false);
|
|
2446
|
+
const startTimeRef = (0, import_react11.useRef)(0);
|
|
2447
|
+
const onProgressChangeRef = (0, import_react11.useRef)(onProgressChange);
|
|
2448
|
+
const onCompleteRef = (0, import_react11.useRef)(onComplete);
|
|
2293
2449
|
onProgressChangeRef.current = onProgressChange;
|
|
2294
2450
|
onCompleteRef.current = onComplete;
|
|
2295
|
-
const initialProgressRef = (0,
|
|
2451
|
+
const initialProgressRef = (0, import_react11.useRef)(initialProgress);
|
|
2296
2452
|
initialProgressRef.current = initialProgress;
|
|
2297
|
-
const estimatedDurationMsRef = (0,
|
|
2453
|
+
const estimatedDurationMsRef = (0, import_react11.useRef)(estimatedDurationMs);
|
|
2298
2454
|
estimatedDurationMsRef.current = estimatedDurationMs;
|
|
2299
|
-
(0,
|
|
2455
|
+
(0, import_react11.useEffect)(() => {
|
|
2300
2456
|
const wasLoading = isLoadingRef.current;
|
|
2301
2457
|
isLoadingRef.current = isLoading;
|
|
2302
2458
|
if (isLoading && !wasLoading) {
|
|
@@ -2358,12 +2514,12 @@ function SorceryProgressBar({
|
|
|
2358
2514
|
const displayProgress = Math.floor(progress);
|
|
2359
2515
|
const isComplete = !isLoading && progress === 100;
|
|
2360
2516
|
const transitionDuration = progress < 50 ? "300ms" : progress < 80 ? "500ms" : "700ms";
|
|
2361
|
-
return /* @__PURE__ */ (0,
|
|
2517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2362
2518
|
"div",
|
|
2363
2519
|
{
|
|
2364
2520
|
className: `relative w-full ${heightClass} bg-sas-panel-alt border border-sas-border rounded-sm overflow-hidden shadow-inner`,
|
|
2365
2521
|
children: [
|
|
2366
|
-
/* @__PURE__ */ (0,
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2367
2523
|
"div",
|
|
2368
2524
|
{
|
|
2369
2525
|
className: `
|
|
@@ -2381,13 +2537,13 @@ function SorceryProgressBar({
|
|
|
2381
2537
|
}
|
|
2382
2538
|
}
|
|
2383
2539
|
),
|
|
2384
|
-
/* @__PURE__ */ (0,
|
|
2540
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "absolute inset-0 flex items-center justify-center", children: isLoading && progress < 100 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "font-mono text-xs text-sas-accent font-bold drop-shadow-md tracking-wider", children: [
|
|
2385
2541
|
statusText,
|
|
2386
2542
|
" ",
|
|
2387
2543
|
displayProgress,
|
|
2388
2544
|
"%"
|
|
2389
|
-
] }) : isComplete ? /* @__PURE__ */ (0,
|
|
2390
|
-
/* @__PURE__ */ (0,
|
|
2545
|
+
] }) : isComplete ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "font-mono text-xs text-sas-text font-bold drop-shadow-md tracking-wider", children: completeText }) : null }),
|
|
2546
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2391
2547
|
"div",
|
|
2392
2548
|
{
|
|
2393
2549
|
className: "absolute inset-0 pointer-events-none opacity-10",
|
|
@@ -2473,7 +2629,7 @@ function parseLLMNoteResponse(content) {
|
|
|
2473
2629
|
}
|
|
2474
2630
|
|
|
2475
2631
|
// src/components/TrackRow.tsx
|
|
2476
|
-
var
|
|
2632
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2477
2633
|
function TrackRow({
|
|
2478
2634
|
track,
|
|
2479
2635
|
prompt,
|
|
@@ -2533,7 +2689,9 @@ function TrackRow({
|
|
|
2533
2689
|
levels
|
|
2534
2690
|
}) {
|
|
2535
2691
|
const { muted: isMuted, solo: isSoloed, volume: currentVolume, pan: currentPan } = runtimeState;
|
|
2536
|
-
const [confirmDelete, setConfirmDelete] =
|
|
2692
|
+
const [confirmDelete, setConfirmDelete] = import_react12.default.useState(false);
|
|
2693
|
+
const freeze = useTrackFreeze(externalFxHost, track.id);
|
|
2694
|
+
const freezeBadge = freeze.state?.frozen === true ? freeze.state.missingDeps.length > 0 ? "missing" : freeze.state.stale ? "stale" : "frozen" : null;
|
|
2537
2695
|
const needsGeneration = !!(prompt?.trim() && !hasMidi && !isGenerating);
|
|
2538
2696
|
const hasFxActive = Object.values(fxDetailState).some(
|
|
2539
2697
|
(d) => d.enabled
|
|
@@ -2543,8 +2701,8 @@ function TrackRow({
|
|
|
2543
2701
|
const handleKeyDown = promptEnterToGenerate(() => onGenerate?.(), !onGenerate);
|
|
2544
2702
|
const borderColorStyle = needsGeneration ? void 0 : accentColor;
|
|
2545
2703
|
const borderClass = needsGeneration ? "border-amber-400 animate-pulse" : "border-sas-border";
|
|
2546
|
-
return /* @__PURE__ */ (0,
|
|
2547
|
-
/* @__PURE__ */ (0,
|
|
2704
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { "data-testid": "sdk-track-row-wrapper", className: "w-full", ...drag?.rowProps ?? {}, children: [
|
|
2705
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2548
2706
|
"div",
|
|
2549
2707
|
{
|
|
2550
2708
|
"data-testid": "sdk-track-row",
|
|
@@ -2554,7 +2712,7 @@ function TrackRow({
|
|
|
2554
2712
|
borderLeftWidth: "3px"
|
|
2555
2713
|
},
|
|
2556
2714
|
children: [
|
|
2557
|
-
drag && /* @__PURE__ */ (0,
|
|
2715
|
+
drag && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2558
2716
|
"div",
|
|
2559
2717
|
{
|
|
2560
2718
|
"data-testid": "sdk-drag-handle",
|
|
@@ -2562,10 +2720,10 @@ function TrackRow({
|
|
|
2562
2720
|
className: "flex-shrink-0 self-stretch flex items-center -ml-0.5 pr-0.5 text-sas-muted/40 hover:text-sas-muted cursor-grab active:cursor-grabbing relative z-30",
|
|
2563
2721
|
title: "Drag to reorder",
|
|
2564
2722
|
"aria-label": "Drag to reorder track",
|
|
2565
|
-
children: /* @__PURE__ */ (0,
|
|
2723
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react.GripVertical, { className: "w-3.5 h-3.5", strokeWidth: 2 })
|
|
2566
2724
|
}
|
|
2567
2725
|
),
|
|
2568
|
-
isGenerating && /* @__PURE__ */ (0,
|
|
2726
|
+
isGenerating && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute left-0 top-0 bottom-0 right-44 z-20", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2569
2727
|
SorceryProgressBar,
|
|
2570
2728
|
{
|
|
2571
2729
|
isLoading: true,
|
|
@@ -2576,14 +2734,14 @@ function TrackRow({
|
|
|
2576
2734
|
estimatedDurationMs: estimatedGenerationMs
|
|
2577
2735
|
}
|
|
2578
2736
|
) }),
|
|
2579
|
-
/* @__PURE__ */ (0,
|
|
2737
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2580
2738
|
"div",
|
|
2581
2739
|
{
|
|
2582
2740
|
"data-testid": "sdk-track-content",
|
|
2583
2741
|
className: `flex flex-col flex-1 min-w-0 relative z-10 transition-opacity ${soloedOut ? "opacity-40" : ""}`,
|
|
2584
2742
|
title: soloedOut ? "Silenced \u2014 another track is soloed" : void 0,
|
|
2585
2743
|
children: [
|
|
2586
|
-
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ (0,
|
|
2744
|
+
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2587
2745
|
"input",
|
|
2588
2746
|
{
|
|
2589
2747
|
type: "text",
|
|
@@ -2596,10 +2754,10 @@ function TrackRow({
|
|
|
2596
2754
|
className: "sas-input w-full px-2 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed"
|
|
2597
2755
|
}
|
|
2598
2756
|
) : null,
|
|
2599
|
-
/* @__PURE__ */ (0,
|
|
2600
|
-
track.name && /* @__PURE__ */ (0,
|
|
2601
|
-
/* @__PURE__ */ (0,
|
|
2602
|
-
/* @__PURE__ */ (0,
|
|
2757
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 mt-1", children: [
|
|
2758
|
+
track.name && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] text-sas-muted/60 truncate pl-2 flex-shrink-0 max-w-[80px]", title: track.name, children: track.name }),
|
|
2759
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "vol:" }),
|
|
2760
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2603
2761
|
VolumeSlider,
|
|
2604
2762
|
{
|
|
2605
2763
|
value: currentVolume,
|
|
@@ -2608,8 +2766,8 @@ function TrackRow({
|
|
|
2608
2766
|
className: "flex-1 min-w-[40px]"
|
|
2609
2767
|
}
|
|
2610
2768
|
),
|
|
2611
|
-
/* @__PURE__ */ (0,
|
|
2612
|
-
/* @__PURE__ */ (0,
|
|
2769
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "pan:" }),
|
|
2770
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2613
2771
|
PanSlider,
|
|
2614
2772
|
{
|
|
2615
2773
|
value: currentPan,
|
|
@@ -2622,27 +2780,27 @@ function TrackRow({
|
|
|
2622
2780
|
]
|
|
2623
2781
|
}
|
|
2624
2782
|
),
|
|
2625
|
-
error && /* @__PURE__ */ (0,
|
|
2783
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2626
2784
|
"div",
|
|
2627
2785
|
{
|
|
2628
2786
|
"data-testid": "sdk-error-indicator",
|
|
2629
2787
|
className: "flex-shrink-0 relative z-10 self-stretch flex items-center px-1 group cursor-help",
|
|
2630
2788
|
title: error,
|
|
2631
|
-
children: /* @__PURE__ */ (0,
|
|
2632
|
-
/* @__PURE__ */ (0,
|
|
2789
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "relative", children: [
|
|
2790
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2633
2791
|
import_lucide_react.AlertCircle,
|
|
2634
2792
|
{
|
|
2635
2793
|
className: "w-5 h-5 text-red-500 animate-pulse",
|
|
2636
2794
|
strokeWidth: 2.5
|
|
2637
2795
|
}
|
|
2638
2796
|
),
|
|
2639
|
-
/* @__PURE__ */ (0,
|
|
2797
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-red-900/95 text-red-100 text-xs rounded shadow-lg whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50 max-w-[200px] truncate", children: error })
|
|
2640
2798
|
] })
|
|
2641
2799
|
}
|
|
2642
2800
|
),
|
|
2643
|
-
/* @__PURE__ */ (0,
|
|
2644
|
-
/* @__PURE__ */ (0,
|
|
2645
|
-
onGenerate && /* @__PURE__ */ (0,
|
|
2801
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col gap-0.5 flex-shrink-0 relative z-30 justify-center", children: [
|
|
2802
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex gap-1 items-center", children: [
|
|
2803
|
+
onGenerate && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2646
2804
|
"button",
|
|
2647
2805
|
{
|
|
2648
2806
|
"data-testid": "sdk-generate-button",
|
|
@@ -2653,7 +2811,7 @@ function TrackRow({
|
|
|
2653
2811
|
children: "Create"
|
|
2654
2812
|
}
|
|
2655
2813
|
),
|
|
2656
|
-
onCopy && /* @__PURE__ */ (0,
|
|
2814
|
+
onCopy && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2657
2815
|
"button",
|
|
2658
2816
|
{
|
|
2659
2817
|
"data-testid": "sdk-copy-button",
|
|
@@ -2664,7 +2822,7 @@ function TrackRow({
|
|
|
2664
2822
|
children: "Copy"
|
|
2665
2823
|
}
|
|
2666
2824
|
),
|
|
2667
|
-
/* @__PURE__ */ (0,
|
|
2825
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2668
2826
|
"button",
|
|
2669
2827
|
{
|
|
2670
2828
|
"data-testid": "sdk-mute-button",
|
|
@@ -2674,7 +2832,7 @@ function TrackRow({
|
|
|
2674
2832
|
children: "M"
|
|
2675
2833
|
}
|
|
2676
2834
|
),
|
|
2677
|
-
onDelete && /* @__PURE__ */ (0,
|
|
2835
|
+
onDelete && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2678
2836
|
"button",
|
|
2679
2837
|
{
|
|
2680
2838
|
"data-testid": "sdk-delete-button",
|
|
@@ -2685,8 +2843,8 @@ function TrackRow({
|
|
|
2685
2843
|
}
|
|
2686
2844
|
)
|
|
2687
2845
|
] }),
|
|
2688
|
-
/* @__PURE__ */ (0,
|
|
2689
|
-
onShuffle && /* @__PURE__ */ (0,
|
|
2846
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex gap-1 items-center", children: [
|
|
2847
|
+
onShuffle && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2690
2848
|
"button",
|
|
2691
2849
|
{
|
|
2692
2850
|
"data-testid": "sdk-shuffle-button",
|
|
@@ -2697,7 +2855,16 @@ function TrackRow({
|
|
|
2697
2855
|
children: "Shuffle"
|
|
2698
2856
|
}
|
|
2699
2857
|
),
|
|
2700
|
-
|
|
2858
|
+
freezeBadge && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2859
|
+
"span",
|
|
2860
|
+
{
|
|
2861
|
+
"data-testid": "sdk-track-freeze-badge",
|
|
2862
|
+
className: `px-1 py-0.5 text-xs leading-none rounded-sm self-center ${freezeBadge === "frozen" ? "text-sas-accent" : "text-amber-400"}`,
|
|
2863
|
+
title: freezeBadge === "frozen" ? "Frozen \u2014 playing the rendered stem (mixer stays live)" : freezeBadge === "stale" ? "Frozen (stale) \u2014 sound edited since the stem rendered; re-freeze in the drawer" : `Frozen \u2014 plugin(s) missing on this machine: ${freeze.state?.missingDeps.map((d) => d.name).join(", ")}`,
|
|
2864
|
+
children: freezeBadge === "frozen" ? "\u2744" : freezeBadge === "stale" ? "\u26A0\u2744" : "\u2744!"
|
|
2865
|
+
}
|
|
2866
|
+
),
|
|
2867
|
+
onToggleFxDrawer && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2701
2868
|
"button",
|
|
2702
2869
|
{
|
|
2703
2870
|
"data-testid": "sdk-fx-button",
|
|
@@ -2708,7 +2875,7 @@ function TrackRow({
|
|
|
2708
2875
|
children: "FX"
|
|
2709
2876
|
}
|
|
2710
2877
|
),
|
|
2711
|
-
/* @__PURE__ */ (0,
|
|
2878
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2712
2879
|
"button",
|
|
2713
2880
|
{
|
|
2714
2881
|
"data-testid": "sdk-solo-button",
|
|
@@ -2719,7 +2886,7 @@ function TrackRow({
|
|
|
2719
2886
|
children: "S"
|
|
2720
2887
|
}
|
|
2721
2888
|
),
|
|
2722
|
-
onToggleDrawer && /* @__PURE__ */ (0,
|
|
2889
|
+
onToggleDrawer && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2723
2890
|
"button",
|
|
2724
2891
|
{
|
|
2725
2892
|
"data-testid": "sdk-plugin-button",
|
|
@@ -2727,7 +2894,7 @@ function TrackRow({
|
|
|
2727
2894
|
disabled: isGenerating,
|
|
2728
2895
|
className: `px-1.5 py-0.5 text-xs font-bold rounded transition-colors ${isGenerating ? "bg-sas-panel text-sas-muted/50 cursor-not-allowed" : soundTabOpen ? "bg-sas-accent border-sas-accent text-sas-bg" : instrumentMissing ? "bg-amber-500/20 text-amber-400 hover:bg-amber-500/40" : "bg-sas-panel-alt text-sas-muted hover:bg-sas-border"}`,
|
|
2729
2896
|
title: `Sound \u2014 presets & history${instrumentMissing ? " (instrument missing)" : ""}`,
|
|
2730
|
-
children: /* @__PURE__ */ (0,
|
|
2897
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react.ChevronDown, { className: "w-3 h-3", strokeWidth: 2.5 })
|
|
2731
2898
|
}
|
|
2732
2899
|
)
|
|
2733
2900
|
] })
|
|
@@ -2735,13 +2902,13 @@ function TrackRow({
|
|
|
2735
2902
|
]
|
|
2736
2903
|
}
|
|
2737
2904
|
),
|
|
2738
|
-
levels && /* @__PURE__ */ (0,
|
|
2739
|
-
drawerOpen && /* @__PURE__ */ (0,
|
|
2905
|
+
levels && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TrackMeterStrip, { levels, trackId: track.id, roundBottom: !drawerOpen }),
|
|
2906
|
+
drawerOpen && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2740
2907
|
"div",
|
|
2741
2908
|
{
|
|
2742
2909
|
"data-testid": "sdk-track-drawer",
|
|
2743
2910
|
className: "border border-t-0 border-sas-border bg-sas-bg rounded-b-sm px-3 py-2 max-h-[260px] overflow-y-auto",
|
|
2744
|
-
children: /* @__PURE__ */ (0,
|
|
2911
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2745
2912
|
TrackDrawer,
|
|
2746
2913
|
{
|
|
2747
2914
|
activeTab: drawerTab,
|
|
@@ -2752,6 +2919,7 @@ function TrackRow({
|
|
|
2752
2919
|
onFxPresetChange,
|
|
2753
2920
|
onFxDryWetChange,
|
|
2754
2921
|
externalFxHost,
|
|
2922
|
+
freeze,
|
|
2755
2923
|
fxDisabled: isGenerating,
|
|
2756
2924
|
instruments: availableInstruments,
|
|
2757
2925
|
currentPluginId: currentInstrumentPluginId ?? null,
|
|
@@ -2778,13 +2946,13 @@ function TrackRow({
|
|
|
2778
2946
|
)
|
|
2779
2947
|
}
|
|
2780
2948
|
),
|
|
2781
|
-
/* @__PURE__ */ (0,
|
|
2949
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2782
2950
|
ConfirmDialog,
|
|
2783
2951
|
{
|
|
2784
2952
|
open: confirmDelete,
|
|
2785
2953
|
title: "Delete track?",
|
|
2786
|
-
message: /* @__PURE__ */ (0,
|
|
2787
|
-
/* @__PURE__ */ (0,
|
|
2954
|
+
message: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2955
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sas-text", children: track.name?.trim() || "This track" }),
|
|
2788
2956
|
" will be permanently removed from this scene. This cannot be undone."
|
|
2789
2957
|
] }),
|
|
2790
2958
|
confirmLabel: "Delete",
|
|
@@ -2800,13 +2968,13 @@ function TrackRow({
|
|
|
2800
2968
|
}
|
|
2801
2969
|
|
|
2802
2970
|
// src/components/CrossfadeTrackRow.tsx
|
|
2803
|
-
var
|
|
2804
|
-
var
|
|
2971
|
+
var import_react13 = __toESM(require("react"));
|
|
2972
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2805
2973
|
function LayerCaption({ tag, layer }) {
|
|
2806
|
-
return /* @__PURE__ */ (0,
|
|
2807
|
-
/* @__PURE__ */ (0,
|
|
2808
|
-
/* @__PURE__ */ (0,
|
|
2809
|
-
layer.soundLabel && /* @__PURE__ */ (0,
|
|
2974
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
2975
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
|
|
2976
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
|
|
2977
|
+
layer.soundLabel && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
|
|
2810
2978
|
"\xB7 ",
|
|
2811
2979
|
layer.soundLabel
|
|
2812
2980
|
] })
|
|
@@ -2825,8 +2993,8 @@ function CrossfadeTrackRow({
|
|
|
2825
2993
|
levels,
|
|
2826
2994
|
accentColor = "#9333EA"
|
|
2827
2995
|
}) {
|
|
2828
|
-
const [confirmDelete, setConfirmDelete] =
|
|
2829
|
-
const renderLayer = (layer, slot, tag) => /* @__PURE__ */ (0,
|
|
2996
|
+
const [confirmDelete, setConfirmDelete] = import_react13.default.useState(false);
|
|
2997
|
+
const renderLayer = (layer, slot, tag) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2830
2998
|
TrackRow,
|
|
2831
2999
|
{
|
|
2832
3000
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -2836,23 +3004,23 @@ function CrossfadeTrackRow({
|
|
|
2836
3004
|
drawerTab: "fx",
|
|
2837
3005
|
levels,
|
|
2838
3006
|
accentColor,
|
|
2839
|
-
contentSlot: /* @__PURE__ */ (0,
|
|
3007
|
+
contentSlot: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LayerCaption, { tag, layer }),
|
|
2840
3008
|
onMuteToggle,
|
|
2841
3009
|
onSoloToggle,
|
|
2842
3010
|
onVolumeChange: (v) => onVolumeChange(slot, v),
|
|
2843
3011
|
onPanChange: (p) => onPanChange(slot, p)
|
|
2844
3012
|
}
|
|
2845
3013
|
);
|
|
2846
|
-
return /* @__PURE__ */ (0,
|
|
3014
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2847
3015
|
"div",
|
|
2848
3016
|
{
|
|
2849
3017
|
"data-testid": "crossfade-track-row",
|
|
2850
3018
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
2851
3019
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
2852
3020
|
children: [
|
|
2853
|
-
/* @__PURE__ */ (0,
|
|
2854
|
-
/* @__PURE__ */ (0,
|
|
2855
|
-
/* @__PURE__ */ (0,
|
|
3021
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
|
|
3022
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-wide", style: { color: accentColor }, children: "\u21C4 Crossfade" }),
|
|
3023
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2856
3024
|
"button",
|
|
2857
3025
|
{
|
|
2858
3026
|
"data-testid": "crossfade-delete-button",
|
|
@@ -2865,8 +3033,8 @@ function CrossfadeTrackRow({
|
|
|
2865
3033
|
)
|
|
2866
3034
|
] }),
|
|
2867
3035
|
renderLayer(origin, "origin", "Origin"),
|
|
2868
|
-
/* @__PURE__ */ (0,
|
|
2869
|
-
/* @__PURE__ */ (0,
|
|
3036
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "crossfade-slider-row", children: [
|
|
3037
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2870
3038
|
"span",
|
|
2871
3039
|
{
|
|
2872
3040
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -2874,7 +3042,7 @@ function CrossfadeTrackRow({
|
|
|
2874
3042
|
children: origin.sourceName ?? origin.name
|
|
2875
3043
|
}
|
|
2876
3044
|
),
|
|
2877
|
-
/* @__PURE__ */ (0,
|
|
3045
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2878
3046
|
"input",
|
|
2879
3047
|
{
|
|
2880
3048
|
type: "range",
|
|
@@ -2890,7 +3058,7 @@ function CrossfadeTrackRow({
|
|
|
2890
3058
|
"aria-label": "Crossfade position"
|
|
2891
3059
|
}
|
|
2892
3060
|
),
|
|
2893
|
-
/* @__PURE__ */ (0,
|
|
3061
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2894
3062
|
"span",
|
|
2895
3063
|
{
|
|
2896
3064
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -2900,12 +3068,12 @@ function CrossfadeTrackRow({
|
|
|
2900
3068
|
)
|
|
2901
3069
|
] }),
|
|
2902
3070
|
renderLayer(target, "target", "Target"),
|
|
2903
|
-
/* @__PURE__ */ (0,
|
|
3071
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2904
3072
|
ConfirmDialog,
|
|
2905
3073
|
{
|
|
2906
3074
|
open: confirmDelete,
|
|
2907
3075
|
title: "Delete crossfade?",
|
|
2908
|
-
message: /* @__PURE__ */ (0,
|
|
3076
|
+
message: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: "This crossfade pair (both layers) will be permanently removed from this scene. This cannot be undone." }),
|
|
2909
3077
|
confirmLabel: "Delete",
|
|
2910
3078
|
onConfirm: () => {
|
|
2911
3079
|
setConfirmDelete(false);
|
|
@@ -3178,22 +3346,22 @@ function defaultFadeGesture(role) {
|
|
|
3178
3346
|
}
|
|
3179
3347
|
|
|
3180
3348
|
// src/components/FadeTrackRow.tsx
|
|
3181
|
-
var
|
|
3182
|
-
var
|
|
3349
|
+
var import_react14 = __toESM(require("react"));
|
|
3350
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
3183
3351
|
function FadeCaption({
|
|
3184
3352
|
layer,
|
|
3185
3353
|
direction,
|
|
3186
3354
|
gesture
|
|
3187
3355
|
}) {
|
|
3188
3356
|
const tag = direction === "in" ? "Fade in" : "Fade out";
|
|
3189
|
-
return /* @__PURE__ */ (0,
|
|
3190
|
-
/* @__PURE__ */ (0,
|
|
3191
|
-
/* @__PURE__ */ (0,
|
|
3192
|
-
layer.soundLabel && /* @__PURE__ */ (0,
|
|
3357
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
3358
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
|
|
3359
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
|
|
3360
|
+
layer.soundLabel && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
|
|
3193
3361
|
"\xB7 ",
|
|
3194
3362
|
layer.soundLabel
|
|
3195
3363
|
] }),
|
|
3196
|
-
/* @__PURE__ */ (0,
|
|
3364
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", title: `Fade gesture: ${gesture}`, children: [
|
|
3197
3365
|
"\xB7 ",
|
|
3198
3366
|
gesture
|
|
3199
3367
|
] })
|
|
@@ -3214,20 +3382,20 @@ function FadeTrackRow({
|
|
|
3214
3382
|
levels,
|
|
3215
3383
|
accentColor = "#9333EA"
|
|
3216
3384
|
}) {
|
|
3217
|
-
const [confirmDelete, setConfirmDelete] =
|
|
3385
|
+
const [confirmDelete, setConfirmDelete] = import_react14.default.useState(false);
|
|
3218
3386
|
const leftLabel = direction === "in" ? "(silent)" : layer.sourceName ?? layer.name;
|
|
3219
3387
|
const rightLabel = direction === "in" ? layer.sourceName ?? layer.name : "(silent)";
|
|
3220
3388
|
const verb = effect && effect !== "fade" ? effect.charAt(0).toUpperCase() + effect.slice(1) : "Fade";
|
|
3221
3389
|
const badge = direction === "in" ? `\u2197 ${verb} in` : `\u2198 ${verb} out`;
|
|
3222
|
-
return /* @__PURE__ */ (0,
|
|
3390
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
3223
3391
|
"div",
|
|
3224
3392
|
{
|
|
3225
3393
|
"data-testid": "fade-track-row",
|
|
3226
3394
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
3227
3395
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
3228
3396
|
children: [
|
|
3229
|
-
/* @__PURE__ */ (0,
|
|
3230
|
-
/* @__PURE__ */ (0,
|
|
3397
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
|
|
3398
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3231
3399
|
"span",
|
|
3232
3400
|
{
|
|
3233
3401
|
"data-testid": "fade-direction-badge",
|
|
@@ -3236,7 +3404,7 @@ function FadeTrackRow({
|
|
|
3236
3404
|
children: badge
|
|
3237
3405
|
}
|
|
3238
3406
|
),
|
|
3239
|
-
/* @__PURE__ */ (0,
|
|
3407
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3240
3408
|
"button",
|
|
3241
3409
|
{
|
|
3242
3410
|
"data-testid": "fade-delete-button",
|
|
@@ -3248,7 +3416,7 @@ function FadeTrackRow({
|
|
|
3248
3416
|
}
|
|
3249
3417
|
)
|
|
3250
3418
|
] }),
|
|
3251
|
-
/* @__PURE__ */ (0,
|
|
3419
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3252
3420
|
TrackRow,
|
|
3253
3421
|
{
|
|
3254
3422
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -3258,15 +3426,15 @@ function FadeTrackRow({
|
|
|
3258
3426
|
drawerTab: "fx",
|
|
3259
3427
|
levels,
|
|
3260
3428
|
accentColor,
|
|
3261
|
-
contentSlot: /* @__PURE__ */ (0,
|
|
3429
|
+
contentSlot: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FadeCaption, { layer, direction, gesture }),
|
|
3262
3430
|
onMuteToggle,
|
|
3263
3431
|
onSoloToggle,
|
|
3264
3432
|
onVolumeChange,
|
|
3265
3433
|
onPanChange
|
|
3266
3434
|
}
|
|
3267
3435
|
),
|
|
3268
|
-
/* @__PURE__ */ (0,
|
|
3269
|
-
/* @__PURE__ */ (0,
|
|
3436
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "fade-slider-row", children: [
|
|
3437
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3270
3438
|
"span",
|
|
3271
3439
|
{
|
|
3272
3440
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -3274,7 +3442,7 @@ function FadeTrackRow({
|
|
|
3274
3442
|
children: leftLabel
|
|
3275
3443
|
}
|
|
3276
3444
|
),
|
|
3277
|
-
/* @__PURE__ */ (0,
|
|
3445
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3278
3446
|
"input",
|
|
3279
3447
|
{
|
|
3280
3448
|
type: "range",
|
|
@@ -3290,7 +3458,7 @@ function FadeTrackRow({
|
|
|
3290
3458
|
"aria-label": "Fade position"
|
|
3291
3459
|
}
|
|
3292
3460
|
),
|
|
3293
|
-
/* @__PURE__ */ (0,
|
|
3461
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3294
3462
|
"span",
|
|
3295
3463
|
{
|
|
3296
3464
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -3299,12 +3467,12 @@ function FadeTrackRow({
|
|
|
3299
3467
|
}
|
|
3300
3468
|
)
|
|
3301
3469
|
] }),
|
|
3302
|
-
/* @__PURE__ */ (0,
|
|
3470
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3303
3471
|
ConfirmDialog,
|
|
3304
3472
|
{
|
|
3305
3473
|
open: confirmDelete,
|
|
3306
3474
|
title: "Delete fade?",
|
|
3307
|
-
message: /* @__PURE__ */ (0,
|
|
3475
|
+
message: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: "This fade track will be permanently removed from this scene. This cannot be undone." }),
|
|
3308
3476
|
confirmLabel: "Delete",
|
|
3309
3477
|
onConfirm: () => {
|
|
3310
3478
|
setConfirmDelete(false);
|
|
@@ -3320,21 +3488,21 @@ function FadeTrackRow({
|
|
|
3320
3488
|
}
|
|
3321
3489
|
|
|
3322
3490
|
// src/components/GroupFadeTrackRow.tsx
|
|
3323
|
-
var
|
|
3324
|
-
var
|
|
3491
|
+
var import_react15 = __toESM(require("react"));
|
|
3492
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
3325
3493
|
function MemberCaption({
|
|
3326
3494
|
member,
|
|
3327
3495
|
direction
|
|
3328
3496
|
}) {
|
|
3329
3497
|
const tag = direction === "in" ? "Fade in" : "Fade out";
|
|
3330
|
-
return /* @__PURE__ */ (0,
|
|
3331
|
-
member.memberLabel && /* @__PURE__ */ (0,
|
|
3332
|
-
/* @__PURE__ */ (0,
|
|
3333
|
-
member.soundLabel && /* @__PURE__ */ (0,
|
|
3498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
3499
|
+
member.memberLabel && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: member.memberLabel }),
|
|
3500
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-[11px] text-sas-text truncate", title: member.sourceName ?? member.name, children: member.sourceName ?? member.name }),
|
|
3501
|
+
member.soundLabel && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: member.soundLabel, children: [
|
|
3334
3502
|
"\xB7 ",
|
|
3335
3503
|
member.soundLabel
|
|
3336
3504
|
] }),
|
|
3337
|
-
!member.memberLabel && /* @__PURE__ */ (0,
|
|
3505
|
+
!member.memberLabel && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: [
|
|
3338
3506
|
"\xB7 ",
|
|
3339
3507
|
tag
|
|
3340
3508
|
] })
|
|
@@ -3357,22 +3525,22 @@ function GroupFadeTrackRow({
|
|
|
3357
3525
|
levels,
|
|
3358
3526
|
accentColor = "#9333EA"
|
|
3359
3527
|
}) {
|
|
3360
|
-
const [confirmDelete, setConfirmDelete] =
|
|
3528
|
+
const [confirmDelete, setConfirmDelete] = import_react15.default.useState(false);
|
|
3361
3529
|
const allMuted = members.length > 0 && members.every((m) => m.runtimeState.muted);
|
|
3362
3530
|
const allSolo = members.length > 0 && members.every((m) => m.runtimeState.solo);
|
|
3363
3531
|
const badge = direction === "in" ? "\u2197 Fade in" : "\u2198 Fade out";
|
|
3364
3532
|
const leftLabel = direction === "in" ? "(silent)" : groupLabel;
|
|
3365
3533
|
const rightLabel = direction === "in" ? groupLabel : "(silent)";
|
|
3366
|
-
return /* @__PURE__ */ (0,
|
|
3534
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3367
3535
|
"div",
|
|
3368
3536
|
{
|
|
3369
3537
|
"data-testid": "group-fade-track-row",
|
|
3370
3538
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
3371
3539
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
3372
3540
|
children: [
|
|
3373
|
-
/* @__PURE__ */ (0,
|
|
3374
|
-
/* @__PURE__ */ (0,
|
|
3375
|
-
/* @__PURE__ */ (0,
|
|
3541
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60 gap-2", children: [
|
|
3542
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
3543
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3376
3544
|
"span",
|
|
3377
3545
|
{
|
|
3378
3546
|
"data-testid": "group-fade-direction-badge",
|
|
@@ -3381,7 +3549,7 @@ function GroupFadeTrackRow({
|
|
|
3381
3549
|
children: badge
|
|
3382
3550
|
}
|
|
3383
3551
|
),
|
|
3384
|
-
/* @__PURE__ */ (0,
|
|
3552
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3385
3553
|
"span",
|
|
3386
3554
|
{
|
|
3387
3555
|
"data-testid": "group-fade-label",
|
|
@@ -3391,8 +3559,8 @@ function GroupFadeTrackRow({
|
|
|
3391
3559
|
}
|
|
3392
3560
|
)
|
|
3393
3561
|
] }),
|
|
3394
|
-
/* @__PURE__ */ (0,
|
|
3395
|
-
/* @__PURE__ */ (0,
|
|
3562
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
|
|
3563
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3396
3564
|
"button",
|
|
3397
3565
|
{
|
|
3398
3566
|
"data-testid": "group-fade-mute-button",
|
|
@@ -3402,7 +3570,7 @@ function GroupFadeTrackRow({
|
|
|
3402
3570
|
children: "M"
|
|
3403
3571
|
}
|
|
3404
3572
|
),
|
|
3405
|
-
/* @__PURE__ */ (0,
|
|
3573
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3406
3574
|
"button",
|
|
3407
3575
|
{
|
|
3408
3576
|
"data-testid": "group-fade-solo-button",
|
|
@@ -3412,7 +3580,7 @@ function GroupFadeTrackRow({
|
|
|
3412
3580
|
children: "S"
|
|
3413
3581
|
}
|
|
3414
3582
|
),
|
|
3415
|
-
/* @__PURE__ */ (0,
|
|
3583
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3416
3584
|
"button",
|
|
3417
3585
|
{
|
|
3418
3586
|
"data-testid": "group-fade-delete-button",
|
|
@@ -3425,7 +3593,7 @@ function GroupFadeTrackRow({
|
|
|
3425
3593
|
)
|
|
3426
3594
|
] })
|
|
3427
3595
|
] }),
|
|
3428
|
-
members.map((member) => /* @__PURE__ */ (0,
|
|
3596
|
+
members.map((member) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3429
3597
|
TrackRow,
|
|
3430
3598
|
{
|
|
3431
3599
|
track: { id: member.trackId, name: "", role: member.role },
|
|
@@ -3435,7 +3603,7 @@ function GroupFadeTrackRow({
|
|
|
3435
3603
|
drawerTab: "fx",
|
|
3436
3604
|
levels,
|
|
3437
3605
|
accentColor,
|
|
3438
|
-
contentSlot: /* @__PURE__ */ (0,
|
|
3606
|
+
contentSlot: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MemberCaption, { member, direction }),
|
|
3439
3607
|
onMuteToggle: () => onMemberMuteToggle(member.trackId),
|
|
3440
3608
|
onSoloToggle: () => onMemberSoloToggle(member.trackId),
|
|
3441
3609
|
onVolumeChange: (vol) => onMemberVolumeChange(member.trackId, vol),
|
|
@@ -3443,8 +3611,8 @@ function GroupFadeTrackRow({
|
|
|
3443
3611
|
},
|
|
3444
3612
|
member.trackId
|
|
3445
3613
|
)),
|
|
3446
|
-
/* @__PURE__ */ (0,
|
|
3447
|
-
/* @__PURE__ */ (0,
|
|
3614
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "group-fade-slider-row", children: [
|
|
3615
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3448
3616
|
"span",
|
|
3449
3617
|
{
|
|
3450
3618
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -3452,7 +3620,7 @@ function GroupFadeTrackRow({
|
|
|
3452
3620
|
children: leftLabel
|
|
3453
3621
|
}
|
|
3454
3622
|
),
|
|
3455
|
-
/* @__PURE__ */ (0,
|
|
3623
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3456
3624
|
"input",
|
|
3457
3625
|
{
|
|
3458
3626
|
type: "range",
|
|
@@ -3468,7 +3636,7 @@ function GroupFadeTrackRow({
|
|
|
3468
3636
|
"aria-label": "Group fade position"
|
|
3469
3637
|
}
|
|
3470
3638
|
),
|
|
3471
|
-
/* @__PURE__ */ (0,
|
|
3639
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3472
3640
|
"span",
|
|
3473
3641
|
{
|
|
3474
3642
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -3477,12 +3645,12 @@ function GroupFadeTrackRow({
|
|
|
3477
3645
|
}
|
|
3478
3646
|
)
|
|
3479
3647
|
] }),
|
|
3480
|
-
/* @__PURE__ */ (0,
|
|
3648
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3481
3649
|
ConfirmDialog,
|
|
3482
3650
|
{
|
|
3483
3651
|
open: confirmDelete,
|
|
3484
3652
|
title: "Delete group fade?",
|
|
3485
|
-
message: /* @__PURE__ */ (0,
|
|
3653
|
+
message: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3486
3654
|
"All ",
|
|
3487
3655
|
members.length,
|
|
3488
3656
|
" member tracks of this fade will be permanently removed from this scene. This cannot be undone."
|
|
@@ -3502,8 +3670,8 @@ function GroupFadeTrackRow({
|
|
|
3502
3670
|
}
|
|
3503
3671
|
|
|
3504
3672
|
// src/components/FadeModal.tsx
|
|
3505
|
-
var
|
|
3506
|
-
var
|
|
3673
|
+
var import_react16 = require("react");
|
|
3674
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
3507
3675
|
function shortId(dbId) {
|
|
3508
3676
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
3509
3677
|
}
|
|
@@ -3546,7 +3714,7 @@ function OrphanRow({
|
|
|
3546
3714
|
}) {
|
|
3547
3715
|
const primary = track.prompt?.trim() || track.name;
|
|
3548
3716
|
const meta = [track.role, shortId(track.dbId), gesture].filter(Boolean).join(" \xB7 ");
|
|
3549
|
-
return /* @__PURE__ */ (0,
|
|
3717
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3550
3718
|
"button",
|
|
3551
3719
|
{
|
|
3552
3720
|
type: "button",
|
|
@@ -3558,8 +3726,8 @@ function OrphanRow({
|
|
|
3558
3726
|
disabled,
|
|
3559
3727
|
className: `w-full text-left px-2 py-1.5 rounded-sm border transition-colors disabled:opacity-50 ${selected ? "bg-sas-accent/15 border-sas-accent" : "bg-sas-panel border-sas-border hover:border-sas-accent/50"}`,
|
|
3560
3728
|
children: [
|
|
3561
|
-
/* @__PURE__ */ (0,
|
|
3562
|
-
meta && /* @__PURE__ */ (0,
|
|
3729
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
|
|
3730
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
|
|
3563
3731
|
]
|
|
3564
3732
|
}
|
|
3565
3733
|
);
|
|
@@ -3576,14 +3744,14 @@ function FadeModal({
|
|
|
3576
3744
|
onCreate,
|
|
3577
3745
|
testIdPrefix = "fade-modal"
|
|
3578
3746
|
}) {
|
|
3579
|
-
const [load, setLoad] = (0,
|
|
3580
|
-
const [selectedDbId, setSelectedDbId] = (0,
|
|
3581
|
-
const [isCreating, setIsCreating] = (0,
|
|
3582
|
-
const [error, setError] = (0,
|
|
3583
|
-
const [fromName, setFromName] = (0,
|
|
3584
|
-
const [toName, setToName] = (0,
|
|
3585
|
-
const cancelRef = (0,
|
|
3586
|
-
const refresh = (0,
|
|
3747
|
+
const [load, setLoad] = (0, import_react16.useState)({ status: "loading" });
|
|
3748
|
+
const [selectedDbId, setSelectedDbId] = (0, import_react16.useState)("");
|
|
3749
|
+
const [isCreating, setIsCreating] = (0, import_react16.useState)(false);
|
|
3750
|
+
const [error, setError] = (0, import_react16.useState)(null);
|
|
3751
|
+
const [fromName, setFromName] = (0, import_react16.useState)(null);
|
|
3752
|
+
const [toName, setToName] = (0, import_react16.useState)(null);
|
|
3753
|
+
const cancelRef = (0, import_react16.useRef)(null);
|
|
3754
|
+
const refresh = (0, import_react16.useCallback)(async () => {
|
|
3587
3755
|
if (!host.listSceneFamilyTracks) {
|
|
3588
3756
|
setLoad({ status: "error", message: "This host does not support fades." });
|
|
3589
3757
|
return;
|
|
@@ -3603,7 +3771,7 @@ function FadeModal({
|
|
|
3603
3771
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
3604
3772
|
}
|
|
3605
3773
|
}, [host, fromSceneId, toSceneId]);
|
|
3606
|
-
(0,
|
|
3774
|
+
(0, import_react16.useEffect)(() => {
|
|
3607
3775
|
if (open) {
|
|
3608
3776
|
setError(null);
|
|
3609
3777
|
setIsCreating(false);
|
|
@@ -3611,29 +3779,29 @@ function FadeModal({
|
|
|
3611
3779
|
void refresh();
|
|
3612
3780
|
}
|
|
3613
3781
|
}, [open, refresh]);
|
|
3614
|
-
const excludeSet = (0,
|
|
3615
|
-
const { fadeOut, fadeIn } = (0,
|
|
3782
|
+
const excludeSet = (0, import_react16.useMemo)(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
3783
|
+
const { fadeOut, fadeIn } = (0, import_react16.useMemo)(
|
|
3616
3784
|
() => load.status === "ready" ? computeOrphans(load.from, load.to, excludeSet) : { fadeOut: [], fadeIn: [] },
|
|
3617
3785
|
[load, excludeSet]
|
|
3618
3786
|
);
|
|
3619
|
-
const allOrphans = (0,
|
|
3787
|
+
const allOrphans = (0, import_react16.useMemo)(
|
|
3620
3788
|
() => [
|
|
3621
3789
|
...fadeOut.map((t) => ({ track: t, direction: "out" })),
|
|
3622
3790
|
...fadeIn.map((t) => ({ track: t, direction: "in" }))
|
|
3623
3791
|
],
|
|
3624
3792
|
[fadeOut, fadeIn]
|
|
3625
3793
|
);
|
|
3626
|
-
(0,
|
|
3794
|
+
(0, import_react16.useEffect)(() => {
|
|
3627
3795
|
if (!allOrphans.some((o) => o.track.dbId === selectedDbId)) {
|
|
3628
3796
|
setSelectedDbId(allOrphans[0]?.track.dbId ?? "");
|
|
3629
3797
|
}
|
|
3630
3798
|
}, [allOrphans, selectedDbId]);
|
|
3631
3799
|
const selected = allOrphans.find((o) => o.track.dbId === selectedDbId) ?? null;
|
|
3632
3800
|
const canCreate = !isCreating && !!selected;
|
|
3633
|
-
const handleClose = (0,
|
|
3801
|
+
const handleClose = (0, import_react16.useCallback)(() => {
|
|
3634
3802
|
if (!isCreating) onClose();
|
|
3635
3803
|
}, [isCreating, onClose]);
|
|
3636
|
-
const handleCreate = (0,
|
|
3804
|
+
const handleCreate = (0, import_react16.useCallback)(async () => {
|
|
3637
3805
|
if (!selected) return;
|
|
3638
3806
|
setIsCreating(true);
|
|
3639
3807
|
setError(null);
|
|
@@ -3654,16 +3822,16 @@ function FadeModal({
|
|
|
3654
3822
|
if (!open) return null;
|
|
3655
3823
|
const renderSection = (heading, list, section) => {
|
|
3656
3824
|
if (list.length === 0) return null;
|
|
3657
|
-
return /* @__PURE__ */ (0,
|
|
3658
|
-
/* @__PURE__ */ (0,
|
|
3659
|
-
/* @__PURE__ */ (0,
|
|
3825
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "block", children: [
|
|
3826
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: heading }),
|
|
3827
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3660
3828
|
"div",
|
|
3661
3829
|
{
|
|
3662
3830
|
role: "radiogroup",
|
|
3663
3831
|
"aria-label": heading,
|
|
3664
3832
|
"data-testid": `${testIdPrefix}-${section === "out" ? "fade-out" : "fade-in"}-list`,
|
|
3665
3833
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3666
|
-
children: list.map((t) => /* @__PURE__ */ (0,
|
|
3834
|
+
children: list.map((t) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3667
3835
|
OrphanRow,
|
|
3668
3836
|
{
|
|
3669
3837
|
track: t,
|
|
@@ -3679,32 +3847,32 @@ function FadeModal({
|
|
|
3679
3847
|
)
|
|
3680
3848
|
] });
|
|
3681
3849
|
};
|
|
3682
|
-
return /* @__PURE__ */ (0,
|
|
3850
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3683
3851
|
"div",
|
|
3684
3852
|
{
|
|
3685
3853
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
3686
3854
|
onClick: (e) => e.stopPropagation(),
|
|
3687
3855
|
"data-testid": `${testIdPrefix}-box`,
|
|
3688
3856
|
children: [
|
|
3689
|
-
/* @__PURE__ */ (0,
|
|
3690
|
-
/* @__PURE__ */ (0,
|
|
3857
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h3", { className: "text-sm font-bold text-sas-text", children: "Add fade" }),
|
|
3858
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
|
|
3691
3859
|
"Tracks with no counterpart between",
|
|
3692
3860
|
" ",
|
|
3693
|
-
/* @__PURE__ */ (0,
|
|
3861
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
3694
3862
|
" and",
|
|
3695
3863
|
" ",
|
|
3696
|
-
/* @__PURE__ */ (0,
|
|
3864
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
3697
3865
|
" can gracefully fade out (leaving) or fade in (entering) across this transition."
|
|
3698
3866
|
] }),
|
|
3699
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
3700
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
3701
|
-
load.status === "ready" && (allOrphans.length === 0 ? /* @__PURE__ */ (0,
|
|
3867
|
+
load.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
|
|
3868
|
+
load.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
|
|
3869
|
+
load.status === "ready" && (allOrphans.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-sas-muted py-4 text-center", "data-testid": `${testIdPrefix}-empty`, children: "Every track has a counterpart in the other scene \u2014 nothing to fade. Use \u201C+ Crossfade\u201D to bridge matching tracks." }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
3702
3870
|
renderSection(`Fade out${fromLabel ? ` (from ${fromLabel})` : ""}`, fadeOut, "out"),
|
|
3703
3871
|
renderSection(`Fade in${toLabel ? ` (to ${toLabel})` : ""}`, fadeIn, "in")
|
|
3704
3872
|
] })),
|
|
3705
|
-
error && /* @__PURE__ */ (0,
|
|
3706
|
-
/* @__PURE__ */ (0,
|
|
3707
|
-
/* @__PURE__ */ (0,
|
|
3873
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
|
|
3874
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
3875
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3708
3876
|
"button",
|
|
3709
3877
|
{
|
|
3710
3878
|
ref: cancelRef,
|
|
@@ -3715,7 +3883,7 @@ function FadeModal({
|
|
|
3715
3883
|
children: "Cancel"
|
|
3716
3884
|
}
|
|
3717
3885
|
),
|
|
3718
|
-
/* @__PURE__ */ (0,
|
|
3886
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3719
3887
|
"button",
|
|
3720
3888
|
{
|
|
3721
3889
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -3732,8 +3900,8 @@ function FadeModal({
|
|
|
3732
3900
|
}
|
|
3733
3901
|
|
|
3734
3902
|
// src/components/ImportTrackModal.tsx
|
|
3735
|
-
var
|
|
3736
|
-
var
|
|
3903
|
+
var import_react17 = require("react");
|
|
3904
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
3737
3905
|
function ImportTrackModal({
|
|
3738
3906
|
host,
|
|
3739
3907
|
open,
|
|
@@ -3745,10 +3913,10 @@ function ImportTrackModal({
|
|
|
3745
3913
|
onPick,
|
|
3746
3914
|
onPortTrack
|
|
3747
3915
|
}) {
|
|
3748
|
-
const [load, setLoad] = (0,
|
|
3749
|
-
const [selectedSceneId, setSelectedSceneId] = (0,
|
|
3750
|
-
const [importingTrackId, setImportingTrackId] = (0,
|
|
3751
|
-
const refresh = (0,
|
|
3916
|
+
const [load, setLoad] = (0, import_react17.useState)({ status: "loading" });
|
|
3917
|
+
const [selectedSceneId, setSelectedSceneId] = (0, import_react17.useState)(null);
|
|
3918
|
+
const [importingTrackId, setImportingTrackId] = (0, import_react17.useState)(null);
|
|
3919
|
+
const refresh = (0, import_react17.useCallback)(async () => {
|
|
3752
3920
|
if (!host.listImportableTracks) {
|
|
3753
3921
|
setLoad({ status: "error", message: "This host does not support importing tracks." });
|
|
3754
3922
|
return;
|
|
@@ -3764,14 +3932,14 @@ function ImportTrackModal({
|
|
|
3764
3932
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load scenes." });
|
|
3765
3933
|
}
|
|
3766
3934
|
}, [host, mode, onPortTrack]);
|
|
3767
|
-
(0,
|
|
3935
|
+
(0, import_react17.useEffect)(() => {
|
|
3768
3936
|
if (open) {
|
|
3769
3937
|
setSelectedSceneId(null);
|
|
3770
3938
|
setImportingTrackId(null);
|
|
3771
3939
|
void refresh();
|
|
3772
3940
|
}
|
|
3773
3941
|
}, [open, refresh]);
|
|
3774
|
-
const handleImport = (0,
|
|
3942
|
+
const handleImport = (0, import_react17.useCallback)(
|
|
3775
3943
|
async (track, sourceSceneId, sceneName, isSameScene) => {
|
|
3776
3944
|
if (isSameScene && onPortTrack) {
|
|
3777
3945
|
if (!track.importable) return;
|
|
@@ -3812,16 +3980,16 @@ function ImportTrackModal({
|
|
|
3812
3980
|
if (!open) return null;
|
|
3813
3981
|
const scenes = load.status === "ready" ? load.scenes : [];
|
|
3814
3982
|
const selectedScene = scenes.find((s) => s.sceneId === selectedSceneId) ?? null;
|
|
3815
|
-
return /* @__PURE__ */ (0,
|
|
3983
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Modal, { open, onClose, testIdPrefix, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3816
3984
|
"div",
|
|
3817
3985
|
{
|
|
3818
3986
|
className: "w-[420px] max-h-[70vh] overflow-hidden flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
3819
3987
|
onClick: (e) => e.stopPropagation(),
|
|
3820
3988
|
"data-testid": `${testIdPrefix}-modal`,
|
|
3821
3989
|
children: [
|
|
3822
|
-
/* @__PURE__ */ (0,
|
|
3823
|
-
/* @__PURE__ */ (0,
|
|
3824
|
-
selectedScene && /* @__PURE__ */ (0,
|
|
3990
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center justify-between px-3 py-2 border-b border-sas-border", children: [
|
|
3991
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3992
|
+
selectedScene && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3825
3993
|
"button",
|
|
3826
3994
|
{
|
|
3827
3995
|
className: "text-sas-muted hover:text-sas-accent text-xs",
|
|
@@ -3830,9 +3998,9 @@ function ImportTrackModal({
|
|
|
3830
3998
|
children: "\u2190"
|
|
3831
3999
|
}
|
|
3832
4000
|
),
|
|
3833
|
-
/* @__PURE__ */ (0,
|
|
4001
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sm font-medium text-sas-text", children: selectedScene ? selectedScene.sceneName : title })
|
|
3834
4002
|
] }),
|
|
3835
|
-
/* @__PURE__ */ (0,
|
|
4003
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3836
4004
|
"button",
|
|
3837
4005
|
{
|
|
3838
4006
|
className: "text-sas-muted hover:text-sas-accent text-sm",
|
|
@@ -3842,30 +4010,30 @@ function ImportTrackModal({
|
|
|
3842
4010
|
}
|
|
3843
4011
|
)
|
|
3844
4012
|
] }),
|
|
3845
|
-
/* @__PURE__ */ (0,
|
|
3846
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
3847
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
3848
|
-
load.status === "ready" && scenes.length === 0 && /* @__PURE__ */ (0,
|
|
3849
|
-
load.status === "ready" && scenes.length > 0 && !selectedScene && /* @__PURE__ */ (0,
|
|
4013
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "overflow-y-auto p-2 flex-1", children: [
|
|
4014
|
+
load.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "py-8 text-center text-xs text-sas-muted", "data-testid": `${testIdPrefix}-loading`, children: "Loading scenes\u2026" }),
|
|
4015
|
+
load.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "py-8 text-center text-xs text-red-400", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
4016
|
+
load.status === "ready" && scenes.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "py-8 text-center text-xs text-sas-muted", "data-testid": `${testIdPrefix}-empty`, children: mode === "sound" ? "No other scenes have a sound to import." : "No other scenes have a compatible track to import." }),
|
|
4017
|
+
load.status === "ready" && scenes.length > 0 && !selectedScene && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-scene-list`, children: scenes.map((scene) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3850
4018
|
"button",
|
|
3851
4019
|
{
|
|
3852
4020
|
className: "w-full flex items-center justify-between px-2 py-1.5 rounded-sm border border-sas-border bg-sas-panel-alt text-left text-xs text-sas-text hover:border-sas-accent hover:text-sas-accent transition-colors",
|
|
3853
4021
|
onClick: () => setSelectedSceneId(scene.sceneId),
|
|
3854
4022
|
"data-testid": `${testIdPrefix}-scene`,
|
|
3855
4023
|
children: [
|
|
3856
|
-
/* @__PURE__ */ (0,
|
|
3857
|
-
/* @__PURE__ */ (0,
|
|
4024
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate", children: scene.sceneName }),
|
|
4025
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "text-sas-muted", children: [
|
|
3858
4026
|
scene.tracks.length,
|
|
3859
4027
|
" \u2192"
|
|
3860
4028
|
] })
|
|
3861
4029
|
]
|
|
3862
4030
|
}
|
|
3863
4031
|
) }, scene.sceneId)) }),
|
|
3864
|
-
selectedScene && /* @__PURE__ */ (0,
|
|
4032
|
+
selectedScene && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-track-list`, children: selectedScene.tracks.map((track) => {
|
|
3865
4033
|
const busy = importingTrackId === track.trackId;
|
|
3866
4034
|
const gated = mode === "track" && !track.importable;
|
|
3867
4035
|
const disabled = gated || busy;
|
|
3868
|
-
return /* @__PURE__ */ (0,
|
|
4036
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3869
4037
|
"button",
|
|
3870
4038
|
{
|
|
3871
4039
|
className: `w-full flex items-center justify-between px-2 py-1.5 rounded-sm border text-left text-xs transition-colors ${disabled ? "bg-sas-panel border-sas-border text-sas-muted/50 cursor-not-allowed" : "bg-sas-panel-alt border-sas-border text-sas-text hover:border-sas-accent hover:text-sas-accent"}`,
|
|
@@ -3875,14 +4043,14 @@ function ImportTrackModal({
|
|
|
3875
4043
|
"data-testid": `${testIdPrefix}-track`,
|
|
3876
4044
|
"data-importable": mode === "sound" || track.importable ? "true" : "false",
|
|
3877
4045
|
children: [
|
|
3878
|
-
/* @__PURE__ */ (0,
|
|
4046
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "truncate", children: [
|
|
3879
4047
|
track.name,
|
|
3880
|
-
track.role ? /* @__PURE__ */ (0,
|
|
4048
|
+
track.role ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "text-sas-muted", children: [
|
|
3881
4049
|
" \xB7 ",
|
|
3882
4050
|
track.role
|
|
3883
4051
|
] }) : null
|
|
3884
4052
|
] }),
|
|
3885
|
-
busy ? /* @__PURE__ */ (0,
|
|
4053
|
+
busy ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sas-muted", children: "\u2026" }) : gated ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sas-muted", children: "\u2298" }) : null
|
|
3886
4054
|
]
|
|
3887
4055
|
}
|
|
3888
4056
|
) }, track.dbId);
|
|
@@ -3894,8 +4062,8 @@ function ImportTrackModal({
|
|
|
3894
4062
|
}
|
|
3895
4063
|
|
|
3896
4064
|
// src/components/CrossfadeModal.tsx
|
|
3897
|
-
var
|
|
3898
|
-
var
|
|
4065
|
+
var import_react18 = require("react");
|
|
4066
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
3899
4067
|
function shortId2(dbId) {
|
|
3900
4068
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
3901
4069
|
}
|
|
@@ -3908,7 +4076,7 @@ function CandidateRow({
|
|
|
3908
4076
|
}) {
|
|
3909
4077
|
const primary = track.prompt?.trim() || track.name;
|
|
3910
4078
|
const meta = [track.role, shortId2(track.dbId)].filter(Boolean).join(" \xB7 ");
|
|
3911
|
-
return /* @__PURE__ */ (0,
|
|
4079
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
3912
4080
|
"button",
|
|
3913
4081
|
{
|
|
3914
4082
|
type: "button",
|
|
@@ -3920,8 +4088,8 @@ function CandidateRow({
|
|
|
3920
4088
|
disabled,
|
|
3921
4089
|
className: `w-full text-left px-2 py-1.5 rounded-sm border transition-colors disabled:opacity-50 ${selected ? "bg-sas-accent/15 border-sas-accent" : "bg-sas-panel border-sas-border hover:border-sas-accent/50"}`,
|
|
3922
4090
|
children: [
|
|
3923
|
-
/* @__PURE__ */ (0,
|
|
3924
|
-
meta && /* @__PURE__ */ (0,
|
|
4091
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
|
|
4092
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
|
|
3925
4093
|
]
|
|
3926
4094
|
}
|
|
3927
4095
|
);
|
|
@@ -3938,15 +4106,15 @@ function CrossfadeModal({
|
|
|
3938
4106
|
onCreate,
|
|
3939
4107
|
testIdPrefix = "crossfade-modal"
|
|
3940
4108
|
}) {
|
|
3941
|
-
const [load, setLoad] = (0,
|
|
3942
|
-
const [originDbId, setOriginDbId] = (0,
|
|
3943
|
-
const [targetDbId, setTargetDbId] = (0,
|
|
3944
|
-
const [isCreating, setIsCreating] = (0,
|
|
3945
|
-
const [error, setError] = (0,
|
|
3946
|
-
const [fromName, setFromName] = (0,
|
|
3947
|
-
const [toName, setToName] = (0,
|
|
3948
|
-
const cancelRef = (0,
|
|
3949
|
-
const refresh = (0,
|
|
4109
|
+
const [load, setLoad] = (0, import_react18.useState)({ status: "loading" });
|
|
4110
|
+
const [originDbId, setOriginDbId] = (0, import_react18.useState)("");
|
|
4111
|
+
const [targetDbId, setTargetDbId] = (0, import_react18.useState)("");
|
|
4112
|
+
const [isCreating, setIsCreating] = (0, import_react18.useState)(false);
|
|
4113
|
+
const [error, setError] = (0, import_react18.useState)(null);
|
|
4114
|
+
const [fromName, setFromName] = (0, import_react18.useState)(null);
|
|
4115
|
+
const [toName, setToName] = (0, import_react18.useState)(null);
|
|
4116
|
+
const cancelRef = (0, import_react18.useRef)(null);
|
|
4117
|
+
const refresh = (0, import_react18.useCallback)(async () => {
|
|
3950
4118
|
if (!host.listSceneFamilyTracks) {
|
|
3951
4119
|
setLoad({ status: "error", message: "This host does not support crossfade tracks." });
|
|
3952
4120
|
return;
|
|
@@ -3966,7 +4134,7 @@ function CrossfadeModal({
|
|
|
3966
4134
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
3967
4135
|
}
|
|
3968
4136
|
}, [host, fromSceneId, toSceneId]);
|
|
3969
|
-
(0,
|
|
4137
|
+
(0, import_react18.useEffect)(() => {
|
|
3970
4138
|
if (open) {
|
|
3971
4139
|
setError(null);
|
|
3972
4140
|
setIsCreating(false);
|
|
@@ -3975,21 +4143,21 @@ function CrossfadeModal({
|
|
|
3975
4143
|
void refresh();
|
|
3976
4144
|
}
|
|
3977
4145
|
}, [open, refresh]);
|
|
3978
|
-
const excludeSet = (0,
|
|
3979
|
-
const originCandidates = (0,
|
|
4146
|
+
const excludeSet = (0, import_react18.useMemo)(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
4147
|
+
const originCandidates = (0, import_react18.useMemo)(
|
|
3980
4148
|
() => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3981
4149
|
[load, excludeSet]
|
|
3982
4150
|
);
|
|
3983
|
-
const targetCandidates = (0,
|
|
4151
|
+
const targetCandidates = (0, import_react18.useMemo)(
|
|
3984
4152
|
() => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3985
4153
|
[load, excludeSet]
|
|
3986
4154
|
);
|
|
3987
|
-
(0,
|
|
4155
|
+
(0, import_react18.useEffect)(() => {
|
|
3988
4156
|
if (!originCandidates.some((t) => t.dbId === originDbId)) {
|
|
3989
4157
|
setOriginDbId(originCandidates[0]?.dbId ?? "");
|
|
3990
4158
|
}
|
|
3991
4159
|
}, [originCandidates, originDbId]);
|
|
3992
|
-
(0,
|
|
4160
|
+
(0, import_react18.useEffect)(() => {
|
|
3993
4161
|
if (!targetCandidates.some((t) => t.dbId === targetDbId)) {
|
|
3994
4162
|
setTargetDbId(targetCandidates[0]?.dbId ?? "");
|
|
3995
4163
|
}
|
|
@@ -3997,10 +4165,10 @@ function CrossfadeModal({
|
|
|
3997
4165
|
const originTrack = originCandidates.find((t) => t.dbId === originDbId) ?? null;
|
|
3998
4166
|
const targetTrack = targetCandidates.find((t) => t.dbId === targetDbId) ?? null;
|
|
3999
4167
|
const canCreate = !isCreating && !!originTrack && !!targetTrack;
|
|
4000
|
-
const handleClose = (0,
|
|
4168
|
+
const handleClose = (0, import_react18.useCallback)(() => {
|
|
4001
4169
|
if (!isCreating) onClose();
|
|
4002
4170
|
}, [isCreating, onClose]);
|
|
4003
|
-
const handleCreate = (0,
|
|
4171
|
+
const handleCreate = (0, import_react18.useCallback)(async () => {
|
|
4004
4172
|
if (!originTrack || !targetTrack) return;
|
|
4005
4173
|
setIsCreating(true);
|
|
4006
4174
|
setError(null);
|
|
@@ -4018,26 +4186,26 @@ function CrossfadeModal({
|
|
|
4018
4186
|
const fromLabel = fromName ?? fromSceneName ?? null;
|
|
4019
4187
|
const toLabel = toName ?? toSceneName ?? null;
|
|
4020
4188
|
if (!open) return null;
|
|
4021
|
-
return /* @__PURE__ */ (0,
|
|
4189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
4022
4190
|
"div",
|
|
4023
4191
|
{
|
|
4024
4192
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
4025
4193
|
onClick: (e) => e.stopPropagation(),
|
|
4026
4194
|
"data-testid": `${testIdPrefix}-box`,
|
|
4027
4195
|
children: [
|
|
4028
|
-
/* @__PURE__ */ (0,
|
|
4029
|
-
/* @__PURE__ */ (0,
|
|
4196
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { className: "text-sm font-bold text-sas-text", children: "Add crossfade" }),
|
|
4197
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
|
|
4030
4198
|
"Bridge a track from",
|
|
4031
4199
|
" ",
|
|
4032
|
-
/* @__PURE__ */ (0,
|
|
4200
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
4033
4201
|
" into one from",
|
|
4034
4202
|
" ",
|
|
4035
|
-
/* @__PURE__ */ (0,
|
|
4203
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
4036
4204
|
". Both layers share one generated part; each keeps its own preset."
|
|
4037
4205
|
] }),
|
|
4038
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
4039
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
4040
|
-
load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */ (0,
|
|
4206
|
+
load.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
|
|
4207
|
+
load.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
|
|
4208
|
+
load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
4041
4209
|
"div",
|
|
4042
4210
|
{
|
|
4043
4211
|
className: "text-xs text-sas-muted py-4 text-center",
|
|
@@ -4048,20 +4216,20 @@ function CrossfadeModal({
|
|
|
4048
4216
|
". Add one (or free one from another crossfade) first."
|
|
4049
4217
|
]
|
|
4050
4218
|
}
|
|
4051
|
-
) : /* @__PURE__ */ (0,
|
|
4052
|
-
/* @__PURE__ */ (0,
|
|
4053
|
-
/* @__PURE__ */ (0,
|
|
4219
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
4220
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "block", children: [
|
|
4221
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
4054
4222
|
"Origin ",
|
|
4055
4223
|
fromLabel ? `(${fromLabel})` : "(top)"
|
|
4056
4224
|
] }),
|
|
4057
|
-
/* @__PURE__ */ (0,
|
|
4225
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4058
4226
|
"div",
|
|
4059
4227
|
{
|
|
4060
4228
|
role: "radiogroup",
|
|
4061
4229
|
"aria-label": "Origin track",
|
|
4062
4230
|
"data-testid": `${testIdPrefix}-origin-list`,
|
|
4063
4231
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
4064
|
-
children: originCandidates.map((t) => /* @__PURE__ */ (0,
|
|
4232
|
+
children: originCandidates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4065
4233
|
CandidateRow,
|
|
4066
4234
|
{
|
|
4067
4235
|
track: t,
|
|
@@ -4075,23 +4243,23 @@ function CrossfadeModal({
|
|
|
4075
4243
|
}
|
|
4076
4244
|
)
|
|
4077
4245
|
] }),
|
|
4078
|
-
/* @__PURE__ */ (0,
|
|
4079
|
-
/* @__PURE__ */ (0,
|
|
4246
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "block", children: [
|
|
4247
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
4080
4248
|
"Target ",
|
|
4081
4249
|
toLabel ? `(${toLabel})` : "(bottom)"
|
|
4082
4250
|
] }),
|
|
4083
|
-
targetCandidates.length === 0 ? /* @__PURE__ */ (0,
|
|
4251
|
+
targetCandidates.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "text-xs text-sas-danger mt-0.5", "data-testid": `${testIdPrefix}-empty-target`, children: [
|
|
4084
4252
|
"No available tracks in ",
|
|
4085
4253
|
toLabel ?? "the target scene",
|
|
4086
4254
|
" to crossfade into."
|
|
4087
|
-
] }) : /* @__PURE__ */ (0,
|
|
4255
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4088
4256
|
"div",
|
|
4089
4257
|
{
|
|
4090
4258
|
role: "radiogroup",
|
|
4091
4259
|
"aria-label": "Target track",
|
|
4092
4260
|
"data-testid": `${testIdPrefix}-target-list`,
|
|
4093
4261
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
4094
|
-
children: targetCandidates.map((t) => /* @__PURE__ */ (0,
|
|
4262
|
+
children: targetCandidates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4095
4263
|
CandidateRow,
|
|
4096
4264
|
{
|
|
4097
4265
|
track: t,
|
|
@@ -4106,9 +4274,9 @@ function CrossfadeModal({
|
|
|
4106
4274
|
)
|
|
4107
4275
|
] })
|
|
4108
4276
|
] })),
|
|
4109
|
-
error && /* @__PURE__ */ (0,
|
|
4110
|
-
/* @__PURE__ */ (0,
|
|
4111
|
-
/* @__PURE__ */ (0,
|
|
4277
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
|
|
4278
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
4279
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4112
4280
|
"button",
|
|
4113
4281
|
{
|
|
4114
4282
|
ref: cancelRef,
|
|
@@ -4119,7 +4287,7 @@ function CrossfadeModal({
|
|
|
4119
4287
|
children: "Cancel"
|
|
4120
4288
|
}
|
|
4121
4289
|
),
|
|
4122
|
-
/* @__PURE__ */ (0,
|
|
4290
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4123
4291
|
"button",
|
|
4124
4292
|
{
|
|
4125
4293
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -4136,10 +4304,10 @@ function CrossfadeModal({
|
|
|
4136
4304
|
}
|
|
4137
4305
|
|
|
4138
4306
|
// src/components/TransitionDesigner.tsx
|
|
4139
|
-
var
|
|
4307
|
+
var import_react20 = require("react");
|
|
4140
4308
|
|
|
4141
4309
|
// src/hooks/useTrackReorder.ts
|
|
4142
|
-
var
|
|
4310
|
+
var import_react19 = require("react");
|
|
4143
4311
|
function moveItem(arr, from, to) {
|
|
4144
4312
|
const next = arr.slice();
|
|
4145
4313
|
if (from === to || from < 0 || to < 0 || from >= next.length || to >= next.length) {
|
|
@@ -4156,12 +4324,12 @@ function useTrackReorder({
|
|
|
4156
4324
|
getId,
|
|
4157
4325
|
onError
|
|
4158
4326
|
}) {
|
|
4159
|
-
const [draggingIndex, setDraggingIndex] = (0,
|
|
4160
|
-
const [dragOverIndex, setDragOverIndex] = (0,
|
|
4161
|
-
const fromRef = (0,
|
|
4162
|
-
const itemsRef = (0,
|
|
4327
|
+
const [draggingIndex, setDraggingIndex] = (0, import_react19.useState)(null);
|
|
4328
|
+
const [dragOverIndex, setDragOverIndex] = (0, import_react19.useState)(null);
|
|
4329
|
+
const fromRef = (0, import_react19.useRef)(null);
|
|
4330
|
+
const itemsRef = (0, import_react19.useRef)(items);
|
|
4163
4331
|
itemsRef.current = items;
|
|
4164
|
-
const dragPropsFor = (0,
|
|
4332
|
+
const dragPropsFor = (0, import_react19.useCallback)(
|
|
4165
4333
|
(index) => ({
|
|
4166
4334
|
handleProps: {
|
|
4167
4335
|
draggable: true,
|
|
@@ -4343,7 +4511,7 @@ function dbIdsFromKeys(keys) {
|
|
|
4343
4511
|
}
|
|
4344
4512
|
|
|
4345
4513
|
// src/components/TransitionDesigner.tsx
|
|
4346
|
-
var
|
|
4514
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
4347
4515
|
var CROSSFADE_ESTIMATE_MS = 15e3;
|
|
4348
4516
|
var FADE_ESTIMATE_MS = 11e3;
|
|
4349
4517
|
var CREATE_ALL_CONCURRENCY = 5;
|
|
@@ -4370,44 +4538,44 @@ function TransitionDesigner({
|
|
|
4370
4538
|
testIdPrefix = "transition-designer"
|
|
4371
4539
|
}) {
|
|
4372
4540
|
const fadeOnly = !onCreateCrossfade;
|
|
4373
|
-
const [load, setLoad] = (0,
|
|
4374
|
-
const [fromName, setFromName] = (0,
|
|
4375
|
-
const [toName, setToName] = (0,
|
|
4376
|
-
const [originSlots, setOriginSlots] = (0,
|
|
4377
|
-
const [targetSlots, setTargetSlots] = (0,
|
|
4378
|
-
const [creatingKeys, setCreatingKeys] = (0,
|
|
4379
|
-
const [rowErrors, setRowErrors] = (0,
|
|
4380
|
-
const [rowEffects, setRowEffects] = (0,
|
|
4381
|
-
const rowEffectsRef = (0,
|
|
4541
|
+
const [load, setLoad] = (0, import_react20.useState)({ status: "loading" });
|
|
4542
|
+
const [fromName, setFromName] = (0, import_react20.useState)(null);
|
|
4543
|
+
const [toName, setToName] = (0, import_react20.useState)(null);
|
|
4544
|
+
const [originSlots, setOriginSlots] = (0, import_react20.useState)([]);
|
|
4545
|
+
const [targetSlots, setTargetSlots] = (0, import_react20.useState)([]);
|
|
4546
|
+
const [creatingKeys, setCreatingKeys] = (0, import_react20.useState)(() => /* @__PURE__ */ new Set());
|
|
4547
|
+
const [rowErrors, setRowErrors] = (0, import_react20.useState)({});
|
|
4548
|
+
const [rowEffects, setRowEffects] = (0, import_react20.useState)({});
|
|
4549
|
+
const rowEffectsRef = (0, import_react20.useRef)(rowEffects);
|
|
4382
4550
|
rowEffectsRef.current = rowEffects;
|
|
4383
4551
|
const audioEffectsEnabled = !!onCreateAudioTransition;
|
|
4384
|
-
const excludeRef = (0,
|
|
4552
|
+
const excludeRef = (0, import_react20.useRef)(excludeSourceDbIds);
|
|
4385
4553
|
excludeRef.current = excludeSourceDbIds;
|
|
4386
|
-
const originSlotsRef = (0,
|
|
4554
|
+
const originSlotsRef = (0, import_react20.useRef)(originSlots);
|
|
4387
4555
|
originSlotsRef.current = originSlots;
|
|
4388
|
-
const targetSlotsRef = (0,
|
|
4556
|
+
const targetSlotsRef = (0, import_react20.useRef)(targetSlots);
|
|
4389
4557
|
targetSlotsRef.current = targetSlots;
|
|
4390
|
-
const creatingKeysRef = (0,
|
|
4558
|
+
const creatingKeysRef = (0, import_react20.useRef)(creatingKeys);
|
|
4391
4559
|
creatingKeysRef.current = creatingKeys;
|
|
4392
|
-
const dragRef = (0,
|
|
4393
|
-
const [dragging, setDragging] = (0,
|
|
4394
|
-
const [dragOver, setDragOver] = (0,
|
|
4395
|
-
const excludeSet = (0,
|
|
4396
|
-
const originPool = (0,
|
|
4560
|
+
const dragRef = (0, import_react20.useRef)(null);
|
|
4561
|
+
const [dragging, setDragging] = (0, import_react20.useState)(null);
|
|
4562
|
+
const [dragOver, setDragOver] = (0, import_react20.useState)(null);
|
|
4563
|
+
const excludeSet = (0, import_react20.useMemo)(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
4564
|
+
const originPool = (0, import_react20.useMemo)(
|
|
4397
4565
|
() => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
4398
4566
|
[load, excludeSet]
|
|
4399
4567
|
);
|
|
4400
|
-
const targetPool = (0,
|
|
4568
|
+
const targetPool = (0, import_react20.useMemo)(
|
|
4401
4569
|
() => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
4402
4570
|
[load, excludeSet]
|
|
4403
4571
|
);
|
|
4404
|
-
const originById = (0,
|
|
4405
|
-
const targetById = (0,
|
|
4406
|
-
const originByIdRef = (0,
|
|
4572
|
+
const originById = (0, import_react20.useMemo)(() => new Map(originPool.map((t) => [t.dbId, t])), [originPool]);
|
|
4573
|
+
const targetById = (0, import_react20.useMemo)(() => new Map(targetPool.map((t) => [t.dbId, t])), [targetPool]);
|
|
4574
|
+
const originByIdRef = (0, import_react20.useRef)(originById);
|
|
4407
4575
|
originByIdRef.current = originById;
|
|
4408
|
-
const targetByIdRef = (0,
|
|
4576
|
+
const targetByIdRef = (0, import_react20.useRef)(targetById);
|
|
4409
4577
|
targetByIdRef.current = targetById;
|
|
4410
|
-
const refresh = (0,
|
|
4578
|
+
const refresh = (0, import_react20.useCallback)(async () => {
|
|
4411
4579
|
if (!host.listSceneFamilyTracks) {
|
|
4412
4580
|
setLoad({ status: "error", message: "This host does not support transition tracks." });
|
|
4413
4581
|
return;
|
|
@@ -4450,10 +4618,10 @@ function TransitionDesigner({
|
|
|
4450
4618
|
});
|
|
4451
4619
|
}
|
|
4452
4620
|
}, [host, fromSceneId, toSceneId, transitionSceneId, mapColumnSubjects]);
|
|
4453
|
-
(0,
|
|
4621
|
+
(0, import_react20.useEffect)(() => {
|
|
4454
4622
|
void refresh();
|
|
4455
4623
|
}, [refresh]);
|
|
4456
|
-
(0,
|
|
4624
|
+
(0, import_react20.useEffect)(() => {
|
|
4457
4625
|
if (load.status !== "ready") return;
|
|
4458
4626
|
const [po, pt] = padPair(
|
|
4459
4627
|
reconcileSlots(originSlotsRef.current, originPool.map((t) => t.dbId)),
|
|
@@ -4462,7 +4630,7 @@ function TransitionDesigner({
|
|
|
4462
4630
|
if (!slotsEqual(po, originSlotsRef.current)) setOriginSlots(po);
|
|
4463
4631
|
if (!slotsEqual(pt, targetSlotsRef.current)) setTargetSlots(pt);
|
|
4464
4632
|
}, [originPool, targetPool, load.status]);
|
|
4465
|
-
const mutate = (0,
|
|
4633
|
+
const mutate = (0, import_react20.useCallback)(
|
|
4466
4634
|
(nextOrigin, nextTarget) => {
|
|
4467
4635
|
const norm = normalizeSlots(nextOrigin, nextTarget);
|
|
4468
4636
|
const [po, pt] = padPair(norm.originOrder, norm.targetOrder);
|
|
@@ -4475,7 +4643,7 @@ function TransitionDesigner({
|
|
|
4475
4643
|
},
|
|
4476
4644
|
[host, transitionSceneId]
|
|
4477
4645
|
);
|
|
4478
|
-
const setRowEffect = (0,
|
|
4646
|
+
const setRowEffect = (0, import_react20.useCallback)(
|
|
4479
4647
|
(sourceDbId, effect) => {
|
|
4480
4648
|
setRowEffects((prev) => {
|
|
4481
4649
|
const next = { ...prev, [sourceDbId]: effect };
|
|
@@ -4489,7 +4657,7 @@ function TransitionDesigner({
|
|
|
4489
4657
|
},
|
|
4490
4658
|
[host, transitionSceneId]
|
|
4491
4659
|
);
|
|
4492
|
-
const insertGapAbove = (0,
|
|
4660
|
+
const insertGapAbove = (0, import_react20.useCallback)(
|
|
4493
4661
|
(col, index) => {
|
|
4494
4662
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
4495
4663
|
const next = [...slots.slice(0, index), null, ...slots.slice(index)];
|
|
@@ -4498,7 +4666,7 @@ function TransitionDesigner({
|
|
|
4498
4666
|
},
|
|
4499
4667
|
[originSlots, targetSlots, mutate]
|
|
4500
4668
|
);
|
|
4501
|
-
const removeGap = (0,
|
|
4669
|
+
const removeGap = (0, import_react20.useCallback)(
|
|
4502
4670
|
(col, index) => {
|
|
4503
4671
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
4504
4672
|
const next = slots.filter((_, i) => i !== index);
|
|
@@ -4507,7 +4675,7 @@ function TransitionDesigner({
|
|
|
4507
4675
|
},
|
|
4508
4676
|
[originSlots, targetSlots, mutate]
|
|
4509
4677
|
);
|
|
4510
|
-
const handleDrop = (0,
|
|
4678
|
+
const handleDrop = (0, import_react20.useCallback)(
|
|
4511
4679
|
(col, to) => {
|
|
4512
4680
|
const from = dragRef.current;
|
|
4513
4681
|
dragRef.current = null;
|
|
@@ -4519,8 +4687,8 @@ function TransitionDesigner({
|
|
|
4519
4687
|
},
|
|
4520
4688
|
[originSlots, targetSlots, mutate]
|
|
4521
4689
|
);
|
|
4522
|
-
const rows = (0,
|
|
4523
|
-
const fadeOnlyRows = (0,
|
|
4690
|
+
const rows = (0, import_react20.useMemo)(() => buildRowSlots(originSlots, targetSlots), [originSlots, targetSlots]);
|
|
4691
|
+
const fadeOnlyRows = (0, import_react20.useMemo)(
|
|
4524
4692
|
() => fadeOnly ? [
|
|
4525
4693
|
...originPool.map((t) => ({
|
|
4526
4694
|
originId: t.dbId,
|
|
@@ -4535,18 +4703,18 @@ function TransitionDesigner({
|
|
|
4535
4703
|
] : [],
|
|
4536
4704
|
[fadeOnly, originPool, targetPool]
|
|
4537
4705
|
);
|
|
4538
|
-
const fadeOnlyRowsRef = (0,
|
|
4706
|
+
const fadeOnlyRowsRef = (0, import_react20.useRef)(fadeOnlyRows);
|
|
4539
4707
|
fadeOnlyRowsRef.current = fadeOnlyRows;
|
|
4540
4708
|
const effectiveRows = fadeOnly ? fadeOnlyRows : rows;
|
|
4541
|
-
const creatingDbIds = (0,
|
|
4542
|
-
const eligibleCount = (0,
|
|
4709
|
+
const creatingDbIds = (0, import_react20.useMemo)(() => dbIdsFromKeys(creatingKeys), [creatingKeys]);
|
|
4710
|
+
const eligibleCount = (0, import_react20.useMemo)(
|
|
4543
4711
|
() => effectiveRows.filter((r) => {
|
|
4544
4712
|
const k = rowKey(r);
|
|
4545
4713
|
return k !== null && !creatingKeys.has(k);
|
|
4546
4714
|
}).length,
|
|
4547
4715
|
[effectiveRows, creatingKeys]
|
|
4548
4716
|
);
|
|
4549
|
-
const createRow = (0,
|
|
4717
|
+
const createRow = (0, import_react20.useCallback)(
|
|
4550
4718
|
async (row) => {
|
|
4551
4719
|
const key = rowKey(row);
|
|
4552
4720
|
if (!key || !row.type || creatingKeysRef.current.has(key)) return;
|
|
@@ -4601,7 +4769,7 @@ function TransitionDesigner({
|
|
|
4601
4769
|
},
|
|
4602
4770
|
[onCreateCrossfade, onCreateFade, onCreateAudioTransition]
|
|
4603
4771
|
);
|
|
4604
|
-
const createAll = (0,
|
|
4772
|
+
const createAll = (0, import_react20.useCallback)(async () => {
|
|
4605
4773
|
const source = fadeOnly ? fadeOnlyRowsRef.current : buildRowSlots(originSlotsRef.current, targetSlotsRef.current);
|
|
4606
4774
|
const eligible = source.filter((r) => {
|
|
4607
4775
|
const k = rowKey(r);
|
|
@@ -4670,15 +4838,15 @@ function TransitionDesigner({
|
|
|
4670
4838
|
const base = "group relative rounded-sm border px-2 py-1.5 text-left transition-colors select-none";
|
|
4671
4839
|
const tone = isDragTarget ? "border-sas-accent bg-sas-accent/10" : "border-sas-border bg-sas-panel";
|
|
4672
4840
|
if (slotId === null) {
|
|
4673
|
-
return /* @__PURE__ */ (0,
|
|
4841
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
4674
4842
|
"div",
|
|
4675
4843
|
{
|
|
4676
4844
|
...cellDragProps(col, index, false),
|
|
4677
4845
|
"data-testid": `${testIdPrefix}-${col}-gap-${index}`,
|
|
4678
4846
|
className: `${base} ${tone} border-dashed flex items-center justify-between ${isDragging ? "opacity-40" : "opacity-70"}`,
|
|
4679
4847
|
children: [
|
|
4680
|
-
/* @__PURE__ */ (0,
|
|
4681
|
-
/* @__PURE__ */ (0,
|
|
4848
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: "\u2014 gap \u2014" }),
|
|
4849
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4682
4850
|
"button",
|
|
4683
4851
|
{
|
|
4684
4852
|
type: "button",
|
|
@@ -4695,7 +4863,7 @@ function TransitionDesigner({
|
|
|
4695
4863
|
}
|
|
4696
4864
|
const primary = track ? track.prompt?.trim() || track.name : slotId;
|
|
4697
4865
|
const meta = track ? [track.role, shortId3(track.dbId)].filter(Boolean).join(" \xB7 ") : "missing";
|
|
4698
|
-
return /* @__PURE__ */ (0,
|
|
4866
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4699
4867
|
"div",
|
|
4700
4868
|
{
|
|
4701
4869
|
...cellDragProps(col, index, locked),
|
|
@@ -4703,13 +4871,13 @@ function TransitionDesigner({
|
|
|
4703
4871
|
"data-value": slotId,
|
|
4704
4872
|
className: `${base} ${tone} ${isDragging ? "opacity-40" : ""} ${locked ? "opacity-60" : "cursor-grab active:cursor-grabbing"}`,
|
|
4705
4873
|
title: track ? track.dbId : "Track no longer available",
|
|
4706
|
-
children: /* @__PURE__ */ (0,
|
|
4707
|
-
/* @__PURE__ */ (0,
|
|
4708
|
-
/* @__PURE__ */ (0,
|
|
4709
|
-
/* @__PURE__ */ (0,
|
|
4710
|
-
meta && /* @__PURE__ */ (0,
|
|
4874
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-start gap-1", children: [
|
|
4875
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-sas-muted/60 text-xs leading-tight pt-0.5", "aria-hidden": true, children: "\u283F" }),
|
|
4876
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
4877
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-xs text-sas-text truncate", children: primary }),
|
|
4878
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", children: meta })
|
|
4711
4879
|
] }),
|
|
4712
|
-
/* @__PURE__ */ (0,
|
|
4880
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4713
4881
|
"button",
|
|
4714
4882
|
{
|
|
4715
4883
|
type: "button",
|
|
@@ -4732,26 +4900,26 @@ function TransitionDesigner({
|
|
|
4732
4900
|
const key = rowKey(row);
|
|
4733
4901
|
const isCreatingThis = key !== null && creatingKeys.has(key);
|
|
4734
4902
|
const errMsg = key !== null ? rowErrors[key] : void 0;
|
|
4735
|
-
return /* @__PURE__ */ (0,
|
|
4903
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
4736
4904
|
"div",
|
|
4737
4905
|
{
|
|
4738
4906
|
"data-testid": `${testIdPrefix}-row-${i}`,
|
|
4739
4907
|
className: "grid grid-cols-[1fr_auto] gap-2 items-center",
|
|
4740
4908
|
children: [
|
|
4741
|
-
/* @__PURE__ */ (0,
|
|
4909
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
4742
4910
|
"div",
|
|
4743
4911
|
{
|
|
4744
4912
|
"data-testid": `${testIdPrefix}-subject-${slotId}`,
|
|
4745
4913
|
className: `rounded-sm border border-sas-border bg-sas-panel px-2 py-1.5 ${isCreatingThis ? "opacity-60" : ""}`,
|
|
4746
4914
|
title: track ? track.dbId : "Track no longer available",
|
|
4747
4915
|
children: [
|
|
4748
|
-
/* @__PURE__ */ (0,
|
|
4749
|
-
/* @__PURE__ */ (0,
|
|
4916
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-xs text-sas-text truncate", children: track ? track.prompt?.trim() || track.name : slotId }),
|
|
4917
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", children: track ? [track.role, shortId3(track.dbId)].filter(Boolean).join(" \xB7 ") : "missing" })
|
|
4750
4918
|
]
|
|
4751
4919
|
}
|
|
4752
4920
|
),
|
|
4753
|
-
/* @__PURE__ */ (0,
|
|
4754
|
-
/* @__PURE__ */ (0,
|
|
4921
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
|
|
4922
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4755
4923
|
"span",
|
|
4756
4924
|
{
|
|
4757
4925
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
@@ -4759,7 +4927,7 @@ function TransitionDesigner({
|
|
|
4759
4927
|
children: row.type ? TYPE_LABEL[row.type] : "\u2014"
|
|
4760
4928
|
}
|
|
4761
4929
|
),
|
|
4762
|
-
isCreatingThis ? /* @__PURE__ */ (0,
|
|
4930
|
+
isCreatingThis ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4763
4931
|
SorceryProgressBar,
|
|
4764
4932
|
{
|
|
4765
4933
|
isLoading: true,
|
|
@@ -4767,7 +4935,7 @@ function TransitionDesigner({
|
|
|
4767
4935
|
statusText: "CREATING",
|
|
4768
4936
|
estimatedDurationMs: fadeEstimateMs ?? FADE_ESTIMATE_MS
|
|
4769
4937
|
}
|
|
4770
|
-
) }) : /* @__PURE__ */ (0,
|
|
4938
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4771
4939
|
"button",
|
|
4772
4940
|
{
|
|
4773
4941
|
type: "button",
|
|
@@ -4777,7 +4945,7 @@ function TransitionDesigner({
|
|
|
4777
4945
|
children: "Create"
|
|
4778
4946
|
}
|
|
4779
4947
|
),
|
|
4780
|
-
errMsg && /* @__PURE__ */ (0,
|
|
4948
|
+
errMsg && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4781
4949
|
"span",
|
|
4782
4950
|
{
|
|
4783
4951
|
"data-testid": `${testIdPrefix}-row-error-${i}`,
|
|
@@ -4794,18 +4962,18 @@ function TransitionDesigner({
|
|
|
4794
4962
|
if (fadeOnly) {
|
|
4795
4963
|
const outRows = fadeOnlyRows.filter((r) => r.type === "fade-out");
|
|
4796
4964
|
const inRows = fadeOnlyRows.filter((r) => r.type === "fade-in");
|
|
4797
|
-
return /* @__PURE__ */ (0,
|
|
4798
|
-
/* @__PURE__ */ (0,
|
|
4799
|
-
/* @__PURE__ */ (0,
|
|
4800
|
-
/* @__PURE__ */ (0,
|
|
4965
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
|
|
4966
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
|
|
4967
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
|
|
4968
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-sas-text", children: fromLabel }),
|
|
4801
4969
|
" \u2192",
|
|
4802
4970
|
" ",
|
|
4803
|
-
/* @__PURE__ */ (0,
|
|
4971
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-sas-text", children: toLabel }),
|
|
4804
4972
|
familyLabel ? ` \xB7 ${familyLabel}` : "",
|
|
4805
4973
|
" \xB7 each entry fades on its own \u2014 origin fades out, target fades in."
|
|
4806
4974
|
] }),
|
|
4807
|
-
/* @__PURE__ */ (0,
|
|
4808
|
-
creatingKeys.size > 0 && /* @__PURE__ */ (0,
|
|
4975
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
4976
|
+
creatingKeys.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
4809
4977
|
"span",
|
|
4810
4978
|
{
|
|
4811
4979
|
className: "text-[10px] text-sas-accent whitespace-nowrap",
|
|
@@ -4816,7 +4984,7 @@ function TransitionDesigner({
|
|
|
4816
4984
|
]
|
|
4817
4985
|
}
|
|
4818
4986
|
),
|
|
4819
|
-
/* @__PURE__ */ (0,
|
|
4987
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
4820
4988
|
"button",
|
|
4821
4989
|
{
|
|
4822
4990
|
type: "button",
|
|
@@ -4833,26 +5001,26 @@ function TransitionDesigner({
|
|
|
4833
5001
|
)
|
|
4834
5002
|
] })
|
|
4835
5003
|
] }),
|
|
4836
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
4837
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
4838
|
-
load.status === "ready" && (fadeOnlyRows.length === 0 ? /* @__PURE__ */ (0,
|
|
5004
|
+
load.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
|
|
5005
|
+
load.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
5006
|
+
load.status === "ready" && (fadeOnlyRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "text-xs text-sas-muted py-6 text-center", "data-testid": `${testIdPrefix}-empty`, children: [
|
|
4839
5007
|
"Nothing to fade in this panel for either scene. Add tracks to ",
|
|
4840
5008
|
fromLabel,
|
|
4841
5009
|
" or ",
|
|
4842
5010
|
toLabel,
|
|
4843
5011
|
" ",
|
|
4844
5012
|
"first (or free one by deleting an existing fade)."
|
|
4845
|
-
] }) : /* @__PURE__ */ (0,
|
|
4846
|
-
outRows.length > 0 && /* @__PURE__ */ (0,
|
|
4847
|
-
/* @__PURE__ */ (0,
|
|
5013
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "space-y-3", children: [
|
|
5014
|
+
outRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-fade-out-section`, children: [
|
|
5015
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
4848
5016
|
"Origin (",
|
|
4849
5017
|
fromLabel,
|
|
4850
5018
|
") \u2014 fades out"
|
|
4851
5019
|
] }),
|
|
4852
5020
|
outRows.map((row, i) => renderFadeOnlyRow(row, i))
|
|
4853
5021
|
] }),
|
|
4854
|
-
inRows.length > 0 && /* @__PURE__ */ (0,
|
|
4855
|
-
/* @__PURE__ */ (0,
|
|
5022
|
+
inRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-fade-in-section`, children: [
|
|
5023
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
4856
5024
|
"Target (",
|
|
4857
5025
|
toLabel,
|
|
4858
5026
|
") \u2014 fades in"
|
|
@@ -4862,22 +5030,22 @@ function TransitionDesigner({
|
|
|
4862
5030
|
] }))
|
|
4863
5031
|
] });
|
|
4864
5032
|
}
|
|
4865
|
-
return /* @__PURE__ */ (0,
|
|
4866
|
-
/* @__PURE__ */ (0,
|
|
4867
|
-
/* @__PURE__ */ (0,
|
|
4868
|
-
/* @__PURE__ */ (0,
|
|
5033
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
|
|
5034
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
|
|
5035
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
|
|
5036
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-sas-text", children: fromLabel }),
|
|
4869
5037
|
" \u2192",
|
|
4870
5038
|
" ",
|
|
4871
|
-
/* @__PURE__ */ (0,
|
|
5039
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-sas-text", children: toLabel }),
|
|
4872
5040
|
familyLabel ? ` \xB7 ${familyLabel}` : "",
|
|
4873
5041
|
" \xB7 line up a track on each side to crossfade; leave one blank (or insert a gap) to fade."
|
|
4874
5042
|
] }),
|
|
4875
|
-
/* @__PURE__ */ (0,
|
|
4876
|
-
creatingKeys.size > 0 && /* @__PURE__ */ (0,
|
|
5043
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
5044
|
+
creatingKeys.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-[10px] text-sas-accent whitespace-nowrap", "data-testid": `${testIdPrefix}-creating-count`, children: [
|
|
4877
5045
|
creatingKeys.size,
|
|
4878
5046
|
" creating\u2026"
|
|
4879
5047
|
] }),
|
|
4880
|
-
/* @__PURE__ */ (0,
|
|
5048
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
4881
5049
|
"button",
|
|
4882
5050
|
{
|
|
4883
5051
|
type: "button",
|
|
@@ -4894,49 +5062,49 @@ function TransitionDesigner({
|
|
|
4894
5062
|
)
|
|
4895
5063
|
] })
|
|
4896
5064
|
] }),
|
|
4897
|
-
/* @__PURE__ */ (0,
|
|
4898
|
-
/* @__PURE__ */ (0,
|
|
5065
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "grid grid-cols-[1fr_auto_1fr] gap-2", children: [
|
|
5066
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate", children: [
|
|
4899
5067
|
"Origin (",
|
|
4900
5068
|
fromLabel,
|
|
4901
5069
|
")"
|
|
4902
5070
|
] }),
|
|
4903
|
-
/* @__PURE__ */ (0,
|
|
4904
|
-
/* @__PURE__ */ (0,
|
|
5071
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted text-center px-2", children: "Transition" }),
|
|
5072
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate text-right", children: [
|
|
4905
5073
|
"Target (",
|
|
4906
5074
|
toLabel,
|
|
4907
5075
|
")"
|
|
4908
5076
|
] })
|
|
4909
5077
|
] }),
|
|
4910
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
4911
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
4912
|
-
load.status === "ready" && (rows.length === 0 ? /* @__PURE__ */ (0,
|
|
5078
|
+
load.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
|
|
5079
|
+
load.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
5080
|
+
load.status === "ready" && (rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "text-xs text-sas-muted py-6 text-center", "data-testid": `${testIdPrefix}-empty`, children: [
|
|
4913
5081
|
"No tracks to arrange in this panel for either scene. Add tracks to ",
|
|
4914
5082
|
fromLabel,
|
|
4915
5083
|
" or ",
|
|
4916
5084
|
toLabel,
|
|
4917
5085
|
" ",
|
|
4918
5086
|
"first (or free one by deleting an existing crossfade/fade)."
|
|
4919
|
-
] }) : /* @__PURE__ */ (0,
|
|
5087
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "space-y-2", children: rows.map((row, i) => {
|
|
4920
5088
|
const key = rowKey(row);
|
|
4921
5089
|
const isCreatingThis = key !== null && creatingKeys.has(key);
|
|
4922
5090
|
const errMsg = key !== null ? rowErrors[key] : void 0;
|
|
4923
|
-
return /* @__PURE__ */ (0,
|
|
5091
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
4924
5092
|
"div",
|
|
4925
5093
|
{
|
|
4926
5094
|
"data-testid": `${testIdPrefix}-row-${i}`,
|
|
4927
5095
|
className: "grid grid-cols-[1fr_auto_1fr] gap-2 items-center",
|
|
4928
5096
|
children: [
|
|
4929
5097
|
renderCell("origin", i, row.originId),
|
|
4930
|
-
/* @__PURE__ */ (0,
|
|
4931
|
-
!row.type ? /* @__PURE__ */ (0,
|
|
5098
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
|
|
5099
|
+
!row.type ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-sas-muted/50", children: "\u2014" }) : row.type === "crossfade" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4932
5100
|
"span",
|
|
4933
5101
|
{
|
|
4934
5102
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
4935
5103
|
className: "text-[10px] font-medium px-1.5 py-0.5 rounded-sm border border-sas-accent/50 text-sas-accent",
|
|
4936
5104
|
children: TYPE_LABEL[row.type]
|
|
4937
5105
|
}
|
|
4938
|
-
) : audioEffectsEnabled ? /* @__PURE__ */ (0,
|
|
4939
|
-
/* @__PURE__ */ (0,
|
|
5106
|
+
) : audioEffectsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1", "data-testid": `${testIdPrefix}-type-${i}`, children: [
|
|
5107
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4940
5108
|
"select",
|
|
4941
5109
|
{
|
|
4942
5110
|
"data-testid": `${testIdPrefix}-effect-${i}`,
|
|
@@ -4946,11 +5114,11 @@ function TransitionDesigner({
|
|
|
4946
5114
|
if (id) setRowEffect(id, e.target.value);
|
|
4947
5115
|
},
|
|
4948
5116
|
className: "text-[10px] bg-sas-panel border border-sas-border rounded-sm px-1 py-0.5 text-sas-text",
|
|
4949
|
-
children: AUDIO_EFFECTS.map((eff) => /* @__PURE__ */ (0,
|
|
5117
|
+
children: AUDIO_EFFECTS.map((eff) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: eff, children: AUDIO_EFFECT_LABEL[eff] }, eff))
|
|
4950
5118
|
}
|
|
4951
5119
|
),
|
|
4952
|
-
/* @__PURE__ */ (0,
|
|
4953
|
-
] }) : /* @__PURE__ */ (0,
|
|
5120
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[9px] text-sas-muted", children: row.type === "fade-out" ? "out" : "in" })
|
|
5121
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4954
5122
|
"span",
|
|
4955
5123
|
{
|
|
4956
5124
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
@@ -4958,7 +5126,7 @@ function TransitionDesigner({
|
|
|
4958
5126
|
children: TYPE_LABEL[row.type]
|
|
4959
5127
|
}
|
|
4960
5128
|
),
|
|
4961
|
-
isCreatingThis ? /* @__PURE__ */ (0,
|
|
5129
|
+
isCreatingThis ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4962
5130
|
SorceryProgressBar,
|
|
4963
5131
|
{
|
|
4964
5132
|
isLoading: true,
|
|
@@ -4966,7 +5134,7 @@ function TransitionDesigner({
|
|
|
4966
5134
|
statusText: "CREATING",
|
|
4967
5135
|
estimatedDurationMs: row.type === "crossfade" ? CROSSFADE_ESTIMATE_MS : FADE_ESTIMATE_MS
|
|
4968
5136
|
}
|
|
4969
|
-
) }) : /* @__PURE__ */ (0,
|
|
5137
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4970
5138
|
"button",
|
|
4971
5139
|
{
|
|
4972
5140
|
type: "button",
|
|
@@ -4977,7 +5145,7 @@ function TransitionDesigner({
|
|
|
4977
5145
|
children: "Create"
|
|
4978
5146
|
}
|
|
4979
5147
|
),
|
|
4980
|
-
errMsg && /* @__PURE__ */ (0,
|
|
5148
|
+
errMsg && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4981
5149
|
"span",
|
|
4982
5150
|
{
|
|
4983
5151
|
"data-testid": `${testIdPrefix}-row-error-${i}`,
|
|
@@ -4996,8 +5164,8 @@ function TransitionDesigner({
|
|
|
4996
5164
|
}
|
|
4997
5165
|
|
|
4998
5166
|
// src/components/PanelMasterStrip.tsx
|
|
4999
|
-
var
|
|
5000
|
-
var
|
|
5167
|
+
var import_react21 = require("react");
|
|
5168
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
5001
5169
|
function PanelMasterStrip({
|
|
5002
5170
|
bus,
|
|
5003
5171
|
levels = null,
|
|
@@ -5016,22 +5184,22 @@ function PanelMasterStrip({
|
|
|
5016
5184
|
onToggleFxEnabled,
|
|
5017
5185
|
onShowFxEditor
|
|
5018
5186
|
}) {
|
|
5019
|
-
const [search, setSearch] = (0,
|
|
5020
|
-
const filtered = (0,
|
|
5187
|
+
const [search, setSearch] = (0, import_react21.useState)("");
|
|
5188
|
+
const filtered = (0, import_react21.useMemo)(() => {
|
|
5021
5189
|
const q = search.trim().toLowerCase();
|
|
5022
5190
|
if (!q) return availableFx;
|
|
5023
5191
|
return availableFx.filter(
|
|
5024
5192
|
(fx) => fx.name.toLowerCase().includes(q) || fx.manufacturer.toLowerCase().includes(q)
|
|
5025
5193
|
);
|
|
5026
5194
|
}, [availableFx, search]);
|
|
5027
|
-
return /* @__PURE__ */ (0,
|
|
5195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
5028
5196
|
"div",
|
|
5029
5197
|
{
|
|
5030
5198
|
"data-testid": "panel-master-strip",
|
|
5031
5199
|
className: `flex flex-col gap-1 px-2 py-1.5 rounded-sm border border-sas-border border-l-2 border-l-sas-accent/50 bg-sas-accent/5 transition-opacity ${soloedOut ? "opacity-40" : ""}`,
|
|
5032
5200
|
children: [
|
|
5033
|
-
/* @__PURE__ */ (0,
|
|
5034
|
-
/* @__PURE__ */ (0,
|
|
5201
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
5202
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5035
5203
|
"span",
|
|
5036
5204
|
{
|
|
5037
5205
|
className: "text-[9px] font-bold tracking-widest text-sas-muted/70 select-none",
|
|
@@ -5039,8 +5207,8 @@ function PanelMasterStrip({
|
|
|
5039
5207
|
children: "BUS"
|
|
5040
5208
|
}
|
|
5041
5209
|
),
|
|
5042
|
-
/* @__PURE__ */ (0,
|
|
5043
|
-
/* @__PURE__ */ (0,
|
|
5210
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 min-w-[8rem] flex flex-col gap-0.5", children: [
|
|
5211
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5044
5212
|
VolumeSlider,
|
|
5045
5213
|
{
|
|
5046
5214
|
value: dbToSlider(bus.volume),
|
|
@@ -5048,8 +5216,8 @@ function PanelMasterStrip({
|
|
|
5048
5216
|
disabled
|
|
5049
5217
|
}
|
|
5050
5218
|
),
|
|
5051
|
-
/* @__PURE__ */ (0,
|
|
5052
|
-
/* @__PURE__ */ (0,
|
|
5219
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-px", "data-testid": "bus-meter", children: [
|
|
5220
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5053
5221
|
LevelMeter,
|
|
5054
5222
|
{
|
|
5055
5223
|
peakDb: levels?.leftDb ?? -120,
|
|
@@ -5059,7 +5227,7 @@ function PanelMasterStrip({
|
|
|
5059
5227
|
"data-testid": "bus-meter-left"
|
|
5060
5228
|
}
|
|
5061
5229
|
),
|
|
5062
|
-
/* @__PURE__ */ (0,
|
|
5230
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5063
5231
|
LevelMeter,
|
|
5064
5232
|
{
|
|
5065
5233
|
peakDb: levels?.rightDb ?? -120,
|
|
@@ -5070,7 +5238,7 @@ function PanelMasterStrip({
|
|
|
5070
5238
|
)
|
|
5071
5239
|
] })
|
|
5072
5240
|
] }),
|
|
5073
|
-
/* @__PURE__ */ (0,
|
|
5241
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5074
5242
|
"button",
|
|
5075
5243
|
{
|
|
5076
5244
|
"data-testid": "bus-mute-button",
|
|
@@ -5081,7 +5249,7 @@ function PanelMasterStrip({
|
|
|
5081
5249
|
children: "M"
|
|
5082
5250
|
}
|
|
5083
5251
|
),
|
|
5084
|
-
/* @__PURE__ */ (0,
|
|
5252
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5085
5253
|
"button",
|
|
5086
5254
|
{
|
|
5087
5255
|
"data-testid": "bus-solo-button",
|
|
@@ -5092,14 +5260,14 @@ function PanelMasterStrip({
|
|
|
5092
5260
|
children: "S"
|
|
5093
5261
|
}
|
|
5094
5262
|
),
|
|
5095
|
-
/* @__PURE__ */ (0,
|
|
5263
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex items-center gap-1 max-w-[45%] min-w-0 overflow-x-auto", children: bus.fx.map((fx) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
5096
5264
|
"span",
|
|
5097
5265
|
{
|
|
5098
5266
|
"data-testid": `bus-fx-chip-${fx.index}`,
|
|
5099
5267
|
className: `flex items-center gap-1 px-1.5 py-0.5 rounded-sm border text-[10px] whitespace-nowrap ${fx.enabled ? "border-sas-accent/60 text-sas-accent bg-sas-accent/10" : "border-sas-border text-sas-muted/50 bg-sas-panel"}`,
|
|
5100
5268
|
title: `${fx.name}${fx.enabled ? "" : " (bypassed)"}`,
|
|
5101
5269
|
children: [
|
|
5102
|
-
/* @__PURE__ */ (0,
|
|
5270
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5103
5271
|
"button",
|
|
5104
5272
|
{
|
|
5105
5273
|
"data-testid": `bus-fx-toggle-${fx.index}`,
|
|
@@ -5110,7 +5278,7 @@ function PanelMasterStrip({
|
|
|
5110
5278
|
children: fx.enabled ? "\u25CF" : "\u25CB"
|
|
5111
5279
|
}
|
|
5112
5280
|
),
|
|
5113
|
-
onShowFxEditor ? /* @__PURE__ */ (0,
|
|
5281
|
+
onShowFxEditor ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5114
5282
|
"button",
|
|
5115
5283
|
{
|
|
5116
5284
|
"data-testid": `bus-fx-edit-${fx.index}`,
|
|
@@ -5120,8 +5288,8 @@ function PanelMasterStrip({
|
|
|
5120
5288
|
title: `Open ${fx.name} editor`,
|
|
5121
5289
|
children: fx.name
|
|
5122
5290
|
}
|
|
5123
|
-
) : /* @__PURE__ */ (0,
|
|
5124
|
-
/* @__PURE__ */ (0,
|
|
5291
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "max-w-[80px] truncate", children: fx.name }),
|
|
5292
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5125
5293
|
"button",
|
|
5126
5294
|
{
|
|
5127
5295
|
"data-testid": `bus-fx-remove-${fx.index}`,
|
|
@@ -5136,7 +5304,7 @@ function PanelMasterStrip({
|
|
|
5136
5304
|
},
|
|
5137
5305
|
`${fx.index}:${fx.pluginId}`
|
|
5138
5306
|
)) }),
|
|
5139
|
-
/* @__PURE__ */ (0,
|
|
5307
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5140
5308
|
"button",
|
|
5141
5309
|
{
|
|
5142
5310
|
"data-testid": "bus-fx-add-button",
|
|
@@ -5148,9 +5316,9 @@ function PanelMasterStrip({
|
|
|
5148
5316
|
}
|
|
5149
5317
|
)
|
|
5150
5318
|
] }),
|
|
5151
|
-
fxPickerOpen && /* @__PURE__ */ (0,
|
|
5152
|
-
/* @__PURE__ */ (0,
|
|
5153
|
-
/* @__PURE__ */ (0,
|
|
5319
|
+
fxPickerOpen && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { "data-testid": "bus-fx-picker", className: "flex flex-col gap-2 pt-1", children: [
|
|
5320
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
5321
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5154
5322
|
"input",
|
|
5155
5323
|
{
|
|
5156
5324
|
type: "text",
|
|
@@ -5160,7 +5328,7 @@ function PanelMasterStrip({
|
|
|
5160
5328
|
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
5161
5329
|
}
|
|
5162
5330
|
),
|
|
5163
|
-
onRefreshFx && /* @__PURE__ */ (0,
|
|
5331
|
+
onRefreshFx && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5164
5332
|
"button",
|
|
5165
5333
|
{
|
|
5166
5334
|
onClick: () => onRefreshFx(),
|
|
@@ -5171,8 +5339,8 @@ function PanelMasterStrip({
|
|
|
5171
5339
|
}
|
|
5172
5340
|
)
|
|
5173
5341
|
] }),
|
|
5174
|
-
fxLoading && availableFx.length === 0 ? /* @__PURE__ */ (0,
|
|
5175
|
-
filtered.map((fx) => /* @__PURE__ */ (0,
|
|
5342
|
+
fxLoading && availableFx.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
5343
|
+
filtered.map((fx) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
5176
5344
|
"button",
|
|
5177
5345
|
{
|
|
5178
5346
|
"data-testid": `bus-fx-pick-${fx.pluginId}`,
|
|
@@ -5180,13 +5348,13 @@ function PanelMasterStrip({
|
|
|
5180
5348
|
className: "flex flex-col items-start px-2 py-1.5 rounded-sm border text-left transition-colors border-sas-border bg-sas-panel-alt text-sas-muted hover:border-sas-accent hover:text-sas-accent",
|
|
5181
5349
|
title: `${fx.name} by ${fx.manufacturer} (${fx.type.toUpperCase()})`,
|
|
5182
5350
|
children: [
|
|
5183
|
-
/* @__PURE__ */ (0,
|
|
5184
|
-
/* @__PURE__ */ (0,
|
|
5351
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs font-medium truncate w-full", children: fx.name }),
|
|
5352
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: fx.manufacturer || fx.type.toUpperCase() })
|
|
5185
5353
|
]
|
|
5186
5354
|
},
|
|
5187
5355
|
fx.pluginId
|
|
5188
5356
|
)),
|
|
5189
|
-
filtered.length === 0 && /* @__PURE__ */ (0,
|
|
5357
|
+
filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "col-span-3 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No FX plugins found" })
|
|
5190
5358
|
] })
|
|
5191
5359
|
] })
|
|
5192
5360
|
]
|
|
@@ -5195,18 +5363,18 @@ function PanelMasterStrip({
|
|
|
5195
5363
|
}
|
|
5196
5364
|
|
|
5197
5365
|
// src/hooks/usePanelBus.ts
|
|
5198
|
-
var
|
|
5366
|
+
var import_react22 = require("react");
|
|
5199
5367
|
var LEVELS_POLL_MS = 66;
|
|
5200
5368
|
function usePanelBus(host, activeSceneId) {
|
|
5201
5369
|
const supported = typeof host.getPanelBusState === "function";
|
|
5202
|
-
const [bus, setBus] = (0,
|
|
5203
|
-
const [levels, setLevels] = (0,
|
|
5204
|
-
const [availableFx, setAvailableFx] = (0,
|
|
5205
|
-
const [fxLoading, setFxLoading] = (0,
|
|
5206
|
-
const [fxPickerOpen, setFxPickerOpen] = (0,
|
|
5207
|
-
const fxLoadedRef = (0,
|
|
5208
|
-
const loadSeqRef = (0,
|
|
5209
|
-
const reload = (0,
|
|
5370
|
+
const [bus, setBus] = (0, import_react22.useState)(null);
|
|
5371
|
+
const [levels, setLevels] = (0, import_react22.useState)(null);
|
|
5372
|
+
const [availableFx, setAvailableFx] = (0, import_react22.useState)([]);
|
|
5373
|
+
const [fxLoading, setFxLoading] = (0, import_react22.useState)(false);
|
|
5374
|
+
const [fxPickerOpen, setFxPickerOpen] = (0, import_react22.useState)(false);
|
|
5375
|
+
const fxLoadedRef = (0, import_react22.useRef)(false);
|
|
5376
|
+
const loadSeqRef = (0, import_react22.useRef)(0);
|
|
5377
|
+
const reload = (0, import_react22.useCallback)(async () => {
|
|
5210
5378
|
if (!supported || !activeSceneId || !host.getPanelBusState) {
|
|
5211
5379
|
setBus(null);
|
|
5212
5380
|
return;
|
|
@@ -5218,12 +5386,12 @@ function usePanelBus(host, activeSceneId) {
|
|
|
5218
5386
|
} catch {
|
|
5219
5387
|
}
|
|
5220
5388
|
}, [host, activeSceneId, supported]);
|
|
5221
|
-
(0,
|
|
5389
|
+
(0, import_react22.useEffect)(() => {
|
|
5222
5390
|
setBus(null);
|
|
5223
5391
|
setFxPickerOpen(false);
|
|
5224
5392
|
void reload();
|
|
5225
5393
|
}, [reload]);
|
|
5226
|
-
(0,
|
|
5394
|
+
(0, import_react22.useEffect)(() => {
|
|
5227
5395
|
if (!supported || !activeSceneId || !bus?.engaged || !host.getPanelBusLevels) {
|
|
5228
5396
|
setLevels(null);
|
|
5229
5397
|
return;
|
|
@@ -5245,7 +5413,7 @@ function usePanelBus(host, activeSceneId) {
|
|
|
5245
5413
|
clearInterval(id);
|
|
5246
5414
|
};
|
|
5247
5415
|
}, [supported, activeSceneId, bus?.engaged, host]);
|
|
5248
|
-
const loadFxList = (0,
|
|
5416
|
+
const loadFxList = (0, import_react22.useCallback)(
|
|
5249
5417
|
async (opts) => {
|
|
5250
5418
|
if (!supported || !host.getAvailableFx) return;
|
|
5251
5419
|
if (fxLoadedRef.current && !opts.force && !opts.rescan) return;
|
|
@@ -5261,14 +5429,14 @@ function usePanelBus(host, activeSceneId) {
|
|
|
5261
5429
|
},
|
|
5262
5430
|
[host, supported]
|
|
5263
5431
|
);
|
|
5264
|
-
const openPicker = (0,
|
|
5432
|
+
const openPicker = (0, import_react22.useCallback)(
|
|
5265
5433
|
(open) => {
|
|
5266
5434
|
setFxPickerOpen(open);
|
|
5267
5435
|
if (open) void loadFxList({});
|
|
5268
5436
|
},
|
|
5269
5437
|
[loadFxList]
|
|
5270
5438
|
);
|
|
5271
|
-
const mutate = (0,
|
|
5439
|
+
const mutate = (0, import_react22.useCallback)(
|
|
5272
5440
|
(fn) => {
|
|
5273
5441
|
if (!fn || !activeSceneId) return;
|
|
5274
5442
|
void (async () => {
|
|
@@ -5312,8 +5480,8 @@ function usePanelBus(host, activeSceneId) {
|
|
|
5312
5480
|
}
|
|
5313
5481
|
|
|
5314
5482
|
// src/components/DownloadPackButton.tsx
|
|
5315
|
-
var
|
|
5316
|
-
var
|
|
5483
|
+
var import_react23 = require("react");
|
|
5484
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
5317
5485
|
function formatSize(bytes) {
|
|
5318
5486
|
if (!bytes || bytes <= 0) return "";
|
|
5319
5487
|
const gb = bytes / 1024 ** 3;
|
|
@@ -5329,10 +5497,10 @@ var DownloadPackButton = ({
|
|
|
5329
5497
|
variant = "compact",
|
|
5330
5498
|
onDownloadComplete
|
|
5331
5499
|
}) => {
|
|
5332
|
-
const [status, setStatus] = (0,
|
|
5333
|
-
const [progress, setProgress] = (0,
|
|
5334
|
-
const [errorMessage, setErrorMessage] = (0,
|
|
5335
|
-
(0,
|
|
5500
|
+
const [status, setStatus] = (0, import_react23.useState)("idle");
|
|
5501
|
+
const [progress, setProgress] = (0, import_react23.useState)(0);
|
|
5502
|
+
const [errorMessage, setErrorMessage] = (0, import_react23.useState)(null);
|
|
5503
|
+
(0, import_react23.useEffect)(() => {
|
|
5336
5504
|
const unsub = host.onSamplePackProgress(packId, (p) => {
|
|
5337
5505
|
setStatus(p.status);
|
|
5338
5506
|
setProgress(p.progress);
|
|
@@ -5347,7 +5515,7 @@ var DownloadPackButton = ({
|
|
|
5347
5515
|
});
|
|
5348
5516
|
return unsub;
|
|
5349
5517
|
}, [host, packId, onDownloadComplete]);
|
|
5350
|
-
const handleClick = (0,
|
|
5518
|
+
const handleClick = (0, import_react23.useCallback)(async () => {
|
|
5351
5519
|
if (status !== "idle" && status !== "error") return;
|
|
5352
5520
|
try {
|
|
5353
5521
|
setStatus("downloading");
|
|
@@ -5401,8 +5569,8 @@ var DownloadPackButton = ({
|
|
|
5401
5569
|
} else {
|
|
5402
5570
|
className = `${baseClasses} text-sas-muted hover:text-sas-accent border-sas-border hover:border-sas-accent`;
|
|
5403
5571
|
}
|
|
5404
|
-
return /* @__PURE__ */ (0,
|
|
5405
|
-
/* @__PURE__ */ (0,
|
|
5572
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
5573
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5406
5574
|
"button",
|
|
5407
5575
|
{
|
|
5408
5576
|
"data-testid": `download-pack-button-${packId}`,
|
|
@@ -5413,12 +5581,12 @@ var DownloadPackButton = ({
|
|
|
5413
5581
|
children: buttonLabel
|
|
5414
5582
|
}
|
|
5415
5583
|
),
|
|
5416
|
-
variant === "large" && status === "error" && errorMessage && /* @__PURE__ */ (0,
|
|
5584
|
+
variant === "large" && status === "error" && errorMessage && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "text-xs text-sas-danger mt-2", "data-testid": `download-pack-error-${packId}`, children: errorMessage })
|
|
5417
5585
|
] });
|
|
5418
5586
|
};
|
|
5419
5587
|
|
|
5420
5588
|
// src/components/SamplePackCTACard.tsx
|
|
5421
|
-
var
|
|
5589
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
5422
5590
|
var SamplePackCTACard = ({
|
|
5423
5591
|
host,
|
|
5424
5592
|
pack,
|
|
@@ -5426,7 +5594,7 @@ var SamplePackCTACard = ({
|
|
|
5426
5594
|
onDownloadComplete
|
|
5427
5595
|
}) => {
|
|
5428
5596
|
if (status === "checking") {
|
|
5429
|
-
return /* @__PURE__ */ (0,
|
|
5597
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5430
5598
|
"div",
|
|
5431
5599
|
{
|
|
5432
5600
|
"data-testid": `sample-pack-cta-checking-${pack.packId}`,
|
|
@@ -5437,16 +5605,16 @@ var SamplePackCTACard = ({
|
|
|
5437
5605
|
}
|
|
5438
5606
|
const headline = status === "stale" ? `${pack.displayName} update available` : `${pack.displayName} not installed`;
|
|
5439
5607
|
const sublabel = status === "stale" ? `A newer version is available for download.` : pack.description;
|
|
5440
|
-
return /* @__PURE__ */ (0,
|
|
5608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
5441
5609
|
"div",
|
|
5442
5610
|
{
|
|
5443
5611
|
"data-testid": `sample-pack-cta-${pack.packId}`,
|
|
5444
5612
|
className: "flex flex-col items-center justify-center py-12 px-6 text-center",
|
|
5445
5613
|
children: [
|
|
5446
|
-
/* @__PURE__ */ (0,
|
|
5447
|
-
/* @__PURE__ */ (0,
|
|
5448
|
-
/* @__PURE__ */ (0,
|
|
5449
|
-
/* @__PURE__ */ (0,
|
|
5614
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-sm uppercase tracking-wide text-sas-muted mb-2", children: status === "stale" ? "Update available" : "Sample library not installed" }),
|
|
5615
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-base text-sas-text mb-1", children: headline }),
|
|
5616
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-xs text-sas-muted mb-6 max-w-md", children: sublabel }),
|
|
5617
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5450
5618
|
DownloadPackButton,
|
|
5451
5619
|
{
|
|
5452
5620
|
host,
|
|
@@ -5463,7 +5631,7 @@ var SamplePackCTACard = ({
|
|
|
5463
5631
|
};
|
|
5464
5632
|
|
|
5465
5633
|
// src/components/WaveformView.tsx
|
|
5466
|
-
var
|
|
5634
|
+
var import_react24 = require("react");
|
|
5467
5635
|
|
|
5468
5636
|
// src/components/waveform.ts
|
|
5469
5637
|
function computePeaks(audioBuffer, bins, targetSamples) {
|
|
@@ -5526,7 +5694,7 @@ function drawWaveform(canvas, peaks, options = {}) {
|
|
|
5526
5694
|
}
|
|
5527
5695
|
|
|
5528
5696
|
// src/components/WaveformView.tsx
|
|
5529
|
-
var
|
|
5697
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
5530
5698
|
var WaveformView = ({
|
|
5531
5699
|
host,
|
|
5532
5700
|
filePath,
|
|
@@ -5535,9 +5703,9 @@ var WaveformView = ({
|
|
|
5535
5703
|
fillStyle,
|
|
5536
5704
|
targetSamples
|
|
5537
5705
|
}) => {
|
|
5538
|
-
const canvasRef = (0,
|
|
5539
|
-
const [peaks, setPeaks] = (0,
|
|
5540
|
-
(0,
|
|
5706
|
+
const canvasRef = (0, import_react24.useRef)(null);
|
|
5707
|
+
const [peaks, setPeaks] = (0, import_react24.useState)(null);
|
|
5708
|
+
(0, import_react24.useEffect)(() => {
|
|
5541
5709
|
let cancelled = false;
|
|
5542
5710
|
let audioContext = null;
|
|
5543
5711
|
(async () => {
|
|
@@ -5563,7 +5731,7 @@ var WaveformView = ({
|
|
|
5563
5731
|
cancelled = true;
|
|
5564
5732
|
};
|
|
5565
5733
|
}, [host, filePath, bins, targetSamples]);
|
|
5566
|
-
(0,
|
|
5734
|
+
(0, import_react24.useEffect)(() => {
|
|
5567
5735
|
if (!peaks) return;
|
|
5568
5736
|
const canvas = canvasRef.current;
|
|
5569
5737
|
if (!canvas) return;
|
|
@@ -5574,7 +5742,7 @@ var WaveformView = ({
|
|
|
5574
5742
|
observer.observe(canvas);
|
|
5575
5743
|
return () => observer.disconnect();
|
|
5576
5744
|
}, [peaks, fillStyle]);
|
|
5577
|
-
return /* @__PURE__ */ (0,
|
|
5745
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
5578
5746
|
"canvas",
|
|
5579
5747
|
{
|
|
5580
5748
|
ref: canvasRef,
|
|
@@ -5585,8 +5753,8 @@ var WaveformView = ({
|
|
|
5585
5753
|
};
|
|
5586
5754
|
|
|
5587
5755
|
// src/components/ScrollingWaveform.tsx
|
|
5588
|
-
var
|
|
5589
|
-
var
|
|
5756
|
+
var import_react25 = require("react");
|
|
5757
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
5590
5758
|
var ScrollingWaveform = ({
|
|
5591
5759
|
getPeakDb,
|
|
5592
5760
|
active,
|
|
@@ -5594,11 +5762,11 @@ var ScrollingWaveform = ({
|
|
|
5594
5762
|
className,
|
|
5595
5763
|
fillStyle
|
|
5596
5764
|
}) => {
|
|
5597
|
-
const canvasRef = (0,
|
|
5598
|
-
const ringRef = (0,
|
|
5599
|
-
const writeIdxRef = (0,
|
|
5600
|
-
const rafRef = (0,
|
|
5601
|
-
(0,
|
|
5765
|
+
const canvasRef = (0, import_react25.useRef)(null);
|
|
5766
|
+
const ringRef = (0, import_react25.useRef)(new Float32Array(columns));
|
|
5767
|
+
const writeIdxRef = (0, import_react25.useRef)(0);
|
|
5768
|
+
const rafRef = (0, import_react25.useRef)(null);
|
|
5769
|
+
(0, import_react25.useEffect)(() => {
|
|
5602
5770
|
if (ringRef.current.length !== columns) {
|
|
5603
5771
|
const next = new Float32Array(columns);
|
|
5604
5772
|
const prev = ringRef.current;
|
|
@@ -5610,7 +5778,7 @@ var ScrollingWaveform = ({
|
|
|
5610
5778
|
writeIdxRef.current = writeIdxRef.current % columns;
|
|
5611
5779
|
}
|
|
5612
5780
|
}, [columns]);
|
|
5613
|
-
(0,
|
|
5781
|
+
(0, import_react25.useEffect)(() => {
|
|
5614
5782
|
if (!active) {
|
|
5615
5783
|
if (rafRef.current !== null) {
|
|
5616
5784
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -5662,7 +5830,7 @@ var ScrollingWaveform = ({
|
|
|
5662
5830
|
}
|
|
5663
5831
|
};
|
|
5664
5832
|
}, [active, getPeakDb, fillStyle]);
|
|
5665
|
-
return /* @__PURE__ */ (0,
|
|
5833
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5666
5834
|
"canvas",
|
|
5667
5835
|
{
|
|
5668
5836
|
ref: canvasRef,
|
|
@@ -5673,8 +5841,8 @@ var ScrollingWaveform = ({
|
|
|
5673
5841
|
};
|
|
5674
5842
|
|
|
5675
5843
|
// src/components/OffsetScrubber.tsx
|
|
5676
|
-
var
|
|
5677
|
-
var
|
|
5844
|
+
var import_react26 = require("react");
|
|
5845
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
5678
5846
|
var SLIDER_HEIGHT_PX = 28;
|
|
5679
5847
|
var TICK_HEIGHT_PX = 14;
|
|
5680
5848
|
var DOWNBEAT_TICK_HEIGHT_PX = 22;
|
|
@@ -5687,40 +5855,40 @@ function OffsetScrubber({
|
|
|
5687
5855
|
onChange,
|
|
5688
5856
|
disabled = false
|
|
5689
5857
|
}) {
|
|
5690
|
-
const trackRef = (0,
|
|
5691
|
-
const [draftOffset, setDraftOffset] = (0,
|
|
5692
|
-
const [isDragging, setIsDragging] = (0,
|
|
5693
|
-
(0,
|
|
5858
|
+
const trackRef = (0, import_react26.useRef)(null);
|
|
5859
|
+
const [draftOffset, setDraftOffset] = (0, import_react26.useState)(offsetSamples);
|
|
5860
|
+
const [isDragging, setIsDragging] = (0, import_react26.useState)(false);
|
|
5861
|
+
(0, import_react26.useEffect)(() => {
|
|
5694
5862
|
if (!isDragging) setDraftOffset(offsetSamples);
|
|
5695
5863
|
}, [offsetSamples, isDragging]);
|
|
5696
5864
|
const sampleRate = cuePoints?.sample_rate ?? 44100;
|
|
5697
5865
|
const detectedBpm = cuePoints?.detected_bpm ?? projectBpm;
|
|
5698
|
-
const beatsForRange = (0,
|
|
5866
|
+
const beatsForRange = (0, import_react26.useMemo)(() => {
|
|
5699
5867
|
return Math.round(60 / projectBpm * sampleRate);
|
|
5700
5868
|
}, [projectBpm, sampleRate]);
|
|
5701
5869
|
const rangeSamples = beatsForRange * meter;
|
|
5702
|
-
const sampleToFraction = (0,
|
|
5870
|
+
const sampleToFraction = (0, import_react26.useCallback)(
|
|
5703
5871
|
(sample) => {
|
|
5704
5872
|
const clamped = Math.max(-rangeSamples, Math.min(rangeSamples, sample));
|
|
5705
5873
|
return (clamped + rangeSamples) / (2 * rangeSamples);
|
|
5706
5874
|
},
|
|
5707
5875
|
[rangeSamples]
|
|
5708
5876
|
);
|
|
5709
|
-
const fractionToSample = (0,
|
|
5877
|
+
const fractionToSample = (0, import_react26.useCallback)(
|
|
5710
5878
|
(fraction) => {
|
|
5711
5879
|
const clamped = Math.max(0, Math.min(1, fraction));
|
|
5712
5880
|
return Math.round(clamped * 2 * rangeSamples - rangeSamples);
|
|
5713
5881
|
},
|
|
5714
5882
|
[rangeSamples]
|
|
5715
5883
|
);
|
|
5716
|
-
const snapTargets = (0,
|
|
5884
|
+
const snapTargets = (0, import_react26.useMemo)(() => {
|
|
5717
5885
|
if (!cuePoints || cuePoints.beats.length === 0) return [];
|
|
5718
5886
|
const downbeat = cuePoints.beats[0];
|
|
5719
5887
|
const positives = cuePoints.beats.map((b) => b - downbeat);
|
|
5720
5888
|
const negatives = positives.slice(1).map((p) => -p);
|
|
5721
5889
|
return [...negatives, ...positives].sort((a, b) => a - b);
|
|
5722
5890
|
}, [cuePoints]);
|
|
5723
|
-
const snapToBeat = (0,
|
|
5891
|
+
const snapToBeat = (0, import_react26.useCallback)(
|
|
5724
5892
|
(sample) => {
|
|
5725
5893
|
if (snapTargets.length === 0) return sample;
|
|
5726
5894
|
let best = snapTargets[0];
|
|
@@ -5736,7 +5904,7 @@ function OffsetScrubber({
|
|
|
5736
5904
|
},
|
|
5737
5905
|
[snapTargets]
|
|
5738
5906
|
);
|
|
5739
|
-
const handlePointerDown = (0,
|
|
5907
|
+
const handlePointerDown = (0, import_react26.useCallback)(
|
|
5740
5908
|
(e) => {
|
|
5741
5909
|
if (disabled || !cuePoints) return;
|
|
5742
5910
|
e.preventDefault();
|
|
@@ -5770,7 +5938,7 @@ function OffsetScrubber({
|
|
|
5770
5938
|
},
|
|
5771
5939
|
[disabled, cuePoints, fractionToSample, onChange, snapToBeat]
|
|
5772
5940
|
);
|
|
5773
|
-
const handleResetToZero = (0,
|
|
5941
|
+
const handleResetToZero = (0, import_react26.useCallback)(() => {
|
|
5774
5942
|
if (disabled) return;
|
|
5775
5943
|
setDraftOffset(0);
|
|
5776
5944
|
onChange(0);
|
|
@@ -5778,7 +5946,7 @@ function OffsetScrubber({
|
|
|
5778
5946
|
const thumbFraction = sampleToFraction(draftOffset);
|
|
5779
5947
|
const thumbLeftPct = `${(thumbFraction * 100).toFixed(2)}%`;
|
|
5780
5948
|
const bpmMismatch = cuePoints?.detected_bpm != null && Math.abs(cuePoints.detected_bpm - projectBpm) > 1;
|
|
5781
|
-
const ticks = (0,
|
|
5949
|
+
const ticks = (0, import_react26.useMemo)(() => {
|
|
5782
5950
|
if (!cuePoints) return [];
|
|
5783
5951
|
const downbeat = cuePoints.beats[0] ?? 0;
|
|
5784
5952
|
return cuePoints.beats.map((b, i) => {
|
|
@@ -5789,9 +5957,9 @@ function OffsetScrubber({
|
|
|
5789
5957
|
});
|
|
5790
5958
|
}, [cuePoints, sampleToFraction]);
|
|
5791
5959
|
const isDisabled = disabled || !cuePoints || cuePoints.beats.length === 0;
|
|
5792
|
-
return /* @__PURE__ */ (0,
|
|
5793
|
-
/* @__PURE__ */ (0,
|
|
5794
|
-
/* @__PURE__ */ (0,
|
|
5960
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { "data-testid": "offset-scrubber", className: "flex items-center gap-2 w-full", children: [
|
|
5961
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-[9px] text-sas-muted/60 uppercase tracking-wide flex-shrink-0", children: "Align" }),
|
|
5962
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
5795
5963
|
"div",
|
|
5796
5964
|
{
|
|
5797
5965
|
ref: trackRef,
|
|
@@ -5807,7 +5975,7 @@ function OffsetScrubber({
|
|
|
5807
5975
|
"aria-valuenow": draftOffset,
|
|
5808
5976
|
"aria-disabled": isDisabled,
|
|
5809
5977
|
children: [
|
|
5810
|
-
/* @__PURE__ */ (0,
|
|
5978
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
5811
5979
|
"div",
|
|
5812
5980
|
{
|
|
5813
5981
|
"aria-hidden": "true",
|
|
@@ -5815,7 +5983,7 @@ function OffsetScrubber({
|
|
|
5815
5983
|
style: { left: "50%" }
|
|
5816
5984
|
}
|
|
5817
5985
|
),
|
|
5818
|
-
ticks.map((t) => /* @__PURE__ */ (0,
|
|
5986
|
+
ticks.map((t) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
5819
5987
|
"div",
|
|
5820
5988
|
{
|
|
5821
5989
|
"data-testid": t.isDownbeat ? "offset-tick-downbeat" : "offset-tick",
|
|
@@ -5830,7 +5998,7 @@ function OffsetScrubber({
|
|
|
5830
5998
|
},
|
|
5831
5999
|
t.i
|
|
5832
6000
|
)),
|
|
5833
|
-
/* @__PURE__ */ (0,
|
|
6001
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
5834
6002
|
"div",
|
|
5835
6003
|
{
|
|
5836
6004
|
"data-testid": "offset-scrubber-thumb",
|
|
@@ -5847,7 +6015,7 @@ function OffsetScrubber({
|
|
|
5847
6015
|
]
|
|
5848
6016
|
}
|
|
5849
6017
|
),
|
|
5850
|
-
/* @__PURE__ */ (0,
|
|
6018
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
5851
6019
|
"span",
|
|
5852
6020
|
{
|
|
5853
6021
|
"data-testid": "offset-scrubber-readout",
|
|
@@ -5855,7 +6023,7 @@ function OffsetScrubber({
|
|
|
5855
6023
|
children: formatOffset(draftOffset, sampleRate)
|
|
5856
6024
|
}
|
|
5857
6025
|
),
|
|
5858
|
-
/* @__PURE__ */ (0,
|
|
6026
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
5859
6027
|
"button",
|
|
5860
6028
|
{
|
|
5861
6029
|
type: "button",
|
|
@@ -5867,7 +6035,7 @@ function OffsetScrubber({
|
|
|
5867
6035
|
children: "\u2316"
|
|
5868
6036
|
}
|
|
5869
6037
|
),
|
|
5870
|
-
bpmMismatch && /* @__PURE__ */ (0,
|
|
6038
|
+
bpmMismatch && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
5871
6039
|
"span",
|
|
5872
6040
|
{
|
|
5873
6041
|
"data-testid": "offset-bpm-mismatch",
|
|
@@ -5939,16 +6107,16 @@ function synthesizeCuePoints({
|
|
|
5939
6107
|
}
|
|
5940
6108
|
|
|
5941
6109
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
5942
|
-
var
|
|
6110
|
+
var import_react31 = require("react");
|
|
5943
6111
|
|
|
5944
6112
|
// src/hooks/useSceneState.ts
|
|
5945
|
-
var
|
|
6113
|
+
var import_react27 = require("react");
|
|
5946
6114
|
function useSceneState(activeSceneId, initialValue) {
|
|
5947
|
-
const [stateMap, setStateMap] = (0,
|
|
5948
|
-
const activeSceneIdRef = (0,
|
|
6115
|
+
const [stateMap, setStateMap] = (0, import_react27.useState)(() => /* @__PURE__ */ new Map());
|
|
6116
|
+
const activeSceneIdRef = (0, import_react27.useRef)(activeSceneId);
|
|
5949
6117
|
activeSceneIdRef.current = activeSceneId;
|
|
5950
6118
|
const currentValue = activeSceneId !== null && stateMap.has(activeSceneId) ? stateMap.get(activeSceneId) : initialValue;
|
|
5951
|
-
const setForCurrentScene = (0,
|
|
6119
|
+
const setForCurrentScene = (0, import_react27.useCallback)((value) => {
|
|
5952
6120
|
const sid = activeSceneIdRef.current;
|
|
5953
6121
|
if (sid === null) return;
|
|
5954
6122
|
setStateMap((prev) => {
|
|
@@ -5959,7 +6127,7 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
5959
6127
|
return newMap;
|
|
5960
6128
|
});
|
|
5961
6129
|
}, [initialValue]);
|
|
5962
|
-
const setForScene = (0,
|
|
6130
|
+
const setForScene = (0, import_react27.useCallback)((sceneId, value) => {
|
|
5963
6131
|
setStateMap((prev) => {
|
|
5964
6132
|
const current = prev.has(sceneId) ? prev.get(sceneId) : initialValue;
|
|
5965
6133
|
const next = typeof value === "function" ? value(current) : value;
|
|
@@ -5972,10 +6140,10 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
5972
6140
|
}
|
|
5973
6141
|
|
|
5974
6142
|
// src/hooks/useAnySolo.ts
|
|
5975
|
-
var
|
|
6143
|
+
var import_react28 = require("react");
|
|
5976
6144
|
function useAnySolo(host) {
|
|
5977
|
-
const [anySolo, setAnySolo] = (0,
|
|
5978
|
-
(0,
|
|
6145
|
+
const [anySolo, setAnySolo] = (0, import_react28.useState)(false);
|
|
6146
|
+
(0, import_react28.useEffect)(() => {
|
|
5979
6147
|
let active = true;
|
|
5980
6148
|
const refresh = () => {
|
|
5981
6149
|
host.isAnySoloActive().then((v) => {
|
|
@@ -5994,7 +6162,7 @@ function useAnySolo(host) {
|
|
|
5994
6162
|
}
|
|
5995
6163
|
|
|
5996
6164
|
// src/hooks/useSoundHistory.ts
|
|
5997
|
-
var
|
|
6165
|
+
var import_react29 = require("react");
|
|
5998
6166
|
var EMPTY = { entries: [], cursor: -1 };
|
|
5999
6167
|
function sameDescriptor(a, b) {
|
|
6000
6168
|
if (a === b) return true;
|
|
@@ -6006,14 +6174,14 @@ function sameDescriptor(a, b) {
|
|
|
6006
6174
|
}
|
|
6007
6175
|
function useSoundHistory(applySound, opts = {}) {
|
|
6008
6176
|
const max = Math.max(2, opts.max ?? 24);
|
|
6009
|
-
const applyRef = (0,
|
|
6177
|
+
const applyRef = (0, import_react29.useRef)(applySound);
|
|
6010
6178
|
applyRef.current = applySound;
|
|
6011
|
-
const onChangeRef = (0,
|
|
6179
|
+
const onChangeRef = (0, import_react29.useRef)(opts.onChange);
|
|
6012
6180
|
onChangeRef.current = opts.onChange;
|
|
6013
|
-
const dataRef = (0,
|
|
6014
|
-
const [, setVersion] = (0,
|
|
6015
|
-
const bump = (0,
|
|
6016
|
-
const commit = (0,
|
|
6181
|
+
const dataRef = (0, import_react29.useRef)({});
|
|
6182
|
+
const [, setVersion] = (0, import_react29.useState)(0);
|
|
6183
|
+
const bump = (0, import_react29.useCallback)(() => setVersion((v) => v + 1), []);
|
|
6184
|
+
const commit = (0, import_react29.useCallback)(
|
|
6017
6185
|
(trackId, next, notify) => {
|
|
6018
6186
|
dataRef.current = { ...dataRef.current, [trackId]: next };
|
|
6019
6187
|
bump();
|
|
@@ -6021,7 +6189,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
6021
6189
|
},
|
|
6022
6190
|
[bump]
|
|
6023
6191
|
);
|
|
6024
|
-
const record = (0,
|
|
6192
|
+
const record = (0, import_react29.useCallback)(
|
|
6025
6193
|
(trackId, descriptor, label) => {
|
|
6026
6194
|
const h = dataRef.current[trackId];
|
|
6027
6195
|
const current = h && h.cursor >= 0 ? h.entries[h.cursor] : void 0;
|
|
@@ -6036,7 +6204,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
6036
6204
|
},
|
|
6037
6205
|
[max, commit]
|
|
6038
6206
|
);
|
|
6039
|
-
const restoreTo = (0,
|
|
6207
|
+
const restoreTo = (0, import_react29.useCallback)(
|
|
6040
6208
|
async (trackId, index) => {
|
|
6041
6209
|
const h = dataRef.current[trackId];
|
|
6042
6210
|
if (!h || index < 0 || index >= h.entries.length || index === h.cursor) return false;
|
|
@@ -6046,7 +6214,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
6046
6214
|
},
|
|
6047
6215
|
[commit]
|
|
6048
6216
|
);
|
|
6049
|
-
const undo = (0,
|
|
6217
|
+
const undo = (0, import_react29.useCallback)(
|
|
6050
6218
|
(trackId) => {
|
|
6051
6219
|
const h = dataRef.current[trackId];
|
|
6052
6220
|
if (!h || h.cursor <= 0) return Promise.resolve(false);
|
|
@@ -6054,7 +6222,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
6054
6222
|
},
|
|
6055
6223
|
[restoreTo]
|
|
6056
6224
|
);
|
|
6057
|
-
const toggleFavorite = (0,
|
|
6225
|
+
const toggleFavorite = (0, import_react29.useCallback)(
|
|
6058
6226
|
(trackId, index) => {
|
|
6059
6227
|
const h = dataRef.current[trackId];
|
|
6060
6228
|
if (!h || index < 0 || index >= h.entries.length) return;
|
|
@@ -6063,7 +6231,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
6063
6231
|
},
|
|
6064
6232
|
[commit]
|
|
6065
6233
|
);
|
|
6066
|
-
const restore = (0,
|
|
6234
|
+
const restore = (0, import_react29.useCallback)(
|
|
6067
6235
|
(trackId, state) => {
|
|
6068
6236
|
const entries = Array.isArray(state?.entries) ? [...state.entries] : [];
|
|
6069
6237
|
const raw = typeof state?.cursor === "number" ? state.cursor : entries.length - 1;
|
|
@@ -6072,15 +6240,15 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
6072
6240
|
},
|
|
6073
6241
|
[commit]
|
|
6074
6242
|
);
|
|
6075
|
-
const list = (0,
|
|
6243
|
+
const list = (0, import_react29.useCallback)(
|
|
6076
6244
|
(trackId) => dataRef.current[trackId] ?? EMPTY,
|
|
6077
6245
|
[]
|
|
6078
6246
|
);
|
|
6079
|
-
const canUndo = (0,
|
|
6247
|
+
const canUndo = (0, import_react29.useCallback)((trackId) => {
|
|
6080
6248
|
const h = dataRef.current[trackId];
|
|
6081
6249
|
return !!h && h.cursor > 0;
|
|
6082
6250
|
}, []);
|
|
6083
|
-
const clear = (0,
|
|
6251
|
+
const clear = (0, import_react29.useCallback)(
|
|
6084
6252
|
(trackId) => {
|
|
6085
6253
|
if (dataRef.current[trackId]) {
|
|
6086
6254
|
const next = { ...dataRef.current };
|
|
@@ -6092,11 +6260,11 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
6092
6260
|
},
|
|
6093
6261
|
[bump]
|
|
6094
6262
|
);
|
|
6095
|
-
const reset = (0,
|
|
6263
|
+
const reset = (0, import_react29.useCallback)(() => {
|
|
6096
6264
|
dataRef.current = {};
|
|
6097
6265
|
bump();
|
|
6098
6266
|
}, [bump]);
|
|
6099
|
-
return (0,
|
|
6267
|
+
return (0, import_react29.useMemo)(
|
|
6100
6268
|
() => ({ record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite }),
|
|
6101
6269
|
[record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite]
|
|
6102
6270
|
);
|
|
@@ -6172,7 +6340,7 @@ function resolveTrackGroups(parsedGroups, tracks, getDbId, opts = {}) {
|
|
|
6172
6340
|
}
|
|
6173
6341
|
|
|
6174
6342
|
// src/panel-core/useTransitionOps.ts
|
|
6175
|
-
var
|
|
6343
|
+
var import_react30 = require("react");
|
|
6176
6344
|
function useTransitionOps({
|
|
6177
6345
|
host,
|
|
6178
6346
|
adapter,
|
|
@@ -6189,8 +6357,8 @@ function useTransitionOps({
|
|
|
6189
6357
|
resolvedFades
|
|
6190
6358
|
}) {
|
|
6191
6359
|
const { identity } = adapter;
|
|
6192
|
-
const appliedFadeAutomationRef = (0,
|
|
6193
|
-
const applyCrossfadeAutomation = (0,
|
|
6360
|
+
const appliedFadeAutomationRef = (0, import_react30.useRef)(/* @__PURE__ */ new Set());
|
|
6361
|
+
const applyCrossfadeAutomation = (0, import_react30.useCallback)(
|
|
6194
6362
|
async (originTrackId, targetTrackId, bars, bpm, sliderPos) => {
|
|
6195
6363
|
if (host.setTrackVolumeAutomation) {
|
|
6196
6364
|
const curves = buildCrossfadeVolumeCurves(bars, bpm, sliderPos);
|
|
@@ -6207,7 +6375,7 @@ function useTransitionOps({
|
|
|
6207
6375
|
},
|
|
6208
6376
|
[host]
|
|
6209
6377
|
);
|
|
6210
|
-
const applyFadeAutomation = (0,
|
|
6378
|
+
const applyFadeAutomation = (0, import_react30.useCallback)(
|
|
6211
6379
|
async (trackId, direction, bars, bpm, sliderPos, gesture) => {
|
|
6212
6380
|
if (!host.setTrackVolumeAutomation) return;
|
|
6213
6381
|
const points = buildFadeVolumeCurve(bars, bpm, direction, sliderPos, gesture);
|
|
@@ -6216,7 +6384,7 @@ function useTransitionOps({
|
|
|
6216
6384
|
},
|
|
6217
6385
|
[host]
|
|
6218
6386
|
);
|
|
6219
|
-
const copyTrackFx = (0,
|
|
6387
|
+
const copyTrackFx = (0, import_react30.useCallback)(
|
|
6220
6388
|
async (newTrackId, sourceDbId) => {
|
|
6221
6389
|
if (typeof host.copyTrackFxFrom !== "function") return;
|
|
6222
6390
|
try {
|
|
@@ -6233,8 +6401,8 @@ function useTransitionOps({
|
|
|
6233
6401
|
},
|
|
6234
6402
|
[host]
|
|
6235
6403
|
);
|
|
6236
|
-
const [isCreatingCrossfade, setIsCreatingCrossfade] = (0,
|
|
6237
|
-
const handleCreateCrossfade = (0,
|
|
6404
|
+
const [isCreatingCrossfade, setIsCreatingCrossfade] = (0, import_react30.useState)(false);
|
|
6405
|
+
const handleCreateCrossfade = (0, import_react30.useCallback)(
|
|
6238
6406
|
async (origin, target) => {
|
|
6239
6407
|
const scene = activeSceneId;
|
|
6240
6408
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -6364,9 +6532,9 @@ function useTransitionOps({
|
|
|
6364
6532
|
loadTracks
|
|
6365
6533
|
]
|
|
6366
6534
|
);
|
|
6367
|
-
const [isCreatingGroupFade, setIsCreatingGroupFade] = (0,
|
|
6368
|
-
const [isCreatingFade, setIsCreatingFade] = (0,
|
|
6369
|
-
const handleCreateFade = (0,
|
|
6535
|
+
const [isCreatingGroupFade, setIsCreatingGroupFade] = (0, import_react30.useState)(false);
|
|
6536
|
+
const [isCreatingFade, setIsCreatingFade] = (0, import_react30.useState)(false);
|
|
6537
|
+
const handleCreateFade = (0, import_react30.useCallback)(
|
|
6370
6538
|
async (selection, direction, gesture) => {
|
|
6371
6539
|
const scene = activeSceneId;
|
|
6372
6540
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -6477,7 +6645,7 @@ function useTransitionOps({
|
|
|
6477
6645
|
loadTracks
|
|
6478
6646
|
]
|
|
6479
6647
|
);
|
|
6480
|
-
const handleCrossfadeMute = (0,
|
|
6648
|
+
const handleCrossfadeMute = (0, import_react30.useCallback)(
|
|
6481
6649
|
(pair) => {
|
|
6482
6650
|
const newMuted = !pair.origin.runtimeState.muted;
|
|
6483
6651
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -6492,7 +6660,7 @@ function useTransitionOps({
|
|
|
6492
6660
|
},
|
|
6493
6661
|
[host, setTracks]
|
|
6494
6662
|
);
|
|
6495
|
-
const handleCrossfadeSolo = (0,
|
|
6663
|
+
const handleCrossfadeSolo = (0, import_react30.useCallback)(
|
|
6496
6664
|
(pair) => {
|
|
6497
6665
|
const newSolo = !pair.origin.runtimeState.solo;
|
|
6498
6666
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -6507,7 +6675,7 @@ function useTransitionOps({
|
|
|
6507
6675
|
},
|
|
6508
6676
|
[host, setTracks]
|
|
6509
6677
|
);
|
|
6510
|
-
const handleCrossfadeDelete = (0,
|
|
6678
|
+
const handleCrossfadeDelete = (0, import_react30.useCallback)(
|
|
6511
6679
|
async (pair) => {
|
|
6512
6680
|
try {
|
|
6513
6681
|
for (const member of [pair.origin, pair.target]) {
|
|
@@ -6533,8 +6701,8 @@ function useTransitionOps({
|
|
|
6533
6701
|
},
|
|
6534
6702
|
[host, activeSceneId, setCrossfadePairsMeta, setTracks]
|
|
6535
6703
|
);
|
|
6536
|
-
const crossfadeSliderTimers = (0,
|
|
6537
|
-
const handleCrossfadeSlider = (0,
|
|
6704
|
+
const crossfadeSliderTimers = (0, import_react30.useRef)({});
|
|
6705
|
+
const handleCrossfadeSlider = (0, import_react30.useCallback)(
|
|
6538
6706
|
(pair, pos) => {
|
|
6539
6707
|
setCrossfadePairsMeta(
|
|
6540
6708
|
(prev) => prev.map((p) => p.groupId === pair.groupId ? { ...p, sliderPos: pos } : p)
|
|
@@ -6567,7 +6735,7 @@ function useTransitionOps({
|
|
|
6567
6735
|
},
|
|
6568
6736
|
[host, activeSceneId, applyCrossfadeAutomation, setCrossfadePairsMeta]
|
|
6569
6737
|
);
|
|
6570
|
-
const handleFadeDelete = (0,
|
|
6738
|
+
const handleFadeDelete = (0, import_react30.useCallback)(
|
|
6571
6739
|
async (fade) => {
|
|
6572
6740
|
try {
|
|
6573
6741
|
await host.deleteTrack(fade.track.handle.id);
|
|
@@ -6587,8 +6755,8 @@ function useTransitionOps({
|
|
|
6587
6755
|
},
|
|
6588
6756
|
[host, activeSceneId, setFadesMeta, setTracks]
|
|
6589
6757
|
);
|
|
6590
|
-
const fadeSliderTimers = (0,
|
|
6591
|
-
const handleFadeSlider = (0,
|
|
6758
|
+
const fadeSliderTimers = (0, import_react30.useRef)({});
|
|
6759
|
+
const handleFadeSlider = (0, import_react30.useCallback)(
|
|
6592
6760
|
(fade, pos) => {
|
|
6593
6761
|
setFadesMeta(
|
|
6594
6762
|
(prev) => prev.map((f) => f.dbId === fade.dbId ? { ...f, meta: { ...f.meta, sliderPos: pos } } : f)
|
|
@@ -6618,7 +6786,7 @@ function useTransitionOps({
|
|
|
6618
6786
|
},
|
|
6619
6787
|
[host, activeSceneId, applyFadeAutomation, setFadesMeta]
|
|
6620
6788
|
);
|
|
6621
|
-
const handleCreateVerbatimGroupFade = (0,
|
|
6789
|
+
const handleCreateVerbatimGroupFade = (0, import_react30.useCallback)(
|
|
6622
6790
|
async (subject, direction) => {
|
|
6623
6791
|
const groupAdapter = adapter.transitionGroup;
|
|
6624
6792
|
if (!groupAdapter) throw new Error("This panel does not support group fades.");
|
|
@@ -6733,8 +6901,8 @@ function useTransitionOps({
|
|
|
6733
6901
|
loadTracks
|
|
6734
6902
|
]
|
|
6735
6903
|
);
|
|
6736
|
-
const groupFadeSliderTimers = (0,
|
|
6737
|
-
const handleGroupFadeSlider = (0,
|
|
6904
|
+
const groupFadeSliderTimers = (0, import_react30.useRef)({});
|
|
6905
|
+
const handleGroupFadeSlider = (0, import_react30.useCallback)(
|
|
6738
6906
|
(group, pos) => {
|
|
6739
6907
|
setFadesMeta(
|
|
6740
6908
|
(prev) => prev.map(
|
|
@@ -6773,7 +6941,7 @@ function useTransitionOps({
|
|
|
6773
6941
|
},
|
|
6774
6942
|
[host, activeSceneId, applyFadeAutomation, setFadesMeta]
|
|
6775
6943
|
);
|
|
6776
|
-
const handleGroupFadeDelete = (0,
|
|
6944
|
+
const handleGroupFadeDelete = (0, import_react30.useCallback)(
|
|
6777
6945
|
async (group) => {
|
|
6778
6946
|
const suffixes = ["fade", ...adapter.transitionGroup?.cleanupKeySuffixes ?? []];
|
|
6779
6947
|
try {
|
|
@@ -6800,8 +6968,8 @@ function useTransitionOps({
|
|
|
6800
6968
|
},
|
|
6801
6969
|
[host, adapter, activeSceneId, setFadesMeta, setTracks]
|
|
6802
6970
|
);
|
|
6803
|
-
const lastResyncKeyRef = (0,
|
|
6804
|
-
(0,
|
|
6971
|
+
const lastResyncKeyRef = (0, import_react30.useRef)("");
|
|
6972
|
+
(0, import_react30.useEffect)(() => {
|
|
6805
6973
|
if (!host.getTrackSound || resolvedCrossfadePairs.length === 0 && resolvedFades.length === 0) {
|
|
6806
6974
|
return;
|
|
6807
6975
|
}
|
|
@@ -6842,7 +7010,7 @@ function useTransitionOps({
|
|
|
6842
7010
|
cancelled = true;
|
|
6843
7011
|
};
|
|
6844
7012
|
}, [resolvedCrossfadePairs, resolvedFades, host, adapter]);
|
|
6845
|
-
(0,
|
|
7013
|
+
(0, import_react30.useEffect)(() => {
|
|
6846
7014
|
if (!host.setTrackVolumeAutomation || resolvedFades.length === 0) return;
|
|
6847
7015
|
void (async () => {
|
|
6848
7016
|
const mc = await host.getMusicalContext();
|
|
@@ -6880,7 +7048,7 @@ function useTransitionOps({
|
|
|
6880
7048
|
}
|
|
6881
7049
|
|
|
6882
7050
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
6883
|
-
var
|
|
7051
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
6884
7052
|
var EMPTY_PLACEHOLDERS = [];
|
|
6885
7053
|
function useGeneratorPanelCore({
|
|
6886
7054
|
ui,
|
|
@@ -6900,8 +7068,8 @@ function useGeneratorPanelCore({
|
|
|
6900
7068
|
} = ui;
|
|
6901
7069
|
const { identity, features } = adapter;
|
|
6902
7070
|
const logTag = identity.logTag;
|
|
6903
|
-
const adapterRef = (0,
|
|
6904
|
-
(0,
|
|
7071
|
+
const adapterRef = (0, import_react31.useRef)(adapter);
|
|
7072
|
+
(0, import_react31.useEffect)(() => {
|
|
6905
7073
|
if (adapterRef.current !== adapter) {
|
|
6906
7074
|
adapterRef.current = adapter;
|
|
6907
7075
|
console.warn(
|
|
@@ -6911,27 +7079,27 @@ function useGeneratorPanelCore({
|
|
|
6911
7079
|
}, [adapter, logTag]);
|
|
6912
7080
|
const supportsMeters = typeof host.getTrackLevels === "function";
|
|
6913
7081
|
const trackLevels = useTrackLevels(host, isExpanded);
|
|
6914
|
-
const [tracks, setTracks] = (0,
|
|
6915
|
-
const [isLoadingTracks, setIsLoadingTracks] = (0,
|
|
6916
|
-
const [importOpen, setImportOpen] = (0,
|
|
6917
|
-
const [soundImportTarget, setSoundImportTarget] = (0,
|
|
6918
|
-
const [designerView, setDesignerView] = (0,
|
|
6919
|
-
const [transitionSourceTotal, setTransitionSourceTotal] = (0,
|
|
6920
|
-
const [crossfadePairsMeta, setCrossfadePairsMeta] = (0,
|
|
6921
|
-
const [fadesMeta, setFadesMeta] = (0,
|
|
6922
|
-
const [genericGroupMetas, setGenericGroupMetas] = (0,
|
|
7082
|
+
const [tracks, setTracks] = (0, import_react31.useState)([]);
|
|
7083
|
+
const [isLoadingTracks, setIsLoadingTracks] = (0, import_react31.useState)(false);
|
|
7084
|
+
const [importOpen, setImportOpen] = (0, import_react31.useState)(false);
|
|
7085
|
+
const [soundImportTarget, setSoundImportTarget] = (0, import_react31.useState)(null);
|
|
7086
|
+
const [designerView, setDesignerView] = (0, import_react31.useState)(false);
|
|
7087
|
+
const [transitionSourceTotal, setTransitionSourceTotal] = (0, import_react31.useState)(0);
|
|
7088
|
+
const [crossfadePairsMeta, setCrossfadePairsMeta] = (0, import_react31.useState)([]);
|
|
7089
|
+
const [fadesMeta, setFadesMeta] = (0, import_react31.useState)([]);
|
|
7090
|
+
const [genericGroupMetas, setGenericGroupMetas] = (0, import_react31.useState)({});
|
|
6923
7091
|
const [isComposing, , setIsComposingForScene] = useSceneState(activeSceneId, false);
|
|
6924
7092
|
const [placeholders, , setPlaceholdersForScene] = useSceneState(
|
|
6925
7093
|
activeSceneId,
|
|
6926
7094
|
EMPTY_PLACEHOLDERS
|
|
6927
7095
|
);
|
|
6928
|
-
const saveTimeoutRefs = (0,
|
|
6929
|
-
const editLoadStartedRef = (0,
|
|
6930
|
-
const [availableInstruments, setAvailableInstruments] = (0,
|
|
6931
|
-
const [instrumentsLoading, setInstrumentsLoading] = (0,
|
|
6932
|
-
const engineToDbIdRef = (0,
|
|
6933
|
-
const tracksLoadedForSceneRef = (0,
|
|
6934
|
-
const persistSoundHistory = (0,
|
|
7096
|
+
const saveTimeoutRefs = (0, import_react31.useRef)({});
|
|
7097
|
+
const editLoadStartedRef = (0, import_react31.useRef)(/* @__PURE__ */ new Set());
|
|
7098
|
+
const [availableInstruments, setAvailableInstruments] = (0, import_react31.useState)([]);
|
|
7099
|
+
const [instrumentsLoading, setInstrumentsLoading] = (0, import_react31.useState)(false);
|
|
7100
|
+
const engineToDbIdRef = (0, import_react31.useRef)(/* @__PURE__ */ new Map());
|
|
7101
|
+
const tracksLoadedForSceneRef = (0, import_react31.useRef)(null);
|
|
7102
|
+
const persistSoundHistory = (0, import_react31.useCallback)(
|
|
6935
7103
|
(trackId, state) => {
|
|
6936
7104
|
if (!activeSceneId) return;
|
|
6937
7105
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -6951,7 +7119,7 @@ function useGeneratorPanelCore({
|
|
|
6951
7119
|
setItems: setTracks,
|
|
6952
7120
|
getId: (t) => t.handle.dbId
|
|
6953
7121
|
});
|
|
6954
|
-
const loadTracks = (0,
|
|
7122
|
+
const loadTracks = (0, import_react31.useCallback)(
|
|
6955
7123
|
async (incremental = false) => {
|
|
6956
7124
|
const sceneAtStart = activeSceneId;
|
|
6957
7125
|
if (!sceneAtStart) {
|
|
@@ -7076,18 +7244,18 @@ function useGeneratorPanelCore({
|
|
|
7076
7244
|
},
|
|
7077
7245
|
[host, activeSceneId, soundHistory, adapter, logTag]
|
|
7078
7246
|
);
|
|
7079
|
-
(0,
|
|
7247
|
+
(0, import_react31.useEffect)(() => {
|
|
7080
7248
|
loadTracks();
|
|
7081
7249
|
}, [loadTracks]);
|
|
7082
|
-
(0,
|
|
7250
|
+
(0, import_react31.useEffect)(() => {
|
|
7083
7251
|
const map = /* @__PURE__ */ new Map();
|
|
7084
7252
|
for (const t of tracks) {
|
|
7085
7253
|
map.set(t.handle.id, t.handle.dbId);
|
|
7086
7254
|
}
|
|
7087
7255
|
engineToDbIdRef.current = map;
|
|
7088
7256
|
}, [tracks]);
|
|
7089
|
-
const loadedCompletedIdsRef = (0,
|
|
7090
|
-
(0,
|
|
7257
|
+
const loadedCompletedIdsRef = (0, import_react31.useRef)(/* @__PURE__ */ new Set());
|
|
7258
|
+
(0, import_react31.useEffect)(() => {
|
|
7091
7259
|
if (placeholders.length === 0) {
|
|
7092
7260
|
loadedCompletedIdsRef.current.clear();
|
|
7093
7261
|
return;
|
|
@@ -7106,16 +7274,16 @@ function useGeneratorPanelCore({
|
|
|
7106
7274
|
loadTracks(true);
|
|
7107
7275
|
}
|
|
7108
7276
|
}, [placeholders, loadTracks, logTag]);
|
|
7109
|
-
const adoptAndLoad = (0,
|
|
7277
|
+
const adoptAndLoad = (0, import_react31.useCallback)(() => {
|
|
7110
7278
|
loadTracks(true);
|
|
7111
7279
|
}, [loadTracks]);
|
|
7112
|
-
(0,
|
|
7280
|
+
(0, import_react31.useEffect)(() => {
|
|
7113
7281
|
const unsub = host.onEngineReady(() => {
|
|
7114
7282
|
adoptAndLoad();
|
|
7115
7283
|
});
|
|
7116
7284
|
return unsub;
|
|
7117
7285
|
}, [host, adoptAndLoad]);
|
|
7118
|
-
(0,
|
|
7286
|
+
(0, import_react31.useEffect)(() => {
|
|
7119
7287
|
if (typeof host.onAfterAgentMutation !== "function") return;
|
|
7120
7288
|
let timer = null;
|
|
7121
7289
|
const unsub = host.onAfterAgentMutation(() => {
|
|
@@ -7130,13 +7298,13 @@ function useGeneratorPanelCore({
|
|
|
7130
7298
|
if (timer) clearTimeout(timer);
|
|
7131
7299
|
};
|
|
7132
7300
|
}, [host, loadTracks]);
|
|
7133
|
-
(0,
|
|
7301
|
+
(0, import_react31.useEffect)(() => {
|
|
7134
7302
|
const unsub = host.onTrackStateChange((trackId, state) => {
|
|
7135
7303
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: state } : t));
|
|
7136
7304
|
});
|
|
7137
7305
|
return unsub;
|
|
7138
7306
|
}, [host]);
|
|
7139
|
-
(0,
|
|
7307
|
+
(0, import_react31.useEffect)(() => {
|
|
7140
7308
|
if (!features.bulkComposePlaceholders) return;
|
|
7141
7309
|
console.log(`[${logTag}] Subscribing to composeProgress`);
|
|
7142
7310
|
const unsub = host.onComposeProgress((event) => {
|
|
@@ -7170,7 +7338,7 @@ function useGeneratorPanelCore({
|
|
|
7170
7338
|
});
|
|
7171
7339
|
return unsub;
|
|
7172
7340
|
}, [host, setIsComposingForScene, setPlaceholdersForScene, features.bulkComposePlaceholders, logTag]);
|
|
7173
|
-
(0,
|
|
7341
|
+
(0, import_react31.useEffect)(() => {
|
|
7174
7342
|
const refs = saveTimeoutRefs;
|
|
7175
7343
|
return () => {
|
|
7176
7344
|
for (const timeout of Object.values(refs.current)) {
|
|
@@ -7178,9 +7346,9 @@ function useGeneratorPanelCore({
|
|
|
7178
7346
|
}
|
|
7179
7347
|
};
|
|
7180
7348
|
}, []);
|
|
7181
|
-
const isAddingTrackRef = (0,
|
|
7182
|
-
const [isAddingTrack, setIsAddingTrack] = (0,
|
|
7183
|
-
const handleAddTrack = (0,
|
|
7349
|
+
const isAddingTrackRef = (0, import_react31.useRef)(false);
|
|
7350
|
+
const [isAddingTrack, setIsAddingTrack] = (0, import_react31.useState)(false);
|
|
7351
|
+
const handleAddTrack = (0, import_react31.useCallback)(async () => {
|
|
7184
7352
|
if (isAddingTrackRef.current) return;
|
|
7185
7353
|
if (!activeSceneId) {
|
|
7186
7354
|
host.showToast("warning", "Select SCENE");
|
|
@@ -7228,7 +7396,7 @@ function useGeneratorPanelCore({
|
|
|
7228
7396
|
setIsAddingTrack(false);
|
|
7229
7397
|
}
|
|
7230
7398
|
}, [host, adapter, identity, activeSceneId, isConnected, isAuthenticated, tracks.length, onExpandSelf, loadTracks, logTag]);
|
|
7231
|
-
const handlePortTrack = (0,
|
|
7399
|
+
const handlePortTrack = (0, import_react31.useCallback)(
|
|
7232
7400
|
async (sel) => {
|
|
7233
7401
|
if (!activeSceneId) {
|
|
7234
7402
|
host.showToast("warning", "Select SCENE");
|
|
@@ -7291,7 +7459,7 @@ function useGeneratorPanelCore({
|
|
|
7291
7459
|
},
|
|
7292
7460
|
[host, adapter, identity, activeSceneId, isConnected, tracks.length, loadTracks]
|
|
7293
7461
|
);
|
|
7294
|
-
const handleSoundImportPick = (0,
|
|
7462
|
+
const handleSoundImportPick = (0, import_react31.useCallback)(
|
|
7295
7463
|
async (sel) => {
|
|
7296
7464
|
const target = soundImportTarget;
|
|
7297
7465
|
if (!target || !host.getTrackSound) {
|
|
@@ -7322,8 +7490,8 @@ function useGeneratorPanelCore({
|
|
|
7322
7490
|
},
|
|
7323
7491
|
[soundImportTarget, host, adapter, identity.familyKey, soundHistory]
|
|
7324
7492
|
);
|
|
7325
|
-
const [isExportingMidi, setIsExportingMidi] = (0,
|
|
7326
|
-
const handleExportMidi = (0,
|
|
7493
|
+
const [isExportingMidi, setIsExportingMidi] = (0, import_react31.useState)(false);
|
|
7494
|
+
const handleExportMidi = (0, import_react31.useCallback)(async () => {
|
|
7327
7495
|
if (isExportingMidi) return;
|
|
7328
7496
|
setIsExportingMidi(true);
|
|
7329
7497
|
try {
|
|
@@ -7354,10 +7522,10 @@ function useGeneratorPanelCore({
|
|
|
7354
7522
|
const xfFromId = sceneContext?.transitionFromSceneId ?? null;
|
|
7355
7523
|
const xfToId = sceneContext?.transitionToSceneId ?? null;
|
|
7356
7524
|
const canCrossfade = features.transitionDesigner && sceneContext?.sceneType === "transition" && !!xfFromId && !!xfToId && !!host.listSceneFamilyTracks;
|
|
7357
|
-
(0,
|
|
7525
|
+
(0, import_react31.useEffect)(() => {
|
|
7358
7526
|
if (!canCrossfade) setDesignerView(false);
|
|
7359
7527
|
}, [canCrossfade]);
|
|
7360
|
-
(0,
|
|
7528
|
+
(0, import_react31.useEffect)(() => {
|
|
7361
7529
|
if (!canCrossfade || !xfFromId || !xfToId || !host.listSceneFamilyTracks) {
|
|
7362
7530
|
setTransitionSourceTotal(0);
|
|
7363
7531
|
return;
|
|
@@ -7373,12 +7541,12 @@ function useGeneratorPanelCore({
|
|
|
7373
7541
|
};
|
|
7374
7542
|
}, [canCrossfade, xfFromId, xfToId, host]);
|
|
7375
7543
|
const transitionDone = crossfadePairsMeta.length * 2 + fadesMeta.length;
|
|
7376
|
-
(0,
|
|
7544
|
+
(0, import_react31.useEffect)(() => {
|
|
7377
7545
|
if (!onHeaderContent) return;
|
|
7378
7546
|
const addDisabled = needsContract || !isConnected || !activeSceneId || tracks.length >= identity.maxTracks || isAddingTrack;
|
|
7379
7547
|
onHeaderContent(
|
|
7380
|
-
/* @__PURE__ */ (0,
|
|
7381
|
-
features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */ (0,
|
|
7548
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex gap-1 items-center", children: [
|
|
7549
|
+
features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
7382
7550
|
"button",
|
|
7383
7551
|
{
|
|
7384
7552
|
"data-testid": `import-from-scene-${identity.familyKey}-button`,
|
|
@@ -7392,7 +7560,7 @@ function useGeneratorPanelCore({
|
|
|
7392
7560
|
children: identity.importTrackLabel ?? "Import Track"
|
|
7393
7561
|
}
|
|
7394
7562
|
),
|
|
7395
|
-
(!canCrossfade || !designerView) && /* @__PURE__ */ (0,
|
|
7563
|
+
(!canCrossfade || !designerView) && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
7396
7564
|
"button",
|
|
7397
7565
|
{
|
|
7398
7566
|
"data-testid": `add-${identity.familyKey}-track-button`,
|
|
@@ -7408,7 +7576,7 @@ function useGeneratorPanelCore({
|
|
|
7408
7576
|
children: identity.addTrackLabel ?? "Add Track"
|
|
7409
7577
|
}
|
|
7410
7578
|
),
|
|
7411
|
-
canCrossfade && /* @__PURE__ */ (0,
|
|
7579
|
+
canCrossfade && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
7412
7580
|
"button",
|
|
7413
7581
|
{
|
|
7414
7582
|
"data-testid": `${identity.familyKey}-view-toggle`,
|
|
@@ -7427,7 +7595,7 @@ function useGeneratorPanelCore({
|
|
|
7427
7595
|
title: designerView ? "Back to the track list" : "Open the transition designer",
|
|
7428
7596
|
className: "relative overflow-hidden px-2 py-0.5 text-[10px] font-medium rounded-sm border border-sas-accent/40 text-sas-accent transition-colors hover:border-sas-accent disabled:opacity-50",
|
|
7429
7597
|
children: [
|
|
7430
|
-
transitionSourceTotal > 0 && /* @__PURE__ */ (0,
|
|
7598
|
+
transitionSourceTotal > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
7431
7599
|
"span",
|
|
7432
7600
|
{
|
|
7433
7601
|
className: "absolute inset-y-0 left-0 bg-sas-accent/25",
|
|
@@ -7435,7 +7603,7 @@ function useGeneratorPanelCore({
|
|
|
7435
7603
|
"aria-hidden": true
|
|
7436
7604
|
}
|
|
7437
7605
|
),
|
|
7438
|
-
/* @__PURE__ */ (0,
|
|
7606
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "relative", children: [
|
|
7439
7607
|
"\u21C4 ",
|
|
7440
7608
|
designerView ? "Transition" : "Tracks",
|
|
7441
7609
|
transitionSourceTotal > 0 ? ` ${transitionDone}/${transitionSourceTotal}` : ""
|
|
@@ -7466,7 +7634,7 @@ function useGeneratorPanelCore({
|
|
|
7466
7634
|
identity,
|
|
7467
7635
|
features.importTracks
|
|
7468
7636
|
]);
|
|
7469
|
-
(0,
|
|
7637
|
+
(0, import_react31.useEffect)(() => {
|
|
7470
7638
|
if (!onLoading) return;
|
|
7471
7639
|
const anyGenerating = tracks.some((t) => t.isGenerating);
|
|
7472
7640
|
onLoading(isLoadingTracks || anyGenerating || isBulkActive);
|
|
@@ -7474,7 +7642,7 @@ function useGeneratorPanelCore({
|
|
|
7474
7642
|
onLoading(false);
|
|
7475
7643
|
};
|
|
7476
7644
|
}, [onLoading, isLoadingTracks, tracks, isBulkActive]);
|
|
7477
|
-
const handleDeleteTrack = (0,
|
|
7645
|
+
const handleDeleteTrack = (0, import_react31.useCallback)(
|
|
7478
7646
|
async (trackId) => {
|
|
7479
7647
|
try {
|
|
7480
7648
|
await host.deleteTrack(trackId);
|
|
@@ -7490,7 +7658,7 @@ function useGeneratorPanelCore({
|
|
|
7490
7658
|
},
|
|
7491
7659
|
[host, activeSceneId]
|
|
7492
7660
|
);
|
|
7493
|
-
const handlePromptChange = (0,
|
|
7661
|
+
const handlePromptChange = (0, import_react31.useCallback)(
|
|
7494
7662
|
(trackId, prompt) => {
|
|
7495
7663
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, prompt } : t));
|
|
7496
7664
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -7506,7 +7674,7 @@ function useGeneratorPanelCore({
|
|
|
7506
7674
|
},
|
|
7507
7675
|
[host, activeSceneId]
|
|
7508
7676
|
);
|
|
7509
|
-
const resolvedGenericGroups = (0,
|
|
7677
|
+
const resolvedGenericGroups = (0, import_react31.useMemo)(() => {
|
|
7510
7678
|
const out = {};
|
|
7511
7679
|
for (const ext of adapter.groupExtensions ?? []) {
|
|
7512
7680
|
out[ext.metaKey] = resolveTrackGroups(
|
|
@@ -7520,18 +7688,18 @@ function useGeneratorPanelCore({
|
|
|
7520
7688
|
}
|
|
7521
7689
|
return out;
|
|
7522
7690
|
}, [adapter, genericGroupMetas, tracks]);
|
|
7523
|
-
const genericGroupMemberDbIds = (0,
|
|
7691
|
+
const genericGroupMemberDbIds = (0, import_react31.useMemo)(() => {
|
|
7524
7692
|
const s = /* @__PURE__ */ new Set();
|
|
7525
7693
|
for (const r of Object.values(resolvedGenericGroups)) {
|
|
7526
7694
|
for (const dbId of r.memberDbIds) s.add(dbId);
|
|
7527
7695
|
}
|
|
7528
7696
|
return s;
|
|
7529
7697
|
}, [resolvedGenericGroups]);
|
|
7530
|
-
const engineToDbId = (0,
|
|
7698
|
+
const engineToDbId = (0, import_react31.useCallback)(
|
|
7531
7699
|
(trackId) => engineToDbIdRef.current.get(trackId) ?? trackId,
|
|
7532
7700
|
[]
|
|
7533
7701
|
);
|
|
7534
|
-
const updateTrack = (0,
|
|
7702
|
+
const updateTrack = (0, import_react31.useCallback)(
|
|
7535
7703
|
(trackId, patch) => {
|
|
7536
7704
|
setTracks(
|
|
7537
7705
|
(prev) => prev.map(
|
|
@@ -7541,18 +7709,18 @@ function useGeneratorPanelCore({
|
|
|
7541
7709
|
},
|
|
7542
7710
|
[]
|
|
7543
7711
|
);
|
|
7544
|
-
const markEditLoaded = (0,
|
|
7712
|
+
const markEditLoaded = (0, import_react31.useCallback)((trackId) => {
|
|
7545
7713
|
editLoadStartedRef.current.add(trackId);
|
|
7546
7714
|
}, []);
|
|
7547
|
-
const tracksRef = (0,
|
|
7548
|
-
(0,
|
|
7715
|
+
const tracksRef = (0, import_react31.useRef)(tracks);
|
|
7716
|
+
(0, import_react31.useEffect)(() => {
|
|
7549
7717
|
tracksRef.current = tracks;
|
|
7550
7718
|
}, [tracks]);
|
|
7551
|
-
const resolvedGenericGroupsRef = (0,
|
|
7552
|
-
(0,
|
|
7719
|
+
const resolvedGenericGroupsRef = (0, import_react31.useRef)(resolvedGenericGroups);
|
|
7720
|
+
(0, import_react31.useEffect)(() => {
|
|
7553
7721
|
resolvedGenericGroupsRef.current = resolvedGenericGroups;
|
|
7554
7722
|
}, [resolvedGenericGroups]);
|
|
7555
|
-
const makeServices = (0,
|
|
7723
|
+
const makeServices = (0, import_react31.useCallback)(() => {
|
|
7556
7724
|
return {
|
|
7557
7725
|
host,
|
|
7558
7726
|
activeSceneId,
|
|
@@ -7571,7 +7739,7 @@ function useGeneratorPanelCore({
|
|
|
7571
7739
|
resolvedGroups: (metaKey) => resolvedGenericGroupsRef.current[metaKey]?.resolved ?? []
|
|
7572
7740
|
};
|
|
7573
7741
|
}, [host, activeSceneId, updateTrack, loadTracks, soundHistory, engineToDbId, markEditLoaded, identity, adapter]);
|
|
7574
|
-
const handleGenerate = (0,
|
|
7742
|
+
const handleGenerate = (0, import_react31.useCallback)(
|
|
7575
7743
|
async (trackId) => {
|
|
7576
7744
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
7577
7745
|
if (!track || !track.prompt.trim()) return;
|
|
@@ -7598,7 +7766,7 @@ function useGeneratorPanelCore({
|
|
|
7598
7766
|
},
|
|
7599
7767
|
[host, adapter, tracks, isAuthenticated, makeServices]
|
|
7600
7768
|
);
|
|
7601
|
-
const handleMuteToggle = (0,
|
|
7769
|
+
const handleMuteToggle = (0, import_react31.useCallback)(
|
|
7602
7770
|
(trackId) => {
|
|
7603
7771
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
7604
7772
|
if (!track) return;
|
|
@@ -7618,7 +7786,7 @@ function useGeneratorPanelCore({
|
|
|
7618
7786
|
},
|
|
7619
7787
|
[host, tracks]
|
|
7620
7788
|
);
|
|
7621
|
-
const handleSoloToggle = (0,
|
|
7789
|
+
const handleSoloToggle = (0, import_react31.useCallback)(
|
|
7622
7790
|
(trackId) => {
|
|
7623
7791
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
7624
7792
|
if (!track) return;
|
|
@@ -7638,7 +7806,7 @@ function useGeneratorPanelCore({
|
|
|
7638
7806
|
},
|
|
7639
7807
|
[host, tracks]
|
|
7640
7808
|
);
|
|
7641
|
-
const handleVolumeChange = (0,
|
|
7809
|
+
const handleVolumeChange = (0, import_react31.useCallback)(
|
|
7642
7810
|
(trackId, volume) => {
|
|
7643
7811
|
setTracks(
|
|
7644
7812
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, volume } } : t)
|
|
@@ -7648,7 +7816,7 @@ function useGeneratorPanelCore({
|
|
|
7648
7816
|
},
|
|
7649
7817
|
[host]
|
|
7650
7818
|
);
|
|
7651
|
-
const handlePanChange = (0,
|
|
7819
|
+
const handlePanChange = (0, import_react31.useCallback)(
|
|
7652
7820
|
(trackId, pan) => {
|
|
7653
7821
|
setTracks(
|
|
7654
7822
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, pan } } : t)
|
|
@@ -7658,7 +7826,7 @@ function useGeneratorPanelCore({
|
|
|
7658
7826
|
},
|
|
7659
7827
|
[host]
|
|
7660
7828
|
);
|
|
7661
|
-
const handleShuffle = (0,
|
|
7829
|
+
const handleShuffle = (0, import_react31.useCallback)(
|
|
7662
7830
|
async (trackId) => {
|
|
7663
7831
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
7664
7832
|
if (!track) return;
|
|
@@ -7700,7 +7868,7 @@ function useGeneratorPanelCore({
|
|
|
7700
7868
|
},
|
|
7701
7869
|
[host, adapter, tracks, soundHistory, logTag]
|
|
7702
7870
|
);
|
|
7703
|
-
const handleCopy = (0,
|
|
7871
|
+
const handleCopy = (0, import_react31.useCallback)(
|
|
7704
7872
|
async (trackId) => {
|
|
7705
7873
|
try {
|
|
7706
7874
|
const newHandle = await host.duplicateTrack(trackId);
|
|
@@ -7713,7 +7881,7 @@ function useGeneratorPanelCore({
|
|
|
7713
7881
|
},
|
|
7714
7882
|
[host, loadTracks]
|
|
7715
7883
|
);
|
|
7716
|
-
const handleFxToggle = (0,
|
|
7884
|
+
const handleFxToggle = (0, import_react31.useCallback)(
|
|
7717
7885
|
(trackId, category, enabled) => {
|
|
7718
7886
|
setTracks(
|
|
7719
7887
|
(prev) => prev.map(
|
|
@@ -7736,7 +7904,7 @@ function useGeneratorPanelCore({
|
|
|
7736
7904
|
},
|
|
7737
7905
|
[host]
|
|
7738
7906
|
);
|
|
7739
|
-
const handleFxPresetChange = (0,
|
|
7907
|
+
const handleFxPresetChange = (0, import_react31.useCallback)(
|
|
7740
7908
|
(trackId, category, presetIndex) => {
|
|
7741
7909
|
setTracks(
|
|
7742
7910
|
(prev) => prev.map(
|
|
@@ -7762,7 +7930,7 @@ function useGeneratorPanelCore({
|
|
|
7762
7930
|
},
|
|
7763
7931
|
[host]
|
|
7764
7932
|
);
|
|
7765
|
-
const handleFxDryWetChange = (0,
|
|
7933
|
+
const handleFxDryWetChange = (0, import_react31.useCallback)(
|
|
7766
7934
|
(trackId, category, value) => {
|
|
7767
7935
|
setTracks(
|
|
7768
7936
|
(prev) => prev.map(
|
|
@@ -7774,7 +7942,7 @@ function useGeneratorPanelCore({
|
|
|
7774
7942
|
},
|
|
7775
7943
|
[host]
|
|
7776
7944
|
);
|
|
7777
|
-
const toggleFxDrawer = (0,
|
|
7945
|
+
const toggleFxDrawer = (0, import_react31.useCallback)(
|
|
7778
7946
|
(trackId) => {
|
|
7779
7947
|
setTracks(
|
|
7780
7948
|
(prev) => prev.map((t) => {
|
|
@@ -7796,7 +7964,7 @@ function useGeneratorPanelCore({
|
|
|
7796
7964
|
},
|
|
7797
7965
|
[host, tracks]
|
|
7798
7966
|
);
|
|
7799
|
-
const loadEditNotes = (0,
|
|
7967
|
+
const loadEditNotes = (0, import_react31.useCallback)(
|
|
7800
7968
|
async (trackId) => {
|
|
7801
7969
|
try {
|
|
7802
7970
|
const mc = await host.getMusicalContext();
|
|
@@ -7814,7 +7982,7 @@ function useGeneratorPanelCore({
|
|
|
7814
7982
|
},
|
|
7815
7983
|
[host, logTag]
|
|
7816
7984
|
);
|
|
7817
|
-
const handleNotesChange = (0,
|
|
7985
|
+
const handleNotesChange = (0, import_react31.useCallback)(
|
|
7818
7986
|
(trackId, notes) => {
|
|
7819
7987
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editNotes: notes } : t));
|
|
7820
7988
|
const key = `edit:${trackId}`;
|
|
@@ -7844,7 +8012,7 @@ function useGeneratorPanelCore({
|
|
|
7844
8012
|
},
|
|
7845
8013
|
[host]
|
|
7846
8014
|
);
|
|
7847
|
-
const handleTabChange = (0,
|
|
8015
|
+
const handleTabChange = (0, import_react31.useCallback)(
|
|
7848
8016
|
(trackId, tab) => {
|
|
7849
8017
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, drawerOpen: true, drawerTab: tab } : t));
|
|
7850
8018
|
if (tab === "fx") {
|
|
@@ -7869,10 +8037,10 @@ function useGeneratorPanelCore({
|
|
|
7869
8037
|
},
|
|
7870
8038
|
[host, availableInstruments.length, instrumentsLoading, loadEditNotes]
|
|
7871
8039
|
);
|
|
7872
|
-
const handleProgressChange = (0,
|
|
8040
|
+
const handleProgressChange = (0, import_react31.useCallback)((trackId, pct) => {
|
|
7873
8041
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, generationProgress: pct } : t));
|
|
7874
8042
|
}, []);
|
|
7875
|
-
const handleToggleDrawer = (0,
|
|
8043
|
+
const handleToggleDrawer = (0, import_react31.useCallback)((trackId) => {
|
|
7876
8044
|
setTracks(
|
|
7877
8045
|
(prev) => prev.map((t) => {
|
|
7878
8046
|
if (t.handle.id !== trackId) return t;
|
|
@@ -7881,7 +8049,7 @@ function useGeneratorPanelCore({
|
|
|
7881
8049
|
})
|
|
7882
8050
|
);
|
|
7883
8051
|
}, []);
|
|
7884
|
-
const handleInstrumentSelect = (0,
|
|
8052
|
+
const handleInstrumentSelect = (0, import_react31.useCallback)(
|
|
7885
8053
|
async (trackId, pluginId) => {
|
|
7886
8054
|
const isDefaultInstrument = pluginId === (identity.defaultInstrumentPluginId ?? "Surge XT");
|
|
7887
8055
|
if (isDefaultInstrument) {
|
|
@@ -7934,7 +8102,7 @@ function useGeneratorPanelCore({
|
|
|
7934
8102
|
},
|
|
7935
8103
|
[host, identity.defaultInstrumentPluginId, logTag]
|
|
7936
8104
|
);
|
|
7937
|
-
const handleShowEditor = (0,
|
|
8105
|
+
const handleShowEditor = (0, import_react31.useCallback)(
|
|
7938
8106
|
async (trackId) => {
|
|
7939
8107
|
try {
|
|
7940
8108
|
await host.showInstrumentEditor(trackId);
|
|
@@ -7945,12 +8113,12 @@ function useGeneratorPanelCore({
|
|
|
7945
8113
|
},
|
|
7946
8114
|
[host]
|
|
7947
8115
|
);
|
|
7948
|
-
const handleBackToInstruments = (0,
|
|
8116
|
+
const handleBackToInstruments = (0, import_react31.useCallback)((trackId) => {
|
|
7949
8117
|
setTracks(
|
|
7950
8118
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editorStage: false } : t)
|
|
7951
8119
|
);
|
|
7952
8120
|
}, []);
|
|
7953
|
-
const handleRefreshInstruments = (0,
|
|
8121
|
+
const handleRefreshInstruments = (0, import_react31.useCallback)(() => {
|
|
7954
8122
|
setInstrumentsLoading(true);
|
|
7955
8123
|
host.getAvailableInstruments().then((instruments) => {
|
|
7956
8124
|
setAvailableInstruments(instruments);
|
|
@@ -7959,13 +8127,13 @@ function useGeneratorPanelCore({
|
|
|
7959
8127
|
setInstrumentsLoading(false);
|
|
7960
8128
|
});
|
|
7961
8129
|
}, [host]);
|
|
7962
|
-
const onAuditionNote = (0,
|
|
8130
|
+
const onAuditionNote = (0, import_react31.useCallback)(
|
|
7963
8131
|
(trackId, pitch, velocity, ms) => {
|
|
7964
8132
|
void host.auditionNote(trackId, pitch, velocity, ms);
|
|
7965
8133
|
},
|
|
7966
8134
|
[host]
|
|
7967
8135
|
);
|
|
7968
|
-
const { resolvedCrossfadePairs, crossfadeMemberDbIds } = (0,
|
|
8136
|
+
const { resolvedCrossfadePairs, crossfadeMemberDbIds } = (0, import_react31.useMemo)(() => {
|
|
7969
8137
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
7970
8138
|
const pairs = [];
|
|
7971
8139
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -7980,7 +8148,7 @@ function useGeneratorPanelCore({
|
|
|
7980
8148
|
}
|
|
7981
8149
|
return { resolvedCrossfadePairs: pairs, crossfadeMemberDbIds: members };
|
|
7982
8150
|
}, [tracks, crossfadePairsMeta]);
|
|
7983
|
-
const { resolvedFades, fadeMemberDbIds } = (0,
|
|
8151
|
+
const { resolvedFades, fadeMemberDbIds } = (0, import_react31.useMemo)(() => {
|
|
7984
8152
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
7985
8153
|
const list = [];
|
|
7986
8154
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -7993,7 +8161,7 @@ function useGeneratorPanelCore({
|
|
|
7993
8161
|
}
|
|
7994
8162
|
return { resolvedFades: list, fadeMemberDbIds: members };
|
|
7995
8163
|
}, [tracks, fadesMeta]);
|
|
7996
|
-
const { singles: resolvedSingleFades, groups: resolvedGroupFades } = (0,
|
|
8164
|
+
const { singles: resolvedSingleFades, groups: resolvedGroupFades } = (0, import_react31.useMemo)(
|
|
7997
8165
|
() => splitFadeEntries(resolvedFades),
|
|
7998
8166
|
[resolvedFades]
|
|
7999
8167
|
);
|
|
@@ -8012,7 +8180,7 @@ function useGeneratorPanelCore({
|
|
|
8012
8180
|
resolvedCrossfadePairs,
|
|
8013
8181
|
resolvedFades
|
|
8014
8182
|
});
|
|
8015
|
-
const setGroupMute = (0,
|
|
8183
|
+
const setGroupMute = (0, import_react31.useCallback)(
|
|
8016
8184
|
(trackIds, muted) => {
|
|
8017
8185
|
for (const id of trackIds) {
|
|
8018
8186
|
setTracks(
|
|
@@ -8024,7 +8192,7 @@ function useGeneratorPanelCore({
|
|
|
8024
8192
|
},
|
|
8025
8193
|
[host]
|
|
8026
8194
|
);
|
|
8027
|
-
const setGroupSolo = (0,
|
|
8195
|
+
const setGroupSolo = (0, import_react31.useCallback)(
|
|
8028
8196
|
(trackIds, solo) => {
|
|
8029
8197
|
for (const id of trackIds) {
|
|
8030
8198
|
setTracks(
|
|
@@ -8036,7 +8204,7 @@ function useGeneratorPanelCore({
|
|
|
8036
8204
|
},
|
|
8037
8205
|
[host]
|
|
8038
8206
|
);
|
|
8039
|
-
const deleteGroup = (0,
|
|
8207
|
+
const deleteGroup = (0, import_react31.useCallback)(
|
|
8040
8208
|
async (members, cleanupKeySuffixes) => {
|
|
8041
8209
|
for (const member of members) {
|
|
8042
8210
|
try {
|
|
@@ -8056,7 +8224,7 @@ function useGeneratorPanelCore({
|
|
|
8056
8224
|
},
|
|
8057
8225
|
[host, activeSceneId, loadTracks]
|
|
8058
8226
|
);
|
|
8059
|
-
const handlers = (0,
|
|
8227
|
+
const handlers = (0, import_react31.useMemo)(
|
|
8060
8228
|
() => ({
|
|
8061
8229
|
promptChange: handlePromptChange,
|
|
8062
8230
|
generate: (trackId) => {
|
|
@@ -8172,8 +8340,8 @@ function useGeneratorPanelCore({
|
|
|
8172
8340
|
}
|
|
8173
8341
|
|
|
8174
8342
|
// src/panel-core/GeneratorPanelShell.tsx
|
|
8175
|
-
var
|
|
8176
|
-
var
|
|
8343
|
+
var import_react32 = __toESM(require("react"));
|
|
8344
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
8177
8345
|
function GeneratorPanelShell({ core, slots }) {
|
|
8178
8346
|
const {
|
|
8179
8347
|
ui,
|
|
@@ -8229,7 +8397,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8229
8397
|
const { host, activeSceneId, isAuthenticated, sceneContext, onSelectScene, onOpenContract } = ui;
|
|
8230
8398
|
const panelBus = usePanelBus(host, activeSceneId);
|
|
8231
8399
|
const { identity, features } = adapter;
|
|
8232
|
-
const buildRowProps = (0,
|
|
8400
|
+
const buildRowProps = (0, import_react32.useCallback)(
|
|
8233
8401
|
(track, drag) => {
|
|
8234
8402
|
const id = track.handle.id;
|
|
8235
8403
|
const pickerProps = features.instrumentPicker ? {
|
|
@@ -8329,12 +8497,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8329
8497
|
]
|
|
8330
8498
|
);
|
|
8331
8499
|
if (!activeSceneId) {
|
|
8332
|
-
return /* @__PURE__ */ (0,
|
|
8500
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8333
8501
|
"div",
|
|
8334
8502
|
{
|
|
8335
8503
|
"data-testid": `no-scene-placeholder-${identity.familyKey}`,
|
|
8336
8504
|
className: "flex items-center justify-center py-8",
|
|
8337
|
-
children: /* @__PURE__ */ (0,
|
|
8505
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8338
8506
|
"button",
|
|
8339
8507
|
{
|
|
8340
8508
|
onClick: () => onSelectScene?.(),
|
|
@@ -8346,12 +8514,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8346
8514
|
);
|
|
8347
8515
|
}
|
|
8348
8516
|
if (!sceneContext?.hasContract) {
|
|
8349
|
-
return /* @__PURE__ */ (0,
|
|
8517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8350
8518
|
"div",
|
|
8351
8519
|
{
|
|
8352
8520
|
"data-testid": `no-contract-placeholder-${identity.familyKey}`,
|
|
8353
8521
|
className: "flex items-center justify-center py-8",
|
|
8354
|
-
children: /* @__PURE__ */ (0,
|
|
8522
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8355
8523
|
"button",
|
|
8356
8524
|
{
|
|
8357
8525
|
onClick: () => onOpenContract?.(),
|
|
@@ -8363,7 +8531,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8363
8531
|
);
|
|
8364
8532
|
}
|
|
8365
8533
|
if (features.bulkComposePlaceholders && isComposing) {
|
|
8366
|
-
return /* @__PURE__ */ (0,
|
|
8534
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SorceryProgressBar, { isLoading: true, statusText: "COMPOSING...", heightClass: "h-10" }) });
|
|
8367
8535
|
}
|
|
8368
8536
|
const activePlaceholders = features.bulkComposePlaceholders ? placeholders : [];
|
|
8369
8537
|
if (activePlaceholders.length > 0) {
|
|
@@ -8374,18 +8542,18 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8374
8542
|
tracksByDbId.set(t.handle.id, t);
|
|
8375
8543
|
}
|
|
8376
8544
|
}
|
|
8377
|
-
return /* @__PURE__ */ (0,
|
|
8545
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: activePlaceholders.map((ph) => {
|
|
8378
8546
|
const loadedTrack = ph.status === "completed" ? tracksByDbId.get(ph.id) : void 0;
|
|
8379
8547
|
if (loadedTrack) {
|
|
8380
|
-
return /* @__PURE__ */ (0,
|
|
8548
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TrackRow, { ...buildRowProps(loadedTrack) }, ph.id);
|
|
8381
8549
|
}
|
|
8382
|
-
return /* @__PURE__ */ (0,
|
|
8550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8383
8551
|
"div",
|
|
8384
8552
|
{
|
|
8385
8553
|
"data-testid": "bulk-placeholder-track",
|
|
8386
8554
|
className: "relative rounded-sm border w-full overflow-hidden border-sas-border bg-sas-panel-alt",
|
|
8387
8555
|
style: { borderLeftColor: identity.placeholderAccentColor, borderLeftWidth: "3px" },
|
|
8388
|
-
children: /* @__PURE__ */ (0,
|
|
8556
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SorceryProgressBar, { isLoading: true, statusText: "CONJURING MIDI...", heightClass: "h-10" })
|
|
8389
8557
|
},
|
|
8390
8558
|
ph.id
|
|
8391
8559
|
);
|
|
@@ -8397,13 +8565,13 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8397
8565
|
supportsMeters,
|
|
8398
8566
|
levels: supportsMeters ? trackLevels : void 0,
|
|
8399
8567
|
handlers,
|
|
8400
|
-
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */ (0,
|
|
8568
|
+
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TrackRow, { ...{ ...buildRowProps(track, drag), ...overrides ?? {} } }, track.handle.id),
|
|
8401
8569
|
setGroupMute,
|
|
8402
8570
|
setGroupSolo,
|
|
8403
8571
|
deleteGroup
|
|
8404
8572
|
};
|
|
8405
|
-
return /* @__PURE__ */ (0,
|
|
8406
|
-
features.importTracks && host.listImportableTracks && /* @__PURE__ */ (0,
|
|
8573
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: [
|
|
8574
|
+
features.importTracks && host.listImportableTracks && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8407
8575
|
ImportTrackModal,
|
|
8408
8576
|
{
|
|
8409
8577
|
host,
|
|
@@ -8416,7 +8584,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8416
8584
|
testIdPrefix: `${identity.familyKey}-import`
|
|
8417
8585
|
}
|
|
8418
8586
|
),
|
|
8419
|
-
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ (0,
|
|
8587
|
+
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8420
8588
|
ImportTrackModal,
|
|
8421
8589
|
{
|
|
8422
8590
|
host,
|
|
@@ -8431,7 +8599,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8431
8599
|
}
|
|
8432
8600
|
),
|
|
8433
8601
|
slots?.modals,
|
|
8434
|
-
canCrossfade && xfFromId && xfToId && /* @__PURE__ */ (0,
|
|
8602
|
+
canCrossfade && xfFromId && xfToId && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: designerView ? "contents" : "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8435
8603
|
TransitionDesigner,
|
|
8436
8604
|
{
|
|
8437
8605
|
host,
|
|
@@ -8450,8 +8618,8 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8450
8618
|
testIdPrefix: `${identity.familyKey}-transition-designer`
|
|
8451
8619
|
}
|
|
8452
8620
|
) }),
|
|
8453
|
-
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */ (0,
|
|
8454
|
-
panelBus.supported && panelBus.bus && /* @__PURE__ */ (0,
|
|
8621
|
+
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "text-sas-muted text-xs text-center py-4", children: "Loading tracks..." }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
8622
|
+
panelBus.supported && panelBus.bus && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8455
8623
|
PanelMasterStrip,
|
|
8456
8624
|
{
|
|
8457
8625
|
bus: panelBus.bus,
|
|
@@ -8472,7 +8640,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8472
8640
|
}
|
|
8473
8641
|
),
|
|
8474
8642
|
slots?.beforeRows,
|
|
8475
|
-
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ (0,
|
|
8643
|
+
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8476
8644
|
CrossfadeTrackRow,
|
|
8477
8645
|
{
|
|
8478
8646
|
accentColor: identity.transitionAccentColor,
|
|
@@ -8509,7 +8677,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8509
8677
|
},
|
|
8510
8678
|
pair.groupId
|
|
8511
8679
|
)),
|
|
8512
|
-
resolvedSingleFades.map((fade) => /* @__PURE__ */ (0,
|
|
8680
|
+
resolvedSingleFades.map((fade) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8513
8681
|
FadeTrackRow,
|
|
8514
8682
|
{
|
|
8515
8683
|
accentColor: identity.transitionAccentColor,
|
|
@@ -8534,7 +8702,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8534
8702
|
},
|
|
8535
8703
|
fade.dbId
|
|
8536
8704
|
)),
|
|
8537
|
-
resolvedGroupFades.map((group) => /* @__PURE__ */ (0,
|
|
8705
|
+
resolvedGroupFades.map((group) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8538
8706
|
GroupFadeTrackRow,
|
|
8539
8707
|
{
|
|
8540
8708
|
accentColor: identity.transitionAccentColor,
|
|
@@ -8570,20 +8738,20 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8570
8738
|
group.groupId
|
|
8571
8739
|
)),
|
|
8572
8740
|
(adapter.groupExtensions ?? []).flatMap(
|
|
8573
|
-
(ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).filter((group) => !group.members.every((m) => fadeMemberDbIds.has(m.dbId))).map((group) => /* @__PURE__ */ (0,
|
|
8741
|
+
(ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).filter((group) => !group.members.every((m) => fadeMemberDbIds.has(m.dbId))).map((group) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react32.default.Fragment, { children: ext.renderGroup(group, groupCtx) }, `${ext.metaKey}:${group.groupId}`))
|
|
8574
8742
|
),
|
|
8575
8743
|
tracks.map((track, index) => {
|
|
8576
8744
|
if (crossfadeMemberDbIds.has(track.handle.dbId) || fadeMemberDbIds.has(track.handle.dbId) || genericGroupMemberDbIds.has(track.handle.dbId)) {
|
|
8577
8745
|
return null;
|
|
8578
8746
|
}
|
|
8579
|
-
return /* @__PURE__ */ (0,
|
|
8747
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TrackRow, { ...buildRowProps(track, reorder.dragPropsFor(index)) }, track.handle.id);
|
|
8580
8748
|
}),
|
|
8581
8749
|
slots?.afterRows
|
|
8582
8750
|
] })),
|
|
8583
8751
|
features.exportMidi && !designerView && !isLoadingTracks && tracks.length > 0 && (() => {
|
|
8584
8752
|
const hasAnyMidi = tracks.some((t) => t.hasMidi);
|
|
8585
8753
|
const exportDisabled = isExportingMidi || !hasAnyMidi;
|
|
8586
|
-
return /* @__PURE__ */ (0,
|
|
8754
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "pt-2", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8587
8755
|
"button",
|
|
8588
8756
|
{
|
|
8589
8757
|
"data-testid": "export-midi-tracks-button",
|
|
@@ -9073,7 +9241,7 @@ Your previous ensemble had these problems \u2014 fix them while keeping everythi
|
|
|
9073
9241
|
}
|
|
9074
9242
|
|
|
9075
9243
|
// src/constants/sdk-version.ts
|
|
9076
|
-
var PLUGIN_SDK_VERSION = "2.
|
|
9244
|
+
var PLUGIN_SDK_VERSION = "2.46.0";
|
|
9077
9245
|
|
|
9078
9246
|
// src/utils/format-concurrent-tracks.ts
|
|
9079
9247
|
function formatConcurrentTracks(ctx) {
|
|
@@ -9282,6 +9450,7 @@ function pickTopKWeighted(scored, options = {}) {
|
|
|
9282
9450
|
TRANSITION_DESIGNER_DRAFT_KEY,
|
|
9283
9451
|
TrackDrawer,
|
|
9284
9452
|
TrackExternalFxSection,
|
|
9453
|
+
TrackFreezeSection,
|
|
9285
9454
|
TrackMeterStrip,
|
|
9286
9455
|
TrackRow,
|
|
9287
9456
|
TransitionDesigner,
|
|
@@ -9352,6 +9521,7 @@ function pickTopKWeighted(scored, options = {}) {
|
|
|
9352
9521
|
useSceneState,
|
|
9353
9522
|
useSoundHistory,
|
|
9354
9523
|
useTrackExternalFx,
|
|
9524
|
+
useTrackFreeze,
|
|
9355
9525
|
useTrackLevel,
|
|
9356
9526
|
useTrackLevels,
|
|
9357
9527
|
useTrackMeter,
|