@signalsandsorcery/plugin-sdk 2.36.2 → 2.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +328 -52
- package/dist/index.d.ts +328 -52
- package/dist/index.js +1431 -759
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1318 -659
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -69,7 +69,7 @@ import React9 from "react";
|
|
|
69
69
|
import { AlertCircle, ChevronDown, GripVertical } from "lucide-react";
|
|
70
70
|
|
|
71
71
|
// src/components/TrackDrawer.tsx
|
|
72
|
-
import { useState as
|
|
72
|
+
import { useState as useState5, useMemo as useMemo3 } from "react";
|
|
73
73
|
|
|
74
74
|
// src/constants/fx-presets.ts
|
|
75
75
|
var EQ_PRESETS = {
|
|
@@ -1051,14 +1051,132 @@ function TrackExternalFxSection({
|
|
|
1051
1051
|
);
|
|
1052
1052
|
}
|
|
1053
1053
|
|
|
1054
|
-
// src/components/
|
|
1054
|
+
// src/components/TrackFreezeSection.tsx
|
|
1055
1055
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1056
|
+
var STALE_LABELS = {
|
|
1057
|
+
midi: "MIDI changed",
|
|
1058
|
+
preset: "preset changed",
|
|
1059
|
+
instrument: "instrument changed",
|
|
1060
|
+
"external-fx": "FX changed",
|
|
1061
|
+
role: "role changed"
|
|
1062
|
+
};
|
|
1063
|
+
function TrackFreezeSection({
|
|
1064
|
+
state,
|
|
1065
|
+
busy,
|
|
1066
|
+
error,
|
|
1067
|
+
onFreeze,
|
|
1068
|
+
onUnfreeze
|
|
1069
|
+
}) {
|
|
1070
|
+
const frozen = state?.frozen === true;
|
|
1071
|
+
const stale = frozen && state?.stale === true;
|
|
1072
|
+
const missing = state?.missingDeps ?? [];
|
|
1073
|
+
const unfreezeBlocked = frozen && missing.length > 0;
|
|
1074
|
+
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.";
|
|
1075
|
+
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";
|
|
1076
|
+
const primaryAction = !frozen || stale ? onFreeze : onUnfreeze;
|
|
1077
|
+
const primaryDisabled = busy !== null || !state || frozen && !stale && unfreezeBlocked;
|
|
1078
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", "data-testid": "sdk-freeze-section", children: [
|
|
1079
|
+
/* @__PURE__ */ jsx4("p", { className: "text-[11px] text-sas-muted/80 leading-snug", "data-testid": "sdk-freeze-status", children: statusLine }),
|
|
1080
|
+
frozen && missing.length > 0 && /* @__PURE__ */ jsxs4(
|
|
1081
|
+
"div",
|
|
1082
|
+
{
|
|
1083
|
+
className: "text-[11px] leading-snug rounded-sm border border-sas-border bg-sas-panel-alt px-2 py-1.5 text-sas-muted",
|
|
1084
|
+
"data-testid": "sdk-freeze-missing-deps",
|
|
1085
|
+
children: [
|
|
1086
|
+
"Missing on this machine:",
|
|
1087
|
+
" ",
|
|
1088
|
+
/* @__PURE__ */ jsx4("span", { className: "text-sas-accent", children: missing.map((d) => d.name).join(", ") }),
|
|
1089
|
+
". ",
|
|
1090
|
+
"Install (or rescan plugins if blacklisted) to unfreeze \u2014 the frozen sound keeps playing meanwhile."
|
|
1091
|
+
]
|
|
1092
|
+
}
|
|
1093
|
+
),
|
|
1094
|
+
/* @__PURE__ */ jsx4(
|
|
1095
|
+
"button",
|
|
1096
|
+
{
|
|
1097
|
+
type: "button",
|
|
1098
|
+
"data-testid": "sdk-freeze-primary",
|
|
1099
|
+
disabled: primaryDisabled,
|
|
1100
|
+
onClick: primaryAction,
|
|
1101
|
+
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"}`,
|
|
1102
|
+
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",
|
|
1103
|
+
children: primaryLabel
|
|
1104
|
+
}
|
|
1105
|
+
),
|
|
1106
|
+
frozen && stale && !unfreezeBlocked && /* @__PURE__ */ jsx4(
|
|
1107
|
+
"button",
|
|
1108
|
+
{
|
|
1109
|
+
type: "button",
|
|
1110
|
+
"data-testid": "sdk-freeze-unfreeze-secondary",
|
|
1111
|
+
disabled: busy !== null,
|
|
1112
|
+
onClick: onUnfreeze,
|
|
1113
|
+
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",
|
|
1114
|
+
children: "Unfreeze instead (back to live editing)"
|
|
1115
|
+
}
|
|
1116
|
+
),
|
|
1117
|
+
error && /* @__PURE__ */ jsx4("p", { className: "text-[11px] text-red-400 leading-snug", "data-testid": "sdk-freeze-error", children: error }),
|
|
1118
|
+
/* @__PURE__ */ jsx4("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." })
|
|
1119
|
+
] });
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// src/hooks/useTrackFreeze.ts
|
|
1123
|
+
import { useState as useState4, useEffect as useEffect2, useCallback as useCallback3 } from "react";
|
|
1124
|
+
function useTrackFreeze(host, trackId) {
|
|
1125
|
+
const enabled = !!host && typeof host.getTrackFreezeState === "function";
|
|
1126
|
+
const [state, setState] = useState4(null);
|
|
1127
|
+
const [busy, setBusy] = useState4(null);
|
|
1128
|
+
const [error, setError] = useState4(null);
|
|
1129
|
+
const refresh = useCallback3(async () => {
|
|
1130
|
+
if (!enabled || !host?.getTrackFreezeState) return;
|
|
1131
|
+
try {
|
|
1132
|
+
setState(await host.getTrackFreezeState(trackId));
|
|
1133
|
+
} catch {
|
|
1134
|
+
setState(null);
|
|
1135
|
+
}
|
|
1136
|
+
}, [enabled, host, trackId]);
|
|
1137
|
+
useEffect2(() => {
|
|
1138
|
+
void refresh();
|
|
1139
|
+
}, [refresh]);
|
|
1140
|
+
useEffect2(() => {
|
|
1141
|
+
if (!enabled) return void 0;
|
|
1142
|
+
const onFreezeChanged = () => {
|
|
1143
|
+
void refresh();
|
|
1144
|
+
};
|
|
1145
|
+
window.addEventListener("sas:freeze-changed", onFreezeChanged);
|
|
1146
|
+
return () => window.removeEventListener("sas:freeze-changed", onFreezeChanged);
|
|
1147
|
+
}, [enabled, refresh]);
|
|
1148
|
+
const run = useCallback3(
|
|
1149
|
+
async (action) => {
|
|
1150
|
+
if (!host) return;
|
|
1151
|
+
const method = action === "freeze" ? host.freezeTrack : host.unfreezeTrack;
|
|
1152
|
+
if (typeof method !== "function") return;
|
|
1153
|
+
setBusy(action);
|
|
1154
|
+
setError(null);
|
|
1155
|
+
try {
|
|
1156
|
+
setState(await method.call(host, trackId));
|
|
1157
|
+
} catch (e) {
|
|
1158
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1159
|
+
void refresh();
|
|
1160
|
+
} finally {
|
|
1161
|
+
setBusy(null);
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
[host, trackId, refresh]
|
|
1165
|
+
);
|
|
1166
|
+
const freeze = useCallback3(() => run("freeze"), [run]);
|
|
1167
|
+
const unfreeze = useCallback3(() => run("unfreeze"), [run]);
|
|
1168
|
+
return { enabled, state, busy, error, refresh, freeze, unfreeze };
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// src/components/TrackDrawer.tsx
|
|
1172
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1056
1173
|
var TAB_LABELS = {
|
|
1057
1174
|
fx: "FX",
|
|
1058
1175
|
pick: "Pick",
|
|
1059
1176
|
history: "History",
|
|
1060
1177
|
import: "Import",
|
|
1061
|
-
edit: "Edit"
|
|
1178
|
+
edit: "Edit",
|
|
1179
|
+
freeze: "\u2744 Freeze"
|
|
1062
1180
|
};
|
|
1063
1181
|
function TrackDrawer({
|
|
1064
1182
|
activeTab,
|
|
@@ -1070,6 +1188,7 @@ function TrackDrawer({
|
|
|
1070
1188
|
onFxDryWetChange,
|
|
1071
1189
|
fxDisabled = false,
|
|
1072
1190
|
externalFxHost,
|
|
1191
|
+
freeze,
|
|
1073
1192
|
instruments = [],
|
|
1074
1193
|
currentPluginId = null,
|
|
1075
1194
|
isLoading = false,
|
|
@@ -1085,6 +1204,7 @@ function TrackDrawer({
|
|
|
1085
1204
|
onToggleFavorite,
|
|
1086
1205
|
onImportSound,
|
|
1087
1206
|
importSoundLabel,
|
|
1207
|
+
linkedSoundHint,
|
|
1088
1208
|
editNotes,
|
|
1089
1209
|
onNotesChange,
|
|
1090
1210
|
editBars,
|
|
@@ -1092,12 +1212,15 @@ function TrackDrawer({
|
|
|
1092
1212
|
editSnap,
|
|
1093
1213
|
onAuditionNote
|
|
1094
1214
|
}) {
|
|
1095
|
-
const [search, setSearch] =
|
|
1215
|
+
const [search, setSearch] = useState5("");
|
|
1096
1216
|
const fxEnabled = !!onFxToggle;
|
|
1097
1217
|
const pickEnabled = !!onSelect;
|
|
1098
1218
|
const historyEnabled = !!onRestoreSound;
|
|
1099
1219
|
const importEnabled = !!onImportSound;
|
|
1100
1220
|
const editEnabled = !!onNotesChange;
|
|
1221
|
+
const internalFreeze = useTrackFreeze(freeze ? void 0 : externalFxHost, trackId);
|
|
1222
|
+
const fz = freeze ?? internalFreeze;
|
|
1223
|
+
const freezeEnabled = fz.enabled;
|
|
1101
1224
|
const enabledTabs = useMemo3(() => {
|
|
1102
1225
|
const tabs = [];
|
|
1103
1226
|
if (fxEnabled) tabs.push("fx");
|
|
@@ -1105,8 +1228,9 @@ function TrackDrawer({
|
|
|
1105
1228
|
if (historyEnabled) tabs.push("history");
|
|
1106
1229
|
if (importEnabled) tabs.push("import");
|
|
1107
1230
|
if (editEnabled) tabs.push("edit");
|
|
1231
|
+
if (freezeEnabled) tabs.push("freeze");
|
|
1108
1232
|
return tabs;
|
|
1109
|
-
}, [fxEnabled, pickEnabled, historyEnabled, importEnabled, editEnabled]);
|
|
1233
|
+
}, [fxEnabled, pickEnabled, historyEnabled, importEnabled, editEnabled, freezeEnabled]);
|
|
1110
1234
|
const SURGE_XT_DEFAULT_ID = "Surge XT";
|
|
1111
1235
|
const filtered = useMemo3(() => {
|
|
1112
1236
|
let all = instruments.filter((i) => i.name !== "Surge XT");
|
|
@@ -1128,12 +1252,12 @@ function TrackDrawer({
|
|
|
1128
1252
|
const history = soundHistory ?? [];
|
|
1129
1253
|
const effectiveTab = enabledTabs.includes(activeTab) ? activeTab : enabledTabs[0] ?? "fx";
|
|
1130
1254
|
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"}`;
|
|
1131
|
-
const strip = enabledTabs.length > 1 ? /* @__PURE__ */
|
|
1255
|
+
const strip = enabledTabs.length > 1 ? /* @__PURE__ */ jsx5(
|
|
1132
1256
|
"div",
|
|
1133
1257
|
{
|
|
1134
1258
|
className: "flex items-center gap-1 border-b border-sas-border pb-1",
|
|
1135
1259
|
"data-testid": "sdk-drawer-tabs",
|
|
1136
|
-
children: enabledTabs.map((tab) => /* @__PURE__ */
|
|
1260
|
+
children: enabledTabs.map((tab) => /* @__PURE__ */ jsx5(
|
|
1137
1261
|
"button",
|
|
1138
1262
|
{
|
|
1139
1263
|
type: "button",
|
|
@@ -1147,21 +1271,60 @@ function TrackDrawer({
|
|
|
1147
1271
|
}
|
|
1148
1272
|
) : null;
|
|
1149
1273
|
const currentSound = soundHistoryCursor >= 0 && soundHistoryCursor < history.length ? history[soundHistoryCursor].label : null;
|
|
1150
|
-
const header = strip || currentSound ? /* @__PURE__ */
|
|
1274
|
+
const header = strip || currentSound || linkedSoundHint ? /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", "data-testid": "sdk-drawer-header", children: [
|
|
1151
1275
|
strip,
|
|
1152
|
-
currentSound && /* @__PURE__ */
|
|
1276
|
+
currentSound && /* @__PURE__ */ jsx5(
|
|
1153
1277
|
"span",
|
|
1154
1278
|
{
|
|
1155
1279
|
className: "text-[10px] text-sas-muted/60 truncate px-0.5",
|
|
1156
1280
|
title: currentSound,
|
|
1157
1281
|
children: currentSound
|
|
1158
1282
|
}
|
|
1283
|
+
),
|
|
1284
|
+
linkedSoundHint && /* @__PURE__ */ jsx5(
|
|
1285
|
+
"span",
|
|
1286
|
+
{
|
|
1287
|
+
"data-testid": "sdk-drawer-linked-hint",
|
|
1288
|
+
className: "text-[10px] text-sas-accent/80 px-0.5",
|
|
1289
|
+
children: linkedSoundHint
|
|
1290
|
+
}
|
|
1159
1291
|
)
|
|
1160
1292
|
] }) : null;
|
|
1293
|
+
if (effectiveTab === "freeze") {
|
|
1294
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-freeze", children: [
|
|
1295
|
+
header,
|
|
1296
|
+
/* @__PURE__ */ jsx5(
|
|
1297
|
+
TrackFreezeSection,
|
|
1298
|
+
{
|
|
1299
|
+
state: fz.state,
|
|
1300
|
+
busy: fz.busy,
|
|
1301
|
+
error: fz.error,
|
|
1302
|
+
onFreeze: () => void fz.freeze(),
|
|
1303
|
+
onUnfreeze: () => void fz.unfreeze()
|
|
1304
|
+
}
|
|
1305
|
+
)
|
|
1306
|
+
] });
|
|
1307
|
+
}
|
|
1308
|
+
if (fz.state?.frozen === true) {
|
|
1309
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-frozen-lock", children: [
|
|
1310
|
+
header,
|
|
1311
|
+
/* @__PURE__ */ jsx5("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." }),
|
|
1312
|
+
/* @__PURE__ */ jsx5(
|
|
1313
|
+
"button",
|
|
1314
|
+
{
|
|
1315
|
+
type: "button",
|
|
1316
|
+
"data-testid": "sdk-drawer-frozen-goto",
|
|
1317
|
+
onClick: () => onTabChange?.("freeze"),
|
|
1318
|
+
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",
|
|
1319
|
+
children: "Open the Freeze tab to unfreeze"
|
|
1320
|
+
}
|
|
1321
|
+
)
|
|
1322
|
+
] });
|
|
1323
|
+
}
|
|
1161
1324
|
if (effectiveTab === "edit") {
|
|
1162
|
-
return /* @__PURE__ */
|
|
1325
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-edit", children: [
|
|
1163
1326
|
header,
|
|
1164
|
-
/* @__PURE__ */
|
|
1327
|
+
/* @__PURE__ */ jsx5(
|
|
1165
1328
|
PianoRollEditor,
|
|
1166
1329
|
{
|
|
1167
1330
|
notes: editNotes ?? [],
|
|
@@ -1176,9 +1339,9 @@ function TrackDrawer({
|
|
|
1176
1339
|
] });
|
|
1177
1340
|
}
|
|
1178
1341
|
if (effectiveTab === "fx") {
|
|
1179
|
-
return /* @__PURE__ */
|
|
1342
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-fx", children: [
|
|
1180
1343
|
header,
|
|
1181
|
-
/* @__PURE__ */
|
|
1344
|
+
/* @__PURE__ */ jsx5(
|
|
1182
1345
|
FxToggleBar,
|
|
1183
1346
|
{
|
|
1184
1347
|
trackId,
|
|
@@ -1189,20 +1352,20 @@ function TrackDrawer({
|
|
|
1189
1352
|
disabled: fxDisabled
|
|
1190
1353
|
}
|
|
1191
1354
|
),
|
|
1192
|
-
externalFxHost && /* @__PURE__ */
|
|
1355
|
+
externalFxHost && /* @__PURE__ */ jsx5(TrackExternalFxSection, { host: externalFxHost, trackId, disabled: fxDisabled })
|
|
1193
1356
|
] });
|
|
1194
1357
|
}
|
|
1195
1358
|
if (effectiveTab === "import") {
|
|
1196
1359
|
const soundNoun = /preset/i.test(importSoundLabel ?? "") ? "preset" : /sample/i.test(importSoundLabel ?? "") ? "sample" : "sound";
|
|
1197
|
-
return /* @__PURE__ */
|
|
1360
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-import", children: [
|
|
1198
1361
|
header,
|
|
1199
|
-
/* @__PURE__ */
|
|
1362
|
+
/* @__PURE__ */ jsxs5("p", { className: "text-[11px] text-sas-muted/70 leading-snug", children: [
|
|
1200
1363
|
"Copy the sound from a matching track in another scene \u2014 your MIDI stays, only the",
|
|
1201
1364
|
" ",
|
|
1202
1365
|
soundNoun,
|
|
1203
1366
|
" changes."
|
|
1204
1367
|
] }),
|
|
1205
|
-
/* @__PURE__ */
|
|
1368
|
+
/* @__PURE__ */ jsxs5(
|
|
1206
1369
|
"button",
|
|
1207
1370
|
{
|
|
1208
1371
|
type: "button",
|
|
@@ -1220,16 +1383,16 @@ function TrackDrawer({
|
|
|
1220
1383
|
}
|
|
1221
1384
|
if (effectiveTab === "history") {
|
|
1222
1385
|
const order = history.map((_, i) => i).reverse();
|
|
1223
|
-
return /* @__PURE__ */
|
|
1386
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
|
|
1224
1387
|
header,
|
|
1225
|
-
history.length === 0 ? /* @__PURE__ */
|
|
1388
|
+
history.length === 0 ? /* @__PURE__ */ jsx5(
|
|
1226
1389
|
"div",
|
|
1227
1390
|
{
|
|
1228
1391
|
className: "text-xs text-sas-muted/60 text-center py-3",
|
|
1229
1392
|
"data-testid": "sdk-history-empty",
|
|
1230
1393
|
children: "No sounds yet \u2014 shuffle to build history."
|
|
1231
1394
|
}
|
|
1232
|
-
) : /* @__PURE__ */
|
|
1395
|
+
) : /* @__PURE__ */ jsx5(
|
|
1233
1396
|
"ul",
|
|
1234
1397
|
{
|
|
1235
1398
|
className: "flex flex-col gap-1 max-h-[160px] overflow-y-auto",
|
|
@@ -1237,8 +1400,8 @@ function TrackDrawer({
|
|
|
1237
1400
|
children: order.map((i) => {
|
|
1238
1401
|
const entry = history[i];
|
|
1239
1402
|
const isCurrent = i === soundHistoryCursor;
|
|
1240
|
-
return /* @__PURE__ */
|
|
1241
|
-
/* @__PURE__ */
|
|
1403
|
+
return /* @__PURE__ */ jsxs5("li", { className: "flex items-center gap-1", children: [
|
|
1404
|
+
/* @__PURE__ */ jsxs5(
|
|
1242
1405
|
"button",
|
|
1243
1406
|
{
|
|
1244
1407
|
type: "button",
|
|
@@ -1248,12 +1411,12 @@ function TrackDrawer({
|
|
|
1248
1411
|
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"}`,
|
|
1249
1412
|
title: isCurrent ? "Current sound" : `Restore: ${entry.label}`,
|
|
1250
1413
|
children: [
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1414
|
+
/* @__PURE__ */ jsx5("span", { className: "truncate", children: entry.label }),
|
|
1415
|
+
/* @__PURE__ */ jsx5("span", { className: "text-[10px] text-sas-muted/60 flex-shrink-0 ml-2", children: isCurrent ? "\u25CF current" : "restore" })
|
|
1253
1416
|
]
|
|
1254
1417
|
}
|
|
1255
1418
|
),
|
|
1256
|
-
onToggleFavorite && /* @__PURE__ */
|
|
1419
|
+
onToggleFavorite && /* @__PURE__ */ jsx5(
|
|
1257
1420
|
"button",
|
|
1258
1421
|
{
|
|
1259
1422
|
type: "button",
|
|
@@ -1271,10 +1434,10 @@ function TrackDrawer({
|
|
|
1271
1434
|
] });
|
|
1272
1435
|
}
|
|
1273
1436
|
if (effectiveTab === "pick" && editorStage) {
|
|
1274
|
-
return /* @__PURE__ */
|
|
1437
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
|
|
1275
1438
|
header,
|
|
1276
|
-
/* @__PURE__ */
|
|
1277
|
-
/* @__PURE__ */
|
|
1439
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
|
|
1440
|
+
/* @__PURE__ */ jsx5(
|
|
1278
1441
|
"button",
|
|
1279
1442
|
{
|
|
1280
1443
|
onClick: () => onBackToInstruments?.(),
|
|
@@ -1282,9 +1445,9 @@ function TrackDrawer({
|
|
|
1282
1445
|
children: "\u2190 Back"
|
|
1283
1446
|
}
|
|
1284
1447
|
),
|
|
1285
|
-
/* @__PURE__ */
|
|
1448
|
+
/* @__PURE__ */ jsx5("span", { className: "text-xs text-sas-muted font-medium truncate flex-1", children: selectedInstrumentName ?? "Plugin" })
|
|
1286
1449
|
] }),
|
|
1287
|
-
/* @__PURE__ */
|
|
1450
|
+
/* @__PURE__ */ jsx5(
|
|
1288
1451
|
"button",
|
|
1289
1452
|
{
|
|
1290
1453
|
onClick: () => onShowEditor?.(),
|
|
@@ -1296,10 +1459,10 @@ function TrackDrawer({
|
|
|
1296
1459
|
}
|
|
1297
1460
|
const isDefaultSelected = currentPluginId === null;
|
|
1298
1461
|
const isSelected = (pluginId) => pluginId === currentPluginId;
|
|
1299
|
-
return /* @__PURE__ */
|
|
1462
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
|
|
1300
1463
|
header,
|
|
1301
|
-
/* @__PURE__ */
|
|
1302
|
-
/* @__PURE__ */
|
|
1464
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
|
|
1465
|
+
/* @__PURE__ */ jsx5(
|
|
1303
1466
|
"input",
|
|
1304
1467
|
{
|
|
1305
1468
|
type: "text",
|
|
@@ -1309,7 +1472,7 @@ function TrackDrawer({
|
|
|
1309
1472
|
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
1310
1473
|
}
|
|
1311
1474
|
),
|
|
1312
|
-
/* @__PURE__ */
|
|
1475
|
+
/* @__PURE__ */ jsx5(
|
|
1313
1476
|
"button",
|
|
1314
1477
|
{
|
|
1315
1478
|
onClick: () => onRefresh?.(),
|
|
@@ -1320,43 +1483,43 @@ function TrackDrawer({
|
|
|
1320
1483
|
}
|
|
1321
1484
|
)
|
|
1322
1485
|
] }),
|
|
1323
|
-
isLoading && instruments.length === 0 ? /* @__PURE__ */
|
|
1324
|
-
/* @__PURE__ */
|
|
1486
|
+
isLoading && instruments.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
1487
|
+
/* @__PURE__ */ jsxs5(
|
|
1325
1488
|
"button",
|
|
1326
1489
|
{
|
|
1327
1490
|
onClick: () => onSelect?.(SURGE_XT_DEFAULT_ID),
|
|
1328
1491
|
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"}`,
|
|
1329
1492
|
title: "Surge XT \u2014 Default instrument",
|
|
1330
1493
|
children: [
|
|
1331
|
-
/* @__PURE__ */
|
|
1494
|
+
/* @__PURE__ */ jsxs5("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1332
1495
|
isDefaultSelected && "\u2713 ",
|
|
1333
1496
|
"Surge XT"
|
|
1334
1497
|
] }),
|
|
1335
|
-
/* @__PURE__ */
|
|
1498
|
+
/* @__PURE__ */ jsx5("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: "Default" })
|
|
1336
1499
|
]
|
|
1337
1500
|
},
|
|
1338
1501
|
"__surge-xt-default__"
|
|
1339
1502
|
),
|
|
1340
1503
|
filtered.map((inst) => {
|
|
1341
1504
|
const selected = isSelected(inst.pluginId);
|
|
1342
|
-
return /* @__PURE__ */
|
|
1505
|
+
return /* @__PURE__ */ jsxs5(
|
|
1343
1506
|
"button",
|
|
1344
1507
|
{
|
|
1345
1508
|
onClick: () => onSelect?.(inst.pluginId),
|
|
1346
1509
|
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"}`,
|
|
1347
1510
|
title: `${inst.name} by ${inst.manufacturer} (${inst.type.toUpperCase()})${inst.missing ? " \u2014 MISSING" : ""}`,
|
|
1348
1511
|
children: [
|
|
1349
|
-
/* @__PURE__ */
|
|
1512
|
+
/* @__PURE__ */ jsxs5("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1350
1513
|
selected && "\u2713 ",
|
|
1351
1514
|
inst.name
|
|
1352
1515
|
] }),
|
|
1353
|
-
/* @__PURE__ */
|
|
1516
|
+
/* @__PURE__ */ jsx5("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: inst.manufacturer || inst.type.toUpperCase() })
|
|
1354
1517
|
]
|
|
1355
1518
|
},
|
|
1356
1519
|
inst.pluginId
|
|
1357
1520
|
);
|
|
1358
1521
|
}),
|
|
1359
|
-
filtered.length === 0 && /* @__PURE__ */
|
|
1522
|
+
filtered.length === 0 && /* @__PURE__ */ jsx5("div", { className: "col-span-2 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No other plugins found" })
|
|
1360
1523
|
] })
|
|
1361
1524
|
] });
|
|
1362
1525
|
}
|
|
@@ -1365,9 +1528,9 @@ function TrackDrawer({
|
|
|
1365
1528
|
import { useRef as useRef3 } from "react";
|
|
1366
1529
|
|
|
1367
1530
|
// src/components/Modal.tsx
|
|
1368
|
-
import { useEffect as
|
|
1531
|
+
import { useEffect as useEffect3 } from "react";
|
|
1369
1532
|
import { createPortal } from "react-dom";
|
|
1370
|
-
import { jsx as
|
|
1533
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1371
1534
|
function Modal({
|
|
1372
1535
|
open,
|
|
1373
1536
|
onClose,
|
|
@@ -1377,7 +1540,7 @@ function Modal({
|
|
|
1377
1540
|
closeOnEscape = true,
|
|
1378
1541
|
initialFocusRef
|
|
1379
1542
|
}) {
|
|
1380
|
-
|
|
1543
|
+
useEffect3(() => {
|
|
1381
1544
|
if (!open) return void 0;
|
|
1382
1545
|
const onKey = (e) => {
|
|
1383
1546
|
if (closeOnEscape && e.key === "Escape") {
|
|
@@ -1391,7 +1554,7 @@ function Modal({
|
|
|
1391
1554
|
}, [open, onClose, closeOnEscape, initialFocusRef]);
|
|
1392
1555
|
if (!open) return null;
|
|
1393
1556
|
return createPortal(
|
|
1394
|
-
/* @__PURE__ */
|
|
1557
|
+
/* @__PURE__ */ jsx6(
|
|
1395
1558
|
"div",
|
|
1396
1559
|
{
|
|
1397
1560
|
className: "fixed inset-0 z-[1000] flex items-center justify-center bg-black/60",
|
|
@@ -1405,7 +1568,7 @@ function Modal({
|
|
|
1405
1568
|
}
|
|
1406
1569
|
|
|
1407
1570
|
// src/components/ConfirmDialog.tsx
|
|
1408
|
-
import { jsx as
|
|
1571
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1409
1572
|
function ConfirmDialog({
|
|
1410
1573
|
open,
|
|
1411
1574
|
title,
|
|
@@ -1418,7 +1581,7 @@ function ConfirmDialog({
|
|
|
1418
1581
|
testIdPrefix = "confirm-dialog"
|
|
1419
1582
|
}) {
|
|
1420
1583
|
const cancelRef = useRef3(null);
|
|
1421
|
-
return /* @__PURE__ */
|
|
1584
|
+
return /* @__PURE__ */ jsx7(Modal, { open, onClose: onCancel, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs6(
|
|
1422
1585
|
"div",
|
|
1423
1586
|
{
|
|
1424
1587
|
className: "w-[360px] max-w-[90vw] flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
@@ -1428,8 +1591,8 @@ function ConfirmDialog({
|
|
|
1428
1591
|
"aria-label": title,
|
|
1429
1592
|
"data-testid": `${testIdPrefix}-modal`,
|
|
1430
1593
|
children: [
|
|
1431
|
-
/* @__PURE__ */
|
|
1432
|
-
/* @__PURE__ */
|
|
1594
|
+
/* @__PURE__ */ jsx7("div", { className: "px-4 py-3 border-b border-sas-border", children: /* @__PURE__ */ jsx7("span", { className: "text-sm font-medium text-sas-text", "data-testid": `${testIdPrefix}-title`, children: title }) }),
|
|
1595
|
+
/* @__PURE__ */ jsx7(
|
|
1433
1596
|
"div",
|
|
1434
1597
|
{
|
|
1435
1598
|
className: "px-4 py-3 text-xs text-sas-muted leading-relaxed break-words",
|
|
@@ -1437,8 +1600,8 @@ function ConfirmDialog({
|
|
|
1437
1600
|
children: message
|
|
1438
1601
|
}
|
|
1439
1602
|
),
|
|
1440
|
-
/* @__PURE__ */
|
|
1441
|
-
/* @__PURE__ */
|
|
1603
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex justify-end gap-2 px-4 py-3 border-t border-sas-border", children: [
|
|
1604
|
+
/* @__PURE__ */ jsx7(
|
|
1442
1605
|
"button",
|
|
1443
1606
|
{
|
|
1444
1607
|
ref: cancelRef,
|
|
@@ -1449,7 +1612,7 @@ function ConfirmDialog({
|
|
|
1449
1612
|
children: cancelLabel
|
|
1450
1613
|
}
|
|
1451
1614
|
),
|
|
1452
|
-
/* @__PURE__ */
|
|
1615
|
+
/* @__PURE__ */ jsx7(
|
|
1453
1616
|
"button",
|
|
1454
1617
|
{
|
|
1455
1618
|
type: "button",
|
|
@@ -1466,7 +1629,7 @@ function ConfirmDialog({
|
|
|
1466
1629
|
}
|
|
1467
1630
|
|
|
1468
1631
|
// src/components/LevelMeter.tsx
|
|
1469
|
-
import { jsx as
|
|
1632
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1470
1633
|
var COLOR_GREEN = "#2BD576";
|
|
1471
1634
|
var COLOR_ORANGE = "#F5A623";
|
|
1472
1635
|
var COLOR_RED = "#FF4D5E";
|
|
@@ -1494,7 +1657,7 @@ var LevelMeter = ({
|
|
|
1494
1657
|
const widthPct = active ? dbToPct(peakDb) : 0;
|
|
1495
1658
|
const showPeak = peakHoldDb != null && active && peakHoldDb > -60;
|
|
1496
1659
|
const peakHoldPct = showPeak ? dbToPct(peakHoldDb) : 0;
|
|
1497
|
-
return /* @__PURE__ */
|
|
1660
|
+
return /* @__PURE__ */ jsxs7(
|
|
1498
1661
|
"div",
|
|
1499
1662
|
{
|
|
1500
1663
|
className: `sas-level-meter ${className ?? ""}`,
|
|
@@ -1505,7 +1668,7 @@ var LevelMeter = ({
|
|
|
1505
1668
|
gap: compact ? 0 : 6
|
|
1506
1669
|
},
|
|
1507
1670
|
children: [
|
|
1508
|
-
/* @__PURE__ */
|
|
1671
|
+
/* @__PURE__ */ jsxs7(
|
|
1509
1672
|
"div",
|
|
1510
1673
|
{
|
|
1511
1674
|
style: {
|
|
@@ -1519,8 +1682,8 @@ var LevelMeter = ({
|
|
|
1519
1682
|
minWidth: compact ? 0 : 60
|
|
1520
1683
|
},
|
|
1521
1684
|
children: [
|
|
1522
|
-
/* @__PURE__ */
|
|
1523
|
-
/* @__PURE__ */
|
|
1685
|
+
/* @__PURE__ */ jsx8("div", { style: { position: "absolute", inset: 0, background: METER_GRADIENT } }),
|
|
1686
|
+
/* @__PURE__ */ jsx8(
|
|
1524
1687
|
"div",
|
|
1525
1688
|
{
|
|
1526
1689
|
style: {
|
|
@@ -1534,7 +1697,7 @@ var LevelMeter = ({
|
|
|
1534
1697
|
}
|
|
1535
1698
|
}
|
|
1536
1699
|
),
|
|
1537
|
-
/* @__PURE__ */
|
|
1700
|
+
/* @__PURE__ */ jsx8(
|
|
1538
1701
|
"div",
|
|
1539
1702
|
{
|
|
1540
1703
|
"data-testid": `${id}-segments`,
|
|
@@ -1547,7 +1710,7 @@ var LevelMeter = ({
|
|
|
1547
1710
|
}
|
|
1548
1711
|
}
|
|
1549
1712
|
),
|
|
1550
|
-
showPeak && /* @__PURE__ */
|
|
1713
|
+
showPeak && /* @__PURE__ */ jsx8(
|
|
1551
1714
|
"div",
|
|
1552
1715
|
{
|
|
1553
1716
|
"data-testid": `${id}-peak`,
|
|
@@ -1568,7 +1731,7 @@ var LevelMeter = ({
|
|
|
1568
1731
|
]
|
|
1569
1732
|
}
|
|
1570
1733
|
),
|
|
1571
|
-
!compact && /* @__PURE__ */
|
|
1734
|
+
!compact && /* @__PURE__ */ jsx8(
|
|
1572
1735
|
"span",
|
|
1573
1736
|
{
|
|
1574
1737
|
style: {
|
|
@@ -1581,7 +1744,7 @@ var LevelMeter = ({
|
|
|
1581
1744
|
children: active && peakDb > -120 ? `${peakDb.toFixed(0)} dB` : "\u2014"
|
|
1582
1745
|
}
|
|
1583
1746
|
),
|
|
1584
|
-
clipped && /* @__PURE__ */
|
|
1747
|
+
clipped && /* @__PURE__ */ jsx8(
|
|
1585
1748
|
"span",
|
|
1586
1749
|
{
|
|
1587
1750
|
"data-testid": `${id}-clip`,
|
|
@@ -1606,7 +1769,7 @@ var LevelMeter = ({
|
|
|
1606
1769
|
};
|
|
1607
1770
|
|
|
1608
1771
|
// src/hooks/useTrackLevels.ts
|
|
1609
|
-
import { useEffect as
|
|
1772
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState6 } from "react";
|
|
1610
1773
|
var meterDiagRLast = /* @__PURE__ */ new Map();
|
|
1611
1774
|
var POLL_INTERVAL_MS = 33;
|
|
1612
1775
|
var HIDDEN_RECHECK_MS = 250;
|
|
@@ -1631,7 +1794,7 @@ function useTrackLevels(host, enabled = true) {
|
|
|
1631
1794
|
}
|
|
1632
1795
|
};
|
|
1633
1796
|
}
|
|
1634
|
-
|
|
1797
|
+
useEffect4(() => {
|
|
1635
1798
|
const notify = () => {
|
|
1636
1799
|
listenersRef.current.forEach((l) => l());
|
|
1637
1800
|
};
|
|
@@ -1701,8 +1864,8 @@ function sameLevel(a, b) {
|
|
|
1701
1864
|
return a.peakDb === b.peakDb && a.clipped === b.clipped;
|
|
1702
1865
|
}
|
|
1703
1866
|
function useTrackLevel(handle, trackId) {
|
|
1704
|
-
const [level, setLevel] =
|
|
1705
|
-
|
|
1867
|
+
const [level, setLevel] = useState6(null);
|
|
1868
|
+
useEffect4(() => {
|
|
1706
1869
|
if (!handle) {
|
|
1707
1870
|
setLevel(null);
|
|
1708
1871
|
return;
|
|
@@ -1726,11 +1889,11 @@ function sameMeter(a, b) {
|
|
|
1726
1889
|
return a.active === b.active && a.clipped === b.clipped && a.peakDb === b.peakDb && Math.round(a.peakHoldDb * 2) === Math.round(b.peakHoldDb * 2);
|
|
1727
1890
|
}
|
|
1728
1891
|
function useTrackMeter(handle, trackId) {
|
|
1729
|
-
const [view, setView] =
|
|
1892
|
+
const [view, setView] = useState6(IDLE_METER_VIEW);
|
|
1730
1893
|
const heldDbRef = useRef4(METER_FLOOR_DB);
|
|
1731
1894
|
const heldAtRef = useRef4(0);
|
|
1732
1895
|
const lastTickRef = useRef4(0);
|
|
1733
|
-
|
|
1896
|
+
useEffect4(() => {
|
|
1734
1897
|
if (!handle) {
|
|
1735
1898
|
heldDbRef.current = METER_FLOOR_DB;
|
|
1736
1899
|
lastTickRef.current = 0;
|
|
@@ -1773,8 +1936,8 @@ function useTrackMeter(handle, trackId) {
|
|
|
1773
1936
|
return view;
|
|
1774
1937
|
}
|
|
1775
1938
|
function useTransportPlaying(host) {
|
|
1776
|
-
const [playing, setPlaying] =
|
|
1777
|
-
|
|
1939
|
+
const [playing, setPlaying] = useState6(false);
|
|
1940
|
+
useEffect4(() => {
|
|
1778
1941
|
if (!host) {
|
|
1779
1942
|
setPlaying(false);
|
|
1780
1943
|
return;
|
|
@@ -1802,7 +1965,7 @@ function useTransportPlaying(host) {
|
|
|
1802
1965
|
}
|
|
1803
1966
|
|
|
1804
1967
|
// src/components/TrackMeterStrip.tsx
|
|
1805
|
-
import { jsx as
|
|
1968
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1806
1969
|
var TrackMeterStrip = ({
|
|
1807
1970
|
levels,
|
|
1808
1971
|
trackId,
|
|
@@ -1810,12 +1973,12 @@ var TrackMeterStrip = ({
|
|
|
1810
1973
|
className
|
|
1811
1974
|
}) => {
|
|
1812
1975
|
const meter = useTrackMeter(levels, trackId);
|
|
1813
|
-
return /* @__PURE__ */
|
|
1976
|
+
return /* @__PURE__ */ jsx9(
|
|
1814
1977
|
"div",
|
|
1815
1978
|
{
|
|
1816
1979
|
"data-testid": "sdk-track-meter",
|
|
1817
1980
|
className: `w-full px-2 py-1 bg-sas-panel-alt border border-t-0 border-sas-border ${roundBottom ? "rounded-b-sm" : ""} ${className ?? ""}`,
|
|
1818
|
-
children: /* @__PURE__ */
|
|
1981
|
+
children: /* @__PURE__ */ jsx9(
|
|
1819
1982
|
LevelMeter,
|
|
1820
1983
|
{
|
|
1821
1984
|
compact: true,
|
|
@@ -1831,7 +1994,7 @@ var TrackMeterStrip = ({
|
|
|
1831
1994
|
};
|
|
1832
1995
|
|
|
1833
1996
|
// src/components/VolumeSlider.tsx
|
|
1834
|
-
import { useCallback as
|
|
1997
|
+
import { useCallback as useCallback4, useState as useState7, useRef as useRef5, useEffect as useEffect5 } from "react";
|
|
1835
1998
|
|
|
1836
1999
|
// src/utils/volume-conversion.ts
|
|
1837
2000
|
var SLIDER_UNITY = 0.75;
|
|
@@ -1853,7 +2016,7 @@ function dbToSlider(db) {
|
|
|
1853
2016
|
}
|
|
1854
2017
|
|
|
1855
2018
|
// src/components/VolumeSlider.tsx
|
|
1856
|
-
import { jsx as
|
|
2019
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1857
2020
|
function formatDb(value) {
|
|
1858
2021
|
const db = sliderToDb(value);
|
|
1859
2022
|
if (db <= -60) return "-\u221E dB";
|
|
@@ -1863,10 +2026,10 @@ function formatDb(value) {
|
|
|
1863
2026
|
function useDebouncedCallback(callback, delay) {
|
|
1864
2027
|
const timeoutRef = useRef5(null);
|
|
1865
2028
|
const callbackRef = useRef5(callback);
|
|
1866
|
-
|
|
2029
|
+
useEffect5(() => {
|
|
1867
2030
|
callbackRef.current = callback;
|
|
1868
2031
|
}, [callback]);
|
|
1869
|
-
const debouncedCallback =
|
|
2032
|
+
const debouncedCallback = useCallback4(
|
|
1870
2033
|
(...args) => {
|
|
1871
2034
|
if (timeoutRef.current) {
|
|
1872
2035
|
clearTimeout(timeoutRef.current);
|
|
@@ -1877,7 +2040,7 @@ function useDebouncedCallback(callback, delay) {
|
|
|
1877
2040
|
},
|
|
1878
2041
|
[delay]
|
|
1879
2042
|
);
|
|
1880
|
-
|
|
2043
|
+
useEffect5(() => {
|
|
1881
2044
|
return () => {
|
|
1882
2045
|
if (timeoutRef.current) {
|
|
1883
2046
|
clearTimeout(timeoutRef.current);
|
|
@@ -1892,15 +2055,15 @@ var VolumeSlider = ({
|
|
|
1892
2055
|
disabled = false,
|
|
1893
2056
|
className = ""
|
|
1894
2057
|
}) => {
|
|
1895
|
-
const [localValue, setLocalValue] =
|
|
1896
|
-
const [isDragging, setIsDragging] =
|
|
1897
|
-
|
|
2058
|
+
const [localValue, setLocalValue] = useState7(value);
|
|
2059
|
+
const [isDragging, setIsDragging] = useState7(false);
|
|
2060
|
+
useEffect5(() => {
|
|
1898
2061
|
if (!isDragging) {
|
|
1899
2062
|
setLocalValue(value);
|
|
1900
2063
|
}
|
|
1901
2064
|
}, [value, isDragging]);
|
|
1902
2065
|
const debouncedOnChange = useDebouncedCallback(onChange, 50);
|
|
1903
|
-
const handleChange =
|
|
2066
|
+
const handleChange = useCallback4(
|
|
1904
2067
|
(e) => {
|
|
1905
2068
|
const newValue = parseFloat(e.target.value);
|
|
1906
2069
|
setLocalValue(newValue);
|
|
@@ -1908,19 +2071,19 @@ var VolumeSlider = ({
|
|
|
1908
2071
|
},
|
|
1909
2072
|
[debouncedOnChange]
|
|
1910
2073
|
);
|
|
1911
|
-
const handleMouseDown =
|
|
2074
|
+
const handleMouseDown = useCallback4(() => {
|
|
1912
2075
|
setIsDragging(true);
|
|
1913
2076
|
}, []);
|
|
1914
|
-
const handleMouseUp =
|
|
2077
|
+
const handleMouseUp = useCallback4(() => {
|
|
1915
2078
|
setIsDragging(false);
|
|
1916
2079
|
onChange(localValue);
|
|
1917
2080
|
}, [localValue, onChange]);
|
|
1918
|
-
return /* @__PURE__ */
|
|
2081
|
+
return /* @__PURE__ */ jsx10(
|
|
1919
2082
|
"div",
|
|
1920
2083
|
{
|
|
1921
2084
|
className: `flex items-center ${className}`,
|
|
1922
2085
|
title: `Volume: ${formatDb(localValue)}`,
|
|
1923
|
-
children: /* @__PURE__ */
|
|
2086
|
+
children: /* @__PURE__ */ jsx10(
|
|
1924
2087
|
"input",
|
|
1925
2088
|
{
|
|
1926
2089
|
type: "range",
|
|
@@ -1960,8 +2123,8 @@ var VolumeSlider = ({
|
|
|
1960
2123
|
};
|
|
1961
2124
|
|
|
1962
2125
|
// src/components/PanSlider.tsx
|
|
1963
|
-
import { useCallback as
|
|
1964
|
-
import { jsx as
|
|
2126
|
+
import { useCallback as useCallback5, useState as useState8, useRef as useRef6, useEffect as useEffect6 } from "react";
|
|
2127
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1965
2128
|
function toPanDisplay(value) {
|
|
1966
2129
|
if (Math.abs(value) < 0.02) {
|
|
1967
2130
|
return "Center";
|
|
@@ -1972,10 +2135,10 @@ function toPanDisplay(value) {
|
|
|
1972
2135
|
function useDebouncedCallback2(callback, delay) {
|
|
1973
2136
|
const timeoutRef = useRef6(null);
|
|
1974
2137
|
const callbackRef = useRef6(callback);
|
|
1975
|
-
|
|
2138
|
+
useEffect6(() => {
|
|
1976
2139
|
callbackRef.current = callback;
|
|
1977
2140
|
}, [callback]);
|
|
1978
|
-
const debouncedCallback =
|
|
2141
|
+
const debouncedCallback = useCallback5(
|
|
1979
2142
|
(...args) => {
|
|
1980
2143
|
if (timeoutRef.current) {
|
|
1981
2144
|
clearTimeout(timeoutRef.current);
|
|
@@ -1986,7 +2149,7 @@ function useDebouncedCallback2(callback, delay) {
|
|
|
1986
2149
|
},
|
|
1987
2150
|
[delay]
|
|
1988
2151
|
);
|
|
1989
|
-
|
|
2152
|
+
useEffect6(() => {
|
|
1990
2153
|
return () => {
|
|
1991
2154
|
if (timeoutRef.current) {
|
|
1992
2155
|
clearTimeout(timeoutRef.current);
|
|
@@ -2001,15 +2164,15 @@ var PanSlider = ({
|
|
|
2001
2164
|
disabled = false,
|
|
2002
2165
|
className = ""
|
|
2003
2166
|
}) => {
|
|
2004
|
-
const [localValue, setLocalValue] =
|
|
2005
|
-
const [isDragging, setIsDragging] =
|
|
2006
|
-
|
|
2167
|
+
const [localValue, setLocalValue] = useState8(value);
|
|
2168
|
+
const [isDragging, setIsDragging] = useState8(false);
|
|
2169
|
+
useEffect6(() => {
|
|
2007
2170
|
if (!isDragging) {
|
|
2008
2171
|
setLocalValue(value);
|
|
2009
2172
|
}
|
|
2010
2173
|
}, [value, isDragging]);
|
|
2011
2174
|
const debouncedOnChange = useDebouncedCallback2(onChange, 50);
|
|
2012
|
-
const handleChange =
|
|
2175
|
+
const handleChange = useCallback5(
|
|
2013
2176
|
(e) => {
|
|
2014
2177
|
const newValue = parseFloat(e.target.value);
|
|
2015
2178
|
setLocalValue(newValue);
|
|
@@ -2017,23 +2180,23 @@ var PanSlider = ({
|
|
|
2017
2180
|
},
|
|
2018
2181
|
[debouncedOnChange]
|
|
2019
2182
|
);
|
|
2020
|
-
const handleMouseDown =
|
|
2183
|
+
const handleMouseDown = useCallback5(() => {
|
|
2021
2184
|
setIsDragging(true);
|
|
2022
2185
|
}, []);
|
|
2023
|
-
const handleMouseUp =
|
|
2186
|
+
const handleMouseUp = useCallback5(() => {
|
|
2024
2187
|
setIsDragging(false);
|
|
2025
2188
|
onChange(localValue);
|
|
2026
2189
|
}, [localValue, onChange]);
|
|
2027
|
-
const handleDoubleClick =
|
|
2190
|
+
const handleDoubleClick = useCallback5(() => {
|
|
2028
2191
|
setLocalValue(0);
|
|
2029
2192
|
onChange(0);
|
|
2030
2193
|
}, [onChange]);
|
|
2031
|
-
return /* @__PURE__ */
|
|
2194
|
+
return /* @__PURE__ */ jsx11(
|
|
2032
2195
|
"div",
|
|
2033
2196
|
{
|
|
2034
2197
|
className: `flex items-center ${className}`,
|
|
2035
2198
|
title: `Pan: ${toPanDisplay(localValue)}`,
|
|
2036
|
-
children: /* @__PURE__ */
|
|
2199
|
+
children: /* @__PURE__ */ jsx11(
|
|
2037
2200
|
"input",
|
|
2038
2201
|
{
|
|
2039
2202
|
type: "range",
|
|
@@ -2074,8 +2237,8 @@ var PanSlider = ({
|
|
|
2074
2237
|
};
|
|
2075
2238
|
|
|
2076
2239
|
// src/components/SorceryProgressBar.tsx
|
|
2077
|
-
import { useState as
|
|
2078
|
-
import { jsx as
|
|
2240
|
+
import { useState as useState9, useEffect as useEffect7, useRef as useRef7 } from "react";
|
|
2241
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2079
2242
|
function calculateTimeBasedTarget(elapsedMs, estimatedDurationMs) {
|
|
2080
2243
|
const t = elapsedMs / estimatedDurationMs;
|
|
2081
2244
|
if (t <= 0) return 0;
|
|
@@ -2120,7 +2283,7 @@ function SorceryProgressBar({
|
|
|
2120
2283
|
onProgressChange,
|
|
2121
2284
|
estimatedDurationMs
|
|
2122
2285
|
}) {
|
|
2123
|
-
const [progress, setProgress] =
|
|
2286
|
+
const [progress, setProgress] = useState9(initialProgress);
|
|
2124
2287
|
const timerRef = useRef7(null);
|
|
2125
2288
|
const isLoadingRef = useRef7(false);
|
|
2126
2289
|
const hasStartedRef = useRef7(false);
|
|
@@ -2133,7 +2296,7 @@ function SorceryProgressBar({
|
|
|
2133
2296
|
initialProgressRef.current = initialProgress;
|
|
2134
2297
|
const estimatedDurationMsRef = useRef7(estimatedDurationMs);
|
|
2135
2298
|
estimatedDurationMsRef.current = estimatedDurationMs;
|
|
2136
|
-
|
|
2299
|
+
useEffect7(() => {
|
|
2137
2300
|
const wasLoading = isLoadingRef.current;
|
|
2138
2301
|
isLoadingRef.current = isLoading;
|
|
2139
2302
|
if (isLoading && !wasLoading) {
|
|
@@ -2195,12 +2358,12 @@ function SorceryProgressBar({
|
|
|
2195
2358
|
const displayProgress = Math.floor(progress);
|
|
2196
2359
|
const isComplete = !isLoading && progress === 100;
|
|
2197
2360
|
const transitionDuration = progress < 50 ? "300ms" : progress < 80 ? "500ms" : "700ms";
|
|
2198
|
-
return /* @__PURE__ */
|
|
2361
|
+
return /* @__PURE__ */ jsxs8(
|
|
2199
2362
|
"div",
|
|
2200
2363
|
{
|
|
2201
2364
|
className: `relative w-full ${heightClass} bg-sas-panel-alt border border-sas-border rounded-sm overflow-hidden shadow-inner`,
|
|
2202
2365
|
children: [
|
|
2203
|
-
/* @__PURE__ */
|
|
2366
|
+
/* @__PURE__ */ jsx12(
|
|
2204
2367
|
"div",
|
|
2205
2368
|
{
|
|
2206
2369
|
className: `
|
|
@@ -2218,13 +2381,13 @@ function SorceryProgressBar({
|
|
|
2218
2381
|
}
|
|
2219
2382
|
}
|
|
2220
2383
|
),
|
|
2221
|
-
/* @__PURE__ */
|
|
2384
|
+
/* @__PURE__ */ jsx12("div", { className: "absolute inset-0 flex items-center justify-center", children: isLoading && progress < 100 ? /* @__PURE__ */ jsxs8("span", { className: "font-mono text-xs text-sas-accent font-bold drop-shadow-md tracking-wider", children: [
|
|
2222
2385
|
statusText,
|
|
2223
2386
|
" ",
|
|
2224
2387
|
displayProgress,
|
|
2225
2388
|
"%"
|
|
2226
|
-
] }) : isComplete ? /* @__PURE__ */
|
|
2227
|
-
/* @__PURE__ */
|
|
2389
|
+
] }) : isComplete ? /* @__PURE__ */ jsx12("span", { className: "font-mono text-xs text-sas-text font-bold drop-shadow-md tracking-wider", children: completeText }) : null }),
|
|
2390
|
+
/* @__PURE__ */ jsx12(
|
|
2228
2391
|
"div",
|
|
2229
2392
|
{
|
|
2230
2393
|
className: "absolute inset-0 pointer-events-none opacity-10",
|
|
@@ -2310,7 +2473,7 @@ function parseLLMNoteResponse(content) {
|
|
|
2310
2473
|
}
|
|
2311
2474
|
|
|
2312
2475
|
// src/components/TrackRow.tsx
|
|
2313
|
-
import { Fragment, jsx as
|
|
2476
|
+
import { Fragment, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2314
2477
|
function TrackRow({
|
|
2315
2478
|
track,
|
|
2316
2479
|
prompt,
|
|
@@ -2360,6 +2523,7 @@ function TrackRow({
|
|
|
2360
2523
|
onToggleFavorite,
|
|
2361
2524
|
onImportSound,
|
|
2362
2525
|
importSoundLabel,
|
|
2526
|
+
linkedSoundHint,
|
|
2363
2527
|
editNotes,
|
|
2364
2528
|
onNotesChange,
|
|
2365
2529
|
editBars,
|
|
@@ -2371,6 +2535,8 @@ function TrackRow({
|
|
|
2371
2535
|
}) {
|
|
2372
2536
|
const { muted: isMuted, solo: isSoloed, volume: currentVolume, pan: currentPan } = runtimeState;
|
|
2373
2537
|
const [confirmDelete, setConfirmDelete] = React9.useState(false);
|
|
2538
|
+
const freeze = useTrackFreeze(externalFxHost, track.id);
|
|
2539
|
+
const freezeBadge = freeze.state?.frozen === true ? freeze.state.missingDeps.length > 0 ? "missing" : freeze.state.stale ? "stale" : "frozen" : null;
|
|
2374
2540
|
const needsGeneration = !!(prompt?.trim() && !hasMidi && !isGenerating);
|
|
2375
2541
|
const hasFxActive = Object.values(fxDetailState).some(
|
|
2376
2542
|
(d) => d.enabled
|
|
@@ -2380,8 +2546,8 @@ function TrackRow({
|
|
|
2380
2546
|
const handleKeyDown = promptEnterToGenerate(() => onGenerate?.(), !onGenerate);
|
|
2381
2547
|
const borderColorStyle = needsGeneration ? void 0 : accentColor;
|
|
2382
2548
|
const borderClass = needsGeneration ? "border-amber-400 animate-pulse" : "border-sas-border";
|
|
2383
|
-
return /* @__PURE__ */
|
|
2384
|
-
/* @__PURE__ */
|
|
2549
|
+
return /* @__PURE__ */ jsxs9("div", { "data-testid": "sdk-track-row-wrapper", className: "w-full", ...drag?.rowProps ?? {}, children: [
|
|
2550
|
+
/* @__PURE__ */ jsxs9(
|
|
2385
2551
|
"div",
|
|
2386
2552
|
{
|
|
2387
2553
|
"data-testid": "sdk-track-row",
|
|
@@ -2391,7 +2557,7 @@ function TrackRow({
|
|
|
2391
2557
|
borderLeftWidth: "3px"
|
|
2392
2558
|
},
|
|
2393
2559
|
children: [
|
|
2394
|
-
drag && /* @__PURE__ */
|
|
2560
|
+
drag && /* @__PURE__ */ jsx13(
|
|
2395
2561
|
"div",
|
|
2396
2562
|
{
|
|
2397
2563
|
"data-testid": "sdk-drag-handle",
|
|
@@ -2399,10 +2565,10 @@ function TrackRow({
|
|
|
2399
2565
|
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",
|
|
2400
2566
|
title: "Drag to reorder",
|
|
2401
2567
|
"aria-label": "Drag to reorder track",
|
|
2402
|
-
children: /* @__PURE__ */
|
|
2568
|
+
children: /* @__PURE__ */ jsx13(GripVertical, { className: "w-3.5 h-3.5", strokeWidth: 2 })
|
|
2403
2569
|
}
|
|
2404
2570
|
),
|
|
2405
|
-
isGenerating && /* @__PURE__ */
|
|
2571
|
+
isGenerating && /* @__PURE__ */ jsx13("div", { className: "absolute left-0 top-0 bottom-0 right-44 z-20", children: /* @__PURE__ */ jsx13(
|
|
2406
2572
|
SorceryProgressBar,
|
|
2407
2573
|
{
|
|
2408
2574
|
isLoading: true,
|
|
@@ -2413,14 +2579,14 @@ function TrackRow({
|
|
|
2413
2579
|
estimatedDurationMs: estimatedGenerationMs
|
|
2414
2580
|
}
|
|
2415
2581
|
) }),
|
|
2416
|
-
/* @__PURE__ */
|
|
2582
|
+
/* @__PURE__ */ jsxs9(
|
|
2417
2583
|
"div",
|
|
2418
2584
|
{
|
|
2419
2585
|
"data-testid": "sdk-track-content",
|
|
2420
2586
|
className: `flex flex-col flex-1 min-w-0 relative z-10 transition-opacity ${soloedOut ? "opacity-40" : ""}`,
|
|
2421
2587
|
title: soloedOut ? "Silenced \u2014 another track is soloed" : void 0,
|
|
2422
2588
|
children: [
|
|
2423
|
-
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */
|
|
2589
|
+
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ jsx13(
|
|
2424
2590
|
"input",
|
|
2425
2591
|
{
|
|
2426
2592
|
type: "text",
|
|
@@ -2433,10 +2599,10 @@ function TrackRow({
|
|
|
2433
2599
|
className: "sas-input w-full px-2 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed"
|
|
2434
2600
|
}
|
|
2435
2601
|
) : null,
|
|
2436
|
-
/* @__PURE__ */
|
|
2437
|
-
track.name && /* @__PURE__ */
|
|
2438
|
-
/* @__PURE__ */
|
|
2439
|
-
/* @__PURE__ */
|
|
2602
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 mt-1", children: [
|
|
2603
|
+
track.name && /* @__PURE__ */ jsx13("span", { className: "text-[10px] text-sas-muted/60 truncate pl-2 flex-shrink-0 max-w-[80px]", title: track.name, children: track.name }),
|
|
2604
|
+
/* @__PURE__ */ jsx13("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "vol:" }),
|
|
2605
|
+
/* @__PURE__ */ jsx13(
|
|
2440
2606
|
VolumeSlider,
|
|
2441
2607
|
{
|
|
2442
2608
|
value: currentVolume,
|
|
@@ -2445,8 +2611,8 @@ function TrackRow({
|
|
|
2445
2611
|
className: "flex-1 min-w-[40px]"
|
|
2446
2612
|
}
|
|
2447
2613
|
),
|
|
2448
|
-
/* @__PURE__ */
|
|
2449
|
-
/* @__PURE__ */
|
|
2614
|
+
/* @__PURE__ */ jsx13("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "pan:" }),
|
|
2615
|
+
/* @__PURE__ */ jsx13(
|
|
2450
2616
|
PanSlider,
|
|
2451
2617
|
{
|
|
2452
2618
|
value: currentPan,
|
|
@@ -2459,27 +2625,27 @@ function TrackRow({
|
|
|
2459
2625
|
]
|
|
2460
2626
|
}
|
|
2461
2627
|
),
|
|
2462
|
-
error && /* @__PURE__ */
|
|
2628
|
+
error && /* @__PURE__ */ jsx13(
|
|
2463
2629
|
"div",
|
|
2464
2630
|
{
|
|
2465
2631
|
"data-testid": "sdk-error-indicator",
|
|
2466
2632
|
className: "flex-shrink-0 relative z-10 self-stretch flex items-center px-1 group cursor-help",
|
|
2467
2633
|
title: error,
|
|
2468
|
-
children: /* @__PURE__ */
|
|
2469
|
-
/* @__PURE__ */
|
|
2634
|
+
children: /* @__PURE__ */ jsxs9("div", { className: "relative", children: [
|
|
2635
|
+
/* @__PURE__ */ jsx13(
|
|
2470
2636
|
AlertCircle,
|
|
2471
2637
|
{
|
|
2472
2638
|
className: "w-5 h-5 text-red-500 animate-pulse",
|
|
2473
2639
|
strokeWidth: 2.5
|
|
2474
2640
|
}
|
|
2475
2641
|
),
|
|
2476
|
-
/* @__PURE__ */
|
|
2642
|
+
/* @__PURE__ */ jsx13("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 })
|
|
2477
2643
|
] })
|
|
2478
2644
|
}
|
|
2479
2645
|
),
|
|
2480
|
-
/* @__PURE__ */
|
|
2481
|
-
/* @__PURE__ */
|
|
2482
|
-
onGenerate && /* @__PURE__ */
|
|
2646
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-0.5 flex-shrink-0 relative z-30 justify-center", children: [
|
|
2647
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex gap-1 items-center", children: [
|
|
2648
|
+
onGenerate && /* @__PURE__ */ jsx13(
|
|
2483
2649
|
"button",
|
|
2484
2650
|
{
|
|
2485
2651
|
"data-testid": "sdk-generate-button",
|
|
@@ -2490,7 +2656,7 @@ function TrackRow({
|
|
|
2490
2656
|
children: "Create"
|
|
2491
2657
|
}
|
|
2492
2658
|
),
|
|
2493
|
-
onCopy && /* @__PURE__ */
|
|
2659
|
+
onCopy && /* @__PURE__ */ jsx13(
|
|
2494
2660
|
"button",
|
|
2495
2661
|
{
|
|
2496
2662
|
"data-testid": "sdk-copy-button",
|
|
@@ -2501,7 +2667,7 @@ function TrackRow({
|
|
|
2501
2667
|
children: "Copy"
|
|
2502
2668
|
}
|
|
2503
2669
|
),
|
|
2504
|
-
/* @__PURE__ */
|
|
2670
|
+
/* @__PURE__ */ jsx13(
|
|
2505
2671
|
"button",
|
|
2506
2672
|
{
|
|
2507
2673
|
"data-testid": "sdk-mute-button",
|
|
@@ -2511,7 +2677,7 @@ function TrackRow({
|
|
|
2511
2677
|
children: "M"
|
|
2512
2678
|
}
|
|
2513
2679
|
),
|
|
2514
|
-
onDelete && /* @__PURE__ */
|
|
2680
|
+
onDelete && /* @__PURE__ */ jsx13(
|
|
2515
2681
|
"button",
|
|
2516
2682
|
{
|
|
2517
2683
|
"data-testid": "sdk-delete-button",
|
|
@@ -2522,19 +2688,39 @@ function TrackRow({
|
|
|
2522
2688
|
}
|
|
2523
2689
|
)
|
|
2524
2690
|
] }),
|
|
2525
|
-
/* @__PURE__ */
|
|
2526
|
-
onShuffle && /* @__PURE__ */
|
|
2691
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex gap-1 items-center", children: [
|
|
2692
|
+
onShuffle && /* @__PURE__ */ jsxs9(
|
|
2527
2693
|
"button",
|
|
2528
2694
|
{
|
|
2529
2695
|
"data-testid": "sdk-shuffle-button",
|
|
2530
2696
|
onClick: onShuffle,
|
|
2531
2697
|
disabled: !hasMidi || isGenerating || !!currentInstrumentPluginId,
|
|
2532
|
-
className: `w-14 py-0.5 rounded-sm text-xs font-medium transition-colors border ${!hasMidi || isGenerating || !!currentInstrumentPluginId ? "bg-sas-panel border-sas-border text-sas-muted/30 cursor-not-allowed" : "bg-sas-panel-alt border-sas-border text-sas-muted hover:border-sas-accent hover:text-sas-accent"}`,
|
|
2533
|
-
title: currentInstrumentPluginId ? "Shuffle only works with default Surge XT" : hasMidi ? "Re-roll sound (keep MIDI)" : "Generate MIDI first",
|
|
2534
|
-
children:
|
|
2698
|
+
className: `relative w-14 py-0.5 rounded-sm text-xs font-medium transition-colors border ${!hasMidi || isGenerating || !!currentInstrumentPluginId ? "bg-sas-panel border-sas-border text-sas-muted/30 cursor-not-allowed" : "bg-sas-panel-alt border-sas-border text-sas-muted hover:border-sas-accent hover:text-sas-accent"}`,
|
|
2699
|
+
title: currentInstrumentPluginId ? "Shuffle only works with default Surge XT" : hasMidi ? linkedSoundHint ? `Re-roll sound (keep MIDI) \u2014 ${linkedSoundHint}` : "Re-roll sound (keep MIDI)" : "Generate MIDI first",
|
|
2700
|
+
children: [
|
|
2701
|
+
"Shuffle",
|
|
2702
|
+
linkedSoundHint && /* @__PURE__ */ jsx13(
|
|
2703
|
+
"span",
|
|
2704
|
+
{
|
|
2705
|
+
"aria-hidden": true,
|
|
2706
|
+
"data-testid": "sdk-shuffle-linked-marker",
|
|
2707
|
+
className: "absolute -top-1 -right-1 text-[8px] leading-none",
|
|
2708
|
+
children: "\u{1F517}"
|
|
2709
|
+
}
|
|
2710
|
+
)
|
|
2711
|
+
]
|
|
2535
2712
|
}
|
|
2536
2713
|
),
|
|
2537
|
-
|
|
2714
|
+
freezeBadge && /* @__PURE__ */ jsx13(
|
|
2715
|
+
"span",
|
|
2716
|
+
{
|
|
2717
|
+
"data-testid": "sdk-track-freeze-badge",
|
|
2718
|
+
className: `px-1 py-0.5 text-xs leading-none rounded-sm self-center ${freezeBadge === "frozen" ? "text-sas-accent" : "text-amber-400"}`,
|
|
2719
|
+
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(", ")}`,
|
|
2720
|
+
children: freezeBadge === "frozen" ? "\u2744" : freezeBadge === "stale" ? "\u26A0\u2744" : "\u2744!"
|
|
2721
|
+
}
|
|
2722
|
+
),
|
|
2723
|
+
onToggleFxDrawer && /* @__PURE__ */ jsx13(
|
|
2538
2724
|
"button",
|
|
2539
2725
|
{
|
|
2540
2726
|
"data-testid": "sdk-fx-button",
|
|
@@ -2545,7 +2731,7 @@ function TrackRow({
|
|
|
2545
2731
|
children: "FX"
|
|
2546
2732
|
}
|
|
2547
2733
|
),
|
|
2548
|
-
/* @__PURE__ */
|
|
2734
|
+
/* @__PURE__ */ jsx13(
|
|
2549
2735
|
"button",
|
|
2550
2736
|
{
|
|
2551
2737
|
"data-testid": "sdk-solo-button",
|
|
@@ -2556,7 +2742,7 @@ function TrackRow({
|
|
|
2556
2742
|
children: "S"
|
|
2557
2743
|
}
|
|
2558
2744
|
),
|
|
2559
|
-
onToggleDrawer && /* @__PURE__ */
|
|
2745
|
+
onToggleDrawer && /* @__PURE__ */ jsx13(
|
|
2560
2746
|
"button",
|
|
2561
2747
|
{
|
|
2562
2748
|
"data-testid": "sdk-plugin-button",
|
|
@@ -2564,7 +2750,7 @@ function TrackRow({
|
|
|
2564
2750
|
disabled: isGenerating,
|
|
2565
2751
|
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"}`,
|
|
2566
2752
|
title: `Sound \u2014 presets & history${instrumentMissing ? " (instrument missing)" : ""}`,
|
|
2567
|
-
children: /* @__PURE__ */
|
|
2753
|
+
children: /* @__PURE__ */ jsx13(ChevronDown, { className: "w-3 h-3", strokeWidth: 2.5 })
|
|
2568
2754
|
}
|
|
2569
2755
|
)
|
|
2570
2756
|
] })
|
|
@@ -2572,13 +2758,13 @@ function TrackRow({
|
|
|
2572
2758
|
]
|
|
2573
2759
|
}
|
|
2574
2760
|
),
|
|
2575
|
-
levels && /* @__PURE__ */
|
|
2576
|
-
drawerOpen && /* @__PURE__ */
|
|
2761
|
+
levels && /* @__PURE__ */ jsx13(TrackMeterStrip, { levels, trackId: track.id, roundBottom: !drawerOpen }),
|
|
2762
|
+
drawerOpen && /* @__PURE__ */ jsx13(
|
|
2577
2763
|
"div",
|
|
2578
2764
|
{
|
|
2579
2765
|
"data-testid": "sdk-track-drawer",
|
|
2580
2766
|
className: "border border-t-0 border-sas-border bg-sas-bg rounded-b-sm px-3 py-2 max-h-[260px] overflow-y-auto",
|
|
2581
|
-
children: /* @__PURE__ */
|
|
2767
|
+
children: /* @__PURE__ */ jsx13(
|
|
2582
2768
|
TrackDrawer,
|
|
2583
2769
|
{
|
|
2584
2770
|
activeTab: drawerTab,
|
|
@@ -2589,6 +2775,7 @@ function TrackRow({
|
|
|
2589
2775
|
onFxPresetChange,
|
|
2590
2776
|
onFxDryWetChange,
|
|
2591
2777
|
externalFxHost,
|
|
2778
|
+
freeze,
|
|
2592
2779
|
fxDisabled: isGenerating,
|
|
2593
2780
|
instruments: availableInstruments,
|
|
2594
2781
|
currentPluginId: currentInstrumentPluginId ?? null,
|
|
@@ -2605,6 +2792,7 @@ function TrackRow({
|
|
|
2605
2792
|
onToggleFavorite,
|
|
2606
2793
|
onImportSound,
|
|
2607
2794
|
importSoundLabel,
|
|
2795
|
+
linkedSoundHint,
|
|
2608
2796
|
editNotes,
|
|
2609
2797
|
onNotesChange,
|
|
2610
2798
|
editBars,
|
|
@@ -2615,13 +2803,13 @@ function TrackRow({
|
|
|
2615
2803
|
)
|
|
2616
2804
|
}
|
|
2617
2805
|
),
|
|
2618
|
-
/* @__PURE__ */
|
|
2806
|
+
/* @__PURE__ */ jsx13(
|
|
2619
2807
|
ConfirmDialog,
|
|
2620
2808
|
{
|
|
2621
2809
|
open: confirmDelete,
|
|
2622
2810
|
title: "Delete track?",
|
|
2623
|
-
message: /* @__PURE__ */
|
|
2624
|
-
/* @__PURE__ */
|
|
2811
|
+
message: /* @__PURE__ */ jsxs9(Fragment, { children: [
|
|
2812
|
+
/* @__PURE__ */ jsx13("span", { className: "text-sas-text", children: track.name?.trim() || "This track" }),
|
|
2625
2813
|
" will be permanently removed from this scene. This cannot be undone."
|
|
2626
2814
|
] }),
|
|
2627
2815
|
confirmLabel: "Delete",
|
|
@@ -2638,12 +2826,12 @@ function TrackRow({
|
|
|
2638
2826
|
|
|
2639
2827
|
// src/components/CrossfadeTrackRow.tsx
|
|
2640
2828
|
import React10 from "react";
|
|
2641
|
-
import { Fragment as Fragment2, jsx as
|
|
2829
|
+
import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2642
2830
|
function LayerCaption({ tag, layer }) {
|
|
2643
|
-
return /* @__PURE__ */
|
|
2644
|
-
/* @__PURE__ */
|
|
2645
|
-
/* @__PURE__ */
|
|
2646
|
-
layer.soundLabel && /* @__PURE__ */
|
|
2831
|
+
return /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
2832
|
+
/* @__PURE__ */ jsx14("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
|
|
2833
|
+
/* @__PURE__ */ jsx14("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
|
|
2834
|
+
layer.soundLabel && /* @__PURE__ */ jsxs10("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
|
|
2647
2835
|
"\xB7 ",
|
|
2648
2836
|
layer.soundLabel
|
|
2649
2837
|
] })
|
|
@@ -2663,7 +2851,7 @@ function CrossfadeTrackRow({
|
|
|
2663
2851
|
accentColor = "#9333EA"
|
|
2664
2852
|
}) {
|
|
2665
2853
|
const [confirmDelete, setConfirmDelete] = React10.useState(false);
|
|
2666
|
-
const renderLayer = (layer, slot, tag) => /* @__PURE__ */
|
|
2854
|
+
const renderLayer = (layer, slot, tag) => /* @__PURE__ */ jsx14(
|
|
2667
2855
|
TrackRow,
|
|
2668
2856
|
{
|
|
2669
2857
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -2673,23 +2861,23 @@ function CrossfadeTrackRow({
|
|
|
2673
2861
|
drawerTab: "fx",
|
|
2674
2862
|
levels,
|
|
2675
2863
|
accentColor,
|
|
2676
|
-
contentSlot: /* @__PURE__ */
|
|
2864
|
+
contentSlot: /* @__PURE__ */ jsx14(LayerCaption, { tag, layer }),
|
|
2677
2865
|
onMuteToggle,
|
|
2678
2866
|
onSoloToggle,
|
|
2679
2867
|
onVolumeChange: (v) => onVolumeChange(slot, v),
|
|
2680
2868
|
onPanChange: (p) => onPanChange(slot, p)
|
|
2681
2869
|
}
|
|
2682
2870
|
);
|
|
2683
|
-
return /* @__PURE__ */
|
|
2871
|
+
return /* @__PURE__ */ jsxs10(
|
|
2684
2872
|
"div",
|
|
2685
2873
|
{
|
|
2686
2874
|
"data-testid": "crossfade-track-row",
|
|
2687
2875
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
2688
2876
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
2689
2877
|
children: [
|
|
2690
|
-
/* @__PURE__ */
|
|
2691
|
-
/* @__PURE__ */
|
|
2692
|
-
/* @__PURE__ */
|
|
2878
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
|
|
2879
|
+
/* @__PURE__ */ jsx14("span", { className: "text-[10px] font-bold uppercase tracking-wide", style: { color: accentColor }, children: "\u21C4 Crossfade" }),
|
|
2880
|
+
/* @__PURE__ */ jsx14(
|
|
2693
2881
|
"button",
|
|
2694
2882
|
{
|
|
2695
2883
|
"data-testid": "crossfade-delete-button",
|
|
@@ -2702,8 +2890,8 @@ function CrossfadeTrackRow({
|
|
|
2702
2890
|
)
|
|
2703
2891
|
] }),
|
|
2704
2892
|
renderLayer(origin, "origin", "Origin"),
|
|
2705
|
-
/* @__PURE__ */
|
|
2706
|
-
/* @__PURE__ */
|
|
2893
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "crossfade-slider-row", children: [
|
|
2894
|
+
/* @__PURE__ */ jsx14(
|
|
2707
2895
|
"span",
|
|
2708
2896
|
{
|
|
2709
2897
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -2711,7 +2899,7 @@ function CrossfadeTrackRow({
|
|
|
2711
2899
|
children: origin.sourceName ?? origin.name
|
|
2712
2900
|
}
|
|
2713
2901
|
),
|
|
2714
|
-
/* @__PURE__ */
|
|
2902
|
+
/* @__PURE__ */ jsx14(
|
|
2715
2903
|
"input",
|
|
2716
2904
|
{
|
|
2717
2905
|
type: "range",
|
|
@@ -2727,7 +2915,7 @@ function CrossfadeTrackRow({
|
|
|
2727
2915
|
"aria-label": "Crossfade position"
|
|
2728
2916
|
}
|
|
2729
2917
|
),
|
|
2730
|
-
/* @__PURE__ */
|
|
2918
|
+
/* @__PURE__ */ jsx14(
|
|
2731
2919
|
"span",
|
|
2732
2920
|
{
|
|
2733
2921
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -2737,12 +2925,12 @@ function CrossfadeTrackRow({
|
|
|
2737
2925
|
)
|
|
2738
2926
|
] }),
|
|
2739
2927
|
renderLayer(target, "target", "Target"),
|
|
2740
|
-
/* @__PURE__ */
|
|
2928
|
+
/* @__PURE__ */ jsx14(
|
|
2741
2929
|
ConfirmDialog,
|
|
2742
2930
|
{
|
|
2743
2931
|
open: confirmDelete,
|
|
2744
2932
|
title: "Delete crossfade?",
|
|
2745
|
-
message: /* @__PURE__ */
|
|
2933
|
+
message: /* @__PURE__ */ jsx14(Fragment2, { children: "This crossfade pair (both layers) will be permanently removed from this scene. This cannot be undone." }),
|
|
2746
2934
|
confirmLabel: "Delete",
|
|
2747
2935
|
onConfirm: () => {
|
|
2748
2936
|
setConfirmDelete(false);
|
|
@@ -3016,21 +3204,21 @@ function defaultFadeGesture(role) {
|
|
|
3016
3204
|
|
|
3017
3205
|
// src/components/FadeTrackRow.tsx
|
|
3018
3206
|
import React11 from "react";
|
|
3019
|
-
import { Fragment as Fragment3, jsx as
|
|
3207
|
+
import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3020
3208
|
function FadeCaption({
|
|
3021
3209
|
layer,
|
|
3022
3210
|
direction,
|
|
3023
3211
|
gesture
|
|
3024
3212
|
}) {
|
|
3025
3213
|
const tag = direction === "in" ? "Fade in" : "Fade out";
|
|
3026
|
-
return /* @__PURE__ */
|
|
3027
|
-
/* @__PURE__ */
|
|
3028
|
-
/* @__PURE__ */
|
|
3029
|
-
layer.soundLabel && /* @__PURE__ */
|
|
3214
|
+
return /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
3215
|
+
/* @__PURE__ */ jsx15("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
|
|
3216
|
+
/* @__PURE__ */ jsx15("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
|
|
3217
|
+
layer.soundLabel && /* @__PURE__ */ jsxs11("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
|
|
3030
3218
|
"\xB7 ",
|
|
3031
3219
|
layer.soundLabel
|
|
3032
3220
|
] }),
|
|
3033
|
-
/* @__PURE__ */
|
|
3221
|
+
/* @__PURE__ */ jsxs11("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", title: `Fade gesture: ${gesture}`, children: [
|
|
3034
3222
|
"\xB7 ",
|
|
3035
3223
|
gesture
|
|
3036
3224
|
] })
|
|
@@ -3056,15 +3244,15 @@ function FadeTrackRow({
|
|
|
3056
3244
|
const rightLabel = direction === "in" ? layer.sourceName ?? layer.name : "(silent)";
|
|
3057
3245
|
const verb = effect && effect !== "fade" ? effect.charAt(0).toUpperCase() + effect.slice(1) : "Fade";
|
|
3058
3246
|
const badge = direction === "in" ? `\u2197 ${verb} in` : `\u2198 ${verb} out`;
|
|
3059
|
-
return /* @__PURE__ */
|
|
3247
|
+
return /* @__PURE__ */ jsxs11(
|
|
3060
3248
|
"div",
|
|
3061
3249
|
{
|
|
3062
3250
|
"data-testid": "fade-track-row",
|
|
3063
3251
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
3064
3252
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
3065
3253
|
children: [
|
|
3066
|
-
/* @__PURE__ */
|
|
3067
|
-
/* @__PURE__ */
|
|
3254
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
|
|
3255
|
+
/* @__PURE__ */ jsx15(
|
|
3068
3256
|
"span",
|
|
3069
3257
|
{
|
|
3070
3258
|
"data-testid": "fade-direction-badge",
|
|
@@ -3073,7 +3261,7 @@ function FadeTrackRow({
|
|
|
3073
3261
|
children: badge
|
|
3074
3262
|
}
|
|
3075
3263
|
),
|
|
3076
|
-
/* @__PURE__ */
|
|
3264
|
+
/* @__PURE__ */ jsx15(
|
|
3077
3265
|
"button",
|
|
3078
3266
|
{
|
|
3079
3267
|
"data-testid": "fade-delete-button",
|
|
@@ -3085,7 +3273,7 @@ function FadeTrackRow({
|
|
|
3085
3273
|
}
|
|
3086
3274
|
)
|
|
3087
3275
|
] }),
|
|
3088
|
-
/* @__PURE__ */
|
|
3276
|
+
/* @__PURE__ */ jsx15(
|
|
3089
3277
|
TrackRow,
|
|
3090
3278
|
{
|
|
3091
3279
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -3095,15 +3283,15 @@ function FadeTrackRow({
|
|
|
3095
3283
|
drawerTab: "fx",
|
|
3096
3284
|
levels,
|
|
3097
3285
|
accentColor,
|
|
3098
|
-
contentSlot: /* @__PURE__ */
|
|
3286
|
+
contentSlot: /* @__PURE__ */ jsx15(FadeCaption, { layer, direction, gesture }),
|
|
3099
3287
|
onMuteToggle,
|
|
3100
3288
|
onSoloToggle,
|
|
3101
3289
|
onVolumeChange,
|
|
3102
3290
|
onPanChange
|
|
3103
3291
|
}
|
|
3104
3292
|
),
|
|
3105
|
-
/* @__PURE__ */
|
|
3106
|
-
/* @__PURE__ */
|
|
3293
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "fade-slider-row", children: [
|
|
3294
|
+
/* @__PURE__ */ jsx15(
|
|
3107
3295
|
"span",
|
|
3108
3296
|
{
|
|
3109
3297
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -3111,7 +3299,7 @@ function FadeTrackRow({
|
|
|
3111
3299
|
children: leftLabel
|
|
3112
3300
|
}
|
|
3113
3301
|
),
|
|
3114
|
-
/* @__PURE__ */
|
|
3302
|
+
/* @__PURE__ */ jsx15(
|
|
3115
3303
|
"input",
|
|
3116
3304
|
{
|
|
3117
3305
|
type: "range",
|
|
@@ -3127,7 +3315,7 @@ function FadeTrackRow({
|
|
|
3127
3315
|
"aria-label": "Fade position"
|
|
3128
3316
|
}
|
|
3129
3317
|
),
|
|
3130
|
-
/* @__PURE__ */
|
|
3318
|
+
/* @__PURE__ */ jsx15(
|
|
3131
3319
|
"span",
|
|
3132
3320
|
{
|
|
3133
3321
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -3136,12 +3324,12 @@ function FadeTrackRow({
|
|
|
3136
3324
|
}
|
|
3137
3325
|
)
|
|
3138
3326
|
] }),
|
|
3139
|
-
/* @__PURE__ */
|
|
3327
|
+
/* @__PURE__ */ jsx15(
|
|
3140
3328
|
ConfirmDialog,
|
|
3141
3329
|
{
|
|
3142
3330
|
open: confirmDelete,
|
|
3143
3331
|
title: "Delete fade?",
|
|
3144
|
-
message: /* @__PURE__ */
|
|
3332
|
+
message: /* @__PURE__ */ jsx15(Fragment3, { children: "This fade track will be permanently removed from this scene. This cannot be undone." }),
|
|
3145
3333
|
confirmLabel: "Delete",
|
|
3146
3334
|
onConfirm: () => {
|
|
3147
3335
|
setConfirmDelete(false);
|
|
@@ -3158,20 +3346,20 @@ function FadeTrackRow({
|
|
|
3158
3346
|
|
|
3159
3347
|
// src/components/GroupFadeTrackRow.tsx
|
|
3160
3348
|
import React12 from "react";
|
|
3161
|
-
import { Fragment as Fragment4, jsx as
|
|
3349
|
+
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3162
3350
|
function MemberCaption({
|
|
3163
3351
|
member,
|
|
3164
3352
|
direction
|
|
3165
3353
|
}) {
|
|
3166
3354
|
const tag = direction === "in" ? "Fade in" : "Fade out";
|
|
3167
|
-
return /* @__PURE__ */
|
|
3168
|
-
member.memberLabel && /* @__PURE__ */
|
|
3169
|
-
/* @__PURE__ */
|
|
3170
|
-
member.soundLabel && /* @__PURE__ */
|
|
3355
|
+
return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
3356
|
+
member.memberLabel && /* @__PURE__ */ jsx16("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: member.memberLabel }),
|
|
3357
|
+
/* @__PURE__ */ jsx16("span", { className: "text-[11px] text-sas-text truncate", title: member.sourceName ?? member.name, children: member.sourceName ?? member.name }),
|
|
3358
|
+
member.soundLabel && /* @__PURE__ */ jsxs12("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: member.soundLabel, children: [
|
|
3171
3359
|
"\xB7 ",
|
|
3172
3360
|
member.soundLabel
|
|
3173
3361
|
] }),
|
|
3174
|
-
!member.memberLabel && /* @__PURE__ */
|
|
3362
|
+
!member.memberLabel && /* @__PURE__ */ jsxs12("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: [
|
|
3175
3363
|
"\xB7 ",
|
|
3176
3364
|
tag
|
|
3177
3365
|
] })
|
|
@@ -3200,16 +3388,16 @@ function GroupFadeTrackRow({
|
|
|
3200
3388
|
const badge = direction === "in" ? "\u2197 Fade in" : "\u2198 Fade out";
|
|
3201
3389
|
const leftLabel = direction === "in" ? "(silent)" : groupLabel;
|
|
3202
3390
|
const rightLabel = direction === "in" ? groupLabel : "(silent)";
|
|
3203
|
-
return /* @__PURE__ */
|
|
3391
|
+
return /* @__PURE__ */ jsxs12(
|
|
3204
3392
|
"div",
|
|
3205
3393
|
{
|
|
3206
3394
|
"data-testid": "group-fade-track-row",
|
|
3207
3395
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
3208
3396
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
3209
3397
|
children: [
|
|
3210
|
-
/* @__PURE__ */
|
|
3211
|
-
/* @__PURE__ */
|
|
3212
|
-
/* @__PURE__ */
|
|
3398
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60 gap-2", children: [
|
|
3399
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
3400
|
+
/* @__PURE__ */ jsx16(
|
|
3213
3401
|
"span",
|
|
3214
3402
|
{
|
|
3215
3403
|
"data-testid": "group-fade-direction-badge",
|
|
@@ -3218,7 +3406,7 @@ function GroupFadeTrackRow({
|
|
|
3218
3406
|
children: badge
|
|
3219
3407
|
}
|
|
3220
3408
|
),
|
|
3221
|
-
/* @__PURE__ */
|
|
3409
|
+
/* @__PURE__ */ jsx16(
|
|
3222
3410
|
"span",
|
|
3223
3411
|
{
|
|
3224
3412
|
"data-testid": "group-fade-label",
|
|
@@ -3228,8 +3416,8 @@ function GroupFadeTrackRow({
|
|
|
3228
3416
|
}
|
|
3229
3417
|
)
|
|
3230
3418
|
] }),
|
|
3231
|
-
/* @__PURE__ */
|
|
3232
|
-
/* @__PURE__ */
|
|
3419
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
|
|
3420
|
+
/* @__PURE__ */ jsx16(
|
|
3233
3421
|
"button",
|
|
3234
3422
|
{
|
|
3235
3423
|
"data-testid": "group-fade-mute-button",
|
|
@@ -3239,7 +3427,7 @@ function GroupFadeTrackRow({
|
|
|
3239
3427
|
children: "M"
|
|
3240
3428
|
}
|
|
3241
3429
|
),
|
|
3242
|
-
/* @__PURE__ */
|
|
3430
|
+
/* @__PURE__ */ jsx16(
|
|
3243
3431
|
"button",
|
|
3244
3432
|
{
|
|
3245
3433
|
"data-testid": "group-fade-solo-button",
|
|
@@ -3249,7 +3437,7 @@ function GroupFadeTrackRow({
|
|
|
3249
3437
|
children: "S"
|
|
3250
3438
|
}
|
|
3251
3439
|
),
|
|
3252
|
-
/* @__PURE__ */
|
|
3440
|
+
/* @__PURE__ */ jsx16(
|
|
3253
3441
|
"button",
|
|
3254
3442
|
{
|
|
3255
3443
|
"data-testid": "group-fade-delete-button",
|
|
@@ -3262,7 +3450,7 @@ function GroupFadeTrackRow({
|
|
|
3262
3450
|
)
|
|
3263
3451
|
] })
|
|
3264
3452
|
] }),
|
|
3265
|
-
members.map((member) => /* @__PURE__ */
|
|
3453
|
+
members.map((member) => /* @__PURE__ */ jsx16(
|
|
3266
3454
|
TrackRow,
|
|
3267
3455
|
{
|
|
3268
3456
|
track: { id: member.trackId, name: "", role: member.role },
|
|
@@ -3272,7 +3460,7 @@ function GroupFadeTrackRow({
|
|
|
3272
3460
|
drawerTab: "fx",
|
|
3273
3461
|
levels,
|
|
3274
3462
|
accentColor,
|
|
3275
|
-
contentSlot: /* @__PURE__ */
|
|
3463
|
+
contentSlot: /* @__PURE__ */ jsx16(MemberCaption, { member, direction }),
|
|
3276
3464
|
onMuteToggle: () => onMemberMuteToggle(member.trackId),
|
|
3277
3465
|
onSoloToggle: () => onMemberSoloToggle(member.trackId),
|
|
3278
3466
|
onVolumeChange: (vol) => onMemberVolumeChange(member.trackId, vol),
|
|
@@ -3280,8 +3468,8 @@ function GroupFadeTrackRow({
|
|
|
3280
3468
|
},
|
|
3281
3469
|
member.trackId
|
|
3282
3470
|
)),
|
|
3283
|
-
/* @__PURE__ */
|
|
3284
|
-
/* @__PURE__ */
|
|
3471
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "group-fade-slider-row", children: [
|
|
3472
|
+
/* @__PURE__ */ jsx16(
|
|
3285
3473
|
"span",
|
|
3286
3474
|
{
|
|
3287
3475
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -3289,7 +3477,7 @@ function GroupFadeTrackRow({
|
|
|
3289
3477
|
children: leftLabel
|
|
3290
3478
|
}
|
|
3291
3479
|
),
|
|
3292
|
-
/* @__PURE__ */
|
|
3480
|
+
/* @__PURE__ */ jsx16(
|
|
3293
3481
|
"input",
|
|
3294
3482
|
{
|
|
3295
3483
|
type: "range",
|
|
@@ -3305,7 +3493,7 @@ function GroupFadeTrackRow({
|
|
|
3305
3493
|
"aria-label": "Group fade position"
|
|
3306
3494
|
}
|
|
3307
3495
|
),
|
|
3308
|
-
/* @__PURE__ */
|
|
3496
|
+
/* @__PURE__ */ jsx16(
|
|
3309
3497
|
"span",
|
|
3310
3498
|
{
|
|
3311
3499
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -3314,12 +3502,12 @@ function GroupFadeTrackRow({
|
|
|
3314
3502
|
}
|
|
3315
3503
|
)
|
|
3316
3504
|
] }),
|
|
3317
|
-
/* @__PURE__ */
|
|
3505
|
+
/* @__PURE__ */ jsx16(
|
|
3318
3506
|
ConfirmDialog,
|
|
3319
3507
|
{
|
|
3320
3508
|
open: confirmDelete,
|
|
3321
3509
|
title: "Delete group fade?",
|
|
3322
|
-
message: /* @__PURE__ */
|
|
3510
|
+
message: /* @__PURE__ */ jsxs12(Fragment4, { children: [
|
|
3323
3511
|
"All ",
|
|
3324
3512
|
members.length,
|
|
3325
3513
|
" member tracks of this fade will be permanently removed from this scene. This cannot be undone."
|
|
@@ -3339,8 +3527,8 @@ function GroupFadeTrackRow({
|
|
|
3339
3527
|
}
|
|
3340
3528
|
|
|
3341
3529
|
// src/components/FadeModal.tsx
|
|
3342
|
-
import { useCallback as
|
|
3343
|
-
import { Fragment as Fragment5, jsx as
|
|
3530
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef8, useState as useState10 } from "react";
|
|
3531
|
+
import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3344
3532
|
function shortId(dbId) {
|
|
3345
3533
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
3346
3534
|
}
|
|
@@ -3383,7 +3571,7 @@ function OrphanRow({
|
|
|
3383
3571
|
}) {
|
|
3384
3572
|
const primary = track.prompt?.trim() || track.name;
|
|
3385
3573
|
const meta = [track.role, shortId(track.dbId), gesture].filter(Boolean).join(" \xB7 ");
|
|
3386
|
-
return /* @__PURE__ */
|
|
3574
|
+
return /* @__PURE__ */ jsxs13(
|
|
3387
3575
|
"button",
|
|
3388
3576
|
{
|
|
3389
3577
|
type: "button",
|
|
@@ -3395,8 +3583,8 @@ function OrphanRow({
|
|
|
3395
3583
|
disabled,
|
|
3396
3584
|
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"}`,
|
|
3397
3585
|
children: [
|
|
3398
|
-
/* @__PURE__ */
|
|
3399
|
-
meta && /* @__PURE__ */
|
|
3586
|
+
/* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
|
|
3587
|
+
meta && /* @__PURE__ */ jsx17("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
|
|
3400
3588
|
]
|
|
3401
3589
|
}
|
|
3402
3590
|
);
|
|
@@ -3413,14 +3601,14 @@ function FadeModal({
|
|
|
3413
3601
|
onCreate,
|
|
3414
3602
|
testIdPrefix = "fade-modal"
|
|
3415
3603
|
}) {
|
|
3416
|
-
const [load, setLoad] =
|
|
3417
|
-
const [selectedDbId, setSelectedDbId] =
|
|
3418
|
-
const [isCreating, setIsCreating] =
|
|
3419
|
-
const [error, setError] =
|
|
3420
|
-
const [fromName, setFromName] =
|
|
3421
|
-
const [toName, setToName] =
|
|
3604
|
+
const [load, setLoad] = useState10({ status: "loading" });
|
|
3605
|
+
const [selectedDbId, setSelectedDbId] = useState10("");
|
|
3606
|
+
const [isCreating, setIsCreating] = useState10(false);
|
|
3607
|
+
const [error, setError] = useState10(null);
|
|
3608
|
+
const [fromName, setFromName] = useState10(null);
|
|
3609
|
+
const [toName, setToName] = useState10(null);
|
|
3422
3610
|
const cancelRef = useRef8(null);
|
|
3423
|
-
const refresh =
|
|
3611
|
+
const refresh = useCallback6(async () => {
|
|
3424
3612
|
if (!host.listSceneFamilyTracks) {
|
|
3425
3613
|
setLoad({ status: "error", message: "This host does not support fades." });
|
|
3426
3614
|
return;
|
|
@@ -3440,7 +3628,7 @@ function FadeModal({
|
|
|
3440
3628
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
3441
3629
|
}
|
|
3442
3630
|
}, [host, fromSceneId, toSceneId]);
|
|
3443
|
-
|
|
3631
|
+
useEffect8(() => {
|
|
3444
3632
|
if (open) {
|
|
3445
3633
|
setError(null);
|
|
3446
3634
|
setIsCreating(false);
|
|
@@ -3460,17 +3648,17 @@ function FadeModal({
|
|
|
3460
3648
|
],
|
|
3461
3649
|
[fadeOut, fadeIn]
|
|
3462
3650
|
);
|
|
3463
|
-
|
|
3651
|
+
useEffect8(() => {
|
|
3464
3652
|
if (!allOrphans.some((o) => o.track.dbId === selectedDbId)) {
|
|
3465
3653
|
setSelectedDbId(allOrphans[0]?.track.dbId ?? "");
|
|
3466
3654
|
}
|
|
3467
3655
|
}, [allOrphans, selectedDbId]);
|
|
3468
3656
|
const selected = allOrphans.find((o) => o.track.dbId === selectedDbId) ?? null;
|
|
3469
3657
|
const canCreate = !isCreating && !!selected;
|
|
3470
|
-
const handleClose =
|
|
3658
|
+
const handleClose = useCallback6(() => {
|
|
3471
3659
|
if (!isCreating) onClose();
|
|
3472
3660
|
}, [isCreating, onClose]);
|
|
3473
|
-
const handleCreate =
|
|
3661
|
+
const handleCreate = useCallback6(async () => {
|
|
3474
3662
|
if (!selected) return;
|
|
3475
3663
|
setIsCreating(true);
|
|
3476
3664
|
setError(null);
|
|
@@ -3491,16 +3679,16 @@ function FadeModal({
|
|
|
3491
3679
|
if (!open) return null;
|
|
3492
3680
|
const renderSection = (heading, list, section) => {
|
|
3493
3681
|
if (list.length === 0) return null;
|
|
3494
|
-
return /* @__PURE__ */
|
|
3495
|
-
/* @__PURE__ */
|
|
3496
|
-
/* @__PURE__ */
|
|
3682
|
+
return /* @__PURE__ */ jsxs13("div", { className: "block", children: [
|
|
3683
|
+
/* @__PURE__ */ jsx17("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: heading }),
|
|
3684
|
+
/* @__PURE__ */ jsx17(
|
|
3497
3685
|
"div",
|
|
3498
3686
|
{
|
|
3499
3687
|
role: "radiogroup",
|
|
3500
3688
|
"aria-label": heading,
|
|
3501
3689
|
"data-testid": `${testIdPrefix}-${section === "out" ? "fade-out" : "fade-in"}-list`,
|
|
3502
3690
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3503
|
-
children: list.map((t) => /* @__PURE__ */
|
|
3691
|
+
children: list.map((t) => /* @__PURE__ */ jsx17(
|
|
3504
3692
|
OrphanRow,
|
|
3505
3693
|
{
|
|
3506
3694
|
track: t,
|
|
@@ -3516,32 +3704,32 @@ function FadeModal({
|
|
|
3516
3704
|
)
|
|
3517
3705
|
] });
|
|
3518
3706
|
};
|
|
3519
|
-
return /* @__PURE__ */
|
|
3707
|
+
return /* @__PURE__ */ jsx17(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs13(
|
|
3520
3708
|
"div",
|
|
3521
3709
|
{
|
|
3522
3710
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
3523
3711
|
onClick: (e) => e.stopPropagation(),
|
|
3524
3712
|
"data-testid": `${testIdPrefix}-box`,
|
|
3525
3713
|
children: [
|
|
3526
|
-
/* @__PURE__ */
|
|
3527
|
-
/* @__PURE__ */
|
|
3714
|
+
/* @__PURE__ */ jsx17("h3", { className: "text-sm font-bold text-sas-text", children: "Add fade" }),
|
|
3715
|
+
/* @__PURE__ */ jsxs13("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
|
|
3528
3716
|
"Tracks with no counterpart between",
|
|
3529
3717
|
" ",
|
|
3530
|
-
/* @__PURE__ */
|
|
3718
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
3531
3719
|
" and",
|
|
3532
3720
|
" ",
|
|
3533
|
-
/* @__PURE__ */
|
|
3721
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
3534
3722
|
" can gracefully fade out (leaving) or fade in (entering) across this transition."
|
|
3535
3723
|
] }),
|
|
3536
|
-
load.status === "loading" && /* @__PURE__ */
|
|
3537
|
-
load.status === "error" && /* @__PURE__ */
|
|
3538
|
-
load.status === "ready" && (allOrphans.length === 0 ? /* @__PURE__ */
|
|
3724
|
+
load.status === "loading" && /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
|
|
3725
|
+
load.status === "error" && /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
|
|
3726
|
+
load.status === "ready" && (allOrphans.length === 0 ? /* @__PURE__ */ jsx17("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__ */ jsxs13(Fragment5, { children: [
|
|
3539
3727
|
renderSection(`Fade out${fromLabel ? ` (from ${fromLabel})` : ""}`, fadeOut, "out"),
|
|
3540
3728
|
renderSection(`Fade in${toLabel ? ` (to ${toLabel})` : ""}`, fadeIn, "in")
|
|
3541
3729
|
] })),
|
|
3542
|
-
error && /* @__PURE__ */
|
|
3543
|
-
/* @__PURE__ */
|
|
3544
|
-
/* @__PURE__ */
|
|
3730
|
+
error && /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
|
|
3731
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
3732
|
+
/* @__PURE__ */ jsx17(
|
|
3545
3733
|
"button",
|
|
3546
3734
|
{
|
|
3547
3735
|
ref: cancelRef,
|
|
@@ -3552,7 +3740,7 @@ function FadeModal({
|
|
|
3552
3740
|
children: "Cancel"
|
|
3553
3741
|
}
|
|
3554
3742
|
),
|
|
3555
|
-
/* @__PURE__ */
|
|
3743
|
+
/* @__PURE__ */ jsx17(
|
|
3556
3744
|
"button",
|
|
3557
3745
|
{
|
|
3558
3746
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -3569,8 +3757,8 @@ function FadeModal({
|
|
|
3569
3757
|
}
|
|
3570
3758
|
|
|
3571
3759
|
// src/components/ImportTrackModal.tsx
|
|
3572
|
-
import { useCallback as
|
|
3573
|
-
import { jsx as
|
|
3760
|
+
import { useCallback as useCallback7, useEffect as useEffect9, useState as useState11 } from "react";
|
|
3761
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3574
3762
|
function ImportTrackModal({
|
|
3575
3763
|
host,
|
|
3576
3764
|
open,
|
|
@@ -3582,10 +3770,10 @@ function ImportTrackModal({
|
|
|
3582
3770
|
onPick,
|
|
3583
3771
|
onPortTrack
|
|
3584
3772
|
}) {
|
|
3585
|
-
const [load, setLoad] =
|
|
3586
|
-
const [selectedSceneId, setSelectedSceneId] =
|
|
3587
|
-
const [importingTrackId, setImportingTrackId] =
|
|
3588
|
-
const refresh =
|
|
3773
|
+
const [load, setLoad] = useState11({ status: "loading" });
|
|
3774
|
+
const [selectedSceneId, setSelectedSceneId] = useState11(null);
|
|
3775
|
+
const [importingTrackId, setImportingTrackId] = useState11(null);
|
|
3776
|
+
const refresh = useCallback7(async () => {
|
|
3589
3777
|
if (!host.listImportableTracks) {
|
|
3590
3778
|
setLoad({ status: "error", message: "This host does not support importing tracks." });
|
|
3591
3779
|
return;
|
|
@@ -3601,14 +3789,14 @@ function ImportTrackModal({
|
|
|
3601
3789
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load scenes." });
|
|
3602
3790
|
}
|
|
3603
3791
|
}, [host, mode, onPortTrack]);
|
|
3604
|
-
|
|
3792
|
+
useEffect9(() => {
|
|
3605
3793
|
if (open) {
|
|
3606
3794
|
setSelectedSceneId(null);
|
|
3607
3795
|
setImportingTrackId(null);
|
|
3608
3796
|
void refresh();
|
|
3609
3797
|
}
|
|
3610
3798
|
}, [open, refresh]);
|
|
3611
|
-
const handleImport =
|
|
3799
|
+
const handleImport = useCallback7(
|
|
3612
3800
|
async (track, sourceSceneId, sceneName, isSameScene) => {
|
|
3613
3801
|
if (isSameScene && onPortTrack) {
|
|
3614
3802
|
if (!track.importable) return;
|
|
@@ -3649,16 +3837,16 @@ function ImportTrackModal({
|
|
|
3649
3837
|
if (!open) return null;
|
|
3650
3838
|
const scenes = load.status === "ready" ? load.scenes : [];
|
|
3651
3839
|
const selectedScene = scenes.find((s) => s.sceneId === selectedSceneId) ?? null;
|
|
3652
|
-
return /* @__PURE__ */
|
|
3840
|
+
return /* @__PURE__ */ jsx18(Modal, { open, onClose, testIdPrefix, children: /* @__PURE__ */ jsxs14(
|
|
3653
3841
|
"div",
|
|
3654
3842
|
{
|
|
3655
3843
|
className: "w-[420px] max-h-[70vh] overflow-hidden flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
3656
3844
|
onClick: (e) => e.stopPropagation(),
|
|
3657
3845
|
"data-testid": `${testIdPrefix}-modal`,
|
|
3658
3846
|
children: [
|
|
3659
|
-
/* @__PURE__ */
|
|
3660
|
-
/* @__PURE__ */
|
|
3661
|
-
selectedScene && /* @__PURE__ */
|
|
3847
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between px-3 py-2 border-b border-sas-border", children: [
|
|
3848
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
|
|
3849
|
+
selectedScene && /* @__PURE__ */ jsx18(
|
|
3662
3850
|
"button",
|
|
3663
3851
|
{
|
|
3664
3852
|
className: "text-sas-muted hover:text-sas-accent text-xs",
|
|
@@ -3667,9 +3855,9 @@ function ImportTrackModal({
|
|
|
3667
3855
|
children: "\u2190"
|
|
3668
3856
|
}
|
|
3669
3857
|
),
|
|
3670
|
-
/* @__PURE__ */
|
|
3858
|
+
/* @__PURE__ */ jsx18("span", { className: "text-sm font-medium text-sas-text", children: selectedScene ? selectedScene.sceneName : title })
|
|
3671
3859
|
] }),
|
|
3672
|
-
/* @__PURE__ */
|
|
3860
|
+
/* @__PURE__ */ jsx18(
|
|
3673
3861
|
"button",
|
|
3674
3862
|
{
|
|
3675
3863
|
className: "text-sas-muted hover:text-sas-accent text-sm",
|
|
@@ -3679,30 +3867,30 @@ function ImportTrackModal({
|
|
|
3679
3867
|
}
|
|
3680
3868
|
)
|
|
3681
3869
|
] }),
|
|
3682
|
-
/* @__PURE__ */
|
|
3683
|
-
load.status === "loading" && /* @__PURE__ */
|
|
3684
|
-
load.status === "error" && /* @__PURE__ */
|
|
3685
|
-
load.status === "ready" && scenes.length === 0 && /* @__PURE__ */
|
|
3686
|
-
load.status === "ready" && scenes.length > 0 && !selectedScene && /* @__PURE__ */
|
|
3870
|
+
/* @__PURE__ */ jsxs14("div", { className: "overflow-y-auto p-2 flex-1", children: [
|
|
3871
|
+
load.status === "loading" && /* @__PURE__ */ jsx18("div", { className: "py-8 text-center text-xs text-sas-muted", "data-testid": `${testIdPrefix}-loading`, children: "Loading scenes\u2026" }),
|
|
3872
|
+
load.status === "error" && /* @__PURE__ */ jsx18("div", { className: "py-8 text-center text-xs text-red-400", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
3873
|
+
load.status === "ready" && scenes.length === 0 && /* @__PURE__ */ jsx18("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." }),
|
|
3874
|
+
load.status === "ready" && scenes.length > 0 && !selectedScene && /* @__PURE__ */ jsx18("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-scene-list`, children: scenes.map((scene) => /* @__PURE__ */ jsx18("li", { children: /* @__PURE__ */ jsxs14(
|
|
3687
3875
|
"button",
|
|
3688
3876
|
{
|
|
3689
3877
|
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",
|
|
3690
3878
|
onClick: () => setSelectedSceneId(scene.sceneId),
|
|
3691
3879
|
"data-testid": `${testIdPrefix}-scene`,
|
|
3692
3880
|
children: [
|
|
3693
|
-
/* @__PURE__ */
|
|
3694
|
-
/* @__PURE__ */
|
|
3881
|
+
/* @__PURE__ */ jsx18("span", { className: "truncate", children: scene.sceneName }),
|
|
3882
|
+
/* @__PURE__ */ jsxs14("span", { className: "text-sas-muted", children: [
|
|
3695
3883
|
scene.tracks.length,
|
|
3696
3884
|
" \u2192"
|
|
3697
3885
|
] })
|
|
3698
3886
|
]
|
|
3699
3887
|
}
|
|
3700
3888
|
) }, scene.sceneId)) }),
|
|
3701
|
-
selectedScene && /* @__PURE__ */
|
|
3889
|
+
selectedScene && /* @__PURE__ */ jsx18("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-track-list`, children: selectedScene.tracks.map((track) => {
|
|
3702
3890
|
const busy = importingTrackId === track.trackId;
|
|
3703
3891
|
const gated = mode === "track" && !track.importable;
|
|
3704
3892
|
const disabled = gated || busy;
|
|
3705
|
-
return /* @__PURE__ */
|
|
3893
|
+
return /* @__PURE__ */ jsx18("li", { children: /* @__PURE__ */ jsxs14(
|
|
3706
3894
|
"button",
|
|
3707
3895
|
{
|
|
3708
3896
|
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"}`,
|
|
@@ -3712,14 +3900,14 @@ function ImportTrackModal({
|
|
|
3712
3900
|
"data-testid": `${testIdPrefix}-track`,
|
|
3713
3901
|
"data-importable": mode === "sound" || track.importable ? "true" : "false",
|
|
3714
3902
|
children: [
|
|
3715
|
-
/* @__PURE__ */
|
|
3903
|
+
/* @__PURE__ */ jsxs14("span", { className: "truncate", children: [
|
|
3716
3904
|
track.name,
|
|
3717
|
-
track.role ? /* @__PURE__ */
|
|
3905
|
+
track.role ? /* @__PURE__ */ jsxs14("span", { className: "text-sas-muted", children: [
|
|
3718
3906
|
" \xB7 ",
|
|
3719
3907
|
track.role
|
|
3720
3908
|
] }) : null
|
|
3721
3909
|
] }),
|
|
3722
|
-
busy ? /* @__PURE__ */
|
|
3910
|
+
busy ? /* @__PURE__ */ jsx18("span", { className: "text-sas-muted", children: "\u2026" }) : gated ? /* @__PURE__ */ jsx18("span", { className: "text-sas-muted", children: "\u2298" }) : null
|
|
3723
3911
|
]
|
|
3724
3912
|
}
|
|
3725
3913
|
) }, track.dbId);
|
|
@@ -3731,8 +3919,8 @@ function ImportTrackModal({
|
|
|
3731
3919
|
}
|
|
3732
3920
|
|
|
3733
3921
|
// src/components/CrossfadeModal.tsx
|
|
3734
|
-
import { useCallback as
|
|
3735
|
-
import { Fragment as Fragment6, jsx as
|
|
3922
|
+
import { useCallback as useCallback8, useEffect as useEffect10, useMemo as useMemo5, useRef as useRef9, useState as useState12 } from "react";
|
|
3923
|
+
import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3736
3924
|
function shortId2(dbId) {
|
|
3737
3925
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
3738
3926
|
}
|
|
@@ -3745,7 +3933,7 @@ function CandidateRow({
|
|
|
3745
3933
|
}) {
|
|
3746
3934
|
const primary = track.prompt?.trim() || track.name;
|
|
3747
3935
|
const meta = [track.role, shortId2(track.dbId)].filter(Boolean).join(" \xB7 ");
|
|
3748
|
-
return /* @__PURE__ */
|
|
3936
|
+
return /* @__PURE__ */ jsxs15(
|
|
3749
3937
|
"button",
|
|
3750
3938
|
{
|
|
3751
3939
|
type: "button",
|
|
@@ -3757,8 +3945,8 @@ function CandidateRow({
|
|
|
3757
3945
|
disabled,
|
|
3758
3946
|
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"}`,
|
|
3759
3947
|
children: [
|
|
3760
|
-
/* @__PURE__ */
|
|
3761
|
-
meta && /* @__PURE__ */
|
|
3948
|
+
/* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
|
|
3949
|
+
meta && /* @__PURE__ */ jsx19("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
|
|
3762
3950
|
]
|
|
3763
3951
|
}
|
|
3764
3952
|
);
|
|
@@ -3775,15 +3963,15 @@ function CrossfadeModal({
|
|
|
3775
3963
|
onCreate,
|
|
3776
3964
|
testIdPrefix = "crossfade-modal"
|
|
3777
3965
|
}) {
|
|
3778
|
-
const [load, setLoad] =
|
|
3779
|
-
const [originDbId, setOriginDbId] =
|
|
3780
|
-
const [targetDbId, setTargetDbId] =
|
|
3781
|
-
const [isCreating, setIsCreating] =
|
|
3782
|
-
const [error, setError] =
|
|
3783
|
-
const [fromName, setFromName] =
|
|
3784
|
-
const [toName, setToName] =
|
|
3966
|
+
const [load, setLoad] = useState12({ status: "loading" });
|
|
3967
|
+
const [originDbId, setOriginDbId] = useState12("");
|
|
3968
|
+
const [targetDbId, setTargetDbId] = useState12("");
|
|
3969
|
+
const [isCreating, setIsCreating] = useState12(false);
|
|
3970
|
+
const [error, setError] = useState12(null);
|
|
3971
|
+
const [fromName, setFromName] = useState12(null);
|
|
3972
|
+
const [toName, setToName] = useState12(null);
|
|
3785
3973
|
const cancelRef = useRef9(null);
|
|
3786
|
-
const refresh =
|
|
3974
|
+
const refresh = useCallback8(async () => {
|
|
3787
3975
|
if (!host.listSceneFamilyTracks) {
|
|
3788
3976
|
setLoad({ status: "error", message: "This host does not support crossfade tracks." });
|
|
3789
3977
|
return;
|
|
@@ -3803,7 +3991,7 @@ function CrossfadeModal({
|
|
|
3803
3991
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
3804
3992
|
}
|
|
3805
3993
|
}, [host, fromSceneId, toSceneId]);
|
|
3806
|
-
|
|
3994
|
+
useEffect10(() => {
|
|
3807
3995
|
if (open) {
|
|
3808
3996
|
setError(null);
|
|
3809
3997
|
setIsCreating(false);
|
|
@@ -3821,12 +4009,12 @@ function CrossfadeModal({
|
|
|
3821
4009
|
() => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3822
4010
|
[load, excludeSet]
|
|
3823
4011
|
);
|
|
3824
|
-
|
|
4012
|
+
useEffect10(() => {
|
|
3825
4013
|
if (!originCandidates.some((t) => t.dbId === originDbId)) {
|
|
3826
4014
|
setOriginDbId(originCandidates[0]?.dbId ?? "");
|
|
3827
4015
|
}
|
|
3828
4016
|
}, [originCandidates, originDbId]);
|
|
3829
|
-
|
|
4017
|
+
useEffect10(() => {
|
|
3830
4018
|
if (!targetCandidates.some((t) => t.dbId === targetDbId)) {
|
|
3831
4019
|
setTargetDbId(targetCandidates[0]?.dbId ?? "");
|
|
3832
4020
|
}
|
|
@@ -3834,10 +4022,10 @@ function CrossfadeModal({
|
|
|
3834
4022
|
const originTrack = originCandidates.find((t) => t.dbId === originDbId) ?? null;
|
|
3835
4023
|
const targetTrack = targetCandidates.find((t) => t.dbId === targetDbId) ?? null;
|
|
3836
4024
|
const canCreate = !isCreating && !!originTrack && !!targetTrack;
|
|
3837
|
-
const handleClose =
|
|
4025
|
+
const handleClose = useCallback8(() => {
|
|
3838
4026
|
if (!isCreating) onClose();
|
|
3839
4027
|
}, [isCreating, onClose]);
|
|
3840
|
-
const handleCreate =
|
|
4028
|
+
const handleCreate = useCallback8(async () => {
|
|
3841
4029
|
if (!originTrack || !targetTrack) return;
|
|
3842
4030
|
setIsCreating(true);
|
|
3843
4031
|
setError(null);
|
|
@@ -3855,26 +4043,26 @@ function CrossfadeModal({
|
|
|
3855
4043
|
const fromLabel = fromName ?? fromSceneName ?? null;
|
|
3856
4044
|
const toLabel = toName ?? toSceneName ?? null;
|
|
3857
4045
|
if (!open) return null;
|
|
3858
|
-
return /* @__PURE__ */
|
|
4046
|
+
return /* @__PURE__ */ jsx19(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs15(
|
|
3859
4047
|
"div",
|
|
3860
4048
|
{
|
|
3861
4049
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
3862
4050
|
onClick: (e) => e.stopPropagation(),
|
|
3863
4051
|
"data-testid": `${testIdPrefix}-box`,
|
|
3864
4052
|
children: [
|
|
3865
|
-
/* @__PURE__ */
|
|
3866
|
-
/* @__PURE__ */
|
|
4053
|
+
/* @__PURE__ */ jsx19("h3", { className: "text-sm font-bold text-sas-text", children: "Add crossfade" }),
|
|
4054
|
+
/* @__PURE__ */ jsxs15("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
|
|
3867
4055
|
"Bridge a track from",
|
|
3868
4056
|
" ",
|
|
3869
|
-
/* @__PURE__ */
|
|
4057
|
+
/* @__PURE__ */ jsx19("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
3870
4058
|
" into one from",
|
|
3871
4059
|
" ",
|
|
3872
|
-
/* @__PURE__ */
|
|
4060
|
+
/* @__PURE__ */ jsx19("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
3873
4061
|
". Both layers share one generated part; each keeps its own preset."
|
|
3874
4062
|
] }),
|
|
3875
|
-
load.status === "loading" && /* @__PURE__ */
|
|
3876
|
-
load.status === "error" && /* @__PURE__ */
|
|
3877
|
-
load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */
|
|
4063
|
+
load.status === "loading" && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
|
|
4064
|
+
load.status === "error" && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
|
|
4065
|
+
load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */ jsxs15(
|
|
3878
4066
|
"div",
|
|
3879
4067
|
{
|
|
3880
4068
|
className: "text-xs text-sas-muted py-4 text-center",
|
|
@@ -3885,20 +4073,20 @@ function CrossfadeModal({
|
|
|
3885
4073
|
". Add one (or free one from another crossfade) first."
|
|
3886
4074
|
]
|
|
3887
4075
|
}
|
|
3888
|
-
) : /* @__PURE__ */
|
|
3889
|
-
/* @__PURE__ */
|
|
3890
|
-
/* @__PURE__ */
|
|
4076
|
+
) : /* @__PURE__ */ jsxs15(Fragment6, { children: [
|
|
4077
|
+
/* @__PURE__ */ jsxs15("div", { className: "block", children: [
|
|
4078
|
+
/* @__PURE__ */ jsxs15("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
3891
4079
|
"Origin ",
|
|
3892
4080
|
fromLabel ? `(${fromLabel})` : "(top)"
|
|
3893
4081
|
] }),
|
|
3894
|
-
/* @__PURE__ */
|
|
4082
|
+
/* @__PURE__ */ jsx19(
|
|
3895
4083
|
"div",
|
|
3896
4084
|
{
|
|
3897
4085
|
role: "radiogroup",
|
|
3898
4086
|
"aria-label": "Origin track",
|
|
3899
4087
|
"data-testid": `${testIdPrefix}-origin-list`,
|
|
3900
4088
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3901
|
-
children: originCandidates.map((t) => /* @__PURE__ */
|
|
4089
|
+
children: originCandidates.map((t) => /* @__PURE__ */ jsx19(
|
|
3902
4090
|
CandidateRow,
|
|
3903
4091
|
{
|
|
3904
4092
|
track: t,
|
|
@@ -3912,23 +4100,23 @@ function CrossfadeModal({
|
|
|
3912
4100
|
}
|
|
3913
4101
|
)
|
|
3914
4102
|
] }),
|
|
3915
|
-
/* @__PURE__ */
|
|
3916
|
-
/* @__PURE__ */
|
|
4103
|
+
/* @__PURE__ */ jsxs15("div", { className: "block", children: [
|
|
4104
|
+
/* @__PURE__ */ jsxs15("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
3917
4105
|
"Target ",
|
|
3918
4106
|
toLabel ? `(${toLabel})` : "(bottom)"
|
|
3919
4107
|
] }),
|
|
3920
|
-
targetCandidates.length === 0 ? /* @__PURE__ */
|
|
4108
|
+
targetCandidates.length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "text-xs text-sas-danger mt-0.5", "data-testid": `${testIdPrefix}-empty-target`, children: [
|
|
3921
4109
|
"No available tracks in ",
|
|
3922
4110
|
toLabel ?? "the target scene",
|
|
3923
4111
|
" to crossfade into."
|
|
3924
|
-
] }) : /* @__PURE__ */
|
|
4112
|
+
] }) : /* @__PURE__ */ jsx19(
|
|
3925
4113
|
"div",
|
|
3926
4114
|
{
|
|
3927
4115
|
role: "radiogroup",
|
|
3928
4116
|
"aria-label": "Target track",
|
|
3929
4117
|
"data-testid": `${testIdPrefix}-target-list`,
|
|
3930
4118
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3931
|
-
children: targetCandidates.map((t) => /* @__PURE__ */
|
|
4119
|
+
children: targetCandidates.map((t) => /* @__PURE__ */ jsx19(
|
|
3932
4120
|
CandidateRow,
|
|
3933
4121
|
{
|
|
3934
4122
|
track: t,
|
|
@@ -3943,9 +4131,9 @@ function CrossfadeModal({
|
|
|
3943
4131
|
)
|
|
3944
4132
|
] })
|
|
3945
4133
|
] })),
|
|
3946
|
-
error && /* @__PURE__ */
|
|
3947
|
-
/* @__PURE__ */
|
|
3948
|
-
/* @__PURE__ */
|
|
4134
|
+
error && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
|
|
4135
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
4136
|
+
/* @__PURE__ */ jsx19(
|
|
3949
4137
|
"button",
|
|
3950
4138
|
{
|
|
3951
4139
|
ref: cancelRef,
|
|
@@ -3956,7 +4144,7 @@ function CrossfadeModal({
|
|
|
3956
4144
|
children: "Cancel"
|
|
3957
4145
|
}
|
|
3958
4146
|
),
|
|
3959
|
-
/* @__PURE__ */
|
|
4147
|
+
/* @__PURE__ */ jsx19(
|
|
3960
4148
|
"button",
|
|
3961
4149
|
{
|
|
3962
4150
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -3973,10 +4161,10 @@ function CrossfadeModal({
|
|
|
3973
4161
|
}
|
|
3974
4162
|
|
|
3975
4163
|
// src/components/TransitionDesigner.tsx
|
|
3976
|
-
import { useCallback as
|
|
4164
|
+
import { useCallback as useCallback10, useEffect as useEffect11, useMemo as useMemo6, useRef as useRef11, useState as useState14 } from "react";
|
|
3977
4165
|
|
|
3978
4166
|
// src/hooks/useTrackReorder.ts
|
|
3979
|
-
import { useCallback as
|
|
4167
|
+
import { useCallback as useCallback9, useRef as useRef10, useState as useState13 } from "react";
|
|
3980
4168
|
function moveItem(arr, from, to) {
|
|
3981
4169
|
const next = arr.slice();
|
|
3982
4170
|
if (from === to || from < 0 || to < 0 || from >= next.length || to >= next.length) {
|
|
@@ -3993,12 +4181,12 @@ function useTrackReorder({
|
|
|
3993
4181
|
getId,
|
|
3994
4182
|
onError
|
|
3995
4183
|
}) {
|
|
3996
|
-
const [draggingIndex, setDraggingIndex] =
|
|
3997
|
-
const [dragOverIndex, setDragOverIndex] =
|
|
4184
|
+
const [draggingIndex, setDraggingIndex] = useState13(null);
|
|
4185
|
+
const [dragOverIndex, setDragOverIndex] = useState13(null);
|
|
3998
4186
|
const fromRef = useRef10(null);
|
|
3999
4187
|
const itemsRef = useRef10(items);
|
|
4000
4188
|
itemsRef.current = items;
|
|
4001
|
-
const dragPropsFor =
|
|
4189
|
+
const dragPropsFor = useCallback9(
|
|
4002
4190
|
(index) => ({
|
|
4003
4191
|
handleProps: {
|
|
4004
4192
|
draggable: true,
|
|
@@ -4180,7 +4368,7 @@ function dbIdsFromKeys(keys) {
|
|
|
4180
4368
|
}
|
|
4181
4369
|
|
|
4182
4370
|
// src/components/TransitionDesigner.tsx
|
|
4183
|
-
import { jsx as
|
|
4371
|
+
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4184
4372
|
var CROSSFADE_ESTIMATE_MS = 15e3;
|
|
4185
4373
|
var FADE_ESTIMATE_MS = 11e3;
|
|
4186
4374
|
var CREATE_ALL_CONCURRENCY = 5;
|
|
@@ -4207,14 +4395,14 @@ function TransitionDesigner({
|
|
|
4207
4395
|
testIdPrefix = "transition-designer"
|
|
4208
4396
|
}) {
|
|
4209
4397
|
const fadeOnly = !onCreateCrossfade;
|
|
4210
|
-
const [load, setLoad] =
|
|
4211
|
-
const [fromName, setFromName] =
|
|
4212
|
-
const [toName, setToName] =
|
|
4213
|
-
const [originSlots, setOriginSlots] =
|
|
4214
|
-
const [targetSlots, setTargetSlots] =
|
|
4215
|
-
const [creatingKeys, setCreatingKeys] =
|
|
4216
|
-
const [rowErrors, setRowErrors] =
|
|
4217
|
-
const [rowEffects, setRowEffects] =
|
|
4398
|
+
const [load, setLoad] = useState14({ status: "loading" });
|
|
4399
|
+
const [fromName, setFromName] = useState14(null);
|
|
4400
|
+
const [toName, setToName] = useState14(null);
|
|
4401
|
+
const [originSlots, setOriginSlots] = useState14([]);
|
|
4402
|
+
const [targetSlots, setTargetSlots] = useState14([]);
|
|
4403
|
+
const [creatingKeys, setCreatingKeys] = useState14(() => /* @__PURE__ */ new Set());
|
|
4404
|
+
const [rowErrors, setRowErrors] = useState14({});
|
|
4405
|
+
const [rowEffects, setRowEffects] = useState14({});
|
|
4218
4406
|
const rowEffectsRef = useRef11(rowEffects);
|
|
4219
4407
|
rowEffectsRef.current = rowEffects;
|
|
4220
4408
|
const audioEffectsEnabled = !!onCreateAudioTransition;
|
|
@@ -4227,8 +4415,8 @@ function TransitionDesigner({
|
|
|
4227
4415
|
const creatingKeysRef = useRef11(creatingKeys);
|
|
4228
4416
|
creatingKeysRef.current = creatingKeys;
|
|
4229
4417
|
const dragRef = useRef11(null);
|
|
4230
|
-
const [dragging, setDragging] =
|
|
4231
|
-
const [dragOver, setDragOver] =
|
|
4418
|
+
const [dragging, setDragging] = useState14(null);
|
|
4419
|
+
const [dragOver, setDragOver] = useState14(null);
|
|
4232
4420
|
const excludeSet = useMemo6(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
4233
4421
|
const originPool = useMemo6(
|
|
4234
4422
|
() => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
@@ -4244,7 +4432,7 @@ function TransitionDesigner({
|
|
|
4244
4432
|
originByIdRef.current = originById;
|
|
4245
4433
|
const targetByIdRef = useRef11(targetById);
|
|
4246
4434
|
targetByIdRef.current = targetById;
|
|
4247
|
-
const refresh =
|
|
4435
|
+
const refresh = useCallback10(async () => {
|
|
4248
4436
|
if (!host.listSceneFamilyTracks) {
|
|
4249
4437
|
setLoad({ status: "error", message: "This host does not support transition tracks." });
|
|
4250
4438
|
return;
|
|
@@ -4287,10 +4475,10 @@ function TransitionDesigner({
|
|
|
4287
4475
|
});
|
|
4288
4476
|
}
|
|
4289
4477
|
}, [host, fromSceneId, toSceneId, transitionSceneId, mapColumnSubjects]);
|
|
4290
|
-
|
|
4478
|
+
useEffect11(() => {
|
|
4291
4479
|
void refresh();
|
|
4292
4480
|
}, [refresh]);
|
|
4293
|
-
|
|
4481
|
+
useEffect11(() => {
|
|
4294
4482
|
if (load.status !== "ready") return;
|
|
4295
4483
|
const [po, pt] = padPair(
|
|
4296
4484
|
reconcileSlots(originSlotsRef.current, originPool.map((t) => t.dbId)),
|
|
@@ -4299,7 +4487,7 @@ function TransitionDesigner({
|
|
|
4299
4487
|
if (!slotsEqual(po, originSlotsRef.current)) setOriginSlots(po);
|
|
4300
4488
|
if (!slotsEqual(pt, targetSlotsRef.current)) setTargetSlots(pt);
|
|
4301
4489
|
}, [originPool, targetPool, load.status]);
|
|
4302
|
-
const mutate =
|
|
4490
|
+
const mutate = useCallback10(
|
|
4303
4491
|
(nextOrigin, nextTarget) => {
|
|
4304
4492
|
const norm = normalizeSlots(nextOrigin, nextTarget);
|
|
4305
4493
|
const [po, pt] = padPair(norm.originOrder, norm.targetOrder);
|
|
@@ -4312,7 +4500,7 @@ function TransitionDesigner({
|
|
|
4312
4500
|
},
|
|
4313
4501
|
[host, transitionSceneId]
|
|
4314
4502
|
);
|
|
4315
|
-
const setRowEffect =
|
|
4503
|
+
const setRowEffect = useCallback10(
|
|
4316
4504
|
(sourceDbId, effect) => {
|
|
4317
4505
|
setRowEffects((prev) => {
|
|
4318
4506
|
const next = { ...prev, [sourceDbId]: effect };
|
|
@@ -4326,7 +4514,7 @@ function TransitionDesigner({
|
|
|
4326
4514
|
},
|
|
4327
4515
|
[host, transitionSceneId]
|
|
4328
4516
|
);
|
|
4329
|
-
const insertGapAbove =
|
|
4517
|
+
const insertGapAbove = useCallback10(
|
|
4330
4518
|
(col, index) => {
|
|
4331
4519
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
4332
4520
|
const next = [...slots.slice(0, index), null, ...slots.slice(index)];
|
|
@@ -4335,7 +4523,7 @@ function TransitionDesigner({
|
|
|
4335
4523
|
},
|
|
4336
4524
|
[originSlots, targetSlots, mutate]
|
|
4337
4525
|
);
|
|
4338
|
-
const removeGap =
|
|
4526
|
+
const removeGap = useCallback10(
|
|
4339
4527
|
(col, index) => {
|
|
4340
4528
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
4341
4529
|
const next = slots.filter((_, i) => i !== index);
|
|
@@ -4344,7 +4532,7 @@ function TransitionDesigner({
|
|
|
4344
4532
|
},
|
|
4345
4533
|
[originSlots, targetSlots, mutate]
|
|
4346
4534
|
);
|
|
4347
|
-
const handleDrop =
|
|
4535
|
+
const handleDrop = useCallback10(
|
|
4348
4536
|
(col, to) => {
|
|
4349
4537
|
const from = dragRef.current;
|
|
4350
4538
|
dragRef.current = null;
|
|
@@ -4383,7 +4571,7 @@ function TransitionDesigner({
|
|
|
4383
4571
|
}).length,
|
|
4384
4572
|
[effectiveRows, creatingKeys]
|
|
4385
4573
|
);
|
|
4386
|
-
const createRow =
|
|
4574
|
+
const createRow = useCallback10(
|
|
4387
4575
|
async (row) => {
|
|
4388
4576
|
const key = rowKey(row);
|
|
4389
4577
|
if (!key || !row.type || creatingKeysRef.current.has(key)) return;
|
|
@@ -4438,7 +4626,7 @@ function TransitionDesigner({
|
|
|
4438
4626
|
},
|
|
4439
4627
|
[onCreateCrossfade, onCreateFade, onCreateAudioTransition]
|
|
4440
4628
|
);
|
|
4441
|
-
const createAll =
|
|
4629
|
+
const createAll = useCallback10(async () => {
|
|
4442
4630
|
const source = fadeOnly ? fadeOnlyRowsRef.current : buildRowSlots(originSlotsRef.current, targetSlotsRef.current);
|
|
4443
4631
|
const eligible = source.filter((r) => {
|
|
4444
4632
|
const k = rowKey(r);
|
|
@@ -4507,15 +4695,15 @@ function TransitionDesigner({
|
|
|
4507
4695
|
const base = "group relative rounded-sm border px-2 py-1.5 text-left transition-colors select-none";
|
|
4508
4696
|
const tone = isDragTarget ? "border-sas-accent bg-sas-accent/10" : "border-sas-border bg-sas-panel";
|
|
4509
4697
|
if (slotId === null) {
|
|
4510
|
-
return /* @__PURE__ */
|
|
4698
|
+
return /* @__PURE__ */ jsxs16(
|
|
4511
4699
|
"div",
|
|
4512
4700
|
{
|
|
4513
4701
|
...cellDragProps(col, index, false),
|
|
4514
4702
|
"data-testid": `${testIdPrefix}-${col}-gap-${index}`,
|
|
4515
4703
|
className: `${base} ${tone} border-dashed flex items-center justify-between ${isDragging ? "opacity-40" : "opacity-70"}`,
|
|
4516
4704
|
children: [
|
|
4517
|
-
/* @__PURE__ */
|
|
4518
|
-
/* @__PURE__ */
|
|
4705
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: "\u2014 gap \u2014" }),
|
|
4706
|
+
/* @__PURE__ */ jsx20(
|
|
4519
4707
|
"button",
|
|
4520
4708
|
{
|
|
4521
4709
|
type: "button",
|
|
@@ -4532,7 +4720,7 @@ function TransitionDesigner({
|
|
|
4532
4720
|
}
|
|
4533
4721
|
const primary = track ? track.prompt?.trim() || track.name : slotId;
|
|
4534
4722
|
const meta = track ? [track.role, shortId3(track.dbId)].filter(Boolean).join(" \xB7 ") : "missing";
|
|
4535
|
-
return /* @__PURE__ */
|
|
4723
|
+
return /* @__PURE__ */ jsx20(
|
|
4536
4724
|
"div",
|
|
4537
4725
|
{
|
|
4538
4726
|
...cellDragProps(col, index, locked),
|
|
@@ -4540,13 +4728,13 @@ function TransitionDesigner({
|
|
|
4540
4728
|
"data-value": slotId,
|
|
4541
4729
|
className: `${base} ${tone} ${isDragging ? "opacity-40" : ""} ${locked ? "opacity-60" : "cursor-grab active:cursor-grabbing"}`,
|
|
4542
4730
|
title: track ? track.dbId : "Track no longer available",
|
|
4543
|
-
children: /* @__PURE__ */
|
|
4544
|
-
/* @__PURE__ */
|
|
4545
|
-
/* @__PURE__ */
|
|
4546
|
-
/* @__PURE__ */
|
|
4547
|
-
meta && /* @__PURE__ */
|
|
4731
|
+
children: /* @__PURE__ */ jsxs16("div", { className: "flex items-start gap-1", children: [
|
|
4732
|
+
/* @__PURE__ */ jsx20("span", { className: "text-sas-muted/60 text-xs leading-tight pt-0.5", "aria-hidden": true, children: "\u283F" }),
|
|
4733
|
+
/* @__PURE__ */ jsxs16("div", { className: "min-w-0 flex-1", children: [
|
|
4734
|
+
/* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-text truncate", children: primary }),
|
|
4735
|
+
meta && /* @__PURE__ */ jsx20("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", children: meta })
|
|
4548
4736
|
] }),
|
|
4549
|
-
/* @__PURE__ */
|
|
4737
|
+
/* @__PURE__ */ jsx20(
|
|
4550
4738
|
"button",
|
|
4551
4739
|
{
|
|
4552
4740
|
type: "button",
|
|
@@ -4569,26 +4757,26 @@ function TransitionDesigner({
|
|
|
4569
4757
|
const key = rowKey(row);
|
|
4570
4758
|
const isCreatingThis = key !== null && creatingKeys.has(key);
|
|
4571
4759
|
const errMsg = key !== null ? rowErrors[key] : void 0;
|
|
4572
|
-
return /* @__PURE__ */
|
|
4760
|
+
return /* @__PURE__ */ jsxs16(
|
|
4573
4761
|
"div",
|
|
4574
4762
|
{
|
|
4575
4763
|
"data-testid": `${testIdPrefix}-row-${i}`,
|
|
4576
4764
|
className: "grid grid-cols-[1fr_auto] gap-2 items-center",
|
|
4577
4765
|
children: [
|
|
4578
|
-
/* @__PURE__ */
|
|
4766
|
+
/* @__PURE__ */ jsxs16(
|
|
4579
4767
|
"div",
|
|
4580
4768
|
{
|
|
4581
4769
|
"data-testid": `${testIdPrefix}-subject-${slotId}`,
|
|
4582
4770
|
className: `rounded-sm border border-sas-border bg-sas-panel px-2 py-1.5 ${isCreatingThis ? "opacity-60" : ""}`,
|
|
4583
4771
|
title: track ? track.dbId : "Track no longer available",
|
|
4584
4772
|
children: [
|
|
4585
|
-
/* @__PURE__ */
|
|
4586
|
-
/* @__PURE__ */
|
|
4773
|
+
/* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-text truncate", children: track ? track.prompt?.trim() || track.name : slotId }),
|
|
4774
|
+
/* @__PURE__ */ jsx20("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", children: track ? [track.role, shortId3(track.dbId)].filter(Boolean).join(" \xB7 ") : "missing" })
|
|
4587
4775
|
]
|
|
4588
4776
|
}
|
|
4589
4777
|
),
|
|
4590
|
-
/* @__PURE__ */
|
|
4591
|
-
/* @__PURE__ */
|
|
4778
|
+
/* @__PURE__ */ jsxs16("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
|
|
4779
|
+
/* @__PURE__ */ jsx20(
|
|
4592
4780
|
"span",
|
|
4593
4781
|
{
|
|
4594
4782
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
@@ -4596,7 +4784,7 @@ function TransitionDesigner({
|
|
|
4596
4784
|
children: row.type ? TYPE_LABEL[row.type] : "\u2014"
|
|
4597
4785
|
}
|
|
4598
4786
|
),
|
|
4599
|
-
isCreatingThis ? /* @__PURE__ */
|
|
4787
|
+
isCreatingThis ? /* @__PURE__ */ jsx20("div", { className: "w-full", children: /* @__PURE__ */ jsx20(
|
|
4600
4788
|
SorceryProgressBar,
|
|
4601
4789
|
{
|
|
4602
4790
|
isLoading: true,
|
|
@@ -4604,7 +4792,7 @@ function TransitionDesigner({
|
|
|
4604
4792
|
statusText: "CREATING",
|
|
4605
4793
|
estimatedDurationMs: fadeEstimateMs ?? FADE_ESTIMATE_MS
|
|
4606
4794
|
}
|
|
4607
|
-
) }) : /* @__PURE__ */
|
|
4795
|
+
) }) : /* @__PURE__ */ jsx20(
|
|
4608
4796
|
"button",
|
|
4609
4797
|
{
|
|
4610
4798
|
type: "button",
|
|
@@ -4614,7 +4802,7 @@ function TransitionDesigner({
|
|
|
4614
4802
|
children: "Create"
|
|
4615
4803
|
}
|
|
4616
4804
|
),
|
|
4617
|
-
errMsg && /* @__PURE__ */
|
|
4805
|
+
errMsg && /* @__PURE__ */ jsx20(
|
|
4618
4806
|
"span",
|
|
4619
4807
|
{
|
|
4620
4808
|
"data-testid": `${testIdPrefix}-row-error-${i}`,
|
|
@@ -4631,18 +4819,18 @@ function TransitionDesigner({
|
|
|
4631
4819
|
if (fadeOnly) {
|
|
4632
4820
|
const outRows = fadeOnlyRows.filter((r) => r.type === "fade-out");
|
|
4633
4821
|
const inRows = fadeOnlyRows.filter((r) => r.type === "fade-in");
|
|
4634
|
-
return /* @__PURE__ */
|
|
4635
|
-
/* @__PURE__ */
|
|
4636
|
-
/* @__PURE__ */
|
|
4637
|
-
/* @__PURE__ */
|
|
4822
|
+
return /* @__PURE__ */ jsxs16("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
|
|
4823
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
|
|
4824
|
+
/* @__PURE__ */ jsxs16("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
|
|
4825
|
+
/* @__PURE__ */ jsx20("span", { className: "text-sas-text", children: fromLabel }),
|
|
4638
4826
|
" \u2192",
|
|
4639
4827
|
" ",
|
|
4640
|
-
/* @__PURE__ */
|
|
4828
|
+
/* @__PURE__ */ jsx20("span", { className: "text-sas-text", children: toLabel }),
|
|
4641
4829
|
familyLabel ? ` \xB7 ${familyLabel}` : "",
|
|
4642
4830
|
" \xB7 each entry fades on its own \u2014 origin fades out, target fades in."
|
|
4643
4831
|
] }),
|
|
4644
|
-
/* @__PURE__ */
|
|
4645
|
-
creatingKeys.size > 0 && /* @__PURE__ */
|
|
4832
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
4833
|
+
creatingKeys.size > 0 && /* @__PURE__ */ jsxs16(
|
|
4646
4834
|
"span",
|
|
4647
4835
|
{
|
|
4648
4836
|
className: "text-[10px] text-sas-accent whitespace-nowrap",
|
|
@@ -4653,7 +4841,7 @@ function TransitionDesigner({
|
|
|
4653
4841
|
]
|
|
4654
4842
|
}
|
|
4655
4843
|
),
|
|
4656
|
-
/* @__PURE__ */
|
|
4844
|
+
/* @__PURE__ */ jsxs16(
|
|
4657
4845
|
"button",
|
|
4658
4846
|
{
|
|
4659
4847
|
type: "button",
|
|
@@ -4670,26 +4858,26 @@ function TransitionDesigner({
|
|
|
4670
4858
|
)
|
|
4671
4859
|
] })
|
|
4672
4860
|
] }),
|
|
4673
|
-
load.status === "loading" && /* @__PURE__ */
|
|
4674
|
-
load.status === "error" && /* @__PURE__ */
|
|
4675
|
-
load.status === "ready" && (fadeOnlyRows.length === 0 ? /* @__PURE__ */
|
|
4861
|
+
load.status === "loading" && /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
|
|
4862
|
+
load.status === "error" && /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
4863
|
+
load.status === "ready" && (fadeOnlyRows.length === 0 ? /* @__PURE__ */ jsxs16("div", { className: "text-xs text-sas-muted py-6 text-center", "data-testid": `${testIdPrefix}-empty`, children: [
|
|
4676
4864
|
"Nothing to fade in this panel for either scene. Add tracks to ",
|
|
4677
4865
|
fromLabel,
|
|
4678
4866
|
" or ",
|
|
4679
4867
|
toLabel,
|
|
4680
4868
|
" ",
|
|
4681
4869
|
"first (or free one by deleting an existing fade)."
|
|
4682
|
-
] }) : /* @__PURE__ */
|
|
4683
|
-
outRows.length > 0 && /* @__PURE__ */
|
|
4684
|
-
/* @__PURE__ */
|
|
4870
|
+
] }) : /* @__PURE__ */ jsxs16("div", { className: "space-y-3", children: [
|
|
4871
|
+
outRows.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-fade-out-section`, children: [
|
|
4872
|
+
/* @__PURE__ */ jsxs16("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
4685
4873
|
"Origin (",
|
|
4686
4874
|
fromLabel,
|
|
4687
4875
|
") \u2014 fades out"
|
|
4688
4876
|
] }),
|
|
4689
4877
|
outRows.map((row, i) => renderFadeOnlyRow(row, i))
|
|
4690
4878
|
] }),
|
|
4691
|
-
inRows.length > 0 && /* @__PURE__ */
|
|
4692
|
-
/* @__PURE__ */
|
|
4879
|
+
inRows.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-fade-in-section`, children: [
|
|
4880
|
+
/* @__PURE__ */ jsxs16("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
4693
4881
|
"Target (",
|
|
4694
4882
|
toLabel,
|
|
4695
4883
|
") \u2014 fades in"
|
|
@@ -4699,22 +4887,22 @@ function TransitionDesigner({
|
|
|
4699
4887
|
] }))
|
|
4700
4888
|
] });
|
|
4701
4889
|
}
|
|
4702
|
-
return /* @__PURE__ */
|
|
4703
|
-
/* @__PURE__ */
|
|
4704
|
-
/* @__PURE__ */
|
|
4705
|
-
/* @__PURE__ */
|
|
4890
|
+
return /* @__PURE__ */ jsxs16("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
|
|
4891
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
|
|
4892
|
+
/* @__PURE__ */ jsxs16("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
|
|
4893
|
+
/* @__PURE__ */ jsx20("span", { className: "text-sas-text", children: fromLabel }),
|
|
4706
4894
|
" \u2192",
|
|
4707
4895
|
" ",
|
|
4708
|
-
/* @__PURE__ */
|
|
4896
|
+
/* @__PURE__ */ jsx20("span", { className: "text-sas-text", children: toLabel }),
|
|
4709
4897
|
familyLabel ? ` \xB7 ${familyLabel}` : "",
|
|
4710
4898
|
" \xB7 line up a track on each side to crossfade; leave one blank (or insert a gap) to fade."
|
|
4711
4899
|
] }),
|
|
4712
|
-
/* @__PURE__ */
|
|
4713
|
-
creatingKeys.size > 0 && /* @__PURE__ */
|
|
4900
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
4901
|
+
creatingKeys.size > 0 && /* @__PURE__ */ jsxs16("span", { className: "text-[10px] text-sas-accent whitespace-nowrap", "data-testid": `${testIdPrefix}-creating-count`, children: [
|
|
4714
4902
|
creatingKeys.size,
|
|
4715
4903
|
" creating\u2026"
|
|
4716
4904
|
] }),
|
|
4717
|
-
/* @__PURE__ */
|
|
4905
|
+
/* @__PURE__ */ jsxs16(
|
|
4718
4906
|
"button",
|
|
4719
4907
|
{
|
|
4720
4908
|
type: "button",
|
|
@@ -4731,49 +4919,49 @@ function TransitionDesigner({
|
|
|
4731
4919
|
)
|
|
4732
4920
|
] })
|
|
4733
4921
|
] }),
|
|
4734
|
-
/* @__PURE__ */
|
|
4735
|
-
/* @__PURE__ */
|
|
4922
|
+
/* @__PURE__ */ jsxs16("div", { className: "grid grid-cols-[1fr_auto_1fr] gap-2", children: [
|
|
4923
|
+
/* @__PURE__ */ jsxs16("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate", children: [
|
|
4736
4924
|
"Origin (",
|
|
4737
4925
|
fromLabel,
|
|
4738
4926
|
")"
|
|
4739
4927
|
] }),
|
|
4740
|
-
/* @__PURE__ */
|
|
4741
|
-
/* @__PURE__ */
|
|
4928
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted text-center px-2", children: "Transition" }),
|
|
4929
|
+
/* @__PURE__ */ jsxs16("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate text-right", children: [
|
|
4742
4930
|
"Target (",
|
|
4743
4931
|
toLabel,
|
|
4744
4932
|
")"
|
|
4745
4933
|
] })
|
|
4746
4934
|
] }),
|
|
4747
|
-
load.status === "loading" && /* @__PURE__ */
|
|
4748
|
-
load.status === "error" && /* @__PURE__ */
|
|
4749
|
-
load.status === "ready" && (rows.length === 0 ? /* @__PURE__ */
|
|
4935
|
+
load.status === "loading" && /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
|
|
4936
|
+
load.status === "error" && /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
4937
|
+
load.status === "ready" && (rows.length === 0 ? /* @__PURE__ */ jsxs16("div", { className: "text-xs text-sas-muted py-6 text-center", "data-testid": `${testIdPrefix}-empty`, children: [
|
|
4750
4938
|
"No tracks to arrange in this panel for either scene. Add tracks to ",
|
|
4751
4939
|
fromLabel,
|
|
4752
4940
|
" or ",
|
|
4753
4941
|
toLabel,
|
|
4754
4942
|
" ",
|
|
4755
4943
|
"first (or free one by deleting an existing crossfade/fade)."
|
|
4756
|
-
] }) : /* @__PURE__ */
|
|
4944
|
+
] }) : /* @__PURE__ */ jsx20("div", { className: "space-y-2", children: rows.map((row, i) => {
|
|
4757
4945
|
const key = rowKey(row);
|
|
4758
4946
|
const isCreatingThis = key !== null && creatingKeys.has(key);
|
|
4759
4947
|
const errMsg = key !== null ? rowErrors[key] : void 0;
|
|
4760
|
-
return /* @__PURE__ */
|
|
4948
|
+
return /* @__PURE__ */ jsxs16(
|
|
4761
4949
|
"div",
|
|
4762
4950
|
{
|
|
4763
4951
|
"data-testid": `${testIdPrefix}-row-${i}`,
|
|
4764
4952
|
className: "grid grid-cols-[1fr_auto_1fr] gap-2 items-center",
|
|
4765
4953
|
children: [
|
|
4766
4954
|
renderCell("origin", i, row.originId),
|
|
4767
|
-
/* @__PURE__ */
|
|
4768
|
-
!row.type ? /* @__PURE__ */
|
|
4955
|
+
/* @__PURE__ */ jsxs16("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
|
|
4956
|
+
!row.type ? /* @__PURE__ */ jsx20("span", { className: "text-[10px] text-sas-muted/50", children: "\u2014" }) : row.type === "crossfade" ? /* @__PURE__ */ jsx20(
|
|
4769
4957
|
"span",
|
|
4770
4958
|
{
|
|
4771
4959
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
4772
4960
|
className: "text-[10px] font-medium px-1.5 py-0.5 rounded-sm border border-sas-accent/50 text-sas-accent",
|
|
4773
4961
|
children: TYPE_LABEL[row.type]
|
|
4774
4962
|
}
|
|
4775
|
-
) : audioEffectsEnabled ? /* @__PURE__ */
|
|
4776
|
-
/* @__PURE__ */
|
|
4963
|
+
) : audioEffectsEnabled ? /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-1", "data-testid": `${testIdPrefix}-type-${i}`, children: [
|
|
4964
|
+
/* @__PURE__ */ jsx20(
|
|
4777
4965
|
"select",
|
|
4778
4966
|
{
|
|
4779
4967
|
"data-testid": `${testIdPrefix}-effect-${i}`,
|
|
@@ -4783,11 +4971,11 @@ function TransitionDesigner({
|
|
|
4783
4971
|
if (id) setRowEffect(id, e.target.value);
|
|
4784
4972
|
},
|
|
4785
4973
|
className: "text-[10px] bg-sas-panel border border-sas-border rounded-sm px-1 py-0.5 text-sas-text",
|
|
4786
|
-
children: AUDIO_EFFECTS.map((eff) => /* @__PURE__ */
|
|
4974
|
+
children: AUDIO_EFFECTS.map((eff) => /* @__PURE__ */ jsx20("option", { value: eff, children: AUDIO_EFFECT_LABEL[eff] }, eff))
|
|
4787
4975
|
}
|
|
4788
4976
|
),
|
|
4789
|
-
/* @__PURE__ */
|
|
4790
|
-
] }) : /* @__PURE__ */
|
|
4977
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[9px] text-sas-muted", children: row.type === "fade-out" ? "out" : "in" })
|
|
4978
|
+
] }) : /* @__PURE__ */ jsx20(
|
|
4791
4979
|
"span",
|
|
4792
4980
|
{
|
|
4793
4981
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
@@ -4795,7 +4983,7 @@ function TransitionDesigner({
|
|
|
4795
4983
|
children: TYPE_LABEL[row.type]
|
|
4796
4984
|
}
|
|
4797
4985
|
),
|
|
4798
|
-
isCreatingThis ? /* @__PURE__ */
|
|
4986
|
+
isCreatingThis ? /* @__PURE__ */ jsx20("div", { className: "w-full", children: /* @__PURE__ */ jsx20(
|
|
4799
4987
|
SorceryProgressBar,
|
|
4800
4988
|
{
|
|
4801
4989
|
isLoading: true,
|
|
@@ -4803,7 +4991,7 @@ function TransitionDesigner({
|
|
|
4803
4991
|
statusText: "CREATING",
|
|
4804
4992
|
estimatedDurationMs: row.type === "crossfade" ? CROSSFADE_ESTIMATE_MS : FADE_ESTIMATE_MS
|
|
4805
4993
|
}
|
|
4806
|
-
) }) : /* @__PURE__ */
|
|
4994
|
+
) }) : /* @__PURE__ */ jsx20(
|
|
4807
4995
|
"button",
|
|
4808
4996
|
{
|
|
4809
4997
|
type: "button",
|
|
@@ -4814,7 +5002,7 @@ function TransitionDesigner({
|
|
|
4814
5002
|
children: "Create"
|
|
4815
5003
|
}
|
|
4816
5004
|
),
|
|
4817
|
-
errMsg && /* @__PURE__ */
|
|
5005
|
+
errMsg && /* @__PURE__ */ jsx20(
|
|
4818
5006
|
"span",
|
|
4819
5007
|
{
|
|
4820
5008
|
"data-testid": `${testIdPrefix}-row-error-${i}`,
|
|
@@ -4832,9 +5020,38 @@ function TransitionDesigner({
|
|
|
4832
5020
|
] });
|
|
4833
5021
|
}
|
|
4834
5022
|
|
|
5023
|
+
// src/components/GroupCollapseChevron.tsx
|
|
5024
|
+
import { ChevronDown as ChevronDown2 } from "lucide-react";
|
|
5025
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
5026
|
+
function GroupCollapseChevron({
|
|
5027
|
+
collapsed = false,
|
|
5028
|
+
onToggle,
|
|
5029
|
+
what = "group"
|
|
5030
|
+
}) {
|
|
5031
|
+
if (!onToggle) return null;
|
|
5032
|
+
return /* @__PURE__ */ jsx21(
|
|
5033
|
+
"button",
|
|
5034
|
+
{
|
|
5035
|
+
type: "button",
|
|
5036
|
+
"data-testid": "sdk-group-collapse",
|
|
5037
|
+
onClick: onToggle,
|
|
5038
|
+
"aria-expanded": !collapsed,
|
|
5039
|
+
title: collapsed ? `Expand ${what}` : `Collapse ${what}`,
|
|
5040
|
+
className: "shrink-0 px-0.5 py-0.5 rounded-sm text-sas-muted hover:text-sas-accent transition-colors",
|
|
5041
|
+
children: /* @__PURE__ */ jsx21(
|
|
5042
|
+
ChevronDown2,
|
|
5043
|
+
{
|
|
5044
|
+
className: `w-3 h-3 transition-transform ${collapsed ? "-rotate-90" : ""}`,
|
|
5045
|
+
strokeWidth: 2.5
|
|
5046
|
+
}
|
|
5047
|
+
)
|
|
5048
|
+
}
|
|
5049
|
+
);
|
|
5050
|
+
}
|
|
5051
|
+
|
|
4835
5052
|
// src/components/PanelMasterStrip.tsx
|
|
4836
|
-
import { useMemo as useMemo7, useState as
|
|
4837
|
-
import { jsx as
|
|
5053
|
+
import { useMemo as useMemo7, useState as useState15 } from "react";
|
|
5054
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4838
5055
|
function PanelMasterStrip({
|
|
4839
5056
|
bus,
|
|
4840
5057
|
levels = null,
|
|
@@ -4853,7 +5070,7 @@ function PanelMasterStrip({
|
|
|
4853
5070
|
onToggleFxEnabled,
|
|
4854
5071
|
onShowFxEditor
|
|
4855
5072
|
}) {
|
|
4856
|
-
const [search, setSearch] =
|
|
5073
|
+
const [search, setSearch] = useState15("");
|
|
4857
5074
|
const filtered = useMemo7(() => {
|
|
4858
5075
|
const q = search.trim().toLowerCase();
|
|
4859
5076
|
if (!q) return availableFx;
|
|
@@ -4861,14 +5078,14 @@ function PanelMasterStrip({
|
|
|
4861
5078
|
(fx) => fx.name.toLowerCase().includes(q) || fx.manufacturer.toLowerCase().includes(q)
|
|
4862
5079
|
);
|
|
4863
5080
|
}, [availableFx, search]);
|
|
4864
|
-
return /* @__PURE__ */
|
|
5081
|
+
return /* @__PURE__ */ jsxs17(
|
|
4865
5082
|
"div",
|
|
4866
5083
|
{
|
|
4867
5084
|
"data-testid": "panel-master-strip",
|
|
4868
5085
|
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" : ""}`,
|
|
4869
5086
|
children: [
|
|
4870
|
-
/* @__PURE__ */
|
|
4871
|
-
/* @__PURE__ */
|
|
5087
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
|
|
5088
|
+
/* @__PURE__ */ jsx22(
|
|
4872
5089
|
"span",
|
|
4873
5090
|
{
|
|
4874
5091
|
className: "text-[9px] font-bold tracking-widest text-sas-muted/70 select-none",
|
|
@@ -4876,8 +5093,8 @@ function PanelMasterStrip({
|
|
|
4876
5093
|
children: "BUS"
|
|
4877
5094
|
}
|
|
4878
5095
|
),
|
|
4879
|
-
/* @__PURE__ */
|
|
4880
|
-
/* @__PURE__ */
|
|
5096
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex-1 min-w-[8rem] flex flex-col gap-0.5", children: [
|
|
5097
|
+
/* @__PURE__ */ jsx22(
|
|
4881
5098
|
VolumeSlider,
|
|
4882
5099
|
{
|
|
4883
5100
|
value: dbToSlider(bus.volume),
|
|
@@ -4885,8 +5102,8 @@ function PanelMasterStrip({
|
|
|
4885
5102
|
disabled
|
|
4886
5103
|
}
|
|
4887
5104
|
),
|
|
4888
|
-
/* @__PURE__ */
|
|
4889
|
-
/* @__PURE__ */
|
|
5105
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex flex-col gap-px", "data-testid": "bus-meter", children: [
|
|
5106
|
+
/* @__PURE__ */ jsx22(
|
|
4890
5107
|
LevelMeter,
|
|
4891
5108
|
{
|
|
4892
5109
|
peakDb: levels?.leftDb ?? -120,
|
|
@@ -4896,7 +5113,7 @@ function PanelMasterStrip({
|
|
|
4896
5113
|
"data-testid": "bus-meter-left"
|
|
4897
5114
|
}
|
|
4898
5115
|
),
|
|
4899
|
-
/* @__PURE__ */
|
|
5116
|
+
/* @__PURE__ */ jsx22(
|
|
4900
5117
|
LevelMeter,
|
|
4901
5118
|
{
|
|
4902
5119
|
peakDb: levels?.rightDb ?? -120,
|
|
@@ -4907,7 +5124,7 @@ function PanelMasterStrip({
|
|
|
4907
5124
|
)
|
|
4908
5125
|
] })
|
|
4909
5126
|
] }),
|
|
4910
|
-
/* @__PURE__ */
|
|
5127
|
+
/* @__PURE__ */ jsx22(
|
|
4911
5128
|
"button",
|
|
4912
5129
|
{
|
|
4913
5130
|
"data-testid": "bus-mute-button",
|
|
@@ -4918,7 +5135,7 @@ function PanelMasterStrip({
|
|
|
4918
5135
|
children: "M"
|
|
4919
5136
|
}
|
|
4920
5137
|
),
|
|
4921
|
-
/* @__PURE__ */
|
|
5138
|
+
/* @__PURE__ */ jsx22(
|
|
4922
5139
|
"button",
|
|
4923
5140
|
{
|
|
4924
5141
|
"data-testid": "bus-solo-button",
|
|
@@ -4929,14 +5146,14 @@ function PanelMasterStrip({
|
|
|
4929
5146
|
children: "S"
|
|
4930
5147
|
}
|
|
4931
5148
|
),
|
|
4932
|
-
/* @__PURE__ */
|
|
5149
|
+
/* @__PURE__ */ jsx22("div", { className: "flex items-center gap-1 max-w-[45%] min-w-0 overflow-x-auto", children: bus.fx.map((fx) => /* @__PURE__ */ jsxs17(
|
|
4933
5150
|
"span",
|
|
4934
5151
|
{
|
|
4935
5152
|
"data-testid": `bus-fx-chip-${fx.index}`,
|
|
4936
5153
|
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"}`,
|
|
4937
5154
|
title: `${fx.name}${fx.enabled ? "" : " (bypassed)"}`,
|
|
4938
5155
|
children: [
|
|
4939
|
-
/* @__PURE__ */
|
|
5156
|
+
/* @__PURE__ */ jsx22(
|
|
4940
5157
|
"button",
|
|
4941
5158
|
{
|
|
4942
5159
|
"data-testid": `bus-fx-toggle-${fx.index}`,
|
|
@@ -4947,7 +5164,7 @@ function PanelMasterStrip({
|
|
|
4947
5164
|
children: fx.enabled ? "\u25CF" : "\u25CB"
|
|
4948
5165
|
}
|
|
4949
5166
|
),
|
|
4950
|
-
onShowFxEditor ? /* @__PURE__ */
|
|
5167
|
+
onShowFxEditor ? /* @__PURE__ */ jsx22(
|
|
4951
5168
|
"button",
|
|
4952
5169
|
{
|
|
4953
5170
|
"data-testid": `bus-fx-edit-${fx.index}`,
|
|
@@ -4957,8 +5174,8 @@ function PanelMasterStrip({
|
|
|
4957
5174
|
title: `Open ${fx.name} editor`,
|
|
4958
5175
|
children: fx.name
|
|
4959
5176
|
}
|
|
4960
|
-
) : /* @__PURE__ */
|
|
4961
|
-
/* @__PURE__ */
|
|
5177
|
+
) : /* @__PURE__ */ jsx22("span", { className: "max-w-[80px] truncate", children: fx.name }),
|
|
5178
|
+
/* @__PURE__ */ jsx22(
|
|
4962
5179
|
"button",
|
|
4963
5180
|
{
|
|
4964
5181
|
"data-testid": `bus-fx-remove-${fx.index}`,
|
|
@@ -4973,7 +5190,7 @@ function PanelMasterStrip({
|
|
|
4973
5190
|
},
|
|
4974
5191
|
`${fx.index}:${fx.pluginId}`
|
|
4975
5192
|
)) }),
|
|
4976
|
-
/* @__PURE__ */
|
|
5193
|
+
/* @__PURE__ */ jsx22(
|
|
4977
5194
|
"button",
|
|
4978
5195
|
{
|
|
4979
5196
|
"data-testid": "bus-fx-add-button",
|
|
@@ -4985,9 +5202,9 @@ function PanelMasterStrip({
|
|
|
4985
5202
|
}
|
|
4986
5203
|
)
|
|
4987
5204
|
] }),
|
|
4988
|
-
fxPickerOpen && /* @__PURE__ */
|
|
4989
|
-
/* @__PURE__ */
|
|
4990
|
-
/* @__PURE__ */
|
|
5205
|
+
fxPickerOpen && /* @__PURE__ */ jsxs17("div", { "data-testid": "bus-fx-picker", className: "flex flex-col gap-2 pt-1", children: [
|
|
5206
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
|
|
5207
|
+
/* @__PURE__ */ jsx22(
|
|
4991
5208
|
"input",
|
|
4992
5209
|
{
|
|
4993
5210
|
type: "text",
|
|
@@ -4997,7 +5214,7 @@ function PanelMasterStrip({
|
|
|
4997
5214
|
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
4998
5215
|
}
|
|
4999
5216
|
),
|
|
5000
|
-
onRefreshFx && /* @__PURE__ */
|
|
5217
|
+
onRefreshFx && /* @__PURE__ */ jsx22(
|
|
5001
5218
|
"button",
|
|
5002
5219
|
{
|
|
5003
5220
|
onClick: () => onRefreshFx(),
|
|
@@ -5008,8 +5225,8 @@ function PanelMasterStrip({
|
|
|
5008
5225
|
}
|
|
5009
5226
|
)
|
|
5010
5227
|
] }),
|
|
5011
|
-
fxLoading && availableFx.length === 0 ? /* @__PURE__ */
|
|
5012
|
-
filtered.map((fx) => /* @__PURE__ */
|
|
5228
|
+
fxLoading && availableFx.length === 0 ? /* @__PURE__ */ jsx22("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ jsxs17("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
5229
|
+
filtered.map((fx) => /* @__PURE__ */ jsxs17(
|
|
5013
5230
|
"button",
|
|
5014
5231
|
{
|
|
5015
5232
|
"data-testid": `bus-fx-pick-${fx.pluginId}`,
|
|
@@ -5017,13 +5234,13 @@ function PanelMasterStrip({
|
|
|
5017
5234
|
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",
|
|
5018
5235
|
title: `${fx.name} by ${fx.manufacturer} (${fx.type.toUpperCase()})`,
|
|
5019
5236
|
children: [
|
|
5020
|
-
/* @__PURE__ */
|
|
5021
|
-
/* @__PURE__ */
|
|
5237
|
+
/* @__PURE__ */ jsx22("span", { className: "text-xs font-medium truncate w-full", children: fx.name }),
|
|
5238
|
+
/* @__PURE__ */ jsx22("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: fx.manufacturer || fx.type.toUpperCase() })
|
|
5022
5239
|
]
|
|
5023
5240
|
},
|
|
5024
5241
|
fx.pluginId
|
|
5025
5242
|
)),
|
|
5026
|
-
filtered.length === 0 && /* @__PURE__ */
|
|
5243
|
+
filtered.length === 0 && /* @__PURE__ */ jsx22("div", { className: "col-span-3 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No FX plugins found" })
|
|
5027
5244
|
] })
|
|
5028
5245
|
] })
|
|
5029
5246
|
]
|
|
@@ -5032,18 +5249,18 @@ function PanelMasterStrip({
|
|
|
5032
5249
|
}
|
|
5033
5250
|
|
|
5034
5251
|
// src/hooks/usePanelBus.ts
|
|
5035
|
-
import { useCallback as
|
|
5252
|
+
import { useCallback as useCallback11, useEffect as useEffect12, useRef as useRef12, useState as useState16 } from "react";
|
|
5036
5253
|
var LEVELS_POLL_MS = 66;
|
|
5037
5254
|
function usePanelBus(host, activeSceneId) {
|
|
5038
5255
|
const supported = typeof host.getPanelBusState === "function";
|
|
5039
|
-
const [bus, setBus] =
|
|
5040
|
-
const [levels, setLevels] =
|
|
5041
|
-
const [availableFx, setAvailableFx] =
|
|
5042
|
-
const [fxLoading, setFxLoading] =
|
|
5043
|
-
const [fxPickerOpen, setFxPickerOpen] =
|
|
5256
|
+
const [bus, setBus] = useState16(null);
|
|
5257
|
+
const [levels, setLevels] = useState16(null);
|
|
5258
|
+
const [availableFx, setAvailableFx] = useState16([]);
|
|
5259
|
+
const [fxLoading, setFxLoading] = useState16(false);
|
|
5260
|
+
const [fxPickerOpen, setFxPickerOpen] = useState16(false);
|
|
5044
5261
|
const fxLoadedRef = useRef12(false);
|
|
5045
5262
|
const loadSeqRef = useRef12(0);
|
|
5046
|
-
const reload =
|
|
5263
|
+
const reload = useCallback11(async () => {
|
|
5047
5264
|
if (!supported || !activeSceneId || !host.getPanelBusState) {
|
|
5048
5265
|
setBus(null);
|
|
5049
5266
|
return;
|
|
@@ -5055,12 +5272,12 @@ function usePanelBus(host, activeSceneId) {
|
|
|
5055
5272
|
} catch {
|
|
5056
5273
|
}
|
|
5057
5274
|
}, [host, activeSceneId, supported]);
|
|
5058
|
-
|
|
5275
|
+
useEffect12(() => {
|
|
5059
5276
|
setBus(null);
|
|
5060
5277
|
setFxPickerOpen(false);
|
|
5061
5278
|
void reload();
|
|
5062
5279
|
}, [reload]);
|
|
5063
|
-
|
|
5280
|
+
useEffect12(() => {
|
|
5064
5281
|
if (!supported || !activeSceneId || !bus?.engaged || !host.getPanelBusLevels) {
|
|
5065
5282
|
setLevels(null);
|
|
5066
5283
|
return;
|
|
@@ -5082,7 +5299,7 @@ function usePanelBus(host, activeSceneId) {
|
|
|
5082
5299
|
clearInterval(id);
|
|
5083
5300
|
};
|
|
5084
5301
|
}, [supported, activeSceneId, bus?.engaged, host]);
|
|
5085
|
-
const loadFxList =
|
|
5302
|
+
const loadFxList = useCallback11(
|
|
5086
5303
|
async (opts) => {
|
|
5087
5304
|
if (!supported || !host.getAvailableFx) return;
|
|
5088
5305
|
if (fxLoadedRef.current && !opts.force && !opts.rescan) return;
|
|
@@ -5098,14 +5315,14 @@ function usePanelBus(host, activeSceneId) {
|
|
|
5098
5315
|
},
|
|
5099
5316
|
[host, supported]
|
|
5100
5317
|
);
|
|
5101
|
-
const openPicker =
|
|
5318
|
+
const openPicker = useCallback11(
|
|
5102
5319
|
(open) => {
|
|
5103
5320
|
setFxPickerOpen(open);
|
|
5104
5321
|
if (open) void loadFxList({});
|
|
5105
5322
|
},
|
|
5106
5323
|
[loadFxList]
|
|
5107
5324
|
);
|
|
5108
|
-
const mutate =
|
|
5325
|
+
const mutate = useCallback11(
|
|
5109
5326
|
(fn) => {
|
|
5110
5327
|
if (!fn || !activeSceneId) return;
|
|
5111
5328
|
void (async () => {
|
|
@@ -5149,8 +5366,8 @@ function usePanelBus(host, activeSceneId) {
|
|
|
5149
5366
|
}
|
|
5150
5367
|
|
|
5151
5368
|
// src/components/DownloadPackButton.tsx
|
|
5152
|
-
import { useCallback as
|
|
5153
|
-
import { jsx as
|
|
5369
|
+
import { useCallback as useCallback12, useEffect as useEffect13, useState as useState17 } from "react";
|
|
5370
|
+
import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
5154
5371
|
function formatSize(bytes) {
|
|
5155
5372
|
if (!bytes || bytes <= 0) return "";
|
|
5156
5373
|
const gb = bytes / 1024 ** 3;
|
|
@@ -5166,10 +5383,10 @@ var DownloadPackButton = ({
|
|
|
5166
5383
|
variant = "compact",
|
|
5167
5384
|
onDownloadComplete
|
|
5168
5385
|
}) => {
|
|
5169
|
-
const [status, setStatus] =
|
|
5170
|
-
const [progress, setProgress] =
|
|
5171
|
-
const [errorMessage, setErrorMessage] =
|
|
5172
|
-
|
|
5386
|
+
const [status, setStatus] = useState17("idle");
|
|
5387
|
+
const [progress, setProgress] = useState17(0);
|
|
5388
|
+
const [errorMessage, setErrorMessage] = useState17(null);
|
|
5389
|
+
useEffect13(() => {
|
|
5173
5390
|
const unsub = host.onSamplePackProgress(packId, (p) => {
|
|
5174
5391
|
setStatus(p.status);
|
|
5175
5392
|
setProgress(p.progress);
|
|
@@ -5184,7 +5401,7 @@ var DownloadPackButton = ({
|
|
|
5184
5401
|
});
|
|
5185
5402
|
return unsub;
|
|
5186
5403
|
}, [host, packId, onDownloadComplete]);
|
|
5187
|
-
const handleClick =
|
|
5404
|
+
const handleClick = useCallback12(async () => {
|
|
5188
5405
|
if (status !== "idle" && status !== "error") return;
|
|
5189
5406
|
try {
|
|
5190
5407
|
setStatus("downloading");
|
|
@@ -5238,8 +5455,8 @@ var DownloadPackButton = ({
|
|
|
5238
5455
|
} else {
|
|
5239
5456
|
className = `${baseClasses} text-sas-muted hover:text-sas-accent border-sas-border hover:border-sas-accent`;
|
|
5240
5457
|
}
|
|
5241
|
-
return /* @__PURE__ */
|
|
5242
|
-
/* @__PURE__ */
|
|
5458
|
+
return /* @__PURE__ */ jsxs18("div", { children: [
|
|
5459
|
+
/* @__PURE__ */ jsx23(
|
|
5243
5460
|
"button",
|
|
5244
5461
|
{
|
|
5245
5462
|
"data-testid": `download-pack-button-${packId}`,
|
|
@@ -5250,12 +5467,12 @@ var DownloadPackButton = ({
|
|
|
5250
5467
|
children: buttonLabel
|
|
5251
5468
|
}
|
|
5252
5469
|
),
|
|
5253
|
-
variant === "large" && status === "error" && errorMessage && /* @__PURE__ */
|
|
5470
|
+
variant === "large" && status === "error" && errorMessage && /* @__PURE__ */ jsx23("div", { className: "text-xs text-sas-danger mt-2", "data-testid": `download-pack-error-${packId}`, children: errorMessage })
|
|
5254
5471
|
] });
|
|
5255
5472
|
};
|
|
5256
5473
|
|
|
5257
5474
|
// src/components/SamplePackCTACard.tsx
|
|
5258
|
-
import { jsx as
|
|
5475
|
+
import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
5259
5476
|
var SamplePackCTACard = ({
|
|
5260
5477
|
host,
|
|
5261
5478
|
pack,
|
|
@@ -5263,7 +5480,7 @@ var SamplePackCTACard = ({
|
|
|
5263
5480
|
onDownloadComplete
|
|
5264
5481
|
}) => {
|
|
5265
5482
|
if (status === "checking") {
|
|
5266
|
-
return /* @__PURE__ */
|
|
5483
|
+
return /* @__PURE__ */ jsx24(
|
|
5267
5484
|
"div",
|
|
5268
5485
|
{
|
|
5269
5486
|
"data-testid": `sample-pack-cta-checking-${pack.packId}`,
|
|
@@ -5274,16 +5491,16 @@ var SamplePackCTACard = ({
|
|
|
5274
5491
|
}
|
|
5275
5492
|
const headline = status === "stale" ? `${pack.displayName} update available` : `${pack.displayName} not installed`;
|
|
5276
5493
|
const sublabel = status === "stale" ? `A newer version is available for download.` : pack.description;
|
|
5277
|
-
return /* @__PURE__ */
|
|
5494
|
+
return /* @__PURE__ */ jsxs19(
|
|
5278
5495
|
"div",
|
|
5279
5496
|
{
|
|
5280
5497
|
"data-testid": `sample-pack-cta-${pack.packId}`,
|
|
5281
5498
|
className: "flex flex-col items-center justify-center py-12 px-6 text-center",
|
|
5282
5499
|
children: [
|
|
5283
|
-
/* @__PURE__ */
|
|
5284
|
-
/* @__PURE__ */
|
|
5285
|
-
/* @__PURE__ */
|
|
5286
|
-
/* @__PURE__ */
|
|
5500
|
+
/* @__PURE__ */ jsx24("div", { className: "text-sm uppercase tracking-wide text-sas-muted mb-2", children: status === "stale" ? "Update available" : "Sample library not installed" }),
|
|
5501
|
+
/* @__PURE__ */ jsx24("div", { className: "text-base text-sas-text mb-1", children: headline }),
|
|
5502
|
+
/* @__PURE__ */ jsx24("div", { className: "text-xs text-sas-muted mb-6 max-w-md", children: sublabel }),
|
|
5503
|
+
/* @__PURE__ */ jsx24(
|
|
5287
5504
|
DownloadPackButton,
|
|
5288
5505
|
{
|
|
5289
5506
|
host,
|
|
@@ -5300,7 +5517,7 @@ var SamplePackCTACard = ({
|
|
|
5300
5517
|
};
|
|
5301
5518
|
|
|
5302
5519
|
// src/components/WaveformView.tsx
|
|
5303
|
-
import { useEffect as
|
|
5520
|
+
import { useEffect as useEffect14, useRef as useRef13, useState as useState18 } from "react";
|
|
5304
5521
|
|
|
5305
5522
|
// src/components/waveform.ts
|
|
5306
5523
|
function computePeaks(audioBuffer, bins, targetSamples) {
|
|
@@ -5363,7 +5580,7 @@ function drawWaveform(canvas, peaks, options = {}) {
|
|
|
5363
5580
|
}
|
|
5364
5581
|
|
|
5365
5582
|
// src/components/WaveformView.tsx
|
|
5366
|
-
import { jsx as
|
|
5583
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
5367
5584
|
var WaveformView = ({
|
|
5368
5585
|
host,
|
|
5369
5586
|
filePath,
|
|
@@ -5373,8 +5590,8 @@ var WaveformView = ({
|
|
|
5373
5590
|
targetSamples
|
|
5374
5591
|
}) => {
|
|
5375
5592
|
const canvasRef = useRef13(null);
|
|
5376
|
-
const [peaks, setPeaks] =
|
|
5377
|
-
|
|
5593
|
+
const [peaks, setPeaks] = useState18(null);
|
|
5594
|
+
useEffect14(() => {
|
|
5378
5595
|
let cancelled = false;
|
|
5379
5596
|
let audioContext = null;
|
|
5380
5597
|
(async () => {
|
|
@@ -5400,7 +5617,7 @@ var WaveformView = ({
|
|
|
5400
5617
|
cancelled = true;
|
|
5401
5618
|
};
|
|
5402
5619
|
}, [host, filePath, bins, targetSamples]);
|
|
5403
|
-
|
|
5620
|
+
useEffect14(() => {
|
|
5404
5621
|
if (!peaks) return;
|
|
5405
5622
|
const canvas = canvasRef.current;
|
|
5406
5623
|
if (!canvas) return;
|
|
@@ -5411,7 +5628,7 @@ var WaveformView = ({
|
|
|
5411
5628
|
observer.observe(canvas);
|
|
5412
5629
|
return () => observer.disconnect();
|
|
5413
5630
|
}, [peaks, fillStyle]);
|
|
5414
|
-
return /* @__PURE__ */
|
|
5631
|
+
return /* @__PURE__ */ jsx25(
|
|
5415
5632
|
"canvas",
|
|
5416
5633
|
{
|
|
5417
5634
|
ref: canvasRef,
|
|
@@ -5422,8 +5639,8 @@ var WaveformView = ({
|
|
|
5422
5639
|
};
|
|
5423
5640
|
|
|
5424
5641
|
// src/components/ScrollingWaveform.tsx
|
|
5425
|
-
import { useEffect as
|
|
5426
|
-
import { jsx as
|
|
5642
|
+
import { useEffect as useEffect15, useRef as useRef14 } from "react";
|
|
5643
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
5427
5644
|
var ScrollingWaveform = ({
|
|
5428
5645
|
getPeakDb,
|
|
5429
5646
|
active,
|
|
@@ -5435,7 +5652,7 @@ var ScrollingWaveform = ({
|
|
|
5435
5652
|
const ringRef = useRef14(new Float32Array(columns));
|
|
5436
5653
|
const writeIdxRef = useRef14(0);
|
|
5437
5654
|
const rafRef = useRef14(null);
|
|
5438
|
-
|
|
5655
|
+
useEffect15(() => {
|
|
5439
5656
|
if (ringRef.current.length !== columns) {
|
|
5440
5657
|
const next = new Float32Array(columns);
|
|
5441
5658
|
const prev = ringRef.current;
|
|
@@ -5447,7 +5664,7 @@ var ScrollingWaveform = ({
|
|
|
5447
5664
|
writeIdxRef.current = writeIdxRef.current % columns;
|
|
5448
5665
|
}
|
|
5449
5666
|
}, [columns]);
|
|
5450
|
-
|
|
5667
|
+
useEffect15(() => {
|
|
5451
5668
|
if (!active) {
|
|
5452
5669
|
if (rafRef.current !== null) {
|
|
5453
5670
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -5499,7 +5716,7 @@ var ScrollingWaveform = ({
|
|
|
5499
5716
|
}
|
|
5500
5717
|
};
|
|
5501
5718
|
}, [active, getPeakDb, fillStyle]);
|
|
5502
|
-
return /* @__PURE__ */
|
|
5719
|
+
return /* @__PURE__ */ jsx26(
|
|
5503
5720
|
"canvas",
|
|
5504
5721
|
{
|
|
5505
5722
|
ref: canvasRef,
|
|
@@ -5510,8 +5727,8 @@ var ScrollingWaveform = ({
|
|
|
5510
5727
|
};
|
|
5511
5728
|
|
|
5512
5729
|
// src/components/OffsetScrubber.tsx
|
|
5513
|
-
import { useCallback as
|
|
5514
|
-
import { jsx as
|
|
5730
|
+
import { useCallback as useCallback13, useEffect as useEffect16, useMemo as useMemo8, useRef as useRef15, useState as useState19 } from "react";
|
|
5731
|
+
import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
5515
5732
|
var SLIDER_HEIGHT_PX = 28;
|
|
5516
5733
|
var TICK_HEIGHT_PX = 14;
|
|
5517
5734
|
var DOWNBEAT_TICK_HEIGHT_PX = 22;
|
|
@@ -5525,9 +5742,9 @@ function OffsetScrubber({
|
|
|
5525
5742
|
disabled = false
|
|
5526
5743
|
}) {
|
|
5527
5744
|
const trackRef = useRef15(null);
|
|
5528
|
-
const [draftOffset, setDraftOffset] =
|
|
5529
|
-
const [isDragging, setIsDragging] =
|
|
5530
|
-
|
|
5745
|
+
const [draftOffset, setDraftOffset] = useState19(offsetSamples);
|
|
5746
|
+
const [isDragging, setIsDragging] = useState19(false);
|
|
5747
|
+
useEffect16(() => {
|
|
5531
5748
|
if (!isDragging) setDraftOffset(offsetSamples);
|
|
5532
5749
|
}, [offsetSamples, isDragging]);
|
|
5533
5750
|
const sampleRate = cuePoints?.sample_rate ?? 44100;
|
|
@@ -5536,14 +5753,14 @@ function OffsetScrubber({
|
|
|
5536
5753
|
return Math.round(60 / projectBpm * sampleRate);
|
|
5537
5754
|
}, [projectBpm, sampleRate]);
|
|
5538
5755
|
const rangeSamples = beatsForRange * meter;
|
|
5539
|
-
const sampleToFraction =
|
|
5756
|
+
const sampleToFraction = useCallback13(
|
|
5540
5757
|
(sample) => {
|
|
5541
5758
|
const clamped = Math.max(-rangeSamples, Math.min(rangeSamples, sample));
|
|
5542
5759
|
return (clamped + rangeSamples) / (2 * rangeSamples);
|
|
5543
5760
|
},
|
|
5544
5761
|
[rangeSamples]
|
|
5545
5762
|
);
|
|
5546
|
-
const fractionToSample =
|
|
5763
|
+
const fractionToSample = useCallback13(
|
|
5547
5764
|
(fraction) => {
|
|
5548
5765
|
const clamped = Math.max(0, Math.min(1, fraction));
|
|
5549
5766
|
return Math.round(clamped * 2 * rangeSamples - rangeSamples);
|
|
@@ -5557,7 +5774,7 @@ function OffsetScrubber({
|
|
|
5557
5774
|
const negatives = positives.slice(1).map((p) => -p);
|
|
5558
5775
|
return [...negatives, ...positives].sort((a, b) => a - b);
|
|
5559
5776
|
}, [cuePoints]);
|
|
5560
|
-
const snapToBeat =
|
|
5777
|
+
const snapToBeat = useCallback13(
|
|
5561
5778
|
(sample) => {
|
|
5562
5779
|
if (snapTargets.length === 0) return sample;
|
|
5563
5780
|
let best = snapTargets[0];
|
|
@@ -5573,7 +5790,7 @@ function OffsetScrubber({
|
|
|
5573
5790
|
},
|
|
5574
5791
|
[snapTargets]
|
|
5575
5792
|
);
|
|
5576
|
-
const handlePointerDown =
|
|
5793
|
+
const handlePointerDown = useCallback13(
|
|
5577
5794
|
(e) => {
|
|
5578
5795
|
if (disabled || !cuePoints) return;
|
|
5579
5796
|
e.preventDefault();
|
|
@@ -5607,7 +5824,7 @@ function OffsetScrubber({
|
|
|
5607
5824
|
},
|
|
5608
5825
|
[disabled, cuePoints, fractionToSample, onChange, snapToBeat]
|
|
5609
5826
|
);
|
|
5610
|
-
const handleResetToZero =
|
|
5827
|
+
const handleResetToZero = useCallback13(() => {
|
|
5611
5828
|
if (disabled) return;
|
|
5612
5829
|
setDraftOffset(0);
|
|
5613
5830
|
onChange(0);
|
|
@@ -5626,9 +5843,9 @@ function OffsetScrubber({
|
|
|
5626
5843
|
});
|
|
5627
5844
|
}, [cuePoints, sampleToFraction]);
|
|
5628
5845
|
const isDisabled = disabled || !cuePoints || cuePoints.beats.length === 0;
|
|
5629
|
-
return /* @__PURE__ */
|
|
5630
|
-
/* @__PURE__ */
|
|
5631
|
-
/* @__PURE__ */
|
|
5846
|
+
return /* @__PURE__ */ jsxs20("div", { "data-testid": "offset-scrubber", className: "flex items-center gap-2 w-full", children: [
|
|
5847
|
+
/* @__PURE__ */ jsx27("span", { className: "text-[9px] text-sas-muted/60 uppercase tracking-wide flex-shrink-0", children: "Align" }),
|
|
5848
|
+
/* @__PURE__ */ jsxs20(
|
|
5632
5849
|
"div",
|
|
5633
5850
|
{
|
|
5634
5851
|
ref: trackRef,
|
|
@@ -5644,7 +5861,7 @@ function OffsetScrubber({
|
|
|
5644
5861
|
"aria-valuenow": draftOffset,
|
|
5645
5862
|
"aria-disabled": isDisabled,
|
|
5646
5863
|
children: [
|
|
5647
|
-
/* @__PURE__ */
|
|
5864
|
+
/* @__PURE__ */ jsx27(
|
|
5648
5865
|
"div",
|
|
5649
5866
|
{
|
|
5650
5867
|
"aria-hidden": "true",
|
|
@@ -5652,7 +5869,7 @@ function OffsetScrubber({
|
|
|
5652
5869
|
style: { left: "50%" }
|
|
5653
5870
|
}
|
|
5654
5871
|
),
|
|
5655
|
-
ticks.map((t) => /* @__PURE__ */
|
|
5872
|
+
ticks.map((t) => /* @__PURE__ */ jsx27(
|
|
5656
5873
|
"div",
|
|
5657
5874
|
{
|
|
5658
5875
|
"data-testid": t.isDownbeat ? "offset-tick-downbeat" : "offset-tick",
|
|
@@ -5667,7 +5884,7 @@ function OffsetScrubber({
|
|
|
5667
5884
|
},
|
|
5668
5885
|
t.i
|
|
5669
5886
|
)),
|
|
5670
|
-
/* @__PURE__ */
|
|
5887
|
+
/* @__PURE__ */ jsx27(
|
|
5671
5888
|
"div",
|
|
5672
5889
|
{
|
|
5673
5890
|
"data-testid": "offset-scrubber-thumb",
|
|
@@ -5684,7 +5901,7 @@ function OffsetScrubber({
|
|
|
5684
5901
|
]
|
|
5685
5902
|
}
|
|
5686
5903
|
),
|
|
5687
|
-
/* @__PURE__ */
|
|
5904
|
+
/* @__PURE__ */ jsx27(
|
|
5688
5905
|
"span",
|
|
5689
5906
|
{
|
|
5690
5907
|
"data-testid": "offset-scrubber-readout",
|
|
@@ -5692,7 +5909,7 @@ function OffsetScrubber({
|
|
|
5692
5909
|
children: formatOffset(draftOffset, sampleRate)
|
|
5693
5910
|
}
|
|
5694
5911
|
),
|
|
5695
|
-
/* @__PURE__ */
|
|
5912
|
+
/* @__PURE__ */ jsx27(
|
|
5696
5913
|
"button",
|
|
5697
5914
|
{
|
|
5698
5915
|
type: "button",
|
|
@@ -5704,7 +5921,7 @@ function OffsetScrubber({
|
|
|
5704
5921
|
children: "\u2316"
|
|
5705
5922
|
}
|
|
5706
5923
|
),
|
|
5707
|
-
bpmMismatch && /* @__PURE__ */
|
|
5924
|
+
bpmMismatch && /* @__PURE__ */ jsx27(
|
|
5708
5925
|
"span",
|
|
5709
5926
|
{
|
|
5710
5927
|
"data-testid": "offset-bpm-mismatch",
|
|
@@ -5776,16 +5993,16 @@ function synthesizeCuePoints({
|
|
|
5776
5993
|
}
|
|
5777
5994
|
|
|
5778
5995
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
5779
|
-
import { useState as
|
|
5996
|
+
import { useState as useState24, useEffect as useEffect19, useCallback as useCallback17, useRef as useRef19, useMemo as useMemo10 } from "react";
|
|
5780
5997
|
|
|
5781
5998
|
// src/hooks/useSceneState.ts
|
|
5782
|
-
import { useState as
|
|
5999
|
+
import { useState as useState20, useCallback as useCallback14, useRef as useRef16 } from "react";
|
|
5783
6000
|
function useSceneState(activeSceneId, initialValue) {
|
|
5784
|
-
const [stateMap, setStateMap] =
|
|
6001
|
+
const [stateMap, setStateMap] = useState20(() => /* @__PURE__ */ new Map());
|
|
5785
6002
|
const activeSceneIdRef = useRef16(activeSceneId);
|
|
5786
6003
|
activeSceneIdRef.current = activeSceneId;
|
|
5787
6004
|
const currentValue = activeSceneId !== null && stateMap.has(activeSceneId) ? stateMap.get(activeSceneId) : initialValue;
|
|
5788
|
-
const setForCurrentScene =
|
|
6005
|
+
const setForCurrentScene = useCallback14((value) => {
|
|
5789
6006
|
const sid = activeSceneIdRef.current;
|
|
5790
6007
|
if (sid === null) return;
|
|
5791
6008
|
setStateMap((prev) => {
|
|
@@ -5796,7 +6013,7 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
5796
6013
|
return newMap;
|
|
5797
6014
|
});
|
|
5798
6015
|
}, [initialValue]);
|
|
5799
|
-
const setForScene =
|
|
6016
|
+
const setForScene = useCallback14((sceneId, value) => {
|
|
5800
6017
|
setStateMap((prev) => {
|
|
5801
6018
|
const current = prev.has(sceneId) ? prev.get(sceneId) : initialValue;
|
|
5802
6019
|
const next = typeof value === "function" ? value(current) : value;
|
|
@@ -5809,10 +6026,10 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
5809
6026
|
}
|
|
5810
6027
|
|
|
5811
6028
|
// src/hooks/useAnySolo.ts
|
|
5812
|
-
import { useEffect as
|
|
6029
|
+
import { useEffect as useEffect17, useState as useState21 } from "react";
|
|
5813
6030
|
function useAnySolo(host) {
|
|
5814
|
-
const [anySolo, setAnySolo] =
|
|
5815
|
-
|
|
6031
|
+
const [anySolo, setAnySolo] = useState21(false);
|
|
6032
|
+
useEffect17(() => {
|
|
5816
6033
|
let active = true;
|
|
5817
6034
|
const refresh = () => {
|
|
5818
6035
|
host.isAnySoloActive().then((v) => {
|
|
@@ -5831,7 +6048,7 @@ function useAnySolo(host) {
|
|
|
5831
6048
|
}
|
|
5832
6049
|
|
|
5833
6050
|
// src/hooks/useSoundHistory.ts
|
|
5834
|
-
import { useCallback as
|
|
6051
|
+
import { useCallback as useCallback15, useMemo as useMemo9, useRef as useRef17, useState as useState22 } from "react";
|
|
5835
6052
|
var EMPTY = { entries: [], cursor: -1 };
|
|
5836
6053
|
function sameDescriptor(a, b) {
|
|
5837
6054
|
if (a === b) return true;
|
|
@@ -5848,9 +6065,9 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5848
6065
|
const onChangeRef = useRef17(opts.onChange);
|
|
5849
6066
|
onChangeRef.current = opts.onChange;
|
|
5850
6067
|
const dataRef = useRef17({});
|
|
5851
|
-
const [, setVersion] =
|
|
5852
|
-
const bump =
|
|
5853
|
-
const commit =
|
|
6068
|
+
const [, setVersion] = useState22(0);
|
|
6069
|
+
const bump = useCallback15(() => setVersion((v) => v + 1), []);
|
|
6070
|
+
const commit = useCallback15(
|
|
5854
6071
|
(trackId, next, notify) => {
|
|
5855
6072
|
dataRef.current = { ...dataRef.current, [trackId]: next };
|
|
5856
6073
|
bump();
|
|
@@ -5858,7 +6075,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5858
6075
|
},
|
|
5859
6076
|
[bump]
|
|
5860
6077
|
);
|
|
5861
|
-
const record =
|
|
6078
|
+
const record = useCallback15(
|
|
5862
6079
|
(trackId, descriptor, label) => {
|
|
5863
6080
|
const h = dataRef.current[trackId];
|
|
5864
6081
|
const current = h && h.cursor >= 0 ? h.entries[h.cursor] : void 0;
|
|
@@ -5873,7 +6090,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5873
6090
|
},
|
|
5874
6091
|
[max, commit]
|
|
5875
6092
|
);
|
|
5876
|
-
const restoreTo =
|
|
6093
|
+
const restoreTo = useCallback15(
|
|
5877
6094
|
async (trackId, index) => {
|
|
5878
6095
|
const h = dataRef.current[trackId];
|
|
5879
6096
|
if (!h || index < 0 || index >= h.entries.length || index === h.cursor) return false;
|
|
@@ -5883,7 +6100,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5883
6100
|
},
|
|
5884
6101
|
[commit]
|
|
5885
6102
|
);
|
|
5886
|
-
const undo =
|
|
6103
|
+
const undo = useCallback15(
|
|
5887
6104
|
(trackId) => {
|
|
5888
6105
|
const h = dataRef.current[trackId];
|
|
5889
6106
|
if (!h || h.cursor <= 0) return Promise.resolve(false);
|
|
@@ -5891,7 +6108,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5891
6108
|
},
|
|
5892
6109
|
[restoreTo]
|
|
5893
6110
|
);
|
|
5894
|
-
const toggleFavorite =
|
|
6111
|
+
const toggleFavorite = useCallback15(
|
|
5895
6112
|
(trackId, index) => {
|
|
5896
6113
|
const h = dataRef.current[trackId];
|
|
5897
6114
|
if (!h || index < 0 || index >= h.entries.length) return;
|
|
@@ -5900,7 +6117,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5900
6117
|
},
|
|
5901
6118
|
[commit]
|
|
5902
6119
|
);
|
|
5903
|
-
const restore =
|
|
6120
|
+
const restore = useCallback15(
|
|
5904
6121
|
(trackId, state) => {
|
|
5905
6122
|
const entries = Array.isArray(state?.entries) ? [...state.entries] : [];
|
|
5906
6123
|
const raw = typeof state?.cursor === "number" ? state.cursor : entries.length - 1;
|
|
@@ -5909,15 +6126,15 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5909
6126
|
},
|
|
5910
6127
|
[commit]
|
|
5911
6128
|
);
|
|
5912
|
-
const list =
|
|
6129
|
+
const list = useCallback15(
|
|
5913
6130
|
(trackId) => dataRef.current[trackId] ?? EMPTY,
|
|
5914
6131
|
[]
|
|
5915
6132
|
);
|
|
5916
|
-
const canUndo =
|
|
6133
|
+
const canUndo = useCallback15((trackId) => {
|
|
5917
6134
|
const h = dataRef.current[trackId];
|
|
5918
6135
|
return !!h && h.cursor > 0;
|
|
5919
6136
|
}, []);
|
|
5920
|
-
const clear =
|
|
6137
|
+
const clear = useCallback15(
|
|
5921
6138
|
(trackId) => {
|
|
5922
6139
|
if (dataRef.current[trackId]) {
|
|
5923
6140
|
const next = { ...dataRef.current };
|
|
@@ -5929,7 +6146,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5929
6146
|
},
|
|
5930
6147
|
[bump]
|
|
5931
6148
|
);
|
|
5932
|
-
const reset =
|
|
6149
|
+
const reset = useCallback15(() => {
|
|
5933
6150
|
dataRef.current = {};
|
|
5934
6151
|
bump();
|
|
5935
6152
|
}, [bump]);
|
|
@@ -6009,7 +6226,7 @@ function resolveTrackGroups(parsedGroups, tracks, getDbId, opts = {}) {
|
|
|
6009
6226
|
}
|
|
6010
6227
|
|
|
6011
6228
|
// src/panel-core/useTransitionOps.ts
|
|
6012
|
-
import { useCallback as
|
|
6229
|
+
import { useCallback as useCallback16, useEffect as useEffect18, useRef as useRef18, useState as useState23 } from "react";
|
|
6013
6230
|
function useTransitionOps({
|
|
6014
6231
|
host,
|
|
6015
6232
|
adapter,
|
|
@@ -6027,7 +6244,7 @@ function useTransitionOps({
|
|
|
6027
6244
|
}) {
|
|
6028
6245
|
const { identity } = adapter;
|
|
6029
6246
|
const appliedFadeAutomationRef = useRef18(/* @__PURE__ */ new Set());
|
|
6030
|
-
const applyCrossfadeAutomation =
|
|
6247
|
+
const applyCrossfadeAutomation = useCallback16(
|
|
6031
6248
|
async (originTrackId, targetTrackId, bars, bpm, sliderPos) => {
|
|
6032
6249
|
if (host.setTrackVolumeAutomation) {
|
|
6033
6250
|
const curves = buildCrossfadeVolumeCurves(bars, bpm, sliderPos);
|
|
@@ -6044,7 +6261,7 @@ function useTransitionOps({
|
|
|
6044
6261
|
},
|
|
6045
6262
|
[host]
|
|
6046
6263
|
);
|
|
6047
|
-
const applyFadeAutomation =
|
|
6264
|
+
const applyFadeAutomation = useCallback16(
|
|
6048
6265
|
async (trackId, direction, bars, bpm, sliderPos, gesture) => {
|
|
6049
6266
|
if (!host.setTrackVolumeAutomation) return;
|
|
6050
6267
|
const points = buildFadeVolumeCurve(bars, bpm, direction, sliderPos, gesture);
|
|
@@ -6053,7 +6270,7 @@ function useTransitionOps({
|
|
|
6053
6270
|
},
|
|
6054
6271
|
[host]
|
|
6055
6272
|
);
|
|
6056
|
-
const copyTrackFx =
|
|
6273
|
+
const copyTrackFx = useCallback16(
|
|
6057
6274
|
async (newTrackId, sourceDbId) => {
|
|
6058
6275
|
if (typeof host.copyTrackFxFrom !== "function") return;
|
|
6059
6276
|
try {
|
|
@@ -6070,8 +6287,8 @@ function useTransitionOps({
|
|
|
6070
6287
|
},
|
|
6071
6288
|
[host]
|
|
6072
6289
|
);
|
|
6073
|
-
const [isCreatingCrossfade, setIsCreatingCrossfade] =
|
|
6074
|
-
const handleCreateCrossfade =
|
|
6290
|
+
const [isCreatingCrossfade, setIsCreatingCrossfade] = useState23(false);
|
|
6291
|
+
const handleCreateCrossfade = useCallback16(
|
|
6075
6292
|
async (origin, target) => {
|
|
6076
6293
|
const scene = activeSceneId;
|
|
6077
6294
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -6201,9 +6418,9 @@ function useTransitionOps({
|
|
|
6201
6418
|
loadTracks
|
|
6202
6419
|
]
|
|
6203
6420
|
);
|
|
6204
|
-
const [isCreatingGroupFade, setIsCreatingGroupFade] =
|
|
6205
|
-
const [isCreatingFade, setIsCreatingFade] =
|
|
6206
|
-
const handleCreateFade =
|
|
6421
|
+
const [isCreatingGroupFade, setIsCreatingGroupFade] = useState23(false);
|
|
6422
|
+
const [isCreatingFade, setIsCreatingFade] = useState23(false);
|
|
6423
|
+
const handleCreateFade = useCallback16(
|
|
6207
6424
|
async (selection, direction, gesture) => {
|
|
6208
6425
|
const scene = activeSceneId;
|
|
6209
6426
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -6314,7 +6531,7 @@ function useTransitionOps({
|
|
|
6314
6531
|
loadTracks
|
|
6315
6532
|
]
|
|
6316
6533
|
);
|
|
6317
|
-
const handleCrossfadeMute =
|
|
6534
|
+
const handleCrossfadeMute = useCallback16(
|
|
6318
6535
|
(pair) => {
|
|
6319
6536
|
const newMuted = !pair.origin.runtimeState.muted;
|
|
6320
6537
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -6329,7 +6546,7 @@ function useTransitionOps({
|
|
|
6329
6546
|
},
|
|
6330
6547
|
[host, setTracks]
|
|
6331
6548
|
);
|
|
6332
|
-
const handleCrossfadeSolo =
|
|
6549
|
+
const handleCrossfadeSolo = useCallback16(
|
|
6333
6550
|
(pair) => {
|
|
6334
6551
|
const newSolo = !pair.origin.runtimeState.solo;
|
|
6335
6552
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -6344,7 +6561,7 @@ function useTransitionOps({
|
|
|
6344
6561
|
},
|
|
6345
6562
|
[host, setTracks]
|
|
6346
6563
|
);
|
|
6347
|
-
const handleCrossfadeDelete =
|
|
6564
|
+
const handleCrossfadeDelete = useCallback16(
|
|
6348
6565
|
async (pair) => {
|
|
6349
6566
|
try {
|
|
6350
6567
|
for (const member of [pair.origin, pair.target]) {
|
|
@@ -6371,7 +6588,7 @@ function useTransitionOps({
|
|
|
6371
6588
|
[host, activeSceneId, setCrossfadePairsMeta, setTracks]
|
|
6372
6589
|
);
|
|
6373
6590
|
const crossfadeSliderTimers = useRef18({});
|
|
6374
|
-
const handleCrossfadeSlider =
|
|
6591
|
+
const handleCrossfadeSlider = useCallback16(
|
|
6375
6592
|
(pair, pos) => {
|
|
6376
6593
|
setCrossfadePairsMeta(
|
|
6377
6594
|
(prev) => prev.map((p) => p.groupId === pair.groupId ? { ...p, sliderPos: pos } : p)
|
|
@@ -6404,7 +6621,7 @@ function useTransitionOps({
|
|
|
6404
6621
|
},
|
|
6405
6622
|
[host, activeSceneId, applyCrossfadeAutomation, setCrossfadePairsMeta]
|
|
6406
6623
|
);
|
|
6407
|
-
const handleFadeDelete =
|
|
6624
|
+
const handleFadeDelete = useCallback16(
|
|
6408
6625
|
async (fade) => {
|
|
6409
6626
|
try {
|
|
6410
6627
|
await host.deleteTrack(fade.track.handle.id);
|
|
@@ -6425,7 +6642,7 @@ function useTransitionOps({
|
|
|
6425
6642
|
[host, activeSceneId, setFadesMeta, setTracks]
|
|
6426
6643
|
);
|
|
6427
6644
|
const fadeSliderTimers = useRef18({});
|
|
6428
|
-
const handleFadeSlider =
|
|
6645
|
+
const handleFadeSlider = useCallback16(
|
|
6429
6646
|
(fade, pos) => {
|
|
6430
6647
|
setFadesMeta(
|
|
6431
6648
|
(prev) => prev.map((f) => f.dbId === fade.dbId ? { ...f, meta: { ...f.meta, sliderPos: pos } } : f)
|
|
@@ -6455,7 +6672,7 @@ function useTransitionOps({
|
|
|
6455
6672
|
},
|
|
6456
6673
|
[host, activeSceneId, applyFadeAutomation, setFadesMeta]
|
|
6457
6674
|
);
|
|
6458
|
-
const handleCreateVerbatimGroupFade =
|
|
6675
|
+
const handleCreateVerbatimGroupFade = useCallback16(
|
|
6459
6676
|
async (subject, direction) => {
|
|
6460
6677
|
const groupAdapter = adapter.transitionGroup;
|
|
6461
6678
|
if (!groupAdapter) throw new Error("This panel does not support group fades.");
|
|
@@ -6571,7 +6788,7 @@ function useTransitionOps({
|
|
|
6571
6788
|
]
|
|
6572
6789
|
);
|
|
6573
6790
|
const groupFadeSliderTimers = useRef18({});
|
|
6574
|
-
const handleGroupFadeSlider =
|
|
6791
|
+
const handleGroupFadeSlider = useCallback16(
|
|
6575
6792
|
(group, pos) => {
|
|
6576
6793
|
setFadesMeta(
|
|
6577
6794
|
(prev) => prev.map(
|
|
@@ -6610,7 +6827,7 @@ function useTransitionOps({
|
|
|
6610
6827
|
},
|
|
6611
6828
|
[host, activeSceneId, applyFadeAutomation, setFadesMeta]
|
|
6612
6829
|
);
|
|
6613
|
-
const handleGroupFadeDelete =
|
|
6830
|
+
const handleGroupFadeDelete = useCallback16(
|
|
6614
6831
|
async (group) => {
|
|
6615
6832
|
const suffixes = ["fade", ...adapter.transitionGroup?.cleanupKeySuffixes ?? []];
|
|
6616
6833
|
try {
|
|
@@ -6638,7 +6855,7 @@ function useTransitionOps({
|
|
|
6638
6855
|
[host, adapter, activeSceneId, setFadesMeta, setTracks]
|
|
6639
6856
|
);
|
|
6640
6857
|
const lastResyncKeyRef = useRef18("");
|
|
6641
|
-
|
|
6858
|
+
useEffect18(() => {
|
|
6642
6859
|
if (!host.getTrackSound || resolvedCrossfadePairs.length === 0 && resolvedFades.length === 0) {
|
|
6643
6860
|
return;
|
|
6644
6861
|
}
|
|
@@ -6679,7 +6896,7 @@ function useTransitionOps({
|
|
|
6679
6896
|
cancelled = true;
|
|
6680
6897
|
};
|
|
6681
6898
|
}, [resolvedCrossfadePairs, resolvedFades, host, adapter]);
|
|
6682
|
-
|
|
6899
|
+
useEffect18(() => {
|
|
6683
6900
|
if (!host.setTrackVolumeAutomation || resolvedFades.length === 0) return;
|
|
6684
6901
|
void (async () => {
|
|
6685
6902
|
const mc = await host.getMusicalContext();
|
|
@@ -6717,7 +6934,7 @@ function useTransitionOps({
|
|
|
6717
6934
|
}
|
|
6718
6935
|
|
|
6719
6936
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
6720
|
-
import { jsx as
|
|
6937
|
+
import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
6721
6938
|
var EMPTY_PLACEHOLDERS = [];
|
|
6722
6939
|
function useGeneratorPanelCore({
|
|
6723
6940
|
ui,
|
|
@@ -6738,7 +6955,7 @@ function useGeneratorPanelCore({
|
|
|
6738
6955
|
const { identity, features } = adapter;
|
|
6739
6956
|
const logTag = identity.logTag;
|
|
6740
6957
|
const adapterRef = useRef19(adapter);
|
|
6741
|
-
|
|
6958
|
+
useEffect19(() => {
|
|
6742
6959
|
if (adapterRef.current !== adapter) {
|
|
6743
6960
|
adapterRef.current = adapter;
|
|
6744
6961
|
console.warn(
|
|
@@ -6748,15 +6965,15 @@ function useGeneratorPanelCore({
|
|
|
6748
6965
|
}, [adapter, logTag]);
|
|
6749
6966
|
const supportsMeters = typeof host.getTrackLevels === "function";
|
|
6750
6967
|
const trackLevels = useTrackLevels(host, isExpanded);
|
|
6751
|
-
const [tracks, setTracks] =
|
|
6752
|
-
const [isLoadingTracks, setIsLoadingTracks] =
|
|
6753
|
-
const [importOpen, setImportOpen] =
|
|
6754
|
-
const [soundImportTarget, setSoundImportTarget] =
|
|
6755
|
-
const [designerView, setDesignerView] =
|
|
6756
|
-
const [transitionSourceTotal, setTransitionSourceTotal] =
|
|
6757
|
-
const [crossfadePairsMeta, setCrossfadePairsMeta] =
|
|
6758
|
-
const [fadesMeta, setFadesMeta] =
|
|
6759
|
-
const [genericGroupMetas, setGenericGroupMetas] =
|
|
6968
|
+
const [tracks, setTracks] = useState24([]);
|
|
6969
|
+
const [isLoadingTracks, setIsLoadingTracks] = useState24(false);
|
|
6970
|
+
const [importOpen, setImportOpen] = useState24(false);
|
|
6971
|
+
const [soundImportTarget, setSoundImportTarget] = useState24(null);
|
|
6972
|
+
const [designerView, setDesignerView] = useState24(false);
|
|
6973
|
+
const [transitionSourceTotal, setTransitionSourceTotal] = useState24(0);
|
|
6974
|
+
const [crossfadePairsMeta, setCrossfadePairsMeta] = useState24([]);
|
|
6975
|
+
const [fadesMeta, setFadesMeta] = useState24([]);
|
|
6976
|
+
const [genericGroupMetas, setGenericGroupMetas] = useState24({});
|
|
6760
6977
|
const [isComposing, , setIsComposingForScene] = useSceneState(activeSceneId, false);
|
|
6761
6978
|
const [placeholders, , setPlaceholdersForScene] = useSceneState(
|
|
6762
6979
|
activeSceneId,
|
|
@@ -6764,11 +6981,11 @@ function useGeneratorPanelCore({
|
|
|
6764
6981
|
);
|
|
6765
6982
|
const saveTimeoutRefs = useRef19({});
|
|
6766
6983
|
const editLoadStartedRef = useRef19(/* @__PURE__ */ new Set());
|
|
6767
|
-
const [availableInstruments, setAvailableInstruments] =
|
|
6768
|
-
const [instrumentsLoading, setInstrumentsLoading] =
|
|
6984
|
+
const [availableInstruments, setAvailableInstruments] = useState24([]);
|
|
6985
|
+
const [instrumentsLoading, setInstrumentsLoading] = useState24(false);
|
|
6769
6986
|
const engineToDbIdRef = useRef19(/* @__PURE__ */ new Map());
|
|
6770
6987
|
const tracksLoadedForSceneRef = useRef19(null);
|
|
6771
|
-
const persistSoundHistory =
|
|
6988
|
+
const persistSoundHistory = useCallback17(
|
|
6772
6989
|
(trackId, state) => {
|
|
6773
6990
|
if (!activeSceneId) return;
|
|
6774
6991
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -6788,7 +7005,7 @@ function useGeneratorPanelCore({
|
|
|
6788
7005
|
setItems: setTracks,
|
|
6789
7006
|
getId: (t) => t.handle.dbId
|
|
6790
7007
|
});
|
|
6791
|
-
const loadTracks =
|
|
7008
|
+
const loadTracks = useCallback17(
|
|
6792
7009
|
async (incremental = false) => {
|
|
6793
7010
|
const sceneAtStart = activeSceneId;
|
|
6794
7011
|
if (!sceneAtStart) {
|
|
@@ -6913,10 +7130,10 @@ function useGeneratorPanelCore({
|
|
|
6913
7130
|
},
|
|
6914
7131
|
[host, activeSceneId, soundHistory, adapter, logTag]
|
|
6915
7132
|
);
|
|
6916
|
-
|
|
7133
|
+
useEffect19(() => {
|
|
6917
7134
|
loadTracks();
|
|
6918
7135
|
}, [loadTracks]);
|
|
6919
|
-
|
|
7136
|
+
useEffect19(() => {
|
|
6920
7137
|
const map = /* @__PURE__ */ new Map();
|
|
6921
7138
|
for (const t of tracks) {
|
|
6922
7139
|
map.set(t.handle.id, t.handle.dbId);
|
|
@@ -6924,7 +7141,7 @@ function useGeneratorPanelCore({
|
|
|
6924
7141
|
engineToDbIdRef.current = map;
|
|
6925
7142
|
}, [tracks]);
|
|
6926
7143
|
const loadedCompletedIdsRef = useRef19(/* @__PURE__ */ new Set());
|
|
6927
|
-
|
|
7144
|
+
useEffect19(() => {
|
|
6928
7145
|
if (placeholders.length === 0) {
|
|
6929
7146
|
loadedCompletedIdsRef.current.clear();
|
|
6930
7147
|
return;
|
|
@@ -6943,16 +7160,16 @@ function useGeneratorPanelCore({
|
|
|
6943
7160
|
loadTracks(true);
|
|
6944
7161
|
}
|
|
6945
7162
|
}, [placeholders, loadTracks, logTag]);
|
|
6946
|
-
const adoptAndLoad =
|
|
7163
|
+
const adoptAndLoad = useCallback17(() => {
|
|
6947
7164
|
loadTracks(true);
|
|
6948
7165
|
}, [loadTracks]);
|
|
6949
|
-
|
|
7166
|
+
useEffect19(() => {
|
|
6950
7167
|
const unsub = host.onEngineReady(() => {
|
|
6951
7168
|
adoptAndLoad();
|
|
6952
7169
|
});
|
|
6953
7170
|
return unsub;
|
|
6954
7171
|
}, [host, adoptAndLoad]);
|
|
6955
|
-
|
|
7172
|
+
useEffect19(() => {
|
|
6956
7173
|
if (typeof host.onAfterAgentMutation !== "function") return;
|
|
6957
7174
|
let timer = null;
|
|
6958
7175
|
const unsub = host.onAfterAgentMutation(() => {
|
|
@@ -6967,13 +7184,13 @@ function useGeneratorPanelCore({
|
|
|
6967
7184
|
if (timer) clearTimeout(timer);
|
|
6968
7185
|
};
|
|
6969
7186
|
}, [host, loadTracks]);
|
|
6970
|
-
|
|
7187
|
+
useEffect19(() => {
|
|
6971
7188
|
const unsub = host.onTrackStateChange((trackId, state) => {
|
|
6972
7189
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: state } : t));
|
|
6973
7190
|
});
|
|
6974
7191
|
return unsub;
|
|
6975
7192
|
}, [host]);
|
|
6976
|
-
|
|
7193
|
+
useEffect19(() => {
|
|
6977
7194
|
if (!features.bulkComposePlaceholders) return;
|
|
6978
7195
|
console.log(`[${logTag}] Subscribing to composeProgress`);
|
|
6979
7196
|
const unsub = host.onComposeProgress((event) => {
|
|
@@ -7007,7 +7224,7 @@ function useGeneratorPanelCore({
|
|
|
7007
7224
|
});
|
|
7008
7225
|
return unsub;
|
|
7009
7226
|
}, [host, setIsComposingForScene, setPlaceholdersForScene, features.bulkComposePlaceholders, logTag]);
|
|
7010
|
-
|
|
7227
|
+
useEffect19(() => {
|
|
7011
7228
|
const refs = saveTimeoutRefs;
|
|
7012
7229
|
return () => {
|
|
7013
7230
|
for (const timeout of Object.values(refs.current)) {
|
|
@@ -7016,8 +7233,8 @@ function useGeneratorPanelCore({
|
|
|
7016
7233
|
};
|
|
7017
7234
|
}, []);
|
|
7018
7235
|
const isAddingTrackRef = useRef19(false);
|
|
7019
|
-
const [isAddingTrack, setIsAddingTrack] =
|
|
7020
|
-
const handleAddTrack =
|
|
7236
|
+
const [isAddingTrack, setIsAddingTrack] = useState24(false);
|
|
7237
|
+
const handleAddTrack = useCallback17(async () => {
|
|
7021
7238
|
if (isAddingTrackRef.current) return;
|
|
7022
7239
|
if (!activeSceneId) {
|
|
7023
7240
|
host.showToast("warning", "Select SCENE");
|
|
@@ -7065,7 +7282,7 @@ function useGeneratorPanelCore({
|
|
|
7065
7282
|
setIsAddingTrack(false);
|
|
7066
7283
|
}
|
|
7067
7284
|
}, [host, adapter, identity, activeSceneId, isConnected, isAuthenticated, tracks.length, onExpandSelf, loadTracks, logTag]);
|
|
7068
|
-
const handlePortTrack =
|
|
7285
|
+
const handlePortTrack = useCallback17(
|
|
7069
7286
|
async (sel) => {
|
|
7070
7287
|
if (!activeSceneId) {
|
|
7071
7288
|
host.showToast("warning", "Select SCENE");
|
|
@@ -7128,39 +7345,8 @@ function useGeneratorPanelCore({
|
|
|
7128
7345
|
},
|
|
7129
7346
|
[host, adapter, identity, activeSceneId, isConnected, tracks.length, loadTracks]
|
|
7130
7347
|
);
|
|
7131
|
-
const
|
|
7132
|
-
|
|
7133
|
-
const target = soundImportTarget;
|
|
7134
|
-
if (!target || !host.getTrackSound) {
|
|
7135
|
-
setSoundImportTarget(null);
|
|
7136
|
-
return;
|
|
7137
|
-
}
|
|
7138
|
-
const noun = adapter.sound.importNoun;
|
|
7139
|
-
const nounTitle = noun.charAt(0).toUpperCase() + noun.slice(1);
|
|
7140
|
-
try {
|
|
7141
|
-
const snap = await host.getTrackSound(sel.sourceTrackDbId);
|
|
7142
|
-
if (!snap || snap.kind !== adapter.sound.acceptedSnapshotKind) {
|
|
7143
|
-
host.showToast(
|
|
7144
|
-
"error",
|
|
7145
|
-
`No ${noun} to import`,
|
|
7146
|
-
`${sel.trackName} has no ${identity.familyKey} ${noun}.`
|
|
7147
|
-
);
|
|
7148
|
-
return;
|
|
7149
|
-
}
|
|
7150
|
-
const descriptor = adapter.sound.descriptorFromSnapshot(snap);
|
|
7151
|
-
await adapter.sound.applySound(target.handle.id, descriptor);
|
|
7152
|
-
soundHistory.record(target.handle.id, descriptor, snap.label);
|
|
7153
|
-
host.showToast("success", `${nounTitle} imported`, `${snap.label} \u2192 ${target.handle.name}`);
|
|
7154
|
-
} catch (err) {
|
|
7155
|
-
host.showToast("error", "Import failed", err instanceof Error ? err.message : String(err));
|
|
7156
|
-
} finally {
|
|
7157
|
-
setSoundImportTarget(null);
|
|
7158
|
-
}
|
|
7159
|
-
},
|
|
7160
|
-
[soundImportTarget, host, adapter, identity.familyKey, soundHistory]
|
|
7161
|
-
);
|
|
7162
|
-
const [isExportingMidi, setIsExportingMidi] = useState23(false);
|
|
7163
|
-
const handleExportMidi = useCallback16(async () => {
|
|
7348
|
+
const [isExportingMidi, setIsExportingMidi] = useState24(false);
|
|
7349
|
+
const handleExportMidi = useCallback17(async () => {
|
|
7164
7350
|
if (isExportingMidi) return;
|
|
7165
7351
|
setIsExportingMidi(true);
|
|
7166
7352
|
try {
|
|
@@ -7191,10 +7377,10 @@ function useGeneratorPanelCore({
|
|
|
7191
7377
|
const xfFromId = sceneContext?.transitionFromSceneId ?? null;
|
|
7192
7378
|
const xfToId = sceneContext?.transitionToSceneId ?? null;
|
|
7193
7379
|
const canCrossfade = features.transitionDesigner && sceneContext?.sceneType === "transition" && !!xfFromId && !!xfToId && !!host.listSceneFamilyTracks;
|
|
7194
|
-
|
|
7380
|
+
useEffect19(() => {
|
|
7195
7381
|
if (!canCrossfade) setDesignerView(false);
|
|
7196
7382
|
}, [canCrossfade]);
|
|
7197
|
-
|
|
7383
|
+
useEffect19(() => {
|
|
7198
7384
|
if (!canCrossfade || !xfFromId || !xfToId || !host.listSceneFamilyTracks) {
|
|
7199
7385
|
setTransitionSourceTotal(0);
|
|
7200
7386
|
return;
|
|
@@ -7210,12 +7396,12 @@ function useGeneratorPanelCore({
|
|
|
7210
7396
|
};
|
|
7211
7397
|
}, [canCrossfade, xfFromId, xfToId, host]);
|
|
7212
7398
|
const transitionDone = crossfadePairsMeta.length * 2 + fadesMeta.length;
|
|
7213
|
-
|
|
7399
|
+
useEffect19(() => {
|
|
7214
7400
|
if (!onHeaderContent) return;
|
|
7215
7401
|
const addDisabled = needsContract || !isConnected || !activeSceneId || tracks.length >= identity.maxTracks || isAddingTrack;
|
|
7216
7402
|
onHeaderContent(
|
|
7217
|
-
/* @__PURE__ */
|
|
7218
|
-
features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */
|
|
7403
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex gap-1 items-center", children: [
|
|
7404
|
+
features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */ jsx28(
|
|
7219
7405
|
"button",
|
|
7220
7406
|
{
|
|
7221
7407
|
"data-testid": `import-from-scene-${identity.familyKey}-button`,
|
|
@@ -7229,7 +7415,7 @@ function useGeneratorPanelCore({
|
|
|
7229
7415
|
children: identity.importTrackLabel ?? "Import Track"
|
|
7230
7416
|
}
|
|
7231
7417
|
),
|
|
7232
|
-
(!canCrossfade || !designerView) && /* @__PURE__ */
|
|
7418
|
+
(!canCrossfade || !designerView) && /* @__PURE__ */ jsx28(
|
|
7233
7419
|
"button",
|
|
7234
7420
|
{
|
|
7235
7421
|
"data-testid": `add-${identity.familyKey}-track-button`,
|
|
@@ -7245,7 +7431,7 @@ function useGeneratorPanelCore({
|
|
|
7245
7431
|
children: identity.addTrackLabel ?? "Add Track"
|
|
7246
7432
|
}
|
|
7247
7433
|
),
|
|
7248
|
-
canCrossfade && /* @__PURE__ */
|
|
7434
|
+
canCrossfade && /* @__PURE__ */ jsxs21(
|
|
7249
7435
|
"button",
|
|
7250
7436
|
{
|
|
7251
7437
|
"data-testid": `${identity.familyKey}-view-toggle`,
|
|
@@ -7264,7 +7450,7 @@ function useGeneratorPanelCore({
|
|
|
7264
7450
|
title: designerView ? "Back to the track list" : "Open the transition designer",
|
|
7265
7451
|
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",
|
|
7266
7452
|
children: [
|
|
7267
|
-
transitionSourceTotal > 0 && /* @__PURE__ */
|
|
7453
|
+
transitionSourceTotal > 0 && /* @__PURE__ */ jsx28(
|
|
7268
7454
|
"span",
|
|
7269
7455
|
{
|
|
7270
7456
|
className: "absolute inset-y-0 left-0 bg-sas-accent/25",
|
|
@@ -7272,7 +7458,7 @@ function useGeneratorPanelCore({
|
|
|
7272
7458
|
"aria-hidden": true
|
|
7273
7459
|
}
|
|
7274
7460
|
),
|
|
7275
|
-
/* @__PURE__ */
|
|
7461
|
+
/* @__PURE__ */ jsxs21("span", { className: "relative", children: [
|
|
7276
7462
|
"\u21C4 ",
|
|
7277
7463
|
designerView ? "Transition" : "Tracks",
|
|
7278
7464
|
transitionSourceTotal > 0 ? ` ${transitionDone}/${transitionSourceTotal}` : ""
|
|
@@ -7303,7 +7489,7 @@ function useGeneratorPanelCore({
|
|
|
7303
7489
|
identity,
|
|
7304
7490
|
features.importTracks
|
|
7305
7491
|
]);
|
|
7306
|
-
|
|
7492
|
+
useEffect19(() => {
|
|
7307
7493
|
if (!onLoading) return;
|
|
7308
7494
|
const anyGenerating = tracks.some((t) => t.isGenerating);
|
|
7309
7495
|
onLoading(isLoadingTracks || anyGenerating || isBulkActive);
|
|
@@ -7311,7 +7497,7 @@ function useGeneratorPanelCore({
|
|
|
7311
7497
|
onLoading(false);
|
|
7312
7498
|
};
|
|
7313
7499
|
}, [onLoading, isLoadingTracks, tracks, isBulkActive]);
|
|
7314
|
-
const handleDeleteTrack =
|
|
7500
|
+
const handleDeleteTrack = useCallback17(
|
|
7315
7501
|
async (trackId) => {
|
|
7316
7502
|
try {
|
|
7317
7503
|
await host.deleteTrack(trackId);
|
|
@@ -7327,7 +7513,7 @@ function useGeneratorPanelCore({
|
|
|
7327
7513
|
},
|
|
7328
7514
|
[host, activeSceneId]
|
|
7329
7515
|
);
|
|
7330
|
-
const handlePromptChange =
|
|
7516
|
+
const handlePromptChange = useCallback17(
|
|
7331
7517
|
(trackId, prompt) => {
|
|
7332
7518
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, prompt } : t));
|
|
7333
7519
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -7364,11 +7550,11 @@ function useGeneratorPanelCore({
|
|
|
7364
7550
|
}
|
|
7365
7551
|
return s;
|
|
7366
7552
|
}, [resolvedGenericGroups]);
|
|
7367
|
-
const engineToDbId =
|
|
7553
|
+
const engineToDbId = useCallback17(
|
|
7368
7554
|
(trackId) => engineToDbIdRef.current.get(trackId) ?? trackId,
|
|
7369
7555
|
[]
|
|
7370
7556
|
);
|
|
7371
|
-
const updateTrack =
|
|
7557
|
+
const updateTrack = useCallback17(
|
|
7372
7558
|
(trackId, patch) => {
|
|
7373
7559
|
setTracks(
|
|
7374
7560
|
(prev) => prev.map(
|
|
@@ -7378,18 +7564,18 @@ function useGeneratorPanelCore({
|
|
|
7378
7564
|
},
|
|
7379
7565
|
[]
|
|
7380
7566
|
);
|
|
7381
|
-
const markEditLoaded =
|
|
7567
|
+
const markEditLoaded = useCallback17((trackId) => {
|
|
7382
7568
|
editLoadStartedRef.current.add(trackId);
|
|
7383
7569
|
}, []);
|
|
7384
7570
|
const tracksRef = useRef19(tracks);
|
|
7385
|
-
|
|
7571
|
+
useEffect19(() => {
|
|
7386
7572
|
tracksRef.current = tracks;
|
|
7387
7573
|
}, [tracks]);
|
|
7388
7574
|
const resolvedGenericGroupsRef = useRef19(resolvedGenericGroups);
|
|
7389
|
-
|
|
7575
|
+
useEffect19(() => {
|
|
7390
7576
|
resolvedGenericGroupsRef.current = resolvedGenericGroups;
|
|
7391
7577
|
}, [resolvedGenericGroups]);
|
|
7392
|
-
const makeServices =
|
|
7578
|
+
const makeServices = useCallback17(() => {
|
|
7393
7579
|
return {
|
|
7394
7580
|
host,
|
|
7395
7581
|
activeSceneId,
|
|
@@ -7405,10 +7591,164 @@ function useGeneratorPanelCore({
|
|
|
7405
7591
|
name: `${identity.trackNamePrefix}-${Date.now()}${nameSuffix}`,
|
|
7406
7592
|
...adapter.createTrackOptions()
|
|
7407
7593
|
}),
|
|
7408
|
-
resolvedGroups: (metaKey) => resolvedGenericGroupsRef.current[metaKey]?.resolved ?? []
|
|
7594
|
+
resolvedGroups: (metaKey) => resolvedGenericGroupsRef.current[metaKey]?.resolved ?? [],
|
|
7595
|
+
sound: adapter.sound
|
|
7409
7596
|
};
|
|
7410
7597
|
}, [host, activeSceneId, updateTrack, loadTracks, soundHistory, engineToDbId, markEditLoaded, identity, adapter]);
|
|
7411
|
-
const
|
|
7598
|
+
const broadcastSoundFromTrack = useCallback17(
|
|
7599
|
+
async (sourceTrackId, descriptor, label) => {
|
|
7600
|
+
const targetsOf = adapter.sound.broadcastTargets;
|
|
7601
|
+
if (!targetsOf) return;
|
|
7602
|
+
const source = tracksRef.current.find((t) => t.handle.id === sourceTrackId);
|
|
7603
|
+
if (!source) return;
|
|
7604
|
+
let targets;
|
|
7605
|
+
try {
|
|
7606
|
+
targets = await targetsOf(source, makeServices());
|
|
7607
|
+
} catch (err) {
|
|
7608
|
+
console.warn(`[${logTag}] broadcastTargets failed:`, err);
|
|
7609
|
+
return;
|
|
7610
|
+
}
|
|
7611
|
+
const sourceInstrument = source.instrumentPluginId ?? null;
|
|
7612
|
+
const others = (targets ?? []).filter((t) => {
|
|
7613
|
+
if (t.engineId === sourceTrackId) return false;
|
|
7614
|
+
const live = tracksRef.current.find((x) => x.handle.id === t.engineId);
|
|
7615
|
+
return (live?.instrumentPluginId ?? null) === sourceInstrument;
|
|
7616
|
+
});
|
|
7617
|
+
if (others.length === 0) return;
|
|
7618
|
+
try {
|
|
7619
|
+
await adapter.sound.persistDescriptor?.(sourceTrackId, descriptor, label);
|
|
7620
|
+
} catch {
|
|
7621
|
+
}
|
|
7622
|
+
const appliedIds = /* @__PURE__ */ new Set();
|
|
7623
|
+
const failed = [];
|
|
7624
|
+
for (const target of others) {
|
|
7625
|
+
try {
|
|
7626
|
+
if (soundHistory.list(target.engineId).entries.length === 0) {
|
|
7627
|
+
try {
|
|
7628
|
+
const cap = await adapter.sound.captureSoundDescriptor(target.engineId);
|
|
7629
|
+
if (cap) {
|
|
7630
|
+
soundHistory.record(target.engineId, cap.descriptor, adapter.sound.previousSoundLabel);
|
|
7631
|
+
}
|
|
7632
|
+
} catch {
|
|
7633
|
+
}
|
|
7634
|
+
}
|
|
7635
|
+
await adapter.sound.applySound(target.engineId, descriptor);
|
|
7636
|
+
try {
|
|
7637
|
+
await adapter.sound.persistDescriptor?.(target.engineId, descriptor, label);
|
|
7638
|
+
} catch {
|
|
7639
|
+
}
|
|
7640
|
+
soundHistory.record(target.engineId, descriptor, label);
|
|
7641
|
+
appliedIds.add(target.engineId);
|
|
7642
|
+
} catch (err) {
|
|
7643
|
+
failed.push(target.label ?? target.engineId);
|
|
7644
|
+
console.warn(`[${logTag}] Linked sound apply failed for ${target.engineId}:`, err);
|
|
7645
|
+
}
|
|
7646
|
+
}
|
|
7647
|
+
if (appliedIds.size > 0) {
|
|
7648
|
+
setTracks(
|
|
7649
|
+
(prev) => prev.map(
|
|
7650
|
+
(t) => appliedIds.has(t.handle.id) ? { ...t, shuffleHistory: /* @__PURE__ */ new Set([...t.shuffleHistory, label]) } : t
|
|
7651
|
+
)
|
|
7652
|
+
);
|
|
7653
|
+
}
|
|
7654
|
+
if (failed.length > 0) {
|
|
7655
|
+
host.showToast(
|
|
7656
|
+
"warning",
|
|
7657
|
+
"Linked sound applied to some parts only",
|
|
7658
|
+
`Skipped: ${failed.join(", ")}`
|
|
7659
|
+
);
|
|
7660
|
+
}
|
|
7661
|
+
},
|
|
7662
|
+
[adapter, makeServices, soundHistory, host, logTag]
|
|
7663
|
+
);
|
|
7664
|
+
const broadcastInstrumentFromTrack = useCallback17(
|
|
7665
|
+
async (sourceTrackId, pluginId) => {
|
|
7666
|
+
const targetsOf = adapter.sound.broadcastTargets;
|
|
7667
|
+
if (!targetsOf) return;
|
|
7668
|
+
const source = tracksRef.current.find((t) => t.handle.id === sourceTrackId);
|
|
7669
|
+
if (!source) return;
|
|
7670
|
+
let targets;
|
|
7671
|
+
try {
|
|
7672
|
+
targets = await targetsOf(source, makeServices());
|
|
7673
|
+
} catch (err) {
|
|
7674
|
+
console.warn(`[${logTag}] broadcastTargets failed:`, err);
|
|
7675
|
+
return;
|
|
7676
|
+
}
|
|
7677
|
+
const others = (targets ?? []).filter((t) => t.engineId !== sourceTrackId);
|
|
7678
|
+
if (others.length === 0) return;
|
|
7679
|
+
const failed = [];
|
|
7680
|
+
for (const target of others) {
|
|
7681
|
+
try {
|
|
7682
|
+
await host.setTrackInstrument(target.engineId, pluginId);
|
|
7683
|
+
const descriptor = await host.getTrackInstrument(target.engineId);
|
|
7684
|
+
setTracks(
|
|
7685
|
+
(prev) => prev.map(
|
|
7686
|
+
(t) => t.handle.id === target.engineId ? {
|
|
7687
|
+
...t,
|
|
7688
|
+
instrumentPluginId: descriptor?.pluginId ?? null,
|
|
7689
|
+
instrumentName: descriptor?.name ?? null,
|
|
7690
|
+
instrumentMissing: descriptor?.missing ?? false
|
|
7691
|
+
} : t
|
|
7692
|
+
)
|
|
7693
|
+
);
|
|
7694
|
+
} catch (err) {
|
|
7695
|
+
failed.push(target.label ?? target.engineId);
|
|
7696
|
+
console.warn(`[${logTag}] Linked instrument apply failed for ${target.engineId}:`, err);
|
|
7697
|
+
}
|
|
7698
|
+
}
|
|
7699
|
+
if (failed.length > 0) {
|
|
7700
|
+
host.showToast(
|
|
7701
|
+
"warning",
|
|
7702
|
+
"Instrument applied to some parts only",
|
|
7703
|
+
`Skipped: ${failed.join(", ")}`
|
|
7704
|
+
);
|
|
7705
|
+
}
|
|
7706
|
+
},
|
|
7707
|
+
[adapter, makeServices, host, logTag]
|
|
7708
|
+
);
|
|
7709
|
+
const handleRestoreSound = useCallback17(
|
|
7710
|
+
async (trackId, index) => {
|
|
7711
|
+
const entry = soundHistory.list(trackId).entries[index];
|
|
7712
|
+
const moved = await soundHistory.restoreTo(trackId, index);
|
|
7713
|
+
if (moved && entry) {
|
|
7714
|
+
await broadcastSoundFromTrack(trackId, entry.descriptor, entry.label);
|
|
7715
|
+
}
|
|
7716
|
+
},
|
|
7717
|
+
[soundHistory, broadcastSoundFromTrack]
|
|
7718
|
+
);
|
|
7719
|
+
const handleSoundImportPick = useCallback17(
|
|
7720
|
+
async (sel) => {
|
|
7721
|
+
const target = soundImportTarget;
|
|
7722
|
+
if (!target || !host.getTrackSound) {
|
|
7723
|
+
setSoundImportTarget(null);
|
|
7724
|
+
return;
|
|
7725
|
+
}
|
|
7726
|
+
const noun = adapter.sound.importNoun;
|
|
7727
|
+
const nounTitle = noun.charAt(0).toUpperCase() + noun.slice(1);
|
|
7728
|
+
try {
|
|
7729
|
+
const snap = await host.getTrackSound(sel.sourceTrackDbId);
|
|
7730
|
+
if (!snap || snap.kind !== adapter.sound.acceptedSnapshotKind) {
|
|
7731
|
+
host.showToast(
|
|
7732
|
+
"error",
|
|
7733
|
+
`No ${noun} to import`,
|
|
7734
|
+
`${sel.trackName} has no ${identity.familyKey} ${noun}.`
|
|
7735
|
+
);
|
|
7736
|
+
return;
|
|
7737
|
+
}
|
|
7738
|
+
const descriptor = adapter.sound.descriptorFromSnapshot(snap);
|
|
7739
|
+
await adapter.sound.applySound(target.handle.id, descriptor);
|
|
7740
|
+
soundHistory.record(target.handle.id, descriptor, snap.label);
|
|
7741
|
+
host.showToast("success", `${nounTitle} imported`, `${snap.label} \u2192 ${target.handle.name}`);
|
|
7742
|
+
await broadcastSoundFromTrack(target.handle.id, descriptor, snap.label);
|
|
7743
|
+
} catch (err) {
|
|
7744
|
+
host.showToast("error", "Import failed", err instanceof Error ? err.message : String(err));
|
|
7745
|
+
} finally {
|
|
7746
|
+
setSoundImportTarget(null);
|
|
7747
|
+
}
|
|
7748
|
+
},
|
|
7749
|
+
[soundImportTarget, host, adapter, identity.familyKey, soundHistory, broadcastSoundFromTrack]
|
|
7750
|
+
);
|
|
7751
|
+
const handleGenerate = useCallback17(
|
|
7412
7752
|
async (trackId) => {
|
|
7413
7753
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
7414
7754
|
if (!track || !track.prompt.trim()) return;
|
|
@@ -7435,7 +7775,7 @@ function useGeneratorPanelCore({
|
|
|
7435
7775
|
},
|
|
7436
7776
|
[host, adapter, tracks, isAuthenticated, makeServices]
|
|
7437
7777
|
);
|
|
7438
|
-
const handleMuteToggle =
|
|
7778
|
+
const handleMuteToggle = useCallback17(
|
|
7439
7779
|
(trackId) => {
|
|
7440
7780
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
7441
7781
|
if (!track) return;
|
|
@@ -7455,7 +7795,7 @@ function useGeneratorPanelCore({
|
|
|
7455
7795
|
},
|
|
7456
7796
|
[host, tracks]
|
|
7457
7797
|
);
|
|
7458
|
-
const handleSoloToggle =
|
|
7798
|
+
const handleSoloToggle = useCallback17(
|
|
7459
7799
|
(trackId) => {
|
|
7460
7800
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
7461
7801
|
if (!track) return;
|
|
@@ -7475,7 +7815,7 @@ function useGeneratorPanelCore({
|
|
|
7475
7815
|
},
|
|
7476
7816
|
[host, tracks]
|
|
7477
7817
|
);
|
|
7478
|
-
const handleVolumeChange =
|
|
7818
|
+
const handleVolumeChange = useCallback17(
|
|
7479
7819
|
(trackId, volume) => {
|
|
7480
7820
|
setTracks(
|
|
7481
7821
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, volume } } : t)
|
|
@@ -7485,7 +7825,7 @@ function useGeneratorPanelCore({
|
|
|
7485
7825
|
},
|
|
7486
7826
|
[host]
|
|
7487
7827
|
);
|
|
7488
|
-
const handlePanChange =
|
|
7828
|
+
const handlePanChange = useCallback17(
|
|
7489
7829
|
(trackId, pan) => {
|
|
7490
7830
|
setTracks(
|
|
7491
7831
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, pan } } : t)
|
|
@@ -7495,7 +7835,7 @@ function useGeneratorPanelCore({
|
|
|
7495
7835
|
},
|
|
7496
7836
|
[host]
|
|
7497
7837
|
);
|
|
7498
|
-
const handleShuffle =
|
|
7838
|
+
const handleShuffle = useCallback17(
|
|
7499
7839
|
async (trackId) => {
|
|
7500
7840
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
7501
7841
|
if (!track) return;
|
|
@@ -7526,7 +7866,10 @@ function useGeneratorPanelCore({
|
|
|
7526
7866
|
);
|
|
7527
7867
|
try {
|
|
7528
7868
|
const cap = await adapter.sound.captureSoundDescriptor(trackId);
|
|
7529
|
-
if (cap)
|
|
7869
|
+
if (cap) {
|
|
7870
|
+
soundHistory.record(trackId, cap.descriptor, result.appliedName);
|
|
7871
|
+
await broadcastSoundFromTrack(trackId, cap.descriptor, result.appliedName);
|
|
7872
|
+
}
|
|
7530
7873
|
} catch {
|
|
7531
7874
|
}
|
|
7532
7875
|
console.log(`[${logTag}] Sound shuffled: ${result.appliedName} (history ${nextHistory.size})`);
|
|
@@ -7535,9 +7878,9 @@ function useGeneratorPanelCore({
|
|
|
7535
7878
|
host.showToast("error", "Shuffle failed", msg);
|
|
7536
7879
|
}
|
|
7537
7880
|
},
|
|
7538
|
-
[host, adapter, tracks, soundHistory, logTag]
|
|
7881
|
+
[host, adapter, tracks, soundHistory, logTag, broadcastSoundFromTrack]
|
|
7539
7882
|
);
|
|
7540
|
-
const handleCopy =
|
|
7883
|
+
const handleCopy = useCallback17(
|
|
7541
7884
|
async (trackId) => {
|
|
7542
7885
|
try {
|
|
7543
7886
|
const newHandle = await host.duplicateTrack(trackId);
|
|
@@ -7550,7 +7893,7 @@ function useGeneratorPanelCore({
|
|
|
7550
7893
|
},
|
|
7551
7894
|
[host, loadTracks]
|
|
7552
7895
|
);
|
|
7553
|
-
const handleFxToggle =
|
|
7896
|
+
const handleFxToggle = useCallback17(
|
|
7554
7897
|
(trackId, category, enabled) => {
|
|
7555
7898
|
setTracks(
|
|
7556
7899
|
(prev) => prev.map(
|
|
@@ -7573,7 +7916,7 @@ function useGeneratorPanelCore({
|
|
|
7573
7916
|
},
|
|
7574
7917
|
[host]
|
|
7575
7918
|
);
|
|
7576
|
-
const handleFxPresetChange =
|
|
7919
|
+
const handleFxPresetChange = useCallback17(
|
|
7577
7920
|
(trackId, category, presetIndex) => {
|
|
7578
7921
|
setTracks(
|
|
7579
7922
|
(prev) => prev.map(
|
|
@@ -7599,7 +7942,7 @@ function useGeneratorPanelCore({
|
|
|
7599
7942
|
},
|
|
7600
7943
|
[host]
|
|
7601
7944
|
);
|
|
7602
|
-
const handleFxDryWetChange =
|
|
7945
|
+
const handleFxDryWetChange = useCallback17(
|
|
7603
7946
|
(trackId, category, value) => {
|
|
7604
7947
|
setTracks(
|
|
7605
7948
|
(prev) => prev.map(
|
|
@@ -7611,7 +7954,7 @@ function useGeneratorPanelCore({
|
|
|
7611
7954
|
},
|
|
7612
7955
|
[host]
|
|
7613
7956
|
);
|
|
7614
|
-
const toggleFxDrawer =
|
|
7957
|
+
const toggleFxDrawer = useCallback17(
|
|
7615
7958
|
(trackId) => {
|
|
7616
7959
|
setTracks(
|
|
7617
7960
|
(prev) => prev.map((t) => {
|
|
@@ -7633,7 +7976,7 @@ function useGeneratorPanelCore({
|
|
|
7633
7976
|
},
|
|
7634
7977
|
[host, tracks]
|
|
7635
7978
|
);
|
|
7636
|
-
const loadEditNotes =
|
|
7979
|
+
const loadEditNotes = useCallback17(
|
|
7637
7980
|
async (trackId) => {
|
|
7638
7981
|
try {
|
|
7639
7982
|
const mc = await host.getMusicalContext();
|
|
@@ -7651,7 +7994,7 @@ function useGeneratorPanelCore({
|
|
|
7651
7994
|
},
|
|
7652
7995
|
[host, logTag]
|
|
7653
7996
|
);
|
|
7654
|
-
const handleNotesChange =
|
|
7997
|
+
const handleNotesChange = useCallback17(
|
|
7655
7998
|
(trackId, notes) => {
|
|
7656
7999
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editNotes: notes } : t));
|
|
7657
8000
|
const key = `edit:${trackId}`;
|
|
@@ -7681,7 +8024,7 @@ function useGeneratorPanelCore({
|
|
|
7681
8024
|
},
|
|
7682
8025
|
[host]
|
|
7683
8026
|
);
|
|
7684
|
-
const handleTabChange =
|
|
8027
|
+
const handleTabChange = useCallback17(
|
|
7685
8028
|
(trackId, tab) => {
|
|
7686
8029
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, drawerOpen: true, drawerTab: tab } : t));
|
|
7687
8030
|
if (tab === "fx") {
|
|
@@ -7706,10 +8049,10 @@ function useGeneratorPanelCore({
|
|
|
7706
8049
|
},
|
|
7707
8050
|
[host, availableInstruments.length, instrumentsLoading, loadEditNotes]
|
|
7708
8051
|
);
|
|
7709
|
-
const handleProgressChange =
|
|
8052
|
+
const handleProgressChange = useCallback17((trackId, pct) => {
|
|
7710
8053
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, generationProgress: pct } : t));
|
|
7711
8054
|
}, []);
|
|
7712
|
-
const handleToggleDrawer =
|
|
8055
|
+
const handleToggleDrawer = useCallback17((trackId) => {
|
|
7713
8056
|
setTracks(
|
|
7714
8057
|
(prev) => prev.map((t) => {
|
|
7715
8058
|
if (t.handle.id !== trackId) return t;
|
|
@@ -7718,7 +8061,7 @@ function useGeneratorPanelCore({
|
|
|
7718
8061
|
})
|
|
7719
8062
|
);
|
|
7720
8063
|
}, []);
|
|
7721
|
-
const handleInstrumentSelect =
|
|
8064
|
+
const handleInstrumentSelect = useCallback17(
|
|
7722
8065
|
async (trackId, pluginId) => {
|
|
7723
8066
|
const isDefaultInstrument = pluginId === (identity.defaultInstrumentPluginId ?? "Surge XT");
|
|
7724
8067
|
if (isDefaultInstrument) {
|
|
@@ -7738,6 +8081,7 @@ function useGeneratorPanelCore({
|
|
|
7738
8081
|
} : t
|
|
7739
8082
|
)
|
|
7740
8083
|
);
|
|
8084
|
+
await broadcastInstrumentFromTrack(trackId, pluginId);
|
|
7741
8085
|
} catch (err) {
|
|
7742
8086
|
const msg = err instanceof Error ? err.message : "Failed to load instrument";
|
|
7743
8087
|
host.showToast("error", "Instrument load failed", msg);
|
|
@@ -7760,6 +8104,7 @@ function useGeneratorPanelCore({
|
|
|
7760
8104
|
} : t
|
|
7761
8105
|
)
|
|
7762
8106
|
);
|
|
8107
|
+
await broadcastInstrumentFromTrack(trackId, pluginId);
|
|
7763
8108
|
} catch (err) {
|
|
7764
8109
|
const msg = err instanceof Error ? err.message : "Failed to load instrument";
|
|
7765
8110
|
console.error(`[${logTag}] Failed to set instrument:`, err);
|
|
@@ -7769,9 +8114,9 @@ function useGeneratorPanelCore({
|
|
|
7769
8114
|
);
|
|
7770
8115
|
}
|
|
7771
8116
|
},
|
|
7772
|
-
[host, identity.defaultInstrumentPluginId, logTag]
|
|
8117
|
+
[host, identity.defaultInstrumentPluginId, logTag, broadcastInstrumentFromTrack]
|
|
7773
8118
|
);
|
|
7774
|
-
const handleShowEditor =
|
|
8119
|
+
const handleShowEditor = useCallback17(
|
|
7775
8120
|
async (trackId) => {
|
|
7776
8121
|
try {
|
|
7777
8122
|
await host.showInstrumentEditor(trackId);
|
|
@@ -7782,12 +8127,12 @@ function useGeneratorPanelCore({
|
|
|
7782
8127
|
},
|
|
7783
8128
|
[host]
|
|
7784
8129
|
);
|
|
7785
|
-
const handleBackToInstruments =
|
|
8130
|
+
const handleBackToInstruments = useCallback17((trackId) => {
|
|
7786
8131
|
setTracks(
|
|
7787
8132
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editorStage: false } : t)
|
|
7788
8133
|
);
|
|
7789
8134
|
}, []);
|
|
7790
|
-
const handleRefreshInstruments =
|
|
8135
|
+
const handleRefreshInstruments = useCallback17(() => {
|
|
7791
8136
|
setInstrumentsLoading(true);
|
|
7792
8137
|
host.getAvailableInstruments().then((instruments) => {
|
|
7793
8138
|
setAvailableInstruments(instruments);
|
|
@@ -7796,7 +8141,7 @@ function useGeneratorPanelCore({
|
|
|
7796
8141
|
setInstrumentsLoading(false);
|
|
7797
8142
|
});
|
|
7798
8143
|
}, [host]);
|
|
7799
|
-
const onAuditionNote =
|
|
8144
|
+
const onAuditionNote = useCallback17(
|
|
7800
8145
|
(trackId, pitch, velocity, ms) => {
|
|
7801
8146
|
void host.auditionNote(trackId, pitch, velocity, ms);
|
|
7802
8147
|
},
|
|
@@ -7849,7 +8194,7 @@ function useGeneratorPanelCore({
|
|
|
7849
8194
|
resolvedCrossfadePairs,
|
|
7850
8195
|
resolvedFades
|
|
7851
8196
|
});
|
|
7852
|
-
const setGroupMute =
|
|
8197
|
+
const setGroupMute = useCallback17(
|
|
7853
8198
|
(trackIds, muted) => {
|
|
7854
8199
|
for (const id of trackIds) {
|
|
7855
8200
|
setTracks(
|
|
@@ -7861,7 +8206,7 @@ function useGeneratorPanelCore({
|
|
|
7861
8206
|
},
|
|
7862
8207
|
[host]
|
|
7863
8208
|
);
|
|
7864
|
-
const setGroupSolo =
|
|
8209
|
+
const setGroupSolo = useCallback17(
|
|
7865
8210
|
(trackIds, solo) => {
|
|
7866
8211
|
for (const id of trackIds) {
|
|
7867
8212
|
setTracks(
|
|
@@ -7873,7 +8218,7 @@ function useGeneratorPanelCore({
|
|
|
7873
8218
|
},
|
|
7874
8219
|
[host]
|
|
7875
8220
|
);
|
|
7876
|
-
const deleteGroup =
|
|
8221
|
+
const deleteGroup = useCallback17(
|
|
7877
8222
|
async (members, cleanupKeySuffixes) => {
|
|
7878
8223
|
for (const member of members) {
|
|
7879
8224
|
try {
|
|
@@ -7979,6 +8324,7 @@ function useGeneratorPanelCore({
|
|
|
7979
8324
|
handlers,
|
|
7980
8325
|
handleGenerate,
|
|
7981
8326
|
handleShuffle,
|
|
8327
|
+
handleRestoreSound,
|
|
7982
8328
|
handleAddTrack,
|
|
7983
8329
|
handleDeleteTrack,
|
|
7984
8330
|
handleExportMidi,
|
|
@@ -8009,8 +8355,41 @@ function useGeneratorPanelCore({
|
|
|
8009
8355
|
}
|
|
8010
8356
|
|
|
8011
8357
|
// src/panel-core/GeneratorPanelShell.tsx
|
|
8012
|
-
import
|
|
8013
|
-
import { Fragment as Fragment7, jsx as
|
|
8358
|
+
import { useCallback as useCallback18, useEffect as useEffect20, useState as useState25 } from "react";
|
|
8359
|
+
import { Fragment as Fragment7, jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
8360
|
+
var GROUP_UI_KEY = "groupUi";
|
|
8361
|
+
function CollapsibleGroup({
|
|
8362
|
+
ext,
|
|
8363
|
+
group,
|
|
8364
|
+
groupCtx
|
|
8365
|
+
}) {
|
|
8366
|
+
const { host, activeSceneId, trackDataKey: trackDataKey2 } = groupCtx.services;
|
|
8367
|
+
const uiKey = trackDataKey2(group.groupId, GROUP_UI_KEY);
|
|
8368
|
+
const [collapsed, setCollapsed] = useState25(false);
|
|
8369
|
+
useEffect20(() => {
|
|
8370
|
+
let cancelled = false;
|
|
8371
|
+
if (!activeSceneId) return void 0;
|
|
8372
|
+
void host.getSceneData(activeSceneId, uiKey).then((raw) => {
|
|
8373
|
+
if (cancelled || !raw || typeof raw !== "object") return;
|
|
8374
|
+
setCollapsed(raw.collapsed === true);
|
|
8375
|
+
}).catch(() => {
|
|
8376
|
+
});
|
|
8377
|
+
return () => {
|
|
8378
|
+
cancelled = true;
|
|
8379
|
+
};
|
|
8380
|
+
}, [host, activeSceneId, uiKey]);
|
|
8381
|
+
const onToggleCollapse = useCallback18(() => {
|
|
8382
|
+
setCollapsed((prev) => {
|
|
8383
|
+
const next = !prev;
|
|
8384
|
+
if (activeSceneId) {
|
|
8385
|
+
void host.setSceneData(activeSceneId, uiKey, { collapsed: next }).catch(() => {
|
|
8386
|
+
});
|
|
8387
|
+
}
|
|
8388
|
+
return next;
|
|
8389
|
+
});
|
|
8390
|
+
}, [host, activeSceneId, uiKey]);
|
|
8391
|
+
return /* @__PURE__ */ jsx29(Fragment7, { children: ext.renderGroup(group, { ...groupCtx, collapsed, onToggleCollapse }) });
|
|
8392
|
+
}
|
|
8014
8393
|
function GeneratorPanelShell({ core, slots }) {
|
|
8015
8394
|
const {
|
|
8016
8395
|
ui,
|
|
@@ -8033,6 +8412,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8033
8412
|
soundImportTarget,
|
|
8034
8413
|
setSoundImportTarget,
|
|
8035
8414
|
handleSoundImportPick,
|
|
8415
|
+
handleRestoreSound,
|
|
8036
8416
|
handlePortTrack,
|
|
8037
8417
|
transition,
|
|
8038
8418
|
crossfadePairsMeta,
|
|
@@ -8066,7 +8446,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8066
8446
|
const { host, activeSceneId, isAuthenticated, sceneContext, onSelectScene, onOpenContract } = ui;
|
|
8067
8447
|
const panelBus = usePanelBus(host, activeSceneId);
|
|
8068
8448
|
const { identity, features } = adapter;
|
|
8069
|
-
const buildRowProps =
|
|
8449
|
+
const buildRowProps = useCallback18(
|
|
8070
8450
|
(track, drag) => {
|
|
8071
8451
|
const id = track.handle.id;
|
|
8072
8452
|
const pickerProps = features.instrumentPicker ? {
|
|
@@ -8128,7 +8508,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8128
8508
|
soundHistory: soundHistory.list(id).entries,
|
|
8129
8509
|
soundHistoryCursor: soundHistory.list(id).cursor,
|
|
8130
8510
|
onRestoreSound: (i) => {
|
|
8131
|
-
void
|
|
8511
|
+
void handleRestoreSound(id, i);
|
|
8132
8512
|
},
|
|
8133
8513
|
onToggleFavorite: (i) => soundHistory.toggleFavorite(id, i),
|
|
8134
8514
|
...importSoundProps,
|
|
@@ -8159,6 +8539,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8159
8539
|
handleBackToInstruments,
|
|
8160
8540
|
setSoundImportTarget,
|
|
8161
8541
|
soundHistory,
|
|
8542
|
+
handleRestoreSound,
|
|
8162
8543
|
handleFxToggle,
|
|
8163
8544
|
handleFxPresetChange,
|
|
8164
8545
|
handleFxDryWetChange,
|
|
@@ -8166,12 +8547,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8166
8547
|
]
|
|
8167
8548
|
);
|
|
8168
8549
|
if (!activeSceneId) {
|
|
8169
|
-
return /* @__PURE__ */
|
|
8550
|
+
return /* @__PURE__ */ jsx29(
|
|
8170
8551
|
"div",
|
|
8171
8552
|
{
|
|
8172
8553
|
"data-testid": `no-scene-placeholder-${identity.familyKey}`,
|
|
8173
8554
|
className: "flex items-center justify-center py-8",
|
|
8174
|
-
children: /* @__PURE__ */
|
|
8555
|
+
children: /* @__PURE__ */ jsx29(
|
|
8175
8556
|
"button",
|
|
8176
8557
|
{
|
|
8177
8558
|
onClick: () => onSelectScene?.(),
|
|
@@ -8183,12 +8564,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8183
8564
|
);
|
|
8184
8565
|
}
|
|
8185
8566
|
if (!sceneContext?.hasContract) {
|
|
8186
|
-
return /* @__PURE__ */
|
|
8567
|
+
return /* @__PURE__ */ jsx29(
|
|
8187
8568
|
"div",
|
|
8188
8569
|
{
|
|
8189
8570
|
"data-testid": `no-contract-placeholder-${identity.familyKey}`,
|
|
8190
8571
|
className: "flex items-center justify-center py-8",
|
|
8191
|
-
children: /* @__PURE__ */
|
|
8572
|
+
children: /* @__PURE__ */ jsx29(
|
|
8192
8573
|
"button",
|
|
8193
8574
|
{
|
|
8194
8575
|
onClick: () => onOpenContract?.(),
|
|
@@ -8200,7 +8581,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8200
8581
|
);
|
|
8201
8582
|
}
|
|
8202
8583
|
if (features.bulkComposePlaceholders && isComposing) {
|
|
8203
|
-
return /* @__PURE__ */
|
|
8584
|
+
return /* @__PURE__ */ jsx29("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2", children: /* @__PURE__ */ jsx29(SorceryProgressBar, { isLoading: true, statusText: "COMPOSING...", heightClass: "h-10" }) });
|
|
8204
8585
|
}
|
|
8205
8586
|
const activePlaceholders = features.bulkComposePlaceholders ? placeholders : [];
|
|
8206
8587
|
if (activePlaceholders.length > 0) {
|
|
@@ -8211,18 +8592,18 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8211
8592
|
tracksByDbId.set(t.handle.id, t);
|
|
8212
8593
|
}
|
|
8213
8594
|
}
|
|
8214
|
-
return /* @__PURE__ */
|
|
8595
|
+
return /* @__PURE__ */ jsx29("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: activePlaceholders.map((ph) => {
|
|
8215
8596
|
const loadedTrack = ph.status === "completed" ? tracksByDbId.get(ph.id) : void 0;
|
|
8216
8597
|
if (loadedTrack) {
|
|
8217
|
-
return /* @__PURE__ */
|
|
8598
|
+
return /* @__PURE__ */ jsx29(TrackRow, { ...buildRowProps(loadedTrack) }, ph.id);
|
|
8218
8599
|
}
|
|
8219
|
-
return /* @__PURE__ */
|
|
8600
|
+
return /* @__PURE__ */ jsx29(
|
|
8220
8601
|
"div",
|
|
8221
8602
|
{
|
|
8222
8603
|
"data-testid": "bulk-placeholder-track",
|
|
8223
8604
|
className: "relative rounded-sm border w-full overflow-hidden border-sas-border bg-sas-panel-alt",
|
|
8224
8605
|
style: { borderLeftColor: identity.placeholderAccentColor, borderLeftWidth: "3px" },
|
|
8225
|
-
children: /* @__PURE__ */
|
|
8606
|
+
children: /* @__PURE__ */ jsx29(SorceryProgressBar, { isLoading: true, statusText: "CONJURING MIDI...", heightClass: "h-10" })
|
|
8226
8607
|
},
|
|
8227
8608
|
ph.id
|
|
8228
8609
|
);
|
|
@@ -8234,13 +8615,13 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8234
8615
|
supportsMeters,
|
|
8235
8616
|
levels: supportsMeters ? trackLevels : void 0,
|
|
8236
8617
|
handlers,
|
|
8237
|
-
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */
|
|
8618
|
+
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */ jsx29(TrackRow, { ...{ ...buildRowProps(track, drag), ...overrides ?? {} } }, track.handle.id),
|
|
8238
8619
|
setGroupMute,
|
|
8239
8620
|
setGroupSolo,
|
|
8240
8621
|
deleteGroup
|
|
8241
8622
|
};
|
|
8242
|
-
return /* @__PURE__ */
|
|
8243
|
-
features.importTracks && host.listImportableTracks && /* @__PURE__ */
|
|
8623
|
+
return /* @__PURE__ */ jsxs22("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: [
|
|
8624
|
+
features.importTracks && host.listImportableTracks && /* @__PURE__ */ jsx29(
|
|
8244
8625
|
ImportTrackModal,
|
|
8245
8626
|
{
|
|
8246
8627
|
host,
|
|
@@ -8253,7 +8634,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8253
8634
|
testIdPrefix: `${identity.familyKey}-import`
|
|
8254
8635
|
}
|
|
8255
8636
|
),
|
|
8256
|
-
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */
|
|
8637
|
+
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ jsx29(
|
|
8257
8638
|
ImportTrackModal,
|
|
8258
8639
|
{
|
|
8259
8640
|
host,
|
|
@@ -8268,7 +8649,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8268
8649
|
}
|
|
8269
8650
|
),
|
|
8270
8651
|
slots?.modals,
|
|
8271
|
-
canCrossfade && xfFromId && xfToId && /* @__PURE__ */
|
|
8652
|
+
canCrossfade && xfFromId && xfToId && /* @__PURE__ */ jsx29("div", { className: designerView ? "contents" : "hidden", children: /* @__PURE__ */ jsx29(
|
|
8272
8653
|
TransitionDesigner,
|
|
8273
8654
|
{
|
|
8274
8655
|
host,
|
|
@@ -8287,8 +8668,8 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8287
8668
|
testIdPrefix: `${identity.familyKey}-transition-designer`
|
|
8288
8669
|
}
|
|
8289
8670
|
) }),
|
|
8290
|
-
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */
|
|
8291
|
-
panelBus.supported && panelBus.bus && /* @__PURE__ */
|
|
8671
|
+
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */ jsx29("div", { className: "text-sas-muted text-xs text-center py-4", children: "Loading tracks..." }) : /* @__PURE__ */ jsxs22(Fragment7, { children: [
|
|
8672
|
+
panelBus.supported && panelBus.bus && /* @__PURE__ */ jsx29(
|
|
8292
8673
|
PanelMasterStrip,
|
|
8293
8674
|
{
|
|
8294
8675
|
bus: panelBus.bus,
|
|
@@ -8309,7 +8690,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8309
8690
|
}
|
|
8310
8691
|
),
|
|
8311
8692
|
slots?.beforeRows,
|
|
8312
|
-
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */
|
|
8693
|
+
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ jsx29(
|
|
8313
8694
|
CrossfadeTrackRow,
|
|
8314
8695
|
{
|
|
8315
8696
|
accentColor: identity.transitionAccentColor,
|
|
@@ -8346,7 +8727,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8346
8727
|
},
|
|
8347
8728
|
pair.groupId
|
|
8348
8729
|
)),
|
|
8349
|
-
resolvedSingleFades.map((fade) => /* @__PURE__ */
|
|
8730
|
+
resolvedSingleFades.map((fade) => /* @__PURE__ */ jsx29(
|
|
8350
8731
|
FadeTrackRow,
|
|
8351
8732
|
{
|
|
8352
8733
|
accentColor: identity.transitionAccentColor,
|
|
@@ -8371,7 +8752,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8371
8752
|
},
|
|
8372
8753
|
fade.dbId
|
|
8373
8754
|
)),
|
|
8374
|
-
resolvedGroupFades.map((group) => /* @__PURE__ */
|
|
8755
|
+
resolvedGroupFades.map((group) => /* @__PURE__ */ jsx29(
|
|
8375
8756
|
GroupFadeTrackRow,
|
|
8376
8757
|
{
|
|
8377
8758
|
accentColor: identity.transitionAccentColor,
|
|
@@ -8407,20 +8788,28 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
8407
8788
|
group.groupId
|
|
8408
8789
|
)),
|
|
8409
8790
|
(adapter.groupExtensions ?? []).flatMap(
|
|
8410
|
-
(ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).filter((group) => !group.members.every((m) => fadeMemberDbIds.has(m.dbId))).map((group) => /* @__PURE__ */
|
|
8791
|
+
(ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).filter((group) => !group.members.every((m) => fadeMemberDbIds.has(m.dbId))).map((group) => /* @__PURE__ */ jsx29(
|
|
8792
|
+
CollapsibleGroup,
|
|
8793
|
+
{
|
|
8794
|
+
ext,
|
|
8795
|
+
group,
|
|
8796
|
+
groupCtx
|
|
8797
|
+
},
|
|
8798
|
+
`${ext.metaKey}:${group.groupId}`
|
|
8799
|
+
))
|
|
8411
8800
|
),
|
|
8412
8801
|
tracks.map((track, index) => {
|
|
8413
8802
|
if (crossfadeMemberDbIds.has(track.handle.dbId) || fadeMemberDbIds.has(track.handle.dbId) || genericGroupMemberDbIds.has(track.handle.dbId)) {
|
|
8414
8803
|
return null;
|
|
8415
8804
|
}
|
|
8416
|
-
return /* @__PURE__ */
|
|
8805
|
+
return /* @__PURE__ */ jsx29(TrackRow, { ...buildRowProps(track, reorder.dragPropsFor(index)) }, track.handle.id);
|
|
8417
8806
|
}),
|
|
8418
8807
|
slots?.afterRows
|
|
8419
8808
|
] })),
|
|
8420
8809
|
features.exportMidi && !designerView && !isLoadingTracks && tracks.length > 0 && (() => {
|
|
8421
8810
|
const hasAnyMidi = tracks.some((t) => t.hasMidi);
|
|
8422
8811
|
const exportDisabled = isExportingMidi || !hasAnyMidi;
|
|
8423
|
-
return /* @__PURE__ */
|
|
8812
|
+
return /* @__PURE__ */ jsx29("div", { className: "pt-2", children: /* @__PURE__ */ jsx29(
|
|
8424
8813
|
"button",
|
|
8425
8814
|
{
|
|
8426
8815
|
"data-testid": "export-midi-tracks-button",
|
|
@@ -8475,6 +8864,15 @@ function createSurgeSoundAdapter(host, overrides = {}) {
|
|
|
8475
8864
|
});
|
|
8476
8865
|
return snap.label;
|
|
8477
8866
|
},
|
|
8867
|
+
persistDescriptor: async (trackId, descriptor, label) => {
|
|
8868
|
+
const { state, stateType } = descriptor;
|
|
8869
|
+
await host.persistTrackPresetState?.(trackId, {
|
|
8870
|
+
state,
|
|
8871
|
+
stateType: stateType ?? "valuetree",
|
|
8872
|
+
name: label
|
|
8873
|
+
}).catch(() => {
|
|
8874
|
+
});
|
|
8875
|
+
},
|
|
8478
8876
|
descriptorFromSnapshot: (snap) => {
|
|
8479
8877
|
const preset = snap;
|
|
8480
8878
|
return { state: preset.state, stateType: preset.stateType };
|
|
@@ -8559,18 +8957,183 @@ var SUB = {
|
|
|
8559
8957
|
rootOnly: true,
|
|
8560
8958
|
monoPreference: "low"
|
|
8561
8959
|
};
|
|
8960
|
+
var HORN_MAX_NOTES_PER_BAR = 12;
|
|
8961
|
+
var HORN_FOLLOW_PALETTE = "EXACTLY the lead voice's rhythm \u2014 every attack lands together with voice 1";
|
|
8962
|
+
var HORN_LEAD = {
|
|
8963
|
+
label: "lead trumpet",
|
|
8964
|
+
role: "brass",
|
|
8965
|
+
registerLow: 60,
|
|
8966
|
+
// C4
|
|
8967
|
+
registerHigh: 84,
|
|
8968
|
+
// C6
|
|
8969
|
+
maxNotesPerBar: HORN_MAX_NOTES_PER_BAR,
|
|
8970
|
+
rhythmPalette: "tight funk syncopation \u2014 8ths and 16ths, staccato punches, off-beat anticipations",
|
|
8971
|
+
harmonicDiscipline: "the section's melodic top \u2014 chord tones on every hit; quick chromatic approach tones only on pickups",
|
|
8972
|
+
monoPreference: "high"
|
|
8973
|
+
};
|
|
8974
|
+
var HORN_TRUMPET_2 = {
|
|
8975
|
+
label: "second trumpet",
|
|
8976
|
+
role: "brass",
|
|
8977
|
+
registerLow: 55,
|
|
8978
|
+
// G3
|
|
8979
|
+
registerHigh: 79,
|
|
8980
|
+
// G5
|
|
8981
|
+
maxNotesPerBar: HORN_MAX_NOTES_PER_BAR,
|
|
8982
|
+
rhythmPalette: HORN_FOLLOW_PALETTE,
|
|
8983
|
+
harmonicDiscipline: "nearest chord tone directly below voice 1 \u2014 tight close voicing",
|
|
8984
|
+
monoPreference: "high"
|
|
8985
|
+
};
|
|
8986
|
+
var HORN_ALTO = {
|
|
8987
|
+
label: "alto sax",
|
|
8988
|
+
role: "brass",
|
|
8989
|
+
registerLow: 53,
|
|
8990
|
+
// F3
|
|
8991
|
+
registerHigh: 77,
|
|
8992
|
+
// F5
|
|
8993
|
+
maxNotesPerBar: HORN_MAX_NOTES_PER_BAR,
|
|
8994
|
+
rhythmPalette: HORN_FOLLOW_PALETTE,
|
|
8995
|
+
harmonicDiscipline: "chord tone below the trumpets \u2014 keep the upper stack inside one octave",
|
|
8996
|
+
monoPreference: "high"
|
|
8997
|
+
};
|
|
8998
|
+
var HORN_TENOR = {
|
|
8999
|
+
label: "tenor sax",
|
|
9000
|
+
role: "brass",
|
|
9001
|
+
registerLow: 46,
|
|
9002
|
+
// Bb2
|
|
9003
|
+
registerHigh: 72,
|
|
9004
|
+
// C5
|
|
9005
|
+
maxNotesPerBar: HORN_MAX_NOTES_PER_BAR,
|
|
9006
|
+
rhythmPalette: HORN_FOLLOW_PALETTE,
|
|
9007
|
+
harmonicDiscipline: "chord tone under the upper horns \u2014 complete the chord",
|
|
9008
|
+
monoPreference: "low"
|
|
9009
|
+
};
|
|
9010
|
+
var HORN_TROMBONE = {
|
|
9011
|
+
label: "trombone",
|
|
9012
|
+
role: "brass",
|
|
9013
|
+
registerLow: 43,
|
|
9014
|
+
// G2
|
|
9015
|
+
registerHigh: 67,
|
|
9016
|
+
// G4
|
|
9017
|
+
maxNotesPerBar: HORN_MAX_NOTES_PER_BAR,
|
|
9018
|
+
rhythmPalette: HORN_FOLLOW_PALETTE,
|
|
9019
|
+
harmonicDiscipline: "chord tone or root below the saxes \u2014 weight in the middle-low stack",
|
|
9020
|
+
monoPreference: "low"
|
|
9021
|
+
};
|
|
9022
|
+
var HORN_BARI = {
|
|
9023
|
+
label: "baritone sax",
|
|
9024
|
+
role: "brass",
|
|
9025
|
+
registerLow: 36,
|
|
9026
|
+
// C2
|
|
9027
|
+
registerHigh: 60,
|
|
9028
|
+
// C4
|
|
9029
|
+
maxNotesPerBar: HORN_MAX_NOTES_PER_BAR,
|
|
9030
|
+
rhythmPalette: HORN_FOLLOW_PALETTE,
|
|
9031
|
+
harmonicDiscipline: "chord roots and lower chord tones \u2014 the section's bottom anchor",
|
|
9032
|
+
monoPreference: "low"
|
|
9033
|
+
};
|
|
9034
|
+
var WIND_FLUTE = {
|
|
9035
|
+
label: "flute",
|
|
9036
|
+
role: "winds",
|
|
9037
|
+
registerLow: 60,
|
|
9038
|
+
// C4
|
|
9039
|
+
registerHigh: 93,
|
|
9040
|
+
// A6
|
|
9041
|
+
maxNotesPerBar: 8,
|
|
9042
|
+
rhythmPalette: "8ths and 16ths; runs, turns and trills welcome",
|
|
9043
|
+
harmonicDiscipline: "freest voice \u2014 non-chord tones as passing/neighbor tones on weak beats, resolving by step",
|
|
9044
|
+
monoPreference: "high"
|
|
9045
|
+
};
|
|
9046
|
+
var WIND_FLUTE_2 = {
|
|
9047
|
+
label: "second flute",
|
|
9048
|
+
role: "winds",
|
|
9049
|
+
registerLow: 60,
|
|
9050
|
+
// C4
|
|
9051
|
+
registerHigh: 86,
|
|
9052
|
+
// D6
|
|
9053
|
+
maxNotesPerBar: 5,
|
|
9054
|
+
rhythmPalette: "quarters with occasional 8th-note motion",
|
|
9055
|
+
harmonicDiscipline: "chord tones; fill gaps the other inner voices leave",
|
|
9056
|
+
monoPreference: "high"
|
|
9057
|
+
};
|
|
9058
|
+
var WIND_OBOE = {
|
|
9059
|
+
label: "oboe",
|
|
9060
|
+
role: "winds",
|
|
9061
|
+
registerLow: 58,
|
|
9062
|
+
// Bb3
|
|
9063
|
+
registerHigh: 84,
|
|
9064
|
+
// C6
|
|
9065
|
+
maxNotesPerBar: 6,
|
|
9066
|
+
rhythmPalette: "8ths and quarters; move when the flute rests",
|
|
9067
|
+
harmonicDiscipline: "mostly chord tones; may imitate the flute's motifs a bar later",
|
|
9068
|
+
monoPreference: "high"
|
|
9069
|
+
};
|
|
9070
|
+
var WIND_CLARINET = {
|
|
9071
|
+
label: "clarinet",
|
|
9072
|
+
role: "winds",
|
|
9073
|
+
registerLow: 50,
|
|
9074
|
+
// D3
|
|
9075
|
+
registerHigh: 81,
|
|
9076
|
+
// A5
|
|
9077
|
+
maxNotesPerBar: 4,
|
|
9078
|
+
rhythmPalette: "quarters and halves",
|
|
9079
|
+
harmonicDiscipline: "chord tones with smooth stepwise motion between them",
|
|
9080
|
+
monoPreference: "high"
|
|
9081
|
+
};
|
|
9082
|
+
var WIND_FRENCH_HORN = {
|
|
9083
|
+
label: "french horn",
|
|
9084
|
+
role: "winds",
|
|
9085
|
+
registerLow: 41,
|
|
9086
|
+
// F2
|
|
9087
|
+
registerHigh: 72,
|
|
9088
|
+
// C5
|
|
9089
|
+
maxNotesPerBar: 4,
|
|
9090
|
+
rhythmPalette: "quarters and halves; long held tones welcome",
|
|
9091
|
+
harmonicDiscipline: "chord tones \u2014 the warm glue in the middle of the ensemble",
|
|
9092
|
+
monoPreference: "low"
|
|
9093
|
+
};
|
|
9094
|
+
var WIND_BASSOON = {
|
|
9095
|
+
label: "bassoon",
|
|
9096
|
+
role: "winds",
|
|
9097
|
+
registerLow: 34,
|
|
9098
|
+
// Bb1
|
|
9099
|
+
registerHigh: 62,
|
|
9100
|
+
// D4
|
|
9101
|
+
maxNotesPerBar: 3,
|
|
9102
|
+
rhythmPalette: "quarters and halves; brief walking figures at cadences",
|
|
9103
|
+
harmonicDiscipline: "roots and fifths emphasized; passing tones only between chord tones",
|
|
9104
|
+
monoPreference: "low"
|
|
9105
|
+
};
|
|
8562
9106
|
var ENSEMBLE_MIN_VOICES = 2;
|
|
8563
9107
|
var ENSEMBLE_MAX_VOICES = 6;
|
|
8564
|
-
var
|
|
9108
|
+
var STRINGS_SPEC_TABLES = {
|
|
8565
9109
|
2: [TOP, BASS],
|
|
8566
9110
|
3: [TOP, INNER, BASS],
|
|
8567
9111
|
4: [TOP, COUNTER, TENOR, BASS],
|
|
8568
9112
|
5: [TOP, COUNTER, INNER, TENOR, SUB],
|
|
8569
9113
|
6: [TOP, COUNTER, INNER_2, INNER, TENOR, SUB]
|
|
8570
9114
|
};
|
|
8571
|
-
|
|
9115
|
+
var HORNS_SPEC_TABLES = {
|
|
9116
|
+
2: [HORN_LEAD, HORN_TENOR],
|
|
9117
|
+
3: [HORN_LEAD, HORN_TENOR, HORN_BARI],
|
|
9118
|
+
4: [HORN_LEAD, HORN_TRUMPET_2, HORN_TENOR, HORN_BARI],
|
|
9119
|
+
5: [HORN_LEAD, HORN_TRUMPET_2, HORN_TENOR, HORN_TROMBONE, HORN_BARI],
|
|
9120
|
+
6: [HORN_LEAD, HORN_TRUMPET_2, HORN_ALTO, HORN_TENOR, HORN_TROMBONE, HORN_BARI]
|
|
9121
|
+
};
|
|
9122
|
+
var WINDS_SPEC_TABLES = {
|
|
9123
|
+
2: [WIND_FLUTE, WIND_BASSOON],
|
|
9124
|
+
3: [WIND_FLUTE, WIND_CLARINET, WIND_BASSOON],
|
|
9125
|
+
4: [WIND_FLUTE, WIND_OBOE, WIND_FRENCH_HORN, WIND_BASSOON],
|
|
9126
|
+
5: [WIND_FLUTE, WIND_OBOE, WIND_CLARINET, WIND_FRENCH_HORN, WIND_BASSOON],
|
|
9127
|
+
6: [WIND_FLUTE, WIND_FLUTE_2, WIND_OBOE, WIND_CLARINET, WIND_FRENCH_HORN, WIND_BASSOON]
|
|
9128
|
+
};
|
|
9129
|
+
var SPEC_TABLES_BY_INSTRUMENTATION = {
|
|
9130
|
+
strings: STRINGS_SPEC_TABLES,
|
|
9131
|
+
horns: HORNS_SPEC_TABLES,
|
|
9132
|
+
winds: WINDS_SPEC_TABLES
|
|
9133
|
+
};
|
|
9134
|
+
function defaultVoiceSpecs(voiceCount, instrumentation = "strings") {
|
|
8572
9135
|
const n = Math.max(ENSEMBLE_MIN_VOICES, Math.min(ENSEMBLE_MAX_VOICES, Math.round(voiceCount)));
|
|
8573
|
-
return
|
|
9136
|
+
return SPEC_TABLES_BY_INSTRUMENTATION[instrumentation][n].map((spec, voiceIndex) => ({ ...spec, voiceIndex }));
|
|
8574
9137
|
}
|
|
8575
9138
|
|
|
8576
9139
|
// src/ensemble-core/enforce-voice.ts
|
|
@@ -8610,10 +9173,14 @@ function enforceVoice(rawNotes, spec, opts) {
|
|
|
8610
9173
|
repairs.push(`voice ${spec.voiceIndex}: dropped note outside the ${opts.bars}-bar clip (start ${n.startBeat})`);
|
|
8611
9174
|
continue;
|
|
8612
9175
|
}
|
|
8613
|
-
|
|
9176
|
+
let durationBeats = Math.max(
|
|
8614
9177
|
MIN_NOTE_DURATION_BEATS,
|
|
8615
9178
|
Math.min(n.durationBeats, clipEnd - n.startBeat)
|
|
8616
9179
|
);
|
|
9180
|
+
if (opts.maxNoteDurationBeats !== void 0 && durationBeats > opts.maxNoteDurationBeats) {
|
|
9181
|
+
durationBeats = Math.max(MIN_NOTE_DURATION_BEATS, opts.maxNoteDurationBeats);
|
|
9182
|
+
repairs.push(`voice ${spec.voiceIndex}: trimmed note at beat ${n.startBeat} to the style's ${opts.maxNoteDurationBeats}-beat stab ceiling`);
|
|
9183
|
+
}
|
|
8617
9184
|
notes.push({ ...n, durationBeats });
|
|
8618
9185
|
}
|
|
8619
9186
|
notes = notes.map((n) => {
|
|
@@ -8774,12 +9341,19 @@ function describeViolations(analysis, rules) {
|
|
|
8774
9341
|
if (pair.onsetIndependence < rules.minOnsetIndependence) {
|
|
8775
9342
|
out.push(`Voices ${pair.upperVoice + 1} and ${pair.upperVoice + 2} attack together too often (independence ${pair.onsetIndependence.toFixed(2)} < ${rules.minOnsetIndependence}) \u2014 stagger entrances and let one voice move while the other holds.`);
|
|
8776
9343
|
}
|
|
9344
|
+
if (rules.maxOnsetIndependence !== void 0 && pair.onsetIndependence > rules.maxOnsetIndependence) {
|
|
9345
|
+
out.push(`Voices ${pair.upperVoice + 1} and ${pair.upperVoice + 2} don't attack together enough (independence ${pair.onsetIndependence.toFixed(2)} > ${rules.maxOnsetIndependence}) \u2014 this is a section: give every voice the SAME rhythm as the lead so the hits land as one.`);
|
|
9346
|
+
}
|
|
8777
9347
|
}
|
|
8778
9348
|
return out;
|
|
8779
9349
|
}
|
|
8780
9350
|
|
|
8781
9351
|
// src/ensemble-core/styles.ts
|
|
8782
9352
|
var ENSEMBLE_STYLES = ["counterpoint", "chorale", "interlock"];
|
|
9353
|
+
var STAB_MAX_ONSET_INDEPENDENCE = 0.25;
|
|
9354
|
+
var RIFF_MAX_ONSET_INDEPENDENCE = 0.3;
|
|
9355
|
+
var UNISON_MAX_ONSET_INDEPENDENCE = 0.15;
|
|
9356
|
+
var STAB_MAX_NOTE_DURATION_BEATS = 1;
|
|
8783
9357
|
var STYLE_RULES = {
|
|
8784
9358
|
counterpoint: {
|
|
8785
9359
|
forbidParallelPerfects: true,
|
|
@@ -8798,9 +9372,55 @@ var STYLE_RULES = {
|
|
|
8798
9372
|
forbidVoiceCrossing: false,
|
|
8799
9373
|
minOnsetIndependence: 0.6,
|
|
8800
9374
|
promptParagraph: "STYLE \u2014 INTERLOCK (minimal / systems music): short repeating cells that mesh like gears. Each voice keeps its own ostinato; onsets rarely coincide with the neighboring voice; parallel motion and doubling are welcome when the composite rhythm stays busy and even."
|
|
9375
|
+
},
|
|
9376
|
+
stabs: {
|
|
9377
|
+
forbidParallelPerfects: false,
|
|
9378
|
+
forbidVoiceCrossing: true,
|
|
9379
|
+
minOnsetIndependence: 0,
|
|
9380
|
+
maxOnsetIndependence: STAB_MAX_ONSET_INDEPENDENCE,
|
|
9381
|
+
maxNoteDurationBeats: STAB_MAX_NOTE_DURATION_BEATS,
|
|
9382
|
+
promptParagraph: `STYLE \u2014 STABS (James Brown funk-45 horn punches): short accented chord hits \u2014 single punches and two-to-four-note kicks \u2014 placed on the groove's pressure points: the one, off-beat "ands", and the pickup into the next bar. Every hit is staccato (a 16th to an 8th long) and voiced as ONE tight chord under the lead. Leave REAL space between hits \u2014 the section punctuates the groove, it never carpets it.`
|
|
9383
|
+
},
|
|
9384
|
+
riffs: {
|
|
9385
|
+
forbidParallelPerfects: false,
|
|
9386
|
+
forbidVoiceCrossing: true,
|
|
9387
|
+
minOnsetIndependence: 0,
|
|
9388
|
+
maxOnsetIndependence: RIFF_MAX_ONSET_INDEPENDENCE,
|
|
9389
|
+
promptParagraph: 'STYLE \u2014 RIFFS (funk/soul section soli): ONE syncopated 8th/16th riff, one or two bars long, repeated with small variations for the whole clip \u2014 the entire section plays it together, harmonized in tight close voicings under the lead. Anticipate downbeats (attack the "and" just before the barline), leave air at phrase ends, and let the final hit of a phrase ring a little longer.'
|
|
9390
|
+
},
|
|
9391
|
+
unison: {
|
|
9392
|
+
forbidParallelPerfects: false,
|
|
9393
|
+
forbidVoiceCrossing: false,
|
|
9394
|
+
minOnsetIndependence: 0,
|
|
9395
|
+
maxOnsetIndependence: UNISON_MAX_ONSET_INDEPENDENCE,
|
|
9396
|
+
promptParagraph: "STYLE \u2014 UNISON (the Cold Sweat power line): every voice doubles the SAME riff in octaves \u2014 no harmony, one line, maximum punch. Keep it syncopated and riff-based with space between phrases; octave doubling and parallel motion are the entire point."
|
|
8801
9397
|
}
|
|
8802
9398
|
};
|
|
8803
9399
|
|
|
9400
|
+
// src/ensemble-core/instrumentation.ts
|
|
9401
|
+
var ENSEMBLE_INSTRUMENTATIONS = [
|
|
9402
|
+
"strings",
|
|
9403
|
+
"horns",
|
|
9404
|
+
"winds"
|
|
9405
|
+
];
|
|
9406
|
+
var STYLES_FOR_INSTRUMENTATION = {
|
|
9407
|
+
strings: ["counterpoint", "chorale", "interlock"],
|
|
9408
|
+
horns: ["stabs", "riffs", "unison"],
|
|
9409
|
+
winds: ["counterpoint", "chorale", "interlock"]
|
|
9410
|
+
};
|
|
9411
|
+
var DEFAULT_STYLE_FOR_INSTRUMENTATION = {
|
|
9412
|
+
strings: "counterpoint",
|
|
9413
|
+
horns: "stabs",
|
|
9414
|
+
winds: "chorale"
|
|
9415
|
+
};
|
|
9416
|
+
function normalizeInstrumentation(raw) {
|
|
9417
|
+
return ENSEMBLE_INSTRUMENTATIONS.includes(raw) ? raw : "strings";
|
|
9418
|
+
}
|
|
9419
|
+
function styleForInstrumentation(instrumentation, rawStyle) {
|
|
9420
|
+
const allowed = STYLES_FOR_INSTRUMENTATION[instrumentation];
|
|
9421
|
+
return allowed.includes(rawStyle ?? "") ? rawStyle : DEFAULT_STYLE_FOR_INSTRUMENTATION[instrumentation];
|
|
9422
|
+
}
|
|
9423
|
+
|
|
8804
9424
|
// src/ensemble-core/ensemble-schema.ts
|
|
8805
9425
|
var SUBMIT_ENSEMBLE_TOOL_NAME = "submit_ensemble";
|
|
8806
9426
|
function buildSubmitEnsembleParameters(voiceCount) {
|
|
@@ -8882,9 +9502,13 @@ function voiceContractLine(spec) {
|
|
|
8882
9502
|
const root = spec.rootOnly ? " ROOT PITCH CLASS ONLY (each bar's chord root)." : "";
|
|
8883
9503
|
return `- Voice ${spec.voiceIndex + 1} (${spec.label}): MIDI ${spec.registerLow}-${spec.registerHigh}, max ${spec.maxNotesPerBar} notes/bar, rhythm: ${spec.rhythmPalette}. ${spec.harmonicDiscipline}.${root}`;
|
|
8884
9504
|
}
|
|
8885
|
-
|
|
9505
|
+
var WOVEN_PERSONA = {
|
|
9506
|
+
strings: "You are an ensemble composer.",
|
|
9507
|
+
winds: "You are a wind-ensemble composer writing for chamber winds (flutes, oboe, clarinet, french horn, bassoon)."
|
|
9508
|
+
};
|
|
9509
|
+
function buildWovenPrompt(specs, style, instrumentation) {
|
|
8886
9510
|
const styleParagraph = STYLE_RULES[style].promptParagraph;
|
|
8887
|
-
return
|
|
9511
|
+
return `${WOVEN_PERSONA[instrumentation]} Compose ${specs.length} voices as ONE piece of music \u2014 not ${specs.length} independent parts.
|
|
8888
9512
|
|
|
8889
9513
|
Submit your composition by calling the ${SUBMIT_ENSEMBLE_TOOL_NAME} tool with all ${specs.length} voices.
|
|
8890
9514
|
|
|
@@ -8901,6 +9525,28 @@ ${styleParagraph}
|
|
|
8901
9525
|
PER-VOICE CONTRACTS:
|
|
8902
9526
|
${specs.map(voiceContractLine).join("\n")}`;
|
|
8903
9527
|
}
|
|
9528
|
+
function buildSectionPrompt(specs, style) {
|
|
9529
|
+
const styleParagraph = STYLE_RULES[style].promptParagraph;
|
|
9530
|
+
return `You are a funk horn-section arranger. Compose ${specs.length} horn parts as ONE section \u2014 ${specs.length} players phrasing like a single instrument, not ${specs.length} independent parts.
|
|
9531
|
+
|
|
9532
|
+
Submit your composition by calling the ${SUBMIT_ENSEMBLE_TOOL_NAME} tool with all ${specs.length} voices.
|
|
9533
|
+
|
|
9534
|
+
THE SECTION RULES (most important):
|
|
9535
|
+
1. The section speaks with ONE rhythm: every voice attacks on the same beats as Voice 1 (the lead). Compose the lead line first, then stack the other voices onto its rhythm exactly \u2014 the style below dictates chord voicing vs octave doubling.
|
|
9536
|
+
2. Each voice is a SINGLE monophonic line \u2014 within one voice, no two notes overlap in time and no chords. The chord is the STACK of voices hitting together.
|
|
9537
|
+
3. SPACE IS THE GROOVE: short phrases and punches separated by real rests. Never carpet the bar \u2014 the section punctuates around the rhythm section, and the silence between hits is part of the riff.
|
|
9538
|
+
4. Follow the chord progression in the musical context exactly \u2014 every hit is voiced from the chord sounding at that beat.
|
|
9539
|
+
5. Make it DANCE: this is funk. Syncopate, anticipate downbeats (attack the "and" just before the barline), and accent with velocity \u2014 105-120 on accented hits, 70-90 on lighter punches. Repeat the riff so the clip locks like a loop.
|
|
9540
|
+
6. startBeat/durationBeats are in quarter-note beats from the start of the clip; respect each voice's register window and notes-per-bar cap exactly; fill the stated bar length and land the first hit in bar 1 (no long silent intro).
|
|
9541
|
+
|
|
9542
|
+
${styleParagraph}
|
|
9543
|
+
|
|
9544
|
+
PER-VOICE CONTRACTS:
|
|
9545
|
+
${specs.map(voiceContractLine).join("\n")}`;
|
|
9546
|
+
}
|
|
9547
|
+
function buildEnsembleSystemPrompt(specs, style, instrumentation = "strings") {
|
|
9548
|
+
return instrumentation === "horns" ? buildSectionPrompt(specs, style) : buildWovenPrompt(specs, style, instrumentation);
|
|
9549
|
+
}
|
|
8904
9550
|
function buildViolationRetrySuffix(violations) {
|
|
8905
9551
|
if (violations.length === 0) return "";
|
|
8906
9552
|
return `
|
|
@@ -8910,7 +9556,7 @@ Your previous ensemble had these problems \u2014 fix them while keeping everythi
|
|
|
8910
9556
|
}
|
|
8911
9557
|
|
|
8912
9558
|
// src/constants/sdk-version.ts
|
|
8913
|
-
var PLUGIN_SDK_VERSION = "2.
|
|
9559
|
+
var PLUGIN_SDK_VERSION = "2.46.0";
|
|
8914
9560
|
|
|
8915
9561
|
// src/utils/format-concurrent-tracks.ts
|
|
8916
9562
|
function formatConcurrentTracks(ctx) {
|
|
@@ -9075,10 +9721,12 @@ export {
|
|
|
9075
9721
|
DB_MIN,
|
|
9076
9722
|
DEFAULT_FX_CATEGORY_DETAIL,
|
|
9077
9723
|
DEFAULT_FX_DRY_WET,
|
|
9724
|
+
DEFAULT_STYLE_FOR_INSTRUMENTATION,
|
|
9078
9725
|
DRAG_DEAD_ZONE,
|
|
9079
9726
|
DownloadPackButton,
|
|
9080
9727
|
EMPTY_FX_DETAIL_STATE,
|
|
9081
9728
|
EMPTY_FX_STATE,
|
|
9729
|
+
ENSEMBLE_INSTRUMENTATIONS,
|
|
9082
9730
|
ENSEMBLE_MAX_VOICES,
|
|
9083
9731
|
ENSEMBLE_MIN_VOICES,
|
|
9084
9732
|
ENSEMBLE_STYLES,
|
|
@@ -9093,7 +9741,9 @@ export {
|
|
|
9093
9741
|
FxToggleBar,
|
|
9094
9742
|
GUTTER_W,
|
|
9095
9743
|
GeneratorPanelShell,
|
|
9744
|
+
GroupCollapseChevron,
|
|
9096
9745
|
GroupFadeTrackRow,
|
|
9746
|
+
HORN_MAX_NOTES_PER_BAR,
|
|
9097
9747
|
ImportTrackModal,
|
|
9098
9748
|
TrackDrawer as InstrumentDrawer,
|
|
9099
9749
|
LevelMeter,
|
|
@@ -9107,8 +9757,12 @@ export {
|
|
|
9107
9757
|
PianoRollEditor,
|
|
9108
9758
|
PluginError,
|
|
9109
9759
|
RESIZE_HANDLE_PX,
|
|
9760
|
+
RIFF_MAX_ONSET_INDEPENDENCE,
|
|
9110
9761
|
ROW_HEIGHT,
|
|
9111
9762
|
SLIDER_UNITY,
|
|
9763
|
+
STAB_MAX_NOTE_DURATION_BEATS,
|
|
9764
|
+
STAB_MAX_ONSET_INDEPENDENCE,
|
|
9765
|
+
STYLES_FOR_INSTRUMENTATION,
|
|
9112
9766
|
STYLE_RULES,
|
|
9113
9767
|
SUBMIT_ENSEMBLE_TOOL_NAME,
|
|
9114
9768
|
SamplePackCTACard,
|
|
@@ -9118,9 +9772,11 @@ export {
|
|
|
9118
9772
|
TRANSITION_DESIGNER_DRAFT_KEY,
|
|
9119
9773
|
TrackDrawer,
|
|
9120
9774
|
TrackExternalFxSection,
|
|
9775
|
+
TrackFreezeSection,
|
|
9121
9776
|
TrackMeterStrip,
|
|
9122
9777
|
TrackRow,
|
|
9123
9778
|
TransitionDesigner,
|
|
9779
|
+
UNISON_MAX_ONSET_INDEPENDENCE,
|
|
9124
9780
|
VolumeSlider,
|
|
9125
9781
|
WaveformView,
|
|
9126
9782
|
analyzeEnsemble,
|
|
@@ -9155,6 +9811,7 @@ export {
|
|
|
9155
9811
|
moveItem,
|
|
9156
9812
|
nearestPitchWithPc,
|
|
9157
9813
|
newTrackState,
|
|
9814
|
+
normalizeInstrumentation,
|
|
9158
9815
|
normalizeSlots,
|
|
9159
9816
|
padPair,
|
|
9160
9817
|
padSlots,
|
|
@@ -9178,6 +9835,7 @@ export {
|
|
|
9178
9835
|
slotsEqual,
|
|
9179
9836
|
soundIdentity,
|
|
9180
9837
|
splitFadeEntries,
|
|
9838
|
+
styleForInstrumentation,
|
|
9181
9839
|
synthesizeCuePoints,
|
|
9182
9840
|
tokenizePrompt,
|
|
9183
9841
|
trackDataKey,
|
|
@@ -9188,6 +9846,7 @@ export {
|
|
|
9188
9846
|
useSceneState,
|
|
9189
9847
|
useSoundHistory,
|
|
9190
9848
|
useTrackExternalFx,
|
|
9849
|
+
useTrackFreeze,
|
|
9191
9850
|
useTrackLevel,
|
|
9192
9851
|
useTrackLevels,
|
|
9193
9852
|
useTrackMeter,
|