@signalsandsorcery/plugin-sdk 2.35.2 → 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 +139 -5
- package/dist/index.d.ts +139 -5
- package/dist/index.js +953 -652
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +951 -652
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -74,6 +74,7 @@ __export(index_exports, {
|
|
|
74
74
|
TEXTURAL_ROLES: () => TEXTURAL_ROLES,
|
|
75
75
|
TRANSITION_DESIGNER_DRAFT_KEY: () => TRANSITION_DESIGNER_DRAFT_KEY,
|
|
76
76
|
TrackDrawer: () => TrackDrawer,
|
|
77
|
+
TrackExternalFxSection: () => TrackExternalFxSection,
|
|
77
78
|
TrackMeterStrip: () => TrackMeterStrip,
|
|
78
79
|
TrackRow: () => TrackRow,
|
|
79
80
|
TransitionDesigner: () => TransitionDesigner,
|
|
@@ -130,6 +131,7 @@ __export(index_exports, {
|
|
|
130
131
|
usePanelBus: () => usePanelBus,
|
|
131
132
|
useSceneState: () => useSceneState,
|
|
132
133
|
useSoundHistory: () => useSoundHistory,
|
|
134
|
+
useTrackExternalFx: () => useTrackExternalFx,
|
|
133
135
|
useTrackLevel: () => useTrackLevel,
|
|
134
136
|
useTrackLevels: () => useTrackLevels,
|
|
135
137
|
useTrackMeter: () => useTrackMeter,
|
|
@@ -206,11 +208,11 @@ var EMPTY_FX_DETAIL_STATE = {
|
|
|
206
208
|
};
|
|
207
209
|
|
|
208
210
|
// src/components/TrackRow.tsx
|
|
209
|
-
var
|
|
211
|
+
var import_react11 = __toESM(require("react"));
|
|
210
212
|
var import_lucide_react = require("lucide-react");
|
|
211
213
|
|
|
212
214
|
// src/components/TrackDrawer.tsx
|
|
213
|
-
var
|
|
215
|
+
var import_react4 = require("react");
|
|
214
216
|
|
|
215
217
|
// src/constants/fx-presets.ts
|
|
216
218
|
var EQ_PRESETS = {
|
|
@@ -930,8 +932,250 @@ function PianoRollEditor({
|
|
|
930
932
|
] });
|
|
931
933
|
}
|
|
932
934
|
|
|
933
|
-
// 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
|
|
934
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");
|
|
935
1179
|
var TAB_LABELS = {
|
|
936
1180
|
fx: "FX",
|
|
937
1181
|
pick: "Pick",
|
|
@@ -948,6 +1192,7 @@ function TrackDrawer({
|
|
|
948
1192
|
onFxPresetChange,
|
|
949
1193
|
onFxDryWetChange,
|
|
950
1194
|
fxDisabled = false,
|
|
1195
|
+
externalFxHost,
|
|
951
1196
|
instruments = [],
|
|
952
1197
|
currentPluginId = null,
|
|
953
1198
|
isLoading = false,
|
|
@@ -970,13 +1215,13 @@ function TrackDrawer({
|
|
|
970
1215
|
editSnap,
|
|
971
1216
|
onAuditionNote
|
|
972
1217
|
}) {
|
|
973
|
-
const [search, setSearch] = (0,
|
|
1218
|
+
const [search, setSearch] = (0, import_react4.useState)("");
|
|
974
1219
|
const fxEnabled = !!onFxToggle;
|
|
975
1220
|
const pickEnabled = !!onSelect;
|
|
976
1221
|
const historyEnabled = !!onRestoreSound;
|
|
977
1222
|
const importEnabled = !!onImportSound;
|
|
978
1223
|
const editEnabled = !!onNotesChange;
|
|
979
|
-
const enabledTabs = (0,
|
|
1224
|
+
const enabledTabs = (0, import_react4.useMemo)(() => {
|
|
980
1225
|
const tabs = [];
|
|
981
1226
|
if (fxEnabled) tabs.push("fx");
|
|
982
1227
|
if (pickEnabled) tabs.push("pick");
|
|
@@ -986,7 +1231,7 @@ function TrackDrawer({
|
|
|
986
1231
|
return tabs;
|
|
987
1232
|
}, [fxEnabled, pickEnabled, historyEnabled, importEnabled, editEnabled]);
|
|
988
1233
|
const SURGE_XT_DEFAULT_ID = "Surge XT";
|
|
989
|
-
const filtered = (0,
|
|
1234
|
+
const filtered = (0, import_react4.useMemo)(() => {
|
|
990
1235
|
let all = instruments.filter((i) => i.name !== "Surge XT");
|
|
991
1236
|
if (search.trim()) {
|
|
992
1237
|
const q = search.toLowerCase();
|
|
@@ -1006,12 +1251,12 @@ function TrackDrawer({
|
|
|
1006
1251
|
const history = soundHistory ?? [];
|
|
1007
1252
|
const effectiveTab = enabledTabs.includes(activeTab) ? activeTab : enabledTabs[0] ?? "fx";
|
|
1008
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"}`;
|
|
1009
|
-
const strip = enabledTabs.length > 1 ? /* @__PURE__ */ (0,
|
|
1254
|
+
const strip = enabledTabs.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1010
1255
|
"div",
|
|
1011
1256
|
{
|
|
1012
1257
|
className: "flex items-center gap-1 border-b border-sas-border pb-1",
|
|
1013
1258
|
"data-testid": "sdk-drawer-tabs",
|
|
1014
|
-
children: enabledTabs.map((tab) => /* @__PURE__ */ (0,
|
|
1259
|
+
children: enabledTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1015
1260
|
"button",
|
|
1016
1261
|
{
|
|
1017
1262
|
type: "button",
|
|
@@ -1025,9 +1270,9 @@ function TrackDrawer({
|
|
|
1025
1270
|
}
|
|
1026
1271
|
) : null;
|
|
1027
1272
|
const currentSound = soundHistoryCursor >= 0 && soundHistoryCursor < history.length ? history[soundHistoryCursor].label : null;
|
|
1028
|
-
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: [
|
|
1029
1274
|
strip,
|
|
1030
|
-
currentSound && /* @__PURE__ */ (0,
|
|
1275
|
+
currentSound && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1031
1276
|
"span",
|
|
1032
1277
|
{
|
|
1033
1278
|
className: "text-[10px] text-sas-muted/60 truncate px-0.5",
|
|
@@ -1037,9 +1282,9 @@ function TrackDrawer({
|
|
|
1037
1282
|
)
|
|
1038
1283
|
] }) : null;
|
|
1039
1284
|
if (effectiveTab === "edit") {
|
|
1040
|
-
return /* @__PURE__ */ (0,
|
|
1285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-edit", children: [
|
|
1041
1286
|
header,
|
|
1042
|
-
/* @__PURE__ */ (0,
|
|
1287
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1043
1288
|
PianoRollEditor,
|
|
1044
1289
|
{
|
|
1045
1290
|
notes: editNotes ?? [],
|
|
@@ -1054,9 +1299,9 @@ function TrackDrawer({
|
|
|
1054
1299
|
] });
|
|
1055
1300
|
}
|
|
1056
1301
|
if (effectiveTab === "fx") {
|
|
1057
|
-
return /* @__PURE__ */ (0,
|
|
1302
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-fx", children: [
|
|
1058
1303
|
header,
|
|
1059
|
-
/* @__PURE__ */ (0,
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1060
1305
|
FxToggleBar,
|
|
1061
1306
|
{
|
|
1062
1307
|
trackId,
|
|
@@ -1066,20 +1311,21 @@ function TrackDrawer({
|
|
|
1066
1311
|
onDryWetChange: (_t, category, value) => onFxDryWetChange?.(category, value),
|
|
1067
1312
|
disabled: fxDisabled
|
|
1068
1313
|
}
|
|
1069
|
-
)
|
|
1314
|
+
),
|
|
1315
|
+
externalFxHost && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TrackExternalFxSection, { host: externalFxHost, trackId, disabled: fxDisabled })
|
|
1070
1316
|
] });
|
|
1071
1317
|
}
|
|
1072
1318
|
if (effectiveTab === "import") {
|
|
1073
1319
|
const soundNoun = /preset/i.test(importSoundLabel ?? "") ? "preset" : /sample/i.test(importSoundLabel ?? "") ? "sample" : "sound";
|
|
1074
|
-
return /* @__PURE__ */ (0,
|
|
1320
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-import", children: [
|
|
1075
1321
|
header,
|
|
1076
|
-
/* @__PURE__ */ (0,
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "text-[11px] text-sas-muted/70 leading-snug", children: [
|
|
1077
1323
|
"Copy the sound from a matching track in another scene \u2014 your MIDI stays, only the",
|
|
1078
1324
|
" ",
|
|
1079
1325
|
soundNoun,
|
|
1080
1326
|
" changes."
|
|
1081
1327
|
] }),
|
|
1082
|
-
/* @__PURE__ */ (0,
|
|
1328
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1083
1329
|
"button",
|
|
1084
1330
|
{
|
|
1085
1331
|
type: "button",
|
|
@@ -1097,16 +1343,16 @@ function TrackDrawer({
|
|
|
1097
1343
|
}
|
|
1098
1344
|
if (effectiveTab === "history") {
|
|
1099
1345
|
const order = history.map((_, i) => i).reverse();
|
|
1100
|
-
return /* @__PURE__ */ (0,
|
|
1346
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1101
1347
|
header,
|
|
1102
|
-
history.length === 0 ? /* @__PURE__ */ (0,
|
|
1348
|
+
history.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1103
1349
|
"div",
|
|
1104
1350
|
{
|
|
1105
1351
|
className: "text-xs text-sas-muted/60 text-center py-3",
|
|
1106
1352
|
"data-testid": "sdk-history-empty",
|
|
1107
1353
|
children: "No sounds yet \u2014 shuffle to build history."
|
|
1108
1354
|
}
|
|
1109
|
-
) : /* @__PURE__ */ (0,
|
|
1355
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1110
1356
|
"ul",
|
|
1111
1357
|
{
|
|
1112
1358
|
className: "flex flex-col gap-1 max-h-[160px] overflow-y-auto",
|
|
@@ -1114,8 +1360,8 @@ function TrackDrawer({
|
|
|
1114
1360
|
children: order.map((i) => {
|
|
1115
1361
|
const entry = history[i];
|
|
1116
1362
|
const isCurrent = i === soundHistoryCursor;
|
|
1117
|
-
return /* @__PURE__ */ (0,
|
|
1118
|
-
/* @__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)(
|
|
1119
1365
|
"button",
|
|
1120
1366
|
{
|
|
1121
1367
|
type: "button",
|
|
@@ -1125,12 +1371,12 @@ function TrackDrawer({
|
|
|
1125
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"}`,
|
|
1126
1372
|
title: isCurrent ? "Current sound" : `Restore: ${entry.label}`,
|
|
1127
1373
|
children: [
|
|
1128
|
-
/* @__PURE__ */ (0,
|
|
1129
|
-
/* @__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" })
|
|
1130
1376
|
]
|
|
1131
1377
|
}
|
|
1132
1378
|
),
|
|
1133
|
-
onToggleFavorite && /* @__PURE__ */ (0,
|
|
1379
|
+
onToggleFavorite && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1134
1380
|
"button",
|
|
1135
1381
|
{
|
|
1136
1382
|
type: "button",
|
|
@@ -1148,10 +1394,10 @@ function TrackDrawer({
|
|
|
1148
1394
|
] });
|
|
1149
1395
|
}
|
|
1150
1396
|
if (effectiveTab === "pick" && editorStage) {
|
|
1151
|
-
return /* @__PURE__ */ (0,
|
|
1397
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1152
1398
|
header,
|
|
1153
|
-
/* @__PURE__ */ (0,
|
|
1154
|
-
/* @__PURE__ */ (0,
|
|
1399
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1400
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1155
1401
|
"button",
|
|
1156
1402
|
{
|
|
1157
1403
|
onClick: () => onBackToInstruments?.(),
|
|
@@ -1159,9 +1405,9 @@ function TrackDrawer({
|
|
|
1159
1405
|
children: "\u2190 Back"
|
|
1160
1406
|
}
|
|
1161
1407
|
),
|
|
1162
|
-
/* @__PURE__ */ (0,
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-xs text-sas-muted font-medium truncate flex-1", children: selectedInstrumentName ?? "Plugin" })
|
|
1163
1409
|
] }),
|
|
1164
|
-
/* @__PURE__ */ (0,
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1165
1411
|
"button",
|
|
1166
1412
|
{
|
|
1167
1413
|
onClick: () => onShowEditor?.(),
|
|
@@ -1173,10 +1419,10 @@ function TrackDrawer({
|
|
|
1173
1419
|
}
|
|
1174
1420
|
const isDefaultSelected = currentPluginId === null;
|
|
1175
1421
|
const isSelected = (pluginId) => pluginId === currentPluginId;
|
|
1176
|
-
return /* @__PURE__ */ (0,
|
|
1422
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1177
1423
|
header,
|
|
1178
|
-
/* @__PURE__ */ (0,
|
|
1179
|
-
/* @__PURE__ */ (0,
|
|
1424
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1425
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1180
1426
|
"input",
|
|
1181
1427
|
{
|
|
1182
1428
|
type: "text",
|
|
@@ -1186,7 +1432,7 @@ function TrackDrawer({
|
|
|
1186
1432
|
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
1187
1433
|
}
|
|
1188
1434
|
),
|
|
1189
|
-
/* @__PURE__ */ (0,
|
|
1435
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1190
1436
|
"button",
|
|
1191
1437
|
{
|
|
1192
1438
|
onClick: () => onRefresh?.(),
|
|
@@ -1197,54 +1443,54 @@ function TrackDrawer({
|
|
|
1197
1443
|
}
|
|
1198
1444
|
)
|
|
1199
1445
|
] }),
|
|
1200
|
-
isLoading && instruments.length === 0 ? /* @__PURE__ */ (0,
|
|
1201
|
-
/* @__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)(
|
|
1202
1448
|
"button",
|
|
1203
1449
|
{
|
|
1204
1450
|
onClick: () => onSelect?.(SURGE_XT_DEFAULT_ID),
|
|
1205
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"}`,
|
|
1206
1452
|
title: "Surge XT \u2014 Default instrument",
|
|
1207
1453
|
children: [
|
|
1208
|
-
/* @__PURE__ */ (0,
|
|
1454
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1209
1455
|
isDefaultSelected && "\u2713 ",
|
|
1210
1456
|
"Surge XT"
|
|
1211
1457
|
] }),
|
|
1212
|
-
/* @__PURE__ */ (0,
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: "Default" })
|
|
1213
1459
|
]
|
|
1214
1460
|
},
|
|
1215
1461
|
"__surge-xt-default__"
|
|
1216
1462
|
),
|
|
1217
1463
|
filtered.map((inst) => {
|
|
1218
1464
|
const selected = isSelected(inst.pluginId);
|
|
1219
|
-
return /* @__PURE__ */ (0,
|
|
1465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1220
1466
|
"button",
|
|
1221
1467
|
{
|
|
1222
1468
|
onClick: () => onSelect?.(inst.pluginId),
|
|
1223
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"}`,
|
|
1224
1470
|
title: `${inst.name} by ${inst.manufacturer} (${inst.type.toUpperCase()})${inst.missing ? " \u2014 MISSING" : ""}`,
|
|
1225
1471
|
children: [
|
|
1226
|
-
/* @__PURE__ */ (0,
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1227
1473
|
selected && "\u2713 ",
|
|
1228
1474
|
inst.name
|
|
1229
1475
|
] }),
|
|
1230
|
-
/* @__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() })
|
|
1231
1477
|
]
|
|
1232
1478
|
},
|
|
1233
1479
|
inst.pluginId
|
|
1234
1480
|
);
|
|
1235
1481
|
}),
|
|
1236
|
-
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" })
|
|
1237
1483
|
] })
|
|
1238
1484
|
] });
|
|
1239
1485
|
}
|
|
1240
1486
|
|
|
1241
1487
|
// src/components/ConfirmDialog.tsx
|
|
1242
|
-
var
|
|
1488
|
+
var import_react6 = require("react");
|
|
1243
1489
|
|
|
1244
1490
|
// src/components/Modal.tsx
|
|
1245
|
-
var
|
|
1491
|
+
var import_react5 = require("react");
|
|
1246
1492
|
var import_react_dom = require("react-dom");
|
|
1247
|
-
var
|
|
1493
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1248
1494
|
function Modal({
|
|
1249
1495
|
open,
|
|
1250
1496
|
onClose,
|
|
@@ -1254,7 +1500,7 @@ function Modal({
|
|
|
1254
1500
|
closeOnEscape = true,
|
|
1255
1501
|
initialFocusRef
|
|
1256
1502
|
}) {
|
|
1257
|
-
(0,
|
|
1503
|
+
(0, import_react5.useEffect)(() => {
|
|
1258
1504
|
if (!open) return void 0;
|
|
1259
1505
|
const onKey = (e) => {
|
|
1260
1506
|
if (closeOnEscape && e.key === "Escape") {
|
|
@@ -1268,7 +1514,7 @@ function Modal({
|
|
|
1268
1514
|
}, [open, onClose, closeOnEscape, initialFocusRef]);
|
|
1269
1515
|
if (!open) return null;
|
|
1270
1516
|
return (0, import_react_dom.createPortal)(
|
|
1271
|
-
/* @__PURE__ */ (0,
|
|
1517
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1272
1518
|
"div",
|
|
1273
1519
|
{
|
|
1274
1520
|
className: "fixed inset-0 z-[1000] flex items-center justify-center bg-black/60",
|
|
@@ -1282,7 +1528,7 @@ function Modal({
|
|
|
1282
1528
|
}
|
|
1283
1529
|
|
|
1284
1530
|
// src/components/ConfirmDialog.tsx
|
|
1285
|
-
var
|
|
1531
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1286
1532
|
function ConfirmDialog({
|
|
1287
1533
|
open,
|
|
1288
1534
|
title,
|
|
@@ -1294,8 +1540,8 @@ function ConfirmDialog({
|
|
|
1294
1540
|
onCancel,
|
|
1295
1541
|
testIdPrefix = "confirm-dialog"
|
|
1296
1542
|
}) {
|
|
1297
|
-
const cancelRef = (0,
|
|
1298
|
-
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)(
|
|
1299
1545
|
"div",
|
|
1300
1546
|
{
|
|
1301
1547
|
className: "w-[360px] max-w-[90vw] flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
@@ -1305,8 +1551,8 @@ function ConfirmDialog({
|
|
|
1305
1551
|
"aria-label": title,
|
|
1306
1552
|
"data-testid": `${testIdPrefix}-modal`,
|
|
1307
1553
|
children: [
|
|
1308
|
-
/* @__PURE__ */ (0,
|
|
1309
|
-
/* @__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)(
|
|
1310
1556
|
"div",
|
|
1311
1557
|
{
|
|
1312
1558
|
className: "px-4 py-3 text-xs text-sas-muted leading-relaxed break-words",
|
|
@@ -1314,8 +1560,8 @@ function ConfirmDialog({
|
|
|
1314
1560
|
children: message
|
|
1315
1561
|
}
|
|
1316
1562
|
),
|
|
1317
|
-
/* @__PURE__ */ (0,
|
|
1318
|
-
/* @__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)(
|
|
1319
1565
|
"button",
|
|
1320
1566
|
{
|
|
1321
1567
|
ref: cancelRef,
|
|
@@ -1326,7 +1572,7 @@ function ConfirmDialog({
|
|
|
1326
1572
|
children: cancelLabel
|
|
1327
1573
|
}
|
|
1328
1574
|
),
|
|
1329
|
-
/* @__PURE__ */ (0,
|
|
1575
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1330
1576
|
"button",
|
|
1331
1577
|
{
|
|
1332
1578
|
type: "button",
|
|
@@ -1343,7 +1589,7 @@ function ConfirmDialog({
|
|
|
1343
1589
|
}
|
|
1344
1590
|
|
|
1345
1591
|
// src/components/LevelMeter.tsx
|
|
1346
|
-
var
|
|
1592
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1347
1593
|
var COLOR_GREEN = "#2BD576";
|
|
1348
1594
|
var COLOR_ORANGE = "#F5A623";
|
|
1349
1595
|
var COLOR_RED = "#FF4D5E";
|
|
@@ -1371,7 +1617,7 @@ var LevelMeter = ({
|
|
|
1371
1617
|
const widthPct = active ? dbToPct(peakDb) : 0;
|
|
1372
1618
|
const showPeak = peakHoldDb != null && active && peakHoldDb > -60;
|
|
1373
1619
|
const peakHoldPct = showPeak ? dbToPct(peakHoldDb) : 0;
|
|
1374
|
-
return /* @__PURE__ */ (0,
|
|
1620
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1375
1621
|
"div",
|
|
1376
1622
|
{
|
|
1377
1623
|
className: `sas-level-meter ${className ?? ""}`,
|
|
@@ -1382,7 +1628,7 @@ var LevelMeter = ({
|
|
|
1382
1628
|
gap: compact ? 0 : 6
|
|
1383
1629
|
},
|
|
1384
1630
|
children: [
|
|
1385
|
-
/* @__PURE__ */ (0,
|
|
1631
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1386
1632
|
"div",
|
|
1387
1633
|
{
|
|
1388
1634
|
style: {
|
|
@@ -1396,8 +1642,8 @@ var LevelMeter = ({
|
|
|
1396
1642
|
minWidth: compact ? 0 : 60
|
|
1397
1643
|
},
|
|
1398
1644
|
children: [
|
|
1399
|
-
/* @__PURE__ */ (0,
|
|
1400
|
-
/* @__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)(
|
|
1401
1647
|
"div",
|
|
1402
1648
|
{
|
|
1403
1649
|
style: {
|
|
@@ -1411,7 +1657,7 @@ var LevelMeter = ({
|
|
|
1411
1657
|
}
|
|
1412
1658
|
}
|
|
1413
1659
|
),
|
|
1414
|
-
/* @__PURE__ */ (0,
|
|
1660
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1415
1661
|
"div",
|
|
1416
1662
|
{
|
|
1417
1663
|
"data-testid": `${id}-segments`,
|
|
@@ -1424,7 +1670,7 @@ var LevelMeter = ({
|
|
|
1424
1670
|
}
|
|
1425
1671
|
}
|
|
1426
1672
|
),
|
|
1427
|
-
showPeak && /* @__PURE__ */ (0,
|
|
1673
|
+
showPeak && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1428
1674
|
"div",
|
|
1429
1675
|
{
|
|
1430
1676
|
"data-testid": `${id}-peak`,
|
|
@@ -1445,7 +1691,7 @@ var LevelMeter = ({
|
|
|
1445
1691
|
]
|
|
1446
1692
|
}
|
|
1447
1693
|
),
|
|
1448
|
-
!compact && /* @__PURE__ */ (0,
|
|
1694
|
+
!compact && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1449
1695
|
"span",
|
|
1450
1696
|
{
|
|
1451
1697
|
style: {
|
|
@@ -1458,7 +1704,7 @@ var LevelMeter = ({
|
|
|
1458
1704
|
children: active && peakDb > -120 ? `${peakDb.toFixed(0)} dB` : "\u2014"
|
|
1459
1705
|
}
|
|
1460
1706
|
),
|
|
1461
|
-
clipped && /* @__PURE__ */ (0,
|
|
1707
|
+
clipped && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1462
1708
|
"span",
|
|
1463
1709
|
{
|
|
1464
1710
|
"data-testid": `${id}-clip`,
|
|
@@ -1483,7 +1729,7 @@ var LevelMeter = ({
|
|
|
1483
1729
|
};
|
|
1484
1730
|
|
|
1485
1731
|
// src/hooks/useTrackLevels.ts
|
|
1486
|
-
var
|
|
1732
|
+
var import_react7 = require("react");
|
|
1487
1733
|
var meterDiagRLast = /* @__PURE__ */ new Map();
|
|
1488
1734
|
var POLL_INTERVAL_MS = 33;
|
|
1489
1735
|
var HIDDEN_RECHECK_MS = 250;
|
|
@@ -1494,9 +1740,9 @@ function isHidden() {
|
|
|
1494
1740
|
return typeof document !== "undefined" && document.hidden === true;
|
|
1495
1741
|
}
|
|
1496
1742
|
function useTrackLevels(host, enabled = true) {
|
|
1497
|
-
const mapRef = (0,
|
|
1498
|
-
const listenersRef = (0,
|
|
1499
|
-
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);
|
|
1500
1746
|
if (handleRef.current === null) {
|
|
1501
1747
|
handleRef.current = {
|
|
1502
1748
|
getLevel: (trackId) => mapRef.current.get(trackId) ?? null,
|
|
@@ -1508,7 +1754,7 @@ function useTrackLevels(host, enabled = true) {
|
|
|
1508
1754
|
}
|
|
1509
1755
|
};
|
|
1510
1756
|
}
|
|
1511
|
-
(0,
|
|
1757
|
+
(0, import_react7.useEffect)(() => {
|
|
1512
1758
|
const notify = () => {
|
|
1513
1759
|
listenersRef.current.forEach((l) => l());
|
|
1514
1760
|
};
|
|
@@ -1578,8 +1824,8 @@ function sameLevel(a, b) {
|
|
|
1578
1824
|
return a.peakDb === b.peakDb && a.clipped === b.clipped;
|
|
1579
1825
|
}
|
|
1580
1826
|
function useTrackLevel(handle, trackId) {
|
|
1581
|
-
const [level, setLevel] = (0,
|
|
1582
|
-
(0,
|
|
1827
|
+
const [level, setLevel] = (0, import_react7.useState)(null);
|
|
1828
|
+
(0, import_react7.useEffect)(() => {
|
|
1583
1829
|
if (!handle) {
|
|
1584
1830
|
setLevel(null);
|
|
1585
1831
|
return;
|
|
@@ -1603,11 +1849,11 @@ function sameMeter(a, b) {
|
|
|
1603
1849
|
return a.active === b.active && a.clipped === b.clipped && a.peakDb === b.peakDb && Math.round(a.peakHoldDb * 2) === Math.round(b.peakHoldDb * 2);
|
|
1604
1850
|
}
|
|
1605
1851
|
function useTrackMeter(handle, trackId) {
|
|
1606
|
-
const [view, setView] = (0,
|
|
1607
|
-
const heldDbRef = (0,
|
|
1608
|
-
const heldAtRef = (0,
|
|
1609
|
-
const lastTickRef = (0,
|
|
1610
|
-
(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)(() => {
|
|
1611
1857
|
if (!handle) {
|
|
1612
1858
|
heldDbRef.current = METER_FLOOR_DB;
|
|
1613
1859
|
lastTickRef.current = 0;
|
|
@@ -1650,8 +1896,8 @@ function useTrackMeter(handle, trackId) {
|
|
|
1650
1896
|
return view;
|
|
1651
1897
|
}
|
|
1652
1898
|
function useTransportPlaying(host) {
|
|
1653
|
-
const [playing, setPlaying] = (0,
|
|
1654
|
-
(0,
|
|
1899
|
+
const [playing, setPlaying] = (0, import_react7.useState)(false);
|
|
1900
|
+
(0, import_react7.useEffect)(() => {
|
|
1655
1901
|
if (!host) {
|
|
1656
1902
|
setPlaying(false);
|
|
1657
1903
|
return;
|
|
@@ -1679,7 +1925,7 @@ function useTransportPlaying(host) {
|
|
|
1679
1925
|
}
|
|
1680
1926
|
|
|
1681
1927
|
// src/components/TrackMeterStrip.tsx
|
|
1682
|
-
var
|
|
1928
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1683
1929
|
var TrackMeterStrip = ({
|
|
1684
1930
|
levels,
|
|
1685
1931
|
trackId,
|
|
@@ -1687,12 +1933,12 @@ var TrackMeterStrip = ({
|
|
|
1687
1933
|
className
|
|
1688
1934
|
}) => {
|
|
1689
1935
|
const meter = useTrackMeter(levels, trackId);
|
|
1690
|
-
return /* @__PURE__ */ (0,
|
|
1936
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1691
1937
|
"div",
|
|
1692
1938
|
{
|
|
1693
1939
|
"data-testid": "sdk-track-meter",
|
|
1694
1940
|
className: `w-full px-2 py-1 bg-sas-panel-alt border border-t-0 border-sas-border ${roundBottom ? "rounded-b-sm" : ""} ${className ?? ""}`,
|
|
1695
|
-
children: /* @__PURE__ */ (0,
|
|
1941
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1696
1942
|
LevelMeter,
|
|
1697
1943
|
{
|
|
1698
1944
|
compact: true,
|
|
@@ -1708,7 +1954,7 @@ var TrackMeterStrip = ({
|
|
|
1708
1954
|
};
|
|
1709
1955
|
|
|
1710
1956
|
// src/components/VolumeSlider.tsx
|
|
1711
|
-
var
|
|
1957
|
+
var import_react8 = require("react");
|
|
1712
1958
|
|
|
1713
1959
|
// src/utils/volume-conversion.ts
|
|
1714
1960
|
var SLIDER_UNITY = 0.75;
|
|
@@ -1730,7 +1976,7 @@ function dbToSlider(db) {
|
|
|
1730
1976
|
}
|
|
1731
1977
|
|
|
1732
1978
|
// src/components/VolumeSlider.tsx
|
|
1733
|
-
var
|
|
1979
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1734
1980
|
function formatDb(value) {
|
|
1735
1981
|
const db = sliderToDb(value);
|
|
1736
1982
|
if (db <= -60) return "-\u221E dB";
|
|
@@ -1738,12 +1984,12 @@ function formatDb(value) {
|
|
|
1738
1984
|
return `${sign}${db.toFixed(1)} dB`;
|
|
1739
1985
|
}
|
|
1740
1986
|
function useDebouncedCallback(callback, delay) {
|
|
1741
|
-
const timeoutRef = (0,
|
|
1742
|
-
const callbackRef = (0,
|
|
1743
|
-
(0,
|
|
1987
|
+
const timeoutRef = (0, import_react8.useRef)(null);
|
|
1988
|
+
const callbackRef = (0, import_react8.useRef)(callback);
|
|
1989
|
+
(0, import_react8.useEffect)(() => {
|
|
1744
1990
|
callbackRef.current = callback;
|
|
1745
1991
|
}, [callback]);
|
|
1746
|
-
const debouncedCallback = (0,
|
|
1992
|
+
const debouncedCallback = (0, import_react8.useCallback)(
|
|
1747
1993
|
(...args) => {
|
|
1748
1994
|
if (timeoutRef.current) {
|
|
1749
1995
|
clearTimeout(timeoutRef.current);
|
|
@@ -1754,7 +2000,7 @@ function useDebouncedCallback(callback, delay) {
|
|
|
1754
2000
|
},
|
|
1755
2001
|
[delay]
|
|
1756
2002
|
);
|
|
1757
|
-
(0,
|
|
2003
|
+
(0, import_react8.useEffect)(() => {
|
|
1758
2004
|
return () => {
|
|
1759
2005
|
if (timeoutRef.current) {
|
|
1760
2006
|
clearTimeout(timeoutRef.current);
|
|
@@ -1769,15 +2015,15 @@ var VolumeSlider = ({
|
|
|
1769
2015
|
disabled = false,
|
|
1770
2016
|
className = ""
|
|
1771
2017
|
}) => {
|
|
1772
|
-
const [localValue, setLocalValue] = (0,
|
|
1773
|
-
const [isDragging, setIsDragging] = (0,
|
|
1774
|
-
(0,
|
|
2018
|
+
const [localValue, setLocalValue] = (0, import_react8.useState)(value);
|
|
2019
|
+
const [isDragging, setIsDragging] = (0, import_react8.useState)(false);
|
|
2020
|
+
(0, import_react8.useEffect)(() => {
|
|
1775
2021
|
if (!isDragging) {
|
|
1776
2022
|
setLocalValue(value);
|
|
1777
2023
|
}
|
|
1778
2024
|
}, [value, isDragging]);
|
|
1779
2025
|
const debouncedOnChange = useDebouncedCallback(onChange, 50);
|
|
1780
|
-
const handleChange = (0,
|
|
2026
|
+
const handleChange = (0, import_react8.useCallback)(
|
|
1781
2027
|
(e) => {
|
|
1782
2028
|
const newValue = parseFloat(e.target.value);
|
|
1783
2029
|
setLocalValue(newValue);
|
|
@@ -1785,19 +2031,19 @@ var VolumeSlider = ({
|
|
|
1785
2031
|
},
|
|
1786
2032
|
[debouncedOnChange]
|
|
1787
2033
|
);
|
|
1788
|
-
const handleMouseDown = (0,
|
|
2034
|
+
const handleMouseDown = (0, import_react8.useCallback)(() => {
|
|
1789
2035
|
setIsDragging(true);
|
|
1790
2036
|
}, []);
|
|
1791
|
-
const handleMouseUp = (0,
|
|
2037
|
+
const handleMouseUp = (0, import_react8.useCallback)(() => {
|
|
1792
2038
|
setIsDragging(false);
|
|
1793
2039
|
onChange(localValue);
|
|
1794
2040
|
}, [localValue, onChange]);
|
|
1795
|
-
return /* @__PURE__ */ (0,
|
|
2041
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1796
2042
|
"div",
|
|
1797
2043
|
{
|
|
1798
2044
|
className: `flex items-center ${className}`,
|
|
1799
2045
|
title: `Volume: ${formatDb(localValue)}`,
|
|
1800
|
-
children: /* @__PURE__ */ (0,
|
|
2046
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1801
2047
|
"input",
|
|
1802
2048
|
{
|
|
1803
2049
|
type: "range",
|
|
@@ -1837,8 +2083,8 @@ var VolumeSlider = ({
|
|
|
1837
2083
|
};
|
|
1838
2084
|
|
|
1839
2085
|
// src/components/PanSlider.tsx
|
|
1840
|
-
var
|
|
1841
|
-
var
|
|
2086
|
+
var import_react9 = require("react");
|
|
2087
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1842
2088
|
function toPanDisplay(value) {
|
|
1843
2089
|
if (Math.abs(value) < 0.02) {
|
|
1844
2090
|
return "Center";
|
|
@@ -1847,12 +2093,12 @@ function toPanDisplay(value) {
|
|
|
1847
2093
|
return value < 0 ? `L${percent}` : `R${percent}`;
|
|
1848
2094
|
}
|
|
1849
2095
|
function useDebouncedCallback2(callback, delay) {
|
|
1850
|
-
const timeoutRef = (0,
|
|
1851
|
-
const callbackRef = (0,
|
|
1852
|
-
(0,
|
|
2096
|
+
const timeoutRef = (0, import_react9.useRef)(null);
|
|
2097
|
+
const callbackRef = (0, import_react9.useRef)(callback);
|
|
2098
|
+
(0, import_react9.useEffect)(() => {
|
|
1853
2099
|
callbackRef.current = callback;
|
|
1854
2100
|
}, [callback]);
|
|
1855
|
-
const debouncedCallback = (0,
|
|
2101
|
+
const debouncedCallback = (0, import_react9.useCallback)(
|
|
1856
2102
|
(...args) => {
|
|
1857
2103
|
if (timeoutRef.current) {
|
|
1858
2104
|
clearTimeout(timeoutRef.current);
|
|
@@ -1863,7 +2109,7 @@ function useDebouncedCallback2(callback, delay) {
|
|
|
1863
2109
|
},
|
|
1864
2110
|
[delay]
|
|
1865
2111
|
);
|
|
1866
|
-
(0,
|
|
2112
|
+
(0, import_react9.useEffect)(() => {
|
|
1867
2113
|
return () => {
|
|
1868
2114
|
if (timeoutRef.current) {
|
|
1869
2115
|
clearTimeout(timeoutRef.current);
|
|
@@ -1878,15 +2124,15 @@ var PanSlider = ({
|
|
|
1878
2124
|
disabled = false,
|
|
1879
2125
|
className = ""
|
|
1880
2126
|
}) => {
|
|
1881
|
-
const [localValue, setLocalValue] = (0,
|
|
1882
|
-
const [isDragging, setIsDragging] = (0,
|
|
1883
|
-
(0,
|
|
2127
|
+
const [localValue, setLocalValue] = (0, import_react9.useState)(value);
|
|
2128
|
+
const [isDragging, setIsDragging] = (0, import_react9.useState)(false);
|
|
2129
|
+
(0, import_react9.useEffect)(() => {
|
|
1884
2130
|
if (!isDragging) {
|
|
1885
2131
|
setLocalValue(value);
|
|
1886
2132
|
}
|
|
1887
2133
|
}, [value, isDragging]);
|
|
1888
2134
|
const debouncedOnChange = useDebouncedCallback2(onChange, 50);
|
|
1889
|
-
const handleChange = (0,
|
|
2135
|
+
const handleChange = (0, import_react9.useCallback)(
|
|
1890
2136
|
(e) => {
|
|
1891
2137
|
const newValue = parseFloat(e.target.value);
|
|
1892
2138
|
setLocalValue(newValue);
|
|
@@ -1894,23 +2140,23 @@ var PanSlider = ({
|
|
|
1894
2140
|
},
|
|
1895
2141
|
[debouncedOnChange]
|
|
1896
2142
|
);
|
|
1897
|
-
const handleMouseDown = (0,
|
|
2143
|
+
const handleMouseDown = (0, import_react9.useCallback)(() => {
|
|
1898
2144
|
setIsDragging(true);
|
|
1899
2145
|
}, []);
|
|
1900
|
-
const handleMouseUp = (0,
|
|
2146
|
+
const handleMouseUp = (0, import_react9.useCallback)(() => {
|
|
1901
2147
|
setIsDragging(false);
|
|
1902
2148
|
onChange(localValue);
|
|
1903
2149
|
}, [localValue, onChange]);
|
|
1904
|
-
const handleDoubleClick = (0,
|
|
2150
|
+
const handleDoubleClick = (0, import_react9.useCallback)(() => {
|
|
1905
2151
|
setLocalValue(0);
|
|
1906
2152
|
onChange(0);
|
|
1907
2153
|
}, [onChange]);
|
|
1908
|
-
return /* @__PURE__ */ (0,
|
|
2154
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1909
2155
|
"div",
|
|
1910
2156
|
{
|
|
1911
2157
|
className: `flex items-center ${className}`,
|
|
1912
2158
|
title: `Pan: ${toPanDisplay(localValue)}`,
|
|
1913
|
-
children: /* @__PURE__ */ (0,
|
|
2159
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1914
2160
|
"input",
|
|
1915
2161
|
{
|
|
1916
2162
|
type: "range",
|
|
@@ -1951,8 +2197,8 @@ var PanSlider = ({
|
|
|
1951
2197
|
};
|
|
1952
2198
|
|
|
1953
2199
|
// src/components/SorceryProgressBar.tsx
|
|
1954
|
-
var
|
|
1955
|
-
var
|
|
2200
|
+
var import_react10 = require("react");
|
|
2201
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1956
2202
|
function calculateTimeBasedTarget(elapsedMs, estimatedDurationMs) {
|
|
1957
2203
|
const t = elapsedMs / estimatedDurationMs;
|
|
1958
2204
|
if (t <= 0) return 0;
|
|
@@ -1997,20 +2243,20 @@ function SorceryProgressBar({
|
|
|
1997
2243
|
onProgressChange,
|
|
1998
2244
|
estimatedDurationMs
|
|
1999
2245
|
}) {
|
|
2000
|
-
const [progress, setProgress] = (0,
|
|
2001
|
-
const timerRef = (0,
|
|
2002
|
-
const isLoadingRef = (0,
|
|
2003
|
-
const hasStartedRef = (0,
|
|
2004
|
-
const startTimeRef = (0,
|
|
2005
|
-
const onProgressChangeRef = (0,
|
|
2006
|
-
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);
|
|
2007
2253
|
onProgressChangeRef.current = onProgressChange;
|
|
2008
2254
|
onCompleteRef.current = onComplete;
|
|
2009
|
-
const initialProgressRef = (0,
|
|
2255
|
+
const initialProgressRef = (0, import_react10.useRef)(initialProgress);
|
|
2010
2256
|
initialProgressRef.current = initialProgress;
|
|
2011
|
-
const estimatedDurationMsRef = (0,
|
|
2257
|
+
const estimatedDurationMsRef = (0, import_react10.useRef)(estimatedDurationMs);
|
|
2012
2258
|
estimatedDurationMsRef.current = estimatedDurationMs;
|
|
2013
|
-
(0,
|
|
2259
|
+
(0, import_react10.useEffect)(() => {
|
|
2014
2260
|
const wasLoading = isLoadingRef.current;
|
|
2015
2261
|
isLoadingRef.current = isLoading;
|
|
2016
2262
|
if (isLoading && !wasLoading) {
|
|
@@ -2072,12 +2318,12 @@ function SorceryProgressBar({
|
|
|
2072
2318
|
const displayProgress = Math.floor(progress);
|
|
2073
2319
|
const isComplete = !isLoading && progress === 100;
|
|
2074
2320
|
const transitionDuration = progress < 50 ? "300ms" : progress < 80 ? "500ms" : "700ms";
|
|
2075
|
-
return /* @__PURE__ */ (0,
|
|
2321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2076
2322
|
"div",
|
|
2077
2323
|
{
|
|
2078
2324
|
className: `relative w-full ${heightClass} bg-sas-panel-alt border border-sas-border rounded-sm overflow-hidden shadow-inner`,
|
|
2079
2325
|
children: [
|
|
2080
|
-
/* @__PURE__ */ (0,
|
|
2326
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2081
2327
|
"div",
|
|
2082
2328
|
{
|
|
2083
2329
|
className: `
|
|
@@ -2095,13 +2341,13 @@ function SorceryProgressBar({
|
|
|
2095
2341
|
}
|
|
2096
2342
|
}
|
|
2097
2343
|
),
|
|
2098
|
-
/* @__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: [
|
|
2099
2345
|
statusText,
|
|
2100
2346
|
" ",
|
|
2101
2347
|
displayProgress,
|
|
2102
2348
|
"%"
|
|
2103
|
-
] }) : isComplete ? /* @__PURE__ */ (0,
|
|
2104
|
-
/* @__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)(
|
|
2105
2351
|
"div",
|
|
2106
2352
|
{
|
|
2107
2353
|
className: "absolute inset-0 pointer-events-none opacity-10",
|
|
@@ -2122,7 +2368,7 @@ function SorceryProgressBar({
|
|
|
2122
2368
|
}
|
|
2123
2369
|
|
|
2124
2370
|
// src/components/TrackRow.tsx
|
|
2125
|
-
var
|
|
2371
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2126
2372
|
function TrackRow({
|
|
2127
2373
|
track,
|
|
2128
2374
|
prompt,
|
|
@@ -2151,6 +2397,7 @@ function TrackRow({
|
|
|
2151
2397
|
onFxToggle,
|
|
2152
2398
|
onFxPresetChange,
|
|
2153
2399
|
onFxDryWetChange,
|
|
2400
|
+
externalFxHost,
|
|
2154
2401
|
onToggleFxDrawer,
|
|
2155
2402
|
onProgressChange,
|
|
2156
2403
|
accentColor = "#A78BFA",
|
|
@@ -2181,7 +2428,7 @@ function TrackRow({
|
|
|
2181
2428
|
levels
|
|
2182
2429
|
}) {
|
|
2183
2430
|
const { muted: isMuted, solo: isSoloed, volume: currentVolume, pan: currentPan } = runtimeState;
|
|
2184
|
-
const [confirmDelete, setConfirmDelete] =
|
|
2431
|
+
const [confirmDelete, setConfirmDelete] = import_react11.default.useState(false);
|
|
2185
2432
|
const needsGeneration = !!(prompt?.trim() && !hasMidi && !isGenerating);
|
|
2186
2433
|
const hasFxActive = Object.values(fxDetailState).some(
|
|
2187
2434
|
(d) => d.enabled
|
|
@@ -2196,8 +2443,8 @@ function TrackRow({
|
|
|
2196
2443
|
};
|
|
2197
2444
|
const borderColorStyle = needsGeneration ? void 0 : accentColor;
|
|
2198
2445
|
const borderClass = needsGeneration ? "border-amber-400 animate-pulse" : "border-sas-border";
|
|
2199
|
-
return /* @__PURE__ */ (0,
|
|
2200
|
-
/* @__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)(
|
|
2201
2448
|
"div",
|
|
2202
2449
|
{
|
|
2203
2450
|
"data-testid": "sdk-track-row",
|
|
@@ -2207,7 +2454,7 @@ function TrackRow({
|
|
|
2207
2454
|
borderLeftWidth: "3px"
|
|
2208
2455
|
},
|
|
2209
2456
|
children: [
|
|
2210
|
-
drag && /* @__PURE__ */ (0,
|
|
2457
|
+
drag && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2211
2458
|
"div",
|
|
2212
2459
|
{
|
|
2213
2460
|
"data-testid": "sdk-drag-handle",
|
|
@@ -2215,10 +2462,10 @@ function TrackRow({
|
|
|
2215
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",
|
|
2216
2463
|
title: "Drag to reorder",
|
|
2217
2464
|
"aria-label": "Drag to reorder track",
|
|
2218
|
-
children: /* @__PURE__ */ (0,
|
|
2465
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react.GripVertical, { className: "w-3.5 h-3.5", strokeWidth: 2 })
|
|
2219
2466
|
}
|
|
2220
2467
|
),
|
|
2221
|
-
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)(
|
|
2222
2469
|
SorceryProgressBar,
|
|
2223
2470
|
{
|
|
2224
2471
|
isLoading: true,
|
|
@@ -2229,14 +2476,14 @@ function TrackRow({
|
|
|
2229
2476
|
estimatedDurationMs: estimatedGenerationMs
|
|
2230
2477
|
}
|
|
2231
2478
|
) }),
|
|
2232
|
-
/* @__PURE__ */ (0,
|
|
2479
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2233
2480
|
"div",
|
|
2234
2481
|
{
|
|
2235
2482
|
"data-testid": "sdk-track-content",
|
|
2236
2483
|
className: `flex flex-col flex-1 min-w-0 relative z-10 transition-opacity ${soloedOut ? "opacity-40" : ""}`,
|
|
2237
2484
|
title: soloedOut ? "Silenced \u2014 another track is soloed" : void 0,
|
|
2238
2485
|
children: [
|
|
2239
|
-
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ (0,
|
|
2486
|
+
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2240
2487
|
"input",
|
|
2241
2488
|
{
|
|
2242
2489
|
type: "text",
|
|
@@ -2249,10 +2496,10 @@ function TrackRow({
|
|
|
2249
2496
|
className: "sas-input w-full px-2 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed"
|
|
2250
2497
|
}
|
|
2251
2498
|
) : null,
|
|
2252
|
-
/* @__PURE__ */ (0,
|
|
2253
|
-
track.name && /* @__PURE__ */ (0,
|
|
2254
|
-
/* @__PURE__ */ (0,
|
|
2255
|
-
/* @__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)(
|
|
2256
2503
|
VolumeSlider,
|
|
2257
2504
|
{
|
|
2258
2505
|
value: currentVolume,
|
|
@@ -2261,8 +2508,8 @@ function TrackRow({
|
|
|
2261
2508
|
className: "flex-1 min-w-[40px]"
|
|
2262
2509
|
}
|
|
2263
2510
|
),
|
|
2264
|
-
/* @__PURE__ */ (0,
|
|
2265
|
-
/* @__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)(
|
|
2266
2513
|
PanSlider,
|
|
2267
2514
|
{
|
|
2268
2515
|
value: currentPan,
|
|
@@ -2275,27 +2522,27 @@ function TrackRow({
|
|
|
2275
2522
|
]
|
|
2276
2523
|
}
|
|
2277
2524
|
),
|
|
2278
|
-
error && /* @__PURE__ */ (0,
|
|
2525
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2279
2526
|
"div",
|
|
2280
2527
|
{
|
|
2281
2528
|
"data-testid": "sdk-error-indicator",
|
|
2282
2529
|
className: "flex-shrink-0 relative z-10 self-stretch flex items-center px-1 group cursor-help",
|
|
2283
2530
|
title: error,
|
|
2284
|
-
children: /* @__PURE__ */ (0,
|
|
2285
|
-
/* @__PURE__ */ (0,
|
|
2531
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative", children: [
|
|
2532
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2286
2533
|
import_lucide_react.AlertCircle,
|
|
2287
2534
|
{
|
|
2288
2535
|
className: "w-5 h-5 text-red-500 animate-pulse",
|
|
2289
2536
|
strokeWidth: 2.5
|
|
2290
2537
|
}
|
|
2291
2538
|
),
|
|
2292
|
-
/* @__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 })
|
|
2293
2540
|
] })
|
|
2294
2541
|
}
|
|
2295
2542
|
),
|
|
2296
|
-
/* @__PURE__ */ (0,
|
|
2297
|
-
/* @__PURE__ */ (0,
|
|
2298
|
-
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)(
|
|
2299
2546
|
"button",
|
|
2300
2547
|
{
|
|
2301
2548
|
"data-testid": "sdk-generate-button",
|
|
@@ -2306,7 +2553,7 @@ function TrackRow({
|
|
|
2306
2553
|
children: "Create"
|
|
2307
2554
|
}
|
|
2308
2555
|
),
|
|
2309
|
-
onCopy && /* @__PURE__ */ (0,
|
|
2556
|
+
onCopy && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2310
2557
|
"button",
|
|
2311
2558
|
{
|
|
2312
2559
|
"data-testid": "sdk-copy-button",
|
|
@@ -2317,7 +2564,7 @@ function TrackRow({
|
|
|
2317
2564
|
children: "Copy"
|
|
2318
2565
|
}
|
|
2319
2566
|
),
|
|
2320
|
-
/* @__PURE__ */ (0,
|
|
2567
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2321
2568
|
"button",
|
|
2322
2569
|
{
|
|
2323
2570
|
"data-testid": "sdk-mute-button",
|
|
@@ -2327,7 +2574,7 @@ function TrackRow({
|
|
|
2327
2574
|
children: "M"
|
|
2328
2575
|
}
|
|
2329
2576
|
),
|
|
2330
|
-
onDelete && /* @__PURE__ */ (0,
|
|
2577
|
+
onDelete && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2331
2578
|
"button",
|
|
2332
2579
|
{
|
|
2333
2580
|
"data-testid": "sdk-delete-button",
|
|
@@ -2338,8 +2585,8 @@ function TrackRow({
|
|
|
2338
2585
|
}
|
|
2339
2586
|
)
|
|
2340
2587
|
] }),
|
|
2341
|
-
/* @__PURE__ */ (0,
|
|
2342
|
-
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)(
|
|
2343
2590
|
"button",
|
|
2344
2591
|
{
|
|
2345
2592
|
"data-testid": "sdk-shuffle-button",
|
|
@@ -2350,7 +2597,7 @@ function TrackRow({
|
|
|
2350
2597
|
children: "Shuffle"
|
|
2351
2598
|
}
|
|
2352
2599
|
),
|
|
2353
|
-
onToggleFxDrawer && /* @__PURE__ */ (0,
|
|
2600
|
+
onToggleFxDrawer && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2354
2601
|
"button",
|
|
2355
2602
|
{
|
|
2356
2603
|
"data-testid": "sdk-fx-button",
|
|
@@ -2361,7 +2608,7 @@ function TrackRow({
|
|
|
2361
2608
|
children: "FX"
|
|
2362
2609
|
}
|
|
2363
2610
|
),
|
|
2364
|
-
/* @__PURE__ */ (0,
|
|
2611
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2365
2612
|
"button",
|
|
2366
2613
|
{
|
|
2367
2614
|
"data-testid": "sdk-solo-button",
|
|
@@ -2372,7 +2619,7 @@ function TrackRow({
|
|
|
2372
2619
|
children: "S"
|
|
2373
2620
|
}
|
|
2374
2621
|
),
|
|
2375
|
-
onToggleDrawer && /* @__PURE__ */ (0,
|
|
2622
|
+
onToggleDrawer && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2376
2623
|
"button",
|
|
2377
2624
|
{
|
|
2378
2625
|
"data-testid": "sdk-plugin-button",
|
|
@@ -2380,7 +2627,7 @@ function TrackRow({
|
|
|
2380
2627
|
disabled: isGenerating,
|
|
2381
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"}`,
|
|
2382
2629
|
title: `Sound \u2014 presets & history${instrumentMissing ? " (instrument missing)" : ""}`,
|
|
2383
|
-
children: /* @__PURE__ */ (0,
|
|
2630
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react.ChevronDown, { className: "w-3 h-3", strokeWidth: 2.5 })
|
|
2384
2631
|
}
|
|
2385
2632
|
)
|
|
2386
2633
|
] })
|
|
@@ -2388,13 +2635,13 @@ function TrackRow({
|
|
|
2388
2635
|
]
|
|
2389
2636
|
}
|
|
2390
2637
|
),
|
|
2391
|
-
levels && /* @__PURE__ */ (0,
|
|
2392
|
-
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)(
|
|
2393
2640
|
"div",
|
|
2394
2641
|
{
|
|
2395
2642
|
"data-testid": "sdk-track-drawer",
|
|
2396
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",
|
|
2397
|
-
children: /* @__PURE__ */ (0,
|
|
2644
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2398
2645
|
TrackDrawer,
|
|
2399
2646
|
{
|
|
2400
2647
|
activeTab: drawerTab,
|
|
@@ -2404,6 +2651,7 @@ function TrackRow({
|
|
|
2404
2651
|
onFxToggle,
|
|
2405
2652
|
onFxPresetChange,
|
|
2406
2653
|
onFxDryWetChange,
|
|
2654
|
+
externalFxHost,
|
|
2407
2655
|
fxDisabled: isGenerating,
|
|
2408
2656
|
instruments: availableInstruments,
|
|
2409
2657
|
currentPluginId: currentInstrumentPluginId ?? null,
|
|
@@ -2430,13 +2678,13 @@ function TrackRow({
|
|
|
2430
2678
|
)
|
|
2431
2679
|
}
|
|
2432
2680
|
),
|
|
2433
|
-
/* @__PURE__ */ (0,
|
|
2681
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2434
2682
|
ConfirmDialog,
|
|
2435
2683
|
{
|
|
2436
2684
|
open: confirmDelete,
|
|
2437
2685
|
title: "Delete track?",
|
|
2438
|
-
message: /* @__PURE__ */ (0,
|
|
2439
|
-
/* @__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" }),
|
|
2440
2688
|
" will be permanently removed from this scene. This cannot be undone."
|
|
2441
2689
|
] }),
|
|
2442
2690
|
confirmLabel: "Delete",
|
|
@@ -2452,13 +2700,13 @@ function TrackRow({
|
|
|
2452
2700
|
}
|
|
2453
2701
|
|
|
2454
2702
|
// src/components/CrossfadeTrackRow.tsx
|
|
2455
|
-
var
|
|
2456
|
-
var
|
|
2703
|
+
var import_react12 = __toESM(require("react"));
|
|
2704
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2457
2705
|
function LayerCaption({ tag, layer }) {
|
|
2458
|
-
return /* @__PURE__ */ (0,
|
|
2459
|
-
/* @__PURE__ */ (0,
|
|
2460
|
-
/* @__PURE__ */ (0,
|
|
2461
|
-
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: [
|
|
2462
2710
|
"\xB7 ",
|
|
2463
2711
|
layer.soundLabel
|
|
2464
2712
|
] })
|
|
@@ -2477,8 +2725,8 @@ function CrossfadeTrackRow({
|
|
|
2477
2725
|
levels,
|
|
2478
2726
|
accentColor = "#9333EA"
|
|
2479
2727
|
}) {
|
|
2480
|
-
const [confirmDelete, setConfirmDelete] =
|
|
2481
|
-
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)(
|
|
2482
2730
|
TrackRow,
|
|
2483
2731
|
{
|
|
2484
2732
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -2488,23 +2736,23 @@ function CrossfadeTrackRow({
|
|
|
2488
2736
|
drawerTab: "fx",
|
|
2489
2737
|
levels,
|
|
2490
2738
|
accentColor,
|
|
2491
|
-
contentSlot: /* @__PURE__ */ (0,
|
|
2739
|
+
contentSlot: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LayerCaption, { tag, layer }),
|
|
2492
2740
|
onMuteToggle,
|
|
2493
2741
|
onSoloToggle,
|
|
2494
2742
|
onVolumeChange: (v) => onVolumeChange(slot, v),
|
|
2495
2743
|
onPanChange: (p) => onPanChange(slot, p)
|
|
2496
2744
|
}
|
|
2497
2745
|
);
|
|
2498
|
-
return /* @__PURE__ */ (0,
|
|
2746
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2499
2747
|
"div",
|
|
2500
2748
|
{
|
|
2501
2749
|
"data-testid": "crossfade-track-row",
|
|
2502
2750
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
2503
2751
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
2504
2752
|
children: [
|
|
2505
|
-
/* @__PURE__ */ (0,
|
|
2506
|
-
/* @__PURE__ */ (0,
|
|
2507
|
-
/* @__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)(
|
|
2508
2756
|
"button",
|
|
2509
2757
|
{
|
|
2510
2758
|
"data-testid": "crossfade-delete-button",
|
|
@@ -2517,8 +2765,8 @@ function CrossfadeTrackRow({
|
|
|
2517
2765
|
)
|
|
2518
2766
|
] }),
|
|
2519
2767
|
renderLayer(origin, "origin", "Origin"),
|
|
2520
|
-
/* @__PURE__ */ (0,
|
|
2521
|
-
/* @__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)(
|
|
2522
2770
|
"span",
|
|
2523
2771
|
{
|
|
2524
2772
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -2526,7 +2774,7 @@ function CrossfadeTrackRow({
|
|
|
2526
2774
|
children: origin.sourceName ?? origin.name
|
|
2527
2775
|
}
|
|
2528
2776
|
),
|
|
2529
|
-
/* @__PURE__ */ (0,
|
|
2777
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2530
2778
|
"input",
|
|
2531
2779
|
{
|
|
2532
2780
|
type: "range",
|
|
@@ -2542,7 +2790,7 @@ function CrossfadeTrackRow({
|
|
|
2542
2790
|
"aria-label": "Crossfade position"
|
|
2543
2791
|
}
|
|
2544
2792
|
),
|
|
2545
|
-
/* @__PURE__ */ (0,
|
|
2793
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2546
2794
|
"span",
|
|
2547
2795
|
{
|
|
2548
2796
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -2552,12 +2800,12 @@ function CrossfadeTrackRow({
|
|
|
2552
2800
|
)
|
|
2553
2801
|
] }),
|
|
2554
2802
|
renderLayer(target, "target", "Target"),
|
|
2555
|
-
/* @__PURE__ */ (0,
|
|
2803
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2556
2804
|
ConfirmDialog,
|
|
2557
2805
|
{
|
|
2558
2806
|
open: confirmDelete,
|
|
2559
2807
|
title: "Delete crossfade?",
|
|
2560
|
-
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." }),
|
|
2561
2809
|
confirmLabel: "Delete",
|
|
2562
2810
|
onConfirm: () => {
|
|
2563
2811
|
setConfirmDelete(false);
|
|
@@ -2800,22 +3048,22 @@ function defaultFadeGesture(role) {
|
|
|
2800
3048
|
}
|
|
2801
3049
|
|
|
2802
3050
|
// src/components/FadeTrackRow.tsx
|
|
2803
|
-
var
|
|
2804
|
-
var
|
|
3051
|
+
var import_react13 = __toESM(require("react"));
|
|
3052
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2805
3053
|
function FadeCaption({
|
|
2806
3054
|
layer,
|
|
2807
3055
|
direction,
|
|
2808
3056
|
gesture
|
|
2809
3057
|
}) {
|
|
2810
3058
|
const tag = direction === "in" ? "Fade in" : "Fade out";
|
|
2811
|
-
return /* @__PURE__ */ (0,
|
|
2812
|
-
/* @__PURE__ */ (0,
|
|
2813
|
-
/* @__PURE__ */ (0,
|
|
2814
|
-
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: [
|
|
2815
3063
|
"\xB7 ",
|
|
2816
3064
|
layer.soundLabel
|
|
2817
3065
|
] }),
|
|
2818
|
-
/* @__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: [
|
|
2819
3067
|
"\xB7 ",
|
|
2820
3068
|
gesture
|
|
2821
3069
|
] })
|
|
@@ -2836,20 +3084,20 @@ function FadeTrackRow({
|
|
|
2836
3084
|
levels,
|
|
2837
3085
|
accentColor = "#9333EA"
|
|
2838
3086
|
}) {
|
|
2839
|
-
const [confirmDelete, setConfirmDelete] =
|
|
3087
|
+
const [confirmDelete, setConfirmDelete] = import_react13.default.useState(false);
|
|
2840
3088
|
const leftLabel = direction === "in" ? "(silent)" : layer.sourceName ?? layer.name;
|
|
2841
3089
|
const rightLabel = direction === "in" ? layer.sourceName ?? layer.name : "(silent)";
|
|
2842
3090
|
const verb = effect && effect !== "fade" ? effect.charAt(0).toUpperCase() + effect.slice(1) : "Fade";
|
|
2843
3091
|
const badge = direction === "in" ? `\u2197 ${verb} in` : `\u2198 ${verb} out`;
|
|
2844
|
-
return /* @__PURE__ */ (0,
|
|
3092
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2845
3093
|
"div",
|
|
2846
3094
|
{
|
|
2847
3095
|
"data-testid": "fade-track-row",
|
|
2848
3096
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
2849
3097
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
2850
3098
|
children: [
|
|
2851
|
-
/* @__PURE__ */ (0,
|
|
2852
|
-
/* @__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)(
|
|
2853
3101
|
"span",
|
|
2854
3102
|
{
|
|
2855
3103
|
"data-testid": "fade-direction-badge",
|
|
@@ -2858,7 +3106,7 @@ function FadeTrackRow({
|
|
|
2858
3106
|
children: badge
|
|
2859
3107
|
}
|
|
2860
3108
|
),
|
|
2861
|
-
/* @__PURE__ */ (0,
|
|
3109
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2862
3110
|
"button",
|
|
2863
3111
|
{
|
|
2864
3112
|
"data-testid": "fade-delete-button",
|
|
@@ -2870,7 +3118,7 @@ function FadeTrackRow({
|
|
|
2870
3118
|
}
|
|
2871
3119
|
)
|
|
2872
3120
|
] }),
|
|
2873
|
-
/* @__PURE__ */ (0,
|
|
3121
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2874
3122
|
TrackRow,
|
|
2875
3123
|
{
|
|
2876
3124
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -2880,15 +3128,15 @@ function FadeTrackRow({
|
|
|
2880
3128
|
drawerTab: "fx",
|
|
2881
3129
|
levels,
|
|
2882
3130
|
accentColor,
|
|
2883
|
-
contentSlot: /* @__PURE__ */ (0,
|
|
3131
|
+
contentSlot: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(FadeCaption, { layer, direction, gesture }),
|
|
2884
3132
|
onMuteToggle,
|
|
2885
3133
|
onSoloToggle,
|
|
2886
3134
|
onVolumeChange,
|
|
2887
3135
|
onPanChange
|
|
2888
3136
|
}
|
|
2889
3137
|
),
|
|
2890
|
-
/* @__PURE__ */ (0,
|
|
2891
|
-
/* @__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)(
|
|
2892
3140
|
"span",
|
|
2893
3141
|
{
|
|
2894
3142
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -2896,7 +3144,7 @@ function FadeTrackRow({
|
|
|
2896
3144
|
children: leftLabel
|
|
2897
3145
|
}
|
|
2898
3146
|
),
|
|
2899
|
-
/* @__PURE__ */ (0,
|
|
3147
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2900
3148
|
"input",
|
|
2901
3149
|
{
|
|
2902
3150
|
type: "range",
|
|
@@ -2912,7 +3160,7 @@ function FadeTrackRow({
|
|
|
2912
3160
|
"aria-label": "Fade position"
|
|
2913
3161
|
}
|
|
2914
3162
|
),
|
|
2915
|
-
/* @__PURE__ */ (0,
|
|
3163
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2916
3164
|
"span",
|
|
2917
3165
|
{
|
|
2918
3166
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -2921,12 +3169,12 @@ function FadeTrackRow({
|
|
|
2921
3169
|
}
|
|
2922
3170
|
)
|
|
2923
3171
|
] }),
|
|
2924
|
-
/* @__PURE__ */ (0,
|
|
3172
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2925
3173
|
ConfirmDialog,
|
|
2926
3174
|
{
|
|
2927
3175
|
open: confirmDelete,
|
|
2928
3176
|
title: "Delete fade?",
|
|
2929
|
-
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." }),
|
|
2930
3178
|
confirmLabel: "Delete",
|
|
2931
3179
|
onConfirm: () => {
|
|
2932
3180
|
setConfirmDelete(false);
|
|
@@ -2942,8 +3190,8 @@ function FadeTrackRow({
|
|
|
2942
3190
|
}
|
|
2943
3191
|
|
|
2944
3192
|
// src/components/FadeModal.tsx
|
|
2945
|
-
var
|
|
2946
|
-
var
|
|
3193
|
+
var import_react14 = require("react");
|
|
3194
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2947
3195
|
function shortId(dbId) {
|
|
2948
3196
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
2949
3197
|
}
|
|
@@ -2986,7 +3234,7 @@ function OrphanRow({
|
|
|
2986
3234
|
}) {
|
|
2987
3235
|
const primary = track.prompt?.trim() || track.name;
|
|
2988
3236
|
const meta = [track.role, shortId(track.dbId), gesture].filter(Boolean).join(" \xB7 ");
|
|
2989
|
-
return /* @__PURE__ */ (0,
|
|
3237
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2990
3238
|
"button",
|
|
2991
3239
|
{
|
|
2992
3240
|
type: "button",
|
|
@@ -2998,8 +3246,8 @@ function OrphanRow({
|
|
|
2998
3246
|
disabled,
|
|
2999
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"}`,
|
|
3000
3248
|
children: [
|
|
3001
|
-
/* @__PURE__ */ (0,
|
|
3002
|
-
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 })
|
|
3003
3251
|
]
|
|
3004
3252
|
}
|
|
3005
3253
|
);
|
|
@@ -3016,14 +3264,14 @@ function FadeModal({
|
|
|
3016
3264
|
onCreate,
|
|
3017
3265
|
testIdPrefix = "fade-modal"
|
|
3018
3266
|
}) {
|
|
3019
|
-
const [load, setLoad] = (0,
|
|
3020
|
-
const [selectedDbId, setSelectedDbId] = (0,
|
|
3021
|
-
const [isCreating, setIsCreating] = (0,
|
|
3022
|
-
const [error, setError] = (0,
|
|
3023
|
-
const [fromName, setFromName] = (0,
|
|
3024
|
-
const [toName, setToName] = (0,
|
|
3025
|
-
const cancelRef = (0,
|
|
3026
|
-
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 () => {
|
|
3027
3275
|
if (!host.listSceneFamilyTracks) {
|
|
3028
3276
|
setLoad({ status: "error", message: "This host does not support fades." });
|
|
3029
3277
|
return;
|
|
@@ -3043,7 +3291,7 @@ function FadeModal({
|
|
|
3043
3291
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
3044
3292
|
}
|
|
3045
3293
|
}, [host, fromSceneId, toSceneId]);
|
|
3046
|
-
(0,
|
|
3294
|
+
(0, import_react14.useEffect)(() => {
|
|
3047
3295
|
if (open) {
|
|
3048
3296
|
setError(null);
|
|
3049
3297
|
setIsCreating(false);
|
|
@@ -3051,29 +3299,29 @@ function FadeModal({
|
|
|
3051
3299
|
void refresh();
|
|
3052
3300
|
}
|
|
3053
3301
|
}, [open, refresh]);
|
|
3054
|
-
const excludeSet = (0,
|
|
3055
|
-
const { fadeOut, fadeIn } = (0,
|
|
3302
|
+
const excludeSet = (0, import_react14.useMemo)(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
3303
|
+
const { fadeOut, fadeIn } = (0, import_react14.useMemo)(
|
|
3056
3304
|
() => load.status === "ready" ? computeOrphans(load.from, load.to, excludeSet) : { fadeOut: [], fadeIn: [] },
|
|
3057
3305
|
[load, excludeSet]
|
|
3058
3306
|
);
|
|
3059
|
-
const allOrphans = (0,
|
|
3307
|
+
const allOrphans = (0, import_react14.useMemo)(
|
|
3060
3308
|
() => [
|
|
3061
3309
|
...fadeOut.map((t) => ({ track: t, direction: "out" })),
|
|
3062
3310
|
...fadeIn.map((t) => ({ track: t, direction: "in" }))
|
|
3063
3311
|
],
|
|
3064
3312
|
[fadeOut, fadeIn]
|
|
3065
3313
|
);
|
|
3066
|
-
(0,
|
|
3314
|
+
(0, import_react14.useEffect)(() => {
|
|
3067
3315
|
if (!allOrphans.some((o) => o.track.dbId === selectedDbId)) {
|
|
3068
3316
|
setSelectedDbId(allOrphans[0]?.track.dbId ?? "");
|
|
3069
3317
|
}
|
|
3070
3318
|
}, [allOrphans, selectedDbId]);
|
|
3071
3319
|
const selected = allOrphans.find((o) => o.track.dbId === selectedDbId) ?? null;
|
|
3072
3320
|
const canCreate = !isCreating && !!selected;
|
|
3073
|
-
const handleClose = (0,
|
|
3321
|
+
const handleClose = (0, import_react14.useCallback)(() => {
|
|
3074
3322
|
if (!isCreating) onClose();
|
|
3075
3323
|
}, [isCreating, onClose]);
|
|
3076
|
-
const handleCreate = (0,
|
|
3324
|
+
const handleCreate = (0, import_react14.useCallback)(async () => {
|
|
3077
3325
|
if (!selected) return;
|
|
3078
3326
|
setIsCreating(true);
|
|
3079
3327
|
setError(null);
|
|
@@ -3094,16 +3342,16 @@ function FadeModal({
|
|
|
3094
3342
|
if (!open) return null;
|
|
3095
3343
|
const renderSection = (heading, list, section) => {
|
|
3096
3344
|
if (list.length === 0) return null;
|
|
3097
|
-
return /* @__PURE__ */ (0,
|
|
3098
|
-
/* @__PURE__ */ (0,
|
|
3099
|
-
/* @__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)(
|
|
3100
3348
|
"div",
|
|
3101
3349
|
{
|
|
3102
3350
|
role: "radiogroup",
|
|
3103
3351
|
"aria-label": heading,
|
|
3104
3352
|
"data-testid": `${testIdPrefix}-${section === "out" ? "fade-out" : "fade-in"}-list`,
|
|
3105
3353
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3106
|
-
children: list.map((t) => /* @__PURE__ */ (0,
|
|
3354
|
+
children: list.map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3107
3355
|
OrphanRow,
|
|
3108
3356
|
{
|
|
3109
3357
|
track: t,
|
|
@@ -3119,32 +3367,32 @@ function FadeModal({
|
|
|
3119
3367
|
)
|
|
3120
3368
|
] });
|
|
3121
3369
|
};
|
|
3122
|
-
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)(
|
|
3123
3371
|
"div",
|
|
3124
3372
|
{
|
|
3125
3373
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
3126
3374
|
onClick: (e) => e.stopPropagation(),
|
|
3127
3375
|
"data-testid": `${testIdPrefix}-box`,
|
|
3128
3376
|
children: [
|
|
3129
|
-
/* @__PURE__ */ (0,
|
|
3130
|
-
/* @__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: [
|
|
3131
3379
|
"Tracks with no counterpart between",
|
|
3132
3380
|
" ",
|
|
3133
|
-
/* @__PURE__ */ (0,
|
|
3381
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
3134
3382
|
" and",
|
|
3135
3383
|
" ",
|
|
3136
|
-
/* @__PURE__ */ (0,
|
|
3384
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
3137
3385
|
" can gracefully fade out (leaving) or fade in (entering) across this transition."
|
|
3138
3386
|
] }),
|
|
3139
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
3140
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
3141
|
-
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: [
|
|
3142
3390
|
renderSection(`Fade out${fromLabel ? ` (from ${fromLabel})` : ""}`, fadeOut, "out"),
|
|
3143
3391
|
renderSection(`Fade in${toLabel ? ` (to ${toLabel})` : ""}`, fadeIn, "in")
|
|
3144
3392
|
] })),
|
|
3145
|
-
error && /* @__PURE__ */ (0,
|
|
3146
|
-
/* @__PURE__ */ (0,
|
|
3147
|
-
/* @__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)(
|
|
3148
3396
|
"button",
|
|
3149
3397
|
{
|
|
3150
3398
|
ref: cancelRef,
|
|
@@ -3155,7 +3403,7 @@ function FadeModal({
|
|
|
3155
3403
|
children: "Cancel"
|
|
3156
3404
|
}
|
|
3157
3405
|
),
|
|
3158
|
-
/* @__PURE__ */ (0,
|
|
3406
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3159
3407
|
"button",
|
|
3160
3408
|
{
|
|
3161
3409
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -3172,8 +3420,8 @@ function FadeModal({
|
|
|
3172
3420
|
}
|
|
3173
3421
|
|
|
3174
3422
|
// src/components/ImportTrackModal.tsx
|
|
3175
|
-
var
|
|
3176
|
-
var
|
|
3423
|
+
var import_react15 = require("react");
|
|
3424
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
3177
3425
|
function ImportTrackModal({
|
|
3178
3426
|
host,
|
|
3179
3427
|
open,
|
|
@@ -3185,10 +3433,10 @@ function ImportTrackModal({
|
|
|
3185
3433
|
onPick,
|
|
3186
3434
|
onPortTrack
|
|
3187
3435
|
}) {
|
|
3188
|
-
const [load, setLoad] = (0,
|
|
3189
|
-
const [selectedSceneId, setSelectedSceneId] = (0,
|
|
3190
|
-
const [importingTrackId, setImportingTrackId] = (0,
|
|
3191
|
-
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 () => {
|
|
3192
3440
|
if (!host.listImportableTracks) {
|
|
3193
3441
|
setLoad({ status: "error", message: "This host does not support importing tracks." });
|
|
3194
3442
|
return;
|
|
@@ -3204,14 +3452,14 @@ function ImportTrackModal({
|
|
|
3204
3452
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load scenes." });
|
|
3205
3453
|
}
|
|
3206
3454
|
}, [host, mode, onPortTrack]);
|
|
3207
|
-
(0,
|
|
3455
|
+
(0, import_react15.useEffect)(() => {
|
|
3208
3456
|
if (open) {
|
|
3209
3457
|
setSelectedSceneId(null);
|
|
3210
3458
|
setImportingTrackId(null);
|
|
3211
3459
|
void refresh();
|
|
3212
3460
|
}
|
|
3213
3461
|
}, [open, refresh]);
|
|
3214
|
-
const handleImport = (0,
|
|
3462
|
+
const handleImport = (0, import_react15.useCallback)(
|
|
3215
3463
|
async (track, sourceSceneId, sceneName, isSameScene) => {
|
|
3216
3464
|
if (isSameScene && onPortTrack) {
|
|
3217
3465
|
if (!track.importable) return;
|
|
@@ -3252,16 +3500,16 @@ function ImportTrackModal({
|
|
|
3252
3500
|
if (!open) return null;
|
|
3253
3501
|
const scenes = load.status === "ready" ? load.scenes : [];
|
|
3254
3502
|
const selectedScene = scenes.find((s) => s.sceneId === selectedSceneId) ?? null;
|
|
3255
|
-
return /* @__PURE__ */ (0,
|
|
3503
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Modal, { open, onClose, testIdPrefix, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3256
3504
|
"div",
|
|
3257
3505
|
{
|
|
3258
3506
|
className: "w-[420px] max-h-[70vh] overflow-hidden flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
3259
3507
|
onClick: (e) => e.stopPropagation(),
|
|
3260
3508
|
"data-testid": `${testIdPrefix}-modal`,
|
|
3261
3509
|
children: [
|
|
3262
|
-
/* @__PURE__ */ (0,
|
|
3263
|
-
/* @__PURE__ */ (0,
|
|
3264
|
-
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)(
|
|
3265
3513
|
"button",
|
|
3266
3514
|
{
|
|
3267
3515
|
className: "text-sas-muted hover:text-sas-accent text-xs",
|
|
@@ -3270,9 +3518,9 @@ function ImportTrackModal({
|
|
|
3270
3518
|
children: "\u2190"
|
|
3271
3519
|
}
|
|
3272
3520
|
),
|
|
3273
|
-
/* @__PURE__ */ (0,
|
|
3521
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sm font-medium text-sas-text", children: selectedScene ? selectedScene.sceneName : title })
|
|
3274
3522
|
] }),
|
|
3275
|
-
/* @__PURE__ */ (0,
|
|
3523
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3276
3524
|
"button",
|
|
3277
3525
|
{
|
|
3278
3526
|
className: "text-sas-muted hover:text-sas-accent text-sm",
|
|
@@ -3282,30 +3530,30 @@ function ImportTrackModal({
|
|
|
3282
3530
|
}
|
|
3283
3531
|
)
|
|
3284
3532
|
] }),
|
|
3285
|
-
/* @__PURE__ */ (0,
|
|
3286
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
3287
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
3288
|
-
load.status === "ready" && scenes.length === 0 && /* @__PURE__ */ (0,
|
|
3289
|
-
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)(
|
|
3290
3538
|
"button",
|
|
3291
3539
|
{
|
|
3292
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",
|
|
3293
3541
|
onClick: () => setSelectedSceneId(scene.sceneId),
|
|
3294
3542
|
"data-testid": `${testIdPrefix}-scene`,
|
|
3295
3543
|
children: [
|
|
3296
|
-
/* @__PURE__ */ (0,
|
|
3297
|
-
/* @__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: [
|
|
3298
3546
|
scene.tracks.length,
|
|
3299
3547
|
" \u2192"
|
|
3300
3548
|
] })
|
|
3301
3549
|
]
|
|
3302
3550
|
}
|
|
3303
3551
|
) }, scene.sceneId)) }),
|
|
3304
|
-
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) => {
|
|
3305
3553
|
const busy = importingTrackId === track.trackId;
|
|
3306
3554
|
const gated = mode === "track" && !track.importable;
|
|
3307
3555
|
const disabled = gated || busy;
|
|
3308
|
-
return /* @__PURE__ */ (0,
|
|
3556
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3309
3557
|
"button",
|
|
3310
3558
|
{
|
|
3311
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"}`,
|
|
@@ -3315,14 +3563,14 @@ function ImportTrackModal({
|
|
|
3315
3563
|
"data-testid": `${testIdPrefix}-track`,
|
|
3316
3564
|
"data-importable": mode === "sound" || track.importable ? "true" : "false",
|
|
3317
3565
|
children: [
|
|
3318
|
-
/* @__PURE__ */ (0,
|
|
3566
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "truncate", children: [
|
|
3319
3567
|
track.name,
|
|
3320
|
-
track.role ? /* @__PURE__ */ (0,
|
|
3568
|
+
track.role ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-sas-muted", children: [
|
|
3321
3569
|
" \xB7 ",
|
|
3322
3570
|
track.role
|
|
3323
3571
|
] }) : null
|
|
3324
3572
|
] }),
|
|
3325
|
-
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
|
|
3326
3574
|
]
|
|
3327
3575
|
}
|
|
3328
3576
|
) }, track.dbId);
|
|
@@ -3334,8 +3582,8 @@ function ImportTrackModal({
|
|
|
3334
3582
|
}
|
|
3335
3583
|
|
|
3336
3584
|
// src/components/CrossfadeModal.tsx
|
|
3337
|
-
var
|
|
3338
|
-
var
|
|
3585
|
+
var import_react16 = require("react");
|
|
3586
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
3339
3587
|
function shortId2(dbId) {
|
|
3340
3588
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
3341
3589
|
}
|
|
@@ -3348,7 +3596,7 @@ function CandidateRow({
|
|
|
3348
3596
|
}) {
|
|
3349
3597
|
const primary = track.prompt?.trim() || track.name;
|
|
3350
3598
|
const meta = [track.role, shortId2(track.dbId)].filter(Boolean).join(" \xB7 ");
|
|
3351
|
-
return /* @__PURE__ */ (0,
|
|
3599
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3352
3600
|
"button",
|
|
3353
3601
|
{
|
|
3354
3602
|
type: "button",
|
|
@@ -3360,8 +3608,8 @@ function CandidateRow({
|
|
|
3360
3608
|
disabled,
|
|
3361
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"}`,
|
|
3362
3610
|
children: [
|
|
3363
|
-
/* @__PURE__ */ (0,
|
|
3364
|
-
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 })
|
|
3365
3613
|
]
|
|
3366
3614
|
}
|
|
3367
3615
|
);
|
|
@@ -3378,15 +3626,15 @@ function CrossfadeModal({
|
|
|
3378
3626
|
onCreate,
|
|
3379
3627
|
testIdPrefix = "crossfade-modal"
|
|
3380
3628
|
}) {
|
|
3381
|
-
const [load, setLoad] = (0,
|
|
3382
|
-
const [originDbId, setOriginDbId] = (0,
|
|
3383
|
-
const [targetDbId, setTargetDbId] = (0,
|
|
3384
|
-
const [isCreating, setIsCreating] = (0,
|
|
3385
|
-
const [error, setError] = (0,
|
|
3386
|
-
const [fromName, setFromName] = (0,
|
|
3387
|
-
const [toName, setToName] = (0,
|
|
3388
|
-
const cancelRef = (0,
|
|
3389
|
-
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 () => {
|
|
3390
3638
|
if (!host.listSceneFamilyTracks) {
|
|
3391
3639
|
setLoad({ status: "error", message: "This host does not support crossfade tracks." });
|
|
3392
3640
|
return;
|
|
@@ -3406,7 +3654,7 @@ function CrossfadeModal({
|
|
|
3406
3654
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
3407
3655
|
}
|
|
3408
3656
|
}, [host, fromSceneId, toSceneId]);
|
|
3409
|
-
(0,
|
|
3657
|
+
(0, import_react16.useEffect)(() => {
|
|
3410
3658
|
if (open) {
|
|
3411
3659
|
setError(null);
|
|
3412
3660
|
setIsCreating(false);
|
|
@@ -3415,21 +3663,21 @@ function CrossfadeModal({
|
|
|
3415
3663
|
void refresh();
|
|
3416
3664
|
}
|
|
3417
3665
|
}, [open, refresh]);
|
|
3418
|
-
const excludeSet = (0,
|
|
3419
|
-
const originCandidates = (0,
|
|
3666
|
+
const excludeSet = (0, import_react16.useMemo)(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
3667
|
+
const originCandidates = (0, import_react16.useMemo)(
|
|
3420
3668
|
() => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3421
3669
|
[load, excludeSet]
|
|
3422
3670
|
);
|
|
3423
|
-
const targetCandidates = (0,
|
|
3671
|
+
const targetCandidates = (0, import_react16.useMemo)(
|
|
3424
3672
|
() => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3425
3673
|
[load, excludeSet]
|
|
3426
3674
|
);
|
|
3427
|
-
(0,
|
|
3675
|
+
(0, import_react16.useEffect)(() => {
|
|
3428
3676
|
if (!originCandidates.some((t) => t.dbId === originDbId)) {
|
|
3429
3677
|
setOriginDbId(originCandidates[0]?.dbId ?? "");
|
|
3430
3678
|
}
|
|
3431
3679
|
}, [originCandidates, originDbId]);
|
|
3432
|
-
(0,
|
|
3680
|
+
(0, import_react16.useEffect)(() => {
|
|
3433
3681
|
if (!targetCandidates.some((t) => t.dbId === targetDbId)) {
|
|
3434
3682
|
setTargetDbId(targetCandidates[0]?.dbId ?? "");
|
|
3435
3683
|
}
|
|
@@ -3437,10 +3685,10 @@ function CrossfadeModal({
|
|
|
3437
3685
|
const originTrack = originCandidates.find((t) => t.dbId === originDbId) ?? null;
|
|
3438
3686
|
const targetTrack = targetCandidates.find((t) => t.dbId === targetDbId) ?? null;
|
|
3439
3687
|
const canCreate = !isCreating && !!originTrack && !!targetTrack;
|
|
3440
|
-
const handleClose = (0,
|
|
3688
|
+
const handleClose = (0, import_react16.useCallback)(() => {
|
|
3441
3689
|
if (!isCreating) onClose();
|
|
3442
3690
|
}, [isCreating, onClose]);
|
|
3443
|
-
const handleCreate = (0,
|
|
3691
|
+
const handleCreate = (0, import_react16.useCallback)(async () => {
|
|
3444
3692
|
if (!originTrack || !targetTrack) return;
|
|
3445
3693
|
setIsCreating(true);
|
|
3446
3694
|
setError(null);
|
|
@@ -3458,26 +3706,26 @@ function CrossfadeModal({
|
|
|
3458
3706
|
const fromLabel = fromName ?? fromSceneName ?? null;
|
|
3459
3707
|
const toLabel = toName ?? toSceneName ?? null;
|
|
3460
3708
|
if (!open) return null;
|
|
3461
|
-
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)(
|
|
3462
3710
|
"div",
|
|
3463
3711
|
{
|
|
3464
3712
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
3465
3713
|
onClick: (e) => e.stopPropagation(),
|
|
3466
3714
|
"data-testid": `${testIdPrefix}-box`,
|
|
3467
3715
|
children: [
|
|
3468
|
-
/* @__PURE__ */ (0,
|
|
3469
|
-
/* @__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: [
|
|
3470
3718
|
"Bridge a track from",
|
|
3471
3719
|
" ",
|
|
3472
|
-
/* @__PURE__ */ (0,
|
|
3720
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
3473
3721
|
" into one from",
|
|
3474
3722
|
" ",
|
|
3475
|
-
/* @__PURE__ */ (0,
|
|
3723
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
3476
3724
|
". Both layers share one generated part; each keeps its own preset."
|
|
3477
3725
|
] }),
|
|
3478
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
3479
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
3480
|
-
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)(
|
|
3481
3729
|
"div",
|
|
3482
3730
|
{
|
|
3483
3731
|
className: "text-xs text-sas-muted py-4 text-center",
|
|
@@ -3488,20 +3736,20 @@ function CrossfadeModal({
|
|
|
3488
3736
|
". Add one (or free one from another crossfade) first."
|
|
3489
3737
|
]
|
|
3490
3738
|
}
|
|
3491
|
-
) : /* @__PURE__ */ (0,
|
|
3492
|
-
/* @__PURE__ */ (0,
|
|
3493
|
-
/* @__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: [
|
|
3494
3742
|
"Origin ",
|
|
3495
3743
|
fromLabel ? `(${fromLabel})` : "(top)"
|
|
3496
3744
|
] }),
|
|
3497
|
-
/* @__PURE__ */ (0,
|
|
3745
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3498
3746
|
"div",
|
|
3499
3747
|
{
|
|
3500
3748
|
role: "radiogroup",
|
|
3501
3749
|
"aria-label": "Origin track",
|
|
3502
3750
|
"data-testid": `${testIdPrefix}-origin-list`,
|
|
3503
3751
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3504
|
-
children: originCandidates.map((t) => /* @__PURE__ */ (0,
|
|
3752
|
+
children: originCandidates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3505
3753
|
CandidateRow,
|
|
3506
3754
|
{
|
|
3507
3755
|
track: t,
|
|
@@ -3515,23 +3763,23 @@ function CrossfadeModal({
|
|
|
3515
3763
|
}
|
|
3516
3764
|
)
|
|
3517
3765
|
] }),
|
|
3518
|
-
/* @__PURE__ */ (0,
|
|
3519
|
-
/* @__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: [
|
|
3520
3768
|
"Target ",
|
|
3521
3769
|
toLabel ? `(${toLabel})` : "(bottom)"
|
|
3522
3770
|
] }),
|
|
3523
|
-
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: [
|
|
3524
3772
|
"No available tracks in ",
|
|
3525
3773
|
toLabel ?? "the target scene",
|
|
3526
3774
|
" to crossfade into."
|
|
3527
|
-
] }) : /* @__PURE__ */ (0,
|
|
3775
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3528
3776
|
"div",
|
|
3529
3777
|
{
|
|
3530
3778
|
role: "radiogroup",
|
|
3531
3779
|
"aria-label": "Target track",
|
|
3532
3780
|
"data-testid": `${testIdPrefix}-target-list`,
|
|
3533
3781
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3534
|
-
children: targetCandidates.map((t) => /* @__PURE__ */ (0,
|
|
3782
|
+
children: targetCandidates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3535
3783
|
CandidateRow,
|
|
3536
3784
|
{
|
|
3537
3785
|
track: t,
|
|
@@ -3546,9 +3794,9 @@ function CrossfadeModal({
|
|
|
3546
3794
|
)
|
|
3547
3795
|
] })
|
|
3548
3796
|
] })),
|
|
3549
|
-
error && /* @__PURE__ */ (0,
|
|
3550
|
-
/* @__PURE__ */ (0,
|
|
3551
|
-
/* @__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)(
|
|
3552
3800
|
"button",
|
|
3553
3801
|
{
|
|
3554
3802
|
ref: cancelRef,
|
|
@@ -3559,7 +3807,7 @@ function CrossfadeModal({
|
|
|
3559
3807
|
children: "Cancel"
|
|
3560
3808
|
}
|
|
3561
3809
|
),
|
|
3562
|
-
/* @__PURE__ */ (0,
|
|
3810
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3563
3811
|
"button",
|
|
3564
3812
|
{
|
|
3565
3813
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -3576,10 +3824,10 @@ function CrossfadeModal({
|
|
|
3576
3824
|
}
|
|
3577
3825
|
|
|
3578
3826
|
// src/components/TransitionDesigner.tsx
|
|
3579
|
-
var
|
|
3827
|
+
var import_react18 = require("react");
|
|
3580
3828
|
|
|
3581
3829
|
// src/hooks/useTrackReorder.ts
|
|
3582
|
-
var
|
|
3830
|
+
var import_react17 = require("react");
|
|
3583
3831
|
function moveItem(arr, from, to) {
|
|
3584
3832
|
const next = arr.slice();
|
|
3585
3833
|
if (from === to || from < 0 || to < 0 || from >= next.length || to >= next.length) {
|
|
@@ -3596,12 +3844,12 @@ function useTrackReorder({
|
|
|
3596
3844
|
getId,
|
|
3597
3845
|
onError
|
|
3598
3846
|
}) {
|
|
3599
|
-
const [draggingIndex, setDraggingIndex] = (0,
|
|
3600
|
-
const [dragOverIndex, setDragOverIndex] = (0,
|
|
3601
|
-
const fromRef = (0,
|
|
3602
|
-
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);
|
|
3603
3851
|
itemsRef.current = items;
|
|
3604
|
-
const dragPropsFor = (0,
|
|
3852
|
+
const dragPropsFor = (0, import_react17.useCallback)(
|
|
3605
3853
|
(index) => ({
|
|
3606
3854
|
handleProps: {
|
|
3607
3855
|
draggable: true,
|
|
@@ -3783,7 +4031,7 @@ function dbIdsFromKeys(keys) {
|
|
|
3783
4031
|
}
|
|
3784
4032
|
|
|
3785
4033
|
// src/components/TransitionDesigner.tsx
|
|
3786
|
-
var
|
|
4034
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
3787
4035
|
var CROSSFADE_ESTIMATE_MS = 15e3;
|
|
3788
4036
|
var FADE_ESTIMATE_MS = 11e3;
|
|
3789
4037
|
var CREATE_ALL_CONCURRENCY = 5;
|
|
@@ -3807,44 +4055,44 @@ function TransitionDesigner({
|
|
|
3807
4055
|
familyLabel,
|
|
3808
4056
|
testIdPrefix = "transition-designer"
|
|
3809
4057
|
}) {
|
|
3810
|
-
const [load, setLoad] = (0,
|
|
3811
|
-
const [fromName, setFromName] = (0,
|
|
3812
|
-
const [toName, setToName] = (0,
|
|
3813
|
-
const [originSlots, setOriginSlots] = (0,
|
|
3814
|
-
const [targetSlots, setTargetSlots] = (0,
|
|
3815
|
-
const [creatingKeys, setCreatingKeys] = (0,
|
|
3816
|
-
const [rowErrors, setRowErrors] = (0,
|
|
3817
|
-
const [rowEffects, setRowEffects] = (0,
|
|
3818
|
-
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);
|
|
3819
4067
|
rowEffectsRef.current = rowEffects;
|
|
3820
4068
|
const audioEffectsEnabled = !!onCreateAudioTransition;
|
|
3821
|
-
const excludeRef = (0,
|
|
4069
|
+
const excludeRef = (0, import_react18.useRef)(excludeSourceDbIds);
|
|
3822
4070
|
excludeRef.current = excludeSourceDbIds;
|
|
3823
|
-
const originSlotsRef = (0,
|
|
4071
|
+
const originSlotsRef = (0, import_react18.useRef)(originSlots);
|
|
3824
4072
|
originSlotsRef.current = originSlots;
|
|
3825
|
-
const targetSlotsRef = (0,
|
|
4073
|
+
const targetSlotsRef = (0, import_react18.useRef)(targetSlots);
|
|
3826
4074
|
targetSlotsRef.current = targetSlots;
|
|
3827
|
-
const creatingKeysRef = (0,
|
|
4075
|
+
const creatingKeysRef = (0, import_react18.useRef)(creatingKeys);
|
|
3828
4076
|
creatingKeysRef.current = creatingKeys;
|
|
3829
|
-
const dragRef = (0,
|
|
3830
|
-
const [dragging, setDragging] = (0,
|
|
3831
|
-
const [dragOver, setDragOver] = (0,
|
|
3832
|
-
const excludeSet = (0,
|
|
3833
|
-
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)(
|
|
3834
4082
|
() => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3835
4083
|
[load, excludeSet]
|
|
3836
4084
|
);
|
|
3837
|
-
const targetPool = (0,
|
|
4085
|
+
const targetPool = (0, import_react18.useMemo)(
|
|
3838
4086
|
() => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3839
4087
|
[load, excludeSet]
|
|
3840
4088
|
);
|
|
3841
|
-
const originById = (0,
|
|
3842
|
-
const targetById = (0,
|
|
3843
|
-
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);
|
|
3844
4092
|
originByIdRef.current = originById;
|
|
3845
|
-
const targetByIdRef = (0,
|
|
4093
|
+
const targetByIdRef = (0, import_react18.useRef)(targetById);
|
|
3846
4094
|
targetByIdRef.current = targetById;
|
|
3847
|
-
const refresh = (0,
|
|
4095
|
+
const refresh = (0, import_react18.useCallback)(async () => {
|
|
3848
4096
|
if (!host.listSceneFamilyTracks) {
|
|
3849
4097
|
setLoad({ status: "error", message: "This host does not support transition tracks." });
|
|
3850
4098
|
return;
|
|
@@ -3879,10 +4127,10 @@ function TransitionDesigner({
|
|
|
3879
4127
|
});
|
|
3880
4128
|
}
|
|
3881
4129
|
}, [host, fromSceneId, toSceneId, transitionSceneId]);
|
|
3882
|
-
(0,
|
|
4130
|
+
(0, import_react18.useEffect)(() => {
|
|
3883
4131
|
void refresh();
|
|
3884
4132
|
}, [refresh]);
|
|
3885
|
-
(0,
|
|
4133
|
+
(0, import_react18.useEffect)(() => {
|
|
3886
4134
|
if (load.status !== "ready") return;
|
|
3887
4135
|
const [po, pt] = padPair(
|
|
3888
4136
|
reconcileSlots(originSlotsRef.current, originPool.map((t) => t.dbId)),
|
|
@@ -3891,7 +4139,7 @@ function TransitionDesigner({
|
|
|
3891
4139
|
if (!slotsEqual(po, originSlotsRef.current)) setOriginSlots(po);
|
|
3892
4140
|
if (!slotsEqual(pt, targetSlotsRef.current)) setTargetSlots(pt);
|
|
3893
4141
|
}, [originPool, targetPool, load.status]);
|
|
3894
|
-
const mutate = (0,
|
|
4142
|
+
const mutate = (0, import_react18.useCallback)(
|
|
3895
4143
|
(nextOrigin, nextTarget) => {
|
|
3896
4144
|
const norm = normalizeSlots(nextOrigin, nextTarget);
|
|
3897
4145
|
const [po, pt] = padPair(norm.originOrder, norm.targetOrder);
|
|
@@ -3904,7 +4152,7 @@ function TransitionDesigner({
|
|
|
3904
4152
|
},
|
|
3905
4153
|
[host, transitionSceneId]
|
|
3906
4154
|
);
|
|
3907
|
-
const setRowEffect = (0,
|
|
4155
|
+
const setRowEffect = (0, import_react18.useCallback)(
|
|
3908
4156
|
(sourceDbId, effect) => {
|
|
3909
4157
|
setRowEffects((prev) => {
|
|
3910
4158
|
const next = { ...prev, [sourceDbId]: effect };
|
|
@@ -3918,7 +4166,7 @@ function TransitionDesigner({
|
|
|
3918
4166
|
},
|
|
3919
4167
|
[host, transitionSceneId]
|
|
3920
4168
|
);
|
|
3921
|
-
const insertGapAbove = (0,
|
|
4169
|
+
const insertGapAbove = (0, import_react18.useCallback)(
|
|
3922
4170
|
(col, index) => {
|
|
3923
4171
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
3924
4172
|
const next = [...slots.slice(0, index), null, ...slots.slice(index)];
|
|
@@ -3927,7 +4175,7 @@ function TransitionDesigner({
|
|
|
3927
4175
|
},
|
|
3928
4176
|
[originSlots, targetSlots, mutate]
|
|
3929
4177
|
);
|
|
3930
|
-
const removeGap = (0,
|
|
4178
|
+
const removeGap = (0, import_react18.useCallback)(
|
|
3931
4179
|
(col, index) => {
|
|
3932
4180
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
3933
4181
|
const next = slots.filter((_, i) => i !== index);
|
|
@@ -3936,7 +4184,7 @@ function TransitionDesigner({
|
|
|
3936
4184
|
},
|
|
3937
4185
|
[originSlots, targetSlots, mutate]
|
|
3938
4186
|
);
|
|
3939
|
-
const handleDrop = (0,
|
|
4187
|
+
const handleDrop = (0, import_react18.useCallback)(
|
|
3940
4188
|
(col, to) => {
|
|
3941
4189
|
const from = dragRef.current;
|
|
3942
4190
|
dragRef.current = null;
|
|
@@ -3948,16 +4196,16 @@ function TransitionDesigner({
|
|
|
3948
4196
|
},
|
|
3949
4197
|
[originSlots, targetSlots, mutate]
|
|
3950
4198
|
);
|
|
3951
|
-
const rows = (0,
|
|
3952
|
-
const creatingDbIds = (0,
|
|
3953
|
-
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)(
|
|
3954
4202
|
() => rows.filter((r) => {
|
|
3955
4203
|
const k = rowKey(r);
|
|
3956
4204
|
return k !== null && !creatingKeys.has(k);
|
|
3957
4205
|
}).length,
|
|
3958
4206
|
[rows, creatingKeys]
|
|
3959
4207
|
);
|
|
3960
|
-
const createRow = (0,
|
|
4208
|
+
const createRow = (0, import_react18.useCallback)(
|
|
3961
4209
|
async (row) => {
|
|
3962
4210
|
const key = rowKey(row);
|
|
3963
4211
|
if (!key || !row.type || creatingKeysRef.current.has(key)) return;
|
|
@@ -4011,7 +4259,7 @@ function TransitionDesigner({
|
|
|
4011
4259
|
},
|
|
4012
4260
|
[onCreateCrossfade, onCreateFade, onCreateAudioTransition]
|
|
4013
4261
|
);
|
|
4014
|
-
const createAll = (0,
|
|
4262
|
+
const createAll = (0, import_react18.useCallback)(async () => {
|
|
4015
4263
|
const eligible = buildRowSlots(originSlotsRef.current, targetSlotsRef.current).filter((r) => {
|
|
4016
4264
|
const k = rowKey(r);
|
|
4017
4265
|
return k !== null && !creatingKeysRef.current.has(k);
|
|
@@ -4079,15 +4327,15 @@ function TransitionDesigner({
|
|
|
4079
4327
|
const base = "group relative rounded-sm border px-2 py-1.5 text-left transition-colors select-none";
|
|
4080
4328
|
const tone = isDragTarget ? "border-sas-accent bg-sas-accent/10" : "border-sas-border bg-sas-panel";
|
|
4081
4329
|
if (slotId === null) {
|
|
4082
|
-
return /* @__PURE__ */ (0,
|
|
4330
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
4083
4331
|
"div",
|
|
4084
4332
|
{
|
|
4085
4333
|
...cellDragProps(col, index, false),
|
|
4086
4334
|
"data-testid": `${testIdPrefix}-${col}-gap-${index}`,
|
|
4087
4335
|
className: `${base} ${tone} border-dashed flex items-center justify-between ${isDragging ? "opacity-40" : "opacity-70"}`,
|
|
4088
4336
|
children: [
|
|
4089
|
-
/* @__PURE__ */ (0,
|
|
4090
|
-
/* @__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)(
|
|
4091
4339
|
"button",
|
|
4092
4340
|
{
|
|
4093
4341
|
type: "button",
|
|
@@ -4104,7 +4352,7 @@ function TransitionDesigner({
|
|
|
4104
4352
|
}
|
|
4105
4353
|
const primary = track ? track.prompt?.trim() || track.name : slotId;
|
|
4106
4354
|
const meta = track ? [track.role, shortId3(track.dbId)].filter(Boolean).join(" \xB7 ") : "missing";
|
|
4107
|
-
return /* @__PURE__ */ (0,
|
|
4355
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4108
4356
|
"div",
|
|
4109
4357
|
{
|
|
4110
4358
|
...cellDragProps(col, index, locked),
|
|
@@ -4112,13 +4360,13 @@ function TransitionDesigner({
|
|
|
4112
4360
|
"data-value": slotId,
|
|
4113
4361
|
className: `${base} ${tone} ${isDragging ? "opacity-40" : ""} ${locked ? "opacity-60" : "cursor-grab active:cursor-grabbing"}`,
|
|
4114
4362
|
title: track ? track.dbId : "Track no longer available",
|
|
4115
|
-
children: /* @__PURE__ */ (0,
|
|
4116
|
-
/* @__PURE__ */ (0,
|
|
4117
|
-
/* @__PURE__ */ (0,
|
|
4118
|
-
/* @__PURE__ */ (0,
|
|
4119
|
-
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 })
|
|
4120
4368
|
] }),
|
|
4121
|
-
/* @__PURE__ */ (0,
|
|
4369
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4122
4370
|
"button",
|
|
4123
4371
|
{
|
|
4124
4372
|
type: "button",
|
|
@@ -4134,22 +4382,22 @@ function TransitionDesigner({
|
|
|
4134
4382
|
}
|
|
4135
4383
|
);
|
|
4136
4384
|
};
|
|
4137
|
-
return /* @__PURE__ */ (0,
|
|
4138
|
-
/* @__PURE__ */ (0,
|
|
4139
|
-
/* @__PURE__ */ (0,
|
|
4140
|
-
/* @__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 }),
|
|
4141
4389
|
" \u2192",
|
|
4142
4390
|
" ",
|
|
4143
|
-
/* @__PURE__ */ (0,
|
|
4391
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sas-text", children: toLabel }),
|
|
4144
4392
|
familyLabel ? ` \xB7 ${familyLabel}` : "",
|
|
4145
4393
|
" \xB7 line up a track on each side to crossfade; leave one blank (or insert a gap) to fade."
|
|
4146
4394
|
] }),
|
|
4147
|
-
/* @__PURE__ */ (0,
|
|
4148
|
-
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: [
|
|
4149
4397
|
creatingKeys.size,
|
|
4150
4398
|
" creating\u2026"
|
|
4151
4399
|
] }),
|
|
4152
|
-
/* @__PURE__ */ (0,
|
|
4400
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
4153
4401
|
"button",
|
|
4154
4402
|
{
|
|
4155
4403
|
type: "button",
|
|
@@ -4166,49 +4414,49 @@ function TransitionDesigner({
|
|
|
4166
4414
|
)
|
|
4167
4415
|
] })
|
|
4168
4416
|
] }),
|
|
4169
|
-
/* @__PURE__ */ (0,
|
|
4170
|
-
/* @__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: [
|
|
4171
4419
|
"Origin (",
|
|
4172
4420
|
fromLabel,
|
|
4173
4421
|
")"
|
|
4174
4422
|
] }),
|
|
4175
|
-
/* @__PURE__ */ (0,
|
|
4176
|
-
/* @__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: [
|
|
4177
4425
|
"Target (",
|
|
4178
4426
|
toLabel,
|
|
4179
4427
|
")"
|
|
4180
4428
|
] })
|
|
4181
4429
|
] }),
|
|
4182
|
-
load.status === "loading" && /* @__PURE__ */ (0,
|
|
4183
|
-
load.status === "error" && /* @__PURE__ */ (0,
|
|
4184
|
-
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: [
|
|
4185
4433
|
"No tracks to arrange in this panel for either scene. Add tracks to ",
|
|
4186
4434
|
fromLabel,
|
|
4187
4435
|
" or ",
|
|
4188
4436
|
toLabel,
|
|
4189
4437
|
" ",
|
|
4190
4438
|
"first (or free one by deleting an existing crossfade/fade)."
|
|
4191
|
-
] }) : /* @__PURE__ */ (0,
|
|
4439
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "space-y-2", children: rows.map((row, i) => {
|
|
4192
4440
|
const key = rowKey(row);
|
|
4193
4441
|
const isCreatingThis = key !== null && creatingKeys.has(key);
|
|
4194
4442
|
const errMsg = key !== null ? rowErrors[key] : void 0;
|
|
4195
|
-
return /* @__PURE__ */ (0,
|
|
4443
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
4196
4444
|
"div",
|
|
4197
4445
|
{
|
|
4198
4446
|
"data-testid": `${testIdPrefix}-row-${i}`,
|
|
4199
4447
|
className: "grid grid-cols-[1fr_auto_1fr] gap-2 items-center",
|
|
4200
4448
|
children: [
|
|
4201
4449
|
renderCell("origin", i, row.originId),
|
|
4202
|
-
/* @__PURE__ */ (0,
|
|
4203
|
-
!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)(
|
|
4204
4452
|
"span",
|
|
4205
4453
|
{
|
|
4206
4454
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
4207
4455
|
className: "text-[10px] font-medium px-1.5 py-0.5 rounded-sm border border-sas-accent/50 text-sas-accent",
|
|
4208
4456
|
children: TYPE_LABEL[row.type]
|
|
4209
4457
|
}
|
|
4210
|
-
) : audioEffectsEnabled ? /* @__PURE__ */ (0,
|
|
4211
|
-
/* @__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)(
|
|
4212
4460
|
"select",
|
|
4213
4461
|
{
|
|
4214
4462
|
"data-testid": `${testIdPrefix}-effect-${i}`,
|
|
@@ -4218,11 +4466,11 @@ function TransitionDesigner({
|
|
|
4218
4466
|
if (id) setRowEffect(id, e.target.value);
|
|
4219
4467
|
},
|
|
4220
4468
|
className: "text-[10px] bg-sas-panel border border-sas-border rounded-sm px-1 py-0.5 text-sas-text",
|
|
4221
|
-
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))
|
|
4222
4470
|
}
|
|
4223
4471
|
),
|
|
4224
|
-
/* @__PURE__ */ (0,
|
|
4225
|
-
] }) : /* @__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)(
|
|
4226
4474
|
"span",
|
|
4227
4475
|
{
|
|
4228
4476
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
@@ -4230,7 +4478,7 @@ function TransitionDesigner({
|
|
|
4230
4478
|
children: TYPE_LABEL[row.type]
|
|
4231
4479
|
}
|
|
4232
4480
|
),
|
|
4233
|
-
isCreatingThis ? /* @__PURE__ */ (0,
|
|
4481
|
+
isCreatingThis ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4234
4482
|
SorceryProgressBar,
|
|
4235
4483
|
{
|
|
4236
4484
|
isLoading: true,
|
|
@@ -4238,7 +4486,7 @@ function TransitionDesigner({
|
|
|
4238
4486
|
statusText: "CREATING",
|
|
4239
4487
|
estimatedDurationMs: row.type === "crossfade" ? CROSSFADE_ESTIMATE_MS : FADE_ESTIMATE_MS
|
|
4240
4488
|
}
|
|
4241
|
-
) }) : /* @__PURE__ */ (0,
|
|
4489
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4242
4490
|
"button",
|
|
4243
4491
|
{
|
|
4244
4492
|
type: "button",
|
|
@@ -4249,7 +4497,7 @@ function TransitionDesigner({
|
|
|
4249
4497
|
children: "Create"
|
|
4250
4498
|
}
|
|
4251
4499
|
),
|
|
4252
|
-
errMsg && /* @__PURE__ */ (0,
|
|
4500
|
+
errMsg && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
4253
4501
|
"span",
|
|
4254
4502
|
{
|
|
4255
4503
|
"data-testid": `${testIdPrefix}-row-error-${i}`,
|
|
@@ -4268,10 +4516,11 @@ function TransitionDesigner({
|
|
|
4268
4516
|
}
|
|
4269
4517
|
|
|
4270
4518
|
// src/components/PanelMasterStrip.tsx
|
|
4271
|
-
var
|
|
4272
|
-
var
|
|
4519
|
+
var import_react19 = require("react");
|
|
4520
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
4273
4521
|
function PanelMasterStrip({
|
|
4274
4522
|
bus,
|
|
4523
|
+
levels = null,
|
|
4275
4524
|
availableFx = [],
|
|
4276
4525
|
fxLoading = false,
|
|
4277
4526
|
soloedOut = false,
|
|
@@ -4287,22 +4536,22 @@ function PanelMasterStrip({
|
|
|
4287
4536
|
onToggleFxEnabled,
|
|
4288
4537
|
onShowFxEditor
|
|
4289
4538
|
}) {
|
|
4290
|
-
const [search, setSearch] = (0,
|
|
4291
|
-
const filtered = (0,
|
|
4539
|
+
const [search, setSearch] = (0, import_react19.useState)("");
|
|
4540
|
+
const filtered = (0, import_react19.useMemo)(() => {
|
|
4292
4541
|
const q = search.trim().toLowerCase();
|
|
4293
4542
|
if (!q) return availableFx;
|
|
4294
4543
|
return availableFx.filter(
|
|
4295
4544
|
(fx) => fx.name.toLowerCase().includes(q) || fx.manufacturer.toLowerCase().includes(q)
|
|
4296
4545
|
);
|
|
4297
4546
|
}, [availableFx, search]);
|
|
4298
|
-
return /* @__PURE__ */ (0,
|
|
4547
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
4299
4548
|
"div",
|
|
4300
4549
|
{
|
|
4301
4550
|
"data-testid": "panel-master-strip",
|
|
4302
|
-
className: `flex flex-col gap-1 px-2 py-1.5 rounded-sm border border-sas-border
|
|
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" : ""}`,
|
|
4303
4552
|
children: [
|
|
4304
|
-
/* @__PURE__ */ (0,
|
|
4305
|
-
/* @__PURE__ */ (0,
|
|
4553
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4554
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4306
4555
|
"span",
|
|
4307
4556
|
{
|
|
4308
4557
|
className: "text-[9px] font-bold tracking-widest text-sas-muted/70 select-none",
|
|
@@ -4310,15 +4559,38 @@ function PanelMasterStrip({
|
|
|
4310
4559
|
children: "BUS"
|
|
4311
4560
|
}
|
|
4312
4561
|
),
|
|
4313
|
-
/* @__PURE__ */ (0,
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
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)(
|
|
4322
4594
|
"button",
|
|
4323
4595
|
{
|
|
4324
4596
|
"data-testid": "bus-mute-button",
|
|
@@ -4329,7 +4601,7 @@ function PanelMasterStrip({
|
|
|
4329
4601
|
children: "M"
|
|
4330
4602
|
}
|
|
4331
4603
|
),
|
|
4332
|
-
/* @__PURE__ */ (0,
|
|
4604
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4333
4605
|
"button",
|
|
4334
4606
|
{
|
|
4335
4607
|
"data-testid": "bus-solo-button",
|
|
@@ -4340,14 +4612,14 @@ function PanelMasterStrip({
|
|
|
4340
4612
|
children: "S"
|
|
4341
4613
|
}
|
|
4342
4614
|
),
|
|
4343
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
4344
4616
|
"span",
|
|
4345
4617
|
{
|
|
4346
4618
|
"data-testid": `bus-fx-chip-${fx.index}`,
|
|
4347
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"}`,
|
|
4348
4620
|
title: `${fx.name}${fx.enabled ? "" : " (bypassed)"}`,
|
|
4349
4621
|
children: [
|
|
4350
|
-
/* @__PURE__ */ (0,
|
|
4622
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4351
4623
|
"button",
|
|
4352
4624
|
{
|
|
4353
4625
|
"data-testid": `bus-fx-toggle-${fx.index}`,
|
|
@@ -4358,7 +4630,7 @@ function PanelMasterStrip({
|
|
|
4358
4630
|
children: fx.enabled ? "\u25CF" : "\u25CB"
|
|
4359
4631
|
}
|
|
4360
4632
|
),
|
|
4361
|
-
onShowFxEditor ? /* @__PURE__ */ (0,
|
|
4633
|
+
onShowFxEditor ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4362
4634
|
"button",
|
|
4363
4635
|
{
|
|
4364
4636
|
"data-testid": `bus-fx-edit-${fx.index}`,
|
|
@@ -4368,8 +4640,8 @@ function PanelMasterStrip({
|
|
|
4368
4640
|
title: `Open ${fx.name} editor`,
|
|
4369
4641
|
children: fx.name
|
|
4370
4642
|
}
|
|
4371
|
-
) : /* @__PURE__ */ (0,
|
|
4372
|
-
/* @__PURE__ */ (0,
|
|
4643
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "max-w-[80px] truncate", children: fx.name }),
|
|
4644
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4373
4645
|
"button",
|
|
4374
4646
|
{
|
|
4375
4647
|
"data-testid": `bus-fx-remove-${fx.index}`,
|
|
@@ -4384,21 +4656,21 @@ function PanelMasterStrip({
|
|
|
4384
4656
|
},
|
|
4385
4657
|
`${fx.index}:${fx.pluginId}`
|
|
4386
4658
|
)) }),
|
|
4387
|
-
/* @__PURE__ */ (0,
|
|
4659
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4388
4660
|
"button",
|
|
4389
4661
|
{
|
|
4390
4662
|
"data-testid": "bus-fx-add-button",
|
|
4391
4663
|
onClick: () => onToggleFxPicker(!fxPickerOpen),
|
|
4392
4664
|
disabled,
|
|
4393
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`,
|
|
4394
|
-
title: "Add an FX plugin to the panel bus",
|
|
4395
|
-
children: "FX +"
|
|
4666
|
+
title: fxPickerOpen ? "Close the FX picker" : "Add an FX plugin to the panel bus",
|
|
4667
|
+
children: fxPickerOpen ? "FX \u25B4" : "FX +"
|
|
4396
4668
|
}
|
|
4397
4669
|
)
|
|
4398
4670
|
] }),
|
|
4399
|
-
fxPickerOpen && /* @__PURE__ */ (0,
|
|
4400
|
-
/* @__PURE__ */ (0,
|
|
4401
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
4402
4674
|
"input",
|
|
4403
4675
|
{
|
|
4404
4676
|
type: "text",
|
|
@@ -4408,7 +4680,7 @@ function PanelMasterStrip({
|
|
|
4408
4680
|
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
4409
4681
|
}
|
|
4410
4682
|
),
|
|
4411
|
-
onRefreshFx && /* @__PURE__ */ (0,
|
|
4683
|
+
onRefreshFx && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4412
4684
|
"button",
|
|
4413
4685
|
{
|
|
4414
4686
|
onClick: () => onRefreshFx(),
|
|
@@ -4419,8 +4691,8 @@ function PanelMasterStrip({
|
|
|
4419
4691
|
}
|
|
4420
4692
|
)
|
|
4421
4693
|
] }),
|
|
4422
|
-
fxLoading && availableFx.length === 0 ? /* @__PURE__ */ (0,
|
|
4423
|
-
filtered.map((fx) => /* @__PURE__ */ (0,
|
|
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)(
|
|
4424
4696
|
"button",
|
|
4425
4697
|
{
|
|
4426
4698
|
"data-testid": `bus-fx-pick-${fx.pluginId}`,
|
|
@@ -4428,13 +4700,13 @@ function PanelMasterStrip({
|
|
|
4428
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",
|
|
4429
4701
|
title: `${fx.name} by ${fx.manufacturer} (${fx.type.toUpperCase()})`,
|
|
4430
4702
|
children: [
|
|
4431
|
-
/* @__PURE__ */ (0,
|
|
4432
|
-
/* @__PURE__ */ (0,
|
|
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() })
|
|
4433
4705
|
]
|
|
4434
4706
|
},
|
|
4435
4707
|
fx.pluginId
|
|
4436
4708
|
)),
|
|
4437
|
-
filtered.length === 0 && /* @__PURE__ */ (0,
|
|
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" })
|
|
4438
4710
|
] })
|
|
4439
4711
|
] })
|
|
4440
4712
|
]
|
|
@@ -4443,16 +4715,18 @@ function PanelMasterStrip({
|
|
|
4443
4715
|
}
|
|
4444
4716
|
|
|
4445
4717
|
// src/hooks/usePanelBus.ts
|
|
4446
|
-
var
|
|
4718
|
+
var import_react20 = require("react");
|
|
4719
|
+
var LEVELS_POLL_MS = 66;
|
|
4447
4720
|
function usePanelBus(host, activeSceneId) {
|
|
4448
4721
|
const supported = typeof host.getPanelBusState === "function";
|
|
4449
|
-
const [bus, setBus] = (0,
|
|
4450
|
-
const [
|
|
4451
|
-
const [
|
|
4452
|
-
const [
|
|
4453
|
-
const
|
|
4454
|
-
const
|
|
4455
|
-
const
|
|
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 () => {
|
|
4456
4730
|
if (!supported || !activeSceneId || !host.getPanelBusState) {
|
|
4457
4731
|
setBus(null);
|
|
4458
4732
|
return;
|
|
@@ -4464,12 +4738,34 @@ function usePanelBus(host, activeSceneId) {
|
|
|
4464
4738
|
} catch {
|
|
4465
4739
|
}
|
|
4466
4740
|
}, [host, activeSceneId, supported]);
|
|
4467
|
-
(0,
|
|
4741
|
+
(0, import_react20.useEffect)(() => {
|
|
4468
4742
|
setBus(null);
|
|
4469
4743
|
setFxPickerOpen(false);
|
|
4470
4744
|
void reload();
|
|
4471
4745
|
}, [reload]);
|
|
4472
|
-
|
|
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)(
|
|
4473
4769
|
async (force) => {
|
|
4474
4770
|
if (!supported || !host.getAvailableFx) return;
|
|
4475
4771
|
if (fxLoadedRef.current && !force) return;
|
|
@@ -4485,14 +4781,14 @@ function usePanelBus(host, activeSceneId) {
|
|
|
4485
4781
|
},
|
|
4486
4782
|
[host, supported]
|
|
4487
4783
|
);
|
|
4488
|
-
const openPicker = (0,
|
|
4784
|
+
const openPicker = (0, import_react20.useCallback)(
|
|
4489
4785
|
(open) => {
|
|
4490
4786
|
setFxPickerOpen(open);
|
|
4491
4787
|
if (open) void loadFxList(false);
|
|
4492
4788
|
},
|
|
4493
4789
|
[loadFxList]
|
|
4494
4790
|
);
|
|
4495
|
-
const mutate = (0,
|
|
4791
|
+
const mutate = (0, import_react20.useCallback)(
|
|
4496
4792
|
(fn) => {
|
|
4497
4793
|
if (!fn || !activeSceneId) return;
|
|
4498
4794
|
void (async () => {
|
|
@@ -4508,6 +4804,7 @@ function usePanelBus(host, activeSceneId) {
|
|
|
4508
4804
|
return {
|
|
4509
4805
|
supported,
|
|
4510
4806
|
bus,
|
|
4807
|
+
levels,
|
|
4511
4808
|
availableFx,
|
|
4512
4809
|
fxLoading,
|
|
4513
4810
|
fxPickerOpen,
|
|
@@ -4535,8 +4832,8 @@ function usePanelBus(host, activeSceneId) {
|
|
|
4535
4832
|
}
|
|
4536
4833
|
|
|
4537
4834
|
// src/components/DownloadPackButton.tsx
|
|
4538
|
-
var
|
|
4539
|
-
var
|
|
4835
|
+
var import_react21 = require("react");
|
|
4836
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
4540
4837
|
function formatSize(bytes) {
|
|
4541
4838
|
if (!bytes || bytes <= 0) return "";
|
|
4542
4839
|
const gb = bytes / 1024 ** 3;
|
|
@@ -4552,10 +4849,10 @@ var DownloadPackButton = ({
|
|
|
4552
4849
|
variant = "compact",
|
|
4553
4850
|
onDownloadComplete
|
|
4554
4851
|
}) => {
|
|
4555
|
-
const [status, setStatus] = (0,
|
|
4556
|
-
const [progress, setProgress] = (0,
|
|
4557
|
-
const [errorMessage, setErrorMessage] = (0,
|
|
4558
|
-
(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)(() => {
|
|
4559
4856
|
const unsub = host.onSamplePackProgress(packId, (p) => {
|
|
4560
4857
|
setStatus(p.status);
|
|
4561
4858
|
setProgress(p.progress);
|
|
@@ -4570,7 +4867,7 @@ var DownloadPackButton = ({
|
|
|
4570
4867
|
});
|
|
4571
4868
|
return unsub;
|
|
4572
4869
|
}, [host, packId, onDownloadComplete]);
|
|
4573
|
-
const handleClick = (0,
|
|
4870
|
+
const handleClick = (0, import_react21.useCallback)(async () => {
|
|
4574
4871
|
if (status !== "idle" && status !== "error") return;
|
|
4575
4872
|
try {
|
|
4576
4873
|
setStatus("downloading");
|
|
@@ -4624,8 +4921,8 @@ var DownloadPackButton = ({
|
|
|
4624
4921
|
} else {
|
|
4625
4922
|
className = `${baseClasses} text-sas-muted hover:text-sas-accent border-sas-border hover:border-sas-accent`;
|
|
4626
4923
|
}
|
|
4627
|
-
return /* @__PURE__ */ (0,
|
|
4628
|
-
/* @__PURE__ */ (0,
|
|
4924
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
|
|
4925
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4629
4926
|
"button",
|
|
4630
4927
|
{
|
|
4631
4928
|
"data-testid": `download-pack-button-${packId}`,
|
|
@@ -4636,12 +4933,12 @@ var DownloadPackButton = ({
|
|
|
4636
4933
|
children: buttonLabel
|
|
4637
4934
|
}
|
|
4638
4935
|
),
|
|
4639
|
-
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 })
|
|
4640
4937
|
] });
|
|
4641
4938
|
};
|
|
4642
4939
|
|
|
4643
4940
|
// src/components/SamplePackCTACard.tsx
|
|
4644
|
-
var
|
|
4941
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
4645
4942
|
var SamplePackCTACard = ({
|
|
4646
4943
|
host,
|
|
4647
4944
|
pack,
|
|
@@ -4649,7 +4946,7 @@ var SamplePackCTACard = ({
|
|
|
4649
4946
|
onDownloadComplete
|
|
4650
4947
|
}) => {
|
|
4651
4948
|
if (status === "checking") {
|
|
4652
|
-
return /* @__PURE__ */ (0,
|
|
4949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4653
4950
|
"div",
|
|
4654
4951
|
{
|
|
4655
4952
|
"data-testid": `sample-pack-cta-checking-${pack.packId}`,
|
|
@@ -4660,16 +4957,16 @@ var SamplePackCTACard = ({
|
|
|
4660
4957
|
}
|
|
4661
4958
|
const headline = status === "stale" ? `${pack.displayName} update available` : `${pack.displayName} not installed`;
|
|
4662
4959
|
const sublabel = status === "stale" ? `A newer version is available for download.` : pack.description;
|
|
4663
|
-
return /* @__PURE__ */ (0,
|
|
4960
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4664
4961
|
"div",
|
|
4665
4962
|
{
|
|
4666
4963
|
"data-testid": `sample-pack-cta-${pack.packId}`,
|
|
4667
4964
|
className: "flex flex-col items-center justify-center py-12 px-6 text-center",
|
|
4668
4965
|
children: [
|
|
4669
|
-
/* @__PURE__ */ (0,
|
|
4670
|
-
/* @__PURE__ */ (0,
|
|
4671
|
-
/* @__PURE__ */ (0,
|
|
4672
|
-
/* @__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)(
|
|
4673
4970
|
DownloadPackButton,
|
|
4674
4971
|
{
|
|
4675
4972
|
host,
|
|
@@ -4686,7 +4983,7 @@ var SamplePackCTACard = ({
|
|
|
4686
4983
|
};
|
|
4687
4984
|
|
|
4688
4985
|
// src/components/WaveformView.tsx
|
|
4689
|
-
var
|
|
4986
|
+
var import_react22 = require("react");
|
|
4690
4987
|
|
|
4691
4988
|
// src/components/waveform.ts
|
|
4692
4989
|
function computePeaks(audioBuffer, bins, targetSamples) {
|
|
@@ -4749,7 +5046,7 @@ function drawWaveform(canvas, peaks, options = {}) {
|
|
|
4749
5046
|
}
|
|
4750
5047
|
|
|
4751
5048
|
// src/components/WaveformView.tsx
|
|
4752
|
-
var
|
|
5049
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
4753
5050
|
var WaveformView = ({
|
|
4754
5051
|
host,
|
|
4755
5052
|
filePath,
|
|
@@ -4758,9 +5055,9 @@ var WaveformView = ({
|
|
|
4758
5055
|
fillStyle,
|
|
4759
5056
|
targetSamples
|
|
4760
5057
|
}) => {
|
|
4761
|
-
const canvasRef = (0,
|
|
4762
|
-
const [peaks, setPeaks] = (0,
|
|
4763
|
-
(0,
|
|
5058
|
+
const canvasRef = (0, import_react22.useRef)(null);
|
|
5059
|
+
const [peaks, setPeaks] = (0, import_react22.useState)(null);
|
|
5060
|
+
(0, import_react22.useEffect)(() => {
|
|
4764
5061
|
let cancelled = false;
|
|
4765
5062
|
let audioContext = null;
|
|
4766
5063
|
(async () => {
|
|
@@ -4786,7 +5083,7 @@ var WaveformView = ({
|
|
|
4786
5083
|
cancelled = true;
|
|
4787
5084
|
};
|
|
4788
5085
|
}, [host, filePath, bins, targetSamples]);
|
|
4789
|
-
(0,
|
|
5086
|
+
(0, import_react22.useEffect)(() => {
|
|
4790
5087
|
if (!peaks) return;
|
|
4791
5088
|
const canvas = canvasRef.current;
|
|
4792
5089
|
if (!canvas) return;
|
|
@@ -4797,7 +5094,7 @@ var WaveformView = ({
|
|
|
4797
5094
|
observer.observe(canvas);
|
|
4798
5095
|
return () => observer.disconnect();
|
|
4799
5096
|
}, [peaks, fillStyle]);
|
|
4800
|
-
return /* @__PURE__ */ (0,
|
|
5097
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
4801
5098
|
"canvas",
|
|
4802
5099
|
{
|
|
4803
5100
|
ref: canvasRef,
|
|
@@ -4808,8 +5105,8 @@ var WaveformView = ({
|
|
|
4808
5105
|
};
|
|
4809
5106
|
|
|
4810
5107
|
// src/components/ScrollingWaveform.tsx
|
|
4811
|
-
var
|
|
4812
|
-
var
|
|
5108
|
+
var import_react23 = require("react");
|
|
5109
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
4813
5110
|
var ScrollingWaveform = ({
|
|
4814
5111
|
getPeakDb,
|
|
4815
5112
|
active,
|
|
@@ -4817,11 +5114,11 @@ var ScrollingWaveform = ({
|
|
|
4817
5114
|
className,
|
|
4818
5115
|
fillStyle
|
|
4819
5116
|
}) => {
|
|
4820
|
-
const canvasRef = (0,
|
|
4821
|
-
const ringRef = (0,
|
|
4822
|
-
const writeIdxRef = (0,
|
|
4823
|
-
const rafRef = (0,
|
|
4824
|
-
(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)(() => {
|
|
4825
5122
|
if (ringRef.current.length !== columns) {
|
|
4826
5123
|
const next = new Float32Array(columns);
|
|
4827
5124
|
const prev = ringRef.current;
|
|
@@ -4833,7 +5130,7 @@ var ScrollingWaveform = ({
|
|
|
4833
5130
|
writeIdxRef.current = writeIdxRef.current % columns;
|
|
4834
5131
|
}
|
|
4835
5132
|
}, [columns]);
|
|
4836
|
-
(0,
|
|
5133
|
+
(0, import_react23.useEffect)(() => {
|
|
4837
5134
|
if (!active) {
|
|
4838
5135
|
if (rafRef.current !== null) {
|
|
4839
5136
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -4885,7 +5182,7 @@ var ScrollingWaveform = ({
|
|
|
4885
5182
|
}
|
|
4886
5183
|
};
|
|
4887
5184
|
}, [active, getPeakDb, fillStyle]);
|
|
4888
|
-
return /* @__PURE__ */ (0,
|
|
5185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
4889
5186
|
"canvas",
|
|
4890
5187
|
{
|
|
4891
5188
|
ref: canvasRef,
|
|
@@ -4896,8 +5193,8 @@ var ScrollingWaveform = ({
|
|
|
4896
5193
|
};
|
|
4897
5194
|
|
|
4898
5195
|
// src/components/OffsetScrubber.tsx
|
|
4899
|
-
var
|
|
4900
|
-
var
|
|
5196
|
+
var import_react24 = require("react");
|
|
5197
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
4901
5198
|
var SLIDER_HEIGHT_PX = 28;
|
|
4902
5199
|
var TICK_HEIGHT_PX = 14;
|
|
4903
5200
|
var DOWNBEAT_TICK_HEIGHT_PX = 22;
|
|
@@ -4910,40 +5207,40 @@ function OffsetScrubber({
|
|
|
4910
5207
|
onChange,
|
|
4911
5208
|
disabled = false
|
|
4912
5209
|
}) {
|
|
4913
|
-
const trackRef = (0,
|
|
4914
|
-
const [draftOffset, setDraftOffset] = (0,
|
|
4915
|
-
const [isDragging, setIsDragging] = (0,
|
|
4916
|
-
(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)(() => {
|
|
4917
5214
|
if (!isDragging) setDraftOffset(offsetSamples);
|
|
4918
5215
|
}, [offsetSamples, isDragging]);
|
|
4919
5216
|
const sampleRate = cuePoints?.sample_rate ?? 44100;
|
|
4920
5217
|
const detectedBpm = cuePoints?.detected_bpm ?? projectBpm;
|
|
4921
|
-
const beatsForRange = (0,
|
|
5218
|
+
const beatsForRange = (0, import_react24.useMemo)(() => {
|
|
4922
5219
|
return Math.round(60 / projectBpm * sampleRate);
|
|
4923
5220
|
}, [projectBpm, sampleRate]);
|
|
4924
5221
|
const rangeSamples = beatsForRange * meter;
|
|
4925
|
-
const sampleToFraction = (0,
|
|
5222
|
+
const sampleToFraction = (0, import_react24.useCallback)(
|
|
4926
5223
|
(sample) => {
|
|
4927
5224
|
const clamped = Math.max(-rangeSamples, Math.min(rangeSamples, sample));
|
|
4928
5225
|
return (clamped + rangeSamples) / (2 * rangeSamples);
|
|
4929
5226
|
},
|
|
4930
5227
|
[rangeSamples]
|
|
4931
5228
|
);
|
|
4932
|
-
const fractionToSample = (0,
|
|
5229
|
+
const fractionToSample = (0, import_react24.useCallback)(
|
|
4933
5230
|
(fraction) => {
|
|
4934
5231
|
const clamped = Math.max(0, Math.min(1, fraction));
|
|
4935
5232
|
return Math.round(clamped * 2 * rangeSamples - rangeSamples);
|
|
4936
5233
|
},
|
|
4937
5234
|
[rangeSamples]
|
|
4938
5235
|
);
|
|
4939
|
-
const snapTargets = (0,
|
|
5236
|
+
const snapTargets = (0, import_react24.useMemo)(() => {
|
|
4940
5237
|
if (!cuePoints || cuePoints.beats.length === 0) return [];
|
|
4941
5238
|
const downbeat = cuePoints.beats[0];
|
|
4942
5239
|
const positives = cuePoints.beats.map((b) => b - downbeat);
|
|
4943
5240
|
const negatives = positives.slice(1).map((p) => -p);
|
|
4944
5241
|
return [...negatives, ...positives].sort((a, b) => a - b);
|
|
4945
5242
|
}, [cuePoints]);
|
|
4946
|
-
const snapToBeat = (0,
|
|
5243
|
+
const snapToBeat = (0, import_react24.useCallback)(
|
|
4947
5244
|
(sample) => {
|
|
4948
5245
|
if (snapTargets.length === 0) return sample;
|
|
4949
5246
|
let best = snapTargets[0];
|
|
@@ -4959,7 +5256,7 @@ function OffsetScrubber({
|
|
|
4959
5256
|
},
|
|
4960
5257
|
[snapTargets]
|
|
4961
5258
|
);
|
|
4962
|
-
const handlePointerDown = (0,
|
|
5259
|
+
const handlePointerDown = (0, import_react24.useCallback)(
|
|
4963
5260
|
(e) => {
|
|
4964
5261
|
if (disabled || !cuePoints) return;
|
|
4965
5262
|
e.preventDefault();
|
|
@@ -4993,7 +5290,7 @@ function OffsetScrubber({
|
|
|
4993
5290
|
},
|
|
4994
5291
|
[disabled, cuePoints, fractionToSample, onChange, snapToBeat]
|
|
4995
5292
|
);
|
|
4996
|
-
const handleResetToZero = (0,
|
|
5293
|
+
const handleResetToZero = (0, import_react24.useCallback)(() => {
|
|
4997
5294
|
if (disabled) return;
|
|
4998
5295
|
setDraftOffset(0);
|
|
4999
5296
|
onChange(0);
|
|
@@ -5001,7 +5298,7 @@ function OffsetScrubber({
|
|
|
5001
5298
|
const thumbFraction = sampleToFraction(draftOffset);
|
|
5002
5299
|
const thumbLeftPct = `${(thumbFraction * 100).toFixed(2)}%`;
|
|
5003
5300
|
const bpmMismatch = cuePoints?.detected_bpm != null && Math.abs(cuePoints.detected_bpm - projectBpm) > 1;
|
|
5004
|
-
const ticks = (0,
|
|
5301
|
+
const ticks = (0, import_react24.useMemo)(() => {
|
|
5005
5302
|
if (!cuePoints) return [];
|
|
5006
5303
|
const downbeat = cuePoints.beats[0] ?? 0;
|
|
5007
5304
|
return cuePoints.beats.map((b, i) => {
|
|
@@ -5012,9 +5309,9 @@ function OffsetScrubber({
|
|
|
5012
5309
|
});
|
|
5013
5310
|
}, [cuePoints, sampleToFraction]);
|
|
5014
5311
|
const isDisabled = disabled || !cuePoints || cuePoints.beats.length === 0;
|
|
5015
|
-
return /* @__PURE__ */ (0,
|
|
5016
|
-
/* @__PURE__ */ (0,
|
|
5017
|
-
/* @__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)(
|
|
5018
5315
|
"div",
|
|
5019
5316
|
{
|
|
5020
5317
|
ref: trackRef,
|
|
@@ -5030,7 +5327,7 @@ function OffsetScrubber({
|
|
|
5030
5327
|
"aria-valuenow": draftOffset,
|
|
5031
5328
|
"aria-disabled": isDisabled,
|
|
5032
5329
|
children: [
|
|
5033
|
-
/* @__PURE__ */ (0,
|
|
5330
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
5034
5331
|
"div",
|
|
5035
5332
|
{
|
|
5036
5333
|
"aria-hidden": "true",
|
|
@@ -5038,7 +5335,7 @@ function OffsetScrubber({
|
|
|
5038
5335
|
style: { left: "50%" }
|
|
5039
5336
|
}
|
|
5040
5337
|
),
|
|
5041
|
-
ticks.map((t) => /* @__PURE__ */ (0,
|
|
5338
|
+
ticks.map((t) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
5042
5339
|
"div",
|
|
5043
5340
|
{
|
|
5044
5341
|
"data-testid": t.isDownbeat ? "offset-tick-downbeat" : "offset-tick",
|
|
@@ -5053,7 +5350,7 @@ function OffsetScrubber({
|
|
|
5053
5350
|
},
|
|
5054
5351
|
t.i
|
|
5055
5352
|
)),
|
|
5056
|
-
/* @__PURE__ */ (0,
|
|
5353
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
5057
5354
|
"div",
|
|
5058
5355
|
{
|
|
5059
5356
|
"data-testid": "offset-scrubber-thumb",
|
|
@@ -5070,7 +5367,7 @@ function OffsetScrubber({
|
|
|
5070
5367
|
]
|
|
5071
5368
|
}
|
|
5072
5369
|
),
|
|
5073
|
-
/* @__PURE__ */ (0,
|
|
5370
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
5074
5371
|
"span",
|
|
5075
5372
|
{
|
|
5076
5373
|
"data-testid": "offset-scrubber-readout",
|
|
@@ -5078,7 +5375,7 @@ function OffsetScrubber({
|
|
|
5078
5375
|
children: formatOffset(draftOffset, sampleRate)
|
|
5079
5376
|
}
|
|
5080
5377
|
),
|
|
5081
|
-
/* @__PURE__ */ (0,
|
|
5378
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
5082
5379
|
"button",
|
|
5083
5380
|
{
|
|
5084
5381
|
type: "button",
|
|
@@ -5090,7 +5387,7 @@ function OffsetScrubber({
|
|
|
5090
5387
|
children: "\u2316"
|
|
5091
5388
|
}
|
|
5092
5389
|
),
|
|
5093
|
-
bpmMismatch && /* @__PURE__ */ (0,
|
|
5390
|
+
bpmMismatch && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
5094
5391
|
"span",
|
|
5095
5392
|
{
|
|
5096
5393
|
"data-testid": "offset-bpm-mismatch",
|
|
@@ -5162,16 +5459,16 @@ function synthesizeCuePoints({
|
|
|
5162
5459
|
}
|
|
5163
5460
|
|
|
5164
5461
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
5165
|
-
var
|
|
5462
|
+
var import_react29 = require("react");
|
|
5166
5463
|
|
|
5167
5464
|
// src/hooks/useSceneState.ts
|
|
5168
|
-
var
|
|
5465
|
+
var import_react25 = require("react");
|
|
5169
5466
|
function useSceneState(activeSceneId, initialValue) {
|
|
5170
|
-
const [stateMap, setStateMap] = (0,
|
|
5171
|
-
const activeSceneIdRef = (0,
|
|
5467
|
+
const [stateMap, setStateMap] = (0, import_react25.useState)(() => /* @__PURE__ */ new Map());
|
|
5468
|
+
const activeSceneIdRef = (0, import_react25.useRef)(activeSceneId);
|
|
5172
5469
|
activeSceneIdRef.current = activeSceneId;
|
|
5173
5470
|
const currentValue = activeSceneId !== null && stateMap.has(activeSceneId) ? stateMap.get(activeSceneId) : initialValue;
|
|
5174
|
-
const setForCurrentScene = (0,
|
|
5471
|
+
const setForCurrentScene = (0, import_react25.useCallback)((value) => {
|
|
5175
5472
|
const sid = activeSceneIdRef.current;
|
|
5176
5473
|
if (sid === null) return;
|
|
5177
5474
|
setStateMap((prev) => {
|
|
@@ -5182,7 +5479,7 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
5182
5479
|
return newMap;
|
|
5183
5480
|
});
|
|
5184
5481
|
}, [initialValue]);
|
|
5185
|
-
const setForScene = (0,
|
|
5482
|
+
const setForScene = (0, import_react25.useCallback)((sceneId, value) => {
|
|
5186
5483
|
setStateMap((prev) => {
|
|
5187
5484
|
const current = prev.has(sceneId) ? prev.get(sceneId) : initialValue;
|
|
5188
5485
|
const next = typeof value === "function" ? value(current) : value;
|
|
@@ -5195,10 +5492,10 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
5195
5492
|
}
|
|
5196
5493
|
|
|
5197
5494
|
// src/hooks/useAnySolo.ts
|
|
5198
|
-
var
|
|
5495
|
+
var import_react26 = require("react");
|
|
5199
5496
|
function useAnySolo(host) {
|
|
5200
|
-
const [anySolo, setAnySolo] = (0,
|
|
5201
|
-
(0,
|
|
5497
|
+
const [anySolo, setAnySolo] = (0, import_react26.useState)(false);
|
|
5498
|
+
(0, import_react26.useEffect)(() => {
|
|
5202
5499
|
let active = true;
|
|
5203
5500
|
const refresh = () => {
|
|
5204
5501
|
host.isAnySoloActive().then((v) => {
|
|
@@ -5217,7 +5514,7 @@ function useAnySolo(host) {
|
|
|
5217
5514
|
}
|
|
5218
5515
|
|
|
5219
5516
|
// src/hooks/useSoundHistory.ts
|
|
5220
|
-
var
|
|
5517
|
+
var import_react27 = require("react");
|
|
5221
5518
|
var EMPTY = { entries: [], cursor: -1 };
|
|
5222
5519
|
function sameDescriptor(a, b) {
|
|
5223
5520
|
if (a === b) return true;
|
|
@@ -5229,14 +5526,14 @@ function sameDescriptor(a, b) {
|
|
|
5229
5526
|
}
|
|
5230
5527
|
function useSoundHistory(applySound, opts = {}) {
|
|
5231
5528
|
const max = Math.max(2, opts.max ?? 24);
|
|
5232
|
-
const applyRef = (0,
|
|
5529
|
+
const applyRef = (0, import_react27.useRef)(applySound);
|
|
5233
5530
|
applyRef.current = applySound;
|
|
5234
|
-
const onChangeRef = (0,
|
|
5531
|
+
const onChangeRef = (0, import_react27.useRef)(opts.onChange);
|
|
5235
5532
|
onChangeRef.current = opts.onChange;
|
|
5236
|
-
const dataRef = (0,
|
|
5237
|
-
const [, setVersion] = (0,
|
|
5238
|
-
const bump = (0,
|
|
5239
|
-
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)(
|
|
5240
5537
|
(trackId, next, notify) => {
|
|
5241
5538
|
dataRef.current = { ...dataRef.current, [trackId]: next };
|
|
5242
5539
|
bump();
|
|
@@ -5244,7 +5541,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5244
5541
|
},
|
|
5245
5542
|
[bump]
|
|
5246
5543
|
);
|
|
5247
|
-
const record = (0,
|
|
5544
|
+
const record = (0, import_react27.useCallback)(
|
|
5248
5545
|
(trackId, descriptor, label) => {
|
|
5249
5546
|
const h = dataRef.current[trackId];
|
|
5250
5547
|
const current = h && h.cursor >= 0 ? h.entries[h.cursor] : void 0;
|
|
@@ -5259,7 +5556,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5259
5556
|
},
|
|
5260
5557
|
[max, commit]
|
|
5261
5558
|
);
|
|
5262
|
-
const restoreTo = (0,
|
|
5559
|
+
const restoreTo = (0, import_react27.useCallback)(
|
|
5263
5560
|
async (trackId, index) => {
|
|
5264
5561
|
const h = dataRef.current[trackId];
|
|
5265
5562
|
if (!h || index < 0 || index >= h.entries.length || index === h.cursor) return false;
|
|
@@ -5269,7 +5566,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5269
5566
|
},
|
|
5270
5567
|
[commit]
|
|
5271
5568
|
);
|
|
5272
|
-
const undo = (0,
|
|
5569
|
+
const undo = (0, import_react27.useCallback)(
|
|
5273
5570
|
(trackId) => {
|
|
5274
5571
|
const h = dataRef.current[trackId];
|
|
5275
5572
|
if (!h || h.cursor <= 0) return Promise.resolve(false);
|
|
@@ -5277,7 +5574,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5277
5574
|
},
|
|
5278
5575
|
[restoreTo]
|
|
5279
5576
|
);
|
|
5280
|
-
const toggleFavorite = (0,
|
|
5577
|
+
const toggleFavorite = (0, import_react27.useCallback)(
|
|
5281
5578
|
(trackId, index) => {
|
|
5282
5579
|
const h = dataRef.current[trackId];
|
|
5283
5580
|
if (!h || index < 0 || index >= h.entries.length) return;
|
|
@@ -5286,7 +5583,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5286
5583
|
},
|
|
5287
5584
|
[commit]
|
|
5288
5585
|
);
|
|
5289
|
-
const restore = (0,
|
|
5586
|
+
const restore = (0, import_react27.useCallback)(
|
|
5290
5587
|
(trackId, state) => {
|
|
5291
5588
|
const entries = Array.isArray(state?.entries) ? [...state.entries] : [];
|
|
5292
5589
|
const raw = typeof state?.cursor === "number" ? state.cursor : entries.length - 1;
|
|
@@ -5295,15 +5592,15 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5295
5592
|
},
|
|
5296
5593
|
[commit]
|
|
5297
5594
|
);
|
|
5298
|
-
const list = (0,
|
|
5595
|
+
const list = (0, import_react27.useCallback)(
|
|
5299
5596
|
(trackId) => dataRef.current[trackId] ?? EMPTY,
|
|
5300
5597
|
[]
|
|
5301
5598
|
);
|
|
5302
|
-
const canUndo = (0,
|
|
5599
|
+
const canUndo = (0, import_react27.useCallback)((trackId) => {
|
|
5303
5600
|
const h = dataRef.current[trackId];
|
|
5304
5601
|
return !!h && h.cursor > 0;
|
|
5305
5602
|
}, []);
|
|
5306
|
-
const clear = (0,
|
|
5603
|
+
const clear = (0, import_react27.useCallback)(
|
|
5307
5604
|
(trackId) => {
|
|
5308
5605
|
if (dataRef.current[trackId]) {
|
|
5309
5606
|
const next = { ...dataRef.current };
|
|
@@ -5315,11 +5612,11 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5315
5612
|
},
|
|
5316
5613
|
[bump]
|
|
5317
5614
|
);
|
|
5318
|
-
const reset = (0,
|
|
5615
|
+
const reset = (0, import_react27.useCallback)(() => {
|
|
5319
5616
|
dataRef.current = {};
|
|
5320
5617
|
bump();
|
|
5321
5618
|
}, [bump]);
|
|
5322
|
-
return (0,
|
|
5619
|
+
return (0, import_react27.useMemo)(
|
|
5323
5620
|
() => ({ record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite }),
|
|
5324
5621
|
[record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite]
|
|
5325
5622
|
);
|
|
@@ -5452,7 +5749,7 @@ function resolveTrackGroups(parsedGroups, tracks, getDbId, opts = {}) {
|
|
|
5452
5749
|
}
|
|
5453
5750
|
|
|
5454
5751
|
// src/panel-core/useTransitionOps.ts
|
|
5455
|
-
var
|
|
5752
|
+
var import_react28 = require("react");
|
|
5456
5753
|
function useTransitionOps({
|
|
5457
5754
|
host,
|
|
5458
5755
|
adapter,
|
|
@@ -5469,8 +5766,8 @@ function useTransitionOps({
|
|
|
5469
5766
|
resolvedFades
|
|
5470
5767
|
}) {
|
|
5471
5768
|
const { identity } = adapter;
|
|
5472
|
-
const appliedFadeAutomationRef = (0,
|
|
5473
|
-
const applyCrossfadeAutomation = (0,
|
|
5769
|
+
const appliedFadeAutomationRef = (0, import_react28.useRef)(/* @__PURE__ */ new Set());
|
|
5770
|
+
const applyCrossfadeAutomation = (0, import_react28.useCallback)(
|
|
5474
5771
|
async (originTrackId, targetTrackId, bars, bpm, sliderPos) => {
|
|
5475
5772
|
if (host.setTrackVolumeAutomation) {
|
|
5476
5773
|
const curves = buildCrossfadeVolumeCurves(bars, bpm, sliderPos);
|
|
@@ -5487,7 +5784,7 @@ function useTransitionOps({
|
|
|
5487
5784
|
},
|
|
5488
5785
|
[host]
|
|
5489
5786
|
);
|
|
5490
|
-
const applyFadeAutomation = (0,
|
|
5787
|
+
const applyFadeAutomation = (0, import_react28.useCallback)(
|
|
5491
5788
|
async (trackId, direction, bars, bpm, sliderPos, gesture) => {
|
|
5492
5789
|
if (!host.setTrackVolumeAutomation) return;
|
|
5493
5790
|
const points = buildFadeVolumeCurve(bars, bpm, direction, sliderPos, gesture);
|
|
@@ -5496,8 +5793,8 @@ function useTransitionOps({
|
|
|
5496
5793
|
},
|
|
5497
5794
|
[host]
|
|
5498
5795
|
);
|
|
5499
|
-
const [isCreatingCrossfade, setIsCreatingCrossfade] = (0,
|
|
5500
|
-
const handleCreateCrossfade = (0,
|
|
5796
|
+
const [isCreatingCrossfade, setIsCreatingCrossfade] = (0, import_react28.useState)(false);
|
|
5797
|
+
const handleCreateCrossfade = (0, import_react28.useCallback)(
|
|
5501
5798
|
async (origin, target) => {
|
|
5502
5799
|
const scene = activeSceneId;
|
|
5503
5800
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -5625,8 +5922,8 @@ function useTransitionOps({
|
|
|
5625
5922
|
loadTracks
|
|
5626
5923
|
]
|
|
5627
5924
|
);
|
|
5628
|
-
const [isCreatingFade, setIsCreatingFade] = (0,
|
|
5629
|
-
const handleCreateFade = (0,
|
|
5925
|
+
const [isCreatingFade, setIsCreatingFade] = (0, import_react28.useState)(false);
|
|
5926
|
+
const handleCreateFade = (0, import_react28.useCallback)(
|
|
5630
5927
|
async (selection, direction, gesture) => {
|
|
5631
5928
|
const scene = activeSceneId;
|
|
5632
5929
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -5736,7 +6033,7 @@ function useTransitionOps({
|
|
|
5736
6033
|
loadTracks
|
|
5737
6034
|
]
|
|
5738
6035
|
);
|
|
5739
|
-
const handleCrossfadeMute = (0,
|
|
6036
|
+
const handleCrossfadeMute = (0, import_react28.useCallback)(
|
|
5740
6037
|
(pair) => {
|
|
5741
6038
|
const newMuted = !pair.origin.runtimeState.muted;
|
|
5742
6039
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -5751,7 +6048,7 @@ function useTransitionOps({
|
|
|
5751
6048
|
},
|
|
5752
6049
|
[host, setTracks]
|
|
5753
6050
|
);
|
|
5754
|
-
const handleCrossfadeSolo = (0,
|
|
6051
|
+
const handleCrossfadeSolo = (0, import_react28.useCallback)(
|
|
5755
6052
|
(pair) => {
|
|
5756
6053
|
const newSolo = !pair.origin.runtimeState.solo;
|
|
5757
6054
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -5766,7 +6063,7 @@ function useTransitionOps({
|
|
|
5766
6063
|
},
|
|
5767
6064
|
[host, setTracks]
|
|
5768
6065
|
);
|
|
5769
|
-
const handleCrossfadeDelete = (0,
|
|
6066
|
+
const handleCrossfadeDelete = (0, import_react28.useCallback)(
|
|
5770
6067
|
async (pair) => {
|
|
5771
6068
|
try {
|
|
5772
6069
|
for (const member of [pair.origin, pair.target]) {
|
|
@@ -5792,8 +6089,8 @@ function useTransitionOps({
|
|
|
5792
6089
|
},
|
|
5793
6090
|
[host, activeSceneId, setCrossfadePairsMeta, setTracks]
|
|
5794
6091
|
);
|
|
5795
|
-
const crossfadeSliderTimers = (0,
|
|
5796
|
-
const handleCrossfadeSlider = (0,
|
|
6092
|
+
const crossfadeSliderTimers = (0, import_react28.useRef)({});
|
|
6093
|
+
const handleCrossfadeSlider = (0, import_react28.useCallback)(
|
|
5797
6094
|
(pair, pos) => {
|
|
5798
6095
|
setCrossfadePairsMeta(
|
|
5799
6096
|
(prev) => prev.map((p) => p.groupId === pair.groupId ? { ...p, sliderPos: pos } : p)
|
|
@@ -5826,7 +6123,7 @@ function useTransitionOps({
|
|
|
5826
6123
|
},
|
|
5827
6124
|
[host, activeSceneId, applyCrossfadeAutomation, setCrossfadePairsMeta]
|
|
5828
6125
|
);
|
|
5829
|
-
const handleFadeDelete = (0,
|
|
6126
|
+
const handleFadeDelete = (0, import_react28.useCallback)(
|
|
5830
6127
|
async (fade) => {
|
|
5831
6128
|
try {
|
|
5832
6129
|
await host.deleteTrack(fade.track.handle.id);
|
|
@@ -5846,8 +6143,8 @@ function useTransitionOps({
|
|
|
5846
6143
|
},
|
|
5847
6144
|
[host, activeSceneId, setFadesMeta, setTracks]
|
|
5848
6145
|
);
|
|
5849
|
-
const fadeSliderTimers = (0,
|
|
5850
|
-
const handleFadeSlider = (0,
|
|
6146
|
+
const fadeSliderTimers = (0, import_react28.useRef)({});
|
|
6147
|
+
const handleFadeSlider = (0, import_react28.useCallback)(
|
|
5851
6148
|
(fade, pos) => {
|
|
5852
6149
|
setFadesMeta(
|
|
5853
6150
|
(prev) => prev.map((f) => f.dbId === fade.dbId ? { ...f, meta: { ...f.meta, sliderPos: pos } } : f)
|
|
@@ -5877,8 +6174,8 @@ function useTransitionOps({
|
|
|
5877
6174
|
},
|
|
5878
6175
|
[host, activeSceneId, applyFadeAutomation, setFadesMeta]
|
|
5879
6176
|
);
|
|
5880
|
-
const lastResyncKeyRef = (0,
|
|
5881
|
-
(0,
|
|
6177
|
+
const lastResyncKeyRef = (0, import_react28.useRef)("");
|
|
6178
|
+
(0, import_react28.useEffect)(() => {
|
|
5882
6179
|
if (!host.getTrackSound || resolvedCrossfadePairs.length === 0 && resolvedFades.length === 0) {
|
|
5883
6180
|
return;
|
|
5884
6181
|
}
|
|
@@ -5919,7 +6216,7 @@ function useTransitionOps({
|
|
|
5919
6216
|
cancelled = true;
|
|
5920
6217
|
};
|
|
5921
6218
|
}, [resolvedCrossfadePairs, resolvedFades, host, adapter]);
|
|
5922
|
-
(0,
|
|
6219
|
+
(0, import_react28.useEffect)(() => {
|
|
5923
6220
|
if (!host.setTrackVolumeAutomation || resolvedFades.length === 0) return;
|
|
5924
6221
|
void (async () => {
|
|
5925
6222
|
const mc = await host.getMusicalContext();
|
|
@@ -5953,7 +6250,7 @@ function useTransitionOps({
|
|
|
5953
6250
|
}
|
|
5954
6251
|
|
|
5955
6252
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
5956
|
-
var
|
|
6253
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
5957
6254
|
var EMPTY_PLACEHOLDERS = [];
|
|
5958
6255
|
function useGeneratorPanelCore({
|
|
5959
6256
|
ui,
|
|
@@ -5973,8 +6270,8 @@ function useGeneratorPanelCore({
|
|
|
5973
6270
|
} = ui;
|
|
5974
6271
|
const { identity, features } = adapter;
|
|
5975
6272
|
const logTag = identity.logTag;
|
|
5976
|
-
const adapterRef = (0,
|
|
5977
|
-
(0,
|
|
6273
|
+
const adapterRef = (0, import_react29.useRef)(adapter);
|
|
6274
|
+
(0, import_react29.useEffect)(() => {
|
|
5978
6275
|
if (adapterRef.current !== adapter) {
|
|
5979
6276
|
adapterRef.current = adapter;
|
|
5980
6277
|
console.warn(
|
|
@@ -5984,27 +6281,27 @@ function useGeneratorPanelCore({
|
|
|
5984
6281
|
}, [adapter, logTag]);
|
|
5985
6282
|
const supportsMeters = typeof host.getTrackLevels === "function";
|
|
5986
6283
|
const trackLevels = useTrackLevels(host, isExpanded);
|
|
5987
|
-
const [tracks, setTracks] = (0,
|
|
5988
|
-
const [isLoadingTracks, setIsLoadingTracks] = (0,
|
|
5989
|
-
const [importOpen, setImportOpen] = (0,
|
|
5990
|
-
const [soundImportTarget, setSoundImportTarget] = (0,
|
|
5991
|
-
const [designerView, setDesignerView] = (0,
|
|
5992
|
-
const [transitionSourceTotal, setTransitionSourceTotal] = (0,
|
|
5993
|
-
const [crossfadePairsMeta, setCrossfadePairsMeta] = (0,
|
|
5994
|
-
const [fadesMeta, setFadesMeta] = (0,
|
|
5995
|
-
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)({});
|
|
5996
6293
|
const [isComposing, , setIsComposingForScene] = useSceneState(activeSceneId, false);
|
|
5997
6294
|
const [placeholders, , setPlaceholdersForScene] = useSceneState(
|
|
5998
6295
|
activeSceneId,
|
|
5999
6296
|
EMPTY_PLACEHOLDERS
|
|
6000
6297
|
);
|
|
6001
|
-
const saveTimeoutRefs = (0,
|
|
6002
|
-
const editLoadStartedRef = (0,
|
|
6003
|
-
const [availableInstruments, setAvailableInstruments] = (0,
|
|
6004
|
-
const [instrumentsLoading, setInstrumentsLoading] = (0,
|
|
6005
|
-
const engineToDbIdRef = (0,
|
|
6006
|
-
const tracksLoadedForSceneRef = (0,
|
|
6007
|
-
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)(
|
|
6008
6305
|
(trackId, state) => {
|
|
6009
6306
|
if (!activeSceneId) return;
|
|
6010
6307
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -6024,7 +6321,7 @@ function useGeneratorPanelCore({
|
|
|
6024
6321
|
setItems: setTracks,
|
|
6025
6322
|
getId: (t) => t.handle.dbId
|
|
6026
6323
|
});
|
|
6027
|
-
const loadTracks = (0,
|
|
6324
|
+
const loadTracks = (0, import_react29.useCallback)(
|
|
6028
6325
|
async (incremental = false) => {
|
|
6029
6326
|
const sceneAtStart = activeSceneId;
|
|
6030
6327
|
if (!sceneAtStart) {
|
|
@@ -6149,18 +6446,18 @@ function useGeneratorPanelCore({
|
|
|
6149
6446
|
},
|
|
6150
6447
|
[host, activeSceneId, soundHistory, adapter, logTag]
|
|
6151
6448
|
);
|
|
6152
|
-
(0,
|
|
6449
|
+
(0, import_react29.useEffect)(() => {
|
|
6153
6450
|
loadTracks();
|
|
6154
6451
|
}, [loadTracks]);
|
|
6155
|
-
(0,
|
|
6452
|
+
(0, import_react29.useEffect)(() => {
|
|
6156
6453
|
const map = /* @__PURE__ */ new Map();
|
|
6157
6454
|
for (const t of tracks) {
|
|
6158
6455
|
map.set(t.handle.id, t.handle.dbId);
|
|
6159
6456
|
}
|
|
6160
6457
|
engineToDbIdRef.current = map;
|
|
6161
6458
|
}, [tracks]);
|
|
6162
|
-
const loadedCompletedIdsRef = (0,
|
|
6163
|
-
(0,
|
|
6459
|
+
const loadedCompletedIdsRef = (0, import_react29.useRef)(/* @__PURE__ */ new Set());
|
|
6460
|
+
(0, import_react29.useEffect)(() => {
|
|
6164
6461
|
if (placeholders.length === 0) {
|
|
6165
6462
|
loadedCompletedIdsRef.current.clear();
|
|
6166
6463
|
return;
|
|
@@ -6179,16 +6476,16 @@ function useGeneratorPanelCore({
|
|
|
6179
6476
|
loadTracks(true);
|
|
6180
6477
|
}
|
|
6181
6478
|
}, [placeholders, loadTracks, logTag]);
|
|
6182
|
-
const adoptAndLoad = (0,
|
|
6479
|
+
const adoptAndLoad = (0, import_react29.useCallback)(() => {
|
|
6183
6480
|
loadTracks(true);
|
|
6184
6481
|
}, [loadTracks]);
|
|
6185
|
-
(0,
|
|
6482
|
+
(0, import_react29.useEffect)(() => {
|
|
6186
6483
|
const unsub = host.onEngineReady(() => {
|
|
6187
6484
|
adoptAndLoad();
|
|
6188
6485
|
});
|
|
6189
6486
|
return unsub;
|
|
6190
6487
|
}, [host, adoptAndLoad]);
|
|
6191
|
-
(0,
|
|
6488
|
+
(0, import_react29.useEffect)(() => {
|
|
6192
6489
|
if (typeof host.onAfterAgentMutation !== "function") return;
|
|
6193
6490
|
let timer = null;
|
|
6194
6491
|
const unsub = host.onAfterAgentMutation(() => {
|
|
@@ -6203,13 +6500,13 @@ function useGeneratorPanelCore({
|
|
|
6203
6500
|
if (timer) clearTimeout(timer);
|
|
6204
6501
|
};
|
|
6205
6502
|
}, [host, loadTracks]);
|
|
6206
|
-
(0,
|
|
6503
|
+
(0, import_react29.useEffect)(() => {
|
|
6207
6504
|
const unsub = host.onTrackStateChange((trackId, state) => {
|
|
6208
6505
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: state } : t));
|
|
6209
6506
|
});
|
|
6210
6507
|
return unsub;
|
|
6211
6508
|
}, [host]);
|
|
6212
|
-
(0,
|
|
6509
|
+
(0, import_react29.useEffect)(() => {
|
|
6213
6510
|
if (!features.bulkComposePlaceholders) return;
|
|
6214
6511
|
console.log(`[${logTag}] Subscribing to composeProgress`);
|
|
6215
6512
|
const unsub = host.onComposeProgress((event) => {
|
|
@@ -6243,7 +6540,7 @@ function useGeneratorPanelCore({
|
|
|
6243
6540
|
});
|
|
6244
6541
|
return unsub;
|
|
6245
6542
|
}, [host, setIsComposingForScene, setPlaceholdersForScene, features.bulkComposePlaceholders, logTag]);
|
|
6246
|
-
(0,
|
|
6543
|
+
(0, import_react29.useEffect)(() => {
|
|
6247
6544
|
const refs = saveTimeoutRefs;
|
|
6248
6545
|
return () => {
|
|
6249
6546
|
for (const timeout of Object.values(refs.current)) {
|
|
@@ -6251,9 +6548,9 @@ function useGeneratorPanelCore({
|
|
|
6251
6548
|
}
|
|
6252
6549
|
};
|
|
6253
6550
|
}, []);
|
|
6254
|
-
const isAddingTrackRef = (0,
|
|
6255
|
-
const [isAddingTrack, setIsAddingTrack] = (0,
|
|
6256
|
-
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 () => {
|
|
6257
6554
|
if (isAddingTrackRef.current) return;
|
|
6258
6555
|
if (!activeSceneId) {
|
|
6259
6556
|
host.showToast("warning", "Select SCENE");
|
|
@@ -6293,7 +6590,7 @@ function useGeneratorPanelCore({
|
|
|
6293
6590
|
setIsAddingTrack(false);
|
|
6294
6591
|
}
|
|
6295
6592
|
}, [host, adapter, identity, activeSceneId, isConnected, isAuthenticated, tracks.length, onExpandSelf]);
|
|
6296
|
-
const handlePortTrack = (0,
|
|
6593
|
+
const handlePortTrack = (0, import_react29.useCallback)(
|
|
6297
6594
|
async (sel) => {
|
|
6298
6595
|
if (!activeSceneId) {
|
|
6299
6596
|
host.showToast("warning", "Select SCENE");
|
|
@@ -6350,7 +6647,7 @@ function useGeneratorPanelCore({
|
|
|
6350
6647
|
},
|
|
6351
6648
|
[host, adapter, identity, activeSceneId, isConnected, tracks.length, loadTracks]
|
|
6352
6649
|
);
|
|
6353
|
-
const handleSoundImportPick = (0,
|
|
6650
|
+
const handleSoundImportPick = (0, import_react29.useCallback)(
|
|
6354
6651
|
async (sel) => {
|
|
6355
6652
|
const target = soundImportTarget;
|
|
6356
6653
|
if (!target || !host.getTrackSound) {
|
|
@@ -6381,8 +6678,8 @@ function useGeneratorPanelCore({
|
|
|
6381
6678
|
},
|
|
6382
6679
|
[soundImportTarget, host, adapter, identity.familyKey, soundHistory]
|
|
6383
6680
|
);
|
|
6384
|
-
const [isExportingMidi, setIsExportingMidi] = (0,
|
|
6385
|
-
const handleExportMidi = (0,
|
|
6681
|
+
const [isExportingMidi, setIsExportingMidi] = (0, import_react29.useState)(false);
|
|
6682
|
+
const handleExportMidi = (0, import_react29.useCallback)(async () => {
|
|
6386
6683
|
if (isExportingMidi) return;
|
|
6387
6684
|
setIsExportingMidi(true);
|
|
6388
6685
|
try {
|
|
@@ -6413,10 +6710,10 @@ function useGeneratorPanelCore({
|
|
|
6413
6710
|
const xfFromId = sceneContext?.transitionFromSceneId ?? null;
|
|
6414
6711
|
const xfToId = sceneContext?.transitionToSceneId ?? null;
|
|
6415
6712
|
const canCrossfade = features.transitionDesigner && sceneContext?.sceneType === "transition" && !!xfFromId && !!xfToId && !!host.listSceneFamilyTracks;
|
|
6416
|
-
(0,
|
|
6713
|
+
(0, import_react29.useEffect)(() => {
|
|
6417
6714
|
if (!canCrossfade) setDesignerView(false);
|
|
6418
6715
|
}, [canCrossfade]);
|
|
6419
|
-
(0,
|
|
6716
|
+
(0, import_react29.useEffect)(() => {
|
|
6420
6717
|
if (!canCrossfade || !xfFromId || !xfToId || !host.listSceneFamilyTracks) {
|
|
6421
6718
|
setTransitionSourceTotal(0);
|
|
6422
6719
|
return;
|
|
@@ -6432,12 +6729,12 @@ function useGeneratorPanelCore({
|
|
|
6432
6729
|
};
|
|
6433
6730
|
}, [canCrossfade, xfFromId, xfToId, host]);
|
|
6434
6731
|
const transitionDone = crossfadePairsMeta.length * 2 + fadesMeta.length;
|
|
6435
|
-
(0,
|
|
6732
|
+
(0, import_react29.useEffect)(() => {
|
|
6436
6733
|
if (!onHeaderContent) return;
|
|
6437
6734
|
const addDisabled = needsContract || !isConnected || !activeSceneId || tracks.length >= identity.maxTracks || isAddingTrack;
|
|
6438
6735
|
onHeaderContent(
|
|
6439
|
-
/* @__PURE__ */ (0,
|
|
6440
|
-
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)(
|
|
6441
6738
|
"button",
|
|
6442
6739
|
{
|
|
6443
6740
|
"data-testid": `import-from-scene-${identity.familyKey}-button`,
|
|
@@ -6451,7 +6748,7 @@ function useGeneratorPanelCore({
|
|
|
6451
6748
|
children: identity.importTrackLabel ?? "Import Track"
|
|
6452
6749
|
}
|
|
6453
6750
|
),
|
|
6454
|
-
(!canCrossfade || !designerView) && /* @__PURE__ */ (0,
|
|
6751
|
+
(!canCrossfade || !designerView) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
6455
6752
|
"button",
|
|
6456
6753
|
{
|
|
6457
6754
|
"data-testid": `add-${identity.familyKey}-track-button`,
|
|
@@ -6467,7 +6764,7 @@ function useGeneratorPanelCore({
|
|
|
6467
6764
|
children: identity.addTrackLabel ?? "Add Track"
|
|
6468
6765
|
}
|
|
6469
6766
|
),
|
|
6470
|
-
canCrossfade && /* @__PURE__ */ (0,
|
|
6767
|
+
canCrossfade && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
6471
6768
|
"button",
|
|
6472
6769
|
{
|
|
6473
6770
|
"data-testid": `${identity.familyKey}-view-toggle`,
|
|
@@ -6486,7 +6783,7 @@ function useGeneratorPanelCore({
|
|
|
6486
6783
|
title: designerView ? "Back to the track list" : "Open the transition designer",
|
|
6487
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",
|
|
6488
6785
|
children: [
|
|
6489
|
-
transitionSourceTotal > 0 && /* @__PURE__ */ (0,
|
|
6786
|
+
transitionSourceTotal > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
6490
6787
|
"span",
|
|
6491
6788
|
{
|
|
6492
6789
|
className: "absolute inset-y-0 left-0 bg-sas-accent/25",
|
|
@@ -6494,7 +6791,7 @@ function useGeneratorPanelCore({
|
|
|
6494
6791
|
"aria-hidden": true
|
|
6495
6792
|
}
|
|
6496
6793
|
),
|
|
6497
|
-
/* @__PURE__ */ (0,
|
|
6794
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "relative", children: [
|
|
6498
6795
|
"\u21C4 ",
|
|
6499
6796
|
designerView ? "Transition" : "Tracks",
|
|
6500
6797
|
transitionSourceTotal > 0 ? ` ${transitionDone}/${transitionSourceTotal}` : ""
|
|
@@ -6525,7 +6822,7 @@ function useGeneratorPanelCore({
|
|
|
6525
6822
|
identity,
|
|
6526
6823
|
features.importTracks
|
|
6527
6824
|
]);
|
|
6528
|
-
(0,
|
|
6825
|
+
(0, import_react29.useEffect)(() => {
|
|
6529
6826
|
if (!onLoading) return;
|
|
6530
6827
|
const anyGenerating = tracks.some((t) => t.isGenerating);
|
|
6531
6828
|
onLoading(isLoadingTracks || anyGenerating || isBulkActive);
|
|
@@ -6533,7 +6830,7 @@ function useGeneratorPanelCore({
|
|
|
6533
6830
|
onLoading(false);
|
|
6534
6831
|
};
|
|
6535
6832
|
}, [onLoading, isLoadingTracks, tracks, isBulkActive]);
|
|
6536
|
-
const handleDeleteTrack = (0,
|
|
6833
|
+
const handleDeleteTrack = (0, import_react29.useCallback)(
|
|
6537
6834
|
async (trackId) => {
|
|
6538
6835
|
try {
|
|
6539
6836
|
await host.deleteTrack(trackId);
|
|
@@ -6549,7 +6846,7 @@ function useGeneratorPanelCore({
|
|
|
6549
6846
|
},
|
|
6550
6847
|
[host, activeSceneId]
|
|
6551
6848
|
);
|
|
6552
|
-
const handlePromptChange = (0,
|
|
6849
|
+
const handlePromptChange = (0, import_react29.useCallback)(
|
|
6553
6850
|
(trackId, prompt) => {
|
|
6554
6851
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, prompt } : t));
|
|
6555
6852
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -6565,7 +6862,7 @@ function useGeneratorPanelCore({
|
|
|
6565
6862
|
},
|
|
6566
6863
|
[host, activeSceneId]
|
|
6567
6864
|
);
|
|
6568
|
-
const resolvedGenericGroups = (0,
|
|
6865
|
+
const resolvedGenericGroups = (0, import_react29.useMemo)(() => {
|
|
6569
6866
|
const out = {};
|
|
6570
6867
|
for (const ext of adapter.groupExtensions ?? []) {
|
|
6571
6868
|
out[ext.metaKey] = resolveTrackGroups(
|
|
@@ -6579,18 +6876,18 @@ function useGeneratorPanelCore({
|
|
|
6579
6876
|
}
|
|
6580
6877
|
return out;
|
|
6581
6878
|
}, [adapter, genericGroupMetas, tracks]);
|
|
6582
|
-
const genericGroupMemberDbIds = (0,
|
|
6879
|
+
const genericGroupMemberDbIds = (0, import_react29.useMemo)(() => {
|
|
6583
6880
|
const s = /* @__PURE__ */ new Set();
|
|
6584
6881
|
for (const r of Object.values(resolvedGenericGroups)) {
|
|
6585
6882
|
for (const dbId of r.memberDbIds) s.add(dbId);
|
|
6586
6883
|
}
|
|
6587
6884
|
return s;
|
|
6588
6885
|
}, [resolvedGenericGroups]);
|
|
6589
|
-
const engineToDbId = (0,
|
|
6886
|
+
const engineToDbId = (0, import_react29.useCallback)(
|
|
6590
6887
|
(trackId) => engineToDbIdRef.current.get(trackId) ?? trackId,
|
|
6591
6888
|
[]
|
|
6592
6889
|
);
|
|
6593
|
-
const updateTrack = (0,
|
|
6890
|
+
const updateTrack = (0, import_react29.useCallback)(
|
|
6594
6891
|
(trackId, patch) => {
|
|
6595
6892
|
setTracks(
|
|
6596
6893
|
(prev) => prev.map(
|
|
@@ -6600,18 +6897,18 @@ function useGeneratorPanelCore({
|
|
|
6600
6897
|
},
|
|
6601
6898
|
[]
|
|
6602
6899
|
);
|
|
6603
|
-
const markEditLoaded = (0,
|
|
6900
|
+
const markEditLoaded = (0, import_react29.useCallback)((trackId) => {
|
|
6604
6901
|
editLoadStartedRef.current.add(trackId);
|
|
6605
6902
|
}, []);
|
|
6606
|
-
const tracksRef = (0,
|
|
6607
|
-
(0,
|
|
6903
|
+
const tracksRef = (0, import_react29.useRef)(tracks);
|
|
6904
|
+
(0, import_react29.useEffect)(() => {
|
|
6608
6905
|
tracksRef.current = tracks;
|
|
6609
6906
|
}, [tracks]);
|
|
6610
|
-
const resolvedGenericGroupsRef = (0,
|
|
6611
|
-
(0,
|
|
6907
|
+
const resolvedGenericGroupsRef = (0, import_react29.useRef)(resolvedGenericGroups);
|
|
6908
|
+
(0, import_react29.useEffect)(() => {
|
|
6612
6909
|
resolvedGenericGroupsRef.current = resolvedGenericGroups;
|
|
6613
6910
|
}, [resolvedGenericGroups]);
|
|
6614
|
-
const makeServices = (0,
|
|
6911
|
+
const makeServices = (0, import_react29.useCallback)(() => {
|
|
6615
6912
|
return {
|
|
6616
6913
|
host,
|
|
6617
6914
|
activeSceneId,
|
|
@@ -6630,7 +6927,7 @@ function useGeneratorPanelCore({
|
|
|
6630
6927
|
resolvedGroups: (metaKey) => resolvedGenericGroupsRef.current[metaKey]?.resolved ?? []
|
|
6631
6928
|
};
|
|
6632
6929
|
}, [host, activeSceneId, updateTrack, loadTracks, soundHistory, engineToDbId, markEditLoaded, identity, adapter]);
|
|
6633
|
-
const handleGenerate = (0,
|
|
6930
|
+
const handleGenerate = (0, import_react29.useCallback)(
|
|
6634
6931
|
async (trackId) => {
|
|
6635
6932
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6636
6933
|
if (!track || !track.prompt.trim()) return;
|
|
@@ -6657,7 +6954,7 @@ function useGeneratorPanelCore({
|
|
|
6657
6954
|
},
|
|
6658
6955
|
[host, adapter, tracks, isAuthenticated, makeServices]
|
|
6659
6956
|
);
|
|
6660
|
-
const handleMuteToggle = (0,
|
|
6957
|
+
const handleMuteToggle = (0, import_react29.useCallback)(
|
|
6661
6958
|
(trackId) => {
|
|
6662
6959
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6663
6960
|
if (!track) return;
|
|
@@ -6677,7 +6974,7 @@ function useGeneratorPanelCore({
|
|
|
6677
6974
|
},
|
|
6678
6975
|
[host, tracks]
|
|
6679
6976
|
);
|
|
6680
|
-
const handleSoloToggle = (0,
|
|
6977
|
+
const handleSoloToggle = (0, import_react29.useCallback)(
|
|
6681
6978
|
(trackId) => {
|
|
6682
6979
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6683
6980
|
if (!track) return;
|
|
@@ -6697,7 +6994,7 @@ function useGeneratorPanelCore({
|
|
|
6697
6994
|
},
|
|
6698
6995
|
[host, tracks]
|
|
6699
6996
|
);
|
|
6700
|
-
const handleVolumeChange = (0,
|
|
6997
|
+
const handleVolumeChange = (0, import_react29.useCallback)(
|
|
6701
6998
|
(trackId, volume) => {
|
|
6702
6999
|
setTracks(
|
|
6703
7000
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, volume } } : t)
|
|
@@ -6707,7 +7004,7 @@ function useGeneratorPanelCore({
|
|
|
6707
7004
|
},
|
|
6708
7005
|
[host]
|
|
6709
7006
|
);
|
|
6710
|
-
const handlePanChange = (0,
|
|
7007
|
+
const handlePanChange = (0, import_react29.useCallback)(
|
|
6711
7008
|
(trackId, pan) => {
|
|
6712
7009
|
setTracks(
|
|
6713
7010
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, pan } } : t)
|
|
@@ -6717,7 +7014,7 @@ function useGeneratorPanelCore({
|
|
|
6717
7014
|
},
|
|
6718
7015
|
[host]
|
|
6719
7016
|
);
|
|
6720
|
-
const handleShuffle = (0,
|
|
7017
|
+
const handleShuffle = (0, import_react29.useCallback)(
|
|
6721
7018
|
async (trackId) => {
|
|
6722
7019
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6723
7020
|
if (!track) return;
|
|
@@ -6759,7 +7056,7 @@ function useGeneratorPanelCore({
|
|
|
6759
7056
|
},
|
|
6760
7057
|
[host, adapter, tracks, soundHistory, logTag]
|
|
6761
7058
|
);
|
|
6762
|
-
const handleCopy = (0,
|
|
7059
|
+
const handleCopy = (0, import_react29.useCallback)(
|
|
6763
7060
|
async (trackId) => {
|
|
6764
7061
|
try {
|
|
6765
7062
|
const newHandle = await host.duplicateTrack(trackId);
|
|
@@ -6772,7 +7069,7 @@ function useGeneratorPanelCore({
|
|
|
6772
7069
|
},
|
|
6773
7070
|
[host, loadTracks]
|
|
6774
7071
|
);
|
|
6775
|
-
const handleFxToggle = (0,
|
|
7072
|
+
const handleFxToggle = (0, import_react29.useCallback)(
|
|
6776
7073
|
(trackId, category, enabled) => {
|
|
6777
7074
|
setTracks(
|
|
6778
7075
|
(prev) => prev.map(
|
|
@@ -6795,7 +7092,7 @@ function useGeneratorPanelCore({
|
|
|
6795
7092
|
},
|
|
6796
7093
|
[host]
|
|
6797
7094
|
);
|
|
6798
|
-
const handleFxPresetChange = (0,
|
|
7095
|
+
const handleFxPresetChange = (0, import_react29.useCallback)(
|
|
6799
7096
|
(trackId, category, presetIndex) => {
|
|
6800
7097
|
setTracks(
|
|
6801
7098
|
(prev) => prev.map(
|
|
@@ -6821,7 +7118,7 @@ function useGeneratorPanelCore({
|
|
|
6821
7118
|
},
|
|
6822
7119
|
[host]
|
|
6823
7120
|
);
|
|
6824
|
-
const handleFxDryWetChange = (0,
|
|
7121
|
+
const handleFxDryWetChange = (0, import_react29.useCallback)(
|
|
6825
7122
|
(trackId, category, value) => {
|
|
6826
7123
|
setTracks(
|
|
6827
7124
|
(prev) => prev.map(
|
|
@@ -6833,7 +7130,7 @@ function useGeneratorPanelCore({
|
|
|
6833
7130
|
},
|
|
6834
7131
|
[host]
|
|
6835
7132
|
);
|
|
6836
|
-
const toggleFxDrawer = (0,
|
|
7133
|
+
const toggleFxDrawer = (0, import_react29.useCallback)(
|
|
6837
7134
|
(trackId) => {
|
|
6838
7135
|
setTracks(
|
|
6839
7136
|
(prev) => prev.map((t) => {
|
|
@@ -6855,7 +7152,7 @@ function useGeneratorPanelCore({
|
|
|
6855
7152
|
},
|
|
6856
7153
|
[host, tracks]
|
|
6857
7154
|
);
|
|
6858
|
-
const loadEditNotes = (0,
|
|
7155
|
+
const loadEditNotes = (0, import_react29.useCallback)(
|
|
6859
7156
|
async (trackId) => {
|
|
6860
7157
|
try {
|
|
6861
7158
|
const mc = await host.getMusicalContext();
|
|
@@ -6873,7 +7170,7 @@ function useGeneratorPanelCore({
|
|
|
6873
7170
|
},
|
|
6874
7171
|
[host, logTag]
|
|
6875
7172
|
);
|
|
6876
|
-
const handleNotesChange = (0,
|
|
7173
|
+
const handleNotesChange = (0, import_react29.useCallback)(
|
|
6877
7174
|
(trackId, notes) => {
|
|
6878
7175
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editNotes: notes } : t));
|
|
6879
7176
|
const key = `edit:${trackId}`;
|
|
@@ -6903,7 +7200,7 @@ function useGeneratorPanelCore({
|
|
|
6903
7200
|
},
|
|
6904
7201
|
[host]
|
|
6905
7202
|
);
|
|
6906
|
-
const handleTabChange = (0,
|
|
7203
|
+
const handleTabChange = (0, import_react29.useCallback)(
|
|
6907
7204
|
(trackId, tab) => {
|
|
6908
7205
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, drawerOpen: true, drawerTab: tab } : t));
|
|
6909
7206
|
if (tab === "fx") {
|
|
@@ -6928,10 +7225,10 @@ function useGeneratorPanelCore({
|
|
|
6928
7225
|
},
|
|
6929
7226
|
[host, availableInstruments.length, instrumentsLoading, loadEditNotes]
|
|
6930
7227
|
);
|
|
6931
|
-
const handleProgressChange = (0,
|
|
7228
|
+
const handleProgressChange = (0, import_react29.useCallback)((trackId, pct) => {
|
|
6932
7229
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, generationProgress: pct } : t));
|
|
6933
7230
|
}, []);
|
|
6934
|
-
const handleToggleDrawer = (0,
|
|
7231
|
+
const handleToggleDrawer = (0, import_react29.useCallback)((trackId) => {
|
|
6935
7232
|
setTracks(
|
|
6936
7233
|
(prev) => prev.map((t) => {
|
|
6937
7234
|
if (t.handle.id !== trackId) return t;
|
|
@@ -6940,7 +7237,7 @@ function useGeneratorPanelCore({
|
|
|
6940
7237
|
})
|
|
6941
7238
|
);
|
|
6942
7239
|
}, []);
|
|
6943
|
-
const handleInstrumentSelect = (0,
|
|
7240
|
+
const handleInstrumentSelect = (0, import_react29.useCallback)(
|
|
6944
7241
|
async (trackId, pluginId) => {
|
|
6945
7242
|
const isDefaultInstrument = pluginId === (identity.defaultInstrumentPluginId ?? "Surge XT");
|
|
6946
7243
|
if (isDefaultInstrument) {
|
|
@@ -6993,7 +7290,7 @@ function useGeneratorPanelCore({
|
|
|
6993
7290
|
},
|
|
6994
7291
|
[host, identity.defaultInstrumentPluginId, logTag]
|
|
6995
7292
|
);
|
|
6996
|
-
const handleShowEditor = (0,
|
|
7293
|
+
const handleShowEditor = (0, import_react29.useCallback)(
|
|
6997
7294
|
async (trackId) => {
|
|
6998
7295
|
try {
|
|
6999
7296
|
await host.showInstrumentEditor(trackId);
|
|
@@ -7004,12 +7301,12 @@ function useGeneratorPanelCore({
|
|
|
7004
7301
|
},
|
|
7005
7302
|
[host]
|
|
7006
7303
|
);
|
|
7007
|
-
const handleBackToInstruments = (0,
|
|
7304
|
+
const handleBackToInstruments = (0, import_react29.useCallback)((trackId) => {
|
|
7008
7305
|
setTracks(
|
|
7009
7306
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editorStage: false } : t)
|
|
7010
7307
|
);
|
|
7011
7308
|
}, []);
|
|
7012
|
-
const handleRefreshInstruments = (0,
|
|
7309
|
+
const handleRefreshInstruments = (0, import_react29.useCallback)(() => {
|
|
7013
7310
|
setInstrumentsLoading(true);
|
|
7014
7311
|
host.getAvailableInstruments().then((instruments) => {
|
|
7015
7312
|
setAvailableInstruments(instruments);
|
|
@@ -7018,13 +7315,13 @@ function useGeneratorPanelCore({
|
|
|
7018
7315
|
setInstrumentsLoading(false);
|
|
7019
7316
|
});
|
|
7020
7317
|
}, [host]);
|
|
7021
|
-
const onAuditionNote = (0,
|
|
7318
|
+
const onAuditionNote = (0, import_react29.useCallback)(
|
|
7022
7319
|
(trackId, pitch, velocity, ms) => {
|
|
7023
7320
|
void host.auditionNote(trackId, pitch, velocity, ms);
|
|
7024
7321
|
},
|
|
7025
7322
|
[host]
|
|
7026
7323
|
);
|
|
7027
|
-
const { resolvedCrossfadePairs, crossfadeMemberDbIds } = (0,
|
|
7324
|
+
const { resolvedCrossfadePairs, crossfadeMemberDbIds } = (0, import_react29.useMemo)(() => {
|
|
7028
7325
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
7029
7326
|
const pairs = [];
|
|
7030
7327
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -7039,7 +7336,7 @@ function useGeneratorPanelCore({
|
|
|
7039
7336
|
}
|
|
7040
7337
|
return { resolvedCrossfadePairs: pairs, crossfadeMemberDbIds: members };
|
|
7041
7338
|
}, [tracks, crossfadePairsMeta]);
|
|
7042
|
-
const { resolvedFades, fadeMemberDbIds } = (0,
|
|
7339
|
+
const { resolvedFades, fadeMemberDbIds } = (0, import_react29.useMemo)(() => {
|
|
7043
7340
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
7044
7341
|
const list = [];
|
|
7045
7342
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -7067,7 +7364,7 @@ function useGeneratorPanelCore({
|
|
|
7067
7364
|
resolvedCrossfadePairs,
|
|
7068
7365
|
resolvedFades
|
|
7069
7366
|
});
|
|
7070
|
-
const setGroupMute = (0,
|
|
7367
|
+
const setGroupMute = (0, import_react29.useCallback)(
|
|
7071
7368
|
(trackIds, muted) => {
|
|
7072
7369
|
for (const id of trackIds) {
|
|
7073
7370
|
setTracks(
|
|
@@ -7079,7 +7376,7 @@ function useGeneratorPanelCore({
|
|
|
7079
7376
|
},
|
|
7080
7377
|
[host]
|
|
7081
7378
|
);
|
|
7082
|
-
const setGroupSolo = (0,
|
|
7379
|
+
const setGroupSolo = (0, import_react29.useCallback)(
|
|
7083
7380
|
(trackIds, solo) => {
|
|
7084
7381
|
for (const id of trackIds) {
|
|
7085
7382
|
setTracks(
|
|
@@ -7091,7 +7388,7 @@ function useGeneratorPanelCore({
|
|
|
7091
7388
|
},
|
|
7092
7389
|
[host]
|
|
7093
7390
|
);
|
|
7094
|
-
const deleteGroup = (0,
|
|
7391
|
+
const deleteGroup = (0, import_react29.useCallback)(
|
|
7095
7392
|
async (members, cleanupKeySuffixes) => {
|
|
7096
7393
|
for (const member of members) {
|
|
7097
7394
|
try {
|
|
@@ -7111,7 +7408,7 @@ function useGeneratorPanelCore({
|
|
|
7111
7408
|
},
|
|
7112
7409
|
[host, activeSceneId, loadTracks]
|
|
7113
7410
|
);
|
|
7114
|
-
const handlers = (0,
|
|
7411
|
+
const handlers = (0, import_react29.useMemo)(
|
|
7115
7412
|
() => ({
|
|
7116
7413
|
promptChange: handlePromptChange,
|
|
7117
7414
|
generate: (trackId) => {
|
|
@@ -7225,8 +7522,8 @@ function useGeneratorPanelCore({
|
|
|
7225
7522
|
}
|
|
7226
7523
|
|
|
7227
7524
|
// src/panel-core/GeneratorPanelShell.tsx
|
|
7228
|
-
var
|
|
7229
|
-
var
|
|
7525
|
+
var import_react30 = __toESM(require("react"));
|
|
7526
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
7230
7527
|
function GeneratorPanelShell({ core, slots }) {
|
|
7231
7528
|
const {
|
|
7232
7529
|
ui,
|
|
@@ -7281,7 +7578,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7281
7578
|
const { host, activeSceneId, isAuthenticated, sceneContext, onSelectScene, onOpenContract } = ui;
|
|
7282
7579
|
const panelBus = usePanelBus(host, activeSceneId);
|
|
7283
7580
|
const { identity, features } = adapter;
|
|
7284
|
-
const buildRowProps = (0,
|
|
7581
|
+
const buildRowProps = (0, import_react30.useCallback)(
|
|
7285
7582
|
(track, drag) => {
|
|
7286
7583
|
const id = track.handle.id;
|
|
7287
7584
|
const pickerProps = features.instrumentPicker ? {
|
|
@@ -7333,6 +7630,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7333
7630
|
onVolumeChange: (vol) => handlers.volumeChange(id, vol),
|
|
7334
7631
|
onPanChange: (pan) => handlers.panChange(id, pan),
|
|
7335
7632
|
onFxToggle: (cat, enabled) => handleFxToggle(id, cat, enabled),
|
|
7633
|
+
externalFxHost: host,
|
|
7336
7634
|
onFxPresetChange: (cat, idx) => handleFxPresetChange(id, cat, idx),
|
|
7337
7635
|
onFxDryWetChange: (cat, val) => handleFxDryWetChange(id, cat, val),
|
|
7338
7636
|
onToggleFxDrawer: () => handlers.toggleFxDrawer(id),
|
|
@@ -7380,12 +7678,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7380
7678
|
]
|
|
7381
7679
|
);
|
|
7382
7680
|
if (!activeSceneId) {
|
|
7383
|
-
return /* @__PURE__ */ (0,
|
|
7681
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7384
7682
|
"div",
|
|
7385
7683
|
{
|
|
7386
7684
|
"data-testid": `no-scene-placeholder-${identity.familyKey}`,
|
|
7387
7685
|
className: "flex items-center justify-center py-8",
|
|
7388
|
-
children: /* @__PURE__ */ (0,
|
|
7686
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7389
7687
|
"button",
|
|
7390
7688
|
{
|
|
7391
7689
|
onClick: () => onSelectScene?.(),
|
|
@@ -7397,12 +7695,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7397
7695
|
);
|
|
7398
7696
|
}
|
|
7399
7697
|
if (!sceneContext?.hasContract) {
|
|
7400
|
-
return /* @__PURE__ */ (0,
|
|
7698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7401
7699
|
"div",
|
|
7402
7700
|
{
|
|
7403
7701
|
"data-testid": `no-contract-placeholder-${identity.familyKey}`,
|
|
7404
7702
|
className: "flex items-center justify-center py-8",
|
|
7405
|
-
children: /* @__PURE__ */ (0,
|
|
7703
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7406
7704
|
"button",
|
|
7407
7705
|
{
|
|
7408
7706
|
onClick: () => onOpenContract?.(),
|
|
@@ -7414,7 +7712,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7414
7712
|
);
|
|
7415
7713
|
}
|
|
7416
7714
|
if (features.bulkComposePlaceholders && isComposing) {
|
|
7417
|
-
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" }) });
|
|
7418
7716
|
}
|
|
7419
7717
|
const activePlaceholders = features.bulkComposePlaceholders ? placeholders : [];
|
|
7420
7718
|
if (activePlaceholders.length > 0) {
|
|
@@ -7425,18 +7723,18 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7425
7723
|
tracksByDbId.set(t.handle.id, t);
|
|
7426
7724
|
}
|
|
7427
7725
|
}
|
|
7428
|
-
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) => {
|
|
7429
7727
|
const loadedTrack = ph.status === "completed" ? tracksByDbId.get(ph.id) : void 0;
|
|
7430
7728
|
if (loadedTrack) {
|
|
7431
|
-
return /* @__PURE__ */ (0,
|
|
7729
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TrackRow, { ...buildRowProps(loadedTrack) }, ph.id);
|
|
7432
7730
|
}
|
|
7433
|
-
return /* @__PURE__ */ (0,
|
|
7731
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7434
7732
|
"div",
|
|
7435
7733
|
{
|
|
7436
7734
|
"data-testid": "bulk-placeholder-track",
|
|
7437
7735
|
className: "relative rounded-sm border w-full overflow-hidden border-sas-border bg-sas-panel-alt",
|
|
7438
7736
|
style: { borderLeftColor: identity.placeholderAccentColor, borderLeftWidth: "3px" },
|
|
7439
|
-
children: /* @__PURE__ */ (0,
|
|
7737
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(SorceryProgressBar, { isLoading: true, statusText: "CONJURING MIDI...", heightClass: "h-10" })
|
|
7440
7738
|
},
|
|
7441
7739
|
ph.id
|
|
7442
7740
|
);
|
|
@@ -7448,13 +7746,13 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7448
7746
|
supportsMeters,
|
|
7449
7747
|
levels: supportsMeters ? trackLevels : void 0,
|
|
7450
7748
|
handlers,
|
|
7451
|
-
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),
|
|
7452
7750
|
setGroupMute,
|
|
7453
7751
|
setGroupSolo,
|
|
7454
7752
|
deleteGroup
|
|
7455
7753
|
};
|
|
7456
|
-
return /* @__PURE__ */ (0,
|
|
7457
|
-
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)(
|
|
7458
7756
|
ImportTrackModal,
|
|
7459
7757
|
{
|
|
7460
7758
|
host,
|
|
@@ -7467,7 +7765,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7467
7765
|
testIdPrefix: `${identity.familyKey}-import`
|
|
7468
7766
|
}
|
|
7469
7767
|
),
|
|
7470
|
-
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ (0,
|
|
7768
|
+
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7471
7769
|
ImportTrackModal,
|
|
7472
7770
|
{
|
|
7473
7771
|
host,
|
|
@@ -7482,7 +7780,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7482
7780
|
}
|
|
7483
7781
|
),
|
|
7484
7782
|
slots?.modals,
|
|
7485
|
-
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)(
|
|
7486
7784
|
TransitionDesigner,
|
|
7487
7785
|
{
|
|
7488
7786
|
host,
|
|
@@ -7499,11 +7797,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7499
7797
|
testIdPrefix: `${identity.familyKey}-transition-designer`
|
|
7500
7798
|
}
|
|
7501
7799
|
) }),
|
|
7502
|
-
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */ (0,
|
|
7503
|
-
panelBus.supported && panelBus.bus && /* @__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)(
|
|
7504
7802
|
PanelMasterStrip,
|
|
7505
7803
|
{
|
|
7506
7804
|
bus: panelBus.bus,
|
|
7805
|
+
levels: panelBus.levels,
|
|
7507
7806
|
availableFx: panelBus.availableFx,
|
|
7508
7807
|
fxLoading: panelBus.fxLoading,
|
|
7509
7808
|
soloedOut: anySolo && !panelBus.bus.soloed,
|
|
@@ -7520,7 +7819,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7520
7819
|
}
|
|
7521
7820
|
),
|
|
7522
7821
|
slots?.beforeRows,
|
|
7523
|
-
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ (0,
|
|
7822
|
+
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7524
7823
|
CrossfadeTrackRow,
|
|
7525
7824
|
{
|
|
7526
7825
|
accentColor: identity.transitionAccentColor,
|
|
@@ -7557,7 +7856,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7557
7856
|
},
|
|
7558
7857
|
pair.groupId
|
|
7559
7858
|
)),
|
|
7560
|
-
resolvedFades.map((fade) => /* @__PURE__ */ (0,
|
|
7859
|
+
resolvedFades.map((fade) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7561
7860
|
FadeTrackRow,
|
|
7562
7861
|
{
|
|
7563
7862
|
accentColor: identity.transitionAccentColor,
|
|
@@ -7583,20 +7882,20 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7583
7882
|
fade.dbId
|
|
7584
7883
|
)),
|
|
7585
7884
|
(adapter.groupExtensions ?? []).flatMap(
|
|
7586
|
-
(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}`))
|
|
7587
7886
|
),
|
|
7588
7887
|
tracks.map((track, index) => {
|
|
7589
7888
|
if (crossfadeMemberDbIds.has(track.handle.dbId) || fadeMemberDbIds.has(track.handle.dbId) || genericGroupMemberDbIds.has(track.handle.dbId)) {
|
|
7590
7889
|
return null;
|
|
7591
7890
|
}
|
|
7592
|
-
return /* @__PURE__ */ (0,
|
|
7891
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TrackRow, { ...buildRowProps(track, reorder.dragPropsFor(index)) }, track.handle.id);
|
|
7593
7892
|
}),
|
|
7594
7893
|
slots?.afterRows
|
|
7595
7894
|
] })),
|
|
7596
7895
|
features.exportMidi && !designerView && !isLoadingTracks && tracks.length > 0 && (() => {
|
|
7597
7896
|
const hasAnyMidi = tracks.some((t) => t.hasMidi);
|
|
7598
7897
|
const exportDisabled = isExportingMidi || !hasAnyMidi;
|
|
7599
|
-
return /* @__PURE__ */ (0,
|
|
7898
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "pt-2", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7600
7899
|
"button",
|
|
7601
7900
|
{
|
|
7602
7901
|
"data-testid": "export-midi-tracks-button",
|
|
@@ -7664,7 +7963,7 @@ function createSurgeSoundAdapter(host, overrides = {}) {
|
|
|
7664
7963
|
}
|
|
7665
7964
|
|
|
7666
7965
|
// src/constants/sdk-version.ts
|
|
7667
|
-
var PLUGIN_SDK_VERSION = "2.
|
|
7966
|
+
var PLUGIN_SDK_VERSION = "2.39.0";
|
|
7668
7967
|
|
|
7669
7968
|
// src/utils/format-concurrent-tracks.ts
|
|
7670
7969
|
function formatConcurrentTracks(ctx) {
|
|
@@ -7852,6 +8151,7 @@ function pickTopKWeighted(scored, options = {}) {
|
|
|
7852
8151
|
TEXTURAL_ROLES,
|
|
7853
8152
|
TRANSITION_DESIGNER_DRAFT_KEY,
|
|
7854
8153
|
TrackDrawer,
|
|
8154
|
+
TrackExternalFxSection,
|
|
7855
8155
|
TrackMeterStrip,
|
|
7856
8156
|
TrackRow,
|
|
7857
8157
|
TransitionDesigner,
|
|
@@ -7908,6 +8208,7 @@ function pickTopKWeighted(scored, options = {}) {
|
|
|
7908
8208
|
usePanelBus,
|
|
7909
8209
|
useSceneState,
|
|
7910
8210
|
useSoundHistory,
|
|
8211
|
+
useTrackExternalFx,
|
|
7911
8212
|
useTrackLevel,
|
|
7912
8213
|
useTrackLevels,
|
|
7913
8214
|
useTrackMeter,
|