@signalsandsorcery/plugin-sdk 2.35.1 → 2.35.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +290 -4
- package/dist/index.d.ts +290 -4
- package/dist/index.js +1196 -604
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1192 -604
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -62,6 +62,7 @@ __export(index_exports, {
|
|
|
62
62
|
PLUGIN_SDK_VERSION: () => PLUGIN_SDK_VERSION,
|
|
63
63
|
PX_PER_BEAT: () => PX_PER_BEAT,
|
|
64
64
|
PanSlider: () => PanSlider,
|
|
65
|
+
PanelMasterStrip: () => PanelMasterStrip,
|
|
65
66
|
PianoRollEditor: () => PianoRollEditor,
|
|
66
67
|
PluginError: () => PluginError,
|
|
67
68
|
RESIZE_HANDLE_PX: () => RESIZE_HANDLE_PX,
|
|
@@ -73,6 +74,7 @@ __export(index_exports, {
|
|
|
73
74
|
TEXTURAL_ROLES: () => TEXTURAL_ROLES,
|
|
74
75
|
TRANSITION_DESIGNER_DRAFT_KEY: () => TRANSITION_DESIGNER_DRAFT_KEY,
|
|
75
76
|
TrackDrawer: () => TrackDrawer,
|
|
77
|
+
TrackExternalFxSection: () => TrackExternalFxSection,
|
|
76
78
|
TrackMeterStrip: () => TrackMeterStrip,
|
|
77
79
|
TrackRow: () => TrackRow,
|
|
78
80
|
TransitionDesigner: () => TransitionDesigner,
|
|
@@ -126,8 +128,10 @@ __export(index_exports, {
|
|
|
126
128
|
transposeNotes: () => transposeNotes,
|
|
127
129
|
useAnySolo: () => useAnySolo,
|
|
128
130
|
useGeneratorPanelCore: () => useGeneratorPanelCore,
|
|
131
|
+
usePanelBus: () => usePanelBus,
|
|
129
132
|
useSceneState: () => useSceneState,
|
|
130
133
|
useSoundHistory: () => useSoundHistory,
|
|
134
|
+
useTrackExternalFx: () => useTrackExternalFx,
|
|
131
135
|
useTrackLevel: () => useTrackLevel,
|
|
132
136
|
useTrackLevels: () => useTrackLevels,
|
|
133
137
|
useTrackMeter: () => useTrackMeter,
|
|
@@ -204,11 +208,11 @@ var EMPTY_FX_DETAIL_STATE = {
|
|
|
204
208
|
};
|
|
205
209
|
|
|
206
210
|
// src/components/TrackRow.tsx
|
|
207
|
-
var
|
|
211
|
+
var import_react11 = __toESM(require("react"));
|
|
208
212
|
var import_lucide_react = require("lucide-react");
|
|
209
213
|
|
|
210
214
|
// src/components/TrackDrawer.tsx
|
|
211
|
-
var
|
|
215
|
+
var import_react4 = require("react");
|
|
212
216
|
|
|
213
217
|
// src/constants/fx-presets.ts
|
|
214
218
|
var EQ_PRESETS = {
|
|
@@ -928,8 +932,250 @@ function PianoRollEditor({
|
|
|
928
932
|
] });
|
|
929
933
|
}
|
|
930
934
|
|
|
931
|
-
// src/components/
|
|
935
|
+
// src/components/TrackExternalFxSection.tsx
|
|
936
|
+
var import_react3 = require("react");
|
|
937
|
+
|
|
938
|
+
// src/hooks/useTrackExternalFx.ts
|
|
939
|
+
var import_react2 = require("react");
|
|
940
|
+
function useTrackExternalFx(host, trackId) {
|
|
941
|
+
const supported = typeof host.getTrackExternalFx === "function";
|
|
942
|
+
const [fx, setFx] = (0, import_react2.useState)(null);
|
|
943
|
+
const [availableFx, setAvailableFx] = (0, import_react2.useState)([]);
|
|
944
|
+
const [fxLoading, setFxLoading] = (0, import_react2.useState)(false);
|
|
945
|
+
const [pickerOpen, setPickerOpen] = (0, import_react2.useState)(false);
|
|
946
|
+
const fxLoadedRef = (0, import_react2.useRef)(false);
|
|
947
|
+
const loadSeqRef = (0, import_react2.useRef)(0);
|
|
948
|
+
const reload = (0, import_react2.useCallback)(async () => {
|
|
949
|
+
if (!supported || !trackId || !host.getTrackExternalFx) {
|
|
950
|
+
setFx(null);
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
const seq = ++loadSeqRef.current;
|
|
954
|
+
try {
|
|
955
|
+
const list = await host.getTrackExternalFx(trackId);
|
|
956
|
+
if (loadSeqRef.current === seq) setFx(list);
|
|
957
|
+
} catch {
|
|
958
|
+
}
|
|
959
|
+
}, [host, trackId, supported]);
|
|
960
|
+
(0, import_react2.useEffect)(() => {
|
|
961
|
+
setFx(null);
|
|
962
|
+
setPickerOpen(false);
|
|
963
|
+
void reload();
|
|
964
|
+
}, [reload]);
|
|
965
|
+
const loadFxList = (0, import_react2.useCallback)(
|
|
966
|
+
async (force) => {
|
|
967
|
+
if (!supported || !host.getAvailableFx) return;
|
|
968
|
+
if (fxLoadedRef.current && !force) return;
|
|
969
|
+
setFxLoading(true);
|
|
970
|
+
try {
|
|
971
|
+
const list = await host.getAvailableFx();
|
|
972
|
+
setAvailableFx(list);
|
|
973
|
+
fxLoadedRef.current = true;
|
|
974
|
+
} catch {
|
|
975
|
+
} finally {
|
|
976
|
+
setFxLoading(false);
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
[host, supported]
|
|
980
|
+
);
|
|
981
|
+
const openPicker = (0, import_react2.useCallback)(
|
|
982
|
+
(open) => {
|
|
983
|
+
setPickerOpen(open);
|
|
984
|
+
if (open) void loadFxList(false);
|
|
985
|
+
},
|
|
986
|
+
[loadFxList]
|
|
987
|
+
);
|
|
988
|
+
const mutate = (0, import_react2.useCallback)(
|
|
989
|
+
(fn) => {
|
|
990
|
+
if (!fn || !trackId) return;
|
|
991
|
+
void (async () => {
|
|
992
|
+
try {
|
|
993
|
+
await fn();
|
|
994
|
+
} catch {
|
|
995
|
+
}
|
|
996
|
+
await reload();
|
|
997
|
+
})();
|
|
998
|
+
},
|
|
999
|
+
[trackId, reload]
|
|
1000
|
+
);
|
|
1001
|
+
return {
|
|
1002
|
+
supported,
|
|
1003
|
+
fx,
|
|
1004
|
+
availableFx,
|
|
1005
|
+
fxLoading,
|
|
1006
|
+
pickerOpen,
|
|
1007
|
+
setPickerOpen: openPicker,
|
|
1008
|
+
refreshFx: () => void loadFxList(true),
|
|
1009
|
+
reload,
|
|
1010
|
+
onAddFx: (pluginId) => mutate(host.loadTrackExternalFx && (async () => {
|
|
1011
|
+
await host.loadTrackExternalFx(trackId, pluginId);
|
|
1012
|
+
})),
|
|
1013
|
+
onRemoveFx: (fxIndex) => mutate(host.removeTrackExternalFx && (() => host.removeTrackExternalFx(trackId, fxIndex))),
|
|
1014
|
+
onToggleFxEnabled: (fxIndex, enabled) => mutate(
|
|
1015
|
+
host.setTrackExternalFxEnabled && (() => host.setTrackExternalFxEnabled(trackId, fxIndex, enabled))
|
|
1016
|
+
),
|
|
1017
|
+
onShowFxEditor: (fxIndex) => mutate(
|
|
1018
|
+
host.showTrackExternalFxEditor && (() => host.showTrackExternalFxEditor(trackId, fxIndex))
|
|
1019
|
+
)
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// src/components/TrackExternalFxSection.tsx
|
|
932
1024
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1025
|
+
function TrackExternalFxSection({
|
|
1026
|
+
host,
|
|
1027
|
+
trackId,
|
|
1028
|
+
disabled = false
|
|
1029
|
+
}) {
|
|
1030
|
+
const {
|
|
1031
|
+
supported,
|
|
1032
|
+
fx,
|
|
1033
|
+
availableFx,
|
|
1034
|
+
fxLoading,
|
|
1035
|
+
pickerOpen,
|
|
1036
|
+
setPickerOpen,
|
|
1037
|
+
refreshFx,
|
|
1038
|
+
onAddFx,
|
|
1039
|
+
onRemoveFx,
|
|
1040
|
+
onToggleFxEnabled,
|
|
1041
|
+
onShowFxEditor
|
|
1042
|
+
} = useTrackExternalFx(host, trackId);
|
|
1043
|
+
const [search, setSearch] = (0, import_react3.useState)("");
|
|
1044
|
+
const filtered = (0, import_react3.useMemo)(() => {
|
|
1045
|
+
const q = search.trim().toLowerCase();
|
|
1046
|
+
if (!q) return availableFx;
|
|
1047
|
+
return availableFx.filter(
|
|
1048
|
+
(candidate) => candidate.name.toLowerCase().includes(q) || candidate.manufacturer.toLowerCase().includes(q)
|
|
1049
|
+
);
|
|
1050
|
+
}, [availableFx, search]);
|
|
1051
|
+
if (!supported) return null;
|
|
1052
|
+
const entries = fx ?? [];
|
|
1053
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1054
|
+
"div",
|
|
1055
|
+
{
|
|
1056
|
+
"data-testid": "track-external-fx-section",
|
|
1057
|
+
className: "flex flex-col gap-1.5 pt-2 mt-1 border-t border-sas-border/60",
|
|
1058
|
+
children: [
|
|
1059
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1060
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1061
|
+
"span",
|
|
1062
|
+
{
|
|
1063
|
+
className: "text-[9px] font-bold tracking-widest text-sas-muted/70 select-none",
|
|
1064
|
+
title: "Third-party FX inserts (VST3/AU) on this track, before its fader. Settings persist with the project.",
|
|
1065
|
+
children: "3RD-PARTY FX"
|
|
1066
|
+
}
|
|
1067
|
+
),
|
|
1068
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-1 flex-1 min-w-0 overflow-x-auto", children: [
|
|
1069
|
+
entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1070
|
+
"span",
|
|
1071
|
+
{
|
|
1072
|
+
"data-testid": `track-fx-chip-${entry.index}`,
|
|
1073
|
+
className: `flex items-center gap-1 px-1.5 py-0.5 rounded-sm border text-[10px] whitespace-nowrap ${entry.enabled ? "border-sas-accent/60 text-sas-accent bg-sas-accent/10" : "border-sas-border text-sas-muted/50 bg-sas-panel"}`,
|
|
1074
|
+
title: `${entry.name}${entry.enabled ? "" : " (bypassed)"}`,
|
|
1075
|
+
children: [
|
|
1076
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1077
|
+
"button",
|
|
1078
|
+
{
|
|
1079
|
+
"data-testid": `track-fx-toggle-${entry.index}`,
|
|
1080
|
+
onClick: () => onToggleFxEnabled(entry.index, !entry.enabled),
|
|
1081
|
+
disabled,
|
|
1082
|
+
className: "hover:opacity-70 disabled:opacity-50",
|
|
1083
|
+
title: entry.enabled ? `Bypass ${entry.name}` : `Enable ${entry.name}`,
|
|
1084
|
+
children: entry.enabled ? "\u25CF" : "\u25CB"
|
|
1085
|
+
}
|
|
1086
|
+
),
|
|
1087
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1088
|
+
"button",
|
|
1089
|
+
{
|
|
1090
|
+
"data-testid": `track-fx-edit-${entry.index}`,
|
|
1091
|
+
onClick: () => onShowFxEditor(entry.index),
|
|
1092
|
+
disabled,
|
|
1093
|
+
className: "max-w-[110px] truncate hover:underline disabled:opacity-50",
|
|
1094
|
+
title: `Open ${entry.name} editor`,
|
|
1095
|
+
children: entry.name
|
|
1096
|
+
}
|
|
1097
|
+
),
|
|
1098
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1099
|
+
"button",
|
|
1100
|
+
{
|
|
1101
|
+
"data-testid": `track-fx-remove-${entry.index}`,
|
|
1102
|
+
onClick: () => onRemoveFx(entry.index),
|
|
1103
|
+
disabled,
|
|
1104
|
+
className: "text-sas-muted/60 hover:text-sas-danger disabled:opacity-50",
|
|
1105
|
+
title: `Remove ${entry.name} from this track`,
|
|
1106
|
+
children: "\u2715"
|
|
1107
|
+
}
|
|
1108
|
+
)
|
|
1109
|
+
]
|
|
1110
|
+
},
|
|
1111
|
+
`${entry.index}:${entry.pluginId}`
|
|
1112
|
+
)),
|
|
1113
|
+
entries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[10px] text-sas-muted/40 select-none", children: "none" })
|
|
1114
|
+
] }),
|
|
1115
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1116
|
+
"button",
|
|
1117
|
+
{
|
|
1118
|
+
"data-testid": "track-fx-add-button",
|
|
1119
|
+
onClick: () => setPickerOpen(!pickerOpen),
|
|
1120
|
+
disabled,
|
|
1121
|
+
className: `px-1.5 py-0.5 rounded-sm border text-xs whitespace-nowrap transition-colors ${pickerOpen ? "border-sas-accent text-sas-accent bg-sas-accent/10" : "border-sas-border text-sas-muted hover:border-sas-accent hover:text-sas-accent"} disabled:opacity-50`,
|
|
1122
|
+
title: pickerOpen ? "Close the FX picker" : "Add a VST3/AU FX plugin to this track",
|
|
1123
|
+
children: pickerOpen ? "FX \u25B4" : "FX +"
|
|
1124
|
+
}
|
|
1125
|
+
)
|
|
1126
|
+
] }),
|
|
1127
|
+
pickerOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { "data-testid": "track-fx-picker", className: "flex flex-col gap-2 pt-1", children: [
|
|
1128
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1129
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1130
|
+
"input",
|
|
1131
|
+
{
|
|
1132
|
+
type: "text",
|
|
1133
|
+
value: search,
|
|
1134
|
+
onChange: (e) => setSearch(e.target.value),
|
|
1135
|
+
placeholder: "Search FX...",
|
|
1136
|
+
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
1137
|
+
}
|
|
1138
|
+
),
|
|
1139
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1140
|
+
"button",
|
|
1141
|
+
{
|
|
1142
|
+
onClick: () => refreshFx(),
|
|
1143
|
+
disabled: fxLoading,
|
|
1144
|
+
className: "px-2 py-1 text-xs rounded-sm border border-sas-border text-sas-muted hover:text-sas-accent hover:border-sas-accent transition-colors disabled:opacity-50",
|
|
1145
|
+
title: "Re-scan plugins",
|
|
1146
|
+
children: fxLoading ? "..." : "Refresh"
|
|
1147
|
+
}
|
|
1148
|
+
)
|
|
1149
|
+
] }),
|
|
1150
|
+
fxLoading && availableFx.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
1151
|
+
filtered.map((candidate) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1152
|
+
"button",
|
|
1153
|
+
{
|
|
1154
|
+
"data-testid": `track-fx-pick-${candidate.pluginId}`,
|
|
1155
|
+
onClick: () => {
|
|
1156
|
+
onAddFx(candidate.pluginId);
|
|
1157
|
+
setPickerOpen(false);
|
|
1158
|
+
},
|
|
1159
|
+
disabled,
|
|
1160
|
+
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 disabled:opacity-50",
|
|
1161
|
+
title: `${candidate.name} by ${candidate.manufacturer} (${candidate.type.toUpperCase()})`,
|
|
1162
|
+
children: [
|
|
1163
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-xs font-medium truncate w-full", children: candidate.name }),
|
|
1164
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: candidate.manufacturer || candidate.type.toUpperCase() })
|
|
1165
|
+
]
|
|
1166
|
+
},
|
|
1167
|
+
candidate.pluginId
|
|
1168
|
+
)),
|
|
1169
|
+
filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "col-span-3 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No FX plugins found" })
|
|
1170
|
+
] })
|
|
1171
|
+
] })
|
|
1172
|
+
]
|
|
1173
|
+
}
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// src/components/TrackDrawer.tsx
|
|
1178
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
933
1179
|
var TAB_LABELS = {
|
|
934
1180
|
fx: "FX",
|
|
935
1181
|
pick: "Pick",
|
|
@@ -946,6 +1192,7 @@ function TrackDrawer({
|
|
|
946
1192
|
onFxPresetChange,
|
|
947
1193
|
onFxDryWetChange,
|
|
948
1194
|
fxDisabled = false,
|
|
1195
|
+
externalFxHost,
|
|
949
1196
|
instruments = [],
|
|
950
1197
|
currentPluginId = null,
|
|
951
1198
|
isLoading = false,
|
|
@@ -968,13 +1215,13 @@ function TrackDrawer({
|
|
|
968
1215
|
editSnap,
|
|
969
1216
|
onAuditionNote
|
|
970
1217
|
}) {
|
|
971
|
-
const [search, setSearch] = (0,
|
|
1218
|
+
const [search, setSearch] = (0, import_react4.useState)("");
|
|
972
1219
|
const fxEnabled = !!onFxToggle;
|
|
973
1220
|
const pickEnabled = !!onSelect;
|
|
974
1221
|
const historyEnabled = !!onRestoreSound;
|
|
975
1222
|
const importEnabled = !!onImportSound;
|
|
976
1223
|
const editEnabled = !!onNotesChange;
|
|
977
|
-
const enabledTabs = (0,
|
|
1224
|
+
const enabledTabs = (0, import_react4.useMemo)(() => {
|
|
978
1225
|
const tabs = [];
|
|
979
1226
|
if (fxEnabled) tabs.push("fx");
|
|
980
1227
|
if (pickEnabled) tabs.push("pick");
|
|
@@ -984,7 +1231,7 @@ function TrackDrawer({
|
|
|
984
1231
|
return tabs;
|
|
985
1232
|
}, [fxEnabled, pickEnabled, historyEnabled, importEnabled, editEnabled]);
|
|
986
1233
|
const SURGE_XT_DEFAULT_ID = "Surge XT";
|
|
987
|
-
const filtered = (0,
|
|
1234
|
+
const filtered = (0, import_react4.useMemo)(() => {
|
|
988
1235
|
let all = instruments.filter((i) => i.name !== "Surge XT");
|
|
989
1236
|
if (search.trim()) {
|
|
990
1237
|
const q = search.toLowerCase();
|
|
@@ -1004,12 +1251,12 @@ function TrackDrawer({
|
|
|
1004
1251
|
const history = soundHistory ?? [];
|
|
1005
1252
|
const effectiveTab = enabledTabs.includes(activeTab) ? activeTab : enabledTabs[0] ?? "fx";
|
|
1006
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"}`;
|
|
1007
|
-
const strip = enabledTabs.length > 1 ? /* @__PURE__ */ (0,
|
|
1254
|
+
const strip = enabledTabs.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1008
1255
|
"div",
|
|
1009
1256
|
{
|
|
1010
1257
|
className: "flex items-center gap-1 border-b border-sas-border pb-1",
|
|
1011
1258
|
"data-testid": "sdk-drawer-tabs",
|
|
1012
|
-
children: enabledTabs.map((tab) => /* @__PURE__ */ (0,
|
|
1259
|
+
children: enabledTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1013
1260
|
"button",
|
|
1014
1261
|
{
|
|
1015
1262
|
type: "button",
|
|
@@ -1023,9 +1270,9 @@ function TrackDrawer({
|
|
|
1023
1270
|
}
|
|
1024
1271
|
) : null;
|
|
1025
1272
|
const currentSound = soundHistoryCursor >= 0 && soundHistoryCursor < history.length ? history[soundHistoryCursor].label : null;
|
|
1026
|
-
const header = strip || currentSound ? /* @__PURE__ */ (0,
|
|
1273
|
+
const header = strip || currentSound ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-1", "data-testid": "sdk-drawer-header", children: [
|
|
1027
1274
|
strip,
|
|
1028
|
-
currentSound && /* @__PURE__ */ (0,
|
|
1275
|
+
currentSound && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1029
1276
|
"span",
|
|
1030
1277
|
{
|
|
1031
1278
|
className: "text-[10px] text-sas-muted/60 truncate px-0.5",
|
|
@@ -1035,9 +1282,9 @@ function TrackDrawer({
|
|
|
1035
1282
|
)
|
|
1036
1283
|
] }) : null;
|
|
1037
1284
|
if (effectiveTab === "edit") {
|
|
1038
|
-
return /* @__PURE__ */ (0,
|
|
1285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-edit", children: [
|
|
1039
1286
|
header,
|
|
1040
|
-
/* @__PURE__ */ (0,
|
|
1287
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1041
1288
|
PianoRollEditor,
|
|
1042
1289
|
{
|
|
1043
1290
|
notes: editNotes ?? [],
|
|
@@ -1052,9 +1299,9 @@ function TrackDrawer({
|
|
|
1052
1299
|
] });
|
|
1053
1300
|
}
|
|
1054
1301
|
if (effectiveTab === "fx") {
|
|
1055
|
-
return /* @__PURE__ */ (0,
|
|
1302
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-fx", children: [
|
|
1056
1303
|
header,
|
|
1057
|
-
/* @__PURE__ */ (0,
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1058
1305
|
FxToggleBar,
|
|
1059
1306
|
{
|
|
1060
1307
|
trackId,
|
|
@@ -1064,20 +1311,21 @@ function TrackDrawer({
|
|
|
1064
1311
|
onDryWetChange: (_t, category, value) => onFxDryWetChange?.(category, value),
|
|
1065
1312
|
disabled: fxDisabled
|
|
1066
1313
|
}
|
|
1067
|
-
)
|
|
1314
|
+
),
|
|
1315
|
+
externalFxHost && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TrackExternalFxSection, { host: externalFxHost, trackId, disabled: fxDisabled })
|
|
1068
1316
|
] });
|
|
1069
1317
|
}
|
|
1070
1318
|
if (effectiveTab === "import") {
|
|
1071
1319
|
const soundNoun = /preset/i.test(importSoundLabel ?? "") ? "preset" : /sample/i.test(importSoundLabel ?? "") ? "sample" : "sound";
|
|
1072
|
-
return /* @__PURE__ */ (0,
|
|
1320
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-import", children: [
|
|
1073
1321
|
header,
|
|
1074
|
-
/* @__PURE__ */ (0,
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "text-[11px] text-sas-muted/70 leading-snug", children: [
|
|
1075
1323
|
"Copy the sound from a matching track in another scene \u2014 your MIDI stays, only the",
|
|
1076
1324
|
" ",
|
|
1077
1325
|
soundNoun,
|
|
1078
1326
|
" changes."
|
|
1079
1327
|
] }),
|
|
1080
|
-
/* @__PURE__ */ (0,
|
|
1328
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1081
1329
|
"button",
|
|
1082
1330
|
{
|
|
1083
1331
|
type: "button",
|
|
@@ -1095,16 +1343,16 @@ function TrackDrawer({
|
|
|
1095
1343
|
}
|
|
1096
1344
|
if (effectiveTab === "history") {
|
|
1097
1345
|
const order = history.map((_, i) => i).reverse();
|
|
1098
|
-
return /* @__PURE__ */ (0,
|
|
1346
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1099
1347
|
header,
|
|
1100
|
-
history.length === 0 ? /* @__PURE__ */ (0,
|
|
1348
|
+
history.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1101
1349
|
"div",
|
|
1102
1350
|
{
|
|
1103
1351
|
className: "text-xs text-sas-muted/60 text-center py-3",
|
|
1104
1352
|
"data-testid": "sdk-history-empty",
|
|
1105
1353
|
children: "No sounds yet \u2014 shuffle to build history."
|
|
1106
1354
|
}
|
|
1107
|
-
) : /* @__PURE__ */ (0,
|
|
1355
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1108
1356
|
"ul",
|
|
1109
1357
|
{
|
|
1110
1358
|
className: "flex flex-col gap-1 max-h-[160px] overflow-y-auto",
|
|
@@ -1112,8 +1360,8 @@ function TrackDrawer({
|
|
|
1112
1360
|
children: order.map((i) => {
|
|
1113
1361
|
const entry = history[i];
|
|
1114
1362
|
const isCurrent = i === soundHistoryCursor;
|
|
1115
|
-
return /* @__PURE__ */ (0,
|
|
1116
|
-
/* @__PURE__ */ (0,
|
|
1363
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "flex items-center gap-1", children: [
|
|
1364
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1117
1365
|
"button",
|
|
1118
1366
|
{
|
|
1119
1367
|
type: "button",
|
|
@@ -1123,12 +1371,12 @@ function TrackDrawer({
|
|
|
1123
1371
|
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"}`,
|
|
1124
1372
|
title: isCurrent ? "Current sound" : `Restore: ${entry.label}`,
|
|
1125
1373
|
children: [
|
|
1126
|
-
/* @__PURE__ */ (0,
|
|
1127
|
-
/* @__PURE__ */ (0,
|
|
1374
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate", children: entry.label }),
|
|
1375
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[10px] text-sas-muted/60 flex-shrink-0 ml-2", children: isCurrent ? "\u25CF current" : "restore" })
|
|
1128
1376
|
]
|
|
1129
1377
|
}
|
|
1130
1378
|
),
|
|
1131
|
-
onToggleFavorite && /* @__PURE__ */ (0,
|
|
1379
|
+
onToggleFavorite && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1132
1380
|
"button",
|
|
1133
1381
|
{
|
|
1134
1382
|
type: "button",
|
|
@@ -1146,10 +1394,10 @@ function TrackDrawer({
|
|
|
1146
1394
|
] });
|
|
1147
1395
|
}
|
|
1148
1396
|
if (effectiveTab === "pick" && editorStage) {
|
|
1149
|
-
return /* @__PURE__ */ (0,
|
|
1397
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1150
1398
|
header,
|
|
1151
|
-
/* @__PURE__ */ (0,
|
|
1152
|
-
/* @__PURE__ */ (0,
|
|
1399
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1400
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1153
1401
|
"button",
|
|
1154
1402
|
{
|
|
1155
1403
|
onClick: () => onBackToInstruments?.(),
|
|
@@ -1157,9 +1405,9 @@ function TrackDrawer({
|
|
|
1157
1405
|
children: "\u2190 Back"
|
|
1158
1406
|
}
|
|
1159
1407
|
),
|
|
1160
|
-
/* @__PURE__ */ (0,
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-xs text-sas-muted font-medium truncate flex-1", children: selectedInstrumentName ?? "Plugin" })
|
|
1161
1409
|
] }),
|
|
1162
|
-
/* @__PURE__ */ (0,
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1163
1411
|
"button",
|
|
1164
1412
|
{
|
|
1165
1413
|
onClick: () => onShowEditor?.(),
|
|
@@ -1171,10 +1419,10 @@ function TrackDrawer({
|
|
|
1171
1419
|
}
|
|
1172
1420
|
const isDefaultSelected = currentPluginId === null;
|
|
1173
1421
|
const isSelected = (pluginId) => pluginId === currentPluginId;
|
|
1174
|
-
return /* @__PURE__ */ (0,
|
|
1422
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1175
1423
|
header,
|
|
1176
|
-
/* @__PURE__ */ (0,
|
|
1177
|
-
/* @__PURE__ */ (0,
|
|
1424
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1425
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1178
1426
|
"input",
|
|
1179
1427
|
{
|
|
1180
1428
|
type: "text",
|
|
@@ -1184,7 +1432,7 @@ function TrackDrawer({
|
|
|
1184
1432
|
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
1185
1433
|
}
|
|
1186
1434
|
),
|
|
1187
|
-
/* @__PURE__ */ (0,
|
|
1435
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1188
1436
|
"button",
|
|
1189
1437
|
{
|
|
1190
1438
|
onClick: () => onRefresh?.(),
|
|
@@ -1195,54 +1443,54 @@ function TrackDrawer({
|
|
|
1195
1443
|
}
|
|
1196
1444
|
)
|
|
1197
1445
|
] }),
|
|
1198
|
-
isLoading && instruments.length === 0 ? /* @__PURE__ */ (0,
|
|
1199
|
-
/* @__PURE__ */ (0,
|
|
1446
|
+
isLoading && instruments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1200
1448
|
"button",
|
|
1201
1449
|
{
|
|
1202
1450
|
onClick: () => onSelect?.(SURGE_XT_DEFAULT_ID),
|
|
1203
1451
|
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"}`,
|
|
1204
1452
|
title: "Surge XT \u2014 Default instrument",
|
|
1205
1453
|
children: [
|
|
1206
|
-
/* @__PURE__ */ (0,
|
|
1454
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1207
1455
|
isDefaultSelected && "\u2713 ",
|
|
1208
1456
|
"Surge XT"
|
|
1209
1457
|
] }),
|
|
1210
|
-
/* @__PURE__ */ (0,
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: "Default" })
|
|
1211
1459
|
]
|
|
1212
1460
|
},
|
|
1213
1461
|
"__surge-xt-default__"
|
|
1214
1462
|
),
|
|
1215
1463
|
filtered.map((inst) => {
|
|
1216
1464
|
const selected = isSelected(inst.pluginId);
|
|
1217
|
-
return /* @__PURE__ */ (0,
|
|
1465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1218
1466
|
"button",
|
|
1219
1467
|
{
|
|
1220
1468
|
onClick: () => onSelect?.(inst.pluginId),
|
|
1221
1469
|
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"}`,
|
|
1222
1470
|
title: `${inst.name} by ${inst.manufacturer} (${inst.type.toUpperCase()})${inst.missing ? " \u2014 MISSING" : ""}`,
|
|
1223
1471
|
children: [
|
|
1224
|
-
/* @__PURE__ */ (0,
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1225
1473
|
selected && "\u2713 ",
|
|
1226
1474
|
inst.name
|
|
1227
1475
|
] }),
|
|
1228
|
-
/* @__PURE__ */ (0,
|
|
1476
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: inst.manufacturer || inst.type.toUpperCase() })
|
|
1229
1477
|
]
|
|
1230
1478
|
},
|
|
1231
1479
|
inst.pluginId
|
|
1232
1480
|
);
|
|
1233
1481
|
}),
|
|
1234
|
-
filtered.length === 0 && /* @__PURE__ */ (0,
|
|
1482
|
+
filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "col-span-2 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No other plugins found" })
|
|
1235
1483
|
] })
|
|
1236
1484
|
] });
|
|
1237
1485
|
}
|
|
1238
1486
|
|
|
1239
1487
|
// src/components/ConfirmDialog.tsx
|
|
1240
|
-
var
|
|
1488
|
+
var import_react6 = require("react");
|
|
1241
1489
|
|
|
1242
1490
|
// src/components/Modal.tsx
|
|
1243
|
-
var
|
|
1491
|
+
var import_react5 = require("react");
|
|
1244
1492
|
var import_react_dom = require("react-dom");
|
|
1245
|
-
var
|
|
1493
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1246
1494
|
function Modal({
|
|
1247
1495
|
open,
|
|
1248
1496
|
onClose,
|
|
@@ -1252,7 +1500,7 @@ function Modal({
|
|
|
1252
1500
|
closeOnEscape = true,
|
|
1253
1501
|
initialFocusRef
|
|
1254
1502
|
}) {
|
|
1255
|
-
(0,
|
|
1503
|
+
(0, import_react5.useEffect)(() => {
|
|
1256
1504
|
if (!open) return void 0;
|
|
1257
1505
|
const onKey = (e) => {
|
|
1258
1506
|
if (closeOnEscape && e.key === "Escape") {
|
|
@@ -1266,7 +1514,7 @@ function Modal({
|
|
|
1266
1514
|
}, [open, onClose, closeOnEscape, initialFocusRef]);
|
|
1267
1515
|
if (!open) return null;
|
|
1268
1516
|
return (0, import_react_dom.createPortal)(
|
|
1269
|
-
/* @__PURE__ */ (0,
|
|
1517
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1270
1518
|
"div",
|
|
1271
1519
|
{
|
|
1272
1520
|
className: "fixed inset-0 z-[1000] flex items-center justify-center bg-black/60",
|
|
@@ -1280,7 +1528,7 @@ function Modal({
|
|
|
1280
1528
|
}
|
|
1281
1529
|
|
|
1282
1530
|
// src/components/ConfirmDialog.tsx
|
|
1283
|
-
var
|
|
1531
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1284
1532
|
function ConfirmDialog({
|
|
1285
1533
|
open,
|
|
1286
1534
|
title,
|
|
@@ -1292,8 +1540,8 @@ function ConfirmDialog({
|
|
|
1292
1540
|
onCancel,
|
|
1293
1541
|
testIdPrefix = "confirm-dialog"
|
|
1294
1542
|
}) {
|
|
1295
|
-
const cancelRef = (0,
|
|
1296
|
-
return /* @__PURE__ */ (0,
|
|
1543
|
+
const cancelRef = (0, import_react6.useRef)(null);
|
|
1544
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Modal, { open, onClose: onCancel, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1297
1545
|
"div",
|
|
1298
1546
|
{
|
|
1299
1547
|
className: "w-[360px] max-w-[90vw] flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
@@ -1303,8 +1551,8 @@ function ConfirmDialog({
|
|
|
1303
1551
|
"aria-label": title,
|
|
1304
1552
|
"data-testid": `${testIdPrefix}-modal`,
|
|
1305
1553
|
children: [
|
|
1306
|
-
/* @__PURE__ */ (0,
|
|
1307
|
-
/* @__PURE__ */ (0,
|
|
1554
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "px-4 py-3 border-b border-sas-border", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-sm font-medium text-sas-text", "data-testid": `${testIdPrefix}-title`, children: title }) }),
|
|
1555
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1308
1556
|
"div",
|
|
1309
1557
|
{
|
|
1310
1558
|
className: "px-4 py-3 text-xs text-sas-muted leading-relaxed break-words",
|
|
@@ -1312,8 +1560,8 @@ function ConfirmDialog({
|
|
|
1312
1560
|
children: message
|
|
1313
1561
|
}
|
|
1314
1562
|
),
|
|
1315
|
-
/* @__PURE__ */ (0,
|
|
1316
|
-
/* @__PURE__ */ (0,
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex justify-end gap-2 px-4 py-3 border-t border-sas-border", children: [
|
|
1564
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1317
1565
|
"button",
|
|
1318
1566
|
{
|
|
1319
1567
|
ref: cancelRef,
|
|
@@ -1324,7 +1572,7 @@ function ConfirmDialog({
|
|
|
1324
1572
|
children: cancelLabel
|
|
1325
1573
|
}
|
|
1326
1574
|
),
|
|
1327
|
-
/* @__PURE__ */ (0,
|
|
1575
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1328
1576
|
"button",
|
|
1329
1577
|
{
|
|
1330
1578
|
type: "button",
|
|
@@ -1341,7 +1589,7 @@ function ConfirmDialog({
|
|
|
1341
1589
|
}
|
|
1342
1590
|
|
|
1343
1591
|
// src/components/LevelMeter.tsx
|
|
1344
|
-
var
|
|
1592
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1345
1593
|
var COLOR_GREEN = "#2BD576";
|
|
1346
1594
|
var COLOR_ORANGE = "#F5A623";
|
|
1347
1595
|
var COLOR_RED = "#FF4D5E";
|
|
@@ -1369,7 +1617,7 @@ var LevelMeter = ({
|
|
|
1369
1617
|
const widthPct = active ? dbToPct(peakDb) : 0;
|
|
1370
1618
|
const showPeak = peakHoldDb != null && active && peakHoldDb > -60;
|
|
1371
1619
|
const peakHoldPct = showPeak ? dbToPct(peakHoldDb) : 0;
|
|
1372
|
-
return /* @__PURE__ */ (0,
|
|
1620
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1373
1621
|
"div",
|
|
1374
1622
|
{
|
|
1375
1623
|
className: `sas-level-meter ${className ?? ""}`,
|
|
@@ -1380,7 +1628,7 @@ var LevelMeter = ({
|
|
|
1380
1628
|
gap: compact ? 0 : 6
|
|
1381
1629
|
},
|
|
1382
1630
|
children: [
|
|
1383
|
-
/* @__PURE__ */ (0,
|
|
1631
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1384
1632
|
"div",
|
|
1385
1633
|
{
|
|
1386
1634
|
style: {
|
|
@@ -1394,8 +1642,8 @@ var LevelMeter = ({
|
|
|
1394
1642
|
minWidth: compact ? 0 : 60
|
|
1395
1643
|
},
|
|
1396
1644
|
children: [
|
|
1397
|
-
/* @__PURE__ */ (0,
|
|
1398
|
-
/* @__PURE__ */ (0,
|
|
1645
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { position: "absolute", inset: 0, background: METER_GRADIENT } }),
|
|
1646
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1399
1647
|
"div",
|
|
1400
1648
|
{
|
|
1401
1649
|
style: {
|
|
@@ -1409,7 +1657,7 @@ var LevelMeter = ({
|
|
|
1409
1657
|
}
|
|
1410
1658
|
}
|
|
1411
1659
|
),
|
|
1412
|
-
/* @__PURE__ */ (0,
|
|
1660
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1413
1661
|
"div",
|
|
1414
1662
|
{
|
|
1415
1663
|
"data-testid": `${id}-segments`,
|
|
@@ -1422,7 +1670,7 @@ var LevelMeter = ({
|
|
|
1422
1670
|
}
|
|
1423
1671
|
}
|
|
1424
1672
|
),
|
|
1425
|
-
showPeak && /* @__PURE__ */ (0,
|
|
1673
|
+
showPeak && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1426
1674
|
"div",
|
|
1427
1675
|
{
|
|
1428
1676
|
"data-testid": `${id}-peak`,
|
|
@@ -1443,7 +1691,7 @@ var LevelMeter = ({
|
|
|
1443
1691
|
]
|
|
1444
1692
|
}
|
|
1445
1693
|
),
|
|
1446
|
-
!compact && /* @__PURE__ */ (0,
|
|
1694
|
+
!compact && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1447
1695
|
"span",
|
|
1448
1696
|
{
|
|
1449
1697
|
style: {
|
|
@@ -1456,7 +1704,7 @@ var LevelMeter = ({
|
|
|
1456
1704
|
children: active && peakDb > -120 ? `${peakDb.toFixed(0)} dB` : "\u2014"
|
|
1457
1705
|
}
|
|
1458
1706
|
),
|
|
1459
|
-
clipped && /* @__PURE__ */ (0,
|
|
1707
|
+
clipped && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1460
1708
|
"span",
|
|
1461
1709
|
{
|
|
1462
1710
|
"data-testid": `${id}-clip`,
|
|
@@ -1481,7 +1729,7 @@ var LevelMeter = ({
|
|
|
1481
1729
|
};
|
|
1482
1730
|
|
|
1483
1731
|
// src/hooks/useTrackLevels.ts
|
|
1484
|
-
var
|
|
1732
|
+
var import_react7 = require("react");
|
|
1485
1733
|
var meterDiagRLast = /* @__PURE__ */ new Map();
|
|
1486
1734
|
var POLL_INTERVAL_MS = 33;
|
|
1487
1735
|
var HIDDEN_RECHECK_MS = 250;
|
|
@@ -1492,9 +1740,9 @@ function isHidden() {
|
|
|
1492
1740
|
return typeof document !== "undefined" && document.hidden === true;
|
|
1493
1741
|
}
|
|
1494
1742
|
function useTrackLevels(host, enabled = true) {
|
|
1495
|
-
const mapRef = (0,
|
|
1496
|
-
const listenersRef = (0,
|
|
1497
|
-
const handleRef = (0,
|
|
1743
|
+
const mapRef = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
1744
|
+
const listenersRef = (0, import_react7.useRef)(/* @__PURE__ */ new Set());
|
|
1745
|
+
const handleRef = (0, import_react7.useRef)(null);
|
|
1498
1746
|
if (handleRef.current === null) {
|
|
1499
1747
|
handleRef.current = {
|
|
1500
1748
|
getLevel: (trackId) => mapRef.current.get(trackId) ?? null,
|
|
@@ -1506,7 +1754,7 @@ function useTrackLevels(host, enabled = true) {
|
|
|
1506
1754
|
}
|
|
1507
1755
|
};
|
|
1508
1756
|
}
|
|
1509
|
-
(0,
|
|
1757
|
+
(0, import_react7.useEffect)(() => {
|
|
1510
1758
|
const notify = () => {
|
|
1511
1759
|
listenersRef.current.forEach((l) => l());
|
|
1512
1760
|
};
|
|
@@ -1576,8 +1824,8 @@ function sameLevel(a, b) {
|
|
|
1576
1824
|
return a.peakDb === b.peakDb && a.clipped === b.clipped;
|
|
1577
1825
|
}
|
|
1578
1826
|
function useTrackLevel(handle, trackId) {
|
|
1579
|
-
const [level, setLevel] = (0,
|
|
1580
|
-
(0,
|
|
1827
|
+
const [level, setLevel] = (0, import_react7.useState)(null);
|
|
1828
|
+
(0, import_react7.useEffect)(() => {
|
|
1581
1829
|
if (!handle) {
|
|
1582
1830
|
setLevel(null);
|
|
1583
1831
|
return;
|
|
@@ -1601,11 +1849,11 @@ function sameMeter(a, b) {
|
|
|
1601
1849
|
return a.active === b.active && a.clipped === b.clipped && a.peakDb === b.peakDb && Math.round(a.peakHoldDb * 2) === Math.round(b.peakHoldDb * 2);
|
|
1602
1850
|
}
|
|
1603
1851
|
function useTrackMeter(handle, trackId) {
|
|
1604
|
-
const [view, setView] = (0,
|
|
1605
|
-
const heldDbRef = (0,
|
|
1606
|
-
const heldAtRef = (0,
|
|
1607
|
-
const lastTickRef = (0,
|
|
1608
|
-
(0,
|
|
1852
|
+
const [view, setView] = (0, import_react7.useState)(IDLE_METER_VIEW);
|
|
1853
|
+
const heldDbRef = (0, import_react7.useRef)(METER_FLOOR_DB);
|
|
1854
|
+
const heldAtRef = (0, import_react7.useRef)(0);
|
|
1855
|
+
const lastTickRef = (0, import_react7.useRef)(0);
|
|
1856
|
+
(0, import_react7.useEffect)(() => {
|
|
1609
1857
|
if (!handle) {
|
|
1610
1858
|
heldDbRef.current = METER_FLOOR_DB;
|
|
1611
1859
|
lastTickRef.current = 0;
|
|
@@ -1648,8 +1896,8 @@ function useTrackMeter(handle, trackId) {
|
|
|
1648
1896
|
return view;
|
|
1649
1897
|
}
|
|
1650
1898
|
function useTransportPlaying(host) {
|
|
1651
|
-
const [playing, setPlaying] = (0,
|
|
1652
|
-
(0,
|
|
1899
|
+
const [playing, setPlaying] = (0, import_react7.useState)(false);
|
|
1900
|
+
(0, import_react7.useEffect)(() => {
|
|
1653
1901
|
if (!host) {
|
|
1654
1902
|
setPlaying(false);
|
|
1655
1903
|
return;
|
|
@@ -1677,7 +1925,7 @@ function useTransportPlaying(host) {
|
|
|
1677
1925
|
}
|
|
1678
1926
|
|
|
1679
1927
|
// src/components/TrackMeterStrip.tsx
|
|
1680
|
-
var
|
|
1928
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1681
1929
|
var TrackMeterStrip = ({
|
|
1682
1930
|
levels,
|
|
1683
1931
|
trackId,
|
|
@@ -1685,12 +1933,12 @@ var TrackMeterStrip = ({
|
|
|
1685
1933
|
className
|
|
1686
1934
|
}) => {
|
|
1687
1935
|
const meter = useTrackMeter(levels, trackId);
|
|
1688
|
-
return /* @__PURE__ */ (0,
|
|
1936
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1689
1937
|
"div",
|
|
1690
1938
|
{
|
|
1691
1939
|
"data-testid": "sdk-track-meter",
|
|
1692
1940
|
className: `w-full px-2 py-1 bg-sas-panel-alt border border-t-0 border-sas-border ${roundBottom ? "rounded-b-sm" : ""} ${className ?? ""}`,
|
|
1693
|
-
children: /* @__PURE__ */ (0,
|
|
1941
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1694
1942
|
LevelMeter,
|
|
1695
1943
|
{
|
|
1696
1944
|
compact: true,
|
|
@@ -1706,7 +1954,7 @@ var TrackMeterStrip = ({
|
|
|
1706
1954
|
};
|
|
1707
1955
|
|
|
1708
1956
|
// src/components/VolumeSlider.tsx
|
|
1709
|
-
var
|
|
1957
|
+
var import_react8 = require("react");
|
|
1710
1958
|
|
|
1711
1959
|
// src/utils/volume-conversion.ts
|
|
1712
1960
|
var SLIDER_UNITY = 0.75;
|
|
@@ -1728,7 +1976,7 @@ function dbToSlider(db) {
|
|
|
1728
1976
|
}
|
|
1729
1977
|
|
|
1730
1978
|
// src/components/VolumeSlider.tsx
|
|
1731
|
-
var
|
|
1979
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1732
1980
|
function formatDb(value) {
|
|
1733
1981
|
const db = sliderToDb(value);
|
|
1734
1982
|
if (db <= -60) return "-\u221E dB";
|
|
@@ -1736,12 +1984,12 @@ function formatDb(value) {
|
|
|
1736
1984
|
return `${sign}${db.toFixed(1)} dB`;
|
|
1737
1985
|
}
|
|
1738
1986
|
function useDebouncedCallback(callback, delay) {
|
|
1739
|
-
const timeoutRef = (0,
|
|
1740
|
-
const callbackRef = (0,
|
|
1741
|
-
(0,
|
|
1987
|
+
const timeoutRef = (0, import_react8.useRef)(null);
|
|
1988
|
+
const callbackRef = (0, import_react8.useRef)(callback);
|
|
1989
|
+
(0, import_react8.useEffect)(() => {
|
|
1742
1990
|
callbackRef.current = callback;
|
|
1743
1991
|
}, [callback]);
|
|
1744
|
-
const debouncedCallback = (0,
|
|
1992
|
+
const debouncedCallback = (0, import_react8.useCallback)(
|
|
1745
1993
|
(...args) => {
|
|
1746
1994
|
if (timeoutRef.current) {
|
|
1747
1995
|
clearTimeout(timeoutRef.current);
|
|
@@ -1752,7 +2000,7 @@ function useDebouncedCallback(callback, delay) {
|
|
|
1752
2000
|
},
|
|
1753
2001
|
[delay]
|
|
1754
2002
|
);
|
|
1755
|
-
(0,
|
|
2003
|
+
(0, import_react8.useEffect)(() => {
|
|
1756
2004
|
return () => {
|
|
1757
2005
|
if (timeoutRef.current) {
|
|
1758
2006
|
clearTimeout(timeoutRef.current);
|
|
@@ -1767,15 +2015,15 @@ var VolumeSlider = ({
|
|
|
1767
2015
|
disabled = false,
|
|
1768
2016
|
className = ""
|
|
1769
2017
|
}) => {
|
|
1770
|
-
const [localValue, setLocalValue] = (0,
|
|
1771
|
-
const [isDragging, setIsDragging] = (0,
|
|
1772
|
-
(0,
|
|
2018
|
+
const [localValue, setLocalValue] = (0, import_react8.useState)(value);
|
|
2019
|
+
const [isDragging, setIsDragging] = (0, import_react8.useState)(false);
|
|
2020
|
+
(0, import_react8.useEffect)(() => {
|
|
1773
2021
|
if (!isDragging) {
|
|
1774
2022
|
setLocalValue(value);
|
|
1775
2023
|
}
|
|
1776
2024
|
}, [value, isDragging]);
|
|
1777
2025
|
const debouncedOnChange = useDebouncedCallback(onChange, 50);
|
|
1778
|
-
const handleChange = (0,
|
|
2026
|
+
const handleChange = (0, import_react8.useCallback)(
|
|
1779
2027
|
(e) => {
|
|
1780
2028
|
const newValue = parseFloat(e.target.value);
|
|
1781
2029
|
setLocalValue(newValue);
|
|
@@ -1783,19 +2031,19 @@ var VolumeSlider = ({
|
|
|
1783
2031
|
},
|
|
1784
2032
|
[debouncedOnChange]
|
|
1785
2033
|
);
|
|
1786
|
-
const handleMouseDown = (0,
|
|
2034
|
+
const handleMouseDown = (0, import_react8.useCallback)(() => {
|
|
1787
2035
|
setIsDragging(true);
|
|
1788
2036
|
}, []);
|
|
1789
|
-
const handleMouseUp = (0,
|
|
2037
|
+
const handleMouseUp = (0, import_react8.useCallback)(() => {
|
|
1790
2038
|
setIsDragging(false);
|
|
1791
2039
|
onChange(localValue);
|
|
1792
2040
|
}, [localValue, onChange]);
|
|
1793
|
-
return /* @__PURE__ */ (0,
|
|
2041
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1794
2042
|
"div",
|
|
1795
2043
|
{
|
|
1796
2044
|
className: `flex items-center ${className}`,
|
|
1797
2045
|
title: `Volume: ${formatDb(localValue)}`,
|
|
1798
|
-
children: /* @__PURE__ */ (0,
|
|
2046
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1799
2047
|
"input",
|
|
1800
2048
|
{
|
|
1801
2049
|
type: "range",
|
|
@@ -1835,8 +2083,8 @@ var VolumeSlider = ({
|
|
|
1835
2083
|
};
|
|
1836
2084
|
|
|
1837
2085
|
// src/components/PanSlider.tsx
|
|
1838
|
-
var
|
|
1839
|
-
var
|
|
2086
|
+
var import_react9 = require("react");
|
|
2087
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1840
2088
|
function toPanDisplay(value) {
|
|
1841
2089
|
if (Math.abs(value) < 0.02) {
|
|
1842
2090
|
return "Center";
|
|
@@ -1845,12 +2093,12 @@ function toPanDisplay(value) {
|
|
|
1845
2093
|
return value < 0 ? `L${percent}` : `R${percent}`;
|
|
1846
2094
|
}
|
|
1847
2095
|
function useDebouncedCallback2(callback, delay) {
|
|
1848
|
-
const timeoutRef = (0,
|
|
1849
|
-
const callbackRef = (0,
|
|
1850
|
-
(0,
|
|
2096
|
+
const timeoutRef = (0, import_react9.useRef)(null);
|
|
2097
|
+
const callbackRef = (0, import_react9.useRef)(callback);
|
|
2098
|
+
(0, import_react9.useEffect)(() => {
|
|
1851
2099
|
callbackRef.current = callback;
|
|
1852
2100
|
}, [callback]);
|
|
1853
|
-
const debouncedCallback = (0,
|
|
2101
|
+
const debouncedCallback = (0, import_react9.useCallback)(
|
|
1854
2102
|
(...args) => {
|
|
1855
2103
|
if (timeoutRef.current) {
|
|
1856
2104
|
clearTimeout(timeoutRef.current);
|
|
@@ -1861,7 +2109,7 @@ function useDebouncedCallback2(callback, delay) {
|
|
|
1861
2109
|
},
|
|
1862
2110
|
[delay]
|
|
1863
2111
|
);
|
|
1864
|
-
(0,
|
|
2112
|
+
(0, import_react9.useEffect)(() => {
|
|
1865
2113
|
return () => {
|
|
1866
2114
|
if (timeoutRef.current) {
|
|
1867
2115
|
clearTimeout(timeoutRef.current);
|
|
@@ -1876,15 +2124,15 @@ var PanSlider = ({
|
|
|
1876
2124
|
disabled = false,
|
|
1877
2125
|
className = ""
|
|
1878
2126
|
}) => {
|
|
1879
|
-
const [localValue, setLocalValue] = (0,
|
|
1880
|
-
const [isDragging, setIsDragging] = (0,
|
|
1881
|
-
(0,
|
|
2127
|
+
const [localValue, setLocalValue] = (0, import_react9.useState)(value);
|
|
2128
|
+
const [isDragging, setIsDragging] = (0, import_react9.useState)(false);
|
|
2129
|
+
(0, import_react9.useEffect)(() => {
|
|
1882
2130
|
if (!isDragging) {
|
|
1883
2131
|
setLocalValue(value);
|
|
1884
2132
|
}
|
|
1885
2133
|
}, [value, isDragging]);
|
|
1886
2134
|
const debouncedOnChange = useDebouncedCallback2(onChange, 50);
|
|
1887
|
-
const handleChange = (0,
|
|
2135
|
+
const handleChange = (0, import_react9.useCallback)(
|
|
1888
2136
|
(e) => {
|
|
1889
2137
|
const newValue = parseFloat(e.target.value);
|
|
1890
2138
|
setLocalValue(newValue);
|
|
@@ -1892,23 +2140,23 @@ var PanSlider = ({
|
|
|
1892
2140
|
},
|
|
1893
2141
|
[debouncedOnChange]
|
|
1894
2142
|
);
|
|
1895
|
-
const handleMouseDown = (0,
|
|
2143
|
+
const handleMouseDown = (0, import_react9.useCallback)(() => {
|
|
1896
2144
|
setIsDragging(true);
|
|
1897
2145
|
}, []);
|
|
1898
|
-
const handleMouseUp = (0,
|
|
2146
|
+
const handleMouseUp = (0, import_react9.useCallback)(() => {
|
|
1899
2147
|
setIsDragging(false);
|
|
1900
2148
|
onChange(localValue);
|
|
1901
2149
|
}, [localValue, onChange]);
|
|
1902
|
-
const handleDoubleClick = (0,
|
|
2150
|
+
const handleDoubleClick = (0, import_react9.useCallback)(() => {
|
|
1903
2151
|
setLocalValue(0);
|
|
1904
2152
|
onChange(0);
|
|
1905
2153
|
}, [onChange]);
|
|
1906
|
-
return /* @__PURE__ */ (0,
|
|
2154
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1907
2155
|
"div",
|
|
1908
2156
|
{
|
|
1909
2157
|
className: `flex items-center ${className}`,
|
|
1910
2158
|
title: `Pan: ${toPanDisplay(localValue)}`,
|
|
1911
|
-
children: /* @__PURE__ */ (0,
|
|
2159
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1912
2160
|
"input",
|
|
1913
2161
|
{
|
|
1914
2162
|
type: "range",
|
|
@@ -1949,8 +2197,8 @@ var PanSlider = ({
|
|
|
1949
2197
|
};
|
|
1950
2198
|
|
|
1951
2199
|
// src/components/SorceryProgressBar.tsx
|
|
1952
|
-
var
|
|
1953
|
-
var
|
|
2200
|
+
var import_react10 = require("react");
|
|
2201
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1954
2202
|
function calculateTimeBasedTarget(elapsedMs, estimatedDurationMs) {
|
|
1955
2203
|
const t = elapsedMs / estimatedDurationMs;
|
|
1956
2204
|
if (t <= 0) return 0;
|
|
@@ -1995,20 +2243,20 @@ function SorceryProgressBar({
|
|
|
1995
2243
|
onProgressChange,
|
|
1996
2244
|
estimatedDurationMs
|
|
1997
2245
|
}) {
|
|
1998
|
-
const [progress, setProgress] = (0,
|
|
1999
|
-
const timerRef = (0,
|
|
2000
|
-
const isLoadingRef = (0,
|
|
2001
|
-
const hasStartedRef = (0,
|
|
2002
|
-
const startTimeRef = (0,
|
|
2003
|
-
const onProgressChangeRef = (0,
|
|
2004
|
-
const onCompleteRef = (0,
|
|
2246
|
+
const [progress, setProgress] = (0, import_react10.useState)(initialProgress);
|
|
2247
|
+
const timerRef = (0, import_react10.useRef)(null);
|
|
2248
|
+
const isLoadingRef = (0, import_react10.useRef)(false);
|
|
2249
|
+
const hasStartedRef = (0, import_react10.useRef)(false);
|
|
2250
|
+
const startTimeRef = (0, import_react10.useRef)(0);
|
|
2251
|
+
const onProgressChangeRef = (0, import_react10.useRef)(onProgressChange);
|
|
2252
|
+
const onCompleteRef = (0, import_react10.useRef)(onComplete);
|
|
2005
2253
|
onProgressChangeRef.current = onProgressChange;
|
|
2006
2254
|
onCompleteRef.current = onComplete;
|
|
2007
|
-
const initialProgressRef = (0,
|
|
2255
|
+
const initialProgressRef = (0, import_react10.useRef)(initialProgress);
|
|
2008
2256
|
initialProgressRef.current = initialProgress;
|
|
2009
|
-
const estimatedDurationMsRef = (0,
|
|
2257
|
+
const estimatedDurationMsRef = (0, import_react10.useRef)(estimatedDurationMs);
|
|
2010
2258
|
estimatedDurationMsRef.current = estimatedDurationMs;
|
|
2011
|
-
(0,
|
|
2259
|
+
(0, import_react10.useEffect)(() => {
|
|
2012
2260
|
const wasLoading = isLoadingRef.current;
|
|
2013
2261
|
isLoadingRef.current = isLoading;
|
|
2014
2262
|
if (isLoading && !wasLoading) {
|
|
@@ -2070,12 +2318,12 @@ function SorceryProgressBar({
|
|
|
2070
2318
|
const displayProgress = Math.floor(progress);
|
|
2071
2319
|
const isComplete = !isLoading && progress === 100;
|
|
2072
2320
|
const transitionDuration = progress < 50 ? "300ms" : progress < 80 ? "500ms" : "700ms";
|
|
2073
|
-
return /* @__PURE__ */ (0,
|
|
2321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2074
2322
|
"div",
|
|
2075
2323
|
{
|
|
2076
2324
|
className: `relative w-full ${heightClass} bg-sas-panel-alt border border-sas-border rounded-sm overflow-hidden shadow-inner`,
|
|
2077
2325
|
children: [
|
|
2078
|
-
/* @__PURE__ */ (0,
|
|
2326
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2079
2327
|
"div",
|
|
2080
2328
|
{
|
|
2081
2329
|
className: `
|
|
@@ -2093,13 +2341,13 @@ function SorceryProgressBar({
|
|
|
2093
2341
|
}
|
|
2094
2342
|
}
|
|
2095
2343
|
),
|
|
2096
|
-
/* @__PURE__ */ (0,
|
|
2344
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "absolute inset-0 flex items-center justify-center", children: isLoading && progress < 100 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "font-mono text-xs text-sas-accent font-bold drop-shadow-md tracking-wider", children: [
|
|
2097
2345
|
statusText,
|
|
2098
2346
|
" ",
|
|
2099
2347
|
displayProgress,
|
|
2100
2348
|
"%"
|
|
2101
|
-
] }) : isComplete ? /* @__PURE__ */ (0,
|
|
2102
|
-
/* @__PURE__ */ (0,
|
|
2349
|
+
] }) : isComplete ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "font-mono text-xs text-sas-text font-bold drop-shadow-md tracking-wider", children: completeText }) : null }),
|
|
2350
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2103
2351
|
"div",
|
|
2104
2352
|
{
|
|
2105
2353
|
className: "absolute inset-0 pointer-events-none opacity-10",
|
|
@@ -2120,7 +2368,7 @@ function SorceryProgressBar({
|
|
|
2120
2368
|
}
|
|
2121
2369
|
|
|
2122
2370
|
// src/components/TrackRow.tsx
|
|
2123
|
-
var
|
|
2371
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2124
2372
|
function TrackRow({
|
|
2125
2373
|
track,
|
|
2126
2374
|
prompt,
|
|
@@ -2149,6 +2397,7 @@ function TrackRow({
|
|
|
2149
2397
|
onFxToggle,
|
|
2150
2398
|
onFxPresetChange,
|
|
2151
2399
|
onFxDryWetChange,
|
|
2400
|
+
externalFxHost,
|
|
2152
2401
|
onToggleFxDrawer,
|
|
2153
2402
|
onProgressChange,
|
|
2154
2403
|
accentColor = "#A78BFA",
|
|
@@ -2179,7 +2428,7 @@ function TrackRow({
|
|
|
2179
2428
|
levels
|
|
2180
2429
|
}) {
|
|
2181
2430
|
const { muted: isMuted, solo: isSoloed, volume: currentVolume, pan: currentPan } = runtimeState;
|
|
2182
|
-
const [confirmDelete, setConfirmDelete] =
|
|
2431
|
+
const [confirmDelete, setConfirmDelete] = import_react11.default.useState(false);
|
|
2183
2432
|
const needsGeneration = !!(prompt?.trim() && !hasMidi && !isGenerating);
|
|
2184
2433
|
const hasFxActive = Object.values(fxDetailState).some(
|
|
2185
2434
|
(d) => d.enabled
|
|
@@ -2194,8 +2443,8 @@ function TrackRow({
|
|
|
2194
2443
|
};
|
|
2195
2444
|
const borderColorStyle = needsGeneration ? void 0 : accentColor;
|
|
2196
2445
|
const borderClass = needsGeneration ? "border-amber-400 animate-pulse" : "border-sas-border";
|
|
2197
|
-
return /* @__PURE__ */ (0,
|
|
2198
|
-
/* @__PURE__ */ (0,
|
|
2446
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { "data-testid": "sdk-track-row-wrapper", className: "w-full", ...drag?.rowProps ?? {}, children: [
|
|
2447
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2199
2448
|
"div",
|
|
2200
2449
|
{
|
|
2201
2450
|
"data-testid": "sdk-track-row",
|
|
@@ -2205,7 +2454,7 @@ function TrackRow({
|
|
|
2205
2454
|
borderLeftWidth: "3px"
|
|
2206
2455
|
},
|
|
2207
2456
|
children: [
|
|
2208
|
-
drag && /* @__PURE__ */ (0,
|
|
2457
|
+
drag && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2209
2458
|
"div",
|
|
2210
2459
|
{
|
|
2211
2460
|
"data-testid": "sdk-drag-handle",
|
|
@@ -2213,10 +2462,10 @@ function TrackRow({
|
|
|
2213
2462
|
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",
|
|
2214
2463
|
title: "Drag to reorder",
|
|
2215
2464
|
"aria-label": "Drag to reorder track",
|
|
2216
|
-
children: /* @__PURE__ */ (0,
|
|
2465
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react.GripVertical, { className: "w-3.5 h-3.5", strokeWidth: 2 })
|
|
2217
2466
|
}
|
|
2218
2467
|
),
|
|
2219
|
-
isGenerating && /* @__PURE__ */ (0,
|
|
2468
|
+
isGenerating && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "absolute left-0 top-0 bottom-0 right-44 z-20", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2220
2469
|
SorceryProgressBar,
|
|
2221
2470
|
{
|
|
2222
2471
|
isLoading: true,
|
|
@@ -2227,14 +2476,14 @@ function TrackRow({
|
|
|
2227
2476
|
estimatedDurationMs: estimatedGenerationMs
|
|
2228
2477
|
}
|
|
2229
2478
|
) }),
|
|
2230
|
-
/* @__PURE__ */ (0,
|
|
2479
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2231
2480
|
"div",
|
|
2232
2481
|
{
|
|
2233
2482
|
"data-testid": "sdk-track-content",
|
|
2234
2483
|
className: `flex flex-col flex-1 min-w-0 relative z-10 transition-opacity ${soloedOut ? "opacity-40" : ""}`,
|
|
2235
2484
|
title: soloedOut ? "Silenced \u2014 another track is soloed" : void 0,
|
|
2236
2485
|
children: [
|
|
2237
|
-
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ (0,
|
|
2486
|
+
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2238
2487
|
"input",
|
|
2239
2488
|
{
|
|
2240
2489
|
type: "text",
|
|
@@ -2247,10 +2496,10 @@ function TrackRow({
|
|
|
2247
2496
|
className: "sas-input w-full px-2 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed"
|
|
2248
2497
|
}
|
|
2249
2498
|
) : null,
|
|
2250
|
-
/* @__PURE__ */ (0,
|
|
2251
|
-
track.name && /* @__PURE__ */ (0,
|
|
2252
|
-
/* @__PURE__ */ (0,
|
|
2253
|
-
/* @__PURE__ */ (0,
|
|
2499
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 mt-1", children: [
|
|
2500
|
+
track.name && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-[10px] text-sas-muted/60 truncate pl-2 flex-shrink-0 max-w-[80px]", title: track.name, children: track.name }),
|
|
2501
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "vol:" }),
|
|
2502
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2254
2503
|
VolumeSlider,
|
|
2255
2504
|
{
|
|
2256
2505
|
value: currentVolume,
|
|
@@ -2259,8 +2508,8 @@ function TrackRow({
|
|
|
2259
2508
|
className: "flex-1 min-w-[40px]"
|
|
2260
2509
|
}
|
|
2261
2510
|
),
|
|
2262
|
-
/* @__PURE__ */ (0,
|
|
2263
|
-
/* @__PURE__ */ (0,
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "pan:" }),
|
|
2512
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2264
2513
|
PanSlider,
|
|
2265
2514
|
{
|
|
2266
2515
|
value: currentPan,
|
|
@@ -2273,27 +2522,27 @@ function TrackRow({
|
|
|
2273
2522
|
]
|
|
2274
2523
|
}
|
|
2275
2524
|
),
|
|
2276
|
-
error && /* @__PURE__ */ (0,
|
|
2525
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2277
2526
|
"div",
|
|
2278
2527
|
{
|
|
2279
2528
|
"data-testid": "sdk-error-indicator",
|
|
2280
2529
|
className: "flex-shrink-0 relative z-10 self-stretch flex items-center px-1 group cursor-help",
|
|
2281
2530
|
title: error,
|
|
2282
|
-
children: /* @__PURE__ */ (0,
|
|
2283
|
-
/* @__PURE__ */ (0,
|
|
2531
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative", children: [
|
|
2532
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2284
2533
|
import_lucide_react.AlertCircle,
|
|
2285
2534
|
{
|
|
2286
2535
|
className: "w-5 h-5 text-red-500 animate-pulse",
|
|
2287
2536
|
strokeWidth: 2.5
|
|
2288
2537
|
}
|
|
2289
2538
|
),
|
|
2290
|
-
/* @__PURE__ */ (0,
|
|
2539
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-red-900/95 text-red-100 text-xs rounded shadow-lg whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50 max-w-[200px] truncate", children: error })
|
|
2291
2540
|
] })
|
|
2292
2541
|
}
|
|
2293
2542
|
),
|
|
2294
|
-
/* @__PURE__ */ (0,
|
|
2295
|
-
/* @__PURE__ */ (0,
|
|
2296
|
-
onGenerate && /* @__PURE__ */ (0,
|
|
2543
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-0.5 flex-shrink-0 relative z-30 justify-center", children: [
|
|
2544
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex gap-1 items-center", children: [
|
|
2545
|
+
onGenerate && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2297
2546
|
"button",
|
|
2298
2547
|
{
|
|
2299
2548
|
"data-testid": "sdk-generate-button",
|
|
@@ -2304,7 +2553,7 @@ function TrackRow({
|
|
|
2304
2553
|
children: "Create"
|
|
2305
2554
|
}
|
|
2306
2555
|
),
|
|
2307
|
-
onCopy && /* @__PURE__ */ (0,
|
|
2556
|
+
onCopy && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2308
2557
|
"button",
|
|
2309
2558
|
{
|
|
2310
2559
|
"data-testid": "sdk-copy-button",
|
|
@@ -2315,7 +2564,7 @@ function TrackRow({
|
|
|
2315
2564
|
children: "Copy"
|
|
2316
2565
|
}
|
|
2317
2566
|
),
|
|
2318
|
-
/* @__PURE__ */ (0,
|
|
2567
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2319
2568
|
"button",
|
|
2320
2569
|
{
|
|
2321
2570
|
"data-testid": "sdk-mute-button",
|
|
@@ -2325,7 +2574,7 @@ function TrackRow({
|
|
|
2325
2574
|
children: "M"
|
|
2326
2575
|
}
|
|
2327
2576
|
),
|
|
2328
|
-
onDelete && /* @__PURE__ */ (0,
|
|
2577
|
+
onDelete && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2329
2578
|
"button",
|
|
2330
2579
|
{
|
|
2331
2580
|
"data-testid": "sdk-delete-button",
|
|
@@ -2336,8 +2585,8 @@ function TrackRow({
|
|
|
2336
2585
|
}
|
|
2337
2586
|
)
|
|
2338
2587
|
] }),
|
|
2339
|
-
/* @__PURE__ */ (0,
|
|
2340
|
-
onShuffle && /* @__PURE__ */ (0,
|
|
2588
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex gap-1 items-center", children: [
|
|
2589
|
+
onShuffle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2341
2590
|
"button",
|
|
2342
2591
|
{
|
|
2343
2592
|
"data-testid": "sdk-shuffle-button",
|
|
@@ -2348,7 +2597,7 @@ function TrackRow({
|
|
|
2348
2597
|
children: "Shuffle"
|
|
2349
2598
|
}
|
|
2350
2599
|
),
|
|
2351
|
-
onToggleFxDrawer && /* @__PURE__ */ (0,
|
|
2600
|
+
onToggleFxDrawer && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2352
2601
|
"button",
|
|
2353
2602
|
{
|
|
2354
2603
|
"data-testid": "sdk-fx-button",
|
|
@@ -2359,7 +2608,7 @@ function TrackRow({
|
|
|
2359
2608
|
children: "FX"
|
|
2360
2609
|
}
|
|
2361
2610
|
),
|
|
2362
|
-
/* @__PURE__ */ (0,
|
|
2611
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2363
2612
|
"button",
|
|
2364
2613
|
{
|
|
2365
2614
|
"data-testid": "sdk-solo-button",
|
|
@@ -2370,7 +2619,7 @@ function TrackRow({
|
|
|
2370
2619
|
children: "S"
|
|
2371
2620
|
}
|
|
2372
2621
|
),
|
|
2373
|
-
onToggleDrawer && /* @__PURE__ */ (0,
|
|
2622
|
+
onToggleDrawer && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2374
2623
|
"button",
|
|
2375
2624
|
{
|
|
2376
2625
|
"data-testid": "sdk-plugin-button",
|
|
@@ -2378,7 +2627,7 @@ function TrackRow({
|
|
|
2378
2627
|
disabled: isGenerating,
|
|
2379
2628
|
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"}`,
|
|
2380
2629
|
title: `Sound \u2014 presets & history${instrumentMissing ? " (instrument missing)" : ""}`,
|
|
2381
|
-
children: /* @__PURE__ */ (0,
|
|
2630
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react.ChevronDown, { className: "w-3 h-3", strokeWidth: 2.5 })
|
|
2382
2631
|
}
|
|
2383
2632
|
)
|
|
2384
2633
|
] })
|
|
@@ -2386,13 +2635,13 @@ function TrackRow({
|
|
|
2386
2635
|
]
|
|
2387
2636
|
}
|
|
2388
2637
|
),
|
|
2389
|
-
levels && /* @__PURE__ */ (0,
|
|
2390
|
-
drawerOpen && /* @__PURE__ */ (0,
|
|
2638
|
+
levels && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TrackMeterStrip, { levels, trackId: track.id, roundBottom: !drawerOpen }),
|
|
2639
|
+
drawerOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2391
2640
|
"div",
|
|
2392
2641
|
{
|
|
2393
2642
|
"data-testid": "sdk-track-drawer",
|
|
2394
2643
|
className: "border border-t-0 border-sas-border bg-sas-bg rounded-b-sm px-3 py-2 max-h-[260px] overflow-y-auto",
|
|
2395
|
-
children: /* @__PURE__ */ (0,
|
|
2644
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2396
2645
|
TrackDrawer,
|
|
2397
2646
|
{
|
|
2398
2647
|
activeTab: drawerTab,
|
|
@@ -2402,6 +2651,7 @@ function TrackRow({
|
|
|
2402
2651
|
onFxToggle,
|
|
2403
2652
|
onFxPresetChange,
|
|
2404
2653
|
onFxDryWetChange,
|
|
2654
|
+
externalFxHost,
|
|
2405
2655
|
fxDisabled: isGenerating,
|
|
2406
2656
|
instruments: availableInstruments,
|
|
2407
2657
|
currentPluginId: currentInstrumentPluginId ?? null,
|
|
@@ -2428,13 +2678,13 @@ function TrackRow({
|
|
|
2428
2678
|
)
|
|
2429
2679
|
}
|
|
2430
2680
|
),
|
|
2431
|
-
/* @__PURE__ */ (0,
|
|
2681
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2432
2682
|
ConfirmDialog,
|
|
2433
2683
|
{
|
|
2434
2684
|
open: confirmDelete,
|
|
2435
2685
|
title: "Delete track?",
|
|
2436
|
-
message: /* @__PURE__ */ (0,
|
|
2437
|
-
/* @__PURE__ */ (0,
|
|
2686
|
+
message: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
2687
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-sas-text", children: track.name?.trim() || "This track" }),
|
|
2438
2688
|
" will be permanently removed from this scene. This cannot be undone."
|
|
2439
2689
|
] }),
|
|
2440
2690
|
confirmLabel: "Delete",
|
|
@@ -2450,13 +2700,13 @@ function TrackRow({
|
|
|
2450
2700
|
}
|
|
2451
2701
|
|
|
2452
2702
|
// src/components/CrossfadeTrackRow.tsx
|
|
2453
|
-
var
|
|
2454
|
-
var
|
|
2703
|
+
var import_react12 = __toESM(require("react"));
|
|
2704
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2455
2705
|
function LayerCaption({ tag, layer }) {
|
|
2456
|
-
return /* @__PURE__ */ (0,
|
|
2457
|
-
/* @__PURE__ */ (0,
|
|
2458
|
-
/* @__PURE__ */ (0,
|
|
2459
|
-
layer.soundLabel && /* @__PURE__ */ (0,
|
|
2706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
2707
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
|
|
2708
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
|
|
2709
|
+
layer.soundLabel && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
|
|
2460
2710
|
"\xB7 ",
|
|
2461
2711
|
layer.soundLabel
|
|
2462
2712
|
] })
|
|
@@ -2475,8 +2725,8 @@ function CrossfadeTrackRow({
|
|
|
2475
2725
|
levels,
|
|
2476
2726
|
accentColor = "#9333EA"
|
|
2477
2727
|
}) {
|
|
2478
|
-
const [confirmDelete, setConfirmDelete] =
|
|
2479
|
-
const renderLayer = (layer, slot, tag) => /* @__PURE__ */ (0,
|
|
2728
|
+
const [confirmDelete, setConfirmDelete] = import_react12.default.useState(false);
|
|
2729
|
+
const renderLayer = (layer, slot, tag) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2480
2730
|
TrackRow,
|
|
2481
2731
|
{
|
|
2482
2732
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -2486,23 +2736,23 @@ function CrossfadeTrackRow({
|
|
|
2486
2736
|
drawerTab: "fx",
|
|
2487
2737
|
levels,
|
|
2488
2738
|
accentColor,
|
|
2489
|
-
contentSlot: /* @__PURE__ */ (0,
|
|
2739
|
+
contentSlot: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LayerCaption, { tag, layer }),
|
|
2490
2740
|
onMuteToggle,
|
|
2491
2741
|
onSoloToggle,
|
|
2492
2742
|
onVolumeChange: (v) => onVolumeChange(slot, v),
|
|
2493
2743
|
onPanChange: (p) => onPanChange(slot, p)
|
|
2494
2744
|
}
|
|
2495
2745
|
);
|
|
2496
|
-
return /* @__PURE__ */ (0,
|
|
2746
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2497
2747
|
"div",
|
|
2498
2748
|
{
|
|
2499
2749
|
"data-testid": "crossfade-track-row",
|
|
2500
2750
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
2501
2751
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
2502
2752
|
children: [
|
|
2503
|
-
/* @__PURE__ */ (0,
|
|
2504
|
-
/* @__PURE__ */ (0,
|
|
2505
|
-
/* @__PURE__ */ (0,
|
|
2753
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
|
|
2754
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-wide", style: { color: accentColor }, children: "\u21C4 Crossfade" }),
|
|
2755
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2506
2756
|
"button",
|
|
2507
2757
|
{
|
|
2508
2758
|
"data-testid": "crossfade-delete-button",
|
|
@@ -2515,8 +2765,8 @@ function CrossfadeTrackRow({
|
|
|
2515
2765
|
)
|
|
2516
2766
|
] }),
|
|
2517
2767
|
renderLayer(origin, "origin", "Origin"),
|
|
2518
|
-
/* @__PURE__ */ (0,
|
|
2519
|
-
/* @__PURE__ */ (0,
|
|
2768
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "crossfade-slider-row", children: [
|
|
2769
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2520
2770
|
"span",
|
|
2521
2771
|
{
|
|
2522
2772
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -2524,7 +2774,7 @@ function CrossfadeTrackRow({
|
|
|
2524
2774
|
children: origin.sourceName ?? origin.name
|
|
2525
2775
|
}
|
|
2526
2776
|
),
|
|
2527
|
-
/* @__PURE__ */ (0,
|
|
2777
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2528
2778
|
"input",
|
|
2529
2779
|
{
|
|
2530
2780
|
type: "range",
|
|
@@ -2540,7 +2790,7 @@ function CrossfadeTrackRow({
|
|
|
2540
2790
|
"aria-label": "Crossfade position"
|
|
2541
2791
|
}
|
|
2542
2792
|
),
|
|
2543
|
-
/* @__PURE__ */ (0,
|
|
2793
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2544
2794
|
"span",
|
|
2545
2795
|
{
|
|
2546
2796
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -2550,12 +2800,12 @@ function CrossfadeTrackRow({
|
|
|
2550
2800
|
)
|
|
2551
2801
|
] }),
|
|
2552
2802
|
renderLayer(target, "target", "Target"),
|
|
2553
|
-
/* @__PURE__ */ (0,
|
|
2803
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2554
2804
|
ConfirmDialog,
|
|
2555
2805
|
{
|
|
2556
2806
|
open: confirmDelete,
|
|
2557
2807
|
title: "Delete crossfade?",
|
|
2558
|
-
message: /* @__PURE__ */ (0,
|
|
2808
|
+
message: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_jsx_runtime13.Fragment, { children: "This crossfade pair (both layers) will be permanently removed from this scene. This cannot be undone." }),
|
|
2559
2809
|
confirmLabel: "Delete",
|
|
2560
2810
|
onConfirm: () => {
|
|
2561
2811
|
setConfirmDelete(false);
|
|
@@ -2798,22 +3048,22 @@ function defaultFadeGesture(role) {
|
|
|
2798
3048
|
}
|
|
2799
3049
|
|
|
2800
3050
|
// src/components/FadeTrackRow.tsx
|
|
2801
|
-
var
|
|
2802
|
-
var
|
|
3051
|
+
var import_react13 = __toESM(require("react"));
|
|
3052
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2803
3053
|
function FadeCaption({
|
|
2804
3054
|
layer,
|
|
2805
3055
|
direction,
|
|
2806
3056
|
gesture
|
|
2807
3057
|
}) {
|
|
2808
3058
|
const tag = direction === "in" ? "Fade in" : "Fade out";
|
|
2809
|
-
return /* @__PURE__ */ (0,
|
|
2810
|
-
/* @__PURE__ */ (0,
|
|
2811
|
-
/* @__PURE__ */ (0,
|
|
2812
|
-
layer.soundLabel && /* @__PURE__ */ (0,
|
|
3059
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
3060
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
|
|
3061
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
|
|
3062
|
+
layer.soundLabel && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
|
|
2813
3063
|
"\xB7 ",
|
|
2814
3064
|
layer.soundLabel
|
|
2815
3065
|
] }),
|
|
2816
|
-
/* @__PURE__ */ (0,
|
|
3066
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", title: `Fade gesture: ${gesture}`, children: [
|
|
2817
3067
|
"\xB7 ",
|
|
2818
3068
|
gesture
|
|
2819
3069
|
] })
|
|
@@ -2834,20 +3084,20 @@ function FadeTrackRow({
|
|
|
2834
3084
|
levels,
|
|
2835
3085
|
accentColor = "#9333EA"
|
|
2836
3086
|
}) {
|
|
2837
|
-
const [confirmDelete, setConfirmDelete] =
|
|
3087
|
+
const [confirmDelete, setConfirmDelete] = import_react13.default.useState(false);
|
|
2838
3088
|
const leftLabel = direction === "in" ? "(silent)" : layer.sourceName ?? layer.name;
|
|
2839
3089
|
const rightLabel = direction === "in" ? layer.sourceName ?? layer.name : "(silent)";
|
|
2840
3090
|
const verb = effect && effect !== "fade" ? effect.charAt(0).toUpperCase() + effect.slice(1) : "Fade";
|
|
2841
3091
|
const badge = direction === "in" ? `\u2197 ${verb} in` : `\u2198 ${verb} out`;
|
|
2842
|
-
return /* @__PURE__ */ (0,
|
|
3092
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2843
3093
|
"div",
|
|
2844
3094
|
{
|
|
2845
3095
|
"data-testid": "fade-track-row",
|
|
2846
3096
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
2847
3097
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
2848
3098
|
children: [
|
|
2849
|
-
/* @__PURE__ */ (0,
|
|
2850
|
-
/* @__PURE__ */ (0,
|
|
3099
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
|
|
3100
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2851
3101
|
"span",
|
|
2852
3102
|
{
|
|
2853
3103
|
"data-testid": "fade-direction-badge",
|
|
@@ -2856,7 +3106,7 @@ function FadeTrackRow({
|
|
|
2856
3106
|
children: badge
|
|
2857
3107
|
}
|
|
2858
3108
|
),
|
|
2859
|
-
/* @__PURE__ */ (0,
|
|
3109
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2860
3110
|
"button",
|
|
2861
3111
|
{
|
|
2862
3112
|
"data-testid": "fade-delete-button",
|
|
@@ -2868,7 +3118,7 @@ function FadeTrackRow({
|
|
|
2868
3118
|
}
|
|
2869
3119
|
)
|
|
2870
3120
|
] }),
|
|
2871
|
-
/* @__PURE__ */ (0,
|
|
3121
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2872
3122
|
TrackRow,
|
|
2873
3123
|
{
|
|
2874
3124
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -2878,15 +3128,15 @@ function FadeTrackRow({
|
|
|
2878
3128
|
drawerTab: "fx",
|
|
2879
3129
|
levels,
|
|
2880
3130
|
accentColor,
|
|
2881
|
-
contentSlot: /* @__PURE__ */ (0,
|
|
3131
|
+
contentSlot: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(FadeCaption, { layer, direction, gesture }),
|
|
2882
3132
|
onMuteToggle,
|
|
2883
3133
|
onSoloToggle,
|
|
2884
3134
|
onVolumeChange,
|
|
2885
3135
|
onPanChange
|
|
2886
3136
|
}
|
|
2887
3137
|
),
|
|
2888
|
-
/* @__PURE__ */ (0,
|
|
2889
|
-
/* @__PURE__ */ (0,
|
|
3138
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "fade-slider-row", children: [
|
|
3139
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2890
3140
|
"span",
|
|
2891
3141
|
{
|
|
2892
3142
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -2894,7 +3144,7 @@ function FadeTrackRow({
|
|
|
2894
3144
|
children: leftLabel
|
|
2895
3145
|
}
|
|
2896
3146
|
),
|
|
2897
|
-
/* @__PURE__ */ (0,
|
|
3147
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2898
3148
|
"input",
|
|
2899
3149
|
{
|
|
2900
3150
|
type: "range",
|
|
@@ -2910,7 +3160,7 @@ function FadeTrackRow({
|
|
|
2910
3160
|
"aria-label": "Fade position"
|
|
2911
3161
|
}
|
|
2912
3162
|
),
|
|
2913
|
-
/* @__PURE__ */ (0,
|
|
3163
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2914
3164
|
"span",
|
|
2915
3165
|
{
|
|
2916
3166
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -2919,12 +3169,12 @@ function FadeTrackRow({
|
|
|
2919
3169
|
}
|
|
2920
3170
|
)
|
|
2921
3171
|
] }),
|
|
2922
|
-
/* @__PURE__ */ (0,
|
|
3172
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2923
3173
|
ConfirmDialog,
|
|
2924
3174
|
{
|
|
2925
3175
|
open: confirmDelete,
|
|
2926
3176
|
title: "Delete fade?",
|
|
2927
|
-
message: /* @__PURE__ */ (0,
|
|
3177
|
+
message: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: "This fade track will be permanently removed from this scene. This cannot be undone." }),
|
|
2928
3178
|
confirmLabel: "Delete",
|
|
2929
3179
|
onConfirm: () => {
|
|
2930
3180
|
setConfirmDelete(false);
|
|
@@ -2940,8 +3190,8 @@ function FadeTrackRow({
|
|
|
2940
3190
|
}
|
|
2941
3191
|
|
|
2942
3192
|
// src/components/FadeModal.tsx
|
|
2943
|
-
var
|
|
2944
|
-
var
|
|
3193
|
+
var import_react14 = require("react");
|
|
3194
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2945
3195
|
function shortId(dbId) {
|
|
2946
3196
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
2947
3197
|
}
|
|
@@ -2984,7 +3234,7 @@ function OrphanRow({
|
|
|
2984
3234
|
}) {
|
|
2985
3235
|
const primary = track.prompt?.trim() || track.name;
|
|
2986
3236
|
const meta = [track.role, shortId(track.dbId), gesture].filter(Boolean).join(" \xB7 ");
|
|
2987
|
-
return /* @__PURE__ */ (0,
|
|
3237
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2988
3238
|
"button",
|
|
2989
3239
|
{
|
|
2990
3240
|
type: "button",
|
|
@@ -2996,8 +3246,8 @@ function OrphanRow({
|
|
|
2996
3246
|
disabled,
|
|
2997
3247
|
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"}`,
|
|
2998
3248
|
children: [
|
|
2999
|
-
/* @__PURE__ */ (0,
|
|
3000
|
-
meta && /* @__PURE__ */ (0,
|
|
3249
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
|
|
3250
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
|
|
3001
3251
|
]
|
|
3002
3252
|
}
|
|
3003
3253
|
);
|
|
@@ -3014,14 +3264,14 @@ function FadeModal({
|
|
|
3014
3264
|
onCreate,
|
|
3015
3265
|
testIdPrefix = "fade-modal"
|
|
3016
3266
|
}) {
|
|
3017
|
-
const [load, setLoad] = (0,
|
|
3018
|
-
const [selectedDbId, setSelectedDbId] = (0,
|
|
3019
|
-
const [isCreating, setIsCreating] = (0,
|
|
3020
|
-
const [error, setError] = (0,
|
|
3021
|
-
const [fromName, setFromName] = (0,
|
|
3022
|
-
const [toName, setToName] = (0,
|
|
3023
|
-
const cancelRef = (0,
|
|
3024
|
-
const refresh = (0,
|
|
3267
|
+
const [load, setLoad] = (0, import_react14.useState)({ status: "loading" });
|
|
3268
|
+
const [selectedDbId, setSelectedDbId] = (0, import_react14.useState)("");
|
|
3269
|
+
const [isCreating, setIsCreating] = (0, import_react14.useState)(false);
|
|
3270
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
3271
|
+
const [fromName, setFromName] = (0, import_react14.useState)(null);
|
|
3272
|
+
const [toName, setToName] = (0, import_react14.useState)(null);
|
|
3273
|
+
const cancelRef = (0, import_react14.useRef)(null);
|
|
3274
|
+
const refresh = (0, import_react14.useCallback)(async () => {
|
|
3025
3275
|
if (!host.listSceneFamilyTracks) {
|
|
3026
3276
|
setLoad({ status: "error", message: "This host does not support fades." });
|
|
3027
3277
|
return;
|
|
@@ -3041,7 +3291,7 @@ function FadeModal({
|
|
|
3041
3291
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
3042
3292
|
}
|
|
3043
3293
|
}, [host, fromSceneId, toSceneId]);
|
|
3044
|
-
(0,
|
|
3294
|
+
(0, import_react14.useEffect)(() => {
|
|
3045
3295
|
if (open) {
|
|
3046
3296
|
setError(null);
|
|
3047
3297
|
setIsCreating(false);
|
|
@@ -3049,29 +3299,29 @@ function FadeModal({
|
|
|
3049
3299
|
void refresh();
|
|
3050
3300
|
}
|
|
3051
3301
|
}, [open, refresh]);
|
|
3052
|
-
const excludeSet = (0,
|
|
3053
|
-
const { fadeOut, fadeIn } = (0,
|
|
3302
|
+
const excludeSet = (0, import_react14.useMemo)(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
3303
|
+
const { fadeOut, fadeIn } = (0, import_react14.useMemo)(
|
|
3054
3304
|
() => load.status === "ready" ? computeOrphans(load.from, load.to, excludeSet) : { fadeOut: [], fadeIn: [] },
|
|
3055
3305
|
[load, excludeSet]
|
|
3056
3306
|
);
|
|
3057
|
-
const allOrphans = (0,
|
|
3307
|
+
const allOrphans = (0, import_react14.useMemo)(
|
|
3058
3308
|
() => [
|
|
3059
3309
|
...fadeOut.map((t) => ({ track: t, direction: "out" })),
|
|
3060
3310
|
...fadeIn.map((t) => ({ track: t, direction: "in" }))
|
|
3061
3311
|
],
|
|
3062
3312
|
[fadeOut, fadeIn]
|
|
3063
3313
|
);
|
|
3064
|
-
(0,
|
|
3314
|
+
(0, import_react14.useEffect)(() => {
|
|
3065
3315
|
if (!allOrphans.some((o) => o.track.dbId === selectedDbId)) {
|
|
3066
3316
|
setSelectedDbId(allOrphans[0]?.track.dbId ?? "");
|
|
3067
3317
|
}
|
|
3068
3318
|
}, [allOrphans, selectedDbId]);
|
|
3069
3319
|
const selected = allOrphans.find((o) => o.track.dbId === selectedDbId) ?? null;
|
|
3070
3320
|
const canCreate = !isCreating && !!selected;
|
|
3071
|
-
const handleClose = (0,
|
|
3321
|
+
const handleClose = (0, import_react14.useCallback)(() => {
|
|
3072
3322
|
if (!isCreating) onClose();
|
|
3073
3323
|
}, [isCreating, onClose]);
|
|
3074
|
-
const handleCreate = (0,
|
|
3324
|
+
const handleCreate = (0, import_react14.useCallback)(async () => {
|
|
3075
3325
|
if (!selected) return;
|
|
3076
3326
|
setIsCreating(true);
|
|
3077
3327
|
setError(null);
|
|
@@ -3092,16 +3342,16 @@ function FadeModal({
|
|
|
3092
3342
|
if (!open) return null;
|
|
3093
3343
|
const renderSection = (heading, list, section) => {
|
|
3094
3344
|
if (list.length === 0) return null;
|
|
3095
|
-
return /* @__PURE__ */ (0,
|
|
3096
|
-
/* @__PURE__ */ (0,
|
|
3097
|
-
/* @__PURE__ */ (0,
|
|
3345
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "block", children: [
|
|
3346
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: heading }),
|
|
3347
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3098
3348
|
"div",
|
|
3099
3349
|
{
|
|
3100
3350
|
role: "radiogroup",
|
|
3101
3351
|
"aria-label": heading,
|
|
3102
3352
|
"data-testid": `${testIdPrefix}-${section === "out" ? "fade-out" : "fade-in"}-list`,
|
|
3103
3353
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3104
|
-
children: list.map((t) => /* @__PURE__ */ (0,
|
|
3354
|
+
children: list.map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3105
3355
|
OrphanRow,
|
|
3106
3356
|
{
|
|
3107
3357
|
track: t,
|
|
@@ -3117,32 +3367,32 @@ function FadeModal({
|
|
|
3117
3367
|
)
|
|
3118
3368
|
] });
|
|
3119
3369
|
};
|
|
3120
|
-
return /* @__PURE__ */ (0,
|
|
3370
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
3121
3371
|
"div",
|
|
3122
3372
|
{
|
|
3123
3373
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
3124
3374
|
onClick: (e) => e.stopPropagation(),
|
|
3125
3375
|
"data-testid": `${testIdPrefix}-box`,
|
|
3126
3376
|
children: [
|
|
3127
|
-
/* @__PURE__ */ (0,
|
|
3128
|
-
/* @__PURE__ */ (0,
|
|
3377
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { className: "text-sm font-bold text-sas-text", children: "Add fade" }),
|
|
3378
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
|
|
3129
3379
|
"Tracks with no counterpart between",
|
|
3130
3380
|
" ",
|
|
3131
|
-
/* @__PURE__ */ (0,
|
|
3381
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
3132
3382
|
" and",
|
|
3133
3383
|
" ",
|
|
3134
|
-
/* @__PURE__ */ (0,
|
|
3384
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
3135
3385
|
" can gracefully fade out (leaving) or fade in (entering) across this transition."
|
|
3136
3386
|
] }),
|
|
3137
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
3138
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
3139
|
-
load.status === "ready" && (allOrphans.length === 0 ? /* @__PURE__ */ (0,
|
|
3387
|
+
load.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
|
|
3388
|
+
load.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
|
|
3389
|
+
load.status === "ready" && (allOrphans.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "text-xs text-sas-muted py-4 text-center", "data-testid": `${testIdPrefix}-empty`, children: "Every track has a counterpart in the other scene \u2014 nothing to fade. Use \u201C+ Crossfade\u201D to bridge matching tracks." }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
3140
3390
|
renderSection(`Fade out${fromLabel ? ` (from ${fromLabel})` : ""}`, fadeOut, "out"),
|
|
3141
3391
|
renderSection(`Fade in${toLabel ? ` (to ${toLabel})` : ""}`, fadeIn, "in")
|
|
3142
3392
|
] })),
|
|
3143
|
-
error && /* @__PURE__ */ (0,
|
|
3144
|
-
/* @__PURE__ */ (0,
|
|
3145
|
-
/* @__PURE__ */ (0,
|
|
3393
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
|
|
3394
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
3395
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3146
3396
|
"button",
|
|
3147
3397
|
{
|
|
3148
3398
|
ref: cancelRef,
|
|
@@ -3153,7 +3403,7 @@ function FadeModal({
|
|
|
3153
3403
|
children: "Cancel"
|
|
3154
3404
|
}
|
|
3155
3405
|
),
|
|
3156
|
-
/* @__PURE__ */ (0,
|
|
3406
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3157
3407
|
"button",
|
|
3158
3408
|
{
|
|
3159
3409
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -3170,8 +3420,8 @@ function FadeModal({
|
|
|
3170
3420
|
}
|
|
3171
3421
|
|
|
3172
3422
|
// src/components/ImportTrackModal.tsx
|
|
3173
|
-
var
|
|
3174
|
-
var
|
|
3423
|
+
var import_react15 = require("react");
|
|
3424
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
3175
3425
|
function ImportTrackModal({
|
|
3176
3426
|
host,
|
|
3177
3427
|
open,
|
|
@@ -3183,10 +3433,10 @@ function ImportTrackModal({
|
|
|
3183
3433
|
onPick,
|
|
3184
3434
|
onPortTrack
|
|
3185
3435
|
}) {
|
|
3186
|
-
const [load, setLoad] = (0,
|
|
3187
|
-
const [selectedSceneId, setSelectedSceneId] = (0,
|
|
3188
|
-
const [importingTrackId, setImportingTrackId] = (0,
|
|
3189
|
-
const refresh = (0,
|
|
3436
|
+
const [load, setLoad] = (0, import_react15.useState)({ status: "loading" });
|
|
3437
|
+
const [selectedSceneId, setSelectedSceneId] = (0, import_react15.useState)(null);
|
|
3438
|
+
const [importingTrackId, setImportingTrackId] = (0, import_react15.useState)(null);
|
|
3439
|
+
const refresh = (0, import_react15.useCallback)(async () => {
|
|
3190
3440
|
if (!host.listImportableTracks) {
|
|
3191
3441
|
setLoad({ status: "error", message: "This host does not support importing tracks." });
|
|
3192
3442
|
return;
|
|
@@ -3202,14 +3452,14 @@ function ImportTrackModal({
|
|
|
3202
3452
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load scenes." });
|
|
3203
3453
|
}
|
|
3204
3454
|
}, [host, mode, onPortTrack]);
|
|
3205
|
-
(0,
|
|
3455
|
+
(0, import_react15.useEffect)(() => {
|
|
3206
3456
|
if (open) {
|
|
3207
3457
|
setSelectedSceneId(null);
|
|
3208
3458
|
setImportingTrackId(null);
|
|
3209
3459
|
void refresh();
|
|
3210
3460
|
}
|
|
3211
3461
|
}, [open, refresh]);
|
|
3212
|
-
const handleImport = (0,
|
|
3462
|
+
const handleImport = (0, import_react15.useCallback)(
|
|
3213
3463
|
async (track, sourceSceneId, sceneName, isSameScene) => {
|
|
3214
3464
|
if (isSameScene && onPortTrack) {
|
|
3215
3465
|
if (!track.importable) return;
|
|
@@ -3250,16 +3500,16 @@ function ImportTrackModal({
|
|
|
3250
3500
|
if (!open) return null;
|
|
3251
3501
|
const scenes = load.status === "ready" ? load.scenes : [];
|
|
3252
3502
|
const selectedScene = scenes.find((s) => s.sceneId === selectedSceneId) ?? null;
|
|
3253
|
-
return /* @__PURE__ */ (0,
|
|
3503
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Modal, { open, onClose, testIdPrefix, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3254
3504
|
"div",
|
|
3255
3505
|
{
|
|
3256
3506
|
className: "w-[420px] max-h-[70vh] overflow-hidden flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
3257
3507
|
onClick: (e) => e.stopPropagation(),
|
|
3258
3508
|
"data-testid": `${testIdPrefix}-modal`,
|
|
3259
3509
|
children: [
|
|
3260
|
-
/* @__PURE__ */ (0,
|
|
3261
|
-
/* @__PURE__ */ (0,
|
|
3262
|
-
selectedScene && /* @__PURE__ */ (0,
|
|
3510
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center justify-between px-3 py-2 border-b border-sas-border", children: [
|
|
3511
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3512
|
+
selectedScene && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3263
3513
|
"button",
|
|
3264
3514
|
{
|
|
3265
3515
|
className: "text-sas-muted hover:text-sas-accent text-xs",
|
|
@@ -3268,9 +3518,9 @@ function ImportTrackModal({
|
|
|
3268
3518
|
children: "\u2190"
|
|
3269
3519
|
}
|
|
3270
3520
|
),
|
|
3271
|
-
/* @__PURE__ */ (0,
|
|
3521
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sm font-medium text-sas-text", children: selectedScene ? selectedScene.sceneName : title })
|
|
3272
3522
|
] }),
|
|
3273
|
-
/* @__PURE__ */ (0,
|
|
3523
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3274
3524
|
"button",
|
|
3275
3525
|
{
|
|
3276
3526
|
className: "text-sas-muted hover:text-sas-accent text-sm",
|
|
@@ -3280,30 +3530,30 @@ function ImportTrackModal({
|
|
|
3280
3530
|
}
|
|
3281
3531
|
)
|
|
3282
3532
|
] }),
|
|
3283
|
-
/* @__PURE__ */ (0,
|
|
3284
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
3285
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
3286
|
-
load.status === "ready" && scenes.length === 0 && /* @__PURE__ */ (0,
|
|
3287
|
-
load.status === "ready" && scenes.length > 0 && !selectedScene && /* @__PURE__ */ (0,
|
|
3533
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "overflow-y-auto p-2 flex-1", children: [
|
|
3534
|
+
load.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "py-8 text-center text-xs text-sas-muted", "data-testid": `${testIdPrefix}-loading`, children: "Loading scenes\u2026" }),
|
|
3535
|
+
load.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "py-8 text-center text-xs text-red-400", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
3536
|
+
load.status === "ready" && scenes.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "py-8 text-center text-xs text-sas-muted", "data-testid": `${testIdPrefix}-empty`, children: mode === "sound" ? "No other scenes have a sound to import." : "No other scenes have a compatible track to import." }),
|
|
3537
|
+
load.status === "ready" && scenes.length > 0 && !selectedScene && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-scene-list`, children: scenes.map((scene) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3288
3538
|
"button",
|
|
3289
3539
|
{
|
|
3290
3540
|
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",
|
|
3291
3541
|
onClick: () => setSelectedSceneId(scene.sceneId),
|
|
3292
3542
|
"data-testid": `${testIdPrefix}-scene`,
|
|
3293
3543
|
children: [
|
|
3294
|
-
/* @__PURE__ */ (0,
|
|
3295
|
-
/* @__PURE__ */ (0,
|
|
3544
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate", children: scene.sceneName }),
|
|
3545
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-sas-muted", children: [
|
|
3296
3546
|
scene.tracks.length,
|
|
3297
3547
|
" \u2192"
|
|
3298
3548
|
] })
|
|
3299
3549
|
]
|
|
3300
3550
|
}
|
|
3301
3551
|
) }, scene.sceneId)) }),
|
|
3302
|
-
selectedScene && /* @__PURE__ */ (0,
|
|
3552
|
+
selectedScene && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-track-list`, children: selectedScene.tracks.map((track) => {
|
|
3303
3553
|
const busy = importingTrackId === track.trackId;
|
|
3304
3554
|
const gated = mode === "track" && !track.importable;
|
|
3305
3555
|
const disabled = gated || busy;
|
|
3306
|
-
return /* @__PURE__ */ (0,
|
|
3556
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3307
3557
|
"button",
|
|
3308
3558
|
{
|
|
3309
3559
|
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"}`,
|
|
@@ -3313,14 +3563,14 @@ function ImportTrackModal({
|
|
|
3313
3563
|
"data-testid": `${testIdPrefix}-track`,
|
|
3314
3564
|
"data-importable": mode === "sound" || track.importable ? "true" : "false",
|
|
3315
3565
|
children: [
|
|
3316
|
-
/* @__PURE__ */ (0,
|
|
3566
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "truncate", children: [
|
|
3317
3567
|
track.name,
|
|
3318
|
-
track.role ? /* @__PURE__ */ (0,
|
|
3568
|
+
track.role ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-sas-muted", children: [
|
|
3319
3569
|
" \xB7 ",
|
|
3320
3570
|
track.role
|
|
3321
3571
|
] }) : null
|
|
3322
3572
|
] }),
|
|
3323
|
-
busy ? /* @__PURE__ */ (0,
|
|
3573
|
+
busy ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sas-muted", children: "\u2026" }) : gated ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sas-muted", children: "\u2298" }) : null
|
|
3324
3574
|
]
|
|
3325
3575
|
}
|
|
3326
3576
|
) }, track.dbId);
|
|
@@ -3332,8 +3582,8 @@ function ImportTrackModal({
|
|
|
3332
3582
|
}
|
|
3333
3583
|
|
|
3334
3584
|
// src/components/CrossfadeModal.tsx
|
|
3335
|
-
var
|
|
3336
|
-
var
|
|
3585
|
+
var import_react16 = require("react");
|
|
3586
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
3337
3587
|
function shortId2(dbId) {
|
|
3338
3588
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
3339
3589
|
}
|
|
@@ -3346,7 +3596,7 @@ function CandidateRow({
|
|
|
3346
3596
|
}) {
|
|
3347
3597
|
const primary = track.prompt?.trim() || track.name;
|
|
3348
3598
|
const meta = [track.role, shortId2(track.dbId)].filter(Boolean).join(" \xB7 ");
|
|
3349
|
-
return /* @__PURE__ */ (0,
|
|
3599
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3350
3600
|
"button",
|
|
3351
3601
|
{
|
|
3352
3602
|
type: "button",
|
|
@@ -3358,8 +3608,8 @@ function CandidateRow({
|
|
|
3358
3608
|
disabled,
|
|
3359
3609
|
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"}`,
|
|
3360
3610
|
children: [
|
|
3361
|
-
/* @__PURE__ */ (0,
|
|
3362
|
-
meta && /* @__PURE__ */ (0,
|
|
3611
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
|
|
3612
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
|
|
3363
3613
|
]
|
|
3364
3614
|
}
|
|
3365
3615
|
);
|
|
@@ -3376,15 +3626,15 @@ function CrossfadeModal({
|
|
|
3376
3626
|
onCreate,
|
|
3377
3627
|
testIdPrefix = "crossfade-modal"
|
|
3378
3628
|
}) {
|
|
3379
|
-
const [load, setLoad] = (0,
|
|
3380
|
-
const [originDbId, setOriginDbId] = (0,
|
|
3381
|
-
const [targetDbId, setTargetDbId] = (0,
|
|
3382
|
-
const [isCreating, setIsCreating] = (0,
|
|
3383
|
-
const [error, setError] = (0,
|
|
3384
|
-
const [fromName, setFromName] = (0,
|
|
3385
|
-
const [toName, setToName] = (0,
|
|
3386
|
-
const cancelRef = (0,
|
|
3387
|
-
const refresh = (0,
|
|
3629
|
+
const [load, setLoad] = (0, import_react16.useState)({ status: "loading" });
|
|
3630
|
+
const [originDbId, setOriginDbId] = (0, import_react16.useState)("");
|
|
3631
|
+
const [targetDbId, setTargetDbId] = (0, import_react16.useState)("");
|
|
3632
|
+
const [isCreating, setIsCreating] = (0, import_react16.useState)(false);
|
|
3633
|
+
const [error, setError] = (0, import_react16.useState)(null);
|
|
3634
|
+
const [fromName, setFromName] = (0, import_react16.useState)(null);
|
|
3635
|
+
const [toName, setToName] = (0, import_react16.useState)(null);
|
|
3636
|
+
const cancelRef = (0, import_react16.useRef)(null);
|
|
3637
|
+
const refresh = (0, import_react16.useCallback)(async () => {
|
|
3388
3638
|
if (!host.listSceneFamilyTracks) {
|
|
3389
3639
|
setLoad({ status: "error", message: "This host does not support crossfade tracks." });
|
|
3390
3640
|
return;
|
|
@@ -3404,7 +3654,7 @@ function CrossfadeModal({
|
|
|
3404
3654
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
3405
3655
|
}
|
|
3406
3656
|
}, [host, fromSceneId, toSceneId]);
|
|
3407
|
-
(0,
|
|
3657
|
+
(0, import_react16.useEffect)(() => {
|
|
3408
3658
|
if (open) {
|
|
3409
3659
|
setError(null);
|
|
3410
3660
|
setIsCreating(false);
|
|
@@ -3413,21 +3663,21 @@ function CrossfadeModal({
|
|
|
3413
3663
|
void refresh();
|
|
3414
3664
|
}
|
|
3415
3665
|
}, [open, refresh]);
|
|
3416
|
-
const excludeSet = (0,
|
|
3417
|
-
const originCandidates = (0,
|
|
3666
|
+
const excludeSet = (0, import_react16.useMemo)(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
3667
|
+
const originCandidates = (0, import_react16.useMemo)(
|
|
3418
3668
|
() => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3419
3669
|
[load, excludeSet]
|
|
3420
3670
|
);
|
|
3421
|
-
const targetCandidates = (0,
|
|
3671
|
+
const targetCandidates = (0, import_react16.useMemo)(
|
|
3422
3672
|
() => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3423
3673
|
[load, excludeSet]
|
|
3424
3674
|
);
|
|
3425
|
-
(0,
|
|
3675
|
+
(0, import_react16.useEffect)(() => {
|
|
3426
3676
|
if (!originCandidates.some((t) => t.dbId === originDbId)) {
|
|
3427
3677
|
setOriginDbId(originCandidates[0]?.dbId ?? "");
|
|
3428
3678
|
}
|
|
3429
3679
|
}, [originCandidates, originDbId]);
|
|
3430
|
-
(0,
|
|
3680
|
+
(0, import_react16.useEffect)(() => {
|
|
3431
3681
|
if (!targetCandidates.some((t) => t.dbId === targetDbId)) {
|
|
3432
3682
|
setTargetDbId(targetCandidates[0]?.dbId ?? "");
|
|
3433
3683
|
}
|
|
@@ -3435,10 +3685,10 @@ function CrossfadeModal({
|
|
|
3435
3685
|
const originTrack = originCandidates.find((t) => t.dbId === originDbId) ?? null;
|
|
3436
3686
|
const targetTrack = targetCandidates.find((t) => t.dbId === targetDbId) ?? null;
|
|
3437
3687
|
const canCreate = !isCreating && !!originTrack && !!targetTrack;
|
|
3438
|
-
const handleClose = (0,
|
|
3688
|
+
const handleClose = (0, import_react16.useCallback)(() => {
|
|
3439
3689
|
if (!isCreating) onClose();
|
|
3440
3690
|
}, [isCreating, onClose]);
|
|
3441
|
-
const handleCreate = (0,
|
|
3691
|
+
const handleCreate = (0, import_react16.useCallback)(async () => {
|
|
3442
3692
|
if (!originTrack || !targetTrack) return;
|
|
3443
3693
|
setIsCreating(true);
|
|
3444
3694
|
setError(null);
|
|
@@ -3456,26 +3706,26 @@ function CrossfadeModal({
|
|
|
3456
3706
|
const fromLabel = fromName ?? fromSceneName ?? null;
|
|
3457
3707
|
const toLabel = toName ?? toSceneName ?? null;
|
|
3458
3708
|
if (!open) return null;
|
|
3459
|
-
return /* @__PURE__ */ (0,
|
|
3709
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3460
3710
|
"div",
|
|
3461
3711
|
{
|
|
3462
3712
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
3463
3713
|
onClick: (e) => e.stopPropagation(),
|
|
3464
3714
|
"data-testid": `${testIdPrefix}-box`,
|
|
3465
3715
|
children: [
|
|
3466
|
-
/* @__PURE__ */ (0,
|
|
3467
|
-
/* @__PURE__ */ (0,
|
|
3716
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h3", { className: "text-sm font-bold text-sas-text", children: "Add crossfade" }),
|
|
3717
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
|
|
3468
3718
|
"Bridge a track from",
|
|
3469
3719
|
" ",
|
|
3470
|
-
/* @__PURE__ */ (0,
|
|
3720
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
3471
3721
|
" into one from",
|
|
3472
3722
|
" ",
|
|
3473
|
-
/* @__PURE__ */ (0,
|
|
3723
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
3474
3724
|
". Both layers share one generated part; each keeps its own preset."
|
|
3475
3725
|
] }),
|
|
3476
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
3477
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
3478
|
-
load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */ (0,
|
|
3726
|
+
load.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
|
|
3727
|
+
load.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
|
|
3728
|
+
load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3479
3729
|
"div",
|
|
3480
3730
|
{
|
|
3481
3731
|
className: "text-xs text-sas-muted py-4 text-center",
|
|
@@ -3486,20 +3736,20 @@ function CrossfadeModal({
|
|
|
3486
3736
|
". Add one (or free one from another crossfade) first."
|
|
3487
3737
|
]
|
|
3488
3738
|
}
|
|
3489
|
-
) : /* @__PURE__ */ (0,
|
|
3490
|
-
/* @__PURE__ */ (0,
|
|
3491
|
-
/* @__PURE__ */ (0,
|
|
3739
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
3740
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "block", children: [
|
|
3741
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
3492
3742
|
"Origin ",
|
|
3493
3743
|
fromLabel ? `(${fromLabel})` : "(top)"
|
|
3494
3744
|
] }),
|
|
3495
|
-
/* @__PURE__ */ (0,
|
|
3745
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3496
3746
|
"div",
|
|
3497
3747
|
{
|
|
3498
3748
|
role: "radiogroup",
|
|
3499
3749
|
"aria-label": "Origin track",
|
|
3500
3750
|
"data-testid": `${testIdPrefix}-origin-list`,
|
|
3501
3751
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3502
|
-
children: originCandidates.map((t) => /* @__PURE__ */ (0,
|
|
3752
|
+
children: originCandidates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3503
3753
|
CandidateRow,
|
|
3504
3754
|
{
|
|
3505
3755
|
track: t,
|
|
@@ -3513,23 +3763,23 @@ function CrossfadeModal({
|
|
|
3513
3763
|
}
|
|
3514
3764
|
)
|
|
3515
3765
|
] }),
|
|
3516
|
-
/* @__PURE__ */ (0,
|
|
3517
|
-
/* @__PURE__ */ (0,
|
|
3766
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "block", children: [
|
|
3767
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
3518
3768
|
"Target ",
|
|
3519
3769
|
toLabel ? `(${toLabel})` : "(bottom)"
|
|
3520
3770
|
] }),
|
|
3521
|
-
targetCandidates.length === 0 ? /* @__PURE__ */ (0,
|
|
3771
|
+
targetCandidates.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "text-xs text-sas-danger mt-0.5", "data-testid": `${testIdPrefix}-empty-target`, children: [
|
|
3522
3772
|
"No available tracks in ",
|
|
3523
3773
|
toLabel ?? "the target scene",
|
|
3524
3774
|
" to crossfade into."
|
|
3525
|
-
] }) : /* @__PURE__ */ (0,
|
|
3775
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3526
3776
|
"div",
|
|
3527
3777
|
{
|
|
3528
3778
|
role: "radiogroup",
|
|
3529
3779
|
"aria-label": "Target track",
|
|
3530
3780
|
"data-testid": `${testIdPrefix}-target-list`,
|
|
3531
3781
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3532
|
-
children: targetCandidates.map((t) => /* @__PURE__ */ (0,
|
|
3782
|
+
children: targetCandidates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3533
3783
|
CandidateRow,
|
|
3534
3784
|
{
|
|
3535
3785
|
track: t,
|
|
@@ -3544,9 +3794,9 @@ function CrossfadeModal({
|
|
|
3544
3794
|
)
|
|
3545
3795
|
] })
|
|
3546
3796
|
] })),
|
|
3547
|
-
error && /* @__PURE__ */ (0,
|
|
3548
|
-
/* @__PURE__ */ (0,
|
|
3549
|
-
/* @__PURE__ */ (0,
|
|
3797
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
|
|
3798
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
3799
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3550
3800
|
"button",
|
|
3551
3801
|
{
|
|
3552
3802
|
ref: cancelRef,
|
|
@@ -3557,7 +3807,7 @@ function CrossfadeModal({
|
|
|
3557
3807
|
children: "Cancel"
|
|
3558
3808
|
}
|
|
3559
3809
|
),
|
|
3560
|
-
/* @__PURE__ */ (0,
|
|
3810
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3561
3811
|
"button",
|
|
3562
3812
|
{
|
|
3563
3813
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -3574,10 +3824,10 @@ function CrossfadeModal({
|
|
|
3574
3824
|
}
|
|
3575
3825
|
|
|
3576
3826
|
// src/components/TransitionDesigner.tsx
|
|
3577
|
-
var
|
|
3827
|
+
var import_react18 = require("react");
|
|
3578
3828
|
|
|
3579
3829
|
// src/hooks/useTrackReorder.ts
|
|
3580
|
-
var
|
|
3830
|
+
var import_react17 = require("react");
|
|
3581
3831
|
function moveItem(arr, from, to) {
|
|
3582
3832
|
const next = arr.slice();
|
|
3583
3833
|
if (from === to || from < 0 || to < 0 || from >= next.length || to >= next.length) {
|
|
@@ -3594,12 +3844,12 @@ function useTrackReorder({
|
|
|
3594
3844
|
getId,
|
|
3595
3845
|
onError
|
|
3596
3846
|
}) {
|
|
3597
|
-
const [draggingIndex, setDraggingIndex] = (0,
|
|
3598
|
-
const [dragOverIndex, setDragOverIndex] = (0,
|
|
3599
|
-
const fromRef = (0,
|
|
3600
|
-
const itemsRef = (0,
|
|
3847
|
+
const [draggingIndex, setDraggingIndex] = (0, import_react17.useState)(null);
|
|
3848
|
+
const [dragOverIndex, setDragOverIndex] = (0, import_react17.useState)(null);
|
|
3849
|
+
const fromRef = (0, import_react17.useRef)(null);
|
|
3850
|
+
const itemsRef = (0, import_react17.useRef)(items);
|
|
3601
3851
|
itemsRef.current = items;
|
|
3602
|
-
const dragPropsFor = (0,
|
|
3852
|
+
const dragPropsFor = (0, import_react17.useCallback)(
|
|
3603
3853
|
(index) => ({
|
|
3604
3854
|
handleProps: {
|
|
3605
3855
|
draggable: true,
|
|
@@ -3781,7 +4031,7 @@ function dbIdsFromKeys(keys) {
|
|
|
3781
4031
|
}
|
|
3782
4032
|
|
|
3783
4033
|
// src/components/TransitionDesigner.tsx
|
|
3784
|
-
var
|
|
4034
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
3785
4035
|
var CROSSFADE_ESTIMATE_MS = 15e3;
|
|
3786
4036
|
var FADE_ESTIMATE_MS = 11e3;
|
|
3787
4037
|
var CREATE_ALL_CONCURRENCY = 5;
|
|
@@ -3805,44 +4055,44 @@ function TransitionDesigner({
|
|
|
3805
4055
|
familyLabel,
|
|
3806
4056
|
testIdPrefix = "transition-designer"
|
|
3807
4057
|
}) {
|
|
3808
|
-
const [load, setLoad] = (0,
|
|
3809
|
-
const [fromName, setFromName] = (0,
|
|
3810
|
-
const [toName, setToName] = (0,
|
|
3811
|
-
const [originSlots, setOriginSlots] = (0,
|
|
3812
|
-
const [targetSlots, setTargetSlots] = (0,
|
|
3813
|
-
const [creatingKeys, setCreatingKeys] = (0,
|
|
3814
|
-
const [rowErrors, setRowErrors] = (0,
|
|
3815
|
-
const [rowEffects, setRowEffects] = (0,
|
|
3816
|
-
const rowEffectsRef = (0,
|
|
4058
|
+
const [load, setLoad] = (0, import_react18.useState)({ status: "loading" });
|
|
4059
|
+
const [fromName, setFromName] = (0, import_react18.useState)(null);
|
|
4060
|
+
const [toName, setToName] = (0, import_react18.useState)(null);
|
|
4061
|
+
const [originSlots, setOriginSlots] = (0, import_react18.useState)([]);
|
|
4062
|
+
const [targetSlots, setTargetSlots] = (0, import_react18.useState)([]);
|
|
4063
|
+
const [creatingKeys, setCreatingKeys] = (0, import_react18.useState)(() => /* @__PURE__ */ new Set());
|
|
4064
|
+
const [rowErrors, setRowErrors] = (0, import_react18.useState)({});
|
|
4065
|
+
const [rowEffects, setRowEffects] = (0, import_react18.useState)({});
|
|
4066
|
+
const rowEffectsRef = (0, import_react18.useRef)(rowEffects);
|
|
3817
4067
|
rowEffectsRef.current = rowEffects;
|
|
3818
4068
|
const audioEffectsEnabled = !!onCreateAudioTransition;
|
|
3819
|
-
const excludeRef = (0,
|
|
4069
|
+
const excludeRef = (0, import_react18.useRef)(excludeSourceDbIds);
|
|
3820
4070
|
excludeRef.current = excludeSourceDbIds;
|
|
3821
|
-
const originSlotsRef = (0,
|
|
4071
|
+
const originSlotsRef = (0, import_react18.useRef)(originSlots);
|
|
3822
4072
|
originSlotsRef.current = originSlots;
|
|
3823
|
-
const targetSlotsRef = (0,
|
|
4073
|
+
const targetSlotsRef = (0, import_react18.useRef)(targetSlots);
|
|
3824
4074
|
targetSlotsRef.current = targetSlots;
|
|
3825
|
-
const creatingKeysRef = (0,
|
|
4075
|
+
const creatingKeysRef = (0, import_react18.useRef)(creatingKeys);
|
|
3826
4076
|
creatingKeysRef.current = creatingKeys;
|
|
3827
|
-
const dragRef = (0,
|
|
3828
|
-
const [dragging, setDragging] = (0,
|
|
3829
|
-
const [dragOver, setDragOver] = (0,
|
|
3830
|
-
const excludeSet = (0,
|
|
3831
|
-
const originPool = (0,
|
|
4077
|
+
const dragRef = (0, import_react18.useRef)(null);
|
|
4078
|
+
const [dragging, setDragging] = (0, import_react18.useState)(null);
|
|
4079
|
+
const [dragOver, setDragOver] = (0, import_react18.useState)(null);
|
|
4080
|
+
const excludeSet = (0, import_react18.useMemo)(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
4081
|
+
const originPool = (0, import_react18.useMemo)(
|
|
3832
4082
|
() => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3833
4083
|
[load, excludeSet]
|
|
3834
4084
|
);
|
|
3835
|
-
const targetPool = (0,
|
|
4085
|
+
const targetPool = (0, import_react18.useMemo)(
|
|
3836
4086
|
() => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3837
4087
|
[load, excludeSet]
|
|
3838
4088
|
);
|
|
3839
|
-
const originById = (0,
|
|
3840
|
-
const targetById = (0,
|
|
3841
|
-
const originByIdRef = (0,
|
|
4089
|
+
const originById = (0, import_react18.useMemo)(() => new Map(originPool.map((t) => [t.dbId, t])), [originPool]);
|
|
4090
|
+
const targetById = (0, import_react18.useMemo)(() => new Map(targetPool.map((t) => [t.dbId, t])), [targetPool]);
|
|
4091
|
+
const originByIdRef = (0, import_react18.useRef)(originById);
|
|
3842
4092
|
originByIdRef.current = originById;
|
|
3843
|
-
const targetByIdRef = (0,
|
|
4093
|
+
const targetByIdRef = (0, import_react18.useRef)(targetById);
|
|
3844
4094
|
targetByIdRef.current = targetById;
|
|
3845
|
-
const refresh = (0,
|
|
4095
|
+
const refresh = (0, import_react18.useCallback)(async () => {
|
|
3846
4096
|
if (!host.listSceneFamilyTracks) {
|
|
3847
4097
|
setLoad({ status: "error", message: "This host does not support transition tracks." });
|
|
3848
4098
|
return;
|
|
@@ -3877,10 +4127,10 @@ function TransitionDesigner({
|
|
|
3877
4127
|
});
|
|
3878
4128
|
}
|
|
3879
4129
|
}, [host, fromSceneId, toSceneId, transitionSceneId]);
|
|
3880
|
-
(0,
|
|
4130
|
+
(0, import_react18.useEffect)(() => {
|
|
3881
4131
|
void refresh();
|
|
3882
4132
|
}, [refresh]);
|
|
3883
|
-
(0,
|
|
4133
|
+
(0, import_react18.useEffect)(() => {
|
|
3884
4134
|
if (load.status !== "ready") return;
|
|
3885
4135
|
const [po, pt] = padPair(
|
|
3886
4136
|
reconcileSlots(originSlotsRef.current, originPool.map((t) => t.dbId)),
|
|
@@ -3889,7 +4139,7 @@ function TransitionDesigner({
|
|
|
3889
4139
|
if (!slotsEqual(po, originSlotsRef.current)) setOriginSlots(po);
|
|
3890
4140
|
if (!slotsEqual(pt, targetSlotsRef.current)) setTargetSlots(pt);
|
|
3891
4141
|
}, [originPool, targetPool, load.status]);
|
|
3892
|
-
const mutate = (0,
|
|
4142
|
+
const mutate = (0, import_react18.useCallback)(
|
|
3893
4143
|
(nextOrigin, nextTarget) => {
|
|
3894
4144
|
const norm = normalizeSlots(nextOrigin, nextTarget);
|
|
3895
4145
|
const [po, pt] = padPair(norm.originOrder, norm.targetOrder);
|
|
@@ -3902,7 +4152,7 @@ function TransitionDesigner({
|
|
|
3902
4152
|
},
|
|
3903
4153
|
[host, transitionSceneId]
|
|
3904
4154
|
);
|
|
3905
|
-
const setRowEffect = (0,
|
|
4155
|
+
const setRowEffect = (0, import_react18.useCallback)(
|
|
3906
4156
|
(sourceDbId, effect) => {
|
|
3907
4157
|
setRowEffects((prev) => {
|
|
3908
4158
|
const next = { ...prev, [sourceDbId]: effect };
|
|
@@ -3916,7 +4166,7 @@ function TransitionDesigner({
|
|
|
3916
4166
|
},
|
|
3917
4167
|
[host, transitionSceneId]
|
|
3918
4168
|
);
|
|
3919
|
-
const insertGapAbove = (0,
|
|
4169
|
+
const insertGapAbove = (0, import_react18.useCallback)(
|
|
3920
4170
|
(col, index) => {
|
|
3921
4171
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
3922
4172
|
const next = [...slots.slice(0, index), null, ...slots.slice(index)];
|
|
@@ -3925,7 +4175,7 @@ function TransitionDesigner({
|
|
|
3925
4175
|
},
|
|
3926
4176
|
[originSlots, targetSlots, mutate]
|
|
3927
4177
|
);
|
|
3928
|
-
const removeGap = (0,
|
|
4178
|
+
const removeGap = (0, import_react18.useCallback)(
|
|
3929
4179
|
(col, index) => {
|
|
3930
4180
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
3931
4181
|
const next = slots.filter((_, i) => i !== index);
|
|
@@ -3934,7 +4184,7 @@ function TransitionDesigner({
|
|
|
3934
4184
|
},
|
|
3935
4185
|
[originSlots, targetSlots, mutate]
|
|
3936
4186
|
);
|
|
3937
|
-
const handleDrop = (0,
|
|
4187
|
+
const handleDrop = (0, import_react18.useCallback)(
|
|
3938
4188
|
(col, to) => {
|
|
3939
4189
|
const from = dragRef.current;
|
|
3940
4190
|
dragRef.current = null;
|
|
@@ -3946,16 +4196,16 @@ function TransitionDesigner({
|
|
|
3946
4196
|
},
|
|
3947
4197
|
[originSlots, targetSlots, mutate]
|
|
3948
4198
|
);
|
|
3949
|
-
const rows = (0,
|
|
3950
|
-
const creatingDbIds = (0,
|
|
3951
|
-
const eligibleCount = (0,
|
|
4199
|
+
const rows = (0, import_react18.useMemo)(() => buildRowSlots(originSlots, targetSlots), [originSlots, targetSlots]);
|
|
4200
|
+
const creatingDbIds = (0, import_react18.useMemo)(() => dbIdsFromKeys(creatingKeys), [creatingKeys]);
|
|
4201
|
+
const eligibleCount = (0, import_react18.useMemo)(
|
|
3952
4202
|
() => rows.filter((r) => {
|
|
3953
4203
|
const k = rowKey(r);
|
|
3954
4204
|
return k !== null && !creatingKeys.has(k);
|
|
3955
4205
|
}).length,
|
|
3956
4206
|
[rows, creatingKeys]
|
|
3957
4207
|
);
|
|
3958
|
-
const createRow = (0,
|
|
4208
|
+
const createRow = (0, import_react18.useCallback)(
|
|
3959
4209
|
async (row) => {
|
|
3960
4210
|
const key = rowKey(row);
|
|
3961
4211
|
if (!key || !row.type || creatingKeysRef.current.has(key)) return;
|
|
@@ -4009,7 +4259,7 @@ function TransitionDesigner({
|
|
|
4009
4259
|
},
|
|
4010
4260
|
[onCreateCrossfade, onCreateFade, onCreateAudioTransition]
|
|
4011
4261
|
);
|
|
4012
|
-
const createAll = (0,
|
|
4262
|
+
const createAll = (0, import_react18.useCallback)(async () => {
|
|
4013
4263
|
const eligible = buildRowSlots(originSlotsRef.current, targetSlotsRef.current).filter((r) => {
|
|
4014
4264
|
const k = rowKey(r);
|
|
4015
4265
|
return k !== null && !creatingKeysRef.current.has(k);
|
|
@@ -4077,15 +4327,15 @@ function TransitionDesigner({
|
|
|
4077
4327
|
const base = "group relative rounded-sm border px-2 py-1.5 text-left transition-colors select-none";
|
|
4078
4328
|
const tone = isDragTarget ? "border-sas-accent bg-sas-accent/10" : "border-sas-border bg-sas-panel";
|
|
4079
4329
|
if (slotId === null) {
|
|
4080
|
-
return /* @__PURE__ */ (0,
|
|
4330
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
4081
4331
|
"div",
|
|
4082
4332
|
{
|
|
4083
4333
|
...cellDragProps(col, index, false),
|
|
4084
4334
|
"data-testid": `${testIdPrefix}-${col}-gap-${index}`,
|
|
4085
4335
|
className: `${base} ${tone} border-dashed flex items-center justify-between ${isDragging ? "opacity-40" : "opacity-70"}`,
|
|
4086
4336
|
children: [
|
|
4087
|
-
/* @__PURE__ */ (0,
|
|
4088
|
-
/* @__PURE__ */ (0,
|
|
4337
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: "\u2014 gap \u2014" }),
|
|
4338
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4089
4339
|
"button",
|
|
4090
4340
|
{
|
|
4091
4341
|
type: "button",
|
|
@@ -4102,7 +4352,7 @@ function TransitionDesigner({
|
|
|
4102
4352
|
}
|
|
4103
4353
|
const primary = track ? track.prompt?.trim() || track.name : slotId;
|
|
4104
4354
|
const meta = track ? [track.role, shortId3(track.dbId)].filter(Boolean).join(" \xB7 ") : "missing";
|
|
4105
|
-
return /* @__PURE__ */ (0,
|
|
4355
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4106
4356
|
"div",
|
|
4107
4357
|
{
|
|
4108
4358
|
...cellDragProps(col, index, locked),
|
|
@@ -4110,13 +4360,13 @@ function TransitionDesigner({
|
|
|
4110
4360
|
"data-value": slotId,
|
|
4111
4361
|
className: `${base} ${tone} ${isDragging ? "opacity-40" : ""} ${locked ? "opacity-60" : "cursor-grab active:cursor-grabbing"}`,
|
|
4112
4362
|
title: track ? track.dbId : "Track no longer available",
|
|
4113
|
-
children: /* @__PURE__ */ (0,
|
|
4114
|
-
/* @__PURE__ */ (0,
|
|
4115
|
-
/* @__PURE__ */ (0,
|
|
4116
|
-
/* @__PURE__ */ (0,
|
|
4117
|
-
meta && /* @__PURE__ */ (0,
|
|
4363
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-start gap-1", children: [
|
|
4364
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sas-muted/60 text-xs leading-tight pt-0.5", "aria-hidden": true, children: "\u283F" }),
|
|
4365
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
4366
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "text-xs text-sas-text truncate", children: primary }),
|
|
4367
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", children: meta })
|
|
4118
4368
|
] }),
|
|
4119
|
-
/* @__PURE__ */ (0,
|
|
4369
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4120
4370
|
"button",
|
|
4121
4371
|
{
|
|
4122
4372
|
type: "button",
|
|
@@ -4132,22 +4382,22 @@ function TransitionDesigner({
|
|
|
4132
4382
|
}
|
|
4133
4383
|
);
|
|
4134
4384
|
};
|
|
4135
|
-
return /* @__PURE__ */ (0,
|
|
4136
|
-
/* @__PURE__ */ (0,
|
|
4137
|
-
/* @__PURE__ */ (0,
|
|
4138
|
-
/* @__PURE__ */ (0,
|
|
4385
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
|
|
4386
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
|
|
4387
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
|
|
4388
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sas-text", children: fromLabel }),
|
|
4139
4389
|
" \u2192",
|
|
4140
4390
|
" ",
|
|
4141
|
-
/* @__PURE__ */ (0,
|
|
4391
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sas-text", children: toLabel }),
|
|
4142
4392
|
familyLabel ? ` \xB7 ${familyLabel}` : "",
|
|
4143
4393
|
" \xB7 line up a track on each side to crossfade; leave one blank (or insert a gap) to fade."
|
|
4144
4394
|
] }),
|
|
4145
|
-
/* @__PURE__ */ (0,
|
|
4146
|
-
creatingKeys.size > 0 && /* @__PURE__ */ (0,
|
|
4395
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
4396
|
+
creatingKeys.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "text-[10px] text-sas-accent whitespace-nowrap", "data-testid": `${testIdPrefix}-creating-count`, children: [
|
|
4147
4397
|
creatingKeys.size,
|
|
4148
4398
|
" creating\u2026"
|
|
4149
4399
|
] }),
|
|
4150
|
-
/* @__PURE__ */ (0,
|
|
4400
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
4151
4401
|
"button",
|
|
4152
4402
|
{
|
|
4153
4403
|
type: "button",
|
|
@@ -4164,49 +4414,49 @@ function TransitionDesigner({
|
|
|
4164
4414
|
)
|
|
4165
4415
|
] })
|
|
4166
4416
|
] }),
|
|
4167
|
-
/* @__PURE__ */ (0,
|
|
4168
|
-
/* @__PURE__ */ (0,
|
|
4417
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "grid grid-cols-[1fr_auto_1fr] gap-2", children: [
|
|
4418
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate", children: [
|
|
4169
4419
|
"Origin (",
|
|
4170
4420
|
fromLabel,
|
|
4171
4421
|
")"
|
|
4172
4422
|
] }),
|
|
4173
|
-
/* @__PURE__ */ (0,
|
|
4174
|
-
/* @__PURE__ */ (0,
|
|
4423
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted text-center px-2", children: "Transition" }),
|
|
4424
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate text-right", children: [
|
|
4175
4425
|
"Target (",
|
|
4176
4426
|
toLabel,
|
|
4177
4427
|
")"
|
|
4178
4428
|
] })
|
|
4179
4429
|
] }),
|
|
4180
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
4181
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
4182
|
-
load.status === "ready" && (rows.length === 0 ? /* @__PURE__ */ (0,
|
|
4430
|
+
load.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
|
|
4431
|
+
load.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
4432
|
+
load.status === "ready" && (rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "text-xs text-sas-muted py-6 text-center", "data-testid": `${testIdPrefix}-empty`, children: [
|
|
4183
4433
|
"No tracks to arrange in this panel for either scene. Add tracks to ",
|
|
4184
4434
|
fromLabel,
|
|
4185
4435
|
" or ",
|
|
4186
4436
|
toLabel,
|
|
4187
4437
|
" ",
|
|
4188
4438
|
"first (or free one by deleting an existing crossfade/fade)."
|
|
4189
|
-
] }) : /* @__PURE__ */ (0,
|
|
4439
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "space-y-2", children: rows.map((row, i) => {
|
|
4190
4440
|
const key = rowKey(row);
|
|
4191
4441
|
const isCreatingThis = key !== null && creatingKeys.has(key);
|
|
4192
4442
|
const errMsg = key !== null ? rowErrors[key] : void 0;
|
|
4193
|
-
return /* @__PURE__ */ (0,
|
|
4443
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
4194
4444
|
"div",
|
|
4195
4445
|
{
|
|
4196
4446
|
"data-testid": `${testIdPrefix}-row-${i}`,
|
|
4197
4447
|
className: "grid grid-cols-[1fr_auto_1fr] gap-2 items-center",
|
|
4198
4448
|
children: [
|
|
4199
4449
|
renderCell("origin", i, row.originId),
|
|
4200
|
-
/* @__PURE__ */ (0,
|
|
4201
|
-
!row.type ? /* @__PURE__ */ (0,
|
|
4450
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
|
|
4451
|
+
!row.type ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-[10px] text-sas-muted/50", children: "\u2014" }) : row.type === "crossfade" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4202
4452
|
"span",
|
|
4203
4453
|
{
|
|
4204
4454
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
4205
4455
|
className: "text-[10px] font-medium px-1.5 py-0.5 rounded-sm border border-sas-accent/50 text-sas-accent",
|
|
4206
4456
|
children: TYPE_LABEL[row.type]
|
|
4207
4457
|
}
|
|
4208
|
-
) : audioEffectsEnabled ? /* @__PURE__ */ (0,
|
|
4209
|
-
/* @__PURE__ */ (0,
|
|
4458
|
+
) : audioEffectsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1", "data-testid": `${testIdPrefix}-type-${i}`, children: [
|
|
4459
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4210
4460
|
"select",
|
|
4211
4461
|
{
|
|
4212
4462
|
"data-testid": `${testIdPrefix}-effect-${i}`,
|
|
@@ -4216,11 +4466,11 @@ function TransitionDesigner({
|
|
|
4216
4466
|
if (id) setRowEffect(id, e.target.value);
|
|
4217
4467
|
},
|
|
4218
4468
|
className: "text-[10px] bg-sas-panel border border-sas-border rounded-sm px-1 py-0.5 text-sas-text",
|
|
4219
|
-
children: AUDIO_EFFECTS.map((eff) => /* @__PURE__ */ (0,
|
|
4469
|
+
children: AUDIO_EFFECTS.map((eff) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: eff, children: AUDIO_EFFECT_LABEL[eff] }, eff))
|
|
4220
4470
|
}
|
|
4221
4471
|
),
|
|
4222
|
-
/* @__PURE__ */ (0,
|
|
4223
|
-
] }) : /* @__PURE__ */ (0,
|
|
4472
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-[9px] text-sas-muted", children: row.type === "fade-out" ? "out" : "in" })
|
|
4473
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4224
4474
|
"span",
|
|
4225
4475
|
{
|
|
4226
4476
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
@@ -4228,7 +4478,7 @@ function TransitionDesigner({
|
|
|
4228
4478
|
children: TYPE_LABEL[row.type]
|
|
4229
4479
|
}
|
|
4230
4480
|
),
|
|
4231
|
-
isCreatingThis ? /* @__PURE__ */ (0,
|
|
4481
|
+
isCreatingThis ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4232
4482
|
SorceryProgressBar,
|
|
4233
4483
|
{
|
|
4234
4484
|
isLoading: true,
|
|
@@ -4236,7 +4486,7 @@ function TransitionDesigner({
|
|
|
4236
4486
|
statusText: "CREATING",
|
|
4237
4487
|
estimatedDurationMs: row.type === "crossfade" ? CROSSFADE_ESTIMATE_MS : FADE_ESTIMATE_MS
|
|
4238
4488
|
}
|
|
4239
|
-
) }) : /* @__PURE__ */ (0,
|
|
4489
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4240
4490
|
"button",
|
|
4241
4491
|
{
|
|
4242
4492
|
type: "button",
|
|
@@ -4247,7 +4497,7 @@ function TransitionDesigner({
|
|
|
4247
4497
|
children: "Create"
|
|
4248
4498
|
}
|
|
4249
4499
|
),
|
|
4250
|
-
errMsg && /* @__PURE__ */ (0,
|
|
4500
|
+
errMsg && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4251
4501
|
"span",
|
|
4252
4502
|
{
|
|
4253
4503
|
"data-testid": `${testIdPrefix}-row-error-${i}`,
|
|
@@ -4265,9 +4515,325 @@ function TransitionDesigner({
|
|
|
4265
4515
|
] });
|
|
4266
4516
|
}
|
|
4267
4517
|
|
|
4518
|
+
// src/components/PanelMasterStrip.tsx
|
|
4519
|
+
var import_react19 = require("react");
|
|
4520
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
4521
|
+
function PanelMasterStrip({
|
|
4522
|
+
bus,
|
|
4523
|
+
levels = null,
|
|
4524
|
+
availableFx = [],
|
|
4525
|
+
fxLoading = false,
|
|
4526
|
+
soloedOut = false,
|
|
4527
|
+
disabled = false,
|
|
4528
|
+
fxPickerOpen,
|
|
4529
|
+
onToggleFxPicker,
|
|
4530
|
+
onRefreshFx,
|
|
4531
|
+
onVolumeChange,
|
|
4532
|
+
onMuteToggle,
|
|
4533
|
+
onSoloToggle,
|
|
4534
|
+
onAddFx,
|
|
4535
|
+
onRemoveFx,
|
|
4536
|
+
onToggleFxEnabled,
|
|
4537
|
+
onShowFxEditor
|
|
4538
|
+
}) {
|
|
4539
|
+
const [search, setSearch] = (0, import_react19.useState)("");
|
|
4540
|
+
const filtered = (0, import_react19.useMemo)(() => {
|
|
4541
|
+
const q = search.trim().toLowerCase();
|
|
4542
|
+
if (!q) return availableFx;
|
|
4543
|
+
return availableFx.filter(
|
|
4544
|
+
(fx) => fx.name.toLowerCase().includes(q) || fx.manufacturer.toLowerCase().includes(q)
|
|
4545
|
+
);
|
|
4546
|
+
}, [availableFx, search]);
|
|
4547
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
4548
|
+
"div",
|
|
4549
|
+
{
|
|
4550
|
+
"data-testid": "panel-master-strip",
|
|
4551
|
+
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" : ""}`,
|
|
4552
|
+
children: [
|
|
4553
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4554
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4555
|
+
"span",
|
|
4556
|
+
{
|
|
4557
|
+
className: "text-[9px] font-bold tracking-widest text-sas-muted/70 select-none",
|
|
4558
|
+
title: "Panel mix bus \u2014 volume, mute/solo and FX applied to this panel's summed output",
|
|
4559
|
+
children: "BUS"
|
|
4560
|
+
}
|
|
4561
|
+
),
|
|
4562
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex-1 min-w-[8rem] flex flex-col gap-0.5", children: [
|
|
4563
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4564
|
+
VolumeSlider,
|
|
4565
|
+
{
|
|
4566
|
+
value: dbToSlider(bus.volume),
|
|
4567
|
+
onChange: (sliderValue) => onVolumeChange(sliderToDb(sliderValue)),
|
|
4568
|
+
disabled
|
|
4569
|
+
}
|
|
4570
|
+
),
|
|
4571
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col gap-px", "data-testid": "bus-meter", children: [
|
|
4572
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4573
|
+
LevelMeter,
|
|
4574
|
+
{
|
|
4575
|
+
peakDb: levels?.leftDb ?? -120,
|
|
4576
|
+
active: levels != null,
|
|
4577
|
+
clipped: levels?.clipped,
|
|
4578
|
+
compact: true,
|
|
4579
|
+
"data-testid": "bus-meter-left"
|
|
4580
|
+
}
|
|
4581
|
+
),
|
|
4582
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4583
|
+
LevelMeter,
|
|
4584
|
+
{
|
|
4585
|
+
peakDb: levels?.rightDb ?? -120,
|
|
4586
|
+
active: levels != null,
|
|
4587
|
+
compact: true,
|
|
4588
|
+
"data-testid": "bus-meter-right"
|
|
4589
|
+
}
|
|
4590
|
+
)
|
|
4591
|
+
] })
|
|
4592
|
+
] }),
|
|
4593
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4594
|
+
"button",
|
|
4595
|
+
{
|
|
4596
|
+
"data-testid": "bus-mute-button",
|
|
4597
|
+
onClick: onMuteToggle,
|
|
4598
|
+
disabled,
|
|
4599
|
+
className: `px-1.5 py-0.5 text-xs font-bold rounded transition-colors ${bus.muted ? "bg-red-600 text-white" : "bg-sas-panel-alt text-sas-muted hover:bg-sas-border"} disabled:opacity-50`,
|
|
4600
|
+
title: bus.muted ? "Unmute panel bus" : "Mute panel bus (silences the whole panel)",
|
|
4601
|
+
children: "M"
|
|
4602
|
+
}
|
|
4603
|
+
),
|
|
4604
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4605
|
+
"button",
|
|
4606
|
+
{
|
|
4607
|
+
"data-testid": "bus-solo-button",
|
|
4608
|
+
onClick: onSoloToggle,
|
|
4609
|
+
disabled,
|
|
4610
|
+
className: `px-1.5 py-0.5 text-xs font-bold rounded transition-colors ${bus.soloed ? "bg-amber-500 text-black" : "bg-sas-panel-alt text-sas-muted hover:bg-sas-border"} disabled:opacity-50`,
|
|
4611
|
+
title: bus.soloed ? "Unsolo panel bus" : "Solo this panel (silences other panels/tracks in scope)",
|
|
4612
|
+
children: "S"
|
|
4613
|
+
}
|
|
4614
|
+
),
|
|
4615
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex items-center gap-1 max-w-[45%] min-w-0 overflow-x-auto", children: bus.fx.map((fx) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
4616
|
+
"span",
|
|
4617
|
+
{
|
|
4618
|
+
"data-testid": `bus-fx-chip-${fx.index}`,
|
|
4619
|
+
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"}`,
|
|
4620
|
+
title: `${fx.name}${fx.enabled ? "" : " (bypassed)"}`,
|
|
4621
|
+
children: [
|
|
4622
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4623
|
+
"button",
|
|
4624
|
+
{
|
|
4625
|
+
"data-testid": `bus-fx-toggle-${fx.index}`,
|
|
4626
|
+
onClick: () => onToggleFxEnabled(fx.index, !fx.enabled),
|
|
4627
|
+
disabled,
|
|
4628
|
+
className: "hover:opacity-70 disabled:opacity-50",
|
|
4629
|
+
title: fx.enabled ? `Bypass ${fx.name}` : `Enable ${fx.name}`,
|
|
4630
|
+
children: fx.enabled ? "\u25CF" : "\u25CB"
|
|
4631
|
+
}
|
|
4632
|
+
),
|
|
4633
|
+
onShowFxEditor ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4634
|
+
"button",
|
|
4635
|
+
{
|
|
4636
|
+
"data-testid": `bus-fx-edit-${fx.index}`,
|
|
4637
|
+
onClick: () => onShowFxEditor(fx.index),
|
|
4638
|
+
disabled,
|
|
4639
|
+
className: "max-w-[80px] truncate hover:underline disabled:opacity-50",
|
|
4640
|
+
title: `Open ${fx.name} editor`,
|
|
4641
|
+
children: fx.name
|
|
4642
|
+
}
|
|
4643
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "max-w-[80px] truncate", children: fx.name }),
|
|
4644
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4645
|
+
"button",
|
|
4646
|
+
{
|
|
4647
|
+
"data-testid": `bus-fx-remove-${fx.index}`,
|
|
4648
|
+
onClick: () => onRemoveFx(fx.index),
|
|
4649
|
+
disabled,
|
|
4650
|
+
className: "text-sas-muted/60 hover:text-sas-danger disabled:opacity-50",
|
|
4651
|
+
title: `Remove ${fx.name} from the bus`,
|
|
4652
|
+
children: "\u2715"
|
|
4653
|
+
}
|
|
4654
|
+
)
|
|
4655
|
+
]
|
|
4656
|
+
},
|
|
4657
|
+
`${fx.index}:${fx.pluginId}`
|
|
4658
|
+
)) }),
|
|
4659
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4660
|
+
"button",
|
|
4661
|
+
{
|
|
4662
|
+
"data-testid": "bus-fx-add-button",
|
|
4663
|
+
onClick: () => onToggleFxPicker(!fxPickerOpen),
|
|
4664
|
+
disabled,
|
|
4665
|
+
className: `px-1.5 py-0.5 rounded-sm border text-xs whitespace-nowrap transition-colors ${fxPickerOpen ? "border-sas-accent text-sas-accent bg-sas-accent/10" : "border-sas-border text-sas-muted hover:border-sas-accent hover:text-sas-accent"} disabled:opacity-50`,
|
|
4666
|
+
title: fxPickerOpen ? "Close the FX picker" : "Add an FX plugin to the panel bus",
|
|
4667
|
+
children: fxPickerOpen ? "FX \u25B4" : "FX +"
|
|
4668
|
+
}
|
|
4669
|
+
)
|
|
4670
|
+
] }),
|
|
4671
|
+
fxPickerOpen && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { "data-testid": "bus-fx-picker", className: "flex flex-col gap-2 pt-1", children: [
|
|
4672
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4673
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4674
|
+
"input",
|
|
4675
|
+
{
|
|
4676
|
+
type: "text",
|
|
4677
|
+
value: search,
|
|
4678
|
+
onChange: (e) => setSearch(e.target.value),
|
|
4679
|
+
placeholder: "Search FX...",
|
|
4680
|
+
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
4681
|
+
}
|
|
4682
|
+
),
|
|
4683
|
+
onRefreshFx && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4684
|
+
"button",
|
|
4685
|
+
{
|
|
4686
|
+
onClick: () => onRefreshFx(),
|
|
4687
|
+
disabled: fxLoading,
|
|
4688
|
+
className: "px-2 py-1 text-xs rounded-sm border border-sas-border text-sas-muted hover:text-sas-accent hover:border-sas-accent transition-colors disabled:opacity-50",
|
|
4689
|
+
title: "Re-scan plugins",
|
|
4690
|
+
children: fxLoading ? "..." : "Refresh"
|
|
4691
|
+
}
|
|
4692
|
+
)
|
|
4693
|
+
] }),
|
|
4694
|
+
fxLoading && availableFx.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
4695
|
+
filtered.map((fx) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
4696
|
+
"button",
|
|
4697
|
+
{
|
|
4698
|
+
"data-testid": `bus-fx-pick-${fx.pluginId}`,
|
|
4699
|
+
onClick: () => onAddFx(fx.pluginId),
|
|
4700
|
+
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",
|
|
4701
|
+
title: `${fx.name} by ${fx.manufacturer} (${fx.type.toUpperCase()})`,
|
|
4702
|
+
children: [
|
|
4703
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-xs font-medium truncate w-full", children: fx.name }),
|
|
4704
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: fx.manufacturer || fx.type.toUpperCase() })
|
|
4705
|
+
]
|
|
4706
|
+
},
|
|
4707
|
+
fx.pluginId
|
|
4708
|
+
)),
|
|
4709
|
+
filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "col-span-3 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No FX plugins found" })
|
|
4710
|
+
] })
|
|
4711
|
+
] })
|
|
4712
|
+
]
|
|
4713
|
+
}
|
|
4714
|
+
);
|
|
4715
|
+
}
|
|
4716
|
+
|
|
4717
|
+
// src/hooks/usePanelBus.ts
|
|
4718
|
+
var import_react20 = require("react");
|
|
4719
|
+
var LEVELS_POLL_MS = 66;
|
|
4720
|
+
function usePanelBus(host, activeSceneId) {
|
|
4721
|
+
const supported = typeof host.getPanelBusState === "function";
|
|
4722
|
+
const [bus, setBus] = (0, import_react20.useState)(null);
|
|
4723
|
+
const [levels, setLevels] = (0, import_react20.useState)(null);
|
|
4724
|
+
const [availableFx, setAvailableFx] = (0, import_react20.useState)([]);
|
|
4725
|
+
const [fxLoading, setFxLoading] = (0, import_react20.useState)(false);
|
|
4726
|
+
const [fxPickerOpen, setFxPickerOpen] = (0, import_react20.useState)(false);
|
|
4727
|
+
const fxLoadedRef = (0, import_react20.useRef)(false);
|
|
4728
|
+
const loadSeqRef = (0, import_react20.useRef)(0);
|
|
4729
|
+
const reload = (0, import_react20.useCallback)(async () => {
|
|
4730
|
+
if (!supported || !activeSceneId || !host.getPanelBusState) {
|
|
4731
|
+
setBus(null);
|
|
4732
|
+
return;
|
|
4733
|
+
}
|
|
4734
|
+
const seq = ++loadSeqRef.current;
|
|
4735
|
+
try {
|
|
4736
|
+
const state = await host.getPanelBusState(activeSceneId);
|
|
4737
|
+
if (loadSeqRef.current === seq) setBus(state);
|
|
4738
|
+
} catch {
|
|
4739
|
+
}
|
|
4740
|
+
}, [host, activeSceneId, supported]);
|
|
4741
|
+
(0, import_react20.useEffect)(() => {
|
|
4742
|
+
setBus(null);
|
|
4743
|
+
setFxPickerOpen(false);
|
|
4744
|
+
void reload();
|
|
4745
|
+
}, [reload]);
|
|
4746
|
+
(0, import_react20.useEffect)(() => {
|
|
4747
|
+
if (!supported || !activeSceneId || !bus?.engaged || !host.getPanelBusLevels) {
|
|
4748
|
+
setLevels(null);
|
|
4749
|
+
return;
|
|
4750
|
+
}
|
|
4751
|
+
let cancelled = false;
|
|
4752
|
+
const tick = async () => {
|
|
4753
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
4754
|
+
try {
|
|
4755
|
+
const next = await host.getPanelBusLevels(activeSceneId);
|
|
4756
|
+
if (!cancelled) setLevels(next);
|
|
4757
|
+
} catch {
|
|
4758
|
+
if (!cancelled) setLevels(null);
|
|
4759
|
+
}
|
|
4760
|
+
};
|
|
4761
|
+
void tick();
|
|
4762
|
+
const id = setInterval(() => void tick(), LEVELS_POLL_MS);
|
|
4763
|
+
return () => {
|
|
4764
|
+
cancelled = true;
|
|
4765
|
+
clearInterval(id);
|
|
4766
|
+
};
|
|
4767
|
+
}, [supported, activeSceneId, bus?.engaged, host]);
|
|
4768
|
+
const loadFxList = (0, import_react20.useCallback)(
|
|
4769
|
+
async (force) => {
|
|
4770
|
+
if (!supported || !host.getAvailableFx) return;
|
|
4771
|
+
if (fxLoadedRef.current && !force) return;
|
|
4772
|
+
setFxLoading(true);
|
|
4773
|
+
try {
|
|
4774
|
+
const list = await host.getAvailableFx();
|
|
4775
|
+
setAvailableFx(list);
|
|
4776
|
+
fxLoadedRef.current = true;
|
|
4777
|
+
} catch {
|
|
4778
|
+
} finally {
|
|
4779
|
+
setFxLoading(false);
|
|
4780
|
+
}
|
|
4781
|
+
},
|
|
4782
|
+
[host, supported]
|
|
4783
|
+
);
|
|
4784
|
+
const openPicker = (0, import_react20.useCallback)(
|
|
4785
|
+
(open) => {
|
|
4786
|
+
setFxPickerOpen(open);
|
|
4787
|
+
if (open) void loadFxList(false);
|
|
4788
|
+
},
|
|
4789
|
+
[loadFxList]
|
|
4790
|
+
);
|
|
4791
|
+
const mutate = (0, import_react20.useCallback)(
|
|
4792
|
+
(fn) => {
|
|
4793
|
+
if (!fn || !activeSceneId) return;
|
|
4794
|
+
void (async () => {
|
|
4795
|
+
try {
|
|
4796
|
+
await fn();
|
|
4797
|
+
} catch {
|
|
4798
|
+
}
|
|
4799
|
+
await reload();
|
|
4800
|
+
})();
|
|
4801
|
+
},
|
|
4802
|
+
[activeSceneId, reload]
|
|
4803
|
+
);
|
|
4804
|
+
return {
|
|
4805
|
+
supported,
|
|
4806
|
+
bus,
|
|
4807
|
+
levels,
|
|
4808
|
+
availableFx,
|
|
4809
|
+
fxLoading,
|
|
4810
|
+
fxPickerOpen,
|
|
4811
|
+
setFxPickerOpen: openPicker,
|
|
4812
|
+
refreshFx: () => void loadFxList(true),
|
|
4813
|
+
reload,
|
|
4814
|
+
onVolumeChange: (volumeDb) => mutate(host.setPanelBusVolume && (() => host.setPanelBusVolume(activeSceneId, volumeDb))),
|
|
4815
|
+
onMuteToggle: () => mutate(
|
|
4816
|
+
host.setPanelBusMute && (() => host.setPanelBusMute(activeSceneId, !(bus?.muted ?? false)))
|
|
4817
|
+
),
|
|
4818
|
+
onSoloToggle: () => mutate(
|
|
4819
|
+
host.setPanelBusSolo && (() => host.setPanelBusSolo(activeSceneId, !(bus?.soloed ?? false)))
|
|
4820
|
+
),
|
|
4821
|
+
onAddFx: (pluginId) => mutate(host.loadPanelBusFx && (async () => {
|
|
4822
|
+
await host.loadPanelBusFx(activeSceneId, pluginId);
|
|
4823
|
+
})),
|
|
4824
|
+
onRemoveFx: (fxIndex) => mutate(host.removePanelBusFx && (() => host.removePanelBusFx(activeSceneId, fxIndex))),
|
|
4825
|
+
onToggleFxEnabled: (fxIndex, enabled) => mutate(
|
|
4826
|
+
host.setPanelBusFxEnabled && (() => host.setPanelBusFxEnabled(activeSceneId, fxIndex, enabled))
|
|
4827
|
+
),
|
|
4828
|
+
onShowFxEditor: (fxIndex) => mutate(
|
|
4829
|
+
host.showPanelBusFxEditor && (() => host.showPanelBusFxEditor(activeSceneId, fxIndex))
|
|
4830
|
+
)
|
|
4831
|
+
};
|
|
4832
|
+
}
|
|
4833
|
+
|
|
4268
4834
|
// src/components/DownloadPackButton.tsx
|
|
4269
|
-
var
|
|
4270
|
-
var
|
|
4835
|
+
var import_react21 = require("react");
|
|
4836
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
4271
4837
|
function formatSize(bytes) {
|
|
4272
4838
|
if (!bytes || bytes <= 0) return "";
|
|
4273
4839
|
const gb = bytes / 1024 ** 3;
|
|
@@ -4283,10 +4849,10 @@ var DownloadPackButton = ({
|
|
|
4283
4849
|
variant = "compact",
|
|
4284
4850
|
onDownloadComplete
|
|
4285
4851
|
}) => {
|
|
4286
|
-
const [status, setStatus] = (0,
|
|
4287
|
-
const [progress, setProgress] = (0,
|
|
4288
|
-
const [errorMessage, setErrorMessage] = (0,
|
|
4289
|
-
(0,
|
|
4852
|
+
const [status, setStatus] = (0, import_react21.useState)("idle");
|
|
4853
|
+
const [progress, setProgress] = (0, import_react21.useState)(0);
|
|
4854
|
+
const [errorMessage, setErrorMessage] = (0, import_react21.useState)(null);
|
|
4855
|
+
(0, import_react21.useEffect)(() => {
|
|
4290
4856
|
const unsub = host.onSamplePackProgress(packId, (p) => {
|
|
4291
4857
|
setStatus(p.status);
|
|
4292
4858
|
setProgress(p.progress);
|
|
@@ -4301,7 +4867,7 @@ var DownloadPackButton = ({
|
|
|
4301
4867
|
});
|
|
4302
4868
|
return unsub;
|
|
4303
4869
|
}, [host, packId, onDownloadComplete]);
|
|
4304
|
-
const handleClick = (0,
|
|
4870
|
+
const handleClick = (0, import_react21.useCallback)(async () => {
|
|
4305
4871
|
if (status !== "idle" && status !== "error") return;
|
|
4306
4872
|
try {
|
|
4307
4873
|
setStatus("downloading");
|
|
@@ -4355,8 +4921,8 @@ var DownloadPackButton = ({
|
|
|
4355
4921
|
} else {
|
|
4356
4922
|
className = `${baseClasses} text-sas-muted hover:text-sas-accent border-sas-border hover:border-sas-accent`;
|
|
4357
4923
|
}
|
|
4358
|
-
return /* @__PURE__ */ (0,
|
|
4359
|
-
/* @__PURE__ */ (0,
|
|
4924
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
|
|
4925
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4360
4926
|
"button",
|
|
4361
4927
|
{
|
|
4362
4928
|
"data-testid": `download-pack-button-${packId}`,
|
|
@@ -4367,12 +4933,12 @@ var DownloadPackButton = ({
|
|
|
4367
4933
|
children: buttonLabel
|
|
4368
4934
|
}
|
|
4369
4935
|
),
|
|
4370
|
-
variant === "large" && status === "error" && errorMessage && /* @__PURE__ */ (0,
|
|
4936
|
+
variant === "large" && status === "error" && errorMessage && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-xs text-sas-danger mt-2", "data-testid": `download-pack-error-${packId}`, children: errorMessage })
|
|
4371
4937
|
] });
|
|
4372
4938
|
};
|
|
4373
4939
|
|
|
4374
4940
|
// src/components/SamplePackCTACard.tsx
|
|
4375
|
-
var
|
|
4941
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
4376
4942
|
var SamplePackCTACard = ({
|
|
4377
4943
|
host,
|
|
4378
4944
|
pack,
|
|
@@ -4380,7 +4946,7 @@ var SamplePackCTACard = ({
|
|
|
4380
4946
|
onDownloadComplete
|
|
4381
4947
|
}) => {
|
|
4382
4948
|
if (status === "checking") {
|
|
4383
|
-
return /* @__PURE__ */ (0,
|
|
4949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4384
4950
|
"div",
|
|
4385
4951
|
{
|
|
4386
4952
|
"data-testid": `sample-pack-cta-checking-${pack.packId}`,
|
|
@@ -4391,16 +4957,16 @@ var SamplePackCTACard = ({
|
|
|
4391
4957
|
}
|
|
4392
4958
|
const headline = status === "stale" ? `${pack.displayName} update available` : `${pack.displayName} not installed`;
|
|
4393
4959
|
const sublabel = status === "stale" ? `A newer version is available for download.` : pack.description;
|
|
4394
|
-
return /* @__PURE__ */ (0,
|
|
4960
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4395
4961
|
"div",
|
|
4396
4962
|
{
|
|
4397
4963
|
"data-testid": `sample-pack-cta-${pack.packId}`,
|
|
4398
4964
|
className: "flex flex-col items-center justify-center py-12 px-6 text-center",
|
|
4399
4965
|
children: [
|
|
4400
|
-
/* @__PURE__ */ (0,
|
|
4401
|
-
/* @__PURE__ */ (0,
|
|
4402
|
-
/* @__PURE__ */ (0,
|
|
4403
|
-
/* @__PURE__ */ (0,
|
|
4966
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-sm uppercase tracking-wide text-sas-muted mb-2", children: status === "stale" ? "Update available" : "Sample library not installed" }),
|
|
4967
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-base text-sas-text mb-1", children: headline }),
|
|
4968
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-xs text-sas-muted mb-6 max-w-md", children: sublabel }),
|
|
4969
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4404
4970
|
DownloadPackButton,
|
|
4405
4971
|
{
|
|
4406
4972
|
host,
|
|
@@ -4417,7 +4983,7 @@ var SamplePackCTACard = ({
|
|
|
4417
4983
|
};
|
|
4418
4984
|
|
|
4419
4985
|
// src/components/WaveformView.tsx
|
|
4420
|
-
var
|
|
4986
|
+
var import_react22 = require("react");
|
|
4421
4987
|
|
|
4422
4988
|
// src/components/waveform.ts
|
|
4423
4989
|
function computePeaks(audioBuffer, bins, targetSamples) {
|
|
@@ -4480,7 +5046,7 @@ function drawWaveform(canvas, peaks, options = {}) {
|
|
|
4480
5046
|
}
|
|
4481
5047
|
|
|
4482
5048
|
// src/components/WaveformView.tsx
|
|
4483
|
-
var
|
|
5049
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
4484
5050
|
var WaveformView = ({
|
|
4485
5051
|
host,
|
|
4486
5052
|
filePath,
|
|
@@ -4489,9 +5055,9 @@ var WaveformView = ({
|
|
|
4489
5055
|
fillStyle,
|
|
4490
5056
|
targetSamples
|
|
4491
5057
|
}) => {
|
|
4492
|
-
const canvasRef = (0,
|
|
4493
|
-
const [peaks, setPeaks] = (0,
|
|
4494
|
-
(0,
|
|
5058
|
+
const canvasRef = (0, import_react22.useRef)(null);
|
|
5059
|
+
const [peaks, setPeaks] = (0, import_react22.useState)(null);
|
|
5060
|
+
(0, import_react22.useEffect)(() => {
|
|
4495
5061
|
let cancelled = false;
|
|
4496
5062
|
let audioContext = null;
|
|
4497
5063
|
(async () => {
|
|
@@ -4517,7 +5083,7 @@ var WaveformView = ({
|
|
|
4517
5083
|
cancelled = true;
|
|
4518
5084
|
};
|
|
4519
5085
|
}, [host, filePath, bins, targetSamples]);
|
|
4520
|
-
(0,
|
|
5086
|
+
(0, import_react22.useEffect)(() => {
|
|
4521
5087
|
if (!peaks) return;
|
|
4522
5088
|
const canvas = canvasRef.current;
|
|
4523
5089
|
if (!canvas) return;
|
|
@@ -4528,7 +5094,7 @@ var WaveformView = ({
|
|
|
4528
5094
|
observer.observe(canvas);
|
|
4529
5095
|
return () => observer.disconnect();
|
|
4530
5096
|
}, [peaks, fillStyle]);
|
|
4531
|
-
return /* @__PURE__ */ (0,
|
|
5097
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
4532
5098
|
"canvas",
|
|
4533
5099
|
{
|
|
4534
5100
|
ref: canvasRef,
|
|
@@ -4539,8 +5105,8 @@ var WaveformView = ({
|
|
|
4539
5105
|
};
|
|
4540
5106
|
|
|
4541
5107
|
// src/components/ScrollingWaveform.tsx
|
|
4542
|
-
var
|
|
4543
|
-
var
|
|
5108
|
+
var import_react23 = require("react");
|
|
5109
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
4544
5110
|
var ScrollingWaveform = ({
|
|
4545
5111
|
getPeakDb,
|
|
4546
5112
|
active,
|
|
@@ -4548,11 +5114,11 @@ var ScrollingWaveform = ({
|
|
|
4548
5114
|
className,
|
|
4549
5115
|
fillStyle
|
|
4550
5116
|
}) => {
|
|
4551
|
-
const canvasRef = (0,
|
|
4552
|
-
const ringRef = (0,
|
|
4553
|
-
const writeIdxRef = (0,
|
|
4554
|
-
const rafRef = (0,
|
|
4555
|
-
(0,
|
|
5117
|
+
const canvasRef = (0, import_react23.useRef)(null);
|
|
5118
|
+
const ringRef = (0, import_react23.useRef)(new Float32Array(columns));
|
|
5119
|
+
const writeIdxRef = (0, import_react23.useRef)(0);
|
|
5120
|
+
const rafRef = (0, import_react23.useRef)(null);
|
|
5121
|
+
(0, import_react23.useEffect)(() => {
|
|
4556
5122
|
if (ringRef.current.length !== columns) {
|
|
4557
5123
|
const next = new Float32Array(columns);
|
|
4558
5124
|
const prev = ringRef.current;
|
|
@@ -4564,7 +5130,7 @@ var ScrollingWaveform = ({
|
|
|
4564
5130
|
writeIdxRef.current = writeIdxRef.current % columns;
|
|
4565
5131
|
}
|
|
4566
5132
|
}, [columns]);
|
|
4567
|
-
(0,
|
|
5133
|
+
(0, import_react23.useEffect)(() => {
|
|
4568
5134
|
if (!active) {
|
|
4569
5135
|
if (rafRef.current !== null) {
|
|
4570
5136
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -4616,7 +5182,7 @@ var ScrollingWaveform = ({
|
|
|
4616
5182
|
}
|
|
4617
5183
|
};
|
|
4618
5184
|
}, [active, getPeakDb, fillStyle]);
|
|
4619
|
-
return /* @__PURE__ */ (0,
|
|
5185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
4620
5186
|
"canvas",
|
|
4621
5187
|
{
|
|
4622
5188
|
ref: canvasRef,
|
|
@@ -4627,8 +5193,8 @@ var ScrollingWaveform = ({
|
|
|
4627
5193
|
};
|
|
4628
5194
|
|
|
4629
5195
|
// src/components/OffsetScrubber.tsx
|
|
4630
|
-
var
|
|
4631
|
-
var
|
|
5196
|
+
var import_react24 = require("react");
|
|
5197
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
4632
5198
|
var SLIDER_HEIGHT_PX = 28;
|
|
4633
5199
|
var TICK_HEIGHT_PX = 14;
|
|
4634
5200
|
var DOWNBEAT_TICK_HEIGHT_PX = 22;
|
|
@@ -4641,40 +5207,40 @@ function OffsetScrubber({
|
|
|
4641
5207
|
onChange,
|
|
4642
5208
|
disabled = false
|
|
4643
5209
|
}) {
|
|
4644
|
-
const trackRef = (0,
|
|
4645
|
-
const [draftOffset, setDraftOffset] = (0,
|
|
4646
|
-
const [isDragging, setIsDragging] = (0,
|
|
4647
|
-
(0,
|
|
5210
|
+
const trackRef = (0, import_react24.useRef)(null);
|
|
5211
|
+
const [draftOffset, setDraftOffset] = (0, import_react24.useState)(offsetSamples);
|
|
5212
|
+
const [isDragging, setIsDragging] = (0, import_react24.useState)(false);
|
|
5213
|
+
(0, import_react24.useEffect)(() => {
|
|
4648
5214
|
if (!isDragging) setDraftOffset(offsetSamples);
|
|
4649
5215
|
}, [offsetSamples, isDragging]);
|
|
4650
5216
|
const sampleRate = cuePoints?.sample_rate ?? 44100;
|
|
4651
5217
|
const detectedBpm = cuePoints?.detected_bpm ?? projectBpm;
|
|
4652
|
-
const beatsForRange = (0,
|
|
5218
|
+
const beatsForRange = (0, import_react24.useMemo)(() => {
|
|
4653
5219
|
return Math.round(60 / projectBpm * sampleRate);
|
|
4654
5220
|
}, [projectBpm, sampleRate]);
|
|
4655
5221
|
const rangeSamples = beatsForRange * meter;
|
|
4656
|
-
const sampleToFraction = (0,
|
|
5222
|
+
const sampleToFraction = (0, import_react24.useCallback)(
|
|
4657
5223
|
(sample) => {
|
|
4658
5224
|
const clamped = Math.max(-rangeSamples, Math.min(rangeSamples, sample));
|
|
4659
5225
|
return (clamped + rangeSamples) / (2 * rangeSamples);
|
|
4660
5226
|
},
|
|
4661
5227
|
[rangeSamples]
|
|
4662
5228
|
);
|
|
4663
|
-
const fractionToSample = (0,
|
|
5229
|
+
const fractionToSample = (0, import_react24.useCallback)(
|
|
4664
5230
|
(fraction) => {
|
|
4665
5231
|
const clamped = Math.max(0, Math.min(1, fraction));
|
|
4666
5232
|
return Math.round(clamped * 2 * rangeSamples - rangeSamples);
|
|
4667
5233
|
},
|
|
4668
5234
|
[rangeSamples]
|
|
4669
5235
|
);
|
|
4670
|
-
const snapTargets = (0,
|
|
5236
|
+
const snapTargets = (0, import_react24.useMemo)(() => {
|
|
4671
5237
|
if (!cuePoints || cuePoints.beats.length === 0) return [];
|
|
4672
5238
|
const downbeat = cuePoints.beats[0];
|
|
4673
5239
|
const positives = cuePoints.beats.map((b) => b - downbeat);
|
|
4674
5240
|
const negatives = positives.slice(1).map((p) => -p);
|
|
4675
5241
|
return [...negatives, ...positives].sort((a, b) => a - b);
|
|
4676
5242
|
}, [cuePoints]);
|
|
4677
|
-
const snapToBeat = (0,
|
|
5243
|
+
const snapToBeat = (0, import_react24.useCallback)(
|
|
4678
5244
|
(sample) => {
|
|
4679
5245
|
if (snapTargets.length === 0) return sample;
|
|
4680
5246
|
let best = snapTargets[0];
|
|
@@ -4690,7 +5256,7 @@ function OffsetScrubber({
|
|
|
4690
5256
|
},
|
|
4691
5257
|
[snapTargets]
|
|
4692
5258
|
);
|
|
4693
|
-
const handlePointerDown = (0,
|
|
5259
|
+
const handlePointerDown = (0, import_react24.useCallback)(
|
|
4694
5260
|
(e) => {
|
|
4695
5261
|
if (disabled || !cuePoints) return;
|
|
4696
5262
|
e.preventDefault();
|
|
@@ -4724,7 +5290,7 @@ function OffsetScrubber({
|
|
|
4724
5290
|
},
|
|
4725
5291
|
[disabled, cuePoints, fractionToSample, onChange, snapToBeat]
|
|
4726
5292
|
);
|
|
4727
|
-
const handleResetToZero = (0,
|
|
5293
|
+
const handleResetToZero = (0, import_react24.useCallback)(() => {
|
|
4728
5294
|
if (disabled) return;
|
|
4729
5295
|
setDraftOffset(0);
|
|
4730
5296
|
onChange(0);
|
|
@@ -4732,7 +5298,7 @@ function OffsetScrubber({
|
|
|
4732
5298
|
const thumbFraction = sampleToFraction(draftOffset);
|
|
4733
5299
|
const thumbLeftPct = `${(thumbFraction * 100).toFixed(2)}%`;
|
|
4734
5300
|
const bpmMismatch = cuePoints?.detected_bpm != null && Math.abs(cuePoints.detected_bpm - projectBpm) > 1;
|
|
4735
|
-
const ticks = (0,
|
|
5301
|
+
const ticks = (0, import_react24.useMemo)(() => {
|
|
4736
5302
|
if (!cuePoints) return [];
|
|
4737
5303
|
const downbeat = cuePoints.beats[0] ?? 0;
|
|
4738
5304
|
return cuePoints.beats.map((b, i) => {
|
|
@@ -4743,9 +5309,9 @@ function OffsetScrubber({
|
|
|
4743
5309
|
});
|
|
4744
5310
|
}, [cuePoints, sampleToFraction]);
|
|
4745
5311
|
const isDisabled = disabled || !cuePoints || cuePoints.beats.length === 0;
|
|
4746
|
-
return /* @__PURE__ */ (0,
|
|
4747
|
-
/* @__PURE__ */ (0,
|
|
4748
|
-
/* @__PURE__ */ (0,
|
|
5312
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { "data-testid": "offset-scrubber", className: "flex items-center gap-2 w-full", children: [
|
|
5313
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-[9px] text-sas-muted/60 uppercase tracking-wide flex-shrink-0", children: "Align" }),
|
|
5314
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
4749
5315
|
"div",
|
|
4750
5316
|
{
|
|
4751
5317
|
ref: trackRef,
|
|
@@ -4761,7 +5327,7 @@ function OffsetScrubber({
|
|
|
4761
5327
|
"aria-valuenow": draftOffset,
|
|
4762
5328
|
"aria-disabled": isDisabled,
|
|
4763
5329
|
children: [
|
|
4764
|
-
/* @__PURE__ */ (0,
|
|
5330
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
4765
5331
|
"div",
|
|
4766
5332
|
{
|
|
4767
5333
|
"aria-hidden": "true",
|
|
@@ -4769,7 +5335,7 @@ function OffsetScrubber({
|
|
|
4769
5335
|
style: { left: "50%" }
|
|
4770
5336
|
}
|
|
4771
5337
|
),
|
|
4772
|
-
ticks.map((t) => /* @__PURE__ */ (0,
|
|
5338
|
+
ticks.map((t) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
4773
5339
|
"div",
|
|
4774
5340
|
{
|
|
4775
5341
|
"data-testid": t.isDownbeat ? "offset-tick-downbeat" : "offset-tick",
|
|
@@ -4784,7 +5350,7 @@ function OffsetScrubber({
|
|
|
4784
5350
|
},
|
|
4785
5351
|
t.i
|
|
4786
5352
|
)),
|
|
4787
|
-
/* @__PURE__ */ (0,
|
|
5353
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
4788
5354
|
"div",
|
|
4789
5355
|
{
|
|
4790
5356
|
"data-testid": "offset-scrubber-thumb",
|
|
@@ -4801,7 +5367,7 @@ function OffsetScrubber({
|
|
|
4801
5367
|
]
|
|
4802
5368
|
}
|
|
4803
5369
|
),
|
|
4804
|
-
/* @__PURE__ */ (0,
|
|
5370
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
4805
5371
|
"span",
|
|
4806
5372
|
{
|
|
4807
5373
|
"data-testid": "offset-scrubber-readout",
|
|
@@ -4809,7 +5375,7 @@ function OffsetScrubber({
|
|
|
4809
5375
|
children: formatOffset(draftOffset, sampleRate)
|
|
4810
5376
|
}
|
|
4811
5377
|
),
|
|
4812
|
-
/* @__PURE__ */ (0,
|
|
5378
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
4813
5379
|
"button",
|
|
4814
5380
|
{
|
|
4815
5381
|
type: "button",
|
|
@@ -4821,7 +5387,7 @@ function OffsetScrubber({
|
|
|
4821
5387
|
children: "\u2316"
|
|
4822
5388
|
}
|
|
4823
5389
|
),
|
|
4824
|
-
bpmMismatch && /* @__PURE__ */ (0,
|
|
5390
|
+
bpmMismatch && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
4825
5391
|
"span",
|
|
4826
5392
|
{
|
|
4827
5393
|
"data-testid": "offset-bpm-mismatch",
|
|
@@ -4893,16 +5459,16 @@ function synthesizeCuePoints({
|
|
|
4893
5459
|
}
|
|
4894
5460
|
|
|
4895
5461
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
4896
|
-
var
|
|
5462
|
+
var import_react29 = require("react");
|
|
4897
5463
|
|
|
4898
5464
|
// src/hooks/useSceneState.ts
|
|
4899
|
-
var
|
|
5465
|
+
var import_react25 = require("react");
|
|
4900
5466
|
function useSceneState(activeSceneId, initialValue) {
|
|
4901
|
-
const [stateMap, setStateMap] = (0,
|
|
4902
|
-
const activeSceneIdRef = (0,
|
|
5467
|
+
const [stateMap, setStateMap] = (0, import_react25.useState)(() => /* @__PURE__ */ new Map());
|
|
5468
|
+
const activeSceneIdRef = (0, import_react25.useRef)(activeSceneId);
|
|
4903
5469
|
activeSceneIdRef.current = activeSceneId;
|
|
4904
5470
|
const currentValue = activeSceneId !== null && stateMap.has(activeSceneId) ? stateMap.get(activeSceneId) : initialValue;
|
|
4905
|
-
const setForCurrentScene = (0,
|
|
5471
|
+
const setForCurrentScene = (0, import_react25.useCallback)((value) => {
|
|
4906
5472
|
const sid = activeSceneIdRef.current;
|
|
4907
5473
|
if (sid === null) return;
|
|
4908
5474
|
setStateMap((prev) => {
|
|
@@ -4913,7 +5479,7 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
4913
5479
|
return newMap;
|
|
4914
5480
|
});
|
|
4915
5481
|
}, [initialValue]);
|
|
4916
|
-
const setForScene = (0,
|
|
5482
|
+
const setForScene = (0, import_react25.useCallback)((sceneId, value) => {
|
|
4917
5483
|
setStateMap((prev) => {
|
|
4918
5484
|
const current = prev.has(sceneId) ? prev.get(sceneId) : initialValue;
|
|
4919
5485
|
const next = typeof value === "function" ? value(current) : value;
|
|
@@ -4926,10 +5492,10 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
4926
5492
|
}
|
|
4927
5493
|
|
|
4928
5494
|
// src/hooks/useAnySolo.ts
|
|
4929
|
-
var
|
|
5495
|
+
var import_react26 = require("react");
|
|
4930
5496
|
function useAnySolo(host) {
|
|
4931
|
-
const [anySolo, setAnySolo] = (0,
|
|
4932
|
-
(0,
|
|
5497
|
+
const [anySolo, setAnySolo] = (0, import_react26.useState)(false);
|
|
5498
|
+
(0, import_react26.useEffect)(() => {
|
|
4933
5499
|
let active = true;
|
|
4934
5500
|
const refresh = () => {
|
|
4935
5501
|
host.isAnySoloActive().then((v) => {
|
|
@@ -4948,7 +5514,7 @@ function useAnySolo(host) {
|
|
|
4948
5514
|
}
|
|
4949
5515
|
|
|
4950
5516
|
// src/hooks/useSoundHistory.ts
|
|
4951
|
-
var
|
|
5517
|
+
var import_react27 = require("react");
|
|
4952
5518
|
var EMPTY = { entries: [], cursor: -1 };
|
|
4953
5519
|
function sameDescriptor(a, b) {
|
|
4954
5520
|
if (a === b) return true;
|
|
@@ -4960,14 +5526,14 @@ function sameDescriptor(a, b) {
|
|
|
4960
5526
|
}
|
|
4961
5527
|
function useSoundHistory(applySound, opts = {}) {
|
|
4962
5528
|
const max = Math.max(2, opts.max ?? 24);
|
|
4963
|
-
const applyRef = (0,
|
|
5529
|
+
const applyRef = (0, import_react27.useRef)(applySound);
|
|
4964
5530
|
applyRef.current = applySound;
|
|
4965
|
-
const onChangeRef = (0,
|
|
5531
|
+
const onChangeRef = (0, import_react27.useRef)(opts.onChange);
|
|
4966
5532
|
onChangeRef.current = opts.onChange;
|
|
4967
|
-
const dataRef = (0,
|
|
4968
|
-
const [, setVersion] = (0,
|
|
4969
|
-
const bump = (0,
|
|
4970
|
-
const commit = (0,
|
|
5533
|
+
const dataRef = (0, import_react27.useRef)({});
|
|
5534
|
+
const [, setVersion] = (0, import_react27.useState)(0);
|
|
5535
|
+
const bump = (0, import_react27.useCallback)(() => setVersion((v) => v + 1), []);
|
|
5536
|
+
const commit = (0, import_react27.useCallback)(
|
|
4971
5537
|
(trackId, next, notify) => {
|
|
4972
5538
|
dataRef.current = { ...dataRef.current, [trackId]: next };
|
|
4973
5539
|
bump();
|
|
@@ -4975,7 +5541,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
4975
5541
|
},
|
|
4976
5542
|
[bump]
|
|
4977
5543
|
);
|
|
4978
|
-
const record = (0,
|
|
5544
|
+
const record = (0, import_react27.useCallback)(
|
|
4979
5545
|
(trackId, descriptor, label) => {
|
|
4980
5546
|
const h = dataRef.current[trackId];
|
|
4981
5547
|
const current = h && h.cursor >= 0 ? h.entries[h.cursor] : void 0;
|
|
@@ -4990,7 +5556,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
4990
5556
|
},
|
|
4991
5557
|
[max, commit]
|
|
4992
5558
|
);
|
|
4993
|
-
const restoreTo = (0,
|
|
5559
|
+
const restoreTo = (0, import_react27.useCallback)(
|
|
4994
5560
|
async (trackId, index) => {
|
|
4995
5561
|
const h = dataRef.current[trackId];
|
|
4996
5562
|
if (!h || index < 0 || index >= h.entries.length || index === h.cursor) return false;
|
|
@@ -5000,7 +5566,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5000
5566
|
},
|
|
5001
5567
|
[commit]
|
|
5002
5568
|
);
|
|
5003
|
-
const undo = (0,
|
|
5569
|
+
const undo = (0, import_react27.useCallback)(
|
|
5004
5570
|
(trackId) => {
|
|
5005
5571
|
const h = dataRef.current[trackId];
|
|
5006
5572
|
if (!h || h.cursor <= 0) return Promise.resolve(false);
|
|
@@ -5008,7 +5574,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5008
5574
|
},
|
|
5009
5575
|
[restoreTo]
|
|
5010
5576
|
);
|
|
5011
|
-
const toggleFavorite = (0,
|
|
5577
|
+
const toggleFavorite = (0, import_react27.useCallback)(
|
|
5012
5578
|
(trackId, index) => {
|
|
5013
5579
|
const h = dataRef.current[trackId];
|
|
5014
5580
|
if (!h || index < 0 || index >= h.entries.length) return;
|
|
@@ -5017,7 +5583,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5017
5583
|
},
|
|
5018
5584
|
[commit]
|
|
5019
5585
|
);
|
|
5020
|
-
const restore = (0,
|
|
5586
|
+
const restore = (0, import_react27.useCallback)(
|
|
5021
5587
|
(trackId, state) => {
|
|
5022
5588
|
const entries = Array.isArray(state?.entries) ? [...state.entries] : [];
|
|
5023
5589
|
const raw = typeof state?.cursor === "number" ? state.cursor : entries.length - 1;
|
|
@@ -5026,15 +5592,15 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5026
5592
|
},
|
|
5027
5593
|
[commit]
|
|
5028
5594
|
);
|
|
5029
|
-
const list = (0,
|
|
5595
|
+
const list = (0, import_react27.useCallback)(
|
|
5030
5596
|
(trackId) => dataRef.current[trackId] ?? EMPTY,
|
|
5031
5597
|
[]
|
|
5032
5598
|
);
|
|
5033
|
-
const canUndo = (0,
|
|
5599
|
+
const canUndo = (0, import_react27.useCallback)((trackId) => {
|
|
5034
5600
|
const h = dataRef.current[trackId];
|
|
5035
5601
|
return !!h && h.cursor > 0;
|
|
5036
5602
|
}, []);
|
|
5037
|
-
const clear = (0,
|
|
5603
|
+
const clear = (0, import_react27.useCallback)(
|
|
5038
5604
|
(trackId) => {
|
|
5039
5605
|
if (dataRef.current[trackId]) {
|
|
5040
5606
|
const next = { ...dataRef.current };
|
|
@@ -5046,11 +5612,11 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5046
5612
|
},
|
|
5047
5613
|
[bump]
|
|
5048
5614
|
);
|
|
5049
|
-
const reset = (0,
|
|
5615
|
+
const reset = (0, import_react27.useCallback)(() => {
|
|
5050
5616
|
dataRef.current = {};
|
|
5051
5617
|
bump();
|
|
5052
5618
|
}, [bump]);
|
|
5053
|
-
return (0,
|
|
5619
|
+
return (0, import_react27.useMemo)(
|
|
5054
5620
|
() => ({ record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite }),
|
|
5055
5621
|
[record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite]
|
|
5056
5622
|
);
|
|
@@ -5183,7 +5749,7 @@ function resolveTrackGroups(parsedGroups, tracks, getDbId, opts = {}) {
|
|
|
5183
5749
|
}
|
|
5184
5750
|
|
|
5185
5751
|
// src/panel-core/useTransitionOps.ts
|
|
5186
|
-
var
|
|
5752
|
+
var import_react28 = require("react");
|
|
5187
5753
|
function useTransitionOps({
|
|
5188
5754
|
host,
|
|
5189
5755
|
adapter,
|
|
@@ -5200,8 +5766,8 @@ function useTransitionOps({
|
|
|
5200
5766
|
resolvedFades
|
|
5201
5767
|
}) {
|
|
5202
5768
|
const { identity } = adapter;
|
|
5203
|
-
const appliedFadeAutomationRef = (0,
|
|
5204
|
-
const applyCrossfadeAutomation = (0,
|
|
5769
|
+
const appliedFadeAutomationRef = (0, import_react28.useRef)(/* @__PURE__ */ new Set());
|
|
5770
|
+
const applyCrossfadeAutomation = (0, import_react28.useCallback)(
|
|
5205
5771
|
async (originTrackId, targetTrackId, bars, bpm, sliderPos) => {
|
|
5206
5772
|
if (host.setTrackVolumeAutomation) {
|
|
5207
5773
|
const curves = buildCrossfadeVolumeCurves(bars, bpm, sliderPos);
|
|
@@ -5218,7 +5784,7 @@ function useTransitionOps({
|
|
|
5218
5784
|
},
|
|
5219
5785
|
[host]
|
|
5220
5786
|
);
|
|
5221
|
-
const applyFadeAutomation = (0,
|
|
5787
|
+
const applyFadeAutomation = (0, import_react28.useCallback)(
|
|
5222
5788
|
async (trackId, direction, bars, bpm, sliderPos, gesture) => {
|
|
5223
5789
|
if (!host.setTrackVolumeAutomation) return;
|
|
5224
5790
|
const points = buildFadeVolumeCurve(bars, bpm, direction, sliderPos, gesture);
|
|
@@ -5227,8 +5793,8 @@ function useTransitionOps({
|
|
|
5227
5793
|
},
|
|
5228
5794
|
[host]
|
|
5229
5795
|
);
|
|
5230
|
-
const [isCreatingCrossfade, setIsCreatingCrossfade] = (0,
|
|
5231
|
-
const handleCreateCrossfade = (0,
|
|
5796
|
+
const [isCreatingCrossfade, setIsCreatingCrossfade] = (0, import_react28.useState)(false);
|
|
5797
|
+
const handleCreateCrossfade = (0, import_react28.useCallback)(
|
|
5232
5798
|
async (origin, target) => {
|
|
5233
5799
|
const scene = activeSceneId;
|
|
5234
5800
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -5356,8 +5922,8 @@ function useTransitionOps({
|
|
|
5356
5922
|
loadTracks
|
|
5357
5923
|
]
|
|
5358
5924
|
);
|
|
5359
|
-
const [isCreatingFade, setIsCreatingFade] = (0,
|
|
5360
|
-
const handleCreateFade = (0,
|
|
5925
|
+
const [isCreatingFade, setIsCreatingFade] = (0, import_react28.useState)(false);
|
|
5926
|
+
const handleCreateFade = (0, import_react28.useCallback)(
|
|
5361
5927
|
async (selection, direction, gesture) => {
|
|
5362
5928
|
const scene = activeSceneId;
|
|
5363
5929
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -5467,7 +6033,7 @@ function useTransitionOps({
|
|
|
5467
6033
|
loadTracks
|
|
5468
6034
|
]
|
|
5469
6035
|
);
|
|
5470
|
-
const handleCrossfadeMute = (0,
|
|
6036
|
+
const handleCrossfadeMute = (0, import_react28.useCallback)(
|
|
5471
6037
|
(pair) => {
|
|
5472
6038
|
const newMuted = !pair.origin.runtimeState.muted;
|
|
5473
6039
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -5482,7 +6048,7 @@ function useTransitionOps({
|
|
|
5482
6048
|
},
|
|
5483
6049
|
[host, setTracks]
|
|
5484
6050
|
);
|
|
5485
|
-
const handleCrossfadeSolo = (0,
|
|
6051
|
+
const handleCrossfadeSolo = (0, import_react28.useCallback)(
|
|
5486
6052
|
(pair) => {
|
|
5487
6053
|
const newSolo = !pair.origin.runtimeState.solo;
|
|
5488
6054
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -5497,7 +6063,7 @@ function useTransitionOps({
|
|
|
5497
6063
|
},
|
|
5498
6064
|
[host, setTracks]
|
|
5499
6065
|
);
|
|
5500
|
-
const handleCrossfadeDelete = (0,
|
|
6066
|
+
const handleCrossfadeDelete = (0, import_react28.useCallback)(
|
|
5501
6067
|
async (pair) => {
|
|
5502
6068
|
try {
|
|
5503
6069
|
for (const member of [pair.origin, pair.target]) {
|
|
@@ -5523,8 +6089,8 @@ function useTransitionOps({
|
|
|
5523
6089
|
},
|
|
5524
6090
|
[host, activeSceneId, setCrossfadePairsMeta, setTracks]
|
|
5525
6091
|
);
|
|
5526
|
-
const crossfadeSliderTimers = (0,
|
|
5527
|
-
const handleCrossfadeSlider = (0,
|
|
6092
|
+
const crossfadeSliderTimers = (0, import_react28.useRef)({});
|
|
6093
|
+
const handleCrossfadeSlider = (0, import_react28.useCallback)(
|
|
5528
6094
|
(pair, pos) => {
|
|
5529
6095
|
setCrossfadePairsMeta(
|
|
5530
6096
|
(prev) => prev.map((p) => p.groupId === pair.groupId ? { ...p, sliderPos: pos } : p)
|
|
@@ -5557,7 +6123,7 @@ function useTransitionOps({
|
|
|
5557
6123
|
},
|
|
5558
6124
|
[host, activeSceneId, applyCrossfadeAutomation, setCrossfadePairsMeta]
|
|
5559
6125
|
);
|
|
5560
|
-
const handleFadeDelete = (0,
|
|
6126
|
+
const handleFadeDelete = (0, import_react28.useCallback)(
|
|
5561
6127
|
async (fade) => {
|
|
5562
6128
|
try {
|
|
5563
6129
|
await host.deleteTrack(fade.track.handle.id);
|
|
@@ -5577,8 +6143,8 @@ function useTransitionOps({
|
|
|
5577
6143
|
},
|
|
5578
6144
|
[host, activeSceneId, setFadesMeta, setTracks]
|
|
5579
6145
|
);
|
|
5580
|
-
const fadeSliderTimers = (0,
|
|
5581
|
-
const handleFadeSlider = (0,
|
|
6146
|
+
const fadeSliderTimers = (0, import_react28.useRef)({});
|
|
6147
|
+
const handleFadeSlider = (0, import_react28.useCallback)(
|
|
5582
6148
|
(fade, pos) => {
|
|
5583
6149
|
setFadesMeta(
|
|
5584
6150
|
(prev) => prev.map((f) => f.dbId === fade.dbId ? { ...f, meta: { ...f.meta, sliderPos: pos } } : f)
|
|
@@ -5608,8 +6174,8 @@ function useTransitionOps({
|
|
|
5608
6174
|
},
|
|
5609
6175
|
[host, activeSceneId, applyFadeAutomation, setFadesMeta]
|
|
5610
6176
|
);
|
|
5611
|
-
const lastResyncKeyRef = (0,
|
|
5612
|
-
(0,
|
|
6177
|
+
const lastResyncKeyRef = (0, import_react28.useRef)("");
|
|
6178
|
+
(0, import_react28.useEffect)(() => {
|
|
5613
6179
|
if (!host.getTrackSound || resolvedCrossfadePairs.length === 0 && resolvedFades.length === 0) {
|
|
5614
6180
|
return;
|
|
5615
6181
|
}
|
|
@@ -5650,7 +6216,7 @@ function useTransitionOps({
|
|
|
5650
6216
|
cancelled = true;
|
|
5651
6217
|
};
|
|
5652
6218
|
}, [resolvedCrossfadePairs, resolvedFades, host, adapter]);
|
|
5653
|
-
(0,
|
|
6219
|
+
(0, import_react28.useEffect)(() => {
|
|
5654
6220
|
if (!host.setTrackVolumeAutomation || resolvedFades.length === 0) return;
|
|
5655
6221
|
void (async () => {
|
|
5656
6222
|
const mc = await host.getMusicalContext();
|
|
@@ -5684,7 +6250,7 @@ function useTransitionOps({
|
|
|
5684
6250
|
}
|
|
5685
6251
|
|
|
5686
6252
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
5687
|
-
var
|
|
6253
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
5688
6254
|
var EMPTY_PLACEHOLDERS = [];
|
|
5689
6255
|
function useGeneratorPanelCore({
|
|
5690
6256
|
ui,
|
|
@@ -5704,8 +6270,8 @@ function useGeneratorPanelCore({
|
|
|
5704
6270
|
} = ui;
|
|
5705
6271
|
const { identity, features } = adapter;
|
|
5706
6272
|
const logTag = identity.logTag;
|
|
5707
|
-
const adapterRef = (0,
|
|
5708
|
-
(0,
|
|
6273
|
+
const adapterRef = (0, import_react29.useRef)(adapter);
|
|
6274
|
+
(0, import_react29.useEffect)(() => {
|
|
5709
6275
|
if (adapterRef.current !== adapter) {
|
|
5710
6276
|
adapterRef.current = adapter;
|
|
5711
6277
|
console.warn(
|
|
@@ -5715,27 +6281,27 @@ function useGeneratorPanelCore({
|
|
|
5715
6281
|
}, [adapter, logTag]);
|
|
5716
6282
|
const supportsMeters = typeof host.getTrackLevels === "function";
|
|
5717
6283
|
const trackLevels = useTrackLevels(host, isExpanded);
|
|
5718
|
-
const [tracks, setTracks] = (0,
|
|
5719
|
-
const [isLoadingTracks, setIsLoadingTracks] = (0,
|
|
5720
|
-
const [importOpen, setImportOpen] = (0,
|
|
5721
|
-
const [soundImportTarget, setSoundImportTarget] = (0,
|
|
5722
|
-
const [designerView, setDesignerView] = (0,
|
|
5723
|
-
const [transitionSourceTotal, setTransitionSourceTotal] = (0,
|
|
5724
|
-
const [crossfadePairsMeta, setCrossfadePairsMeta] = (0,
|
|
5725
|
-
const [fadesMeta, setFadesMeta] = (0,
|
|
5726
|
-
const [genericGroupMetas, setGenericGroupMetas] = (0,
|
|
6284
|
+
const [tracks, setTracks] = (0, import_react29.useState)([]);
|
|
6285
|
+
const [isLoadingTracks, setIsLoadingTracks] = (0, import_react29.useState)(false);
|
|
6286
|
+
const [importOpen, setImportOpen] = (0, import_react29.useState)(false);
|
|
6287
|
+
const [soundImportTarget, setSoundImportTarget] = (0, import_react29.useState)(null);
|
|
6288
|
+
const [designerView, setDesignerView] = (0, import_react29.useState)(false);
|
|
6289
|
+
const [transitionSourceTotal, setTransitionSourceTotal] = (0, import_react29.useState)(0);
|
|
6290
|
+
const [crossfadePairsMeta, setCrossfadePairsMeta] = (0, import_react29.useState)([]);
|
|
6291
|
+
const [fadesMeta, setFadesMeta] = (0, import_react29.useState)([]);
|
|
6292
|
+
const [genericGroupMetas, setGenericGroupMetas] = (0, import_react29.useState)({});
|
|
5727
6293
|
const [isComposing, , setIsComposingForScene] = useSceneState(activeSceneId, false);
|
|
5728
6294
|
const [placeholders, , setPlaceholdersForScene] = useSceneState(
|
|
5729
6295
|
activeSceneId,
|
|
5730
6296
|
EMPTY_PLACEHOLDERS
|
|
5731
6297
|
);
|
|
5732
|
-
const saveTimeoutRefs = (0,
|
|
5733
|
-
const editLoadStartedRef = (0,
|
|
5734
|
-
const [availableInstruments, setAvailableInstruments] = (0,
|
|
5735
|
-
const [instrumentsLoading, setInstrumentsLoading] = (0,
|
|
5736
|
-
const engineToDbIdRef = (0,
|
|
5737
|
-
const tracksLoadedForSceneRef = (0,
|
|
5738
|
-
const persistSoundHistory = (0,
|
|
6298
|
+
const saveTimeoutRefs = (0, import_react29.useRef)({});
|
|
6299
|
+
const editLoadStartedRef = (0, import_react29.useRef)(/* @__PURE__ */ new Set());
|
|
6300
|
+
const [availableInstruments, setAvailableInstruments] = (0, import_react29.useState)([]);
|
|
6301
|
+
const [instrumentsLoading, setInstrumentsLoading] = (0, import_react29.useState)(false);
|
|
6302
|
+
const engineToDbIdRef = (0, import_react29.useRef)(/* @__PURE__ */ new Map());
|
|
6303
|
+
const tracksLoadedForSceneRef = (0, import_react29.useRef)(null);
|
|
6304
|
+
const persistSoundHistory = (0, import_react29.useCallback)(
|
|
5739
6305
|
(trackId, state) => {
|
|
5740
6306
|
if (!activeSceneId) return;
|
|
5741
6307
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -5755,7 +6321,7 @@ function useGeneratorPanelCore({
|
|
|
5755
6321
|
setItems: setTracks,
|
|
5756
6322
|
getId: (t) => t.handle.dbId
|
|
5757
6323
|
});
|
|
5758
|
-
const loadTracks = (0,
|
|
6324
|
+
const loadTracks = (0, import_react29.useCallback)(
|
|
5759
6325
|
async (incremental = false) => {
|
|
5760
6326
|
const sceneAtStart = activeSceneId;
|
|
5761
6327
|
if (!sceneAtStart) {
|
|
@@ -5880,18 +6446,18 @@ function useGeneratorPanelCore({
|
|
|
5880
6446
|
},
|
|
5881
6447
|
[host, activeSceneId, soundHistory, adapter, logTag]
|
|
5882
6448
|
);
|
|
5883
|
-
(0,
|
|
6449
|
+
(0, import_react29.useEffect)(() => {
|
|
5884
6450
|
loadTracks();
|
|
5885
6451
|
}, [loadTracks]);
|
|
5886
|
-
(0,
|
|
6452
|
+
(0, import_react29.useEffect)(() => {
|
|
5887
6453
|
const map = /* @__PURE__ */ new Map();
|
|
5888
6454
|
for (const t of tracks) {
|
|
5889
6455
|
map.set(t.handle.id, t.handle.dbId);
|
|
5890
6456
|
}
|
|
5891
6457
|
engineToDbIdRef.current = map;
|
|
5892
6458
|
}, [tracks]);
|
|
5893
|
-
const loadedCompletedIdsRef = (0,
|
|
5894
|
-
(0,
|
|
6459
|
+
const loadedCompletedIdsRef = (0, import_react29.useRef)(/* @__PURE__ */ new Set());
|
|
6460
|
+
(0, import_react29.useEffect)(() => {
|
|
5895
6461
|
if (placeholders.length === 0) {
|
|
5896
6462
|
loadedCompletedIdsRef.current.clear();
|
|
5897
6463
|
return;
|
|
@@ -5910,16 +6476,16 @@ function useGeneratorPanelCore({
|
|
|
5910
6476
|
loadTracks(true);
|
|
5911
6477
|
}
|
|
5912
6478
|
}, [placeholders, loadTracks, logTag]);
|
|
5913
|
-
const adoptAndLoad = (0,
|
|
6479
|
+
const adoptAndLoad = (0, import_react29.useCallback)(() => {
|
|
5914
6480
|
loadTracks(true);
|
|
5915
6481
|
}, [loadTracks]);
|
|
5916
|
-
(0,
|
|
6482
|
+
(0, import_react29.useEffect)(() => {
|
|
5917
6483
|
const unsub = host.onEngineReady(() => {
|
|
5918
6484
|
adoptAndLoad();
|
|
5919
6485
|
});
|
|
5920
6486
|
return unsub;
|
|
5921
6487
|
}, [host, adoptAndLoad]);
|
|
5922
|
-
(0,
|
|
6488
|
+
(0, import_react29.useEffect)(() => {
|
|
5923
6489
|
if (typeof host.onAfterAgentMutation !== "function") return;
|
|
5924
6490
|
let timer = null;
|
|
5925
6491
|
const unsub = host.onAfterAgentMutation(() => {
|
|
@@ -5934,13 +6500,13 @@ function useGeneratorPanelCore({
|
|
|
5934
6500
|
if (timer) clearTimeout(timer);
|
|
5935
6501
|
};
|
|
5936
6502
|
}, [host, loadTracks]);
|
|
5937
|
-
(0,
|
|
6503
|
+
(0, import_react29.useEffect)(() => {
|
|
5938
6504
|
const unsub = host.onTrackStateChange((trackId, state) => {
|
|
5939
6505
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: state } : t));
|
|
5940
6506
|
});
|
|
5941
6507
|
return unsub;
|
|
5942
6508
|
}, [host]);
|
|
5943
|
-
(0,
|
|
6509
|
+
(0, import_react29.useEffect)(() => {
|
|
5944
6510
|
if (!features.bulkComposePlaceholders) return;
|
|
5945
6511
|
console.log(`[${logTag}] Subscribing to composeProgress`);
|
|
5946
6512
|
const unsub = host.onComposeProgress((event) => {
|
|
@@ -5974,7 +6540,7 @@ function useGeneratorPanelCore({
|
|
|
5974
6540
|
});
|
|
5975
6541
|
return unsub;
|
|
5976
6542
|
}, [host, setIsComposingForScene, setPlaceholdersForScene, features.bulkComposePlaceholders, logTag]);
|
|
5977
|
-
(0,
|
|
6543
|
+
(0, import_react29.useEffect)(() => {
|
|
5978
6544
|
const refs = saveTimeoutRefs;
|
|
5979
6545
|
return () => {
|
|
5980
6546
|
for (const timeout of Object.values(refs.current)) {
|
|
@@ -5982,9 +6548,9 @@ function useGeneratorPanelCore({
|
|
|
5982
6548
|
}
|
|
5983
6549
|
};
|
|
5984
6550
|
}, []);
|
|
5985
|
-
const isAddingTrackRef = (0,
|
|
5986
|
-
const [isAddingTrack, setIsAddingTrack] = (0,
|
|
5987
|
-
const handleAddTrack = (0,
|
|
6551
|
+
const isAddingTrackRef = (0, import_react29.useRef)(false);
|
|
6552
|
+
const [isAddingTrack, setIsAddingTrack] = (0, import_react29.useState)(false);
|
|
6553
|
+
const handleAddTrack = (0, import_react29.useCallback)(async () => {
|
|
5988
6554
|
if (isAddingTrackRef.current) return;
|
|
5989
6555
|
if (!activeSceneId) {
|
|
5990
6556
|
host.showToast("warning", "Select SCENE");
|
|
@@ -6024,7 +6590,7 @@ function useGeneratorPanelCore({
|
|
|
6024
6590
|
setIsAddingTrack(false);
|
|
6025
6591
|
}
|
|
6026
6592
|
}, [host, adapter, identity, activeSceneId, isConnected, isAuthenticated, tracks.length, onExpandSelf]);
|
|
6027
|
-
const handlePortTrack = (0,
|
|
6593
|
+
const handlePortTrack = (0, import_react29.useCallback)(
|
|
6028
6594
|
async (sel) => {
|
|
6029
6595
|
if (!activeSceneId) {
|
|
6030
6596
|
host.showToast("warning", "Select SCENE");
|
|
@@ -6081,7 +6647,7 @@ function useGeneratorPanelCore({
|
|
|
6081
6647
|
},
|
|
6082
6648
|
[host, adapter, identity, activeSceneId, isConnected, tracks.length, loadTracks]
|
|
6083
6649
|
);
|
|
6084
|
-
const handleSoundImportPick = (0,
|
|
6650
|
+
const handleSoundImportPick = (0, import_react29.useCallback)(
|
|
6085
6651
|
async (sel) => {
|
|
6086
6652
|
const target = soundImportTarget;
|
|
6087
6653
|
if (!target || !host.getTrackSound) {
|
|
@@ -6112,8 +6678,8 @@ function useGeneratorPanelCore({
|
|
|
6112
6678
|
},
|
|
6113
6679
|
[soundImportTarget, host, adapter, identity.familyKey, soundHistory]
|
|
6114
6680
|
);
|
|
6115
|
-
const [isExportingMidi, setIsExportingMidi] = (0,
|
|
6116
|
-
const handleExportMidi = (0,
|
|
6681
|
+
const [isExportingMidi, setIsExportingMidi] = (0, import_react29.useState)(false);
|
|
6682
|
+
const handleExportMidi = (0, import_react29.useCallback)(async () => {
|
|
6117
6683
|
if (isExportingMidi) return;
|
|
6118
6684
|
setIsExportingMidi(true);
|
|
6119
6685
|
try {
|
|
@@ -6144,10 +6710,10 @@ function useGeneratorPanelCore({
|
|
|
6144
6710
|
const xfFromId = sceneContext?.transitionFromSceneId ?? null;
|
|
6145
6711
|
const xfToId = sceneContext?.transitionToSceneId ?? null;
|
|
6146
6712
|
const canCrossfade = features.transitionDesigner && sceneContext?.sceneType === "transition" && !!xfFromId && !!xfToId && !!host.listSceneFamilyTracks;
|
|
6147
|
-
(0,
|
|
6713
|
+
(0, import_react29.useEffect)(() => {
|
|
6148
6714
|
if (!canCrossfade) setDesignerView(false);
|
|
6149
6715
|
}, [canCrossfade]);
|
|
6150
|
-
(0,
|
|
6716
|
+
(0, import_react29.useEffect)(() => {
|
|
6151
6717
|
if (!canCrossfade || !xfFromId || !xfToId || !host.listSceneFamilyTracks) {
|
|
6152
6718
|
setTransitionSourceTotal(0);
|
|
6153
6719
|
return;
|
|
@@ -6163,12 +6729,12 @@ function useGeneratorPanelCore({
|
|
|
6163
6729
|
};
|
|
6164
6730
|
}, [canCrossfade, xfFromId, xfToId, host]);
|
|
6165
6731
|
const transitionDone = crossfadePairsMeta.length * 2 + fadesMeta.length;
|
|
6166
|
-
(0,
|
|
6732
|
+
(0, import_react29.useEffect)(() => {
|
|
6167
6733
|
if (!onHeaderContent) return;
|
|
6168
6734
|
const addDisabled = needsContract || !isConnected || !activeSceneId || tracks.length >= identity.maxTracks || isAddingTrack;
|
|
6169
6735
|
onHeaderContent(
|
|
6170
|
-
/* @__PURE__ */ (0,
|
|
6171
|
-
features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */ (0,
|
|
6736
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex gap-1 items-center", children: [
|
|
6737
|
+
features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
6172
6738
|
"button",
|
|
6173
6739
|
{
|
|
6174
6740
|
"data-testid": `import-from-scene-${identity.familyKey}-button`,
|
|
@@ -6182,7 +6748,7 @@ function useGeneratorPanelCore({
|
|
|
6182
6748
|
children: identity.importTrackLabel ?? "Import Track"
|
|
6183
6749
|
}
|
|
6184
6750
|
),
|
|
6185
|
-
(!canCrossfade || !designerView) && /* @__PURE__ */ (0,
|
|
6751
|
+
(!canCrossfade || !designerView) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
6186
6752
|
"button",
|
|
6187
6753
|
{
|
|
6188
6754
|
"data-testid": `add-${identity.familyKey}-track-button`,
|
|
@@ -6198,7 +6764,7 @@ function useGeneratorPanelCore({
|
|
|
6198
6764
|
children: identity.addTrackLabel ?? "Add Track"
|
|
6199
6765
|
}
|
|
6200
6766
|
),
|
|
6201
|
-
canCrossfade && /* @__PURE__ */ (0,
|
|
6767
|
+
canCrossfade && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
6202
6768
|
"button",
|
|
6203
6769
|
{
|
|
6204
6770
|
"data-testid": `${identity.familyKey}-view-toggle`,
|
|
@@ -6217,7 +6783,7 @@ function useGeneratorPanelCore({
|
|
|
6217
6783
|
title: designerView ? "Back to the track list" : "Open the transition designer",
|
|
6218
6784
|
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",
|
|
6219
6785
|
children: [
|
|
6220
|
-
transitionSourceTotal > 0 && /* @__PURE__ */ (0,
|
|
6786
|
+
transitionSourceTotal > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
6221
6787
|
"span",
|
|
6222
6788
|
{
|
|
6223
6789
|
className: "absolute inset-y-0 left-0 bg-sas-accent/25",
|
|
@@ -6225,7 +6791,7 @@ function useGeneratorPanelCore({
|
|
|
6225
6791
|
"aria-hidden": true
|
|
6226
6792
|
}
|
|
6227
6793
|
),
|
|
6228
|
-
/* @__PURE__ */ (0,
|
|
6794
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "relative", children: [
|
|
6229
6795
|
"\u21C4 ",
|
|
6230
6796
|
designerView ? "Transition" : "Tracks",
|
|
6231
6797
|
transitionSourceTotal > 0 ? ` ${transitionDone}/${transitionSourceTotal}` : ""
|
|
@@ -6256,7 +6822,7 @@ function useGeneratorPanelCore({
|
|
|
6256
6822
|
identity,
|
|
6257
6823
|
features.importTracks
|
|
6258
6824
|
]);
|
|
6259
|
-
(0,
|
|
6825
|
+
(0, import_react29.useEffect)(() => {
|
|
6260
6826
|
if (!onLoading) return;
|
|
6261
6827
|
const anyGenerating = tracks.some((t) => t.isGenerating);
|
|
6262
6828
|
onLoading(isLoadingTracks || anyGenerating || isBulkActive);
|
|
@@ -6264,7 +6830,7 @@ function useGeneratorPanelCore({
|
|
|
6264
6830
|
onLoading(false);
|
|
6265
6831
|
};
|
|
6266
6832
|
}, [onLoading, isLoadingTracks, tracks, isBulkActive]);
|
|
6267
|
-
const handleDeleteTrack = (0,
|
|
6833
|
+
const handleDeleteTrack = (0, import_react29.useCallback)(
|
|
6268
6834
|
async (trackId) => {
|
|
6269
6835
|
try {
|
|
6270
6836
|
await host.deleteTrack(trackId);
|
|
@@ -6280,7 +6846,7 @@ function useGeneratorPanelCore({
|
|
|
6280
6846
|
},
|
|
6281
6847
|
[host, activeSceneId]
|
|
6282
6848
|
);
|
|
6283
|
-
const handlePromptChange = (0,
|
|
6849
|
+
const handlePromptChange = (0, import_react29.useCallback)(
|
|
6284
6850
|
(trackId, prompt) => {
|
|
6285
6851
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, prompt } : t));
|
|
6286
6852
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -6296,7 +6862,7 @@ function useGeneratorPanelCore({
|
|
|
6296
6862
|
},
|
|
6297
6863
|
[host, activeSceneId]
|
|
6298
6864
|
);
|
|
6299
|
-
const resolvedGenericGroups = (0,
|
|
6865
|
+
const resolvedGenericGroups = (0, import_react29.useMemo)(() => {
|
|
6300
6866
|
const out = {};
|
|
6301
6867
|
for (const ext of adapter.groupExtensions ?? []) {
|
|
6302
6868
|
out[ext.metaKey] = resolveTrackGroups(
|
|
@@ -6310,18 +6876,18 @@ function useGeneratorPanelCore({
|
|
|
6310
6876
|
}
|
|
6311
6877
|
return out;
|
|
6312
6878
|
}, [adapter, genericGroupMetas, tracks]);
|
|
6313
|
-
const genericGroupMemberDbIds = (0,
|
|
6879
|
+
const genericGroupMemberDbIds = (0, import_react29.useMemo)(() => {
|
|
6314
6880
|
const s = /* @__PURE__ */ new Set();
|
|
6315
6881
|
for (const r of Object.values(resolvedGenericGroups)) {
|
|
6316
6882
|
for (const dbId of r.memberDbIds) s.add(dbId);
|
|
6317
6883
|
}
|
|
6318
6884
|
return s;
|
|
6319
6885
|
}, [resolvedGenericGroups]);
|
|
6320
|
-
const engineToDbId = (0,
|
|
6886
|
+
const engineToDbId = (0, import_react29.useCallback)(
|
|
6321
6887
|
(trackId) => engineToDbIdRef.current.get(trackId) ?? trackId,
|
|
6322
6888
|
[]
|
|
6323
6889
|
);
|
|
6324
|
-
const updateTrack = (0,
|
|
6890
|
+
const updateTrack = (0, import_react29.useCallback)(
|
|
6325
6891
|
(trackId, patch) => {
|
|
6326
6892
|
setTracks(
|
|
6327
6893
|
(prev) => prev.map(
|
|
@@ -6331,18 +6897,18 @@ function useGeneratorPanelCore({
|
|
|
6331
6897
|
},
|
|
6332
6898
|
[]
|
|
6333
6899
|
);
|
|
6334
|
-
const markEditLoaded = (0,
|
|
6900
|
+
const markEditLoaded = (0, import_react29.useCallback)((trackId) => {
|
|
6335
6901
|
editLoadStartedRef.current.add(trackId);
|
|
6336
6902
|
}, []);
|
|
6337
|
-
const tracksRef = (0,
|
|
6338
|
-
(0,
|
|
6903
|
+
const tracksRef = (0, import_react29.useRef)(tracks);
|
|
6904
|
+
(0, import_react29.useEffect)(() => {
|
|
6339
6905
|
tracksRef.current = tracks;
|
|
6340
6906
|
}, [tracks]);
|
|
6341
|
-
const resolvedGenericGroupsRef = (0,
|
|
6342
|
-
(0,
|
|
6907
|
+
const resolvedGenericGroupsRef = (0, import_react29.useRef)(resolvedGenericGroups);
|
|
6908
|
+
(0, import_react29.useEffect)(() => {
|
|
6343
6909
|
resolvedGenericGroupsRef.current = resolvedGenericGroups;
|
|
6344
6910
|
}, [resolvedGenericGroups]);
|
|
6345
|
-
const makeServices = (0,
|
|
6911
|
+
const makeServices = (0, import_react29.useCallback)(() => {
|
|
6346
6912
|
return {
|
|
6347
6913
|
host,
|
|
6348
6914
|
activeSceneId,
|
|
@@ -6361,7 +6927,7 @@ function useGeneratorPanelCore({
|
|
|
6361
6927
|
resolvedGroups: (metaKey) => resolvedGenericGroupsRef.current[metaKey]?.resolved ?? []
|
|
6362
6928
|
};
|
|
6363
6929
|
}, [host, activeSceneId, updateTrack, loadTracks, soundHistory, engineToDbId, markEditLoaded, identity, adapter]);
|
|
6364
|
-
const handleGenerate = (0,
|
|
6930
|
+
const handleGenerate = (0, import_react29.useCallback)(
|
|
6365
6931
|
async (trackId) => {
|
|
6366
6932
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6367
6933
|
if (!track || !track.prompt.trim()) return;
|
|
@@ -6388,7 +6954,7 @@ function useGeneratorPanelCore({
|
|
|
6388
6954
|
},
|
|
6389
6955
|
[host, adapter, tracks, isAuthenticated, makeServices]
|
|
6390
6956
|
);
|
|
6391
|
-
const handleMuteToggle = (0,
|
|
6957
|
+
const handleMuteToggle = (0, import_react29.useCallback)(
|
|
6392
6958
|
(trackId) => {
|
|
6393
6959
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6394
6960
|
if (!track) return;
|
|
@@ -6408,7 +6974,7 @@ function useGeneratorPanelCore({
|
|
|
6408
6974
|
},
|
|
6409
6975
|
[host, tracks]
|
|
6410
6976
|
);
|
|
6411
|
-
const handleSoloToggle = (0,
|
|
6977
|
+
const handleSoloToggle = (0, import_react29.useCallback)(
|
|
6412
6978
|
(trackId) => {
|
|
6413
6979
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6414
6980
|
if (!track) return;
|
|
@@ -6428,7 +6994,7 @@ function useGeneratorPanelCore({
|
|
|
6428
6994
|
},
|
|
6429
6995
|
[host, tracks]
|
|
6430
6996
|
);
|
|
6431
|
-
const handleVolumeChange = (0,
|
|
6997
|
+
const handleVolumeChange = (0, import_react29.useCallback)(
|
|
6432
6998
|
(trackId, volume) => {
|
|
6433
6999
|
setTracks(
|
|
6434
7000
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, volume } } : t)
|
|
@@ -6438,7 +7004,7 @@ function useGeneratorPanelCore({
|
|
|
6438
7004
|
},
|
|
6439
7005
|
[host]
|
|
6440
7006
|
);
|
|
6441
|
-
const handlePanChange = (0,
|
|
7007
|
+
const handlePanChange = (0, import_react29.useCallback)(
|
|
6442
7008
|
(trackId, pan) => {
|
|
6443
7009
|
setTracks(
|
|
6444
7010
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, pan } } : t)
|
|
@@ -6448,7 +7014,7 @@ function useGeneratorPanelCore({
|
|
|
6448
7014
|
},
|
|
6449
7015
|
[host]
|
|
6450
7016
|
);
|
|
6451
|
-
const handleShuffle = (0,
|
|
7017
|
+
const handleShuffle = (0, import_react29.useCallback)(
|
|
6452
7018
|
async (trackId) => {
|
|
6453
7019
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6454
7020
|
if (!track) return;
|
|
@@ -6490,7 +7056,7 @@ function useGeneratorPanelCore({
|
|
|
6490
7056
|
},
|
|
6491
7057
|
[host, adapter, tracks, soundHistory, logTag]
|
|
6492
7058
|
);
|
|
6493
|
-
const handleCopy = (0,
|
|
7059
|
+
const handleCopy = (0, import_react29.useCallback)(
|
|
6494
7060
|
async (trackId) => {
|
|
6495
7061
|
try {
|
|
6496
7062
|
const newHandle = await host.duplicateTrack(trackId);
|
|
@@ -6503,7 +7069,7 @@ function useGeneratorPanelCore({
|
|
|
6503
7069
|
},
|
|
6504
7070
|
[host, loadTracks]
|
|
6505
7071
|
);
|
|
6506
|
-
const handleFxToggle = (0,
|
|
7072
|
+
const handleFxToggle = (0, import_react29.useCallback)(
|
|
6507
7073
|
(trackId, category, enabled) => {
|
|
6508
7074
|
setTracks(
|
|
6509
7075
|
(prev) => prev.map(
|
|
@@ -6526,7 +7092,7 @@ function useGeneratorPanelCore({
|
|
|
6526
7092
|
},
|
|
6527
7093
|
[host]
|
|
6528
7094
|
);
|
|
6529
|
-
const handleFxPresetChange = (0,
|
|
7095
|
+
const handleFxPresetChange = (0, import_react29.useCallback)(
|
|
6530
7096
|
(trackId, category, presetIndex) => {
|
|
6531
7097
|
setTracks(
|
|
6532
7098
|
(prev) => prev.map(
|
|
@@ -6552,7 +7118,7 @@ function useGeneratorPanelCore({
|
|
|
6552
7118
|
},
|
|
6553
7119
|
[host]
|
|
6554
7120
|
);
|
|
6555
|
-
const handleFxDryWetChange = (0,
|
|
7121
|
+
const handleFxDryWetChange = (0, import_react29.useCallback)(
|
|
6556
7122
|
(trackId, category, value) => {
|
|
6557
7123
|
setTracks(
|
|
6558
7124
|
(prev) => prev.map(
|
|
@@ -6564,7 +7130,7 @@ function useGeneratorPanelCore({
|
|
|
6564
7130
|
},
|
|
6565
7131
|
[host]
|
|
6566
7132
|
);
|
|
6567
|
-
const toggleFxDrawer = (0,
|
|
7133
|
+
const toggleFxDrawer = (0, import_react29.useCallback)(
|
|
6568
7134
|
(trackId) => {
|
|
6569
7135
|
setTracks(
|
|
6570
7136
|
(prev) => prev.map((t) => {
|
|
@@ -6586,7 +7152,7 @@ function useGeneratorPanelCore({
|
|
|
6586
7152
|
},
|
|
6587
7153
|
[host, tracks]
|
|
6588
7154
|
);
|
|
6589
|
-
const loadEditNotes = (0,
|
|
7155
|
+
const loadEditNotes = (0, import_react29.useCallback)(
|
|
6590
7156
|
async (trackId) => {
|
|
6591
7157
|
try {
|
|
6592
7158
|
const mc = await host.getMusicalContext();
|
|
@@ -6604,7 +7170,7 @@ function useGeneratorPanelCore({
|
|
|
6604
7170
|
},
|
|
6605
7171
|
[host, logTag]
|
|
6606
7172
|
);
|
|
6607
|
-
const handleNotesChange = (0,
|
|
7173
|
+
const handleNotesChange = (0, import_react29.useCallback)(
|
|
6608
7174
|
(trackId, notes) => {
|
|
6609
7175
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editNotes: notes } : t));
|
|
6610
7176
|
const key = `edit:${trackId}`;
|
|
@@ -6634,7 +7200,7 @@ function useGeneratorPanelCore({
|
|
|
6634
7200
|
},
|
|
6635
7201
|
[host]
|
|
6636
7202
|
);
|
|
6637
|
-
const handleTabChange = (0,
|
|
7203
|
+
const handleTabChange = (0, import_react29.useCallback)(
|
|
6638
7204
|
(trackId, tab) => {
|
|
6639
7205
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, drawerOpen: true, drawerTab: tab } : t));
|
|
6640
7206
|
if (tab === "fx") {
|
|
@@ -6659,10 +7225,10 @@ function useGeneratorPanelCore({
|
|
|
6659
7225
|
},
|
|
6660
7226
|
[host, availableInstruments.length, instrumentsLoading, loadEditNotes]
|
|
6661
7227
|
);
|
|
6662
|
-
const handleProgressChange = (0,
|
|
7228
|
+
const handleProgressChange = (0, import_react29.useCallback)((trackId, pct) => {
|
|
6663
7229
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, generationProgress: pct } : t));
|
|
6664
7230
|
}, []);
|
|
6665
|
-
const handleToggleDrawer = (0,
|
|
7231
|
+
const handleToggleDrawer = (0, import_react29.useCallback)((trackId) => {
|
|
6666
7232
|
setTracks(
|
|
6667
7233
|
(prev) => prev.map((t) => {
|
|
6668
7234
|
if (t.handle.id !== trackId) return t;
|
|
@@ -6671,7 +7237,7 @@ function useGeneratorPanelCore({
|
|
|
6671
7237
|
})
|
|
6672
7238
|
);
|
|
6673
7239
|
}, []);
|
|
6674
|
-
const handleInstrumentSelect = (0,
|
|
7240
|
+
const handleInstrumentSelect = (0, import_react29.useCallback)(
|
|
6675
7241
|
async (trackId, pluginId) => {
|
|
6676
7242
|
const isDefaultInstrument = pluginId === (identity.defaultInstrumentPluginId ?? "Surge XT");
|
|
6677
7243
|
if (isDefaultInstrument) {
|
|
@@ -6724,7 +7290,7 @@ function useGeneratorPanelCore({
|
|
|
6724
7290
|
},
|
|
6725
7291
|
[host, identity.defaultInstrumentPluginId, logTag]
|
|
6726
7292
|
);
|
|
6727
|
-
const handleShowEditor = (0,
|
|
7293
|
+
const handleShowEditor = (0, import_react29.useCallback)(
|
|
6728
7294
|
async (trackId) => {
|
|
6729
7295
|
try {
|
|
6730
7296
|
await host.showInstrumentEditor(trackId);
|
|
@@ -6735,12 +7301,12 @@ function useGeneratorPanelCore({
|
|
|
6735
7301
|
},
|
|
6736
7302
|
[host]
|
|
6737
7303
|
);
|
|
6738
|
-
const handleBackToInstruments = (0,
|
|
7304
|
+
const handleBackToInstruments = (0, import_react29.useCallback)((trackId) => {
|
|
6739
7305
|
setTracks(
|
|
6740
7306
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editorStage: false } : t)
|
|
6741
7307
|
);
|
|
6742
7308
|
}, []);
|
|
6743
|
-
const handleRefreshInstruments = (0,
|
|
7309
|
+
const handleRefreshInstruments = (0, import_react29.useCallback)(() => {
|
|
6744
7310
|
setInstrumentsLoading(true);
|
|
6745
7311
|
host.getAvailableInstruments().then((instruments) => {
|
|
6746
7312
|
setAvailableInstruments(instruments);
|
|
@@ -6749,13 +7315,13 @@ function useGeneratorPanelCore({
|
|
|
6749
7315
|
setInstrumentsLoading(false);
|
|
6750
7316
|
});
|
|
6751
7317
|
}, [host]);
|
|
6752
|
-
const onAuditionNote = (0,
|
|
7318
|
+
const onAuditionNote = (0, import_react29.useCallback)(
|
|
6753
7319
|
(trackId, pitch, velocity, ms) => {
|
|
6754
7320
|
void host.auditionNote(trackId, pitch, velocity, ms);
|
|
6755
7321
|
},
|
|
6756
7322
|
[host]
|
|
6757
7323
|
);
|
|
6758
|
-
const { resolvedCrossfadePairs, crossfadeMemberDbIds } = (0,
|
|
7324
|
+
const { resolvedCrossfadePairs, crossfadeMemberDbIds } = (0, import_react29.useMemo)(() => {
|
|
6759
7325
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
6760
7326
|
const pairs = [];
|
|
6761
7327
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -6770,7 +7336,7 @@ function useGeneratorPanelCore({
|
|
|
6770
7336
|
}
|
|
6771
7337
|
return { resolvedCrossfadePairs: pairs, crossfadeMemberDbIds: members };
|
|
6772
7338
|
}, [tracks, crossfadePairsMeta]);
|
|
6773
|
-
const { resolvedFades, fadeMemberDbIds } = (0,
|
|
7339
|
+
const { resolvedFades, fadeMemberDbIds } = (0, import_react29.useMemo)(() => {
|
|
6774
7340
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
6775
7341
|
const list = [];
|
|
6776
7342
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -6798,7 +7364,7 @@ function useGeneratorPanelCore({
|
|
|
6798
7364
|
resolvedCrossfadePairs,
|
|
6799
7365
|
resolvedFades
|
|
6800
7366
|
});
|
|
6801
|
-
const setGroupMute = (0,
|
|
7367
|
+
const setGroupMute = (0, import_react29.useCallback)(
|
|
6802
7368
|
(trackIds, muted) => {
|
|
6803
7369
|
for (const id of trackIds) {
|
|
6804
7370
|
setTracks(
|
|
@@ -6810,7 +7376,7 @@ function useGeneratorPanelCore({
|
|
|
6810
7376
|
},
|
|
6811
7377
|
[host]
|
|
6812
7378
|
);
|
|
6813
|
-
const setGroupSolo = (0,
|
|
7379
|
+
const setGroupSolo = (0, import_react29.useCallback)(
|
|
6814
7380
|
(trackIds, solo) => {
|
|
6815
7381
|
for (const id of trackIds) {
|
|
6816
7382
|
setTracks(
|
|
@@ -6822,7 +7388,7 @@ function useGeneratorPanelCore({
|
|
|
6822
7388
|
},
|
|
6823
7389
|
[host]
|
|
6824
7390
|
);
|
|
6825
|
-
const deleteGroup = (0,
|
|
7391
|
+
const deleteGroup = (0, import_react29.useCallback)(
|
|
6826
7392
|
async (members, cleanupKeySuffixes) => {
|
|
6827
7393
|
for (const member of members) {
|
|
6828
7394
|
try {
|
|
@@ -6842,7 +7408,7 @@ function useGeneratorPanelCore({
|
|
|
6842
7408
|
},
|
|
6843
7409
|
[host, activeSceneId, loadTracks]
|
|
6844
7410
|
);
|
|
6845
|
-
const handlers = (0,
|
|
7411
|
+
const handlers = (0, import_react29.useMemo)(
|
|
6846
7412
|
() => ({
|
|
6847
7413
|
promptChange: handlePromptChange,
|
|
6848
7414
|
generate: (trackId) => {
|
|
@@ -6956,8 +7522,8 @@ function useGeneratorPanelCore({
|
|
|
6956
7522
|
}
|
|
6957
7523
|
|
|
6958
7524
|
// src/panel-core/GeneratorPanelShell.tsx
|
|
6959
|
-
var
|
|
6960
|
-
var
|
|
7525
|
+
var import_react30 = __toESM(require("react"));
|
|
7526
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
6961
7527
|
function GeneratorPanelShell({ core, slots }) {
|
|
6962
7528
|
const {
|
|
6963
7529
|
ui,
|
|
@@ -7010,8 +7576,9 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7010
7576
|
deleteGroup
|
|
7011
7577
|
} = core;
|
|
7012
7578
|
const { host, activeSceneId, isAuthenticated, sceneContext, onSelectScene, onOpenContract } = ui;
|
|
7579
|
+
const panelBus = usePanelBus(host, activeSceneId);
|
|
7013
7580
|
const { identity, features } = adapter;
|
|
7014
|
-
const buildRowProps = (0,
|
|
7581
|
+
const buildRowProps = (0, import_react30.useCallback)(
|
|
7015
7582
|
(track, drag) => {
|
|
7016
7583
|
const id = track.handle.id;
|
|
7017
7584
|
const pickerProps = features.instrumentPicker ? {
|
|
@@ -7063,6 +7630,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7063
7630
|
onVolumeChange: (vol) => handlers.volumeChange(id, vol),
|
|
7064
7631
|
onPanChange: (pan) => handlers.panChange(id, pan),
|
|
7065
7632
|
onFxToggle: (cat, enabled) => handleFxToggle(id, cat, enabled),
|
|
7633
|
+
externalFxHost: host,
|
|
7066
7634
|
onFxPresetChange: (cat, idx) => handleFxPresetChange(id, cat, idx),
|
|
7067
7635
|
onFxDryWetChange: (cat, val) => handleFxDryWetChange(id, cat, val),
|
|
7068
7636
|
onToggleFxDrawer: () => handlers.toggleFxDrawer(id),
|
|
@@ -7110,12 +7678,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7110
7678
|
]
|
|
7111
7679
|
);
|
|
7112
7680
|
if (!activeSceneId) {
|
|
7113
|
-
return /* @__PURE__ */ (0,
|
|
7681
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7114
7682
|
"div",
|
|
7115
7683
|
{
|
|
7116
7684
|
"data-testid": `no-scene-placeholder-${identity.familyKey}`,
|
|
7117
7685
|
className: "flex items-center justify-center py-8",
|
|
7118
|
-
children: /* @__PURE__ */ (0,
|
|
7686
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7119
7687
|
"button",
|
|
7120
7688
|
{
|
|
7121
7689
|
onClick: () => onSelectScene?.(),
|
|
@@ -7127,12 +7695,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7127
7695
|
);
|
|
7128
7696
|
}
|
|
7129
7697
|
if (!sceneContext?.hasContract) {
|
|
7130
|
-
return /* @__PURE__ */ (0,
|
|
7698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7131
7699
|
"div",
|
|
7132
7700
|
{
|
|
7133
7701
|
"data-testid": `no-contract-placeholder-${identity.familyKey}`,
|
|
7134
7702
|
className: "flex items-center justify-center py-8",
|
|
7135
|
-
children: /* @__PURE__ */ (0,
|
|
7703
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7136
7704
|
"button",
|
|
7137
7705
|
{
|
|
7138
7706
|
onClick: () => onOpenContract?.(),
|
|
@@ -7144,7 +7712,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7144
7712
|
);
|
|
7145
7713
|
}
|
|
7146
7714
|
if (features.bulkComposePlaceholders && isComposing) {
|
|
7147
|
-
return /* @__PURE__ */ (0,
|
|
7715
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(SorceryProgressBar, { isLoading: true, statusText: "COMPOSING...", heightClass: "h-10" }) });
|
|
7148
7716
|
}
|
|
7149
7717
|
const activePlaceholders = features.bulkComposePlaceholders ? placeholders : [];
|
|
7150
7718
|
if (activePlaceholders.length > 0) {
|
|
@@ -7155,18 +7723,18 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7155
7723
|
tracksByDbId.set(t.handle.id, t);
|
|
7156
7724
|
}
|
|
7157
7725
|
}
|
|
7158
|
-
return /* @__PURE__ */ (0,
|
|
7726
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: activePlaceholders.map((ph) => {
|
|
7159
7727
|
const loadedTrack = ph.status === "completed" ? tracksByDbId.get(ph.id) : void 0;
|
|
7160
7728
|
if (loadedTrack) {
|
|
7161
|
-
return /* @__PURE__ */ (0,
|
|
7729
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TrackRow, { ...buildRowProps(loadedTrack) }, ph.id);
|
|
7162
7730
|
}
|
|
7163
|
-
return /* @__PURE__ */ (0,
|
|
7731
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7164
7732
|
"div",
|
|
7165
7733
|
{
|
|
7166
7734
|
"data-testid": "bulk-placeholder-track",
|
|
7167
7735
|
className: "relative rounded-sm border w-full overflow-hidden border-sas-border bg-sas-panel-alt",
|
|
7168
7736
|
style: { borderLeftColor: identity.placeholderAccentColor, borderLeftWidth: "3px" },
|
|
7169
|
-
children: /* @__PURE__ */ (0,
|
|
7737
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(SorceryProgressBar, { isLoading: true, statusText: "CONJURING MIDI...", heightClass: "h-10" })
|
|
7170
7738
|
},
|
|
7171
7739
|
ph.id
|
|
7172
7740
|
);
|
|
@@ -7178,13 +7746,13 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7178
7746
|
supportsMeters,
|
|
7179
7747
|
levels: supportsMeters ? trackLevels : void 0,
|
|
7180
7748
|
handlers,
|
|
7181
|
-
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */ (0,
|
|
7749
|
+
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TrackRow, { ...{ ...buildRowProps(track, drag), ...overrides ?? {} } }, track.handle.id),
|
|
7182
7750
|
setGroupMute,
|
|
7183
7751
|
setGroupSolo,
|
|
7184
7752
|
deleteGroup
|
|
7185
7753
|
};
|
|
7186
|
-
return /* @__PURE__ */ (0,
|
|
7187
|
-
features.importTracks && host.listImportableTracks && /* @__PURE__ */ (0,
|
|
7754
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: [
|
|
7755
|
+
features.importTracks && host.listImportableTracks && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7188
7756
|
ImportTrackModal,
|
|
7189
7757
|
{
|
|
7190
7758
|
host,
|
|
@@ -7197,7 +7765,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7197
7765
|
testIdPrefix: `${identity.familyKey}-import`
|
|
7198
7766
|
}
|
|
7199
7767
|
),
|
|
7200
|
-
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ (0,
|
|
7768
|
+
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7201
7769
|
ImportTrackModal,
|
|
7202
7770
|
{
|
|
7203
7771
|
host,
|
|
@@ -7212,7 +7780,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7212
7780
|
}
|
|
7213
7781
|
),
|
|
7214
7782
|
slots?.modals,
|
|
7215
|
-
canCrossfade && xfFromId && xfToId && /* @__PURE__ */ (0,
|
|
7783
|
+
canCrossfade && xfFromId && xfToId && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: designerView ? "contents" : "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7216
7784
|
TransitionDesigner,
|
|
7217
7785
|
{
|
|
7218
7786
|
host,
|
|
@@ -7229,9 +7797,29 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7229
7797
|
testIdPrefix: `${identity.familyKey}-transition-designer`
|
|
7230
7798
|
}
|
|
7231
7799
|
) }),
|
|
7232
|
-
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */ (0,
|
|
7800
|
+
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "text-sas-muted text-xs text-center py-4", children: "Loading tracks..." }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
7801
|
+
panelBus.supported && panelBus.bus && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7802
|
+
PanelMasterStrip,
|
|
7803
|
+
{
|
|
7804
|
+
bus: panelBus.bus,
|
|
7805
|
+
levels: panelBus.levels,
|
|
7806
|
+
availableFx: panelBus.availableFx,
|
|
7807
|
+
fxLoading: panelBus.fxLoading,
|
|
7808
|
+
soloedOut: anySolo && !panelBus.bus.soloed,
|
|
7809
|
+
fxPickerOpen: panelBus.fxPickerOpen,
|
|
7810
|
+
onToggleFxPicker: panelBus.setFxPickerOpen,
|
|
7811
|
+
onRefreshFx: panelBus.refreshFx,
|
|
7812
|
+
onVolumeChange: panelBus.onVolumeChange,
|
|
7813
|
+
onMuteToggle: panelBus.onMuteToggle,
|
|
7814
|
+
onSoloToggle: panelBus.onSoloToggle,
|
|
7815
|
+
onAddFx: panelBus.onAddFx,
|
|
7816
|
+
onRemoveFx: panelBus.onRemoveFx,
|
|
7817
|
+
onToggleFxEnabled: panelBus.onToggleFxEnabled,
|
|
7818
|
+
onShowFxEditor: panelBus.onShowFxEditor
|
|
7819
|
+
}
|
|
7820
|
+
),
|
|
7233
7821
|
slots?.beforeRows,
|
|
7234
|
-
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ (0,
|
|
7822
|
+
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7235
7823
|
CrossfadeTrackRow,
|
|
7236
7824
|
{
|
|
7237
7825
|
accentColor: identity.transitionAccentColor,
|
|
@@ -7268,7 +7856,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7268
7856
|
},
|
|
7269
7857
|
pair.groupId
|
|
7270
7858
|
)),
|
|
7271
|
-
resolvedFades.map((fade) => /* @__PURE__ */ (0,
|
|
7859
|
+
resolvedFades.map((fade) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7272
7860
|
FadeTrackRow,
|
|
7273
7861
|
{
|
|
7274
7862
|
accentColor: identity.transitionAccentColor,
|
|
@@ -7294,20 +7882,20 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7294
7882
|
fade.dbId
|
|
7295
7883
|
)),
|
|
7296
7884
|
(adapter.groupExtensions ?? []).flatMap(
|
|
7297
|
-
(ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).map((group) => /* @__PURE__ */ (0,
|
|
7885
|
+
(ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).map((group) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react30.default.Fragment, { children: ext.renderGroup(group, groupCtx) }, `${ext.metaKey}:${group.groupId}`))
|
|
7298
7886
|
),
|
|
7299
7887
|
tracks.map((track, index) => {
|
|
7300
7888
|
if (crossfadeMemberDbIds.has(track.handle.dbId) || fadeMemberDbIds.has(track.handle.dbId) || genericGroupMemberDbIds.has(track.handle.dbId)) {
|
|
7301
7889
|
return null;
|
|
7302
7890
|
}
|
|
7303
|
-
return /* @__PURE__ */ (0,
|
|
7891
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TrackRow, { ...buildRowProps(track, reorder.dragPropsFor(index)) }, track.handle.id);
|
|
7304
7892
|
}),
|
|
7305
7893
|
slots?.afterRows
|
|
7306
7894
|
] })),
|
|
7307
7895
|
features.exportMidi && !designerView && !isLoadingTracks && tracks.length > 0 && (() => {
|
|
7308
7896
|
const hasAnyMidi = tracks.some((t) => t.hasMidi);
|
|
7309
7897
|
const exportDisabled = isExportingMidi || !hasAnyMidi;
|
|
7310
|
-
return /* @__PURE__ */ (0,
|
|
7898
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "pt-2", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7311
7899
|
"button",
|
|
7312
7900
|
{
|
|
7313
7901
|
"data-testid": "export-midi-tracks-button",
|
|
@@ -7375,7 +7963,7 @@ function createSurgeSoundAdapter(host, overrides = {}) {
|
|
|
7375
7963
|
}
|
|
7376
7964
|
|
|
7377
7965
|
// src/constants/sdk-version.ts
|
|
7378
|
-
var PLUGIN_SDK_VERSION = "2.
|
|
7966
|
+
var PLUGIN_SDK_VERSION = "2.39.0";
|
|
7379
7967
|
|
|
7380
7968
|
// src/utils/format-concurrent-tracks.ts
|
|
7381
7969
|
function formatConcurrentTracks(ctx) {
|
|
@@ -7551,6 +8139,7 @@ function pickTopKWeighted(scored, options = {}) {
|
|
|
7551
8139
|
PLUGIN_SDK_VERSION,
|
|
7552
8140
|
PX_PER_BEAT,
|
|
7553
8141
|
PanSlider,
|
|
8142
|
+
PanelMasterStrip,
|
|
7554
8143
|
PianoRollEditor,
|
|
7555
8144
|
PluginError,
|
|
7556
8145
|
RESIZE_HANDLE_PX,
|
|
@@ -7562,6 +8151,7 @@ function pickTopKWeighted(scored, options = {}) {
|
|
|
7562
8151
|
TEXTURAL_ROLES,
|
|
7563
8152
|
TRANSITION_DESIGNER_DRAFT_KEY,
|
|
7564
8153
|
TrackDrawer,
|
|
8154
|
+
TrackExternalFxSection,
|
|
7565
8155
|
TrackMeterStrip,
|
|
7566
8156
|
TrackRow,
|
|
7567
8157
|
TransitionDesigner,
|
|
@@ -7615,8 +8205,10 @@ function pickTopKWeighted(scored, options = {}) {
|
|
|
7615
8205
|
transposeNotes,
|
|
7616
8206
|
useAnySolo,
|
|
7617
8207
|
useGeneratorPanelCore,
|
|
8208
|
+
usePanelBus,
|
|
7618
8209
|
useSceneState,
|
|
7619
8210
|
useSoundHistory,
|
|
8211
|
+
useTrackExternalFx,
|
|
7620
8212
|
useTrackLevel,
|
|
7621
8213
|
useTrackLevels,
|
|
7622
8214
|
useTrackMeter,
|