@signalsandsorcery/plugin-sdk 2.36.1 → 2.38.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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 useState4, useMemo as useMemo3 } from "react";
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/TrackDrawer.tsx
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,
@@ -1092,12 +1211,15 @@ function TrackDrawer({
1092
1211
  editSnap,
1093
1212
  onAuditionNote
1094
1213
  }) {
1095
- const [search, setSearch] = useState4("");
1214
+ const [search, setSearch] = useState5("");
1096
1215
  const fxEnabled = !!onFxToggle;
1097
1216
  const pickEnabled = !!onSelect;
1098
1217
  const historyEnabled = !!onRestoreSound;
1099
1218
  const importEnabled = !!onImportSound;
1100
1219
  const editEnabled = !!onNotesChange;
1220
+ const internalFreeze = useTrackFreeze(freeze ? void 0 : externalFxHost, trackId);
1221
+ const fz = freeze ?? internalFreeze;
1222
+ const freezeEnabled = fz.enabled;
1101
1223
  const enabledTabs = useMemo3(() => {
1102
1224
  const tabs = [];
1103
1225
  if (fxEnabled) tabs.push("fx");
@@ -1105,8 +1227,9 @@ function TrackDrawer({
1105
1227
  if (historyEnabled) tabs.push("history");
1106
1228
  if (importEnabled) tabs.push("import");
1107
1229
  if (editEnabled) tabs.push("edit");
1230
+ if (freezeEnabled) tabs.push("freeze");
1108
1231
  return tabs;
1109
- }, [fxEnabled, pickEnabled, historyEnabled, importEnabled, editEnabled]);
1232
+ }, [fxEnabled, pickEnabled, historyEnabled, importEnabled, editEnabled, freezeEnabled]);
1110
1233
  const SURGE_XT_DEFAULT_ID = "Surge XT";
1111
1234
  const filtered = useMemo3(() => {
1112
1235
  let all = instruments.filter((i) => i.name !== "Surge XT");
@@ -1128,12 +1251,12 @@ function TrackDrawer({
1128
1251
  const history = soundHistory ?? [];
1129
1252
  const effectiveTab = enabledTabs.includes(activeTab) ? activeTab : enabledTabs[0] ?? "fx";
1130
1253
  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__ */ jsx4(
1254
+ const strip = enabledTabs.length > 1 ? /* @__PURE__ */ jsx5(
1132
1255
  "div",
1133
1256
  {
1134
1257
  className: "flex items-center gap-1 border-b border-sas-border pb-1",
1135
1258
  "data-testid": "sdk-drawer-tabs",
1136
- children: enabledTabs.map((tab) => /* @__PURE__ */ jsx4(
1259
+ children: enabledTabs.map((tab) => /* @__PURE__ */ jsx5(
1137
1260
  "button",
1138
1261
  {
1139
1262
  type: "button",
@@ -1147,9 +1270,9 @@ function TrackDrawer({
1147
1270
  }
1148
1271
  ) : null;
1149
1272
  const currentSound = soundHistoryCursor >= 0 && soundHistoryCursor < history.length ? history[soundHistoryCursor].label : null;
1150
- const header = strip || currentSound ? /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", "data-testid": "sdk-drawer-header", children: [
1273
+ const header = strip || currentSound ? /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", "data-testid": "sdk-drawer-header", children: [
1151
1274
  strip,
1152
- currentSound && /* @__PURE__ */ jsx4(
1275
+ currentSound && /* @__PURE__ */ jsx5(
1153
1276
  "span",
1154
1277
  {
1155
1278
  className: "text-[10px] text-sas-muted/60 truncate px-0.5",
@@ -1158,10 +1281,41 @@ function TrackDrawer({
1158
1281
  }
1159
1282
  )
1160
1283
  ] }) : null;
1284
+ if (effectiveTab === "freeze") {
1285
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-freeze", children: [
1286
+ header,
1287
+ /* @__PURE__ */ jsx5(
1288
+ TrackFreezeSection,
1289
+ {
1290
+ state: fz.state,
1291
+ busy: fz.busy,
1292
+ error: fz.error,
1293
+ onFreeze: () => void fz.freeze(),
1294
+ onUnfreeze: () => void fz.unfreeze()
1295
+ }
1296
+ )
1297
+ ] });
1298
+ }
1299
+ if (fz.state?.frozen === true) {
1300
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-frozen-lock", children: [
1301
+ header,
1302
+ /* @__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." }),
1303
+ /* @__PURE__ */ jsx5(
1304
+ "button",
1305
+ {
1306
+ type: "button",
1307
+ "data-testid": "sdk-drawer-frozen-goto",
1308
+ onClick: () => onTabChange?.("freeze"),
1309
+ 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",
1310
+ children: "Open the Freeze tab to unfreeze"
1311
+ }
1312
+ )
1313
+ ] });
1314
+ }
1161
1315
  if (effectiveTab === "edit") {
1162
- return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-edit", children: [
1316
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-edit", children: [
1163
1317
  header,
1164
- /* @__PURE__ */ jsx4(
1318
+ /* @__PURE__ */ jsx5(
1165
1319
  PianoRollEditor,
1166
1320
  {
1167
1321
  notes: editNotes ?? [],
@@ -1176,9 +1330,9 @@ function TrackDrawer({
1176
1330
  ] });
1177
1331
  }
1178
1332
  if (effectiveTab === "fx") {
1179
- return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-fx", children: [
1333
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-fx", children: [
1180
1334
  header,
1181
- /* @__PURE__ */ jsx4(
1335
+ /* @__PURE__ */ jsx5(
1182
1336
  FxToggleBar,
1183
1337
  {
1184
1338
  trackId,
@@ -1189,20 +1343,20 @@ function TrackDrawer({
1189
1343
  disabled: fxDisabled
1190
1344
  }
1191
1345
  ),
1192
- externalFxHost && /* @__PURE__ */ jsx4(TrackExternalFxSection, { host: externalFxHost, trackId, disabled: fxDisabled })
1346
+ externalFxHost && /* @__PURE__ */ jsx5(TrackExternalFxSection, { host: externalFxHost, trackId, disabled: fxDisabled })
1193
1347
  ] });
1194
1348
  }
1195
1349
  if (effectiveTab === "import") {
1196
1350
  const soundNoun = /preset/i.test(importSoundLabel ?? "") ? "preset" : /sample/i.test(importSoundLabel ?? "") ? "sample" : "sound";
1197
- return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-import", children: [
1351
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-import", children: [
1198
1352
  header,
1199
- /* @__PURE__ */ jsxs4("p", { className: "text-[11px] text-sas-muted/70 leading-snug", children: [
1353
+ /* @__PURE__ */ jsxs5("p", { className: "text-[11px] text-sas-muted/70 leading-snug", children: [
1200
1354
  "Copy the sound from a matching track in another scene \u2014 your MIDI stays, only the",
1201
1355
  " ",
1202
1356
  soundNoun,
1203
1357
  " changes."
1204
1358
  ] }),
1205
- /* @__PURE__ */ jsxs4(
1359
+ /* @__PURE__ */ jsxs5(
1206
1360
  "button",
1207
1361
  {
1208
1362
  type: "button",
@@ -1220,16 +1374,16 @@ function TrackDrawer({
1220
1374
  }
1221
1375
  if (effectiveTab === "history") {
1222
1376
  const order = history.map((_, i) => i).reverse();
1223
- return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
1377
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
1224
1378
  header,
1225
- history.length === 0 ? /* @__PURE__ */ jsx4(
1379
+ history.length === 0 ? /* @__PURE__ */ jsx5(
1226
1380
  "div",
1227
1381
  {
1228
1382
  className: "text-xs text-sas-muted/60 text-center py-3",
1229
1383
  "data-testid": "sdk-history-empty",
1230
1384
  children: "No sounds yet \u2014 shuffle to build history."
1231
1385
  }
1232
- ) : /* @__PURE__ */ jsx4(
1386
+ ) : /* @__PURE__ */ jsx5(
1233
1387
  "ul",
1234
1388
  {
1235
1389
  className: "flex flex-col gap-1 max-h-[160px] overflow-y-auto",
@@ -1237,8 +1391,8 @@ function TrackDrawer({
1237
1391
  children: order.map((i) => {
1238
1392
  const entry = history[i];
1239
1393
  const isCurrent = i === soundHistoryCursor;
1240
- return /* @__PURE__ */ jsxs4("li", { className: "flex items-center gap-1", children: [
1241
- /* @__PURE__ */ jsxs4(
1394
+ return /* @__PURE__ */ jsxs5("li", { className: "flex items-center gap-1", children: [
1395
+ /* @__PURE__ */ jsxs5(
1242
1396
  "button",
1243
1397
  {
1244
1398
  type: "button",
@@ -1248,12 +1402,12 @@ function TrackDrawer({
1248
1402
  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
1403
  title: isCurrent ? "Current sound" : `Restore: ${entry.label}`,
1250
1404
  children: [
1251
- /* @__PURE__ */ jsx4("span", { className: "truncate", children: entry.label }),
1252
- /* @__PURE__ */ jsx4("span", { className: "text-[10px] text-sas-muted/60 flex-shrink-0 ml-2", children: isCurrent ? "\u25CF current" : "restore" })
1405
+ /* @__PURE__ */ jsx5("span", { className: "truncate", children: entry.label }),
1406
+ /* @__PURE__ */ jsx5("span", { className: "text-[10px] text-sas-muted/60 flex-shrink-0 ml-2", children: isCurrent ? "\u25CF current" : "restore" })
1253
1407
  ]
1254
1408
  }
1255
1409
  ),
1256
- onToggleFavorite && /* @__PURE__ */ jsx4(
1410
+ onToggleFavorite && /* @__PURE__ */ jsx5(
1257
1411
  "button",
1258
1412
  {
1259
1413
  type: "button",
@@ -1271,10 +1425,10 @@ function TrackDrawer({
1271
1425
  ] });
1272
1426
  }
1273
1427
  if (effectiveTab === "pick" && editorStage) {
1274
- return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
1428
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
1275
1429
  header,
1276
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
1277
- /* @__PURE__ */ jsx4(
1430
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
1431
+ /* @__PURE__ */ jsx5(
1278
1432
  "button",
1279
1433
  {
1280
1434
  onClick: () => onBackToInstruments?.(),
@@ -1282,9 +1436,9 @@ function TrackDrawer({
1282
1436
  children: "\u2190 Back"
1283
1437
  }
1284
1438
  ),
1285
- /* @__PURE__ */ jsx4("span", { className: "text-xs text-sas-muted font-medium truncate flex-1", children: selectedInstrumentName ?? "Plugin" })
1439
+ /* @__PURE__ */ jsx5("span", { className: "text-xs text-sas-muted font-medium truncate flex-1", children: selectedInstrumentName ?? "Plugin" })
1286
1440
  ] }),
1287
- /* @__PURE__ */ jsx4(
1441
+ /* @__PURE__ */ jsx5(
1288
1442
  "button",
1289
1443
  {
1290
1444
  onClick: () => onShowEditor?.(),
@@ -1296,10 +1450,10 @@ function TrackDrawer({
1296
1450
  }
1297
1451
  const isDefaultSelected = currentPluginId === null;
1298
1452
  const isSelected = (pluginId) => pluginId === currentPluginId;
1299
- return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
1453
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
1300
1454
  header,
1301
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
1302
- /* @__PURE__ */ jsx4(
1455
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
1456
+ /* @__PURE__ */ jsx5(
1303
1457
  "input",
1304
1458
  {
1305
1459
  type: "text",
@@ -1309,7 +1463,7 @@ function TrackDrawer({
1309
1463
  className: "sas-input flex-1 px-2 py-1 text-xs"
1310
1464
  }
1311
1465
  ),
1312
- /* @__PURE__ */ jsx4(
1466
+ /* @__PURE__ */ jsx5(
1313
1467
  "button",
1314
1468
  {
1315
1469
  onClick: () => onRefresh?.(),
@@ -1320,43 +1474,43 @@ function TrackDrawer({
1320
1474
  }
1321
1475
  )
1322
1476
  ] }),
1323
- isLoading && instruments.length === 0 ? /* @__PURE__ */ jsx4("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ jsxs4("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
1324
- /* @__PURE__ */ jsxs4(
1477
+ 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: [
1478
+ /* @__PURE__ */ jsxs5(
1325
1479
  "button",
1326
1480
  {
1327
1481
  onClick: () => onSelect?.(SURGE_XT_DEFAULT_ID),
1328
1482
  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
1483
  title: "Surge XT \u2014 Default instrument",
1330
1484
  children: [
1331
- /* @__PURE__ */ jsxs4("span", { className: "text-xs font-medium truncate w-full", children: [
1485
+ /* @__PURE__ */ jsxs5("span", { className: "text-xs font-medium truncate w-full", children: [
1332
1486
  isDefaultSelected && "\u2713 ",
1333
1487
  "Surge XT"
1334
1488
  ] }),
1335
- /* @__PURE__ */ jsx4("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: "Default" })
1489
+ /* @__PURE__ */ jsx5("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: "Default" })
1336
1490
  ]
1337
1491
  },
1338
1492
  "__surge-xt-default__"
1339
1493
  ),
1340
1494
  filtered.map((inst) => {
1341
1495
  const selected = isSelected(inst.pluginId);
1342
- return /* @__PURE__ */ jsxs4(
1496
+ return /* @__PURE__ */ jsxs5(
1343
1497
  "button",
1344
1498
  {
1345
1499
  onClick: () => onSelect?.(inst.pluginId),
1346
1500
  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
1501
  title: `${inst.name} by ${inst.manufacturer} (${inst.type.toUpperCase()})${inst.missing ? " \u2014 MISSING" : ""}`,
1348
1502
  children: [
1349
- /* @__PURE__ */ jsxs4("span", { className: "text-xs font-medium truncate w-full", children: [
1503
+ /* @__PURE__ */ jsxs5("span", { className: "text-xs font-medium truncate w-full", children: [
1350
1504
  selected && "\u2713 ",
1351
1505
  inst.name
1352
1506
  ] }),
1353
- /* @__PURE__ */ jsx4("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: inst.manufacturer || inst.type.toUpperCase() })
1507
+ /* @__PURE__ */ jsx5("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: inst.manufacturer || inst.type.toUpperCase() })
1354
1508
  ]
1355
1509
  },
1356
1510
  inst.pluginId
1357
1511
  );
1358
1512
  }),
1359
- filtered.length === 0 && /* @__PURE__ */ jsx4("div", { className: "col-span-2 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No other plugins found" })
1513
+ 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
1514
  ] })
1361
1515
  ] });
1362
1516
  }
@@ -1365,9 +1519,9 @@ function TrackDrawer({
1365
1519
  import { useRef as useRef3 } from "react";
1366
1520
 
1367
1521
  // src/components/Modal.tsx
1368
- import { useEffect as useEffect2 } from "react";
1522
+ import { useEffect as useEffect3 } from "react";
1369
1523
  import { createPortal } from "react-dom";
1370
- import { jsx as jsx5 } from "react/jsx-runtime";
1524
+ import { jsx as jsx6 } from "react/jsx-runtime";
1371
1525
  function Modal({
1372
1526
  open,
1373
1527
  onClose,
@@ -1377,7 +1531,7 @@ function Modal({
1377
1531
  closeOnEscape = true,
1378
1532
  initialFocusRef
1379
1533
  }) {
1380
- useEffect2(() => {
1534
+ useEffect3(() => {
1381
1535
  if (!open) return void 0;
1382
1536
  const onKey = (e) => {
1383
1537
  if (closeOnEscape && e.key === "Escape") {
@@ -1391,7 +1545,7 @@ function Modal({
1391
1545
  }, [open, onClose, closeOnEscape, initialFocusRef]);
1392
1546
  if (!open) return null;
1393
1547
  return createPortal(
1394
- /* @__PURE__ */ jsx5(
1548
+ /* @__PURE__ */ jsx6(
1395
1549
  "div",
1396
1550
  {
1397
1551
  className: "fixed inset-0 z-[1000] flex items-center justify-center bg-black/60",
@@ -1405,7 +1559,7 @@ function Modal({
1405
1559
  }
1406
1560
 
1407
1561
  // src/components/ConfirmDialog.tsx
1408
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1562
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1409
1563
  function ConfirmDialog({
1410
1564
  open,
1411
1565
  title,
@@ -1418,7 +1572,7 @@ function ConfirmDialog({
1418
1572
  testIdPrefix = "confirm-dialog"
1419
1573
  }) {
1420
1574
  const cancelRef = useRef3(null);
1421
- return /* @__PURE__ */ jsx6(Modal, { open, onClose: onCancel, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs5(
1575
+ return /* @__PURE__ */ jsx7(Modal, { open, onClose: onCancel, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs6(
1422
1576
  "div",
1423
1577
  {
1424
1578
  className: "w-[360px] max-w-[90vw] flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
@@ -1428,8 +1582,8 @@ function ConfirmDialog({
1428
1582
  "aria-label": title,
1429
1583
  "data-testid": `${testIdPrefix}-modal`,
1430
1584
  children: [
1431
- /* @__PURE__ */ jsx6("div", { className: "px-4 py-3 border-b border-sas-border", children: /* @__PURE__ */ jsx6("span", { className: "text-sm font-medium text-sas-text", "data-testid": `${testIdPrefix}-title`, children: title }) }),
1432
- /* @__PURE__ */ jsx6(
1585
+ /* @__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 }) }),
1586
+ /* @__PURE__ */ jsx7(
1433
1587
  "div",
1434
1588
  {
1435
1589
  className: "px-4 py-3 text-xs text-sas-muted leading-relaxed break-words",
@@ -1437,8 +1591,8 @@ function ConfirmDialog({
1437
1591
  children: message
1438
1592
  }
1439
1593
  ),
1440
- /* @__PURE__ */ jsxs5("div", { className: "flex justify-end gap-2 px-4 py-3 border-t border-sas-border", children: [
1441
- /* @__PURE__ */ jsx6(
1594
+ /* @__PURE__ */ jsxs6("div", { className: "flex justify-end gap-2 px-4 py-3 border-t border-sas-border", children: [
1595
+ /* @__PURE__ */ jsx7(
1442
1596
  "button",
1443
1597
  {
1444
1598
  ref: cancelRef,
@@ -1449,7 +1603,7 @@ function ConfirmDialog({
1449
1603
  children: cancelLabel
1450
1604
  }
1451
1605
  ),
1452
- /* @__PURE__ */ jsx6(
1606
+ /* @__PURE__ */ jsx7(
1453
1607
  "button",
1454
1608
  {
1455
1609
  type: "button",
@@ -1466,7 +1620,7 @@ function ConfirmDialog({
1466
1620
  }
1467
1621
 
1468
1622
  // src/components/LevelMeter.tsx
1469
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1623
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1470
1624
  var COLOR_GREEN = "#2BD576";
1471
1625
  var COLOR_ORANGE = "#F5A623";
1472
1626
  var COLOR_RED = "#FF4D5E";
@@ -1494,7 +1648,7 @@ var LevelMeter = ({
1494
1648
  const widthPct = active ? dbToPct(peakDb) : 0;
1495
1649
  const showPeak = peakHoldDb != null && active && peakHoldDb > -60;
1496
1650
  const peakHoldPct = showPeak ? dbToPct(peakHoldDb) : 0;
1497
- return /* @__PURE__ */ jsxs6(
1651
+ return /* @__PURE__ */ jsxs7(
1498
1652
  "div",
1499
1653
  {
1500
1654
  className: `sas-level-meter ${className ?? ""}`,
@@ -1505,7 +1659,7 @@ var LevelMeter = ({
1505
1659
  gap: compact ? 0 : 6
1506
1660
  },
1507
1661
  children: [
1508
- /* @__PURE__ */ jsxs6(
1662
+ /* @__PURE__ */ jsxs7(
1509
1663
  "div",
1510
1664
  {
1511
1665
  style: {
@@ -1519,8 +1673,8 @@ var LevelMeter = ({
1519
1673
  minWidth: compact ? 0 : 60
1520
1674
  },
1521
1675
  children: [
1522
- /* @__PURE__ */ jsx7("div", { style: { position: "absolute", inset: 0, background: METER_GRADIENT } }),
1523
- /* @__PURE__ */ jsx7(
1676
+ /* @__PURE__ */ jsx8("div", { style: { position: "absolute", inset: 0, background: METER_GRADIENT } }),
1677
+ /* @__PURE__ */ jsx8(
1524
1678
  "div",
1525
1679
  {
1526
1680
  style: {
@@ -1534,7 +1688,7 @@ var LevelMeter = ({
1534
1688
  }
1535
1689
  }
1536
1690
  ),
1537
- /* @__PURE__ */ jsx7(
1691
+ /* @__PURE__ */ jsx8(
1538
1692
  "div",
1539
1693
  {
1540
1694
  "data-testid": `${id}-segments`,
@@ -1547,7 +1701,7 @@ var LevelMeter = ({
1547
1701
  }
1548
1702
  }
1549
1703
  ),
1550
- showPeak && /* @__PURE__ */ jsx7(
1704
+ showPeak && /* @__PURE__ */ jsx8(
1551
1705
  "div",
1552
1706
  {
1553
1707
  "data-testid": `${id}-peak`,
@@ -1568,7 +1722,7 @@ var LevelMeter = ({
1568
1722
  ]
1569
1723
  }
1570
1724
  ),
1571
- !compact && /* @__PURE__ */ jsx7(
1725
+ !compact && /* @__PURE__ */ jsx8(
1572
1726
  "span",
1573
1727
  {
1574
1728
  style: {
@@ -1581,7 +1735,7 @@ var LevelMeter = ({
1581
1735
  children: active && peakDb > -120 ? `${peakDb.toFixed(0)} dB` : "\u2014"
1582
1736
  }
1583
1737
  ),
1584
- clipped && /* @__PURE__ */ jsx7(
1738
+ clipped && /* @__PURE__ */ jsx8(
1585
1739
  "span",
1586
1740
  {
1587
1741
  "data-testid": `${id}-clip`,
@@ -1606,7 +1760,7 @@ var LevelMeter = ({
1606
1760
  };
1607
1761
 
1608
1762
  // src/hooks/useTrackLevels.ts
1609
- import { useEffect as useEffect3, useRef as useRef4, useState as useState5 } from "react";
1763
+ import { useEffect as useEffect4, useRef as useRef4, useState as useState6 } from "react";
1610
1764
  var meterDiagRLast = /* @__PURE__ */ new Map();
1611
1765
  var POLL_INTERVAL_MS = 33;
1612
1766
  var HIDDEN_RECHECK_MS = 250;
@@ -1631,7 +1785,7 @@ function useTrackLevels(host, enabled = true) {
1631
1785
  }
1632
1786
  };
1633
1787
  }
1634
- useEffect3(() => {
1788
+ useEffect4(() => {
1635
1789
  const notify = () => {
1636
1790
  listenersRef.current.forEach((l) => l());
1637
1791
  };
@@ -1701,8 +1855,8 @@ function sameLevel(a, b) {
1701
1855
  return a.peakDb === b.peakDb && a.clipped === b.clipped;
1702
1856
  }
1703
1857
  function useTrackLevel(handle, trackId) {
1704
- const [level, setLevel] = useState5(null);
1705
- useEffect3(() => {
1858
+ const [level, setLevel] = useState6(null);
1859
+ useEffect4(() => {
1706
1860
  if (!handle) {
1707
1861
  setLevel(null);
1708
1862
  return;
@@ -1726,11 +1880,11 @@ function sameMeter(a, b) {
1726
1880
  return a.active === b.active && a.clipped === b.clipped && a.peakDb === b.peakDb && Math.round(a.peakHoldDb * 2) === Math.round(b.peakHoldDb * 2);
1727
1881
  }
1728
1882
  function useTrackMeter(handle, trackId) {
1729
- const [view, setView] = useState5(IDLE_METER_VIEW);
1883
+ const [view, setView] = useState6(IDLE_METER_VIEW);
1730
1884
  const heldDbRef = useRef4(METER_FLOOR_DB);
1731
1885
  const heldAtRef = useRef4(0);
1732
1886
  const lastTickRef = useRef4(0);
1733
- useEffect3(() => {
1887
+ useEffect4(() => {
1734
1888
  if (!handle) {
1735
1889
  heldDbRef.current = METER_FLOOR_DB;
1736
1890
  lastTickRef.current = 0;
@@ -1773,8 +1927,8 @@ function useTrackMeter(handle, trackId) {
1773
1927
  return view;
1774
1928
  }
1775
1929
  function useTransportPlaying(host) {
1776
- const [playing, setPlaying] = useState5(false);
1777
- useEffect3(() => {
1930
+ const [playing, setPlaying] = useState6(false);
1931
+ useEffect4(() => {
1778
1932
  if (!host) {
1779
1933
  setPlaying(false);
1780
1934
  return;
@@ -1802,7 +1956,7 @@ function useTransportPlaying(host) {
1802
1956
  }
1803
1957
 
1804
1958
  // src/components/TrackMeterStrip.tsx
1805
- import { jsx as jsx8 } from "react/jsx-runtime";
1959
+ import { jsx as jsx9 } from "react/jsx-runtime";
1806
1960
  var TrackMeterStrip = ({
1807
1961
  levels,
1808
1962
  trackId,
@@ -1810,12 +1964,12 @@ var TrackMeterStrip = ({
1810
1964
  className
1811
1965
  }) => {
1812
1966
  const meter = useTrackMeter(levels, trackId);
1813
- return /* @__PURE__ */ jsx8(
1967
+ return /* @__PURE__ */ jsx9(
1814
1968
  "div",
1815
1969
  {
1816
1970
  "data-testid": "sdk-track-meter",
1817
1971
  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__ */ jsx8(
1972
+ children: /* @__PURE__ */ jsx9(
1819
1973
  LevelMeter,
1820
1974
  {
1821
1975
  compact: true,
@@ -1831,7 +1985,7 @@ var TrackMeterStrip = ({
1831
1985
  };
1832
1986
 
1833
1987
  // src/components/VolumeSlider.tsx
1834
- import { useCallback as useCallback3, useState as useState6, useRef as useRef5, useEffect as useEffect4 } from "react";
1988
+ import { useCallback as useCallback4, useState as useState7, useRef as useRef5, useEffect as useEffect5 } from "react";
1835
1989
 
1836
1990
  // src/utils/volume-conversion.ts
1837
1991
  var SLIDER_UNITY = 0.75;
@@ -1853,7 +2007,7 @@ function dbToSlider(db) {
1853
2007
  }
1854
2008
 
1855
2009
  // src/components/VolumeSlider.tsx
1856
- import { jsx as jsx9 } from "react/jsx-runtime";
2010
+ import { jsx as jsx10 } from "react/jsx-runtime";
1857
2011
  function formatDb(value) {
1858
2012
  const db = sliderToDb(value);
1859
2013
  if (db <= -60) return "-\u221E dB";
@@ -1863,10 +2017,10 @@ function formatDb(value) {
1863
2017
  function useDebouncedCallback(callback, delay) {
1864
2018
  const timeoutRef = useRef5(null);
1865
2019
  const callbackRef = useRef5(callback);
1866
- useEffect4(() => {
2020
+ useEffect5(() => {
1867
2021
  callbackRef.current = callback;
1868
2022
  }, [callback]);
1869
- const debouncedCallback = useCallback3(
2023
+ const debouncedCallback = useCallback4(
1870
2024
  (...args) => {
1871
2025
  if (timeoutRef.current) {
1872
2026
  clearTimeout(timeoutRef.current);
@@ -1877,7 +2031,7 @@ function useDebouncedCallback(callback, delay) {
1877
2031
  },
1878
2032
  [delay]
1879
2033
  );
1880
- useEffect4(() => {
2034
+ useEffect5(() => {
1881
2035
  return () => {
1882
2036
  if (timeoutRef.current) {
1883
2037
  clearTimeout(timeoutRef.current);
@@ -1892,15 +2046,15 @@ var VolumeSlider = ({
1892
2046
  disabled = false,
1893
2047
  className = ""
1894
2048
  }) => {
1895
- const [localValue, setLocalValue] = useState6(value);
1896
- const [isDragging, setIsDragging] = useState6(false);
1897
- useEffect4(() => {
2049
+ const [localValue, setLocalValue] = useState7(value);
2050
+ const [isDragging, setIsDragging] = useState7(false);
2051
+ useEffect5(() => {
1898
2052
  if (!isDragging) {
1899
2053
  setLocalValue(value);
1900
2054
  }
1901
2055
  }, [value, isDragging]);
1902
2056
  const debouncedOnChange = useDebouncedCallback(onChange, 50);
1903
- const handleChange = useCallback3(
2057
+ const handleChange = useCallback4(
1904
2058
  (e) => {
1905
2059
  const newValue = parseFloat(e.target.value);
1906
2060
  setLocalValue(newValue);
@@ -1908,19 +2062,19 @@ var VolumeSlider = ({
1908
2062
  },
1909
2063
  [debouncedOnChange]
1910
2064
  );
1911
- const handleMouseDown = useCallback3(() => {
2065
+ const handleMouseDown = useCallback4(() => {
1912
2066
  setIsDragging(true);
1913
2067
  }, []);
1914
- const handleMouseUp = useCallback3(() => {
2068
+ const handleMouseUp = useCallback4(() => {
1915
2069
  setIsDragging(false);
1916
2070
  onChange(localValue);
1917
2071
  }, [localValue, onChange]);
1918
- return /* @__PURE__ */ jsx9(
2072
+ return /* @__PURE__ */ jsx10(
1919
2073
  "div",
1920
2074
  {
1921
2075
  className: `flex items-center ${className}`,
1922
2076
  title: `Volume: ${formatDb(localValue)}`,
1923
- children: /* @__PURE__ */ jsx9(
2077
+ children: /* @__PURE__ */ jsx10(
1924
2078
  "input",
1925
2079
  {
1926
2080
  type: "range",
@@ -1960,8 +2114,8 @@ var VolumeSlider = ({
1960
2114
  };
1961
2115
 
1962
2116
  // src/components/PanSlider.tsx
1963
- import { useCallback as useCallback4, useState as useState7, useRef as useRef6, useEffect as useEffect5 } from "react";
1964
- import { jsx as jsx10 } from "react/jsx-runtime";
2117
+ import { useCallback as useCallback5, useState as useState8, useRef as useRef6, useEffect as useEffect6 } from "react";
2118
+ import { jsx as jsx11 } from "react/jsx-runtime";
1965
2119
  function toPanDisplay(value) {
1966
2120
  if (Math.abs(value) < 0.02) {
1967
2121
  return "Center";
@@ -1972,10 +2126,10 @@ function toPanDisplay(value) {
1972
2126
  function useDebouncedCallback2(callback, delay) {
1973
2127
  const timeoutRef = useRef6(null);
1974
2128
  const callbackRef = useRef6(callback);
1975
- useEffect5(() => {
2129
+ useEffect6(() => {
1976
2130
  callbackRef.current = callback;
1977
2131
  }, [callback]);
1978
- const debouncedCallback = useCallback4(
2132
+ const debouncedCallback = useCallback5(
1979
2133
  (...args) => {
1980
2134
  if (timeoutRef.current) {
1981
2135
  clearTimeout(timeoutRef.current);
@@ -1986,7 +2140,7 @@ function useDebouncedCallback2(callback, delay) {
1986
2140
  },
1987
2141
  [delay]
1988
2142
  );
1989
- useEffect5(() => {
2143
+ useEffect6(() => {
1990
2144
  return () => {
1991
2145
  if (timeoutRef.current) {
1992
2146
  clearTimeout(timeoutRef.current);
@@ -2001,15 +2155,15 @@ var PanSlider = ({
2001
2155
  disabled = false,
2002
2156
  className = ""
2003
2157
  }) => {
2004
- const [localValue, setLocalValue] = useState7(value);
2005
- const [isDragging, setIsDragging] = useState7(false);
2006
- useEffect5(() => {
2158
+ const [localValue, setLocalValue] = useState8(value);
2159
+ const [isDragging, setIsDragging] = useState8(false);
2160
+ useEffect6(() => {
2007
2161
  if (!isDragging) {
2008
2162
  setLocalValue(value);
2009
2163
  }
2010
2164
  }, [value, isDragging]);
2011
2165
  const debouncedOnChange = useDebouncedCallback2(onChange, 50);
2012
- const handleChange = useCallback4(
2166
+ const handleChange = useCallback5(
2013
2167
  (e) => {
2014
2168
  const newValue = parseFloat(e.target.value);
2015
2169
  setLocalValue(newValue);
@@ -2017,23 +2171,23 @@ var PanSlider = ({
2017
2171
  },
2018
2172
  [debouncedOnChange]
2019
2173
  );
2020
- const handleMouseDown = useCallback4(() => {
2174
+ const handleMouseDown = useCallback5(() => {
2021
2175
  setIsDragging(true);
2022
2176
  }, []);
2023
- const handleMouseUp = useCallback4(() => {
2177
+ const handleMouseUp = useCallback5(() => {
2024
2178
  setIsDragging(false);
2025
2179
  onChange(localValue);
2026
2180
  }, [localValue, onChange]);
2027
- const handleDoubleClick = useCallback4(() => {
2181
+ const handleDoubleClick = useCallback5(() => {
2028
2182
  setLocalValue(0);
2029
2183
  onChange(0);
2030
2184
  }, [onChange]);
2031
- return /* @__PURE__ */ jsx10(
2185
+ return /* @__PURE__ */ jsx11(
2032
2186
  "div",
2033
2187
  {
2034
2188
  className: `flex items-center ${className}`,
2035
2189
  title: `Pan: ${toPanDisplay(localValue)}`,
2036
- children: /* @__PURE__ */ jsx10(
2190
+ children: /* @__PURE__ */ jsx11(
2037
2191
  "input",
2038
2192
  {
2039
2193
  type: "range",
@@ -2074,8 +2228,8 @@ var PanSlider = ({
2074
2228
  };
2075
2229
 
2076
2230
  // src/components/SorceryProgressBar.tsx
2077
- import { useState as useState8, useEffect as useEffect6, useRef as useRef7 } from "react";
2078
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
2231
+ import { useState as useState9, useEffect as useEffect7, useRef as useRef7 } from "react";
2232
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
2079
2233
  function calculateTimeBasedTarget(elapsedMs, estimatedDurationMs) {
2080
2234
  const t = elapsedMs / estimatedDurationMs;
2081
2235
  if (t <= 0) return 0;
@@ -2120,7 +2274,7 @@ function SorceryProgressBar({
2120
2274
  onProgressChange,
2121
2275
  estimatedDurationMs
2122
2276
  }) {
2123
- const [progress, setProgress] = useState8(initialProgress);
2277
+ const [progress, setProgress] = useState9(initialProgress);
2124
2278
  const timerRef = useRef7(null);
2125
2279
  const isLoadingRef = useRef7(false);
2126
2280
  const hasStartedRef = useRef7(false);
@@ -2133,7 +2287,7 @@ function SorceryProgressBar({
2133
2287
  initialProgressRef.current = initialProgress;
2134
2288
  const estimatedDurationMsRef = useRef7(estimatedDurationMs);
2135
2289
  estimatedDurationMsRef.current = estimatedDurationMs;
2136
- useEffect6(() => {
2290
+ useEffect7(() => {
2137
2291
  const wasLoading = isLoadingRef.current;
2138
2292
  isLoadingRef.current = isLoading;
2139
2293
  if (isLoading && !wasLoading) {
@@ -2195,12 +2349,12 @@ function SorceryProgressBar({
2195
2349
  const displayProgress = Math.floor(progress);
2196
2350
  const isComplete = !isLoading && progress === 100;
2197
2351
  const transitionDuration = progress < 50 ? "300ms" : progress < 80 ? "500ms" : "700ms";
2198
- return /* @__PURE__ */ jsxs7(
2352
+ return /* @__PURE__ */ jsxs8(
2199
2353
  "div",
2200
2354
  {
2201
2355
  className: `relative w-full ${heightClass} bg-sas-panel-alt border border-sas-border rounded-sm overflow-hidden shadow-inner`,
2202
2356
  children: [
2203
- /* @__PURE__ */ jsx11(
2357
+ /* @__PURE__ */ jsx12(
2204
2358
  "div",
2205
2359
  {
2206
2360
  className: `
@@ -2218,13 +2372,13 @@ function SorceryProgressBar({
2218
2372
  }
2219
2373
  }
2220
2374
  ),
2221
- /* @__PURE__ */ jsx11("div", { className: "absolute inset-0 flex items-center justify-center", children: isLoading && progress < 100 ? /* @__PURE__ */ jsxs7("span", { className: "font-mono text-xs text-sas-accent font-bold drop-shadow-md tracking-wider", children: [
2375
+ /* @__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
2376
  statusText,
2223
2377
  " ",
2224
2378
  displayProgress,
2225
2379
  "%"
2226
- ] }) : isComplete ? /* @__PURE__ */ jsx11("span", { className: "font-mono text-xs text-sas-text font-bold drop-shadow-md tracking-wider", children: completeText }) : null }),
2227
- /* @__PURE__ */ jsx11(
2380
+ ] }) : isComplete ? /* @__PURE__ */ jsx12("span", { className: "font-mono text-xs text-sas-text font-bold drop-shadow-md tracking-wider", children: completeText }) : null }),
2381
+ /* @__PURE__ */ jsx12(
2228
2382
  "div",
2229
2383
  {
2230
2384
  className: "absolute inset-0 pointer-events-none opacity-10",
@@ -2310,7 +2464,7 @@ function parseLLMNoteResponse(content) {
2310
2464
  }
2311
2465
 
2312
2466
  // src/components/TrackRow.tsx
2313
- import { Fragment, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
2467
+ import { Fragment, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
2314
2468
  function TrackRow({
2315
2469
  track,
2316
2470
  prompt,
@@ -2371,6 +2525,8 @@ function TrackRow({
2371
2525
  }) {
2372
2526
  const { muted: isMuted, solo: isSoloed, volume: currentVolume, pan: currentPan } = runtimeState;
2373
2527
  const [confirmDelete, setConfirmDelete] = React9.useState(false);
2528
+ const freeze = useTrackFreeze(externalFxHost, track.id);
2529
+ const freezeBadge = freeze.state?.frozen === true ? freeze.state.missingDeps.length > 0 ? "missing" : freeze.state.stale ? "stale" : "frozen" : null;
2374
2530
  const needsGeneration = !!(prompt?.trim() && !hasMidi && !isGenerating);
2375
2531
  const hasFxActive = Object.values(fxDetailState).some(
2376
2532
  (d) => d.enabled
@@ -2380,8 +2536,8 @@ function TrackRow({
2380
2536
  const handleKeyDown = promptEnterToGenerate(() => onGenerate?.(), !onGenerate);
2381
2537
  const borderColorStyle = needsGeneration ? void 0 : accentColor;
2382
2538
  const borderClass = needsGeneration ? "border-amber-400 animate-pulse" : "border-sas-border";
2383
- return /* @__PURE__ */ jsxs8("div", { "data-testid": "sdk-track-row-wrapper", className: "w-full", ...drag?.rowProps ?? {}, children: [
2384
- /* @__PURE__ */ jsxs8(
2539
+ return /* @__PURE__ */ jsxs9("div", { "data-testid": "sdk-track-row-wrapper", className: "w-full", ...drag?.rowProps ?? {}, children: [
2540
+ /* @__PURE__ */ jsxs9(
2385
2541
  "div",
2386
2542
  {
2387
2543
  "data-testid": "sdk-track-row",
@@ -2391,7 +2547,7 @@ function TrackRow({
2391
2547
  borderLeftWidth: "3px"
2392
2548
  },
2393
2549
  children: [
2394
- drag && /* @__PURE__ */ jsx12(
2550
+ drag && /* @__PURE__ */ jsx13(
2395
2551
  "div",
2396
2552
  {
2397
2553
  "data-testid": "sdk-drag-handle",
@@ -2399,10 +2555,10 @@ function TrackRow({
2399
2555
  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
2556
  title: "Drag to reorder",
2401
2557
  "aria-label": "Drag to reorder track",
2402
- children: /* @__PURE__ */ jsx12(GripVertical, { className: "w-3.5 h-3.5", strokeWidth: 2 })
2558
+ children: /* @__PURE__ */ jsx13(GripVertical, { className: "w-3.5 h-3.5", strokeWidth: 2 })
2403
2559
  }
2404
2560
  ),
2405
- isGenerating && /* @__PURE__ */ jsx12("div", { className: "absolute left-0 top-0 bottom-0 right-44 z-20", children: /* @__PURE__ */ jsx12(
2561
+ isGenerating && /* @__PURE__ */ jsx13("div", { className: "absolute left-0 top-0 bottom-0 right-44 z-20", children: /* @__PURE__ */ jsx13(
2406
2562
  SorceryProgressBar,
2407
2563
  {
2408
2564
  isLoading: true,
@@ -2413,14 +2569,14 @@ function TrackRow({
2413
2569
  estimatedDurationMs: estimatedGenerationMs
2414
2570
  }
2415
2571
  ) }),
2416
- /* @__PURE__ */ jsxs8(
2572
+ /* @__PURE__ */ jsxs9(
2417
2573
  "div",
2418
2574
  {
2419
2575
  "data-testid": "sdk-track-content",
2420
2576
  className: `flex flex-col flex-1 min-w-0 relative z-10 transition-opacity ${soloedOut ? "opacity-40" : ""}`,
2421
2577
  title: soloedOut ? "Silenced \u2014 another track is soloed" : void 0,
2422
2578
  children: [
2423
- contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ jsx12(
2579
+ contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ jsx13(
2424
2580
  "input",
2425
2581
  {
2426
2582
  type: "text",
@@ -2433,10 +2589,10 @@ function TrackRow({
2433
2589
  className: "sas-input w-full px-2 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed"
2434
2590
  }
2435
2591
  ) : null,
2436
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2 mt-1", children: [
2437
- track.name && /* @__PURE__ */ jsx12("span", { className: "text-[10px] text-sas-muted/60 truncate pl-2 flex-shrink-0 max-w-[80px]", title: track.name, children: track.name }),
2438
- /* @__PURE__ */ jsx12("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "vol:" }),
2439
- /* @__PURE__ */ jsx12(
2592
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 mt-1", children: [
2593
+ 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 }),
2594
+ /* @__PURE__ */ jsx13("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "vol:" }),
2595
+ /* @__PURE__ */ jsx13(
2440
2596
  VolumeSlider,
2441
2597
  {
2442
2598
  value: currentVolume,
@@ -2445,8 +2601,8 @@ function TrackRow({
2445
2601
  className: "flex-1 min-w-[40px]"
2446
2602
  }
2447
2603
  ),
2448
- /* @__PURE__ */ jsx12("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "pan:" }),
2449
- /* @__PURE__ */ jsx12(
2604
+ /* @__PURE__ */ jsx13("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "pan:" }),
2605
+ /* @__PURE__ */ jsx13(
2450
2606
  PanSlider,
2451
2607
  {
2452
2608
  value: currentPan,
@@ -2459,27 +2615,27 @@ function TrackRow({
2459
2615
  ]
2460
2616
  }
2461
2617
  ),
2462
- error && /* @__PURE__ */ jsx12(
2618
+ error && /* @__PURE__ */ jsx13(
2463
2619
  "div",
2464
2620
  {
2465
2621
  "data-testid": "sdk-error-indicator",
2466
2622
  className: "flex-shrink-0 relative z-10 self-stretch flex items-center px-1 group cursor-help",
2467
2623
  title: error,
2468
- children: /* @__PURE__ */ jsxs8("div", { className: "relative", children: [
2469
- /* @__PURE__ */ jsx12(
2624
+ children: /* @__PURE__ */ jsxs9("div", { className: "relative", children: [
2625
+ /* @__PURE__ */ jsx13(
2470
2626
  AlertCircle,
2471
2627
  {
2472
2628
  className: "w-5 h-5 text-red-500 animate-pulse",
2473
2629
  strokeWidth: 2.5
2474
2630
  }
2475
2631
  ),
2476
- /* @__PURE__ */ jsx12("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 })
2632
+ /* @__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
2633
  ] })
2478
2634
  }
2479
2635
  ),
2480
- /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-0.5 flex-shrink-0 relative z-30 justify-center", children: [
2481
- /* @__PURE__ */ jsxs8("div", { className: "flex gap-1 items-center", children: [
2482
- onGenerate && /* @__PURE__ */ jsx12(
2636
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-0.5 flex-shrink-0 relative z-30 justify-center", children: [
2637
+ /* @__PURE__ */ jsxs9("div", { className: "flex gap-1 items-center", children: [
2638
+ onGenerate && /* @__PURE__ */ jsx13(
2483
2639
  "button",
2484
2640
  {
2485
2641
  "data-testid": "sdk-generate-button",
@@ -2490,7 +2646,7 @@ function TrackRow({
2490
2646
  children: "Create"
2491
2647
  }
2492
2648
  ),
2493
- onCopy && /* @__PURE__ */ jsx12(
2649
+ onCopy && /* @__PURE__ */ jsx13(
2494
2650
  "button",
2495
2651
  {
2496
2652
  "data-testid": "sdk-copy-button",
@@ -2501,7 +2657,7 @@ function TrackRow({
2501
2657
  children: "Copy"
2502
2658
  }
2503
2659
  ),
2504
- /* @__PURE__ */ jsx12(
2660
+ /* @__PURE__ */ jsx13(
2505
2661
  "button",
2506
2662
  {
2507
2663
  "data-testid": "sdk-mute-button",
@@ -2511,7 +2667,7 @@ function TrackRow({
2511
2667
  children: "M"
2512
2668
  }
2513
2669
  ),
2514
- onDelete && /* @__PURE__ */ jsx12(
2670
+ onDelete && /* @__PURE__ */ jsx13(
2515
2671
  "button",
2516
2672
  {
2517
2673
  "data-testid": "sdk-delete-button",
@@ -2522,8 +2678,8 @@ function TrackRow({
2522
2678
  }
2523
2679
  )
2524
2680
  ] }),
2525
- /* @__PURE__ */ jsxs8("div", { className: "flex gap-1 items-center", children: [
2526
- onShuffle && /* @__PURE__ */ jsx12(
2681
+ /* @__PURE__ */ jsxs9("div", { className: "flex gap-1 items-center", children: [
2682
+ onShuffle && /* @__PURE__ */ jsx13(
2527
2683
  "button",
2528
2684
  {
2529
2685
  "data-testid": "sdk-shuffle-button",
@@ -2534,7 +2690,16 @@ function TrackRow({
2534
2690
  children: "Shuffle"
2535
2691
  }
2536
2692
  ),
2537
- onToggleFxDrawer && /* @__PURE__ */ jsx12(
2693
+ freezeBadge && /* @__PURE__ */ jsx13(
2694
+ "span",
2695
+ {
2696
+ "data-testid": "sdk-track-freeze-badge",
2697
+ className: `px-1 py-0.5 text-xs leading-none rounded-sm self-center ${freezeBadge === "frozen" ? "text-sas-accent" : "text-amber-400"}`,
2698
+ 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(", ")}`,
2699
+ children: freezeBadge === "frozen" ? "\u2744" : freezeBadge === "stale" ? "\u26A0\u2744" : "\u2744!"
2700
+ }
2701
+ ),
2702
+ onToggleFxDrawer && /* @__PURE__ */ jsx13(
2538
2703
  "button",
2539
2704
  {
2540
2705
  "data-testid": "sdk-fx-button",
@@ -2545,7 +2710,7 @@ function TrackRow({
2545
2710
  children: "FX"
2546
2711
  }
2547
2712
  ),
2548
- /* @__PURE__ */ jsx12(
2713
+ /* @__PURE__ */ jsx13(
2549
2714
  "button",
2550
2715
  {
2551
2716
  "data-testid": "sdk-solo-button",
@@ -2556,7 +2721,7 @@ function TrackRow({
2556
2721
  children: "S"
2557
2722
  }
2558
2723
  ),
2559
- onToggleDrawer && /* @__PURE__ */ jsx12(
2724
+ onToggleDrawer && /* @__PURE__ */ jsx13(
2560
2725
  "button",
2561
2726
  {
2562
2727
  "data-testid": "sdk-plugin-button",
@@ -2564,7 +2729,7 @@ function TrackRow({
2564
2729
  disabled: isGenerating,
2565
2730
  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
2731
  title: `Sound \u2014 presets & history${instrumentMissing ? " (instrument missing)" : ""}`,
2567
- children: /* @__PURE__ */ jsx12(ChevronDown, { className: "w-3 h-3", strokeWidth: 2.5 })
2732
+ children: /* @__PURE__ */ jsx13(ChevronDown, { className: "w-3 h-3", strokeWidth: 2.5 })
2568
2733
  }
2569
2734
  )
2570
2735
  ] })
@@ -2572,13 +2737,13 @@ function TrackRow({
2572
2737
  ]
2573
2738
  }
2574
2739
  ),
2575
- levels && /* @__PURE__ */ jsx12(TrackMeterStrip, { levels, trackId: track.id, roundBottom: !drawerOpen }),
2576
- drawerOpen && /* @__PURE__ */ jsx12(
2740
+ levels && /* @__PURE__ */ jsx13(TrackMeterStrip, { levels, trackId: track.id, roundBottom: !drawerOpen }),
2741
+ drawerOpen && /* @__PURE__ */ jsx13(
2577
2742
  "div",
2578
2743
  {
2579
2744
  "data-testid": "sdk-track-drawer",
2580
2745
  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__ */ jsx12(
2746
+ children: /* @__PURE__ */ jsx13(
2582
2747
  TrackDrawer,
2583
2748
  {
2584
2749
  activeTab: drawerTab,
@@ -2589,6 +2754,7 @@ function TrackRow({
2589
2754
  onFxPresetChange,
2590
2755
  onFxDryWetChange,
2591
2756
  externalFxHost,
2757
+ freeze,
2592
2758
  fxDisabled: isGenerating,
2593
2759
  instruments: availableInstruments,
2594
2760
  currentPluginId: currentInstrumentPluginId ?? null,
@@ -2615,13 +2781,13 @@ function TrackRow({
2615
2781
  )
2616
2782
  }
2617
2783
  ),
2618
- /* @__PURE__ */ jsx12(
2784
+ /* @__PURE__ */ jsx13(
2619
2785
  ConfirmDialog,
2620
2786
  {
2621
2787
  open: confirmDelete,
2622
2788
  title: "Delete track?",
2623
- message: /* @__PURE__ */ jsxs8(Fragment, { children: [
2624
- /* @__PURE__ */ jsx12("span", { className: "text-sas-text", children: track.name?.trim() || "This track" }),
2789
+ message: /* @__PURE__ */ jsxs9(Fragment, { children: [
2790
+ /* @__PURE__ */ jsx13("span", { className: "text-sas-text", children: track.name?.trim() || "This track" }),
2625
2791
  " will be permanently removed from this scene. This cannot be undone."
2626
2792
  ] }),
2627
2793
  confirmLabel: "Delete",
@@ -2638,12 +2804,12 @@ function TrackRow({
2638
2804
 
2639
2805
  // src/components/CrossfadeTrackRow.tsx
2640
2806
  import React10 from "react";
2641
- import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
2807
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
2642
2808
  function LayerCaption({ tag, layer }) {
2643
- return /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
2644
- /* @__PURE__ */ jsx13("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
2645
- /* @__PURE__ */ jsx13("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
2646
- layer.soundLabel && /* @__PURE__ */ jsxs9("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
2809
+ return /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
2810
+ /* @__PURE__ */ jsx14("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
2811
+ /* @__PURE__ */ jsx14("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
2812
+ layer.soundLabel && /* @__PURE__ */ jsxs10("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
2647
2813
  "\xB7 ",
2648
2814
  layer.soundLabel
2649
2815
  ] })
@@ -2663,7 +2829,7 @@ function CrossfadeTrackRow({
2663
2829
  accentColor = "#9333EA"
2664
2830
  }) {
2665
2831
  const [confirmDelete, setConfirmDelete] = React10.useState(false);
2666
- const renderLayer = (layer, slot, tag) => /* @__PURE__ */ jsx13(
2832
+ const renderLayer = (layer, slot, tag) => /* @__PURE__ */ jsx14(
2667
2833
  TrackRow,
2668
2834
  {
2669
2835
  track: { id: layer.trackId, name: "", role: layer.role },
@@ -2673,23 +2839,23 @@ function CrossfadeTrackRow({
2673
2839
  drawerTab: "fx",
2674
2840
  levels,
2675
2841
  accentColor,
2676
- contentSlot: /* @__PURE__ */ jsx13(LayerCaption, { tag, layer }),
2842
+ contentSlot: /* @__PURE__ */ jsx14(LayerCaption, { tag, layer }),
2677
2843
  onMuteToggle,
2678
2844
  onSoloToggle,
2679
2845
  onVolumeChange: (v) => onVolumeChange(slot, v),
2680
2846
  onPanChange: (p) => onPanChange(slot, p)
2681
2847
  }
2682
2848
  );
2683
- return /* @__PURE__ */ jsxs9(
2849
+ return /* @__PURE__ */ jsxs10(
2684
2850
  "div",
2685
2851
  {
2686
2852
  "data-testid": "crossfade-track-row",
2687
2853
  className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
2688
2854
  style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
2689
2855
  children: [
2690
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
2691
- /* @__PURE__ */ jsx13("span", { className: "text-[10px] font-bold uppercase tracking-wide", style: { color: accentColor }, children: "\u21C4 Crossfade" }),
2692
- /* @__PURE__ */ jsx13(
2856
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
2857
+ /* @__PURE__ */ jsx14("span", { className: "text-[10px] font-bold uppercase tracking-wide", style: { color: accentColor }, children: "\u21C4 Crossfade" }),
2858
+ /* @__PURE__ */ jsx14(
2693
2859
  "button",
2694
2860
  {
2695
2861
  "data-testid": "crossfade-delete-button",
@@ -2702,8 +2868,8 @@ function CrossfadeTrackRow({
2702
2868
  )
2703
2869
  ] }),
2704
2870
  renderLayer(origin, "origin", "Origin"),
2705
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "crossfade-slider-row", children: [
2706
- /* @__PURE__ */ jsx13(
2871
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "crossfade-slider-row", children: [
2872
+ /* @__PURE__ */ jsx14(
2707
2873
  "span",
2708
2874
  {
2709
2875
  className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
@@ -2711,7 +2877,7 @@ function CrossfadeTrackRow({
2711
2877
  children: origin.sourceName ?? origin.name
2712
2878
  }
2713
2879
  ),
2714
- /* @__PURE__ */ jsx13(
2880
+ /* @__PURE__ */ jsx14(
2715
2881
  "input",
2716
2882
  {
2717
2883
  type: "range",
@@ -2727,7 +2893,7 @@ function CrossfadeTrackRow({
2727
2893
  "aria-label": "Crossfade position"
2728
2894
  }
2729
2895
  ),
2730
- /* @__PURE__ */ jsx13(
2896
+ /* @__PURE__ */ jsx14(
2731
2897
  "span",
2732
2898
  {
2733
2899
  className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
@@ -2737,12 +2903,12 @@ function CrossfadeTrackRow({
2737
2903
  )
2738
2904
  ] }),
2739
2905
  renderLayer(target, "target", "Target"),
2740
- /* @__PURE__ */ jsx13(
2906
+ /* @__PURE__ */ jsx14(
2741
2907
  ConfirmDialog,
2742
2908
  {
2743
2909
  open: confirmDelete,
2744
2910
  title: "Delete crossfade?",
2745
- message: /* @__PURE__ */ jsx13(Fragment2, { children: "This crossfade pair (both layers) will be permanently removed from this scene. This cannot be undone." }),
2911
+ message: /* @__PURE__ */ jsx14(Fragment2, { children: "This crossfade pair (both layers) will be permanently removed from this scene. This cannot be undone." }),
2746
2912
  confirmLabel: "Delete",
2747
2913
  onConfirm: () => {
2748
2914
  setConfirmDelete(false);
@@ -3016,21 +3182,21 @@ function defaultFadeGesture(role) {
3016
3182
 
3017
3183
  // src/components/FadeTrackRow.tsx
3018
3184
  import React11 from "react";
3019
- import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
3185
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
3020
3186
  function FadeCaption({
3021
3187
  layer,
3022
3188
  direction,
3023
3189
  gesture
3024
3190
  }) {
3025
3191
  const tag = direction === "in" ? "Fade in" : "Fade out";
3026
- return /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
3027
- /* @__PURE__ */ jsx14("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
3028
- /* @__PURE__ */ jsx14("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
3029
- layer.soundLabel && /* @__PURE__ */ jsxs10("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
3192
+ return /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
3193
+ /* @__PURE__ */ jsx15("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
3194
+ /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
3195
+ layer.soundLabel && /* @__PURE__ */ jsxs11("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
3030
3196
  "\xB7 ",
3031
3197
  layer.soundLabel
3032
3198
  ] }),
3033
- /* @__PURE__ */ jsxs10("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", title: `Fade gesture: ${gesture}`, children: [
3199
+ /* @__PURE__ */ jsxs11("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", title: `Fade gesture: ${gesture}`, children: [
3034
3200
  "\xB7 ",
3035
3201
  gesture
3036
3202
  ] })
@@ -3056,15 +3222,15 @@ function FadeTrackRow({
3056
3222
  const rightLabel = direction === "in" ? layer.sourceName ?? layer.name : "(silent)";
3057
3223
  const verb = effect && effect !== "fade" ? effect.charAt(0).toUpperCase() + effect.slice(1) : "Fade";
3058
3224
  const badge = direction === "in" ? `\u2197 ${verb} in` : `\u2198 ${verb} out`;
3059
- return /* @__PURE__ */ jsxs10(
3225
+ return /* @__PURE__ */ jsxs11(
3060
3226
  "div",
3061
3227
  {
3062
3228
  "data-testid": "fade-track-row",
3063
3229
  className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
3064
3230
  style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
3065
3231
  children: [
3066
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
3067
- /* @__PURE__ */ jsx14(
3232
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
3233
+ /* @__PURE__ */ jsx15(
3068
3234
  "span",
3069
3235
  {
3070
3236
  "data-testid": "fade-direction-badge",
@@ -3073,7 +3239,7 @@ function FadeTrackRow({
3073
3239
  children: badge
3074
3240
  }
3075
3241
  ),
3076
- /* @__PURE__ */ jsx14(
3242
+ /* @__PURE__ */ jsx15(
3077
3243
  "button",
3078
3244
  {
3079
3245
  "data-testid": "fade-delete-button",
@@ -3085,7 +3251,7 @@ function FadeTrackRow({
3085
3251
  }
3086
3252
  )
3087
3253
  ] }),
3088
- /* @__PURE__ */ jsx14(
3254
+ /* @__PURE__ */ jsx15(
3089
3255
  TrackRow,
3090
3256
  {
3091
3257
  track: { id: layer.trackId, name: "", role: layer.role },
@@ -3095,15 +3261,15 @@ function FadeTrackRow({
3095
3261
  drawerTab: "fx",
3096
3262
  levels,
3097
3263
  accentColor,
3098
- contentSlot: /* @__PURE__ */ jsx14(FadeCaption, { layer, direction, gesture }),
3264
+ contentSlot: /* @__PURE__ */ jsx15(FadeCaption, { layer, direction, gesture }),
3099
3265
  onMuteToggle,
3100
3266
  onSoloToggle,
3101
3267
  onVolumeChange,
3102
3268
  onPanChange
3103
3269
  }
3104
3270
  ),
3105
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "fade-slider-row", children: [
3106
- /* @__PURE__ */ jsx14(
3271
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "fade-slider-row", children: [
3272
+ /* @__PURE__ */ jsx15(
3107
3273
  "span",
3108
3274
  {
3109
3275
  className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
@@ -3111,7 +3277,7 @@ function FadeTrackRow({
3111
3277
  children: leftLabel
3112
3278
  }
3113
3279
  ),
3114
- /* @__PURE__ */ jsx14(
3280
+ /* @__PURE__ */ jsx15(
3115
3281
  "input",
3116
3282
  {
3117
3283
  type: "range",
@@ -3127,7 +3293,7 @@ function FadeTrackRow({
3127
3293
  "aria-label": "Fade position"
3128
3294
  }
3129
3295
  ),
3130
- /* @__PURE__ */ jsx14(
3296
+ /* @__PURE__ */ jsx15(
3131
3297
  "span",
3132
3298
  {
3133
3299
  className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
@@ -3136,12 +3302,12 @@ function FadeTrackRow({
3136
3302
  }
3137
3303
  )
3138
3304
  ] }),
3139
- /* @__PURE__ */ jsx14(
3305
+ /* @__PURE__ */ jsx15(
3140
3306
  ConfirmDialog,
3141
3307
  {
3142
3308
  open: confirmDelete,
3143
3309
  title: "Delete fade?",
3144
- message: /* @__PURE__ */ jsx14(Fragment3, { children: "This fade track will be permanently removed from this scene. This cannot be undone." }),
3310
+ message: /* @__PURE__ */ jsx15(Fragment3, { children: "This fade track will be permanently removed from this scene. This cannot be undone." }),
3145
3311
  confirmLabel: "Delete",
3146
3312
  onConfirm: () => {
3147
3313
  setConfirmDelete(false);
@@ -3158,20 +3324,20 @@ function FadeTrackRow({
3158
3324
 
3159
3325
  // src/components/GroupFadeTrackRow.tsx
3160
3326
  import React12 from "react";
3161
- import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
3327
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
3162
3328
  function MemberCaption({
3163
3329
  member,
3164
3330
  direction
3165
3331
  }) {
3166
3332
  const tag = direction === "in" ? "Fade in" : "Fade out";
3167
- return /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
3168
- member.memberLabel && /* @__PURE__ */ jsx15("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: member.memberLabel }),
3169
- /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-sas-text truncate", title: member.sourceName ?? member.name, children: member.sourceName ?? member.name }),
3170
- member.soundLabel && /* @__PURE__ */ jsxs11("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: member.soundLabel, children: [
3333
+ return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
3334
+ member.memberLabel && /* @__PURE__ */ jsx16("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: member.memberLabel }),
3335
+ /* @__PURE__ */ jsx16("span", { className: "text-[11px] text-sas-text truncate", title: member.sourceName ?? member.name, children: member.sourceName ?? member.name }),
3336
+ member.soundLabel && /* @__PURE__ */ jsxs12("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: member.soundLabel, children: [
3171
3337
  "\xB7 ",
3172
3338
  member.soundLabel
3173
3339
  ] }),
3174
- !member.memberLabel && /* @__PURE__ */ jsxs11("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: [
3340
+ !member.memberLabel && /* @__PURE__ */ jsxs12("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: [
3175
3341
  "\xB7 ",
3176
3342
  tag
3177
3343
  ] })
@@ -3200,16 +3366,16 @@ function GroupFadeTrackRow({
3200
3366
  const badge = direction === "in" ? "\u2197 Fade in" : "\u2198 Fade out";
3201
3367
  const leftLabel = direction === "in" ? "(silent)" : groupLabel;
3202
3368
  const rightLabel = direction === "in" ? groupLabel : "(silent)";
3203
- return /* @__PURE__ */ jsxs11(
3369
+ return /* @__PURE__ */ jsxs12(
3204
3370
  "div",
3205
3371
  {
3206
3372
  "data-testid": "group-fade-track-row",
3207
3373
  className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
3208
3374
  style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
3209
3375
  children: [
3210
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60 gap-2", children: [
3211
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 min-w-0", children: [
3212
- /* @__PURE__ */ jsx15(
3376
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60 gap-2", children: [
3377
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2 min-w-0", children: [
3378
+ /* @__PURE__ */ jsx16(
3213
3379
  "span",
3214
3380
  {
3215
3381
  "data-testid": "group-fade-direction-badge",
@@ -3218,7 +3384,7 @@ function GroupFadeTrackRow({
3218
3384
  children: badge
3219
3385
  }
3220
3386
  ),
3221
- /* @__PURE__ */ jsx15(
3387
+ /* @__PURE__ */ jsx16(
3222
3388
  "span",
3223
3389
  {
3224
3390
  "data-testid": "group-fade-label",
@@ -3228,8 +3394,8 @@ function GroupFadeTrackRow({
3228
3394
  }
3229
3395
  )
3230
3396
  ] }),
3231
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
3232
- /* @__PURE__ */ jsx15(
3397
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
3398
+ /* @__PURE__ */ jsx16(
3233
3399
  "button",
3234
3400
  {
3235
3401
  "data-testid": "group-fade-mute-button",
@@ -3239,7 +3405,7 @@ function GroupFadeTrackRow({
3239
3405
  children: "M"
3240
3406
  }
3241
3407
  ),
3242
- /* @__PURE__ */ jsx15(
3408
+ /* @__PURE__ */ jsx16(
3243
3409
  "button",
3244
3410
  {
3245
3411
  "data-testid": "group-fade-solo-button",
@@ -3249,7 +3415,7 @@ function GroupFadeTrackRow({
3249
3415
  children: "S"
3250
3416
  }
3251
3417
  ),
3252
- /* @__PURE__ */ jsx15(
3418
+ /* @__PURE__ */ jsx16(
3253
3419
  "button",
3254
3420
  {
3255
3421
  "data-testid": "group-fade-delete-button",
@@ -3262,7 +3428,7 @@ function GroupFadeTrackRow({
3262
3428
  )
3263
3429
  ] })
3264
3430
  ] }),
3265
- members.map((member) => /* @__PURE__ */ jsx15(
3431
+ members.map((member) => /* @__PURE__ */ jsx16(
3266
3432
  TrackRow,
3267
3433
  {
3268
3434
  track: { id: member.trackId, name: "", role: member.role },
@@ -3272,7 +3438,7 @@ function GroupFadeTrackRow({
3272
3438
  drawerTab: "fx",
3273
3439
  levels,
3274
3440
  accentColor,
3275
- contentSlot: /* @__PURE__ */ jsx15(MemberCaption, { member, direction }),
3441
+ contentSlot: /* @__PURE__ */ jsx16(MemberCaption, { member, direction }),
3276
3442
  onMuteToggle: () => onMemberMuteToggle(member.trackId),
3277
3443
  onSoloToggle: () => onMemberSoloToggle(member.trackId),
3278
3444
  onVolumeChange: (vol) => onMemberVolumeChange(member.trackId, vol),
@@ -3280,8 +3446,8 @@ function GroupFadeTrackRow({
3280
3446
  },
3281
3447
  member.trackId
3282
3448
  )),
3283
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "group-fade-slider-row", children: [
3284
- /* @__PURE__ */ jsx15(
3449
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "group-fade-slider-row", children: [
3450
+ /* @__PURE__ */ jsx16(
3285
3451
  "span",
3286
3452
  {
3287
3453
  className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
@@ -3289,7 +3455,7 @@ function GroupFadeTrackRow({
3289
3455
  children: leftLabel
3290
3456
  }
3291
3457
  ),
3292
- /* @__PURE__ */ jsx15(
3458
+ /* @__PURE__ */ jsx16(
3293
3459
  "input",
3294
3460
  {
3295
3461
  type: "range",
@@ -3305,7 +3471,7 @@ function GroupFadeTrackRow({
3305
3471
  "aria-label": "Group fade position"
3306
3472
  }
3307
3473
  ),
3308
- /* @__PURE__ */ jsx15(
3474
+ /* @__PURE__ */ jsx16(
3309
3475
  "span",
3310
3476
  {
3311
3477
  className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
@@ -3314,12 +3480,12 @@ function GroupFadeTrackRow({
3314
3480
  }
3315
3481
  )
3316
3482
  ] }),
3317
- /* @__PURE__ */ jsx15(
3483
+ /* @__PURE__ */ jsx16(
3318
3484
  ConfirmDialog,
3319
3485
  {
3320
3486
  open: confirmDelete,
3321
3487
  title: "Delete group fade?",
3322
- message: /* @__PURE__ */ jsxs11(Fragment4, { children: [
3488
+ message: /* @__PURE__ */ jsxs12(Fragment4, { children: [
3323
3489
  "All ",
3324
3490
  members.length,
3325
3491
  " member tracks of this fade will be permanently removed from this scene. This cannot be undone."
@@ -3339,8 +3505,8 @@ function GroupFadeTrackRow({
3339
3505
  }
3340
3506
 
3341
3507
  // src/components/FadeModal.tsx
3342
- import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef8, useState as useState9 } from "react";
3343
- import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
3508
+ import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef8, useState as useState10 } from "react";
3509
+ import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
3344
3510
  function shortId(dbId) {
3345
3511
  return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
3346
3512
  }
@@ -3383,7 +3549,7 @@ function OrphanRow({
3383
3549
  }) {
3384
3550
  const primary = track.prompt?.trim() || track.name;
3385
3551
  const meta = [track.role, shortId(track.dbId), gesture].filter(Boolean).join(" \xB7 ");
3386
- return /* @__PURE__ */ jsxs12(
3552
+ return /* @__PURE__ */ jsxs13(
3387
3553
  "button",
3388
3554
  {
3389
3555
  type: "button",
@@ -3395,8 +3561,8 @@ function OrphanRow({
3395
3561
  disabled,
3396
3562
  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
3563
  children: [
3398
- /* @__PURE__ */ jsx16("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
3399
- meta && /* @__PURE__ */ jsx16("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
3564
+ /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
3565
+ meta && /* @__PURE__ */ jsx17("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
3400
3566
  ]
3401
3567
  }
3402
3568
  );
@@ -3413,14 +3579,14 @@ function FadeModal({
3413
3579
  onCreate,
3414
3580
  testIdPrefix = "fade-modal"
3415
3581
  }) {
3416
- const [load, setLoad] = useState9({ status: "loading" });
3417
- const [selectedDbId, setSelectedDbId] = useState9("");
3418
- const [isCreating, setIsCreating] = useState9(false);
3419
- const [error, setError] = useState9(null);
3420
- const [fromName, setFromName] = useState9(null);
3421
- const [toName, setToName] = useState9(null);
3582
+ const [load, setLoad] = useState10({ status: "loading" });
3583
+ const [selectedDbId, setSelectedDbId] = useState10("");
3584
+ const [isCreating, setIsCreating] = useState10(false);
3585
+ const [error, setError] = useState10(null);
3586
+ const [fromName, setFromName] = useState10(null);
3587
+ const [toName, setToName] = useState10(null);
3422
3588
  const cancelRef = useRef8(null);
3423
- const refresh = useCallback5(async () => {
3589
+ const refresh = useCallback6(async () => {
3424
3590
  if (!host.listSceneFamilyTracks) {
3425
3591
  setLoad({ status: "error", message: "This host does not support fades." });
3426
3592
  return;
@@ -3440,7 +3606,7 @@ function FadeModal({
3440
3606
  setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
3441
3607
  }
3442
3608
  }, [host, fromSceneId, toSceneId]);
3443
- useEffect7(() => {
3609
+ useEffect8(() => {
3444
3610
  if (open) {
3445
3611
  setError(null);
3446
3612
  setIsCreating(false);
@@ -3460,17 +3626,17 @@ function FadeModal({
3460
3626
  ],
3461
3627
  [fadeOut, fadeIn]
3462
3628
  );
3463
- useEffect7(() => {
3629
+ useEffect8(() => {
3464
3630
  if (!allOrphans.some((o) => o.track.dbId === selectedDbId)) {
3465
3631
  setSelectedDbId(allOrphans[0]?.track.dbId ?? "");
3466
3632
  }
3467
3633
  }, [allOrphans, selectedDbId]);
3468
3634
  const selected = allOrphans.find((o) => o.track.dbId === selectedDbId) ?? null;
3469
3635
  const canCreate = !isCreating && !!selected;
3470
- const handleClose = useCallback5(() => {
3636
+ const handleClose = useCallback6(() => {
3471
3637
  if (!isCreating) onClose();
3472
3638
  }, [isCreating, onClose]);
3473
- const handleCreate = useCallback5(async () => {
3639
+ const handleCreate = useCallback6(async () => {
3474
3640
  if (!selected) return;
3475
3641
  setIsCreating(true);
3476
3642
  setError(null);
@@ -3491,16 +3657,16 @@ function FadeModal({
3491
3657
  if (!open) return null;
3492
3658
  const renderSection = (heading, list, section) => {
3493
3659
  if (list.length === 0) return null;
3494
- return /* @__PURE__ */ jsxs12("div", { className: "block", children: [
3495
- /* @__PURE__ */ jsx16("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: heading }),
3496
- /* @__PURE__ */ jsx16(
3660
+ return /* @__PURE__ */ jsxs13("div", { className: "block", children: [
3661
+ /* @__PURE__ */ jsx17("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: heading }),
3662
+ /* @__PURE__ */ jsx17(
3497
3663
  "div",
3498
3664
  {
3499
3665
  role: "radiogroup",
3500
3666
  "aria-label": heading,
3501
3667
  "data-testid": `${testIdPrefix}-${section === "out" ? "fade-out" : "fade-in"}-list`,
3502
3668
  className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
3503
- children: list.map((t) => /* @__PURE__ */ jsx16(
3669
+ children: list.map((t) => /* @__PURE__ */ jsx17(
3504
3670
  OrphanRow,
3505
3671
  {
3506
3672
  track: t,
@@ -3516,32 +3682,32 @@ function FadeModal({
3516
3682
  )
3517
3683
  ] });
3518
3684
  };
3519
- return /* @__PURE__ */ jsx16(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs12(
3685
+ return /* @__PURE__ */ jsx17(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs13(
3520
3686
  "div",
3521
3687
  {
3522
3688
  className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
3523
3689
  onClick: (e) => e.stopPropagation(),
3524
3690
  "data-testid": `${testIdPrefix}-box`,
3525
3691
  children: [
3526
- /* @__PURE__ */ jsx16("h3", { className: "text-sm font-bold text-sas-text", children: "Add fade" }),
3527
- /* @__PURE__ */ jsxs12("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
3692
+ /* @__PURE__ */ jsx17("h3", { className: "text-sm font-bold text-sas-text", children: "Add fade" }),
3693
+ /* @__PURE__ */ jsxs13("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
3528
3694
  "Tracks with no counterpart between",
3529
3695
  " ",
3530
- /* @__PURE__ */ jsx16("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
3696
+ /* @__PURE__ */ jsx17("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
3531
3697
  " and",
3532
3698
  " ",
3533
- /* @__PURE__ */ jsx16("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
3699
+ /* @__PURE__ */ jsx17("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
3534
3700
  " can gracefully fade out (leaving) or fade in (entering) across this transition."
3535
3701
  ] }),
3536
- load.status === "loading" && /* @__PURE__ */ jsx16("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
3537
- load.status === "error" && /* @__PURE__ */ jsx16("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
3538
- load.status === "ready" && (allOrphans.length === 0 ? /* @__PURE__ */ jsx16("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__ */ jsxs12(Fragment5, { children: [
3702
+ load.status === "loading" && /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
3703
+ load.status === "error" && /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
3704
+ 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
3705
  renderSection(`Fade out${fromLabel ? ` (from ${fromLabel})` : ""}`, fadeOut, "out"),
3540
3706
  renderSection(`Fade in${toLabel ? ` (to ${toLabel})` : ""}`, fadeIn, "in")
3541
3707
  ] })),
3542
- error && /* @__PURE__ */ jsx16("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
3543
- /* @__PURE__ */ jsxs12("div", { className: "flex justify-end gap-2 pt-1", children: [
3544
- /* @__PURE__ */ jsx16(
3708
+ error && /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
3709
+ /* @__PURE__ */ jsxs13("div", { className: "flex justify-end gap-2 pt-1", children: [
3710
+ /* @__PURE__ */ jsx17(
3545
3711
  "button",
3546
3712
  {
3547
3713
  ref: cancelRef,
@@ -3552,7 +3718,7 @@ function FadeModal({
3552
3718
  children: "Cancel"
3553
3719
  }
3554
3720
  ),
3555
- /* @__PURE__ */ jsx16(
3721
+ /* @__PURE__ */ jsx17(
3556
3722
  "button",
3557
3723
  {
3558
3724
  "data-testid": `${testIdPrefix}-confirm`,
@@ -3569,8 +3735,8 @@ function FadeModal({
3569
3735
  }
3570
3736
 
3571
3737
  // src/components/ImportTrackModal.tsx
3572
- import { useCallback as useCallback6, useEffect as useEffect8, useState as useState10 } from "react";
3573
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
3738
+ import { useCallback as useCallback7, useEffect as useEffect9, useState as useState11 } from "react";
3739
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
3574
3740
  function ImportTrackModal({
3575
3741
  host,
3576
3742
  open,
@@ -3582,10 +3748,10 @@ function ImportTrackModal({
3582
3748
  onPick,
3583
3749
  onPortTrack
3584
3750
  }) {
3585
- const [load, setLoad] = useState10({ status: "loading" });
3586
- const [selectedSceneId, setSelectedSceneId] = useState10(null);
3587
- const [importingTrackId, setImportingTrackId] = useState10(null);
3588
- const refresh = useCallback6(async () => {
3751
+ const [load, setLoad] = useState11({ status: "loading" });
3752
+ const [selectedSceneId, setSelectedSceneId] = useState11(null);
3753
+ const [importingTrackId, setImportingTrackId] = useState11(null);
3754
+ const refresh = useCallback7(async () => {
3589
3755
  if (!host.listImportableTracks) {
3590
3756
  setLoad({ status: "error", message: "This host does not support importing tracks." });
3591
3757
  return;
@@ -3601,14 +3767,14 @@ function ImportTrackModal({
3601
3767
  setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load scenes." });
3602
3768
  }
3603
3769
  }, [host, mode, onPortTrack]);
3604
- useEffect8(() => {
3770
+ useEffect9(() => {
3605
3771
  if (open) {
3606
3772
  setSelectedSceneId(null);
3607
3773
  setImportingTrackId(null);
3608
3774
  void refresh();
3609
3775
  }
3610
3776
  }, [open, refresh]);
3611
- const handleImport = useCallback6(
3777
+ const handleImport = useCallback7(
3612
3778
  async (track, sourceSceneId, sceneName, isSameScene) => {
3613
3779
  if (isSameScene && onPortTrack) {
3614
3780
  if (!track.importable) return;
@@ -3649,16 +3815,16 @@ function ImportTrackModal({
3649
3815
  if (!open) return null;
3650
3816
  const scenes = load.status === "ready" ? load.scenes : [];
3651
3817
  const selectedScene = scenes.find((s) => s.sceneId === selectedSceneId) ?? null;
3652
- return /* @__PURE__ */ jsx17(Modal, { open, onClose, testIdPrefix, children: /* @__PURE__ */ jsxs13(
3818
+ return /* @__PURE__ */ jsx18(Modal, { open, onClose, testIdPrefix, children: /* @__PURE__ */ jsxs14(
3653
3819
  "div",
3654
3820
  {
3655
3821
  className: "w-[420px] max-h-[70vh] overflow-hidden flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
3656
3822
  onClick: (e) => e.stopPropagation(),
3657
3823
  "data-testid": `${testIdPrefix}-modal`,
3658
3824
  children: [
3659
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between px-3 py-2 border-b border-sas-border", children: [
3660
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
3661
- selectedScene && /* @__PURE__ */ jsx17(
3825
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between px-3 py-2 border-b border-sas-border", children: [
3826
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
3827
+ selectedScene && /* @__PURE__ */ jsx18(
3662
3828
  "button",
3663
3829
  {
3664
3830
  className: "text-sas-muted hover:text-sas-accent text-xs",
@@ -3667,9 +3833,9 @@ function ImportTrackModal({
3667
3833
  children: "\u2190"
3668
3834
  }
3669
3835
  ),
3670
- /* @__PURE__ */ jsx17("span", { className: "text-sm font-medium text-sas-text", children: selectedScene ? selectedScene.sceneName : title })
3836
+ /* @__PURE__ */ jsx18("span", { className: "text-sm font-medium text-sas-text", children: selectedScene ? selectedScene.sceneName : title })
3671
3837
  ] }),
3672
- /* @__PURE__ */ jsx17(
3838
+ /* @__PURE__ */ jsx18(
3673
3839
  "button",
3674
3840
  {
3675
3841
  className: "text-sas-muted hover:text-sas-accent text-sm",
@@ -3679,30 +3845,30 @@ function ImportTrackModal({
3679
3845
  }
3680
3846
  )
3681
3847
  ] }),
3682
- /* @__PURE__ */ jsxs13("div", { className: "overflow-y-auto p-2 flex-1", children: [
3683
- load.status === "loading" && /* @__PURE__ */ jsx17("div", { className: "py-8 text-center text-xs text-sas-muted", "data-testid": `${testIdPrefix}-loading`, children: "Loading scenes\u2026" }),
3684
- load.status === "error" && /* @__PURE__ */ jsx17("div", { className: "py-8 text-center text-xs text-red-400", "data-testid": `${testIdPrefix}-error`, children: load.message }),
3685
- load.status === "ready" && scenes.length === 0 && /* @__PURE__ */ jsx17("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." }),
3686
- load.status === "ready" && scenes.length > 0 && !selectedScene && /* @__PURE__ */ jsx17("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-scene-list`, children: scenes.map((scene) => /* @__PURE__ */ jsx17("li", { children: /* @__PURE__ */ jsxs13(
3848
+ /* @__PURE__ */ jsxs14("div", { className: "overflow-y-auto p-2 flex-1", children: [
3849
+ load.status === "loading" && /* @__PURE__ */ jsx18("div", { className: "py-8 text-center text-xs text-sas-muted", "data-testid": `${testIdPrefix}-loading`, children: "Loading scenes\u2026" }),
3850
+ load.status === "error" && /* @__PURE__ */ jsx18("div", { className: "py-8 text-center text-xs text-red-400", "data-testid": `${testIdPrefix}-error`, children: load.message }),
3851
+ 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." }),
3852
+ 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
3853
  "button",
3688
3854
  {
3689
3855
  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
3856
  onClick: () => setSelectedSceneId(scene.sceneId),
3691
3857
  "data-testid": `${testIdPrefix}-scene`,
3692
3858
  children: [
3693
- /* @__PURE__ */ jsx17("span", { className: "truncate", children: scene.sceneName }),
3694
- /* @__PURE__ */ jsxs13("span", { className: "text-sas-muted", children: [
3859
+ /* @__PURE__ */ jsx18("span", { className: "truncate", children: scene.sceneName }),
3860
+ /* @__PURE__ */ jsxs14("span", { className: "text-sas-muted", children: [
3695
3861
  scene.tracks.length,
3696
3862
  " \u2192"
3697
3863
  ] })
3698
3864
  ]
3699
3865
  }
3700
3866
  ) }, scene.sceneId)) }),
3701
- selectedScene && /* @__PURE__ */ jsx17("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-track-list`, children: selectedScene.tracks.map((track) => {
3867
+ selectedScene && /* @__PURE__ */ jsx18("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-track-list`, children: selectedScene.tracks.map((track) => {
3702
3868
  const busy = importingTrackId === track.trackId;
3703
3869
  const gated = mode === "track" && !track.importable;
3704
3870
  const disabled = gated || busy;
3705
- return /* @__PURE__ */ jsx17("li", { children: /* @__PURE__ */ jsxs13(
3871
+ return /* @__PURE__ */ jsx18("li", { children: /* @__PURE__ */ jsxs14(
3706
3872
  "button",
3707
3873
  {
3708
3874
  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 +3878,14 @@ function ImportTrackModal({
3712
3878
  "data-testid": `${testIdPrefix}-track`,
3713
3879
  "data-importable": mode === "sound" || track.importable ? "true" : "false",
3714
3880
  children: [
3715
- /* @__PURE__ */ jsxs13("span", { className: "truncate", children: [
3881
+ /* @__PURE__ */ jsxs14("span", { className: "truncate", children: [
3716
3882
  track.name,
3717
- track.role ? /* @__PURE__ */ jsxs13("span", { className: "text-sas-muted", children: [
3883
+ track.role ? /* @__PURE__ */ jsxs14("span", { className: "text-sas-muted", children: [
3718
3884
  " \xB7 ",
3719
3885
  track.role
3720
3886
  ] }) : null
3721
3887
  ] }),
3722
- busy ? /* @__PURE__ */ jsx17("span", { className: "text-sas-muted", children: "\u2026" }) : gated ? /* @__PURE__ */ jsx17("span", { className: "text-sas-muted", children: "\u2298" }) : null
3888
+ busy ? /* @__PURE__ */ jsx18("span", { className: "text-sas-muted", children: "\u2026" }) : gated ? /* @__PURE__ */ jsx18("span", { className: "text-sas-muted", children: "\u2298" }) : null
3723
3889
  ]
3724
3890
  }
3725
3891
  ) }, track.dbId);
@@ -3731,8 +3897,8 @@ function ImportTrackModal({
3731
3897
  }
3732
3898
 
3733
3899
  // src/components/CrossfadeModal.tsx
3734
- import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef9, useState as useState11 } from "react";
3735
- import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
3900
+ import { useCallback as useCallback8, useEffect as useEffect10, useMemo as useMemo5, useRef as useRef9, useState as useState12 } from "react";
3901
+ import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
3736
3902
  function shortId2(dbId) {
3737
3903
  return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
3738
3904
  }
@@ -3745,7 +3911,7 @@ function CandidateRow({
3745
3911
  }) {
3746
3912
  const primary = track.prompt?.trim() || track.name;
3747
3913
  const meta = [track.role, shortId2(track.dbId)].filter(Boolean).join(" \xB7 ");
3748
- return /* @__PURE__ */ jsxs14(
3914
+ return /* @__PURE__ */ jsxs15(
3749
3915
  "button",
3750
3916
  {
3751
3917
  type: "button",
@@ -3757,8 +3923,8 @@ function CandidateRow({
3757
3923
  disabled,
3758
3924
  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
3925
  children: [
3760
- /* @__PURE__ */ jsx18("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
3761
- meta && /* @__PURE__ */ jsx18("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
3926
+ /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
3927
+ meta && /* @__PURE__ */ jsx19("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
3762
3928
  ]
3763
3929
  }
3764
3930
  );
@@ -3775,15 +3941,15 @@ function CrossfadeModal({
3775
3941
  onCreate,
3776
3942
  testIdPrefix = "crossfade-modal"
3777
3943
  }) {
3778
- const [load, setLoad] = useState11({ status: "loading" });
3779
- const [originDbId, setOriginDbId] = useState11("");
3780
- const [targetDbId, setTargetDbId] = useState11("");
3781
- const [isCreating, setIsCreating] = useState11(false);
3782
- const [error, setError] = useState11(null);
3783
- const [fromName, setFromName] = useState11(null);
3784
- const [toName, setToName] = useState11(null);
3944
+ const [load, setLoad] = useState12({ status: "loading" });
3945
+ const [originDbId, setOriginDbId] = useState12("");
3946
+ const [targetDbId, setTargetDbId] = useState12("");
3947
+ const [isCreating, setIsCreating] = useState12(false);
3948
+ const [error, setError] = useState12(null);
3949
+ const [fromName, setFromName] = useState12(null);
3950
+ const [toName, setToName] = useState12(null);
3785
3951
  const cancelRef = useRef9(null);
3786
- const refresh = useCallback7(async () => {
3952
+ const refresh = useCallback8(async () => {
3787
3953
  if (!host.listSceneFamilyTracks) {
3788
3954
  setLoad({ status: "error", message: "This host does not support crossfade tracks." });
3789
3955
  return;
@@ -3803,7 +3969,7 @@ function CrossfadeModal({
3803
3969
  setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
3804
3970
  }
3805
3971
  }, [host, fromSceneId, toSceneId]);
3806
- useEffect9(() => {
3972
+ useEffect10(() => {
3807
3973
  if (open) {
3808
3974
  setError(null);
3809
3975
  setIsCreating(false);
@@ -3821,12 +3987,12 @@ function CrossfadeModal({
3821
3987
  () => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
3822
3988
  [load, excludeSet]
3823
3989
  );
3824
- useEffect9(() => {
3990
+ useEffect10(() => {
3825
3991
  if (!originCandidates.some((t) => t.dbId === originDbId)) {
3826
3992
  setOriginDbId(originCandidates[0]?.dbId ?? "");
3827
3993
  }
3828
3994
  }, [originCandidates, originDbId]);
3829
- useEffect9(() => {
3995
+ useEffect10(() => {
3830
3996
  if (!targetCandidates.some((t) => t.dbId === targetDbId)) {
3831
3997
  setTargetDbId(targetCandidates[0]?.dbId ?? "");
3832
3998
  }
@@ -3834,10 +4000,10 @@ function CrossfadeModal({
3834
4000
  const originTrack = originCandidates.find((t) => t.dbId === originDbId) ?? null;
3835
4001
  const targetTrack = targetCandidates.find((t) => t.dbId === targetDbId) ?? null;
3836
4002
  const canCreate = !isCreating && !!originTrack && !!targetTrack;
3837
- const handleClose = useCallback7(() => {
4003
+ const handleClose = useCallback8(() => {
3838
4004
  if (!isCreating) onClose();
3839
4005
  }, [isCreating, onClose]);
3840
- const handleCreate = useCallback7(async () => {
4006
+ const handleCreate = useCallback8(async () => {
3841
4007
  if (!originTrack || !targetTrack) return;
3842
4008
  setIsCreating(true);
3843
4009
  setError(null);
@@ -3855,26 +4021,26 @@ function CrossfadeModal({
3855
4021
  const fromLabel = fromName ?? fromSceneName ?? null;
3856
4022
  const toLabel = toName ?? toSceneName ?? null;
3857
4023
  if (!open) return null;
3858
- return /* @__PURE__ */ jsx18(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs14(
4024
+ return /* @__PURE__ */ jsx19(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs15(
3859
4025
  "div",
3860
4026
  {
3861
4027
  className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
3862
4028
  onClick: (e) => e.stopPropagation(),
3863
4029
  "data-testid": `${testIdPrefix}-box`,
3864
4030
  children: [
3865
- /* @__PURE__ */ jsx18("h3", { className: "text-sm font-bold text-sas-text", children: "Add crossfade" }),
3866
- /* @__PURE__ */ jsxs14("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
4031
+ /* @__PURE__ */ jsx19("h3", { className: "text-sm font-bold text-sas-text", children: "Add crossfade" }),
4032
+ /* @__PURE__ */ jsxs15("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
3867
4033
  "Bridge a track from",
3868
4034
  " ",
3869
- /* @__PURE__ */ jsx18("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
4035
+ /* @__PURE__ */ jsx19("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
3870
4036
  " into one from",
3871
4037
  " ",
3872
- /* @__PURE__ */ jsx18("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
4038
+ /* @__PURE__ */ jsx19("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
3873
4039
  ". Both layers share one generated part; each keeps its own preset."
3874
4040
  ] }),
3875
- load.status === "loading" && /* @__PURE__ */ jsx18("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
3876
- load.status === "error" && /* @__PURE__ */ jsx18("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
3877
- load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */ jsxs14(
4041
+ load.status === "loading" && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
4042
+ load.status === "error" && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
4043
+ load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */ jsxs15(
3878
4044
  "div",
3879
4045
  {
3880
4046
  className: "text-xs text-sas-muted py-4 text-center",
@@ -3885,20 +4051,20 @@ function CrossfadeModal({
3885
4051
  ". Add one (or free one from another crossfade) first."
3886
4052
  ]
3887
4053
  }
3888
- ) : /* @__PURE__ */ jsxs14(Fragment6, { children: [
3889
- /* @__PURE__ */ jsxs14("div", { className: "block", children: [
3890
- /* @__PURE__ */ jsxs14("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
4054
+ ) : /* @__PURE__ */ jsxs15(Fragment6, { children: [
4055
+ /* @__PURE__ */ jsxs15("div", { className: "block", children: [
4056
+ /* @__PURE__ */ jsxs15("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
3891
4057
  "Origin ",
3892
4058
  fromLabel ? `(${fromLabel})` : "(top)"
3893
4059
  ] }),
3894
- /* @__PURE__ */ jsx18(
4060
+ /* @__PURE__ */ jsx19(
3895
4061
  "div",
3896
4062
  {
3897
4063
  role: "radiogroup",
3898
4064
  "aria-label": "Origin track",
3899
4065
  "data-testid": `${testIdPrefix}-origin-list`,
3900
4066
  className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
3901
- children: originCandidates.map((t) => /* @__PURE__ */ jsx18(
4067
+ children: originCandidates.map((t) => /* @__PURE__ */ jsx19(
3902
4068
  CandidateRow,
3903
4069
  {
3904
4070
  track: t,
@@ -3912,23 +4078,23 @@ function CrossfadeModal({
3912
4078
  }
3913
4079
  )
3914
4080
  ] }),
3915
- /* @__PURE__ */ jsxs14("div", { className: "block", children: [
3916
- /* @__PURE__ */ jsxs14("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
4081
+ /* @__PURE__ */ jsxs15("div", { className: "block", children: [
4082
+ /* @__PURE__ */ jsxs15("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
3917
4083
  "Target ",
3918
4084
  toLabel ? `(${toLabel})` : "(bottom)"
3919
4085
  ] }),
3920
- targetCandidates.length === 0 ? /* @__PURE__ */ jsxs14("div", { className: "text-xs text-sas-danger mt-0.5", "data-testid": `${testIdPrefix}-empty-target`, children: [
4086
+ targetCandidates.length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "text-xs text-sas-danger mt-0.5", "data-testid": `${testIdPrefix}-empty-target`, children: [
3921
4087
  "No available tracks in ",
3922
4088
  toLabel ?? "the target scene",
3923
4089
  " to crossfade into."
3924
- ] }) : /* @__PURE__ */ jsx18(
4090
+ ] }) : /* @__PURE__ */ jsx19(
3925
4091
  "div",
3926
4092
  {
3927
4093
  role: "radiogroup",
3928
4094
  "aria-label": "Target track",
3929
4095
  "data-testid": `${testIdPrefix}-target-list`,
3930
4096
  className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
3931
- children: targetCandidates.map((t) => /* @__PURE__ */ jsx18(
4097
+ children: targetCandidates.map((t) => /* @__PURE__ */ jsx19(
3932
4098
  CandidateRow,
3933
4099
  {
3934
4100
  track: t,
@@ -3943,9 +4109,9 @@ function CrossfadeModal({
3943
4109
  )
3944
4110
  ] })
3945
4111
  ] })),
3946
- error && /* @__PURE__ */ jsx18("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
3947
- /* @__PURE__ */ jsxs14("div", { className: "flex justify-end gap-2 pt-1", children: [
3948
- /* @__PURE__ */ jsx18(
4112
+ error && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
4113
+ /* @__PURE__ */ jsxs15("div", { className: "flex justify-end gap-2 pt-1", children: [
4114
+ /* @__PURE__ */ jsx19(
3949
4115
  "button",
3950
4116
  {
3951
4117
  ref: cancelRef,
@@ -3956,7 +4122,7 @@ function CrossfadeModal({
3956
4122
  children: "Cancel"
3957
4123
  }
3958
4124
  ),
3959
- /* @__PURE__ */ jsx18(
4125
+ /* @__PURE__ */ jsx19(
3960
4126
  "button",
3961
4127
  {
3962
4128
  "data-testid": `${testIdPrefix}-confirm`,
@@ -3973,10 +4139,10 @@ function CrossfadeModal({
3973
4139
  }
3974
4140
 
3975
4141
  // src/components/TransitionDesigner.tsx
3976
- import { useCallback as useCallback9, useEffect as useEffect10, useMemo as useMemo6, useRef as useRef11, useState as useState13 } from "react";
4142
+ import { useCallback as useCallback10, useEffect as useEffect11, useMemo as useMemo6, useRef as useRef11, useState as useState14 } from "react";
3977
4143
 
3978
4144
  // src/hooks/useTrackReorder.ts
3979
- import { useCallback as useCallback8, useRef as useRef10, useState as useState12 } from "react";
4145
+ import { useCallback as useCallback9, useRef as useRef10, useState as useState13 } from "react";
3980
4146
  function moveItem(arr, from, to) {
3981
4147
  const next = arr.slice();
3982
4148
  if (from === to || from < 0 || to < 0 || from >= next.length || to >= next.length) {
@@ -3993,12 +4159,12 @@ function useTrackReorder({
3993
4159
  getId,
3994
4160
  onError
3995
4161
  }) {
3996
- const [draggingIndex, setDraggingIndex] = useState12(null);
3997
- const [dragOverIndex, setDragOverIndex] = useState12(null);
4162
+ const [draggingIndex, setDraggingIndex] = useState13(null);
4163
+ const [dragOverIndex, setDragOverIndex] = useState13(null);
3998
4164
  const fromRef = useRef10(null);
3999
4165
  const itemsRef = useRef10(items);
4000
4166
  itemsRef.current = items;
4001
- const dragPropsFor = useCallback8(
4167
+ const dragPropsFor = useCallback9(
4002
4168
  (index) => ({
4003
4169
  handleProps: {
4004
4170
  draggable: true,
@@ -4180,7 +4346,7 @@ function dbIdsFromKeys(keys) {
4180
4346
  }
4181
4347
 
4182
4348
  // src/components/TransitionDesigner.tsx
4183
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
4349
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
4184
4350
  var CROSSFADE_ESTIMATE_MS = 15e3;
4185
4351
  var FADE_ESTIMATE_MS = 11e3;
4186
4352
  var CREATE_ALL_CONCURRENCY = 5;
@@ -4207,14 +4373,14 @@ function TransitionDesigner({
4207
4373
  testIdPrefix = "transition-designer"
4208
4374
  }) {
4209
4375
  const fadeOnly = !onCreateCrossfade;
4210
- const [load, setLoad] = useState13({ status: "loading" });
4211
- const [fromName, setFromName] = useState13(null);
4212
- const [toName, setToName] = useState13(null);
4213
- const [originSlots, setOriginSlots] = useState13([]);
4214
- const [targetSlots, setTargetSlots] = useState13([]);
4215
- const [creatingKeys, setCreatingKeys] = useState13(() => /* @__PURE__ */ new Set());
4216
- const [rowErrors, setRowErrors] = useState13({});
4217
- const [rowEffects, setRowEffects] = useState13({});
4376
+ const [load, setLoad] = useState14({ status: "loading" });
4377
+ const [fromName, setFromName] = useState14(null);
4378
+ const [toName, setToName] = useState14(null);
4379
+ const [originSlots, setOriginSlots] = useState14([]);
4380
+ const [targetSlots, setTargetSlots] = useState14([]);
4381
+ const [creatingKeys, setCreatingKeys] = useState14(() => /* @__PURE__ */ new Set());
4382
+ const [rowErrors, setRowErrors] = useState14({});
4383
+ const [rowEffects, setRowEffects] = useState14({});
4218
4384
  const rowEffectsRef = useRef11(rowEffects);
4219
4385
  rowEffectsRef.current = rowEffects;
4220
4386
  const audioEffectsEnabled = !!onCreateAudioTransition;
@@ -4227,8 +4393,8 @@ function TransitionDesigner({
4227
4393
  const creatingKeysRef = useRef11(creatingKeys);
4228
4394
  creatingKeysRef.current = creatingKeys;
4229
4395
  const dragRef = useRef11(null);
4230
- const [dragging, setDragging] = useState13(null);
4231
- const [dragOver, setDragOver] = useState13(null);
4396
+ const [dragging, setDragging] = useState14(null);
4397
+ const [dragOver, setDragOver] = useState14(null);
4232
4398
  const excludeSet = useMemo6(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
4233
4399
  const originPool = useMemo6(
4234
4400
  () => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
@@ -4244,7 +4410,7 @@ function TransitionDesigner({
4244
4410
  originByIdRef.current = originById;
4245
4411
  const targetByIdRef = useRef11(targetById);
4246
4412
  targetByIdRef.current = targetById;
4247
- const refresh = useCallback9(async () => {
4413
+ const refresh = useCallback10(async () => {
4248
4414
  if (!host.listSceneFamilyTracks) {
4249
4415
  setLoad({ status: "error", message: "This host does not support transition tracks." });
4250
4416
  return;
@@ -4287,10 +4453,10 @@ function TransitionDesigner({
4287
4453
  });
4288
4454
  }
4289
4455
  }, [host, fromSceneId, toSceneId, transitionSceneId, mapColumnSubjects]);
4290
- useEffect10(() => {
4456
+ useEffect11(() => {
4291
4457
  void refresh();
4292
4458
  }, [refresh]);
4293
- useEffect10(() => {
4459
+ useEffect11(() => {
4294
4460
  if (load.status !== "ready") return;
4295
4461
  const [po, pt] = padPair(
4296
4462
  reconcileSlots(originSlotsRef.current, originPool.map((t) => t.dbId)),
@@ -4299,7 +4465,7 @@ function TransitionDesigner({
4299
4465
  if (!slotsEqual(po, originSlotsRef.current)) setOriginSlots(po);
4300
4466
  if (!slotsEqual(pt, targetSlotsRef.current)) setTargetSlots(pt);
4301
4467
  }, [originPool, targetPool, load.status]);
4302
- const mutate = useCallback9(
4468
+ const mutate = useCallback10(
4303
4469
  (nextOrigin, nextTarget) => {
4304
4470
  const norm = normalizeSlots(nextOrigin, nextTarget);
4305
4471
  const [po, pt] = padPair(norm.originOrder, norm.targetOrder);
@@ -4312,7 +4478,7 @@ function TransitionDesigner({
4312
4478
  },
4313
4479
  [host, transitionSceneId]
4314
4480
  );
4315
- const setRowEffect = useCallback9(
4481
+ const setRowEffect = useCallback10(
4316
4482
  (sourceDbId, effect) => {
4317
4483
  setRowEffects((prev) => {
4318
4484
  const next = { ...prev, [sourceDbId]: effect };
@@ -4326,7 +4492,7 @@ function TransitionDesigner({
4326
4492
  },
4327
4493
  [host, transitionSceneId]
4328
4494
  );
4329
- const insertGapAbove = useCallback9(
4495
+ const insertGapAbove = useCallback10(
4330
4496
  (col, index) => {
4331
4497
  const slots = col === "origin" ? originSlots : targetSlots;
4332
4498
  const next = [...slots.slice(0, index), null, ...slots.slice(index)];
@@ -4335,7 +4501,7 @@ function TransitionDesigner({
4335
4501
  },
4336
4502
  [originSlots, targetSlots, mutate]
4337
4503
  );
4338
- const removeGap = useCallback9(
4504
+ const removeGap = useCallback10(
4339
4505
  (col, index) => {
4340
4506
  const slots = col === "origin" ? originSlots : targetSlots;
4341
4507
  const next = slots.filter((_, i) => i !== index);
@@ -4344,7 +4510,7 @@ function TransitionDesigner({
4344
4510
  },
4345
4511
  [originSlots, targetSlots, mutate]
4346
4512
  );
4347
- const handleDrop = useCallback9(
4513
+ const handleDrop = useCallback10(
4348
4514
  (col, to) => {
4349
4515
  const from = dragRef.current;
4350
4516
  dragRef.current = null;
@@ -4383,7 +4549,7 @@ function TransitionDesigner({
4383
4549
  }).length,
4384
4550
  [effectiveRows, creatingKeys]
4385
4551
  );
4386
- const createRow = useCallback9(
4552
+ const createRow = useCallback10(
4387
4553
  async (row) => {
4388
4554
  const key = rowKey(row);
4389
4555
  if (!key || !row.type || creatingKeysRef.current.has(key)) return;
@@ -4438,7 +4604,7 @@ function TransitionDesigner({
4438
4604
  },
4439
4605
  [onCreateCrossfade, onCreateFade, onCreateAudioTransition]
4440
4606
  );
4441
- const createAll = useCallback9(async () => {
4607
+ const createAll = useCallback10(async () => {
4442
4608
  const source = fadeOnly ? fadeOnlyRowsRef.current : buildRowSlots(originSlotsRef.current, targetSlotsRef.current);
4443
4609
  const eligible = source.filter((r) => {
4444
4610
  const k = rowKey(r);
@@ -4507,15 +4673,15 @@ function TransitionDesigner({
4507
4673
  const base = "group relative rounded-sm border px-2 py-1.5 text-left transition-colors select-none";
4508
4674
  const tone = isDragTarget ? "border-sas-accent bg-sas-accent/10" : "border-sas-border bg-sas-panel";
4509
4675
  if (slotId === null) {
4510
- return /* @__PURE__ */ jsxs15(
4676
+ return /* @__PURE__ */ jsxs16(
4511
4677
  "div",
4512
4678
  {
4513
4679
  ...cellDragProps(col, index, false),
4514
4680
  "data-testid": `${testIdPrefix}-${col}-gap-${index}`,
4515
4681
  className: `${base} ${tone} border-dashed flex items-center justify-between ${isDragging ? "opacity-40" : "opacity-70"}`,
4516
4682
  children: [
4517
- /* @__PURE__ */ jsx19("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: "\u2014 gap \u2014" }),
4518
- /* @__PURE__ */ jsx19(
4683
+ /* @__PURE__ */ jsx20("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: "\u2014 gap \u2014" }),
4684
+ /* @__PURE__ */ jsx20(
4519
4685
  "button",
4520
4686
  {
4521
4687
  type: "button",
@@ -4532,7 +4698,7 @@ function TransitionDesigner({
4532
4698
  }
4533
4699
  const primary = track ? track.prompt?.trim() || track.name : slotId;
4534
4700
  const meta = track ? [track.role, shortId3(track.dbId)].filter(Boolean).join(" \xB7 ") : "missing";
4535
- return /* @__PURE__ */ jsx19(
4701
+ return /* @__PURE__ */ jsx20(
4536
4702
  "div",
4537
4703
  {
4538
4704
  ...cellDragProps(col, index, locked),
@@ -4540,13 +4706,13 @@ function TransitionDesigner({
4540
4706
  "data-value": slotId,
4541
4707
  className: `${base} ${tone} ${isDragging ? "opacity-40" : ""} ${locked ? "opacity-60" : "cursor-grab active:cursor-grabbing"}`,
4542
4708
  title: track ? track.dbId : "Track no longer available",
4543
- children: /* @__PURE__ */ jsxs15("div", { className: "flex items-start gap-1", children: [
4544
- /* @__PURE__ */ jsx19("span", { className: "text-sas-muted/60 text-xs leading-tight pt-0.5", "aria-hidden": true, children: "\u283F" }),
4545
- /* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1", children: [
4546
- /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-text truncate", children: primary }),
4547
- meta && /* @__PURE__ */ jsx19("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", children: meta })
4709
+ children: /* @__PURE__ */ jsxs16("div", { className: "flex items-start gap-1", children: [
4710
+ /* @__PURE__ */ jsx20("span", { className: "text-sas-muted/60 text-xs leading-tight pt-0.5", "aria-hidden": true, children: "\u283F" }),
4711
+ /* @__PURE__ */ jsxs16("div", { className: "min-w-0 flex-1", children: [
4712
+ /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-text truncate", children: primary }),
4713
+ meta && /* @__PURE__ */ jsx20("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", children: meta })
4548
4714
  ] }),
4549
- /* @__PURE__ */ jsx19(
4715
+ /* @__PURE__ */ jsx20(
4550
4716
  "button",
4551
4717
  {
4552
4718
  type: "button",
@@ -4569,26 +4735,26 @@ function TransitionDesigner({
4569
4735
  const key = rowKey(row);
4570
4736
  const isCreatingThis = key !== null && creatingKeys.has(key);
4571
4737
  const errMsg = key !== null ? rowErrors[key] : void 0;
4572
- return /* @__PURE__ */ jsxs15(
4738
+ return /* @__PURE__ */ jsxs16(
4573
4739
  "div",
4574
4740
  {
4575
4741
  "data-testid": `${testIdPrefix}-row-${i}`,
4576
4742
  className: "grid grid-cols-[1fr_auto] gap-2 items-center",
4577
4743
  children: [
4578
- /* @__PURE__ */ jsxs15(
4744
+ /* @__PURE__ */ jsxs16(
4579
4745
  "div",
4580
4746
  {
4581
4747
  "data-testid": `${testIdPrefix}-subject-${slotId}`,
4582
4748
  className: `rounded-sm border border-sas-border bg-sas-panel px-2 py-1.5 ${isCreatingThis ? "opacity-60" : ""}`,
4583
4749
  title: track ? track.dbId : "Track no longer available",
4584
4750
  children: [
4585
- /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-text truncate", children: track ? track.prompt?.trim() || track.name : slotId }),
4586
- /* @__PURE__ */ jsx19("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", children: track ? [track.role, shortId3(track.dbId)].filter(Boolean).join(" \xB7 ") : "missing" })
4751
+ /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-text truncate", children: track ? track.prompt?.trim() || track.name : slotId }),
4752
+ /* @__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
4753
  ]
4588
4754
  }
4589
4755
  ),
4590
- /* @__PURE__ */ jsxs15("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
4591
- /* @__PURE__ */ jsx19(
4756
+ /* @__PURE__ */ jsxs16("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
4757
+ /* @__PURE__ */ jsx20(
4592
4758
  "span",
4593
4759
  {
4594
4760
  "data-testid": `${testIdPrefix}-type-${i}`,
@@ -4596,7 +4762,7 @@ function TransitionDesigner({
4596
4762
  children: row.type ? TYPE_LABEL[row.type] : "\u2014"
4597
4763
  }
4598
4764
  ),
4599
- isCreatingThis ? /* @__PURE__ */ jsx19("div", { className: "w-full", children: /* @__PURE__ */ jsx19(
4765
+ isCreatingThis ? /* @__PURE__ */ jsx20("div", { className: "w-full", children: /* @__PURE__ */ jsx20(
4600
4766
  SorceryProgressBar,
4601
4767
  {
4602
4768
  isLoading: true,
@@ -4604,7 +4770,7 @@ function TransitionDesigner({
4604
4770
  statusText: "CREATING",
4605
4771
  estimatedDurationMs: fadeEstimateMs ?? FADE_ESTIMATE_MS
4606
4772
  }
4607
- ) }) : /* @__PURE__ */ jsx19(
4773
+ ) }) : /* @__PURE__ */ jsx20(
4608
4774
  "button",
4609
4775
  {
4610
4776
  type: "button",
@@ -4614,7 +4780,7 @@ function TransitionDesigner({
4614
4780
  children: "Create"
4615
4781
  }
4616
4782
  ),
4617
- errMsg && /* @__PURE__ */ jsx19(
4783
+ errMsg && /* @__PURE__ */ jsx20(
4618
4784
  "span",
4619
4785
  {
4620
4786
  "data-testid": `${testIdPrefix}-row-error-${i}`,
@@ -4631,18 +4797,18 @@ function TransitionDesigner({
4631
4797
  if (fadeOnly) {
4632
4798
  const outRows = fadeOnlyRows.filter((r) => r.type === "fade-out");
4633
4799
  const inRows = fadeOnlyRows.filter((r) => r.type === "fade-in");
4634
- return /* @__PURE__ */ jsxs15("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
4635
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
4636
- /* @__PURE__ */ jsxs15("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
4637
- /* @__PURE__ */ jsx19("span", { className: "text-sas-text", children: fromLabel }),
4800
+ return /* @__PURE__ */ jsxs16("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
4801
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
4802
+ /* @__PURE__ */ jsxs16("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
4803
+ /* @__PURE__ */ jsx20("span", { className: "text-sas-text", children: fromLabel }),
4638
4804
  " \u2192",
4639
4805
  " ",
4640
- /* @__PURE__ */ jsx19("span", { className: "text-sas-text", children: toLabel }),
4806
+ /* @__PURE__ */ jsx20("span", { className: "text-sas-text", children: toLabel }),
4641
4807
  familyLabel ? ` \xB7 ${familyLabel}` : "",
4642
4808
  " \xB7 each entry fades on its own \u2014 origin fades out, target fades in."
4643
4809
  ] }),
4644
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 shrink-0", children: [
4645
- creatingKeys.size > 0 && /* @__PURE__ */ jsxs15(
4810
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 shrink-0", children: [
4811
+ creatingKeys.size > 0 && /* @__PURE__ */ jsxs16(
4646
4812
  "span",
4647
4813
  {
4648
4814
  className: "text-[10px] text-sas-accent whitespace-nowrap",
@@ -4653,7 +4819,7 @@ function TransitionDesigner({
4653
4819
  ]
4654
4820
  }
4655
4821
  ),
4656
- /* @__PURE__ */ jsxs15(
4822
+ /* @__PURE__ */ jsxs16(
4657
4823
  "button",
4658
4824
  {
4659
4825
  type: "button",
@@ -4670,26 +4836,26 @@ function TransitionDesigner({
4670
4836
  )
4671
4837
  ] })
4672
4838
  ] }),
4673
- load.status === "loading" && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
4674
- load.status === "error" && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
4675
- load.status === "ready" && (fadeOnlyRows.length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "text-xs text-sas-muted py-6 text-center", "data-testid": `${testIdPrefix}-empty`, children: [
4839
+ load.status === "loading" && /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
4840
+ load.status === "error" && /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
4841
+ 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
4842
  "Nothing to fade in this panel for either scene. Add tracks to ",
4677
4843
  fromLabel,
4678
4844
  " or ",
4679
4845
  toLabel,
4680
4846
  " ",
4681
4847
  "first (or free one by deleting an existing fade)."
4682
- ] }) : /* @__PURE__ */ jsxs15("div", { className: "space-y-3", children: [
4683
- outRows.length > 0 && /* @__PURE__ */ jsxs15("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-fade-out-section`, children: [
4684
- /* @__PURE__ */ jsxs15("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
4848
+ ] }) : /* @__PURE__ */ jsxs16("div", { className: "space-y-3", children: [
4849
+ outRows.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-fade-out-section`, children: [
4850
+ /* @__PURE__ */ jsxs16("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
4685
4851
  "Origin (",
4686
4852
  fromLabel,
4687
4853
  ") \u2014 fades out"
4688
4854
  ] }),
4689
4855
  outRows.map((row, i) => renderFadeOnlyRow(row, i))
4690
4856
  ] }),
4691
- inRows.length > 0 && /* @__PURE__ */ jsxs15("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-fade-in-section`, children: [
4692
- /* @__PURE__ */ jsxs15("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
4857
+ inRows.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-fade-in-section`, children: [
4858
+ /* @__PURE__ */ jsxs16("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
4693
4859
  "Target (",
4694
4860
  toLabel,
4695
4861
  ") \u2014 fades in"
@@ -4699,22 +4865,22 @@ function TransitionDesigner({
4699
4865
  ] }))
4700
4866
  ] });
4701
4867
  }
4702
- return /* @__PURE__ */ jsxs15("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
4703
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
4704
- /* @__PURE__ */ jsxs15("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
4705
- /* @__PURE__ */ jsx19("span", { className: "text-sas-text", children: fromLabel }),
4868
+ return /* @__PURE__ */ jsxs16("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
4869
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
4870
+ /* @__PURE__ */ jsxs16("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
4871
+ /* @__PURE__ */ jsx20("span", { className: "text-sas-text", children: fromLabel }),
4706
4872
  " \u2192",
4707
4873
  " ",
4708
- /* @__PURE__ */ jsx19("span", { className: "text-sas-text", children: toLabel }),
4874
+ /* @__PURE__ */ jsx20("span", { className: "text-sas-text", children: toLabel }),
4709
4875
  familyLabel ? ` \xB7 ${familyLabel}` : "",
4710
4876
  " \xB7 line up a track on each side to crossfade; leave one blank (or insert a gap) to fade."
4711
4877
  ] }),
4712
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 shrink-0", children: [
4713
- creatingKeys.size > 0 && /* @__PURE__ */ jsxs15("span", { className: "text-[10px] text-sas-accent whitespace-nowrap", "data-testid": `${testIdPrefix}-creating-count`, children: [
4878
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 shrink-0", children: [
4879
+ creatingKeys.size > 0 && /* @__PURE__ */ jsxs16("span", { className: "text-[10px] text-sas-accent whitespace-nowrap", "data-testid": `${testIdPrefix}-creating-count`, children: [
4714
4880
  creatingKeys.size,
4715
4881
  " creating\u2026"
4716
4882
  ] }),
4717
- /* @__PURE__ */ jsxs15(
4883
+ /* @__PURE__ */ jsxs16(
4718
4884
  "button",
4719
4885
  {
4720
4886
  type: "button",
@@ -4731,49 +4897,49 @@ function TransitionDesigner({
4731
4897
  )
4732
4898
  ] })
4733
4899
  ] }),
4734
- /* @__PURE__ */ jsxs15("div", { className: "grid grid-cols-[1fr_auto_1fr] gap-2", children: [
4735
- /* @__PURE__ */ jsxs15("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate", children: [
4900
+ /* @__PURE__ */ jsxs16("div", { className: "grid grid-cols-[1fr_auto_1fr] gap-2", children: [
4901
+ /* @__PURE__ */ jsxs16("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate", children: [
4736
4902
  "Origin (",
4737
4903
  fromLabel,
4738
4904
  ")"
4739
4905
  ] }),
4740
- /* @__PURE__ */ jsx19("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted text-center px-2", children: "Transition" }),
4741
- /* @__PURE__ */ jsxs15("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate text-right", children: [
4906
+ /* @__PURE__ */ jsx20("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted text-center px-2", children: "Transition" }),
4907
+ /* @__PURE__ */ jsxs16("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate text-right", children: [
4742
4908
  "Target (",
4743
4909
  toLabel,
4744
4910
  ")"
4745
4911
  ] })
4746
4912
  ] }),
4747
- load.status === "loading" && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
4748
- load.status === "error" && /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
4749
- load.status === "ready" && (rows.length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "text-xs text-sas-muted py-6 text-center", "data-testid": `${testIdPrefix}-empty`, children: [
4913
+ load.status === "loading" && /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
4914
+ load.status === "error" && /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
4915
+ 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
4916
  "No tracks to arrange in this panel for either scene. Add tracks to ",
4751
4917
  fromLabel,
4752
4918
  " or ",
4753
4919
  toLabel,
4754
4920
  " ",
4755
4921
  "first (or free one by deleting an existing crossfade/fade)."
4756
- ] }) : /* @__PURE__ */ jsx19("div", { className: "space-y-2", children: rows.map((row, i) => {
4922
+ ] }) : /* @__PURE__ */ jsx20("div", { className: "space-y-2", children: rows.map((row, i) => {
4757
4923
  const key = rowKey(row);
4758
4924
  const isCreatingThis = key !== null && creatingKeys.has(key);
4759
4925
  const errMsg = key !== null ? rowErrors[key] : void 0;
4760
- return /* @__PURE__ */ jsxs15(
4926
+ return /* @__PURE__ */ jsxs16(
4761
4927
  "div",
4762
4928
  {
4763
4929
  "data-testid": `${testIdPrefix}-row-${i}`,
4764
4930
  className: "grid grid-cols-[1fr_auto_1fr] gap-2 items-center",
4765
4931
  children: [
4766
4932
  renderCell("origin", i, row.originId),
4767
- /* @__PURE__ */ jsxs15("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
4768
- !row.type ? /* @__PURE__ */ jsx19("span", { className: "text-[10px] text-sas-muted/50", children: "\u2014" }) : row.type === "crossfade" ? /* @__PURE__ */ jsx19(
4933
+ /* @__PURE__ */ jsxs16("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
4934
+ !row.type ? /* @__PURE__ */ jsx20("span", { className: "text-[10px] text-sas-muted/50", children: "\u2014" }) : row.type === "crossfade" ? /* @__PURE__ */ jsx20(
4769
4935
  "span",
4770
4936
  {
4771
4937
  "data-testid": `${testIdPrefix}-type-${i}`,
4772
4938
  className: "text-[10px] font-medium px-1.5 py-0.5 rounded-sm border border-sas-accent/50 text-sas-accent",
4773
4939
  children: TYPE_LABEL[row.type]
4774
4940
  }
4775
- ) : audioEffectsEnabled ? /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1", "data-testid": `${testIdPrefix}-type-${i}`, children: [
4776
- /* @__PURE__ */ jsx19(
4941
+ ) : audioEffectsEnabled ? /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-1", "data-testid": `${testIdPrefix}-type-${i}`, children: [
4942
+ /* @__PURE__ */ jsx20(
4777
4943
  "select",
4778
4944
  {
4779
4945
  "data-testid": `${testIdPrefix}-effect-${i}`,
@@ -4783,11 +4949,11 @@ function TransitionDesigner({
4783
4949
  if (id) setRowEffect(id, e.target.value);
4784
4950
  },
4785
4951
  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__ */ jsx19("option", { value: eff, children: AUDIO_EFFECT_LABEL[eff] }, eff))
4952
+ children: AUDIO_EFFECTS.map((eff) => /* @__PURE__ */ jsx20("option", { value: eff, children: AUDIO_EFFECT_LABEL[eff] }, eff))
4787
4953
  }
4788
4954
  ),
4789
- /* @__PURE__ */ jsx19("span", { className: "text-[9px] text-sas-muted", children: row.type === "fade-out" ? "out" : "in" })
4790
- ] }) : /* @__PURE__ */ jsx19(
4955
+ /* @__PURE__ */ jsx20("span", { className: "text-[9px] text-sas-muted", children: row.type === "fade-out" ? "out" : "in" })
4956
+ ] }) : /* @__PURE__ */ jsx20(
4791
4957
  "span",
4792
4958
  {
4793
4959
  "data-testid": `${testIdPrefix}-type-${i}`,
@@ -4795,7 +4961,7 @@ function TransitionDesigner({
4795
4961
  children: TYPE_LABEL[row.type]
4796
4962
  }
4797
4963
  ),
4798
- isCreatingThis ? /* @__PURE__ */ jsx19("div", { className: "w-full", children: /* @__PURE__ */ jsx19(
4964
+ isCreatingThis ? /* @__PURE__ */ jsx20("div", { className: "w-full", children: /* @__PURE__ */ jsx20(
4799
4965
  SorceryProgressBar,
4800
4966
  {
4801
4967
  isLoading: true,
@@ -4803,7 +4969,7 @@ function TransitionDesigner({
4803
4969
  statusText: "CREATING",
4804
4970
  estimatedDurationMs: row.type === "crossfade" ? CROSSFADE_ESTIMATE_MS : FADE_ESTIMATE_MS
4805
4971
  }
4806
- ) }) : /* @__PURE__ */ jsx19(
4972
+ ) }) : /* @__PURE__ */ jsx20(
4807
4973
  "button",
4808
4974
  {
4809
4975
  type: "button",
@@ -4814,7 +4980,7 @@ function TransitionDesigner({
4814
4980
  children: "Create"
4815
4981
  }
4816
4982
  ),
4817
- errMsg && /* @__PURE__ */ jsx19(
4983
+ errMsg && /* @__PURE__ */ jsx20(
4818
4984
  "span",
4819
4985
  {
4820
4986
  "data-testid": `${testIdPrefix}-row-error-${i}`,
@@ -4833,8 +4999,8 @@ function TransitionDesigner({
4833
4999
  }
4834
5000
 
4835
5001
  // src/components/PanelMasterStrip.tsx
4836
- import { useMemo as useMemo7, useState as useState14 } from "react";
4837
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
5002
+ import { useMemo as useMemo7, useState as useState15 } from "react";
5003
+ import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
4838
5004
  function PanelMasterStrip({
4839
5005
  bus,
4840
5006
  levels = null,
@@ -4853,7 +5019,7 @@ function PanelMasterStrip({
4853
5019
  onToggleFxEnabled,
4854
5020
  onShowFxEditor
4855
5021
  }) {
4856
- const [search, setSearch] = useState14("");
5022
+ const [search, setSearch] = useState15("");
4857
5023
  const filtered = useMemo7(() => {
4858
5024
  const q = search.trim().toLowerCase();
4859
5025
  if (!q) return availableFx;
@@ -4861,14 +5027,14 @@ function PanelMasterStrip({
4861
5027
  (fx) => fx.name.toLowerCase().includes(q) || fx.manufacturer.toLowerCase().includes(q)
4862
5028
  );
4863
5029
  }, [availableFx, search]);
4864
- return /* @__PURE__ */ jsxs16(
5030
+ return /* @__PURE__ */ jsxs17(
4865
5031
  "div",
4866
5032
  {
4867
5033
  "data-testid": "panel-master-strip",
4868
5034
  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
5035
  children: [
4870
- /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
4871
- /* @__PURE__ */ jsx20(
5036
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
5037
+ /* @__PURE__ */ jsx21(
4872
5038
  "span",
4873
5039
  {
4874
5040
  className: "text-[9px] font-bold tracking-widest text-sas-muted/70 select-none",
@@ -4876,8 +5042,8 @@ function PanelMasterStrip({
4876
5042
  children: "BUS"
4877
5043
  }
4878
5044
  ),
4879
- /* @__PURE__ */ jsxs16("div", { className: "flex-1 min-w-[8rem] flex flex-col gap-0.5", children: [
4880
- /* @__PURE__ */ jsx20(
5045
+ /* @__PURE__ */ jsxs17("div", { className: "flex-1 min-w-[8rem] flex flex-col gap-0.5", children: [
5046
+ /* @__PURE__ */ jsx21(
4881
5047
  VolumeSlider,
4882
5048
  {
4883
5049
  value: dbToSlider(bus.volume),
@@ -4885,8 +5051,8 @@ function PanelMasterStrip({
4885
5051
  disabled
4886
5052
  }
4887
5053
  ),
4888
- /* @__PURE__ */ jsxs16("div", { className: "flex flex-col gap-px", "data-testid": "bus-meter", children: [
4889
- /* @__PURE__ */ jsx20(
5054
+ /* @__PURE__ */ jsxs17("div", { className: "flex flex-col gap-px", "data-testid": "bus-meter", children: [
5055
+ /* @__PURE__ */ jsx21(
4890
5056
  LevelMeter,
4891
5057
  {
4892
5058
  peakDb: levels?.leftDb ?? -120,
@@ -4896,7 +5062,7 @@ function PanelMasterStrip({
4896
5062
  "data-testid": "bus-meter-left"
4897
5063
  }
4898
5064
  ),
4899
- /* @__PURE__ */ jsx20(
5065
+ /* @__PURE__ */ jsx21(
4900
5066
  LevelMeter,
4901
5067
  {
4902
5068
  peakDb: levels?.rightDb ?? -120,
@@ -4907,7 +5073,7 @@ function PanelMasterStrip({
4907
5073
  )
4908
5074
  ] })
4909
5075
  ] }),
4910
- /* @__PURE__ */ jsx20(
5076
+ /* @__PURE__ */ jsx21(
4911
5077
  "button",
4912
5078
  {
4913
5079
  "data-testid": "bus-mute-button",
@@ -4918,7 +5084,7 @@ function PanelMasterStrip({
4918
5084
  children: "M"
4919
5085
  }
4920
5086
  ),
4921
- /* @__PURE__ */ jsx20(
5087
+ /* @__PURE__ */ jsx21(
4922
5088
  "button",
4923
5089
  {
4924
5090
  "data-testid": "bus-solo-button",
@@ -4929,14 +5095,14 @@ function PanelMasterStrip({
4929
5095
  children: "S"
4930
5096
  }
4931
5097
  ),
4932
- /* @__PURE__ */ jsx20("div", { className: "flex items-center gap-1 max-w-[45%] min-w-0 overflow-x-auto", children: bus.fx.map((fx) => /* @__PURE__ */ jsxs16(
5098
+ /* @__PURE__ */ jsx21("div", { className: "flex items-center gap-1 max-w-[45%] min-w-0 overflow-x-auto", children: bus.fx.map((fx) => /* @__PURE__ */ jsxs17(
4933
5099
  "span",
4934
5100
  {
4935
5101
  "data-testid": `bus-fx-chip-${fx.index}`,
4936
5102
  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
5103
  title: `${fx.name}${fx.enabled ? "" : " (bypassed)"}`,
4938
5104
  children: [
4939
- /* @__PURE__ */ jsx20(
5105
+ /* @__PURE__ */ jsx21(
4940
5106
  "button",
4941
5107
  {
4942
5108
  "data-testid": `bus-fx-toggle-${fx.index}`,
@@ -4947,7 +5113,7 @@ function PanelMasterStrip({
4947
5113
  children: fx.enabled ? "\u25CF" : "\u25CB"
4948
5114
  }
4949
5115
  ),
4950
- onShowFxEditor ? /* @__PURE__ */ jsx20(
5116
+ onShowFxEditor ? /* @__PURE__ */ jsx21(
4951
5117
  "button",
4952
5118
  {
4953
5119
  "data-testid": `bus-fx-edit-${fx.index}`,
@@ -4957,8 +5123,8 @@ function PanelMasterStrip({
4957
5123
  title: `Open ${fx.name} editor`,
4958
5124
  children: fx.name
4959
5125
  }
4960
- ) : /* @__PURE__ */ jsx20("span", { className: "max-w-[80px] truncate", children: fx.name }),
4961
- /* @__PURE__ */ jsx20(
5126
+ ) : /* @__PURE__ */ jsx21("span", { className: "max-w-[80px] truncate", children: fx.name }),
5127
+ /* @__PURE__ */ jsx21(
4962
5128
  "button",
4963
5129
  {
4964
5130
  "data-testid": `bus-fx-remove-${fx.index}`,
@@ -4973,7 +5139,7 @@ function PanelMasterStrip({
4973
5139
  },
4974
5140
  `${fx.index}:${fx.pluginId}`
4975
5141
  )) }),
4976
- /* @__PURE__ */ jsx20(
5142
+ /* @__PURE__ */ jsx21(
4977
5143
  "button",
4978
5144
  {
4979
5145
  "data-testid": "bus-fx-add-button",
@@ -4985,9 +5151,9 @@ function PanelMasterStrip({
4985
5151
  }
4986
5152
  )
4987
5153
  ] }),
4988
- fxPickerOpen && /* @__PURE__ */ jsxs16("div", { "data-testid": "bus-fx-picker", className: "flex flex-col gap-2 pt-1", children: [
4989
- /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
4990
- /* @__PURE__ */ jsx20(
5154
+ fxPickerOpen && /* @__PURE__ */ jsxs17("div", { "data-testid": "bus-fx-picker", className: "flex flex-col gap-2 pt-1", children: [
5155
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
5156
+ /* @__PURE__ */ jsx21(
4991
5157
  "input",
4992
5158
  {
4993
5159
  type: "text",
@@ -4997,7 +5163,7 @@ function PanelMasterStrip({
4997
5163
  className: "sas-input flex-1 px-2 py-1 text-xs"
4998
5164
  }
4999
5165
  ),
5000
- onRefreshFx && /* @__PURE__ */ jsx20(
5166
+ onRefreshFx && /* @__PURE__ */ jsx21(
5001
5167
  "button",
5002
5168
  {
5003
5169
  onClick: () => onRefreshFx(),
@@ -5008,8 +5174,8 @@ function PanelMasterStrip({
5008
5174
  }
5009
5175
  )
5010
5176
  ] }),
5011
- fxLoading && availableFx.length === 0 ? /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ jsxs16("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
5012
- filtered.map((fx) => /* @__PURE__ */ jsxs16(
5177
+ fxLoading && availableFx.length === 0 ? /* @__PURE__ */ jsx21("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: [
5178
+ filtered.map((fx) => /* @__PURE__ */ jsxs17(
5013
5179
  "button",
5014
5180
  {
5015
5181
  "data-testid": `bus-fx-pick-${fx.pluginId}`,
@@ -5017,13 +5183,13 @@ function PanelMasterStrip({
5017
5183
  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
5184
  title: `${fx.name} by ${fx.manufacturer} (${fx.type.toUpperCase()})`,
5019
5185
  children: [
5020
- /* @__PURE__ */ jsx20("span", { className: "text-xs font-medium truncate w-full", children: fx.name }),
5021
- /* @__PURE__ */ jsx20("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: fx.manufacturer || fx.type.toUpperCase() })
5186
+ /* @__PURE__ */ jsx21("span", { className: "text-xs font-medium truncate w-full", children: fx.name }),
5187
+ /* @__PURE__ */ jsx21("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: fx.manufacturer || fx.type.toUpperCase() })
5022
5188
  ]
5023
5189
  },
5024
5190
  fx.pluginId
5025
5191
  )),
5026
- filtered.length === 0 && /* @__PURE__ */ jsx20("div", { className: "col-span-3 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No FX plugins found" })
5192
+ filtered.length === 0 && /* @__PURE__ */ jsx21("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
5193
  ] })
5028
5194
  ] })
5029
5195
  ]
@@ -5032,18 +5198,18 @@ function PanelMasterStrip({
5032
5198
  }
5033
5199
 
5034
5200
  // src/hooks/usePanelBus.ts
5035
- import { useCallback as useCallback10, useEffect as useEffect11, useRef as useRef12, useState as useState15 } from "react";
5201
+ import { useCallback as useCallback11, useEffect as useEffect12, useRef as useRef12, useState as useState16 } from "react";
5036
5202
  var LEVELS_POLL_MS = 66;
5037
5203
  function usePanelBus(host, activeSceneId) {
5038
5204
  const supported = typeof host.getPanelBusState === "function";
5039
- const [bus, setBus] = useState15(null);
5040
- const [levels, setLevels] = useState15(null);
5041
- const [availableFx, setAvailableFx] = useState15([]);
5042
- const [fxLoading, setFxLoading] = useState15(false);
5043
- const [fxPickerOpen, setFxPickerOpen] = useState15(false);
5205
+ const [bus, setBus] = useState16(null);
5206
+ const [levels, setLevels] = useState16(null);
5207
+ const [availableFx, setAvailableFx] = useState16([]);
5208
+ const [fxLoading, setFxLoading] = useState16(false);
5209
+ const [fxPickerOpen, setFxPickerOpen] = useState16(false);
5044
5210
  const fxLoadedRef = useRef12(false);
5045
5211
  const loadSeqRef = useRef12(0);
5046
- const reload = useCallback10(async () => {
5212
+ const reload = useCallback11(async () => {
5047
5213
  if (!supported || !activeSceneId || !host.getPanelBusState) {
5048
5214
  setBus(null);
5049
5215
  return;
@@ -5055,12 +5221,12 @@ function usePanelBus(host, activeSceneId) {
5055
5221
  } catch {
5056
5222
  }
5057
5223
  }, [host, activeSceneId, supported]);
5058
- useEffect11(() => {
5224
+ useEffect12(() => {
5059
5225
  setBus(null);
5060
5226
  setFxPickerOpen(false);
5061
5227
  void reload();
5062
5228
  }, [reload]);
5063
- useEffect11(() => {
5229
+ useEffect12(() => {
5064
5230
  if (!supported || !activeSceneId || !bus?.engaged || !host.getPanelBusLevels) {
5065
5231
  setLevels(null);
5066
5232
  return;
@@ -5082,7 +5248,7 @@ function usePanelBus(host, activeSceneId) {
5082
5248
  clearInterval(id);
5083
5249
  };
5084
5250
  }, [supported, activeSceneId, bus?.engaged, host]);
5085
- const loadFxList = useCallback10(
5251
+ const loadFxList = useCallback11(
5086
5252
  async (opts) => {
5087
5253
  if (!supported || !host.getAvailableFx) return;
5088
5254
  if (fxLoadedRef.current && !opts.force && !opts.rescan) return;
@@ -5098,14 +5264,14 @@ function usePanelBus(host, activeSceneId) {
5098
5264
  },
5099
5265
  [host, supported]
5100
5266
  );
5101
- const openPicker = useCallback10(
5267
+ const openPicker = useCallback11(
5102
5268
  (open) => {
5103
5269
  setFxPickerOpen(open);
5104
5270
  if (open) void loadFxList({});
5105
5271
  },
5106
5272
  [loadFxList]
5107
5273
  );
5108
- const mutate = useCallback10(
5274
+ const mutate = useCallback11(
5109
5275
  (fn) => {
5110
5276
  if (!fn || !activeSceneId) return;
5111
5277
  void (async () => {
@@ -5149,8 +5315,8 @@ function usePanelBus(host, activeSceneId) {
5149
5315
  }
5150
5316
 
5151
5317
  // src/components/DownloadPackButton.tsx
5152
- import { useCallback as useCallback11, useEffect as useEffect12, useState as useState16 } from "react";
5153
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
5318
+ import { useCallback as useCallback12, useEffect as useEffect13, useState as useState17 } from "react";
5319
+ import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
5154
5320
  function formatSize(bytes) {
5155
5321
  if (!bytes || bytes <= 0) return "";
5156
5322
  const gb = bytes / 1024 ** 3;
@@ -5166,10 +5332,10 @@ var DownloadPackButton = ({
5166
5332
  variant = "compact",
5167
5333
  onDownloadComplete
5168
5334
  }) => {
5169
- const [status, setStatus] = useState16("idle");
5170
- const [progress, setProgress] = useState16(0);
5171
- const [errorMessage, setErrorMessage] = useState16(null);
5172
- useEffect12(() => {
5335
+ const [status, setStatus] = useState17("idle");
5336
+ const [progress, setProgress] = useState17(0);
5337
+ const [errorMessage, setErrorMessage] = useState17(null);
5338
+ useEffect13(() => {
5173
5339
  const unsub = host.onSamplePackProgress(packId, (p) => {
5174
5340
  setStatus(p.status);
5175
5341
  setProgress(p.progress);
@@ -5184,7 +5350,7 @@ var DownloadPackButton = ({
5184
5350
  });
5185
5351
  return unsub;
5186
5352
  }, [host, packId, onDownloadComplete]);
5187
- const handleClick = useCallback11(async () => {
5353
+ const handleClick = useCallback12(async () => {
5188
5354
  if (status !== "idle" && status !== "error") return;
5189
5355
  try {
5190
5356
  setStatus("downloading");
@@ -5238,8 +5404,8 @@ var DownloadPackButton = ({
5238
5404
  } else {
5239
5405
  className = `${baseClasses} text-sas-muted hover:text-sas-accent border-sas-border hover:border-sas-accent`;
5240
5406
  }
5241
- return /* @__PURE__ */ jsxs17("div", { children: [
5242
- /* @__PURE__ */ jsx21(
5407
+ return /* @__PURE__ */ jsxs18("div", { children: [
5408
+ /* @__PURE__ */ jsx22(
5243
5409
  "button",
5244
5410
  {
5245
5411
  "data-testid": `download-pack-button-${packId}`,
@@ -5250,12 +5416,12 @@ var DownloadPackButton = ({
5250
5416
  children: buttonLabel
5251
5417
  }
5252
5418
  ),
5253
- variant === "large" && status === "error" && errorMessage && /* @__PURE__ */ jsx21("div", { className: "text-xs text-sas-danger mt-2", "data-testid": `download-pack-error-${packId}`, children: errorMessage })
5419
+ variant === "large" && status === "error" && errorMessage && /* @__PURE__ */ jsx22("div", { className: "text-xs text-sas-danger mt-2", "data-testid": `download-pack-error-${packId}`, children: errorMessage })
5254
5420
  ] });
5255
5421
  };
5256
5422
 
5257
5423
  // src/components/SamplePackCTACard.tsx
5258
- import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
5424
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
5259
5425
  var SamplePackCTACard = ({
5260
5426
  host,
5261
5427
  pack,
@@ -5263,7 +5429,7 @@ var SamplePackCTACard = ({
5263
5429
  onDownloadComplete
5264
5430
  }) => {
5265
5431
  if (status === "checking") {
5266
- return /* @__PURE__ */ jsx22(
5432
+ return /* @__PURE__ */ jsx23(
5267
5433
  "div",
5268
5434
  {
5269
5435
  "data-testid": `sample-pack-cta-checking-${pack.packId}`,
@@ -5274,16 +5440,16 @@ var SamplePackCTACard = ({
5274
5440
  }
5275
5441
  const headline = status === "stale" ? `${pack.displayName} update available` : `${pack.displayName} not installed`;
5276
5442
  const sublabel = status === "stale" ? `A newer version is available for download.` : pack.description;
5277
- return /* @__PURE__ */ jsxs18(
5443
+ return /* @__PURE__ */ jsxs19(
5278
5444
  "div",
5279
5445
  {
5280
5446
  "data-testid": `sample-pack-cta-${pack.packId}`,
5281
5447
  className: "flex flex-col items-center justify-center py-12 px-6 text-center",
5282
5448
  children: [
5283
- /* @__PURE__ */ jsx22("div", { className: "text-sm uppercase tracking-wide text-sas-muted mb-2", children: status === "stale" ? "Update available" : "Sample library not installed" }),
5284
- /* @__PURE__ */ jsx22("div", { className: "text-base text-sas-text mb-1", children: headline }),
5285
- /* @__PURE__ */ jsx22("div", { className: "text-xs text-sas-muted mb-6 max-w-md", children: sublabel }),
5286
- /* @__PURE__ */ jsx22(
5449
+ /* @__PURE__ */ jsx23("div", { className: "text-sm uppercase tracking-wide text-sas-muted mb-2", children: status === "stale" ? "Update available" : "Sample library not installed" }),
5450
+ /* @__PURE__ */ jsx23("div", { className: "text-base text-sas-text mb-1", children: headline }),
5451
+ /* @__PURE__ */ jsx23("div", { className: "text-xs text-sas-muted mb-6 max-w-md", children: sublabel }),
5452
+ /* @__PURE__ */ jsx23(
5287
5453
  DownloadPackButton,
5288
5454
  {
5289
5455
  host,
@@ -5300,7 +5466,7 @@ var SamplePackCTACard = ({
5300
5466
  };
5301
5467
 
5302
5468
  // src/components/WaveformView.tsx
5303
- import { useEffect as useEffect13, useRef as useRef13, useState as useState17 } from "react";
5469
+ import { useEffect as useEffect14, useRef as useRef13, useState as useState18 } from "react";
5304
5470
 
5305
5471
  // src/components/waveform.ts
5306
5472
  function computePeaks(audioBuffer, bins, targetSamples) {
@@ -5363,7 +5529,7 @@ function drawWaveform(canvas, peaks, options = {}) {
5363
5529
  }
5364
5530
 
5365
5531
  // src/components/WaveformView.tsx
5366
- import { jsx as jsx23 } from "react/jsx-runtime";
5532
+ import { jsx as jsx24 } from "react/jsx-runtime";
5367
5533
  var WaveformView = ({
5368
5534
  host,
5369
5535
  filePath,
@@ -5373,8 +5539,8 @@ var WaveformView = ({
5373
5539
  targetSamples
5374
5540
  }) => {
5375
5541
  const canvasRef = useRef13(null);
5376
- const [peaks, setPeaks] = useState17(null);
5377
- useEffect13(() => {
5542
+ const [peaks, setPeaks] = useState18(null);
5543
+ useEffect14(() => {
5378
5544
  let cancelled = false;
5379
5545
  let audioContext = null;
5380
5546
  (async () => {
@@ -5400,7 +5566,7 @@ var WaveformView = ({
5400
5566
  cancelled = true;
5401
5567
  };
5402
5568
  }, [host, filePath, bins, targetSamples]);
5403
- useEffect13(() => {
5569
+ useEffect14(() => {
5404
5570
  if (!peaks) return;
5405
5571
  const canvas = canvasRef.current;
5406
5572
  if (!canvas) return;
@@ -5411,7 +5577,7 @@ var WaveformView = ({
5411
5577
  observer.observe(canvas);
5412
5578
  return () => observer.disconnect();
5413
5579
  }, [peaks, fillStyle]);
5414
- return /* @__PURE__ */ jsx23(
5580
+ return /* @__PURE__ */ jsx24(
5415
5581
  "canvas",
5416
5582
  {
5417
5583
  ref: canvasRef,
@@ -5422,8 +5588,8 @@ var WaveformView = ({
5422
5588
  };
5423
5589
 
5424
5590
  // src/components/ScrollingWaveform.tsx
5425
- import { useEffect as useEffect14, useRef as useRef14 } from "react";
5426
- import { jsx as jsx24 } from "react/jsx-runtime";
5591
+ import { useEffect as useEffect15, useRef as useRef14 } from "react";
5592
+ import { jsx as jsx25 } from "react/jsx-runtime";
5427
5593
  var ScrollingWaveform = ({
5428
5594
  getPeakDb,
5429
5595
  active,
@@ -5435,7 +5601,7 @@ var ScrollingWaveform = ({
5435
5601
  const ringRef = useRef14(new Float32Array(columns));
5436
5602
  const writeIdxRef = useRef14(0);
5437
5603
  const rafRef = useRef14(null);
5438
- useEffect14(() => {
5604
+ useEffect15(() => {
5439
5605
  if (ringRef.current.length !== columns) {
5440
5606
  const next = new Float32Array(columns);
5441
5607
  const prev = ringRef.current;
@@ -5447,7 +5613,7 @@ var ScrollingWaveform = ({
5447
5613
  writeIdxRef.current = writeIdxRef.current % columns;
5448
5614
  }
5449
5615
  }, [columns]);
5450
- useEffect14(() => {
5616
+ useEffect15(() => {
5451
5617
  if (!active) {
5452
5618
  if (rafRef.current !== null) {
5453
5619
  cancelAnimationFrame(rafRef.current);
@@ -5499,7 +5665,7 @@ var ScrollingWaveform = ({
5499
5665
  }
5500
5666
  };
5501
5667
  }, [active, getPeakDb, fillStyle]);
5502
- return /* @__PURE__ */ jsx24(
5668
+ return /* @__PURE__ */ jsx25(
5503
5669
  "canvas",
5504
5670
  {
5505
5671
  ref: canvasRef,
@@ -5510,8 +5676,8 @@ var ScrollingWaveform = ({
5510
5676
  };
5511
5677
 
5512
5678
  // src/components/OffsetScrubber.tsx
5513
- import { useCallback as useCallback12, useEffect as useEffect15, useMemo as useMemo8, useRef as useRef15, useState as useState18 } from "react";
5514
- import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
5679
+ import { useCallback as useCallback13, useEffect as useEffect16, useMemo as useMemo8, useRef as useRef15, useState as useState19 } from "react";
5680
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
5515
5681
  var SLIDER_HEIGHT_PX = 28;
5516
5682
  var TICK_HEIGHT_PX = 14;
5517
5683
  var DOWNBEAT_TICK_HEIGHT_PX = 22;
@@ -5525,9 +5691,9 @@ function OffsetScrubber({
5525
5691
  disabled = false
5526
5692
  }) {
5527
5693
  const trackRef = useRef15(null);
5528
- const [draftOffset, setDraftOffset] = useState18(offsetSamples);
5529
- const [isDragging, setIsDragging] = useState18(false);
5530
- useEffect15(() => {
5694
+ const [draftOffset, setDraftOffset] = useState19(offsetSamples);
5695
+ const [isDragging, setIsDragging] = useState19(false);
5696
+ useEffect16(() => {
5531
5697
  if (!isDragging) setDraftOffset(offsetSamples);
5532
5698
  }, [offsetSamples, isDragging]);
5533
5699
  const sampleRate = cuePoints?.sample_rate ?? 44100;
@@ -5536,14 +5702,14 @@ function OffsetScrubber({
5536
5702
  return Math.round(60 / projectBpm * sampleRate);
5537
5703
  }, [projectBpm, sampleRate]);
5538
5704
  const rangeSamples = beatsForRange * meter;
5539
- const sampleToFraction = useCallback12(
5705
+ const sampleToFraction = useCallback13(
5540
5706
  (sample) => {
5541
5707
  const clamped = Math.max(-rangeSamples, Math.min(rangeSamples, sample));
5542
5708
  return (clamped + rangeSamples) / (2 * rangeSamples);
5543
5709
  },
5544
5710
  [rangeSamples]
5545
5711
  );
5546
- const fractionToSample = useCallback12(
5712
+ const fractionToSample = useCallback13(
5547
5713
  (fraction) => {
5548
5714
  const clamped = Math.max(0, Math.min(1, fraction));
5549
5715
  return Math.round(clamped * 2 * rangeSamples - rangeSamples);
@@ -5557,7 +5723,7 @@ function OffsetScrubber({
5557
5723
  const negatives = positives.slice(1).map((p) => -p);
5558
5724
  return [...negatives, ...positives].sort((a, b) => a - b);
5559
5725
  }, [cuePoints]);
5560
- const snapToBeat = useCallback12(
5726
+ const snapToBeat = useCallback13(
5561
5727
  (sample) => {
5562
5728
  if (snapTargets.length === 0) return sample;
5563
5729
  let best = snapTargets[0];
@@ -5573,7 +5739,7 @@ function OffsetScrubber({
5573
5739
  },
5574
5740
  [snapTargets]
5575
5741
  );
5576
- const handlePointerDown = useCallback12(
5742
+ const handlePointerDown = useCallback13(
5577
5743
  (e) => {
5578
5744
  if (disabled || !cuePoints) return;
5579
5745
  e.preventDefault();
@@ -5607,7 +5773,7 @@ function OffsetScrubber({
5607
5773
  },
5608
5774
  [disabled, cuePoints, fractionToSample, onChange, snapToBeat]
5609
5775
  );
5610
- const handleResetToZero = useCallback12(() => {
5776
+ const handleResetToZero = useCallback13(() => {
5611
5777
  if (disabled) return;
5612
5778
  setDraftOffset(0);
5613
5779
  onChange(0);
@@ -5626,9 +5792,9 @@ function OffsetScrubber({
5626
5792
  });
5627
5793
  }, [cuePoints, sampleToFraction]);
5628
5794
  const isDisabled = disabled || !cuePoints || cuePoints.beats.length === 0;
5629
- return /* @__PURE__ */ jsxs19("div", { "data-testid": "offset-scrubber", className: "flex items-center gap-2 w-full", children: [
5630
- /* @__PURE__ */ jsx25("span", { className: "text-[9px] text-sas-muted/60 uppercase tracking-wide flex-shrink-0", children: "Align" }),
5631
- /* @__PURE__ */ jsxs19(
5795
+ return /* @__PURE__ */ jsxs20("div", { "data-testid": "offset-scrubber", className: "flex items-center gap-2 w-full", children: [
5796
+ /* @__PURE__ */ jsx26("span", { className: "text-[9px] text-sas-muted/60 uppercase tracking-wide flex-shrink-0", children: "Align" }),
5797
+ /* @__PURE__ */ jsxs20(
5632
5798
  "div",
5633
5799
  {
5634
5800
  ref: trackRef,
@@ -5644,7 +5810,7 @@ function OffsetScrubber({
5644
5810
  "aria-valuenow": draftOffset,
5645
5811
  "aria-disabled": isDisabled,
5646
5812
  children: [
5647
- /* @__PURE__ */ jsx25(
5813
+ /* @__PURE__ */ jsx26(
5648
5814
  "div",
5649
5815
  {
5650
5816
  "aria-hidden": "true",
@@ -5652,7 +5818,7 @@ function OffsetScrubber({
5652
5818
  style: { left: "50%" }
5653
5819
  }
5654
5820
  ),
5655
- ticks.map((t) => /* @__PURE__ */ jsx25(
5821
+ ticks.map((t) => /* @__PURE__ */ jsx26(
5656
5822
  "div",
5657
5823
  {
5658
5824
  "data-testid": t.isDownbeat ? "offset-tick-downbeat" : "offset-tick",
@@ -5667,7 +5833,7 @@ function OffsetScrubber({
5667
5833
  },
5668
5834
  t.i
5669
5835
  )),
5670
- /* @__PURE__ */ jsx25(
5836
+ /* @__PURE__ */ jsx26(
5671
5837
  "div",
5672
5838
  {
5673
5839
  "data-testid": "offset-scrubber-thumb",
@@ -5684,7 +5850,7 @@ function OffsetScrubber({
5684
5850
  ]
5685
5851
  }
5686
5852
  ),
5687
- /* @__PURE__ */ jsx25(
5853
+ /* @__PURE__ */ jsx26(
5688
5854
  "span",
5689
5855
  {
5690
5856
  "data-testid": "offset-scrubber-readout",
@@ -5692,7 +5858,7 @@ function OffsetScrubber({
5692
5858
  children: formatOffset(draftOffset, sampleRate)
5693
5859
  }
5694
5860
  ),
5695
- /* @__PURE__ */ jsx25(
5861
+ /* @__PURE__ */ jsx26(
5696
5862
  "button",
5697
5863
  {
5698
5864
  type: "button",
@@ -5704,7 +5870,7 @@ function OffsetScrubber({
5704
5870
  children: "\u2316"
5705
5871
  }
5706
5872
  ),
5707
- bpmMismatch && /* @__PURE__ */ jsx25(
5873
+ bpmMismatch && /* @__PURE__ */ jsx26(
5708
5874
  "span",
5709
5875
  {
5710
5876
  "data-testid": "offset-bpm-mismatch",
@@ -5776,16 +5942,16 @@ function synthesizeCuePoints({
5776
5942
  }
5777
5943
 
5778
5944
  // src/panel-core/useGeneratorPanelCore.tsx
5779
- import { useState as useState23, useEffect as useEffect18, useCallback as useCallback16, useRef as useRef19, useMemo as useMemo10 } from "react";
5945
+ import { useState as useState24, useEffect as useEffect19, useCallback as useCallback17, useRef as useRef19, useMemo as useMemo10 } from "react";
5780
5946
 
5781
5947
  // src/hooks/useSceneState.ts
5782
- import { useState as useState19, useCallback as useCallback13, useRef as useRef16 } from "react";
5948
+ import { useState as useState20, useCallback as useCallback14, useRef as useRef16 } from "react";
5783
5949
  function useSceneState(activeSceneId, initialValue) {
5784
- const [stateMap, setStateMap] = useState19(() => /* @__PURE__ */ new Map());
5950
+ const [stateMap, setStateMap] = useState20(() => /* @__PURE__ */ new Map());
5785
5951
  const activeSceneIdRef = useRef16(activeSceneId);
5786
5952
  activeSceneIdRef.current = activeSceneId;
5787
5953
  const currentValue = activeSceneId !== null && stateMap.has(activeSceneId) ? stateMap.get(activeSceneId) : initialValue;
5788
- const setForCurrentScene = useCallback13((value) => {
5954
+ const setForCurrentScene = useCallback14((value) => {
5789
5955
  const sid = activeSceneIdRef.current;
5790
5956
  if (sid === null) return;
5791
5957
  setStateMap((prev) => {
@@ -5796,7 +5962,7 @@ function useSceneState(activeSceneId, initialValue) {
5796
5962
  return newMap;
5797
5963
  });
5798
5964
  }, [initialValue]);
5799
- const setForScene = useCallback13((sceneId, value) => {
5965
+ const setForScene = useCallback14((sceneId, value) => {
5800
5966
  setStateMap((prev) => {
5801
5967
  const current = prev.has(sceneId) ? prev.get(sceneId) : initialValue;
5802
5968
  const next = typeof value === "function" ? value(current) : value;
@@ -5809,10 +5975,10 @@ function useSceneState(activeSceneId, initialValue) {
5809
5975
  }
5810
5976
 
5811
5977
  // src/hooks/useAnySolo.ts
5812
- import { useEffect as useEffect16, useState as useState20 } from "react";
5978
+ import { useEffect as useEffect17, useState as useState21 } from "react";
5813
5979
  function useAnySolo(host) {
5814
- const [anySolo, setAnySolo] = useState20(false);
5815
- useEffect16(() => {
5980
+ const [anySolo, setAnySolo] = useState21(false);
5981
+ useEffect17(() => {
5816
5982
  let active = true;
5817
5983
  const refresh = () => {
5818
5984
  host.isAnySoloActive().then((v) => {
@@ -5831,7 +5997,7 @@ function useAnySolo(host) {
5831
5997
  }
5832
5998
 
5833
5999
  // src/hooks/useSoundHistory.ts
5834
- import { useCallback as useCallback14, useMemo as useMemo9, useRef as useRef17, useState as useState21 } from "react";
6000
+ import { useCallback as useCallback15, useMemo as useMemo9, useRef as useRef17, useState as useState22 } from "react";
5835
6001
  var EMPTY = { entries: [], cursor: -1 };
5836
6002
  function sameDescriptor(a, b) {
5837
6003
  if (a === b) return true;
@@ -5848,9 +6014,9 @@ function useSoundHistory(applySound, opts = {}) {
5848
6014
  const onChangeRef = useRef17(opts.onChange);
5849
6015
  onChangeRef.current = opts.onChange;
5850
6016
  const dataRef = useRef17({});
5851
- const [, setVersion] = useState21(0);
5852
- const bump = useCallback14(() => setVersion((v) => v + 1), []);
5853
- const commit = useCallback14(
6017
+ const [, setVersion] = useState22(0);
6018
+ const bump = useCallback15(() => setVersion((v) => v + 1), []);
6019
+ const commit = useCallback15(
5854
6020
  (trackId, next, notify) => {
5855
6021
  dataRef.current = { ...dataRef.current, [trackId]: next };
5856
6022
  bump();
@@ -5858,7 +6024,7 @@ function useSoundHistory(applySound, opts = {}) {
5858
6024
  },
5859
6025
  [bump]
5860
6026
  );
5861
- const record = useCallback14(
6027
+ const record = useCallback15(
5862
6028
  (trackId, descriptor, label) => {
5863
6029
  const h = dataRef.current[trackId];
5864
6030
  const current = h && h.cursor >= 0 ? h.entries[h.cursor] : void 0;
@@ -5873,7 +6039,7 @@ function useSoundHistory(applySound, opts = {}) {
5873
6039
  },
5874
6040
  [max, commit]
5875
6041
  );
5876
- const restoreTo = useCallback14(
6042
+ const restoreTo = useCallback15(
5877
6043
  async (trackId, index) => {
5878
6044
  const h = dataRef.current[trackId];
5879
6045
  if (!h || index < 0 || index >= h.entries.length || index === h.cursor) return false;
@@ -5883,7 +6049,7 @@ function useSoundHistory(applySound, opts = {}) {
5883
6049
  },
5884
6050
  [commit]
5885
6051
  );
5886
- const undo = useCallback14(
6052
+ const undo = useCallback15(
5887
6053
  (trackId) => {
5888
6054
  const h = dataRef.current[trackId];
5889
6055
  if (!h || h.cursor <= 0) return Promise.resolve(false);
@@ -5891,7 +6057,7 @@ function useSoundHistory(applySound, opts = {}) {
5891
6057
  },
5892
6058
  [restoreTo]
5893
6059
  );
5894
- const toggleFavorite = useCallback14(
6060
+ const toggleFavorite = useCallback15(
5895
6061
  (trackId, index) => {
5896
6062
  const h = dataRef.current[trackId];
5897
6063
  if (!h || index < 0 || index >= h.entries.length) return;
@@ -5900,7 +6066,7 @@ function useSoundHistory(applySound, opts = {}) {
5900
6066
  },
5901
6067
  [commit]
5902
6068
  );
5903
- const restore = useCallback14(
6069
+ const restore = useCallback15(
5904
6070
  (trackId, state) => {
5905
6071
  const entries = Array.isArray(state?.entries) ? [...state.entries] : [];
5906
6072
  const raw = typeof state?.cursor === "number" ? state.cursor : entries.length - 1;
@@ -5909,15 +6075,15 @@ function useSoundHistory(applySound, opts = {}) {
5909
6075
  },
5910
6076
  [commit]
5911
6077
  );
5912
- const list = useCallback14(
6078
+ const list = useCallback15(
5913
6079
  (trackId) => dataRef.current[trackId] ?? EMPTY,
5914
6080
  []
5915
6081
  );
5916
- const canUndo = useCallback14((trackId) => {
6082
+ const canUndo = useCallback15((trackId) => {
5917
6083
  const h = dataRef.current[trackId];
5918
6084
  return !!h && h.cursor > 0;
5919
6085
  }, []);
5920
- const clear = useCallback14(
6086
+ const clear = useCallback15(
5921
6087
  (trackId) => {
5922
6088
  if (dataRef.current[trackId]) {
5923
6089
  const next = { ...dataRef.current };
@@ -5929,7 +6095,7 @@ function useSoundHistory(applySound, opts = {}) {
5929
6095
  },
5930
6096
  [bump]
5931
6097
  );
5932
- const reset = useCallback14(() => {
6098
+ const reset = useCallback15(() => {
5933
6099
  dataRef.current = {};
5934
6100
  bump();
5935
6101
  }, [bump]);
@@ -6009,7 +6175,7 @@ function resolveTrackGroups(parsedGroups, tracks, getDbId, opts = {}) {
6009
6175
  }
6010
6176
 
6011
6177
  // src/panel-core/useTransitionOps.ts
6012
- import { useCallback as useCallback15, useEffect as useEffect17, useRef as useRef18, useState as useState22 } from "react";
6178
+ import { useCallback as useCallback16, useEffect as useEffect18, useRef as useRef18, useState as useState23 } from "react";
6013
6179
  function useTransitionOps({
6014
6180
  host,
6015
6181
  adapter,
@@ -6027,7 +6193,7 @@ function useTransitionOps({
6027
6193
  }) {
6028
6194
  const { identity } = adapter;
6029
6195
  const appliedFadeAutomationRef = useRef18(/* @__PURE__ */ new Set());
6030
- const applyCrossfadeAutomation = useCallback15(
6196
+ const applyCrossfadeAutomation = useCallback16(
6031
6197
  async (originTrackId, targetTrackId, bars, bpm, sliderPos) => {
6032
6198
  if (host.setTrackVolumeAutomation) {
6033
6199
  const curves = buildCrossfadeVolumeCurves(bars, bpm, sliderPos);
@@ -6044,7 +6210,7 @@ function useTransitionOps({
6044
6210
  },
6045
6211
  [host]
6046
6212
  );
6047
- const applyFadeAutomation = useCallback15(
6213
+ const applyFadeAutomation = useCallback16(
6048
6214
  async (trackId, direction, bars, bpm, sliderPos, gesture) => {
6049
6215
  if (!host.setTrackVolumeAutomation) return;
6050
6216
  const points = buildFadeVolumeCurve(bars, bpm, direction, sliderPos, gesture);
@@ -6053,7 +6219,7 @@ function useTransitionOps({
6053
6219
  },
6054
6220
  [host]
6055
6221
  );
6056
- const copyTrackFx = useCallback15(
6222
+ const copyTrackFx = useCallback16(
6057
6223
  async (newTrackId, sourceDbId) => {
6058
6224
  if (typeof host.copyTrackFxFrom !== "function") return;
6059
6225
  try {
@@ -6070,8 +6236,8 @@ function useTransitionOps({
6070
6236
  },
6071
6237
  [host]
6072
6238
  );
6073
- const [isCreatingCrossfade, setIsCreatingCrossfade] = useState22(false);
6074
- const handleCreateCrossfade = useCallback15(
6239
+ const [isCreatingCrossfade, setIsCreatingCrossfade] = useState23(false);
6240
+ const handleCreateCrossfade = useCallback16(
6075
6241
  async (origin, target) => {
6076
6242
  const scene = activeSceneId;
6077
6243
  const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
@@ -6201,9 +6367,9 @@ function useTransitionOps({
6201
6367
  loadTracks
6202
6368
  ]
6203
6369
  );
6204
- const [isCreatingGroupFade, setIsCreatingGroupFade] = useState22(false);
6205
- const [isCreatingFade, setIsCreatingFade] = useState22(false);
6206
- const handleCreateFade = useCallback15(
6370
+ const [isCreatingGroupFade, setIsCreatingGroupFade] = useState23(false);
6371
+ const [isCreatingFade, setIsCreatingFade] = useState23(false);
6372
+ const handleCreateFade = useCallback16(
6207
6373
  async (selection, direction, gesture) => {
6208
6374
  const scene = activeSceneId;
6209
6375
  const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
@@ -6314,7 +6480,7 @@ function useTransitionOps({
6314
6480
  loadTracks
6315
6481
  ]
6316
6482
  );
6317
- const handleCrossfadeMute = useCallback15(
6483
+ const handleCrossfadeMute = useCallback16(
6318
6484
  (pair) => {
6319
6485
  const newMuted = !pair.origin.runtimeState.muted;
6320
6486
  for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
@@ -6329,7 +6495,7 @@ function useTransitionOps({
6329
6495
  },
6330
6496
  [host, setTracks]
6331
6497
  );
6332
- const handleCrossfadeSolo = useCallback15(
6498
+ const handleCrossfadeSolo = useCallback16(
6333
6499
  (pair) => {
6334
6500
  const newSolo = !pair.origin.runtimeState.solo;
6335
6501
  for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
@@ -6344,7 +6510,7 @@ function useTransitionOps({
6344
6510
  },
6345
6511
  [host, setTracks]
6346
6512
  );
6347
- const handleCrossfadeDelete = useCallback15(
6513
+ const handleCrossfadeDelete = useCallback16(
6348
6514
  async (pair) => {
6349
6515
  try {
6350
6516
  for (const member of [pair.origin, pair.target]) {
@@ -6371,7 +6537,7 @@ function useTransitionOps({
6371
6537
  [host, activeSceneId, setCrossfadePairsMeta, setTracks]
6372
6538
  );
6373
6539
  const crossfadeSliderTimers = useRef18({});
6374
- const handleCrossfadeSlider = useCallback15(
6540
+ const handleCrossfadeSlider = useCallback16(
6375
6541
  (pair, pos) => {
6376
6542
  setCrossfadePairsMeta(
6377
6543
  (prev) => prev.map((p) => p.groupId === pair.groupId ? { ...p, sliderPos: pos } : p)
@@ -6404,7 +6570,7 @@ function useTransitionOps({
6404
6570
  },
6405
6571
  [host, activeSceneId, applyCrossfadeAutomation, setCrossfadePairsMeta]
6406
6572
  );
6407
- const handleFadeDelete = useCallback15(
6573
+ const handleFadeDelete = useCallback16(
6408
6574
  async (fade) => {
6409
6575
  try {
6410
6576
  await host.deleteTrack(fade.track.handle.id);
@@ -6425,7 +6591,7 @@ function useTransitionOps({
6425
6591
  [host, activeSceneId, setFadesMeta, setTracks]
6426
6592
  );
6427
6593
  const fadeSliderTimers = useRef18({});
6428
- const handleFadeSlider = useCallback15(
6594
+ const handleFadeSlider = useCallback16(
6429
6595
  (fade, pos) => {
6430
6596
  setFadesMeta(
6431
6597
  (prev) => prev.map((f) => f.dbId === fade.dbId ? { ...f, meta: { ...f.meta, sliderPos: pos } } : f)
@@ -6455,7 +6621,7 @@ function useTransitionOps({
6455
6621
  },
6456
6622
  [host, activeSceneId, applyFadeAutomation, setFadesMeta]
6457
6623
  );
6458
- const handleCreateVerbatimGroupFade = useCallback15(
6624
+ const handleCreateVerbatimGroupFade = useCallback16(
6459
6625
  async (subject, direction) => {
6460
6626
  const groupAdapter = adapter.transitionGroup;
6461
6627
  if (!groupAdapter) throw new Error("This panel does not support group fades.");
@@ -6571,7 +6737,7 @@ function useTransitionOps({
6571
6737
  ]
6572
6738
  );
6573
6739
  const groupFadeSliderTimers = useRef18({});
6574
- const handleGroupFadeSlider = useCallback15(
6740
+ const handleGroupFadeSlider = useCallback16(
6575
6741
  (group, pos) => {
6576
6742
  setFadesMeta(
6577
6743
  (prev) => prev.map(
@@ -6610,7 +6776,7 @@ function useTransitionOps({
6610
6776
  },
6611
6777
  [host, activeSceneId, applyFadeAutomation, setFadesMeta]
6612
6778
  );
6613
- const handleGroupFadeDelete = useCallback15(
6779
+ const handleGroupFadeDelete = useCallback16(
6614
6780
  async (group) => {
6615
6781
  const suffixes = ["fade", ...adapter.transitionGroup?.cleanupKeySuffixes ?? []];
6616
6782
  try {
@@ -6638,7 +6804,7 @@ function useTransitionOps({
6638
6804
  [host, adapter, activeSceneId, setFadesMeta, setTracks]
6639
6805
  );
6640
6806
  const lastResyncKeyRef = useRef18("");
6641
- useEffect17(() => {
6807
+ useEffect18(() => {
6642
6808
  if (!host.getTrackSound || resolvedCrossfadePairs.length === 0 && resolvedFades.length === 0) {
6643
6809
  return;
6644
6810
  }
@@ -6679,7 +6845,7 @@ function useTransitionOps({
6679
6845
  cancelled = true;
6680
6846
  };
6681
6847
  }, [resolvedCrossfadePairs, resolvedFades, host, adapter]);
6682
- useEffect17(() => {
6848
+ useEffect18(() => {
6683
6849
  if (!host.setTrackVolumeAutomation || resolvedFades.length === 0) return;
6684
6850
  void (async () => {
6685
6851
  const mc = await host.getMusicalContext();
@@ -6717,7 +6883,7 @@ function useTransitionOps({
6717
6883
  }
6718
6884
 
6719
6885
  // src/panel-core/useGeneratorPanelCore.tsx
6720
- import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
6886
+ import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
6721
6887
  var EMPTY_PLACEHOLDERS = [];
6722
6888
  function useGeneratorPanelCore({
6723
6889
  ui,
@@ -6738,7 +6904,7 @@ function useGeneratorPanelCore({
6738
6904
  const { identity, features } = adapter;
6739
6905
  const logTag = identity.logTag;
6740
6906
  const adapterRef = useRef19(adapter);
6741
- useEffect18(() => {
6907
+ useEffect19(() => {
6742
6908
  if (adapterRef.current !== adapter) {
6743
6909
  adapterRef.current = adapter;
6744
6910
  console.warn(
@@ -6748,15 +6914,15 @@ function useGeneratorPanelCore({
6748
6914
  }, [adapter, logTag]);
6749
6915
  const supportsMeters = typeof host.getTrackLevels === "function";
6750
6916
  const trackLevels = useTrackLevels(host, isExpanded);
6751
- const [tracks, setTracks] = useState23([]);
6752
- const [isLoadingTracks, setIsLoadingTracks] = useState23(false);
6753
- const [importOpen, setImportOpen] = useState23(false);
6754
- const [soundImportTarget, setSoundImportTarget] = useState23(null);
6755
- const [designerView, setDesignerView] = useState23(false);
6756
- const [transitionSourceTotal, setTransitionSourceTotal] = useState23(0);
6757
- const [crossfadePairsMeta, setCrossfadePairsMeta] = useState23([]);
6758
- const [fadesMeta, setFadesMeta] = useState23([]);
6759
- const [genericGroupMetas, setGenericGroupMetas] = useState23({});
6917
+ const [tracks, setTracks] = useState24([]);
6918
+ const [isLoadingTracks, setIsLoadingTracks] = useState24(false);
6919
+ const [importOpen, setImportOpen] = useState24(false);
6920
+ const [soundImportTarget, setSoundImportTarget] = useState24(null);
6921
+ const [designerView, setDesignerView] = useState24(false);
6922
+ const [transitionSourceTotal, setTransitionSourceTotal] = useState24(0);
6923
+ const [crossfadePairsMeta, setCrossfadePairsMeta] = useState24([]);
6924
+ const [fadesMeta, setFadesMeta] = useState24([]);
6925
+ const [genericGroupMetas, setGenericGroupMetas] = useState24({});
6760
6926
  const [isComposing, , setIsComposingForScene] = useSceneState(activeSceneId, false);
6761
6927
  const [placeholders, , setPlaceholdersForScene] = useSceneState(
6762
6928
  activeSceneId,
@@ -6764,11 +6930,11 @@ function useGeneratorPanelCore({
6764
6930
  );
6765
6931
  const saveTimeoutRefs = useRef19({});
6766
6932
  const editLoadStartedRef = useRef19(/* @__PURE__ */ new Set());
6767
- const [availableInstruments, setAvailableInstruments] = useState23([]);
6768
- const [instrumentsLoading, setInstrumentsLoading] = useState23(false);
6933
+ const [availableInstruments, setAvailableInstruments] = useState24([]);
6934
+ const [instrumentsLoading, setInstrumentsLoading] = useState24(false);
6769
6935
  const engineToDbIdRef = useRef19(/* @__PURE__ */ new Map());
6770
6936
  const tracksLoadedForSceneRef = useRef19(null);
6771
- const persistSoundHistory = useCallback16(
6937
+ const persistSoundHistory = useCallback17(
6772
6938
  (trackId, state) => {
6773
6939
  if (!activeSceneId) return;
6774
6940
  const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
@@ -6788,7 +6954,7 @@ function useGeneratorPanelCore({
6788
6954
  setItems: setTracks,
6789
6955
  getId: (t) => t.handle.dbId
6790
6956
  });
6791
- const loadTracks = useCallback16(
6957
+ const loadTracks = useCallback17(
6792
6958
  async (incremental = false) => {
6793
6959
  const sceneAtStart = activeSceneId;
6794
6960
  if (!sceneAtStart) {
@@ -6913,10 +7079,10 @@ function useGeneratorPanelCore({
6913
7079
  },
6914
7080
  [host, activeSceneId, soundHistory, adapter, logTag]
6915
7081
  );
6916
- useEffect18(() => {
7082
+ useEffect19(() => {
6917
7083
  loadTracks();
6918
7084
  }, [loadTracks]);
6919
- useEffect18(() => {
7085
+ useEffect19(() => {
6920
7086
  const map = /* @__PURE__ */ new Map();
6921
7087
  for (const t of tracks) {
6922
7088
  map.set(t.handle.id, t.handle.dbId);
@@ -6924,7 +7090,7 @@ function useGeneratorPanelCore({
6924
7090
  engineToDbIdRef.current = map;
6925
7091
  }, [tracks]);
6926
7092
  const loadedCompletedIdsRef = useRef19(/* @__PURE__ */ new Set());
6927
- useEffect18(() => {
7093
+ useEffect19(() => {
6928
7094
  if (placeholders.length === 0) {
6929
7095
  loadedCompletedIdsRef.current.clear();
6930
7096
  return;
@@ -6943,16 +7109,16 @@ function useGeneratorPanelCore({
6943
7109
  loadTracks(true);
6944
7110
  }
6945
7111
  }, [placeholders, loadTracks, logTag]);
6946
- const adoptAndLoad = useCallback16(() => {
7112
+ const adoptAndLoad = useCallback17(() => {
6947
7113
  loadTracks(true);
6948
7114
  }, [loadTracks]);
6949
- useEffect18(() => {
7115
+ useEffect19(() => {
6950
7116
  const unsub = host.onEngineReady(() => {
6951
7117
  adoptAndLoad();
6952
7118
  });
6953
7119
  return unsub;
6954
7120
  }, [host, adoptAndLoad]);
6955
- useEffect18(() => {
7121
+ useEffect19(() => {
6956
7122
  if (typeof host.onAfterAgentMutation !== "function") return;
6957
7123
  let timer = null;
6958
7124
  const unsub = host.onAfterAgentMutation(() => {
@@ -6967,13 +7133,13 @@ function useGeneratorPanelCore({
6967
7133
  if (timer) clearTimeout(timer);
6968
7134
  };
6969
7135
  }, [host, loadTracks]);
6970
- useEffect18(() => {
7136
+ useEffect19(() => {
6971
7137
  const unsub = host.onTrackStateChange((trackId, state) => {
6972
7138
  setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: state } : t));
6973
7139
  });
6974
7140
  return unsub;
6975
7141
  }, [host]);
6976
- useEffect18(() => {
7142
+ useEffect19(() => {
6977
7143
  if (!features.bulkComposePlaceholders) return;
6978
7144
  console.log(`[${logTag}] Subscribing to composeProgress`);
6979
7145
  const unsub = host.onComposeProgress((event) => {
@@ -7007,7 +7173,7 @@ function useGeneratorPanelCore({
7007
7173
  });
7008
7174
  return unsub;
7009
7175
  }, [host, setIsComposingForScene, setPlaceholdersForScene, features.bulkComposePlaceholders, logTag]);
7010
- useEffect18(() => {
7176
+ useEffect19(() => {
7011
7177
  const refs = saveTimeoutRefs;
7012
7178
  return () => {
7013
7179
  for (const timeout of Object.values(refs.current)) {
@@ -7016,8 +7182,8 @@ function useGeneratorPanelCore({
7016
7182
  };
7017
7183
  }, []);
7018
7184
  const isAddingTrackRef = useRef19(false);
7019
- const [isAddingTrack, setIsAddingTrack] = useState23(false);
7020
- const handleAddTrack = useCallback16(async () => {
7185
+ const [isAddingTrack, setIsAddingTrack] = useState24(false);
7186
+ const handleAddTrack = useCallback17(async () => {
7021
7187
  if (isAddingTrackRef.current) return;
7022
7188
  if (!activeSceneId) {
7023
7189
  host.showToast("warning", "Select SCENE");
@@ -7065,7 +7231,7 @@ function useGeneratorPanelCore({
7065
7231
  setIsAddingTrack(false);
7066
7232
  }
7067
7233
  }, [host, adapter, identity, activeSceneId, isConnected, isAuthenticated, tracks.length, onExpandSelf, loadTracks, logTag]);
7068
- const handlePortTrack = useCallback16(
7234
+ const handlePortTrack = useCallback17(
7069
7235
  async (sel) => {
7070
7236
  if (!activeSceneId) {
7071
7237
  host.showToast("warning", "Select SCENE");
@@ -7128,7 +7294,7 @@ function useGeneratorPanelCore({
7128
7294
  },
7129
7295
  [host, adapter, identity, activeSceneId, isConnected, tracks.length, loadTracks]
7130
7296
  );
7131
- const handleSoundImportPick = useCallback16(
7297
+ const handleSoundImportPick = useCallback17(
7132
7298
  async (sel) => {
7133
7299
  const target = soundImportTarget;
7134
7300
  if (!target || !host.getTrackSound) {
@@ -7159,8 +7325,8 @@ function useGeneratorPanelCore({
7159
7325
  },
7160
7326
  [soundImportTarget, host, adapter, identity.familyKey, soundHistory]
7161
7327
  );
7162
- const [isExportingMidi, setIsExportingMidi] = useState23(false);
7163
- const handleExportMidi = useCallback16(async () => {
7328
+ const [isExportingMidi, setIsExportingMidi] = useState24(false);
7329
+ const handleExportMidi = useCallback17(async () => {
7164
7330
  if (isExportingMidi) return;
7165
7331
  setIsExportingMidi(true);
7166
7332
  try {
@@ -7191,10 +7357,10 @@ function useGeneratorPanelCore({
7191
7357
  const xfFromId = sceneContext?.transitionFromSceneId ?? null;
7192
7358
  const xfToId = sceneContext?.transitionToSceneId ?? null;
7193
7359
  const canCrossfade = features.transitionDesigner && sceneContext?.sceneType === "transition" && !!xfFromId && !!xfToId && !!host.listSceneFamilyTracks;
7194
- useEffect18(() => {
7360
+ useEffect19(() => {
7195
7361
  if (!canCrossfade) setDesignerView(false);
7196
7362
  }, [canCrossfade]);
7197
- useEffect18(() => {
7363
+ useEffect19(() => {
7198
7364
  if (!canCrossfade || !xfFromId || !xfToId || !host.listSceneFamilyTracks) {
7199
7365
  setTransitionSourceTotal(0);
7200
7366
  return;
@@ -7210,12 +7376,12 @@ function useGeneratorPanelCore({
7210
7376
  };
7211
7377
  }, [canCrossfade, xfFromId, xfToId, host]);
7212
7378
  const transitionDone = crossfadePairsMeta.length * 2 + fadesMeta.length;
7213
- useEffect18(() => {
7379
+ useEffect19(() => {
7214
7380
  if (!onHeaderContent) return;
7215
7381
  const addDisabled = needsContract || !isConnected || !activeSceneId || tracks.length >= identity.maxTracks || isAddingTrack;
7216
7382
  onHeaderContent(
7217
- /* @__PURE__ */ jsxs20("div", { className: "flex gap-1 items-center", children: [
7218
- features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */ jsx26(
7383
+ /* @__PURE__ */ jsxs21("div", { className: "flex gap-1 items-center", children: [
7384
+ features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */ jsx27(
7219
7385
  "button",
7220
7386
  {
7221
7387
  "data-testid": `import-from-scene-${identity.familyKey}-button`,
@@ -7229,7 +7395,7 @@ function useGeneratorPanelCore({
7229
7395
  children: identity.importTrackLabel ?? "Import Track"
7230
7396
  }
7231
7397
  ),
7232
- (!canCrossfade || !designerView) && /* @__PURE__ */ jsx26(
7398
+ (!canCrossfade || !designerView) && /* @__PURE__ */ jsx27(
7233
7399
  "button",
7234
7400
  {
7235
7401
  "data-testid": `add-${identity.familyKey}-track-button`,
@@ -7245,7 +7411,7 @@ function useGeneratorPanelCore({
7245
7411
  children: identity.addTrackLabel ?? "Add Track"
7246
7412
  }
7247
7413
  ),
7248
- canCrossfade && /* @__PURE__ */ jsxs20(
7414
+ canCrossfade && /* @__PURE__ */ jsxs21(
7249
7415
  "button",
7250
7416
  {
7251
7417
  "data-testid": `${identity.familyKey}-view-toggle`,
@@ -7264,7 +7430,7 @@ function useGeneratorPanelCore({
7264
7430
  title: designerView ? "Back to the track list" : "Open the transition designer",
7265
7431
  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
7432
  children: [
7267
- transitionSourceTotal > 0 && /* @__PURE__ */ jsx26(
7433
+ transitionSourceTotal > 0 && /* @__PURE__ */ jsx27(
7268
7434
  "span",
7269
7435
  {
7270
7436
  className: "absolute inset-y-0 left-0 bg-sas-accent/25",
@@ -7272,7 +7438,7 @@ function useGeneratorPanelCore({
7272
7438
  "aria-hidden": true
7273
7439
  }
7274
7440
  ),
7275
- /* @__PURE__ */ jsxs20("span", { className: "relative", children: [
7441
+ /* @__PURE__ */ jsxs21("span", { className: "relative", children: [
7276
7442
  "\u21C4 ",
7277
7443
  designerView ? "Transition" : "Tracks",
7278
7444
  transitionSourceTotal > 0 ? ` ${transitionDone}/${transitionSourceTotal}` : ""
@@ -7303,7 +7469,7 @@ function useGeneratorPanelCore({
7303
7469
  identity,
7304
7470
  features.importTracks
7305
7471
  ]);
7306
- useEffect18(() => {
7472
+ useEffect19(() => {
7307
7473
  if (!onLoading) return;
7308
7474
  const anyGenerating = tracks.some((t) => t.isGenerating);
7309
7475
  onLoading(isLoadingTracks || anyGenerating || isBulkActive);
@@ -7311,7 +7477,7 @@ function useGeneratorPanelCore({
7311
7477
  onLoading(false);
7312
7478
  };
7313
7479
  }, [onLoading, isLoadingTracks, tracks, isBulkActive]);
7314
- const handleDeleteTrack = useCallback16(
7480
+ const handleDeleteTrack = useCallback17(
7315
7481
  async (trackId) => {
7316
7482
  try {
7317
7483
  await host.deleteTrack(trackId);
@@ -7327,7 +7493,7 @@ function useGeneratorPanelCore({
7327
7493
  },
7328
7494
  [host, activeSceneId]
7329
7495
  );
7330
- const handlePromptChange = useCallback16(
7496
+ const handlePromptChange = useCallback17(
7331
7497
  (trackId, prompt) => {
7332
7498
  setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, prompt } : t));
7333
7499
  const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
@@ -7364,11 +7530,11 @@ function useGeneratorPanelCore({
7364
7530
  }
7365
7531
  return s;
7366
7532
  }, [resolvedGenericGroups]);
7367
- const engineToDbId = useCallback16(
7533
+ const engineToDbId = useCallback17(
7368
7534
  (trackId) => engineToDbIdRef.current.get(trackId) ?? trackId,
7369
7535
  []
7370
7536
  );
7371
- const updateTrack = useCallback16(
7537
+ const updateTrack = useCallback17(
7372
7538
  (trackId, patch) => {
7373
7539
  setTracks(
7374
7540
  (prev) => prev.map(
@@ -7378,18 +7544,18 @@ function useGeneratorPanelCore({
7378
7544
  },
7379
7545
  []
7380
7546
  );
7381
- const markEditLoaded = useCallback16((trackId) => {
7547
+ const markEditLoaded = useCallback17((trackId) => {
7382
7548
  editLoadStartedRef.current.add(trackId);
7383
7549
  }, []);
7384
7550
  const tracksRef = useRef19(tracks);
7385
- useEffect18(() => {
7551
+ useEffect19(() => {
7386
7552
  tracksRef.current = tracks;
7387
7553
  }, [tracks]);
7388
7554
  const resolvedGenericGroupsRef = useRef19(resolvedGenericGroups);
7389
- useEffect18(() => {
7555
+ useEffect19(() => {
7390
7556
  resolvedGenericGroupsRef.current = resolvedGenericGroups;
7391
7557
  }, [resolvedGenericGroups]);
7392
- const makeServices = useCallback16(() => {
7558
+ const makeServices = useCallback17(() => {
7393
7559
  return {
7394
7560
  host,
7395
7561
  activeSceneId,
@@ -7408,7 +7574,7 @@ function useGeneratorPanelCore({
7408
7574
  resolvedGroups: (metaKey) => resolvedGenericGroupsRef.current[metaKey]?.resolved ?? []
7409
7575
  };
7410
7576
  }, [host, activeSceneId, updateTrack, loadTracks, soundHistory, engineToDbId, markEditLoaded, identity, adapter]);
7411
- const handleGenerate = useCallback16(
7577
+ const handleGenerate = useCallback17(
7412
7578
  async (trackId) => {
7413
7579
  const track = tracks.find((t) => t.handle.id === trackId);
7414
7580
  if (!track || !track.prompt.trim()) return;
@@ -7435,7 +7601,7 @@ function useGeneratorPanelCore({
7435
7601
  },
7436
7602
  [host, adapter, tracks, isAuthenticated, makeServices]
7437
7603
  );
7438
- const handleMuteToggle = useCallback16(
7604
+ const handleMuteToggle = useCallback17(
7439
7605
  (trackId) => {
7440
7606
  const track = tracks.find((t) => t.handle.id === trackId);
7441
7607
  if (!track) return;
@@ -7455,7 +7621,7 @@ function useGeneratorPanelCore({
7455
7621
  },
7456
7622
  [host, tracks]
7457
7623
  );
7458
- const handleSoloToggle = useCallback16(
7624
+ const handleSoloToggle = useCallback17(
7459
7625
  (trackId) => {
7460
7626
  const track = tracks.find((t) => t.handle.id === trackId);
7461
7627
  if (!track) return;
@@ -7475,7 +7641,7 @@ function useGeneratorPanelCore({
7475
7641
  },
7476
7642
  [host, tracks]
7477
7643
  );
7478
- const handleVolumeChange = useCallback16(
7644
+ const handleVolumeChange = useCallback17(
7479
7645
  (trackId, volume) => {
7480
7646
  setTracks(
7481
7647
  (prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, volume } } : t)
@@ -7485,7 +7651,7 @@ function useGeneratorPanelCore({
7485
7651
  },
7486
7652
  [host]
7487
7653
  );
7488
- const handlePanChange = useCallback16(
7654
+ const handlePanChange = useCallback17(
7489
7655
  (trackId, pan) => {
7490
7656
  setTracks(
7491
7657
  (prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, pan } } : t)
@@ -7495,7 +7661,7 @@ function useGeneratorPanelCore({
7495
7661
  },
7496
7662
  [host]
7497
7663
  );
7498
- const handleShuffle = useCallback16(
7664
+ const handleShuffle = useCallback17(
7499
7665
  async (trackId) => {
7500
7666
  const track = tracks.find((t) => t.handle.id === trackId);
7501
7667
  if (!track) return;
@@ -7537,7 +7703,7 @@ function useGeneratorPanelCore({
7537
7703
  },
7538
7704
  [host, adapter, tracks, soundHistory, logTag]
7539
7705
  );
7540
- const handleCopy = useCallback16(
7706
+ const handleCopy = useCallback17(
7541
7707
  async (trackId) => {
7542
7708
  try {
7543
7709
  const newHandle = await host.duplicateTrack(trackId);
@@ -7550,7 +7716,7 @@ function useGeneratorPanelCore({
7550
7716
  },
7551
7717
  [host, loadTracks]
7552
7718
  );
7553
- const handleFxToggle = useCallback16(
7719
+ const handleFxToggle = useCallback17(
7554
7720
  (trackId, category, enabled) => {
7555
7721
  setTracks(
7556
7722
  (prev) => prev.map(
@@ -7573,7 +7739,7 @@ function useGeneratorPanelCore({
7573
7739
  },
7574
7740
  [host]
7575
7741
  );
7576
- const handleFxPresetChange = useCallback16(
7742
+ const handleFxPresetChange = useCallback17(
7577
7743
  (trackId, category, presetIndex) => {
7578
7744
  setTracks(
7579
7745
  (prev) => prev.map(
@@ -7599,7 +7765,7 @@ function useGeneratorPanelCore({
7599
7765
  },
7600
7766
  [host]
7601
7767
  );
7602
- const handleFxDryWetChange = useCallback16(
7768
+ const handleFxDryWetChange = useCallback17(
7603
7769
  (trackId, category, value) => {
7604
7770
  setTracks(
7605
7771
  (prev) => prev.map(
@@ -7611,7 +7777,7 @@ function useGeneratorPanelCore({
7611
7777
  },
7612
7778
  [host]
7613
7779
  );
7614
- const toggleFxDrawer = useCallback16(
7780
+ const toggleFxDrawer = useCallback17(
7615
7781
  (trackId) => {
7616
7782
  setTracks(
7617
7783
  (prev) => prev.map((t) => {
@@ -7633,7 +7799,7 @@ function useGeneratorPanelCore({
7633
7799
  },
7634
7800
  [host, tracks]
7635
7801
  );
7636
- const loadEditNotes = useCallback16(
7802
+ const loadEditNotes = useCallback17(
7637
7803
  async (trackId) => {
7638
7804
  try {
7639
7805
  const mc = await host.getMusicalContext();
@@ -7651,7 +7817,7 @@ function useGeneratorPanelCore({
7651
7817
  },
7652
7818
  [host, logTag]
7653
7819
  );
7654
- const handleNotesChange = useCallback16(
7820
+ const handleNotesChange = useCallback17(
7655
7821
  (trackId, notes) => {
7656
7822
  setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editNotes: notes } : t));
7657
7823
  const key = `edit:${trackId}`;
@@ -7681,7 +7847,7 @@ function useGeneratorPanelCore({
7681
7847
  },
7682
7848
  [host]
7683
7849
  );
7684
- const handleTabChange = useCallback16(
7850
+ const handleTabChange = useCallback17(
7685
7851
  (trackId, tab) => {
7686
7852
  setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, drawerOpen: true, drawerTab: tab } : t));
7687
7853
  if (tab === "fx") {
@@ -7706,10 +7872,10 @@ function useGeneratorPanelCore({
7706
7872
  },
7707
7873
  [host, availableInstruments.length, instrumentsLoading, loadEditNotes]
7708
7874
  );
7709
- const handleProgressChange = useCallback16((trackId, pct) => {
7875
+ const handleProgressChange = useCallback17((trackId, pct) => {
7710
7876
  setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, generationProgress: pct } : t));
7711
7877
  }, []);
7712
- const handleToggleDrawer = useCallback16((trackId) => {
7878
+ const handleToggleDrawer = useCallback17((trackId) => {
7713
7879
  setTracks(
7714
7880
  (prev) => prev.map((t) => {
7715
7881
  if (t.handle.id !== trackId) return t;
@@ -7718,7 +7884,7 @@ function useGeneratorPanelCore({
7718
7884
  })
7719
7885
  );
7720
7886
  }, []);
7721
- const handleInstrumentSelect = useCallback16(
7887
+ const handleInstrumentSelect = useCallback17(
7722
7888
  async (trackId, pluginId) => {
7723
7889
  const isDefaultInstrument = pluginId === (identity.defaultInstrumentPluginId ?? "Surge XT");
7724
7890
  if (isDefaultInstrument) {
@@ -7771,7 +7937,7 @@ function useGeneratorPanelCore({
7771
7937
  },
7772
7938
  [host, identity.defaultInstrumentPluginId, logTag]
7773
7939
  );
7774
- const handleShowEditor = useCallback16(
7940
+ const handleShowEditor = useCallback17(
7775
7941
  async (trackId) => {
7776
7942
  try {
7777
7943
  await host.showInstrumentEditor(trackId);
@@ -7782,12 +7948,12 @@ function useGeneratorPanelCore({
7782
7948
  },
7783
7949
  [host]
7784
7950
  );
7785
- const handleBackToInstruments = useCallback16((trackId) => {
7951
+ const handleBackToInstruments = useCallback17((trackId) => {
7786
7952
  setTracks(
7787
7953
  (prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editorStage: false } : t)
7788
7954
  );
7789
7955
  }, []);
7790
- const handleRefreshInstruments = useCallback16(() => {
7956
+ const handleRefreshInstruments = useCallback17(() => {
7791
7957
  setInstrumentsLoading(true);
7792
7958
  host.getAvailableInstruments().then((instruments) => {
7793
7959
  setAvailableInstruments(instruments);
@@ -7796,7 +7962,7 @@ function useGeneratorPanelCore({
7796
7962
  setInstrumentsLoading(false);
7797
7963
  });
7798
7964
  }, [host]);
7799
- const onAuditionNote = useCallback16(
7965
+ const onAuditionNote = useCallback17(
7800
7966
  (trackId, pitch, velocity, ms) => {
7801
7967
  void host.auditionNote(trackId, pitch, velocity, ms);
7802
7968
  },
@@ -7849,7 +8015,7 @@ function useGeneratorPanelCore({
7849
8015
  resolvedCrossfadePairs,
7850
8016
  resolvedFades
7851
8017
  });
7852
- const setGroupMute = useCallback16(
8018
+ const setGroupMute = useCallback17(
7853
8019
  (trackIds, muted) => {
7854
8020
  for (const id of trackIds) {
7855
8021
  setTracks(
@@ -7861,7 +8027,7 @@ function useGeneratorPanelCore({
7861
8027
  },
7862
8028
  [host]
7863
8029
  );
7864
- const setGroupSolo = useCallback16(
8030
+ const setGroupSolo = useCallback17(
7865
8031
  (trackIds, solo) => {
7866
8032
  for (const id of trackIds) {
7867
8033
  setTracks(
@@ -7873,7 +8039,7 @@ function useGeneratorPanelCore({
7873
8039
  },
7874
8040
  [host]
7875
8041
  );
7876
- const deleteGroup = useCallback16(
8042
+ const deleteGroup = useCallback17(
7877
8043
  async (members, cleanupKeySuffixes) => {
7878
8044
  for (const member of members) {
7879
8045
  try {
@@ -8009,8 +8175,8 @@ function useGeneratorPanelCore({
8009
8175
  }
8010
8176
 
8011
8177
  // src/panel-core/GeneratorPanelShell.tsx
8012
- import React23, { useCallback as useCallback17 } from "react";
8013
- import { Fragment as Fragment7, jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
8178
+ import React23, { useCallback as useCallback18 } from "react";
8179
+ import { Fragment as Fragment7, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
8014
8180
  function GeneratorPanelShell({ core, slots }) {
8015
8181
  const {
8016
8182
  ui,
@@ -8066,7 +8232,7 @@ function GeneratorPanelShell({ core, slots }) {
8066
8232
  const { host, activeSceneId, isAuthenticated, sceneContext, onSelectScene, onOpenContract } = ui;
8067
8233
  const panelBus = usePanelBus(host, activeSceneId);
8068
8234
  const { identity, features } = adapter;
8069
- const buildRowProps = useCallback17(
8235
+ const buildRowProps = useCallback18(
8070
8236
  (track, drag) => {
8071
8237
  const id = track.handle.id;
8072
8238
  const pickerProps = features.instrumentPicker ? {
@@ -8166,12 +8332,12 @@ function GeneratorPanelShell({ core, slots }) {
8166
8332
  ]
8167
8333
  );
8168
8334
  if (!activeSceneId) {
8169
- return /* @__PURE__ */ jsx27(
8335
+ return /* @__PURE__ */ jsx28(
8170
8336
  "div",
8171
8337
  {
8172
8338
  "data-testid": `no-scene-placeholder-${identity.familyKey}`,
8173
8339
  className: "flex items-center justify-center py-8",
8174
- children: /* @__PURE__ */ jsx27(
8340
+ children: /* @__PURE__ */ jsx28(
8175
8341
  "button",
8176
8342
  {
8177
8343
  onClick: () => onSelectScene?.(),
@@ -8183,12 +8349,12 @@ function GeneratorPanelShell({ core, slots }) {
8183
8349
  );
8184
8350
  }
8185
8351
  if (!sceneContext?.hasContract) {
8186
- return /* @__PURE__ */ jsx27(
8352
+ return /* @__PURE__ */ jsx28(
8187
8353
  "div",
8188
8354
  {
8189
8355
  "data-testid": `no-contract-placeholder-${identity.familyKey}`,
8190
8356
  className: "flex items-center justify-center py-8",
8191
- children: /* @__PURE__ */ jsx27(
8357
+ children: /* @__PURE__ */ jsx28(
8192
8358
  "button",
8193
8359
  {
8194
8360
  onClick: () => onOpenContract?.(),
@@ -8200,7 +8366,7 @@ function GeneratorPanelShell({ core, slots }) {
8200
8366
  );
8201
8367
  }
8202
8368
  if (features.bulkComposePlaceholders && isComposing) {
8203
- return /* @__PURE__ */ jsx27("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2", children: /* @__PURE__ */ jsx27(SorceryProgressBar, { isLoading: true, statusText: "COMPOSING...", heightClass: "h-10" }) });
8369
+ return /* @__PURE__ */ jsx28("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2", children: /* @__PURE__ */ jsx28(SorceryProgressBar, { isLoading: true, statusText: "COMPOSING...", heightClass: "h-10" }) });
8204
8370
  }
8205
8371
  const activePlaceholders = features.bulkComposePlaceholders ? placeholders : [];
8206
8372
  if (activePlaceholders.length > 0) {
@@ -8211,18 +8377,18 @@ function GeneratorPanelShell({ core, slots }) {
8211
8377
  tracksByDbId.set(t.handle.id, t);
8212
8378
  }
8213
8379
  }
8214
- return /* @__PURE__ */ jsx27("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: activePlaceholders.map((ph) => {
8380
+ return /* @__PURE__ */ jsx28("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: activePlaceholders.map((ph) => {
8215
8381
  const loadedTrack = ph.status === "completed" ? tracksByDbId.get(ph.id) : void 0;
8216
8382
  if (loadedTrack) {
8217
- return /* @__PURE__ */ jsx27(TrackRow, { ...buildRowProps(loadedTrack) }, ph.id);
8383
+ return /* @__PURE__ */ jsx28(TrackRow, { ...buildRowProps(loadedTrack) }, ph.id);
8218
8384
  }
8219
- return /* @__PURE__ */ jsx27(
8385
+ return /* @__PURE__ */ jsx28(
8220
8386
  "div",
8221
8387
  {
8222
8388
  "data-testid": "bulk-placeholder-track",
8223
8389
  className: "relative rounded-sm border w-full overflow-hidden border-sas-border bg-sas-panel-alt",
8224
8390
  style: { borderLeftColor: identity.placeholderAccentColor, borderLeftWidth: "3px" },
8225
- children: /* @__PURE__ */ jsx27(SorceryProgressBar, { isLoading: true, statusText: "CONJURING MIDI...", heightClass: "h-10" })
8391
+ children: /* @__PURE__ */ jsx28(SorceryProgressBar, { isLoading: true, statusText: "CONJURING MIDI...", heightClass: "h-10" })
8226
8392
  },
8227
8393
  ph.id
8228
8394
  );
@@ -8234,13 +8400,13 @@ function GeneratorPanelShell({ core, slots }) {
8234
8400
  supportsMeters,
8235
8401
  levels: supportsMeters ? trackLevels : void 0,
8236
8402
  handlers,
8237
- renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */ jsx27(TrackRow, { ...{ ...buildRowProps(track, drag), ...overrides ?? {} } }, track.handle.id),
8403
+ renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */ jsx28(TrackRow, { ...{ ...buildRowProps(track, drag), ...overrides ?? {} } }, track.handle.id),
8238
8404
  setGroupMute,
8239
8405
  setGroupSolo,
8240
8406
  deleteGroup
8241
8407
  };
8242
- return /* @__PURE__ */ jsxs21("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: [
8243
- features.importTracks && host.listImportableTracks && /* @__PURE__ */ jsx27(
8408
+ return /* @__PURE__ */ jsxs22("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: [
8409
+ features.importTracks && host.listImportableTracks && /* @__PURE__ */ jsx28(
8244
8410
  ImportTrackModal,
8245
8411
  {
8246
8412
  host,
@@ -8253,7 +8419,7 @@ function GeneratorPanelShell({ core, slots }) {
8253
8419
  testIdPrefix: `${identity.familyKey}-import`
8254
8420
  }
8255
8421
  ),
8256
- features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ jsx27(
8422
+ features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ jsx28(
8257
8423
  ImportTrackModal,
8258
8424
  {
8259
8425
  host,
@@ -8268,7 +8434,7 @@ function GeneratorPanelShell({ core, slots }) {
8268
8434
  }
8269
8435
  ),
8270
8436
  slots?.modals,
8271
- canCrossfade && xfFromId && xfToId && /* @__PURE__ */ jsx27("div", { className: designerView ? "contents" : "hidden", children: /* @__PURE__ */ jsx27(
8437
+ canCrossfade && xfFromId && xfToId && /* @__PURE__ */ jsx28("div", { className: designerView ? "contents" : "hidden", children: /* @__PURE__ */ jsx28(
8272
8438
  TransitionDesigner,
8273
8439
  {
8274
8440
  host,
@@ -8287,8 +8453,8 @@ function GeneratorPanelShell({ core, slots }) {
8287
8453
  testIdPrefix: `${identity.familyKey}-transition-designer`
8288
8454
  }
8289
8455
  ) }),
8290
- !(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */ jsx27("div", { className: "text-sas-muted text-xs text-center py-4", children: "Loading tracks..." }) : /* @__PURE__ */ jsxs21(Fragment7, { children: [
8291
- panelBus.supported && panelBus.bus && /* @__PURE__ */ jsx27(
8456
+ !(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */ jsx28("div", { className: "text-sas-muted text-xs text-center py-4", children: "Loading tracks..." }) : /* @__PURE__ */ jsxs22(Fragment7, { children: [
8457
+ panelBus.supported && panelBus.bus && /* @__PURE__ */ jsx28(
8292
8458
  PanelMasterStrip,
8293
8459
  {
8294
8460
  bus: panelBus.bus,
@@ -8309,7 +8475,7 @@ function GeneratorPanelShell({ core, slots }) {
8309
8475
  }
8310
8476
  ),
8311
8477
  slots?.beforeRows,
8312
- resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ jsx27(
8478
+ resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ jsx28(
8313
8479
  CrossfadeTrackRow,
8314
8480
  {
8315
8481
  accentColor: identity.transitionAccentColor,
@@ -8346,7 +8512,7 @@ function GeneratorPanelShell({ core, slots }) {
8346
8512
  },
8347
8513
  pair.groupId
8348
8514
  )),
8349
- resolvedSingleFades.map((fade) => /* @__PURE__ */ jsx27(
8515
+ resolvedSingleFades.map((fade) => /* @__PURE__ */ jsx28(
8350
8516
  FadeTrackRow,
8351
8517
  {
8352
8518
  accentColor: identity.transitionAccentColor,
@@ -8371,7 +8537,7 @@ function GeneratorPanelShell({ core, slots }) {
8371
8537
  },
8372
8538
  fade.dbId
8373
8539
  )),
8374
- resolvedGroupFades.map((group) => /* @__PURE__ */ jsx27(
8540
+ resolvedGroupFades.map((group) => /* @__PURE__ */ jsx28(
8375
8541
  GroupFadeTrackRow,
8376
8542
  {
8377
8543
  accentColor: identity.transitionAccentColor,
@@ -8407,20 +8573,20 @@ function GeneratorPanelShell({ core, slots }) {
8407
8573
  group.groupId
8408
8574
  )),
8409
8575
  (adapter.groupExtensions ?? []).flatMap(
8410
- (ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).filter((group) => !group.members.every((m) => fadeMemberDbIds.has(m.dbId))).map((group) => /* @__PURE__ */ jsx27(React23.Fragment, { children: ext.renderGroup(group, groupCtx) }, `${ext.metaKey}:${group.groupId}`))
8576
+ (ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).filter((group) => !group.members.every((m) => fadeMemberDbIds.has(m.dbId))).map((group) => /* @__PURE__ */ jsx28(React23.Fragment, { children: ext.renderGroup(group, groupCtx) }, `${ext.metaKey}:${group.groupId}`))
8411
8577
  ),
8412
8578
  tracks.map((track, index) => {
8413
8579
  if (crossfadeMemberDbIds.has(track.handle.dbId) || fadeMemberDbIds.has(track.handle.dbId) || genericGroupMemberDbIds.has(track.handle.dbId)) {
8414
8580
  return null;
8415
8581
  }
8416
- return /* @__PURE__ */ jsx27(TrackRow, { ...buildRowProps(track, reorder.dragPropsFor(index)) }, track.handle.id);
8582
+ return /* @__PURE__ */ jsx28(TrackRow, { ...buildRowProps(track, reorder.dragPropsFor(index)) }, track.handle.id);
8417
8583
  }),
8418
8584
  slots?.afterRows
8419
8585
  ] })),
8420
8586
  features.exportMidi && !designerView && !isLoadingTracks && tracks.length > 0 && (() => {
8421
8587
  const hasAnyMidi = tracks.some((t) => t.hasMidi);
8422
8588
  const exportDisabled = isExportingMidi || !hasAnyMidi;
8423
- return /* @__PURE__ */ jsx27("div", { className: "pt-2", children: /* @__PURE__ */ jsx27(
8589
+ return /* @__PURE__ */ jsx28("div", { className: "pt-2", children: /* @__PURE__ */ jsx28(
8424
8590
  "button",
8425
8591
  {
8426
8592
  "data-testid": "export-midi-tracks-button",
@@ -8910,7 +9076,7 @@ Your previous ensemble had these problems \u2014 fix them while keeping everythi
8910
9076
  }
8911
9077
 
8912
9078
  // src/constants/sdk-version.ts
8913
- var PLUGIN_SDK_VERSION = "2.43.0";
9079
+ var PLUGIN_SDK_VERSION = "2.46.0";
8914
9080
 
8915
9081
  // src/utils/format-concurrent-tracks.ts
8916
9082
  function formatConcurrentTracks(ctx) {
@@ -9118,6 +9284,7 @@ export {
9118
9284
  TRANSITION_DESIGNER_DRAFT_KEY,
9119
9285
  TrackDrawer,
9120
9286
  TrackExternalFxSection,
9287
+ TrackFreezeSection,
9121
9288
  TrackMeterStrip,
9122
9289
  TrackRow,
9123
9290
  TransitionDesigner,
@@ -9188,6 +9355,7 @@ export {
9188
9355
  useSceneState,
9189
9356
  useSoundHistory,
9190
9357
  useTrackExternalFx,
9358
+ useTrackFreeze,
9191
9359
  useTrackLevel,
9192
9360
  useTrackLevels,
9193
9361
  useTrackMeter,