@signalsandsorcery/plugin-sdk 2.35.1 → 2.35.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +290 -4
- package/dist/index.d.ts +290 -4
- package/dist/index.js +1196 -604
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1192 -604
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -65,11 +65,11 @@ var EMPTY_FX_DETAIL_STATE = {
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
// src/components/TrackRow.tsx
|
|
68
|
-
import
|
|
68
|
+
import React9 from "react";
|
|
69
69
|
import { AlertCircle, ChevronDown, GripVertical } from "lucide-react";
|
|
70
70
|
|
|
71
71
|
// src/components/TrackDrawer.tsx
|
|
72
|
-
import { useState as
|
|
72
|
+
import { useState as useState4, useMemo as useMemo3 } from "react";
|
|
73
73
|
|
|
74
74
|
// src/constants/fx-presets.ts
|
|
75
75
|
var EQ_PRESETS = {
|
|
@@ -789,8 +789,250 @@ function PianoRollEditor({
|
|
|
789
789
|
] });
|
|
790
790
|
}
|
|
791
791
|
|
|
792
|
-
// src/components/
|
|
792
|
+
// src/components/TrackExternalFxSection.tsx
|
|
793
|
+
import { useMemo as useMemo2, useState as useState3 } from "react";
|
|
794
|
+
|
|
795
|
+
// src/hooks/useTrackExternalFx.ts
|
|
796
|
+
import { useCallback as useCallback2, useEffect, useRef as useRef2, useState as useState2 } from "react";
|
|
797
|
+
function useTrackExternalFx(host, trackId) {
|
|
798
|
+
const supported = typeof host.getTrackExternalFx === "function";
|
|
799
|
+
const [fx, setFx] = useState2(null);
|
|
800
|
+
const [availableFx, setAvailableFx] = useState2([]);
|
|
801
|
+
const [fxLoading, setFxLoading] = useState2(false);
|
|
802
|
+
const [pickerOpen, setPickerOpen] = useState2(false);
|
|
803
|
+
const fxLoadedRef = useRef2(false);
|
|
804
|
+
const loadSeqRef = useRef2(0);
|
|
805
|
+
const reload = useCallback2(async () => {
|
|
806
|
+
if (!supported || !trackId || !host.getTrackExternalFx) {
|
|
807
|
+
setFx(null);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
const seq = ++loadSeqRef.current;
|
|
811
|
+
try {
|
|
812
|
+
const list = await host.getTrackExternalFx(trackId);
|
|
813
|
+
if (loadSeqRef.current === seq) setFx(list);
|
|
814
|
+
} catch {
|
|
815
|
+
}
|
|
816
|
+
}, [host, trackId, supported]);
|
|
817
|
+
useEffect(() => {
|
|
818
|
+
setFx(null);
|
|
819
|
+
setPickerOpen(false);
|
|
820
|
+
void reload();
|
|
821
|
+
}, [reload]);
|
|
822
|
+
const loadFxList = useCallback2(
|
|
823
|
+
async (force) => {
|
|
824
|
+
if (!supported || !host.getAvailableFx) return;
|
|
825
|
+
if (fxLoadedRef.current && !force) return;
|
|
826
|
+
setFxLoading(true);
|
|
827
|
+
try {
|
|
828
|
+
const list = await host.getAvailableFx();
|
|
829
|
+
setAvailableFx(list);
|
|
830
|
+
fxLoadedRef.current = true;
|
|
831
|
+
} catch {
|
|
832
|
+
} finally {
|
|
833
|
+
setFxLoading(false);
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
[host, supported]
|
|
837
|
+
);
|
|
838
|
+
const openPicker = useCallback2(
|
|
839
|
+
(open) => {
|
|
840
|
+
setPickerOpen(open);
|
|
841
|
+
if (open) void loadFxList(false);
|
|
842
|
+
},
|
|
843
|
+
[loadFxList]
|
|
844
|
+
);
|
|
845
|
+
const mutate = useCallback2(
|
|
846
|
+
(fn) => {
|
|
847
|
+
if (!fn || !trackId) return;
|
|
848
|
+
void (async () => {
|
|
849
|
+
try {
|
|
850
|
+
await fn();
|
|
851
|
+
} catch {
|
|
852
|
+
}
|
|
853
|
+
await reload();
|
|
854
|
+
})();
|
|
855
|
+
},
|
|
856
|
+
[trackId, reload]
|
|
857
|
+
);
|
|
858
|
+
return {
|
|
859
|
+
supported,
|
|
860
|
+
fx,
|
|
861
|
+
availableFx,
|
|
862
|
+
fxLoading,
|
|
863
|
+
pickerOpen,
|
|
864
|
+
setPickerOpen: openPicker,
|
|
865
|
+
refreshFx: () => void loadFxList(true),
|
|
866
|
+
reload,
|
|
867
|
+
onAddFx: (pluginId) => mutate(host.loadTrackExternalFx && (async () => {
|
|
868
|
+
await host.loadTrackExternalFx(trackId, pluginId);
|
|
869
|
+
})),
|
|
870
|
+
onRemoveFx: (fxIndex) => mutate(host.removeTrackExternalFx && (() => host.removeTrackExternalFx(trackId, fxIndex))),
|
|
871
|
+
onToggleFxEnabled: (fxIndex, enabled) => mutate(
|
|
872
|
+
host.setTrackExternalFxEnabled && (() => host.setTrackExternalFxEnabled(trackId, fxIndex, enabled))
|
|
873
|
+
),
|
|
874
|
+
onShowFxEditor: (fxIndex) => mutate(
|
|
875
|
+
host.showTrackExternalFxEditor && (() => host.showTrackExternalFxEditor(trackId, fxIndex))
|
|
876
|
+
)
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// src/components/TrackExternalFxSection.tsx
|
|
793
881
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
882
|
+
function TrackExternalFxSection({
|
|
883
|
+
host,
|
|
884
|
+
trackId,
|
|
885
|
+
disabled = false
|
|
886
|
+
}) {
|
|
887
|
+
const {
|
|
888
|
+
supported,
|
|
889
|
+
fx,
|
|
890
|
+
availableFx,
|
|
891
|
+
fxLoading,
|
|
892
|
+
pickerOpen,
|
|
893
|
+
setPickerOpen,
|
|
894
|
+
refreshFx,
|
|
895
|
+
onAddFx,
|
|
896
|
+
onRemoveFx,
|
|
897
|
+
onToggleFxEnabled,
|
|
898
|
+
onShowFxEditor
|
|
899
|
+
} = useTrackExternalFx(host, trackId);
|
|
900
|
+
const [search, setSearch] = useState3("");
|
|
901
|
+
const filtered = useMemo2(() => {
|
|
902
|
+
const q = search.trim().toLowerCase();
|
|
903
|
+
if (!q) return availableFx;
|
|
904
|
+
return availableFx.filter(
|
|
905
|
+
(candidate) => candidate.name.toLowerCase().includes(q) || candidate.manufacturer.toLowerCase().includes(q)
|
|
906
|
+
);
|
|
907
|
+
}, [availableFx, search]);
|
|
908
|
+
if (!supported) return null;
|
|
909
|
+
const entries = fx ?? [];
|
|
910
|
+
return /* @__PURE__ */ jsxs3(
|
|
911
|
+
"div",
|
|
912
|
+
{
|
|
913
|
+
"data-testid": "track-external-fx-section",
|
|
914
|
+
className: "flex flex-col gap-1.5 pt-2 mt-1 border-t border-sas-border/60",
|
|
915
|
+
children: [
|
|
916
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
917
|
+
/* @__PURE__ */ jsx3(
|
|
918
|
+
"span",
|
|
919
|
+
{
|
|
920
|
+
className: "text-[9px] font-bold tracking-widest text-sas-muted/70 select-none",
|
|
921
|
+
title: "Third-party FX inserts (VST3/AU) on this track, before its fader. Settings persist with the project.",
|
|
922
|
+
children: "3RD-PARTY FX"
|
|
923
|
+
}
|
|
924
|
+
),
|
|
925
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 flex-1 min-w-0 overflow-x-auto", children: [
|
|
926
|
+
entries.map((entry) => /* @__PURE__ */ jsxs3(
|
|
927
|
+
"span",
|
|
928
|
+
{
|
|
929
|
+
"data-testid": `track-fx-chip-${entry.index}`,
|
|
930
|
+
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"}`,
|
|
931
|
+
title: `${entry.name}${entry.enabled ? "" : " (bypassed)"}`,
|
|
932
|
+
children: [
|
|
933
|
+
/* @__PURE__ */ jsx3(
|
|
934
|
+
"button",
|
|
935
|
+
{
|
|
936
|
+
"data-testid": `track-fx-toggle-${entry.index}`,
|
|
937
|
+
onClick: () => onToggleFxEnabled(entry.index, !entry.enabled),
|
|
938
|
+
disabled,
|
|
939
|
+
className: "hover:opacity-70 disabled:opacity-50",
|
|
940
|
+
title: entry.enabled ? `Bypass ${entry.name}` : `Enable ${entry.name}`,
|
|
941
|
+
children: entry.enabled ? "\u25CF" : "\u25CB"
|
|
942
|
+
}
|
|
943
|
+
),
|
|
944
|
+
/* @__PURE__ */ jsx3(
|
|
945
|
+
"button",
|
|
946
|
+
{
|
|
947
|
+
"data-testid": `track-fx-edit-${entry.index}`,
|
|
948
|
+
onClick: () => onShowFxEditor(entry.index),
|
|
949
|
+
disabled,
|
|
950
|
+
className: "max-w-[110px] truncate hover:underline disabled:opacity-50",
|
|
951
|
+
title: `Open ${entry.name} editor`,
|
|
952
|
+
children: entry.name
|
|
953
|
+
}
|
|
954
|
+
),
|
|
955
|
+
/* @__PURE__ */ jsx3(
|
|
956
|
+
"button",
|
|
957
|
+
{
|
|
958
|
+
"data-testid": `track-fx-remove-${entry.index}`,
|
|
959
|
+
onClick: () => onRemoveFx(entry.index),
|
|
960
|
+
disabled,
|
|
961
|
+
className: "text-sas-muted/60 hover:text-sas-danger disabled:opacity-50",
|
|
962
|
+
title: `Remove ${entry.name} from this track`,
|
|
963
|
+
children: "\u2715"
|
|
964
|
+
}
|
|
965
|
+
)
|
|
966
|
+
]
|
|
967
|
+
},
|
|
968
|
+
`${entry.index}:${entry.pluginId}`
|
|
969
|
+
)),
|
|
970
|
+
entries.length === 0 && /* @__PURE__ */ jsx3("span", { className: "text-[10px] text-sas-muted/40 select-none", children: "none" })
|
|
971
|
+
] }),
|
|
972
|
+
/* @__PURE__ */ jsx3(
|
|
973
|
+
"button",
|
|
974
|
+
{
|
|
975
|
+
"data-testid": "track-fx-add-button",
|
|
976
|
+
onClick: () => setPickerOpen(!pickerOpen),
|
|
977
|
+
disabled,
|
|
978
|
+
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`,
|
|
979
|
+
title: pickerOpen ? "Close the FX picker" : "Add a VST3/AU FX plugin to this track",
|
|
980
|
+
children: pickerOpen ? "FX \u25B4" : "FX +"
|
|
981
|
+
}
|
|
982
|
+
)
|
|
983
|
+
] }),
|
|
984
|
+
pickerOpen && /* @__PURE__ */ jsxs3("div", { "data-testid": "track-fx-picker", className: "flex flex-col gap-2 pt-1", children: [
|
|
985
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
986
|
+
/* @__PURE__ */ jsx3(
|
|
987
|
+
"input",
|
|
988
|
+
{
|
|
989
|
+
type: "text",
|
|
990
|
+
value: search,
|
|
991
|
+
onChange: (e) => setSearch(e.target.value),
|
|
992
|
+
placeholder: "Search FX...",
|
|
993
|
+
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
994
|
+
}
|
|
995
|
+
),
|
|
996
|
+
/* @__PURE__ */ jsx3(
|
|
997
|
+
"button",
|
|
998
|
+
{
|
|
999
|
+
onClick: () => refreshFx(),
|
|
1000
|
+
disabled: fxLoading,
|
|
1001
|
+
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",
|
|
1002
|
+
title: "Re-scan plugins",
|
|
1003
|
+
children: fxLoading ? "..." : "Refresh"
|
|
1004
|
+
}
|
|
1005
|
+
)
|
|
1006
|
+
] }),
|
|
1007
|
+
fxLoading && availableFx.length === 0 ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
1008
|
+
filtered.map((candidate) => /* @__PURE__ */ jsxs3(
|
|
1009
|
+
"button",
|
|
1010
|
+
{
|
|
1011
|
+
"data-testid": `track-fx-pick-${candidate.pluginId}`,
|
|
1012
|
+
onClick: () => {
|
|
1013
|
+
onAddFx(candidate.pluginId);
|
|
1014
|
+
setPickerOpen(false);
|
|
1015
|
+
},
|
|
1016
|
+
disabled,
|
|
1017
|
+
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",
|
|
1018
|
+
title: `${candidate.name} by ${candidate.manufacturer} (${candidate.type.toUpperCase()})`,
|
|
1019
|
+
children: [
|
|
1020
|
+
/* @__PURE__ */ jsx3("span", { className: "text-xs font-medium truncate w-full", children: candidate.name }),
|
|
1021
|
+
/* @__PURE__ */ jsx3("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: candidate.manufacturer || candidate.type.toUpperCase() })
|
|
1022
|
+
]
|
|
1023
|
+
},
|
|
1024
|
+
candidate.pluginId
|
|
1025
|
+
)),
|
|
1026
|
+
filtered.length === 0 && /* @__PURE__ */ jsx3("div", { className: "col-span-3 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No FX plugins found" })
|
|
1027
|
+
] })
|
|
1028
|
+
] })
|
|
1029
|
+
]
|
|
1030
|
+
}
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// src/components/TrackDrawer.tsx
|
|
1035
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
794
1036
|
var TAB_LABELS = {
|
|
795
1037
|
fx: "FX",
|
|
796
1038
|
pick: "Pick",
|
|
@@ -807,6 +1049,7 @@ function TrackDrawer({
|
|
|
807
1049
|
onFxPresetChange,
|
|
808
1050
|
onFxDryWetChange,
|
|
809
1051
|
fxDisabled = false,
|
|
1052
|
+
externalFxHost,
|
|
810
1053
|
instruments = [],
|
|
811
1054
|
currentPluginId = null,
|
|
812
1055
|
isLoading = false,
|
|
@@ -829,13 +1072,13 @@ function TrackDrawer({
|
|
|
829
1072
|
editSnap,
|
|
830
1073
|
onAuditionNote
|
|
831
1074
|
}) {
|
|
832
|
-
const [search, setSearch] =
|
|
1075
|
+
const [search, setSearch] = useState4("");
|
|
833
1076
|
const fxEnabled = !!onFxToggle;
|
|
834
1077
|
const pickEnabled = !!onSelect;
|
|
835
1078
|
const historyEnabled = !!onRestoreSound;
|
|
836
1079
|
const importEnabled = !!onImportSound;
|
|
837
1080
|
const editEnabled = !!onNotesChange;
|
|
838
|
-
const enabledTabs =
|
|
1081
|
+
const enabledTabs = useMemo3(() => {
|
|
839
1082
|
const tabs = [];
|
|
840
1083
|
if (fxEnabled) tabs.push("fx");
|
|
841
1084
|
if (pickEnabled) tabs.push("pick");
|
|
@@ -845,7 +1088,7 @@ function TrackDrawer({
|
|
|
845
1088
|
return tabs;
|
|
846
1089
|
}, [fxEnabled, pickEnabled, historyEnabled, importEnabled, editEnabled]);
|
|
847
1090
|
const SURGE_XT_DEFAULT_ID = "Surge XT";
|
|
848
|
-
const filtered =
|
|
1091
|
+
const filtered = useMemo3(() => {
|
|
849
1092
|
let all = instruments.filter((i) => i.name !== "Surge XT");
|
|
850
1093
|
if (search.trim()) {
|
|
851
1094
|
const q = search.toLowerCase();
|
|
@@ -865,12 +1108,12 @@ function TrackDrawer({
|
|
|
865
1108
|
const history = soundHistory ?? [];
|
|
866
1109
|
const effectiveTab = enabledTabs.includes(activeTab) ? activeTab : enabledTabs[0] ?? "fx";
|
|
867
1110
|
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"}`;
|
|
868
|
-
const strip = enabledTabs.length > 1 ? /* @__PURE__ */
|
|
1111
|
+
const strip = enabledTabs.length > 1 ? /* @__PURE__ */ jsx4(
|
|
869
1112
|
"div",
|
|
870
1113
|
{
|
|
871
1114
|
className: "flex items-center gap-1 border-b border-sas-border pb-1",
|
|
872
1115
|
"data-testid": "sdk-drawer-tabs",
|
|
873
|
-
children: enabledTabs.map((tab) => /* @__PURE__ */
|
|
1116
|
+
children: enabledTabs.map((tab) => /* @__PURE__ */ jsx4(
|
|
874
1117
|
"button",
|
|
875
1118
|
{
|
|
876
1119
|
type: "button",
|
|
@@ -884,9 +1127,9 @@ function TrackDrawer({
|
|
|
884
1127
|
}
|
|
885
1128
|
) : null;
|
|
886
1129
|
const currentSound = soundHistoryCursor >= 0 && soundHistoryCursor < history.length ? history[soundHistoryCursor].label : null;
|
|
887
|
-
const header = strip || currentSound ? /* @__PURE__ */
|
|
1130
|
+
const header = strip || currentSound ? /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", "data-testid": "sdk-drawer-header", children: [
|
|
888
1131
|
strip,
|
|
889
|
-
currentSound && /* @__PURE__ */
|
|
1132
|
+
currentSound && /* @__PURE__ */ jsx4(
|
|
890
1133
|
"span",
|
|
891
1134
|
{
|
|
892
1135
|
className: "text-[10px] text-sas-muted/60 truncate px-0.5",
|
|
@@ -896,9 +1139,9 @@ function TrackDrawer({
|
|
|
896
1139
|
)
|
|
897
1140
|
] }) : null;
|
|
898
1141
|
if (effectiveTab === "edit") {
|
|
899
|
-
return /* @__PURE__ */
|
|
1142
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-edit", children: [
|
|
900
1143
|
header,
|
|
901
|
-
/* @__PURE__ */
|
|
1144
|
+
/* @__PURE__ */ jsx4(
|
|
902
1145
|
PianoRollEditor,
|
|
903
1146
|
{
|
|
904
1147
|
notes: editNotes ?? [],
|
|
@@ -913,9 +1156,9 @@ function TrackDrawer({
|
|
|
913
1156
|
] });
|
|
914
1157
|
}
|
|
915
1158
|
if (effectiveTab === "fx") {
|
|
916
|
-
return /* @__PURE__ */
|
|
1159
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-fx", children: [
|
|
917
1160
|
header,
|
|
918
|
-
/* @__PURE__ */
|
|
1161
|
+
/* @__PURE__ */ jsx4(
|
|
919
1162
|
FxToggleBar,
|
|
920
1163
|
{
|
|
921
1164
|
trackId,
|
|
@@ -925,20 +1168,21 @@ function TrackDrawer({
|
|
|
925
1168
|
onDryWetChange: (_t, category, value) => onFxDryWetChange?.(category, value),
|
|
926
1169
|
disabled: fxDisabled
|
|
927
1170
|
}
|
|
928
|
-
)
|
|
1171
|
+
),
|
|
1172
|
+
externalFxHost && /* @__PURE__ */ jsx4(TrackExternalFxSection, { host: externalFxHost, trackId, disabled: fxDisabled })
|
|
929
1173
|
] });
|
|
930
1174
|
}
|
|
931
1175
|
if (effectiveTab === "import") {
|
|
932
1176
|
const soundNoun = /preset/i.test(importSoundLabel ?? "") ? "preset" : /sample/i.test(importSoundLabel ?? "") ? "sample" : "sound";
|
|
933
|
-
return /* @__PURE__ */
|
|
1177
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", "data-testid": "sdk-drawer-import", children: [
|
|
934
1178
|
header,
|
|
935
|
-
/* @__PURE__ */
|
|
1179
|
+
/* @__PURE__ */ jsxs4("p", { className: "text-[11px] text-sas-muted/70 leading-snug", children: [
|
|
936
1180
|
"Copy the sound from a matching track in another scene \u2014 your MIDI stays, only the",
|
|
937
1181
|
" ",
|
|
938
1182
|
soundNoun,
|
|
939
1183
|
" changes."
|
|
940
1184
|
] }),
|
|
941
|
-
/* @__PURE__ */
|
|
1185
|
+
/* @__PURE__ */ jsxs4(
|
|
942
1186
|
"button",
|
|
943
1187
|
{
|
|
944
1188
|
type: "button",
|
|
@@ -956,16 +1200,16 @@ function TrackDrawer({
|
|
|
956
1200
|
}
|
|
957
1201
|
if (effectiveTab === "history") {
|
|
958
1202
|
const order = history.map((_, i) => i).reverse();
|
|
959
|
-
return /* @__PURE__ */
|
|
1203
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
960
1204
|
header,
|
|
961
|
-
history.length === 0 ? /* @__PURE__ */
|
|
1205
|
+
history.length === 0 ? /* @__PURE__ */ jsx4(
|
|
962
1206
|
"div",
|
|
963
1207
|
{
|
|
964
1208
|
className: "text-xs text-sas-muted/60 text-center py-3",
|
|
965
1209
|
"data-testid": "sdk-history-empty",
|
|
966
1210
|
children: "No sounds yet \u2014 shuffle to build history."
|
|
967
1211
|
}
|
|
968
|
-
) : /* @__PURE__ */
|
|
1212
|
+
) : /* @__PURE__ */ jsx4(
|
|
969
1213
|
"ul",
|
|
970
1214
|
{
|
|
971
1215
|
className: "flex flex-col gap-1 max-h-[160px] overflow-y-auto",
|
|
@@ -973,8 +1217,8 @@ function TrackDrawer({
|
|
|
973
1217
|
children: order.map((i) => {
|
|
974
1218
|
const entry = history[i];
|
|
975
1219
|
const isCurrent = i === soundHistoryCursor;
|
|
976
|
-
return /* @__PURE__ */
|
|
977
|
-
/* @__PURE__ */
|
|
1220
|
+
return /* @__PURE__ */ jsxs4("li", { className: "flex items-center gap-1", children: [
|
|
1221
|
+
/* @__PURE__ */ jsxs4(
|
|
978
1222
|
"button",
|
|
979
1223
|
{
|
|
980
1224
|
type: "button",
|
|
@@ -984,12 +1228,12 @@ function TrackDrawer({
|
|
|
984
1228
|
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"}`,
|
|
985
1229
|
title: isCurrent ? "Current sound" : `Restore: ${entry.label}`,
|
|
986
1230
|
children: [
|
|
987
|
-
/* @__PURE__ */
|
|
988
|
-
/* @__PURE__ */
|
|
1231
|
+
/* @__PURE__ */ jsx4("span", { className: "truncate", children: entry.label }),
|
|
1232
|
+
/* @__PURE__ */ jsx4("span", { className: "text-[10px] text-sas-muted/60 flex-shrink-0 ml-2", children: isCurrent ? "\u25CF current" : "restore" })
|
|
989
1233
|
]
|
|
990
1234
|
}
|
|
991
1235
|
),
|
|
992
|
-
onToggleFavorite && /* @__PURE__ */
|
|
1236
|
+
onToggleFavorite && /* @__PURE__ */ jsx4(
|
|
993
1237
|
"button",
|
|
994
1238
|
{
|
|
995
1239
|
type: "button",
|
|
@@ -1007,10 +1251,10 @@ function TrackDrawer({
|
|
|
1007
1251
|
] });
|
|
1008
1252
|
}
|
|
1009
1253
|
if (effectiveTab === "pick" && editorStage) {
|
|
1010
|
-
return /* @__PURE__ */
|
|
1254
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
1011
1255
|
header,
|
|
1012
|
-
/* @__PURE__ */
|
|
1013
|
-
/* @__PURE__ */
|
|
1256
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
1257
|
+
/* @__PURE__ */ jsx4(
|
|
1014
1258
|
"button",
|
|
1015
1259
|
{
|
|
1016
1260
|
onClick: () => onBackToInstruments?.(),
|
|
@@ -1018,9 +1262,9 @@ function TrackDrawer({
|
|
|
1018
1262
|
children: "\u2190 Back"
|
|
1019
1263
|
}
|
|
1020
1264
|
),
|
|
1021
|
-
/* @__PURE__ */
|
|
1265
|
+
/* @__PURE__ */ jsx4("span", { className: "text-xs text-sas-muted font-medium truncate flex-1", children: selectedInstrumentName ?? "Plugin" })
|
|
1022
1266
|
] }),
|
|
1023
|
-
/* @__PURE__ */
|
|
1267
|
+
/* @__PURE__ */ jsx4(
|
|
1024
1268
|
"button",
|
|
1025
1269
|
{
|
|
1026
1270
|
onClick: () => onShowEditor?.(),
|
|
@@ -1032,10 +1276,10 @@ function TrackDrawer({
|
|
|
1032
1276
|
}
|
|
1033
1277
|
const isDefaultSelected = currentPluginId === null;
|
|
1034
1278
|
const isSelected = (pluginId) => pluginId === currentPluginId;
|
|
1035
|
-
return /* @__PURE__ */
|
|
1279
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
1036
1280
|
header,
|
|
1037
|
-
/* @__PURE__ */
|
|
1038
|
-
/* @__PURE__ */
|
|
1281
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
1282
|
+
/* @__PURE__ */ jsx4(
|
|
1039
1283
|
"input",
|
|
1040
1284
|
{
|
|
1041
1285
|
type: "text",
|
|
@@ -1045,7 +1289,7 @@ function TrackDrawer({
|
|
|
1045
1289
|
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
1046
1290
|
}
|
|
1047
1291
|
),
|
|
1048
|
-
/* @__PURE__ */
|
|
1292
|
+
/* @__PURE__ */ jsx4(
|
|
1049
1293
|
"button",
|
|
1050
1294
|
{
|
|
1051
1295
|
onClick: () => onRefresh?.(),
|
|
@@ -1056,54 +1300,54 @@ function TrackDrawer({
|
|
|
1056
1300
|
}
|
|
1057
1301
|
)
|
|
1058
1302
|
] }),
|
|
1059
|
-
isLoading && instruments.length === 0 ? /* @__PURE__ */
|
|
1060
|
-
/* @__PURE__ */
|
|
1303
|
+
isLoading && instruments.length === 0 ? /* @__PURE__ */ jsx4("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ jsxs4("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
1304
|
+
/* @__PURE__ */ jsxs4(
|
|
1061
1305
|
"button",
|
|
1062
1306
|
{
|
|
1063
1307
|
onClick: () => onSelect?.(SURGE_XT_DEFAULT_ID),
|
|
1064
1308
|
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"}`,
|
|
1065
1309
|
title: "Surge XT \u2014 Default instrument",
|
|
1066
1310
|
children: [
|
|
1067
|
-
/* @__PURE__ */
|
|
1311
|
+
/* @__PURE__ */ jsxs4("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1068
1312
|
isDefaultSelected && "\u2713 ",
|
|
1069
1313
|
"Surge XT"
|
|
1070
1314
|
] }),
|
|
1071
|
-
/* @__PURE__ */
|
|
1315
|
+
/* @__PURE__ */ jsx4("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: "Default" })
|
|
1072
1316
|
]
|
|
1073
1317
|
},
|
|
1074
1318
|
"__surge-xt-default__"
|
|
1075
1319
|
),
|
|
1076
1320
|
filtered.map((inst) => {
|
|
1077
1321
|
const selected = isSelected(inst.pluginId);
|
|
1078
|
-
return /* @__PURE__ */
|
|
1322
|
+
return /* @__PURE__ */ jsxs4(
|
|
1079
1323
|
"button",
|
|
1080
1324
|
{
|
|
1081
1325
|
onClick: () => onSelect?.(inst.pluginId),
|
|
1082
1326
|
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"}`,
|
|
1083
1327
|
title: `${inst.name} by ${inst.manufacturer} (${inst.type.toUpperCase()})${inst.missing ? " \u2014 MISSING" : ""}`,
|
|
1084
1328
|
children: [
|
|
1085
|
-
/* @__PURE__ */
|
|
1329
|
+
/* @__PURE__ */ jsxs4("span", { className: "text-xs font-medium truncate w-full", children: [
|
|
1086
1330
|
selected && "\u2713 ",
|
|
1087
1331
|
inst.name
|
|
1088
1332
|
] }),
|
|
1089
|
-
/* @__PURE__ */
|
|
1333
|
+
/* @__PURE__ */ jsx4("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: inst.manufacturer || inst.type.toUpperCase() })
|
|
1090
1334
|
]
|
|
1091
1335
|
},
|
|
1092
1336
|
inst.pluginId
|
|
1093
1337
|
);
|
|
1094
1338
|
}),
|
|
1095
|
-
filtered.length === 0 && /* @__PURE__ */
|
|
1339
|
+
filtered.length === 0 && /* @__PURE__ */ jsx4("div", { className: "col-span-2 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No other plugins found" })
|
|
1096
1340
|
] })
|
|
1097
1341
|
] });
|
|
1098
1342
|
}
|
|
1099
1343
|
|
|
1100
1344
|
// src/components/ConfirmDialog.tsx
|
|
1101
|
-
import { useRef as
|
|
1345
|
+
import { useRef as useRef3 } from "react";
|
|
1102
1346
|
|
|
1103
1347
|
// src/components/Modal.tsx
|
|
1104
|
-
import { useEffect } from "react";
|
|
1348
|
+
import { useEffect as useEffect2 } from "react";
|
|
1105
1349
|
import { createPortal } from "react-dom";
|
|
1106
|
-
import { jsx as
|
|
1350
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1107
1351
|
function Modal({
|
|
1108
1352
|
open,
|
|
1109
1353
|
onClose,
|
|
@@ -1113,7 +1357,7 @@ function Modal({
|
|
|
1113
1357
|
closeOnEscape = true,
|
|
1114
1358
|
initialFocusRef
|
|
1115
1359
|
}) {
|
|
1116
|
-
|
|
1360
|
+
useEffect2(() => {
|
|
1117
1361
|
if (!open) return void 0;
|
|
1118
1362
|
const onKey = (e) => {
|
|
1119
1363
|
if (closeOnEscape && e.key === "Escape") {
|
|
@@ -1127,7 +1371,7 @@ function Modal({
|
|
|
1127
1371
|
}, [open, onClose, closeOnEscape, initialFocusRef]);
|
|
1128
1372
|
if (!open) return null;
|
|
1129
1373
|
return createPortal(
|
|
1130
|
-
/* @__PURE__ */
|
|
1374
|
+
/* @__PURE__ */ jsx5(
|
|
1131
1375
|
"div",
|
|
1132
1376
|
{
|
|
1133
1377
|
className: "fixed inset-0 z-[1000] flex items-center justify-center bg-black/60",
|
|
@@ -1141,7 +1385,7 @@ function Modal({
|
|
|
1141
1385
|
}
|
|
1142
1386
|
|
|
1143
1387
|
// src/components/ConfirmDialog.tsx
|
|
1144
|
-
import { jsx as
|
|
1388
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1145
1389
|
function ConfirmDialog({
|
|
1146
1390
|
open,
|
|
1147
1391
|
title,
|
|
@@ -1153,8 +1397,8 @@ function ConfirmDialog({
|
|
|
1153
1397
|
onCancel,
|
|
1154
1398
|
testIdPrefix = "confirm-dialog"
|
|
1155
1399
|
}) {
|
|
1156
|
-
const cancelRef =
|
|
1157
|
-
return /* @__PURE__ */
|
|
1400
|
+
const cancelRef = useRef3(null);
|
|
1401
|
+
return /* @__PURE__ */ jsx6(Modal, { open, onClose: onCancel, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs5(
|
|
1158
1402
|
"div",
|
|
1159
1403
|
{
|
|
1160
1404
|
className: "w-[360px] max-w-[90vw] flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
@@ -1164,8 +1408,8 @@ function ConfirmDialog({
|
|
|
1164
1408
|
"aria-label": title,
|
|
1165
1409
|
"data-testid": `${testIdPrefix}-modal`,
|
|
1166
1410
|
children: [
|
|
1167
|
-
/* @__PURE__ */
|
|
1168
|
-
/* @__PURE__ */
|
|
1411
|
+
/* @__PURE__ */ jsx6("div", { className: "px-4 py-3 border-b border-sas-border", children: /* @__PURE__ */ jsx6("span", { className: "text-sm font-medium text-sas-text", "data-testid": `${testIdPrefix}-title`, children: title }) }),
|
|
1412
|
+
/* @__PURE__ */ jsx6(
|
|
1169
1413
|
"div",
|
|
1170
1414
|
{
|
|
1171
1415
|
className: "px-4 py-3 text-xs text-sas-muted leading-relaxed break-words",
|
|
@@ -1173,8 +1417,8 @@ function ConfirmDialog({
|
|
|
1173
1417
|
children: message
|
|
1174
1418
|
}
|
|
1175
1419
|
),
|
|
1176
|
-
/* @__PURE__ */
|
|
1177
|
-
/* @__PURE__ */
|
|
1420
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex justify-end gap-2 px-4 py-3 border-t border-sas-border", children: [
|
|
1421
|
+
/* @__PURE__ */ jsx6(
|
|
1178
1422
|
"button",
|
|
1179
1423
|
{
|
|
1180
1424
|
ref: cancelRef,
|
|
@@ -1185,7 +1429,7 @@ function ConfirmDialog({
|
|
|
1185
1429
|
children: cancelLabel
|
|
1186
1430
|
}
|
|
1187
1431
|
),
|
|
1188
|
-
/* @__PURE__ */
|
|
1432
|
+
/* @__PURE__ */ jsx6(
|
|
1189
1433
|
"button",
|
|
1190
1434
|
{
|
|
1191
1435
|
type: "button",
|
|
@@ -1202,7 +1446,7 @@ function ConfirmDialog({
|
|
|
1202
1446
|
}
|
|
1203
1447
|
|
|
1204
1448
|
// src/components/LevelMeter.tsx
|
|
1205
|
-
import { jsx as
|
|
1449
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1206
1450
|
var COLOR_GREEN = "#2BD576";
|
|
1207
1451
|
var COLOR_ORANGE = "#F5A623";
|
|
1208
1452
|
var COLOR_RED = "#FF4D5E";
|
|
@@ -1230,7 +1474,7 @@ var LevelMeter = ({
|
|
|
1230
1474
|
const widthPct = active ? dbToPct(peakDb) : 0;
|
|
1231
1475
|
const showPeak = peakHoldDb != null && active && peakHoldDb > -60;
|
|
1232
1476
|
const peakHoldPct = showPeak ? dbToPct(peakHoldDb) : 0;
|
|
1233
|
-
return /* @__PURE__ */
|
|
1477
|
+
return /* @__PURE__ */ jsxs6(
|
|
1234
1478
|
"div",
|
|
1235
1479
|
{
|
|
1236
1480
|
className: `sas-level-meter ${className ?? ""}`,
|
|
@@ -1241,7 +1485,7 @@ var LevelMeter = ({
|
|
|
1241
1485
|
gap: compact ? 0 : 6
|
|
1242
1486
|
},
|
|
1243
1487
|
children: [
|
|
1244
|
-
/* @__PURE__ */
|
|
1488
|
+
/* @__PURE__ */ jsxs6(
|
|
1245
1489
|
"div",
|
|
1246
1490
|
{
|
|
1247
1491
|
style: {
|
|
@@ -1255,8 +1499,8 @@ var LevelMeter = ({
|
|
|
1255
1499
|
minWidth: compact ? 0 : 60
|
|
1256
1500
|
},
|
|
1257
1501
|
children: [
|
|
1258
|
-
/* @__PURE__ */
|
|
1259
|
-
/* @__PURE__ */
|
|
1502
|
+
/* @__PURE__ */ jsx7("div", { style: { position: "absolute", inset: 0, background: METER_GRADIENT } }),
|
|
1503
|
+
/* @__PURE__ */ jsx7(
|
|
1260
1504
|
"div",
|
|
1261
1505
|
{
|
|
1262
1506
|
style: {
|
|
@@ -1270,7 +1514,7 @@ var LevelMeter = ({
|
|
|
1270
1514
|
}
|
|
1271
1515
|
}
|
|
1272
1516
|
),
|
|
1273
|
-
/* @__PURE__ */
|
|
1517
|
+
/* @__PURE__ */ jsx7(
|
|
1274
1518
|
"div",
|
|
1275
1519
|
{
|
|
1276
1520
|
"data-testid": `${id}-segments`,
|
|
@@ -1283,7 +1527,7 @@ var LevelMeter = ({
|
|
|
1283
1527
|
}
|
|
1284
1528
|
}
|
|
1285
1529
|
),
|
|
1286
|
-
showPeak && /* @__PURE__ */
|
|
1530
|
+
showPeak && /* @__PURE__ */ jsx7(
|
|
1287
1531
|
"div",
|
|
1288
1532
|
{
|
|
1289
1533
|
"data-testid": `${id}-peak`,
|
|
@@ -1304,7 +1548,7 @@ var LevelMeter = ({
|
|
|
1304
1548
|
]
|
|
1305
1549
|
}
|
|
1306
1550
|
),
|
|
1307
|
-
!compact && /* @__PURE__ */
|
|
1551
|
+
!compact && /* @__PURE__ */ jsx7(
|
|
1308
1552
|
"span",
|
|
1309
1553
|
{
|
|
1310
1554
|
style: {
|
|
@@ -1317,7 +1561,7 @@ var LevelMeter = ({
|
|
|
1317
1561
|
children: active && peakDb > -120 ? `${peakDb.toFixed(0)} dB` : "\u2014"
|
|
1318
1562
|
}
|
|
1319
1563
|
),
|
|
1320
|
-
clipped && /* @__PURE__ */
|
|
1564
|
+
clipped && /* @__PURE__ */ jsx7(
|
|
1321
1565
|
"span",
|
|
1322
1566
|
{
|
|
1323
1567
|
"data-testid": `${id}-clip`,
|
|
@@ -1342,7 +1586,7 @@ var LevelMeter = ({
|
|
|
1342
1586
|
};
|
|
1343
1587
|
|
|
1344
1588
|
// src/hooks/useTrackLevels.ts
|
|
1345
|
-
import { useEffect as
|
|
1589
|
+
import { useEffect as useEffect3, useRef as useRef4, useState as useState5 } from "react";
|
|
1346
1590
|
var meterDiagRLast = /* @__PURE__ */ new Map();
|
|
1347
1591
|
var POLL_INTERVAL_MS = 33;
|
|
1348
1592
|
var HIDDEN_RECHECK_MS = 250;
|
|
@@ -1353,9 +1597,9 @@ function isHidden() {
|
|
|
1353
1597
|
return typeof document !== "undefined" && document.hidden === true;
|
|
1354
1598
|
}
|
|
1355
1599
|
function useTrackLevels(host, enabled = true) {
|
|
1356
|
-
const mapRef =
|
|
1357
|
-
const listenersRef =
|
|
1358
|
-
const handleRef =
|
|
1600
|
+
const mapRef = useRef4(/* @__PURE__ */ new Map());
|
|
1601
|
+
const listenersRef = useRef4(/* @__PURE__ */ new Set());
|
|
1602
|
+
const handleRef = useRef4(null);
|
|
1359
1603
|
if (handleRef.current === null) {
|
|
1360
1604
|
handleRef.current = {
|
|
1361
1605
|
getLevel: (trackId) => mapRef.current.get(trackId) ?? null,
|
|
@@ -1367,7 +1611,7 @@ function useTrackLevels(host, enabled = true) {
|
|
|
1367
1611
|
}
|
|
1368
1612
|
};
|
|
1369
1613
|
}
|
|
1370
|
-
|
|
1614
|
+
useEffect3(() => {
|
|
1371
1615
|
const notify = () => {
|
|
1372
1616
|
listenersRef.current.forEach((l) => l());
|
|
1373
1617
|
};
|
|
@@ -1437,8 +1681,8 @@ function sameLevel(a, b) {
|
|
|
1437
1681
|
return a.peakDb === b.peakDb && a.clipped === b.clipped;
|
|
1438
1682
|
}
|
|
1439
1683
|
function useTrackLevel(handle, trackId) {
|
|
1440
|
-
const [level, setLevel] =
|
|
1441
|
-
|
|
1684
|
+
const [level, setLevel] = useState5(null);
|
|
1685
|
+
useEffect3(() => {
|
|
1442
1686
|
if (!handle) {
|
|
1443
1687
|
setLevel(null);
|
|
1444
1688
|
return;
|
|
@@ -1462,11 +1706,11 @@ function sameMeter(a, b) {
|
|
|
1462
1706
|
return a.active === b.active && a.clipped === b.clipped && a.peakDb === b.peakDb && Math.round(a.peakHoldDb * 2) === Math.round(b.peakHoldDb * 2);
|
|
1463
1707
|
}
|
|
1464
1708
|
function useTrackMeter(handle, trackId) {
|
|
1465
|
-
const [view, setView] =
|
|
1466
|
-
const heldDbRef =
|
|
1467
|
-
const heldAtRef =
|
|
1468
|
-
const lastTickRef =
|
|
1469
|
-
|
|
1709
|
+
const [view, setView] = useState5(IDLE_METER_VIEW);
|
|
1710
|
+
const heldDbRef = useRef4(METER_FLOOR_DB);
|
|
1711
|
+
const heldAtRef = useRef4(0);
|
|
1712
|
+
const lastTickRef = useRef4(0);
|
|
1713
|
+
useEffect3(() => {
|
|
1470
1714
|
if (!handle) {
|
|
1471
1715
|
heldDbRef.current = METER_FLOOR_DB;
|
|
1472
1716
|
lastTickRef.current = 0;
|
|
@@ -1509,8 +1753,8 @@ function useTrackMeter(handle, trackId) {
|
|
|
1509
1753
|
return view;
|
|
1510
1754
|
}
|
|
1511
1755
|
function useTransportPlaying(host) {
|
|
1512
|
-
const [playing, setPlaying] =
|
|
1513
|
-
|
|
1756
|
+
const [playing, setPlaying] = useState5(false);
|
|
1757
|
+
useEffect3(() => {
|
|
1514
1758
|
if (!host) {
|
|
1515
1759
|
setPlaying(false);
|
|
1516
1760
|
return;
|
|
@@ -1538,7 +1782,7 @@ function useTransportPlaying(host) {
|
|
|
1538
1782
|
}
|
|
1539
1783
|
|
|
1540
1784
|
// src/components/TrackMeterStrip.tsx
|
|
1541
|
-
import { jsx as
|
|
1785
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1542
1786
|
var TrackMeterStrip = ({
|
|
1543
1787
|
levels,
|
|
1544
1788
|
trackId,
|
|
@@ -1546,12 +1790,12 @@ var TrackMeterStrip = ({
|
|
|
1546
1790
|
className
|
|
1547
1791
|
}) => {
|
|
1548
1792
|
const meter = useTrackMeter(levels, trackId);
|
|
1549
|
-
return /* @__PURE__ */
|
|
1793
|
+
return /* @__PURE__ */ jsx8(
|
|
1550
1794
|
"div",
|
|
1551
1795
|
{
|
|
1552
1796
|
"data-testid": "sdk-track-meter",
|
|
1553
1797
|
className: `w-full px-2 py-1 bg-sas-panel-alt border border-t-0 border-sas-border ${roundBottom ? "rounded-b-sm" : ""} ${className ?? ""}`,
|
|
1554
|
-
children: /* @__PURE__ */
|
|
1798
|
+
children: /* @__PURE__ */ jsx8(
|
|
1555
1799
|
LevelMeter,
|
|
1556
1800
|
{
|
|
1557
1801
|
compact: true,
|
|
@@ -1567,7 +1811,7 @@ var TrackMeterStrip = ({
|
|
|
1567
1811
|
};
|
|
1568
1812
|
|
|
1569
1813
|
// src/components/VolumeSlider.tsx
|
|
1570
|
-
import { useCallback as
|
|
1814
|
+
import { useCallback as useCallback3, useState as useState6, useRef as useRef5, useEffect as useEffect4 } from "react";
|
|
1571
1815
|
|
|
1572
1816
|
// src/utils/volume-conversion.ts
|
|
1573
1817
|
var SLIDER_UNITY = 0.75;
|
|
@@ -1589,7 +1833,7 @@ function dbToSlider(db) {
|
|
|
1589
1833
|
}
|
|
1590
1834
|
|
|
1591
1835
|
// src/components/VolumeSlider.tsx
|
|
1592
|
-
import { jsx as
|
|
1836
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1593
1837
|
function formatDb(value) {
|
|
1594
1838
|
const db = sliderToDb(value);
|
|
1595
1839
|
if (db <= -60) return "-\u221E dB";
|
|
@@ -1597,12 +1841,12 @@ function formatDb(value) {
|
|
|
1597
1841
|
return `${sign}${db.toFixed(1)} dB`;
|
|
1598
1842
|
}
|
|
1599
1843
|
function useDebouncedCallback(callback, delay) {
|
|
1600
|
-
const timeoutRef =
|
|
1601
|
-
const callbackRef =
|
|
1602
|
-
|
|
1844
|
+
const timeoutRef = useRef5(null);
|
|
1845
|
+
const callbackRef = useRef5(callback);
|
|
1846
|
+
useEffect4(() => {
|
|
1603
1847
|
callbackRef.current = callback;
|
|
1604
1848
|
}, [callback]);
|
|
1605
|
-
const debouncedCallback =
|
|
1849
|
+
const debouncedCallback = useCallback3(
|
|
1606
1850
|
(...args) => {
|
|
1607
1851
|
if (timeoutRef.current) {
|
|
1608
1852
|
clearTimeout(timeoutRef.current);
|
|
@@ -1613,7 +1857,7 @@ function useDebouncedCallback(callback, delay) {
|
|
|
1613
1857
|
},
|
|
1614
1858
|
[delay]
|
|
1615
1859
|
);
|
|
1616
|
-
|
|
1860
|
+
useEffect4(() => {
|
|
1617
1861
|
return () => {
|
|
1618
1862
|
if (timeoutRef.current) {
|
|
1619
1863
|
clearTimeout(timeoutRef.current);
|
|
@@ -1628,15 +1872,15 @@ var VolumeSlider = ({
|
|
|
1628
1872
|
disabled = false,
|
|
1629
1873
|
className = ""
|
|
1630
1874
|
}) => {
|
|
1631
|
-
const [localValue, setLocalValue] =
|
|
1632
|
-
const [isDragging, setIsDragging] =
|
|
1633
|
-
|
|
1875
|
+
const [localValue, setLocalValue] = useState6(value);
|
|
1876
|
+
const [isDragging, setIsDragging] = useState6(false);
|
|
1877
|
+
useEffect4(() => {
|
|
1634
1878
|
if (!isDragging) {
|
|
1635
1879
|
setLocalValue(value);
|
|
1636
1880
|
}
|
|
1637
1881
|
}, [value, isDragging]);
|
|
1638
1882
|
const debouncedOnChange = useDebouncedCallback(onChange, 50);
|
|
1639
|
-
const handleChange =
|
|
1883
|
+
const handleChange = useCallback3(
|
|
1640
1884
|
(e) => {
|
|
1641
1885
|
const newValue = parseFloat(e.target.value);
|
|
1642
1886
|
setLocalValue(newValue);
|
|
@@ -1644,19 +1888,19 @@ var VolumeSlider = ({
|
|
|
1644
1888
|
},
|
|
1645
1889
|
[debouncedOnChange]
|
|
1646
1890
|
);
|
|
1647
|
-
const handleMouseDown =
|
|
1891
|
+
const handleMouseDown = useCallback3(() => {
|
|
1648
1892
|
setIsDragging(true);
|
|
1649
1893
|
}, []);
|
|
1650
|
-
const handleMouseUp =
|
|
1894
|
+
const handleMouseUp = useCallback3(() => {
|
|
1651
1895
|
setIsDragging(false);
|
|
1652
1896
|
onChange(localValue);
|
|
1653
1897
|
}, [localValue, onChange]);
|
|
1654
|
-
return /* @__PURE__ */
|
|
1898
|
+
return /* @__PURE__ */ jsx9(
|
|
1655
1899
|
"div",
|
|
1656
1900
|
{
|
|
1657
1901
|
className: `flex items-center ${className}`,
|
|
1658
1902
|
title: `Volume: ${formatDb(localValue)}`,
|
|
1659
|
-
children: /* @__PURE__ */
|
|
1903
|
+
children: /* @__PURE__ */ jsx9(
|
|
1660
1904
|
"input",
|
|
1661
1905
|
{
|
|
1662
1906
|
type: "range",
|
|
@@ -1696,8 +1940,8 @@ var VolumeSlider = ({
|
|
|
1696
1940
|
};
|
|
1697
1941
|
|
|
1698
1942
|
// src/components/PanSlider.tsx
|
|
1699
|
-
import { useCallback as
|
|
1700
|
-
import { jsx as
|
|
1943
|
+
import { useCallback as useCallback4, useState as useState7, useRef as useRef6, useEffect as useEffect5 } from "react";
|
|
1944
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1701
1945
|
function toPanDisplay(value) {
|
|
1702
1946
|
if (Math.abs(value) < 0.02) {
|
|
1703
1947
|
return "Center";
|
|
@@ -1706,12 +1950,12 @@ function toPanDisplay(value) {
|
|
|
1706
1950
|
return value < 0 ? `L${percent}` : `R${percent}`;
|
|
1707
1951
|
}
|
|
1708
1952
|
function useDebouncedCallback2(callback, delay) {
|
|
1709
|
-
const timeoutRef =
|
|
1710
|
-
const callbackRef =
|
|
1711
|
-
|
|
1953
|
+
const timeoutRef = useRef6(null);
|
|
1954
|
+
const callbackRef = useRef6(callback);
|
|
1955
|
+
useEffect5(() => {
|
|
1712
1956
|
callbackRef.current = callback;
|
|
1713
1957
|
}, [callback]);
|
|
1714
|
-
const debouncedCallback =
|
|
1958
|
+
const debouncedCallback = useCallback4(
|
|
1715
1959
|
(...args) => {
|
|
1716
1960
|
if (timeoutRef.current) {
|
|
1717
1961
|
clearTimeout(timeoutRef.current);
|
|
@@ -1722,7 +1966,7 @@ function useDebouncedCallback2(callback, delay) {
|
|
|
1722
1966
|
},
|
|
1723
1967
|
[delay]
|
|
1724
1968
|
);
|
|
1725
|
-
|
|
1969
|
+
useEffect5(() => {
|
|
1726
1970
|
return () => {
|
|
1727
1971
|
if (timeoutRef.current) {
|
|
1728
1972
|
clearTimeout(timeoutRef.current);
|
|
@@ -1737,15 +1981,15 @@ var PanSlider = ({
|
|
|
1737
1981
|
disabled = false,
|
|
1738
1982
|
className = ""
|
|
1739
1983
|
}) => {
|
|
1740
|
-
const [localValue, setLocalValue] =
|
|
1741
|
-
const [isDragging, setIsDragging] =
|
|
1742
|
-
|
|
1984
|
+
const [localValue, setLocalValue] = useState7(value);
|
|
1985
|
+
const [isDragging, setIsDragging] = useState7(false);
|
|
1986
|
+
useEffect5(() => {
|
|
1743
1987
|
if (!isDragging) {
|
|
1744
1988
|
setLocalValue(value);
|
|
1745
1989
|
}
|
|
1746
1990
|
}, [value, isDragging]);
|
|
1747
1991
|
const debouncedOnChange = useDebouncedCallback2(onChange, 50);
|
|
1748
|
-
const handleChange =
|
|
1992
|
+
const handleChange = useCallback4(
|
|
1749
1993
|
(e) => {
|
|
1750
1994
|
const newValue = parseFloat(e.target.value);
|
|
1751
1995
|
setLocalValue(newValue);
|
|
@@ -1753,23 +1997,23 @@ var PanSlider = ({
|
|
|
1753
1997
|
},
|
|
1754
1998
|
[debouncedOnChange]
|
|
1755
1999
|
);
|
|
1756
|
-
const handleMouseDown =
|
|
2000
|
+
const handleMouseDown = useCallback4(() => {
|
|
1757
2001
|
setIsDragging(true);
|
|
1758
2002
|
}, []);
|
|
1759
|
-
const handleMouseUp =
|
|
2003
|
+
const handleMouseUp = useCallback4(() => {
|
|
1760
2004
|
setIsDragging(false);
|
|
1761
2005
|
onChange(localValue);
|
|
1762
2006
|
}, [localValue, onChange]);
|
|
1763
|
-
const handleDoubleClick =
|
|
2007
|
+
const handleDoubleClick = useCallback4(() => {
|
|
1764
2008
|
setLocalValue(0);
|
|
1765
2009
|
onChange(0);
|
|
1766
2010
|
}, [onChange]);
|
|
1767
|
-
return /* @__PURE__ */
|
|
2011
|
+
return /* @__PURE__ */ jsx10(
|
|
1768
2012
|
"div",
|
|
1769
2013
|
{
|
|
1770
2014
|
className: `flex items-center ${className}`,
|
|
1771
2015
|
title: `Pan: ${toPanDisplay(localValue)}`,
|
|
1772
|
-
children: /* @__PURE__ */
|
|
2016
|
+
children: /* @__PURE__ */ jsx10(
|
|
1773
2017
|
"input",
|
|
1774
2018
|
{
|
|
1775
2019
|
type: "range",
|
|
@@ -1810,8 +2054,8 @@ var PanSlider = ({
|
|
|
1810
2054
|
};
|
|
1811
2055
|
|
|
1812
2056
|
// src/components/SorceryProgressBar.tsx
|
|
1813
|
-
import { useState as
|
|
1814
|
-
import { jsx as
|
|
2057
|
+
import { useState as useState8, useEffect as useEffect6, useRef as useRef7 } from "react";
|
|
2058
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1815
2059
|
function calculateTimeBasedTarget(elapsedMs, estimatedDurationMs) {
|
|
1816
2060
|
const t = elapsedMs / estimatedDurationMs;
|
|
1817
2061
|
if (t <= 0) return 0;
|
|
@@ -1856,20 +2100,20 @@ function SorceryProgressBar({
|
|
|
1856
2100
|
onProgressChange,
|
|
1857
2101
|
estimatedDurationMs
|
|
1858
2102
|
}) {
|
|
1859
|
-
const [progress, setProgress] =
|
|
1860
|
-
const timerRef =
|
|
1861
|
-
const isLoadingRef =
|
|
1862
|
-
const hasStartedRef =
|
|
1863
|
-
const startTimeRef =
|
|
1864
|
-
const onProgressChangeRef =
|
|
1865
|
-
const onCompleteRef =
|
|
2103
|
+
const [progress, setProgress] = useState8(initialProgress);
|
|
2104
|
+
const timerRef = useRef7(null);
|
|
2105
|
+
const isLoadingRef = useRef7(false);
|
|
2106
|
+
const hasStartedRef = useRef7(false);
|
|
2107
|
+
const startTimeRef = useRef7(0);
|
|
2108
|
+
const onProgressChangeRef = useRef7(onProgressChange);
|
|
2109
|
+
const onCompleteRef = useRef7(onComplete);
|
|
1866
2110
|
onProgressChangeRef.current = onProgressChange;
|
|
1867
2111
|
onCompleteRef.current = onComplete;
|
|
1868
|
-
const initialProgressRef =
|
|
2112
|
+
const initialProgressRef = useRef7(initialProgress);
|
|
1869
2113
|
initialProgressRef.current = initialProgress;
|
|
1870
|
-
const estimatedDurationMsRef =
|
|
2114
|
+
const estimatedDurationMsRef = useRef7(estimatedDurationMs);
|
|
1871
2115
|
estimatedDurationMsRef.current = estimatedDurationMs;
|
|
1872
|
-
|
|
2116
|
+
useEffect6(() => {
|
|
1873
2117
|
const wasLoading = isLoadingRef.current;
|
|
1874
2118
|
isLoadingRef.current = isLoading;
|
|
1875
2119
|
if (isLoading && !wasLoading) {
|
|
@@ -1931,12 +2175,12 @@ function SorceryProgressBar({
|
|
|
1931
2175
|
const displayProgress = Math.floor(progress);
|
|
1932
2176
|
const isComplete = !isLoading && progress === 100;
|
|
1933
2177
|
const transitionDuration = progress < 50 ? "300ms" : progress < 80 ? "500ms" : "700ms";
|
|
1934
|
-
return /* @__PURE__ */
|
|
2178
|
+
return /* @__PURE__ */ jsxs7(
|
|
1935
2179
|
"div",
|
|
1936
2180
|
{
|
|
1937
2181
|
className: `relative w-full ${heightClass} bg-sas-panel-alt border border-sas-border rounded-sm overflow-hidden shadow-inner`,
|
|
1938
2182
|
children: [
|
|
1939
|
-
/* @__PURE__ */
|
|
2183
|
+
/* @__PURE__ */ jsx11(
|
|
1940
2184
|
"div",
|
|
1941
2185
|
{
|
|
1942
2186
|
className: `
|
|
@@ -1954,13 +2198,13 @@ function SorceryProgressBar({
|
|
|
1954
2198
|
}
|
|
1955
2199
|
}
|
|
1956
2200
|
),
|
|
1957
|
-
/* @__PURE__ */
|
|
2201
|
+
/* @__PURE__ */ jsx11("div", { className: "absolute inset-0 flex items-center justify-center", children: isLoading && progress < 100 ? /* @__PURE__ */ jsxs7("span", { className: "font-mono text-xs text-sas-accent font-bold drop-shadow-md tracking-wider", children: [
|
|
1958
2202
|
statusText,
|
|
1959
2203
|
" ",
|
|
1960
2204
|
displayProgress,
|
|
1961
2205
|
"%"
|
|
1962
|
-
] }) : isComplete ? /* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
2206
|
+
] }) : isComplete ? /* @__PURE__ */ jsx11("span", { className: "font-mono text-xs text-sas-text font-bold drop-shadow-md tracking-wider", children: completeText }) : null }),
|
|
2207
|
+
/* @__PURE__ */ jsx11(
|
|
1964
2208
|
"div",
|
|
1965
2209
|
{
|
|
1966
2210
|
className: "absolute inset-0 pointer-events-none opacity-10",
|
|
@@ -1981,7 +2225,7 @@ function SorceryProgressBar({
|
|
|
1981
2225
|
}
|
|
1982
2226
|
|
|
1983
2227
|
// src/components/TrackRow.tsx
|
|
1984
|
-
import { Fragment, jsx as
|
|
2228
|
+
import { Fragment, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1985
2229
|
function TrackRow({
|
|
1986
2230
|
track,
|
|
1987
2231
|
prompt,
|
|
@@ -2010,6 +2254,7 @@ function TrackRow({
|
|
|
2010
2254
|
onFxToggle,
|
|
2011
2255
|
onFxPresetChange,
|
|
2012
2256
|
onFxDryWetChange,
|
|
2257
|
+
externalFxHost,
|
|
2013
2258
|
onToggleFxDrawer,
|
|
2014
2259
|
onProgressChange,
|
|
2015
2260
|
accentColor = "#A78BFA",
|
|
@@ -2040,7 +2285,7 @@ function TrackRow({
|
|
|
2040
2285
|
levels
|
|
2041
2286
|
}) {
|
|
2042
2287
|
const { muted: isMuted, solo: isSoloed, volume: currentVolume, pan: currentPan } = runtimeState;
|
|
2043
|
-
const [confirmDelete, setConfirmDelete] =
|
|
2288
|
+
const [confirmDelete, setConfirmDelete] = React9.useState(false);
|
|
2044
2289
|
const needsGeneration = !!(prompt?.trim() && !hasMidi && !isGenerating);
|
|
2045
2290
|
const hasFxActive = Object.values(fxDetailState).some(
|
|
2046
2291
|
(d) => d.enabled
|
|
@@ -2055,8 +2300,8 @@ function TrackRow({
|
|
|
2055
2300
|
};
|
|
2056
2301
|
const borderColorStyle = needsGeneration ? void 0 : accentColor;
|
|
2057
2302
|
const borderClass = needsGeneration ? "border-amber-400 animate-pulse" : "border-sas-border";
|
|
2058
|
-
return /* @__PURE__ */
|
|
2059
|
-
/* @__PURE__ */
|
|
2303
|
+
return /* @__PURE__ */ jsxs8("div", { "data-testid": "sdk-track-row-wrapper", className: "w-full", ...drag?.rowProps ?? {}, children: [
|
|
2304
|
+
/* @__PURE__ */ jsxs8(
|
|
2060
2305
|
"div",
|
|
2061
2306
|
{
|
|
2062
2307
|
"data-testid": "sdk-track-row",
|
|
@@ -2066,7 +2311,7 @@ function TrackRow({
|
|
|
2066
2311
|
borderLeftWidth: "3px"
|
|
2067
2312
|
},
|
|
2068
2313
|
children: [
|
|
2069
|
-
drag && /* @__PURE__ */
|
|
2314
|
+
drag && /* @__PURE__ */ jsx12(
|
|
2070
2315
|
"div",
|
|
2071
2316
|
{
|
|
2072
2317
|
"data-testid": "sdk-drag-handle",
|
|
@@ -2074,10 +2319,10 @@ function TrackRow({
|
|
|
2074
2319
|
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",
|
|
2075
2320
|
title: "Drag to reorder",
|
|
2076
2321
|
"aria-label": "Drag to reorder track",
|
|
2077
|
-
children: /* @__PURE__ */
|
|
2322
|
+
children: /* @__PURE__ */ jsx12(GripVertical, { className: "w-3.5 h-3.5", strokeWidth: 2 })
|
|
2078
2323
|
}
|
|
2079
2324
|
),
|
|
2080
|
-
isGenerating && /* @__PURE__ */
|
|
2325
|
+
isGenerating && /* @__PURE__ */ jsx12("div", { className: "absolute left-0 top-0 bottom-0 right-44 z-20", children: /* @__PURE__ */ jsx12(
|
|
2081
2326
|
SorceryProgressBar,
|
|
2082
2327
|
{
|
|
2083
2328
|
isLoading: true,
|
|
@@ -2088,14 +2333,14 @@ function TrackRow({
|
|
|
2088
2333
|
estimatedDurationMs: estimatedGenerationMs
|
|
2089
2334
|
}
|
|
2090
2335
|
) }),
|
|
2091
|
-
/* @__PURE__ */
|
|
2336
|
+
/* @__PURE__ */ jsxs8(
|
|
2092
2337
|
"div",
|
|
2093
2338
|
{
|
|
2094
2339
|
"data-testid": "sdk-track-content",
|
|
2095
2340
|
className: `flex flex-col flex-1 min-w-0 relative z-10 transition-opacity ${soloedOut ? "opacity-40" : ""}`,
|
|
2096
2341
|
title: soloedOut ? "Silenced \u2014 another track is soloed" : void 0,
|
|
2097
2342
|
children: [
|
|
2098
|
-
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */
|
|
2343
|
+
contentSlot ? contentSlot : onPromptChange ? /* @__PURE__ */ jsx12(
|
|
2099
2344
|
"input",
|
|
2100
2345
|
{
|
|
2101
2346
|
type: "text",
|
|
@@ -2108,10 +2353,10 @@ function TrackRow({
|
|
|
2108
2353
|
className: "sas-input w-full px-2 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed"
|
|
2109
2354
|
}
|
|
2110
2355
|
) : null,
|
|
2111
|
-
/* @__PURE__ */
|
|
2112
|
-
track.name && /* @__PURE__ */
|
|
2113
|
-
/* @__PURE__ */
|
|
2114
|
-
/* @__PURE__ */
|
|
2356
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2 mt-1", children: [
|
|
2357
|
+
track.name && /* @__PURE__ */ jsx12("span", { className: "text-[10px] text-sas-muted/60 truncate pl-2 flex-shrink-0 max-w-[80px]", title: track.name, children: track.name }),
|
|
2358
|
+
/* @__PURE__ */ jsx12("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "vol:" }),
|
|
2359
|
+
/* @__PURE__ */ jsx12(
|
|
2115
2360
|
VolumeSlider,
|
|
2116
2361
|
{
|
|
2117
2362
|
value: currentVolume,
|
|
@@ -2120,8 +2365,8 @@ function TrackRow({
|
|
|
2120
2365
|
className: "flex-1 min-w-[40px]"
|
|
2121
2366
|
}
|
|
2122
2367
|
),
|
|
2123
|
-
/* @__PURE__ */
|
|
2124
|
-
/* @__PURE__ */
|
|
2368
|
+
/* @__PURE__ */ jsx12("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", children: "pan:" }),
|
|
2369
|
+
/* @__PURE__ */ jsx12(
|
|
2125
2370
|
PanSlider,
|
|
2126
2371
|
{
|
|
2127
2372
|
value: currentPan,
|
|
@@ -2134,27 +2379,27 @@ function TrackRow({
|
|
|
2134
2379
|
]
|
|
2135
2380
|
}
|
|
2136
2381
|
),
|
|
2137
|
-
error && /* @__PURE__ */
|
|
2382
|
+
error && /* @__PURE__ */ jsx12(
|
|
2138
2383
|
"div",
|
|
2139
2384
|
{
|
|
2140
2385
|
"data-testid": "sdk-error-indicator",
|
|
2141
2386
|
className: "flex-shrink-0 relative z-10 self-stretch flex items-center px-1 group cursor-help",
|
|
2142
2387
|
title: error,
|
|
2143
|
-
children: /* @__PURE__ */
|
|
2144
|
-
/* @__PURE__ */
|
|
2388
|
+
children: /* @__PURE__ */ jsxs8("div", { className: "relative", children: [
|
|
2389
|
+
/* @__PURE__ */ jsx12(
|
|
2145
2390
|
AlertCircle,
|
|
2146
2391
|
{
|
|
2147
2392
|
className: "w-5 h-5 text-red-500 animate-pulse",
|
|
2148
2393
|
strokeWidth: 2.5
|
|
2149
2394
|
}
|
|
2150
2395
|
),
|
|
2151
|
-
/* @__PURE__ */
|
|
2396
|
+
/* @__PURE__ */ jsx12("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-red-900/95 text-red-100 text-xs rounded shadow-lg whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50 max-w-[200px] truncate", children: error })
|
|
2152
2397
|
] })
|
|
2153
2398
|
}
|
|
2154
2399
|
),
|
|
2155
|
-
/* @__PURE__ */
|
|
2156
|
-
/* @__PURE__ */
|
|
2157
|
-
onGenerate && /* @__PURE__ */
|
|
2400
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-0.5 flex-shrink-0 relative z-30 justify-center", children: [
|
|
2401
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex gap-1 items-center", children: [
|
|
2402
|
+
onGenerate && /* @__PURE__ */ jsx12(
|
|
2158
2403
|
"button",
|
|
2159
2404
|
{
|
|
2160
2405
|
"data-testid": "sdk-generate-button",
|
|
@@ -2165,7 +2410,7 @@ function TrackRow({
|
|
|
2165
2410
|
children: "Create"
|
|
2166
2411
|
}
|
|
2167
2412
|
),
|
|
2168
|
-
onCopy && /* @__PURE__ */
|
|
2413
|
+
onCopy && /* @__PURE__ */ jsx12(
|
|
2169
2414
|
"button",
|
|
2170
2415
|
{
|
|
2171
2416
|
"data-testid": "sdk-copy-button",
|
|
@@ -2176,7 +2421,7 @@ function TrackRow({
|
|
|
2176
2421
|
children: "Copy"
|
|
2177
2422
|
}
|
|
2178
2423
|
),
|
|
2179
|
-
/* @__PURE__ */
|
|
2424
|
+
/* @__PURE__ */ jsx12(
|
|
2180
2425
|
"button",
|
|
2181
2426
|
{
|
|
2182
2427
|
"data-testid": "sdk-mute-button",
|
|
@@ -2186,7 +2431,7 @@ function TrackRow({
|
|
|
2186
2431
|
children: "M"
|
|
2187
2432
|
}
|
|
2188
2433
|
),
|
|
2189
|
-
onDelete && /* @__PURE__ */
|
|
2434
|
+
onDelete && /* @__PURE__ */ jsx12(
|
|
2190
2435
|
"button",
|
|
2191
2436
|
{
|
|
2192
2437
|
"data-testid": "sdk-delete-button",
|
|
@@ -2197,8 +2442,8 @@ function TrackRow({
|
|
|
2197
2442
|
}
|
|
2198
2443
|
)
|
|
2199
2444
|
] }),
|
|
2200
|
-
/* @__PURE__ */
|
|
2201
|
-
onShuffle && /* @__PURE__ */
|
|
2445
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex gap-1 items-center", children: [
|
|
2446
|
+
onShuffle && /* @__PURE__ */ jsx12(
|
|
2202
2447
|
"button",
|
|
2203
2448
|
{
|
|
2204
2449
|
"data-testid": "sdk-shuffle-button",
|
|
@@ -2209,7 +2454,7 @@ function TrackRow({
|
|
|
2209
2454
|
children: "Shuffle"
|
|
2210
2455
|
}
|
|
2211
2456
|
),
|
|
2212
|
-
onToggleFxDrawer && /* @__PURE__ */
|
|
2457
|
+
onToggleFxDrawer && /* @__PURE__ */ jsx12(
|
|
2213
2458
|
"button",
|
|
2214
2459
|
{
|
|
2215
2460
|
"data-testid": "sdk-fx-button",
|
|
@@ -2220,7 +2465,7 @@ function TrackRow({
|
|
|
2220
2465
|
children: "FX"
|
|
2221
2466
|
}
|
|
2222
2467
|
),
|
|
2223
|
-
/* @__PURE__ */
|
|
2468
|
+
/* @__PURE__ */ jsx12(
|
|
2224
2469
|
"button",
|
|
2225
2470
|
{
|
|
2226
2471
|
"data-testid": "sdk-solo-button",
|
|
@@ -2231,7 +2476,7 @@ function TrackRow({
|
|
|
2231
2476
|
children: "S"
|
|
2232
2477
|
}
|
|
2233
2478
|
),
|
|
2234
|
-
onToggleDrawer && /* @__PURE__ */
|
|
2479
|
+
onToggleDrawer && /* @__PURE__ */ jsx12(
|
|
2235
2480
|
"button",
|
|
2236
2481
|
{
|
|
2237
2482
|
"data-testid": "sdk-plugin-button",
|
|
@@ -2239,7 +2484,7 @@ function TrackRow({
|
|
|
2239
2484
|
disabled: isGenerating,
|
|
2240
2485
|
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"}`,
|
|
2241
2486
|
title: `Sound \u2014 presets & history${instrumentMissing ? " (instrument missing)" : ""}`,
|
|
2242
|
-
children: /* @__PURE__ */
|
|
2487
|
+
children: /* @__PURE__ */ jsx12(ChevronDown, { className: "w-3 h-3", strokeWidth: 2.5 })
|
|
2243
2488
|
}
|
|
2244
2489
|
)
|
|
2245
2490
|
] })
|
|
@@ -2247,13 +2492,13 @@ function TrackRow({
|
|
|
2247
2492
|
]
|
|
2248
2493
|
}
|
|
2249
2494
|
),
|
|
2250
|
-
levels && /* @__PURE__ */
|
|
2251
|
-
drawerOpen && /* @__PURE__ */
|
|
2495
|
+
levels && /* @__PURE__ */ jsx12(TrackMeterStrip, { levels, trackId: track.id, roundBottom: !drawerOpen }),
|
|
2496
|
+
drawerOpen && /* @__PURE__ */ jsx12(
|
|
2252
2497
|
"div",
|
|
2253
2498
|
{
|
|
2254
2499
|
"data-testid": "sdk-track-drawer",
|
|
2255
2500
|
className: "border border-t-0 border-sas-border bg-sas-bg rounded-b-sm px-3 py-2 max-h-[260px] overflow-y-auto",
|
|
2256
|
-
children: /* @__PURE__ */
|
|
2501
|
+
children: /* @__PURE__ */ jsx12(
|
|
2257
2502
|
TrackDrawer,
|
|
2258
2503
|
{
|
|
2259
2504
|
activeTab: drawerTab,
|
|
@@ -2263,6 +2508,7 @@ function TrackRow({
|
|
|
2263
2508
|
onFxToggle,
|
|
2264
2509
|
onFxPresetChange,
|
|
2265
2510
|
onFxDryWetChange,
|
|
2511
|
+
externalFxHost,
|
|
2266
2512
|
fxDisabled: isGenerating,
|
|
2267
2513
|
instruments: availableInstruments,
|
|
2268
2514
|
currentPluginId: currentInstrumentPluginId ?? null,
|
|
@@ -2289,13 +2535,13 @@ function TrackRow({
|
|
|
2289
2535
|
)
|
|
2290
2536
|
}
|
|
2291
2537
|
),
|
|
2292
|
-
/* @__PURE__ */
|
|
2538
|
+
/* @__PURE__ */ jsx12(
|
|
2293
2539
|
ConfirmDialog,
|
|
2294
2540
|
{
|
|
2295
2541
|
open: confirmDelete,
|
|
2296
2542
|
title: "Delete track?",
|
|
2297
|
-
message: /* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
2543
|
+
message: /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
2544
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sas-text", children: track.name?.trim() || "This track" }),
|
|
2299
2545
|
" will be permanently removed from this scene. This cannot be undone."
|
|
2300
2546
|
] }),
|
|
2301
2547
|
confirmLabel: "Delete",
|
|
@@ -2311,13 +2557,13 @@ function TrackRow({
|
|
|
2311
2557
|
}
|
|
2312
2558
|
|
|
2313
2559
|
// src/components/CrossfadeTrackRow.tsx
|
|
2314
|
-
import
|
|
2315
|
-
import { Fragment as Fragment2, jsx as
|
|
2560
|
+
import React10 from "react";
|
|
2561
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2316
2562
|
function LayerCaption({ tag, layer }) {
|
|
2317
|
-
return /* @__PURE__ */
|
|
2318
|
-
/* @__PURE__ */
|
|
2319
|
-
/* @__PURE__ */
|
|
2320
|
-
layer.soundLabel && /* @__PURE__ */
|
|
2563
|
+
return /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
2564
|
+
/* @__PURE__ */ jsx13("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
|
|
2565
|
+
/* @__PURE__ */ jsx13("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
|
|
2566
|
+
layer.soundLabel && /* @__PURE__ */ jsxs9("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
|
|
2321
2567
|
"\xB7 ",
|
|
2322
2568
|
layer.soundLabel
|
|
2323
2569
|
] })
|
|
@@ -2336,8 +2582,8 @@ function CrossfadeTrackRow({
|
|
|
2336
2582
|
levels,
|
|
2337
2583
|
accentColor = "#9333EA"
|
|
2338
2584
|
}) {
|
|
2339
|
-
const [confirmDelete, setConfirmDelete] =
|
|
2340
|
-
const renderLayer = (layer, slot, tag) => /* @__PURE__ */
|
|
2585
|
+
const [confirmDelete, setConfirmDelete] = React10.useState(false);
|
|
2586
|
+
const renderLayer = (layer, slot, tag) => /* @__PURE__ */ jsx13(
|
|
2341
2587
|
TrackRow,
|
|
2342
2588
|
{
|
|
2343
2589
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -2347,23 +2593,23 @@ function CrossfadeTrackRow({
|
|
|
2347
2593
|
drawerTab: "fx",
|
|
2348
2594
|
levels,
|
|
2349
2595
|
accentColor,
|
|
2350
|
-
contentSlot: /* @__PURE__ */
|
|
2596
|
+
contentSlot: /* @__PURE__ */ jsx13(LayerCaption, { tag, layer }),
|
|
2351
2597
|
onMuteToggle,
|
|
2352
2598
|
onSoloToggle,
|
|
2353
2599
|
onVolumeChange: (v) => onVolumeChange(slot, v),
|
|
2354
2600
|
onPanChange: (p) => onPanChange(slot, p)
|
|
2355
2601
|
}
|
|
2356
2602
|
);
|
|
2357
|
-
return /* @__PURE__ */
|
|
2603
|
+
return /* @__PURE__ */ jsxs9(
|
|
2358
2604
|
"div",
|
|
2359
2605
|
{
|
|
2360
2606
|
"data-testid": "crossfade-track-row",
|
|
2361
2607
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
2362
2608
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
2363
2609
|
children: [
|
|
2364
|
-
/* @__PURE__ */
|
|
2365
|
-
/* @__PURE__ */
|
|
2366
|
-
/* @__PURE__ */
|
|
2610
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
|
|
2611
|
+
/* @__PURE__ */ jsx13("span", { className: "text-[10px] font-bold uppercase tracking-wide", style: { color: accentColor }, children: "\u21C4 Crossfade" }),
|
|
2612
|
+
/* @__PURE__ */ jsx13(
|
|
2367
2613
|
"button",
|
|
2368
2614
|
{
|
|
2369
2615
|
"data-testid": "crossfade-delete-button",
|
|
@@ -2376,8 +2622,8 @@ function CrossfadeTrackRow({
|
|
|
2376
2622
|
)
|
|
2377
2623
|
] }),
|
|
2378
2624
|
renderLayer(origin, "origin", "Origin"),
|
|
2379
|
-
/* @__PURE__ */
|
|
2380
|
-
/* @__PURE__ */
|
|
2625
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "crossfade-slider-row", children: [
|
|
2626
|
+
/* @__PURE__ */ jsx13(
|
|
2381
2627
|
"span",
|
|
2382
2628
|
{
|
|
2383
2629
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -2385,7 +2631,7 @@ function CrossfadeTrackRow({
|
|
|
2385
2631
|
children: origin.sourceName ?? origin.name
|
|
2386
2632
|
}
|
|
2387
2633
|
),
|
|
2388
|
-
/* @__PURE__ */
|
|
2634
|
+
/* @__PURE__ */ jsx13(
|
|
2389
2635
|
"input",
|
|
2390
2636
|
{
|
|
2391
2637
|
type: "range",
|
|
@@ -2401,7 +2647,7 @@ function CrossfadeTrackRow({
|
|
|
2401
2647
|
"aria-label": "Crossfade position"
|
|
2402
2648
|
}
|
|
2403
2649
|
),
|
|
2404
|
-
/* @__PURE__ */
|
|
2650
|
+
/* @__PURE__ */ jsx13(
|
|
2405
2651
|
"span",
|
|
2406
2652
|
{
|
|
2407
2653
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -2411,12 +2657,12 @@ function CrossfadeTrackRow({
|
|
|
2411
2657
|
)
|
|
2412
2658
|
] }),
|
|
2413
2659
|
renderLayer(target, "target", "Target"),
|
|
2414
|
-
/* @__PURE__ */
|
|
2660
|
+
/* @__PURE__ */ jsx13(
|
|
2415
2661
|
ConfirmDialog,
|
|
2416
2662
|
{
|
|
2417
2663
|
open: confirmDelete,
|
|
2418
2664
|
title: "Delete crossfade?",
|
|
2419
|
-
message: /* @__PURE__ */
|
|
2665
|
+
message: /* @__PURE__ */ jsx13(Fragment2, { children: "This crossfade pair (both layers) will be permanently removed from this scene. This cannot be undone." }),
|
|
2420
2666
|
confirmLabel: "Delete",
|
|
2421
2667
|
onConfirm: () => {
|
|
2422
2668
|
setConfirmDelete(false);
|
|
@@ -2659,22 +2905,22 @@ function defaultFadeGesture(role) {
|
|
|
2659
2905
|
}
|
|
2660
2906
|
|
|
2661
2907
|
// src/components/FadeTrackRow.tsx
|
|
2662
|
-
import
|
|
2663
|
-
import { Fragment as Fragment3, jsx as
|
|
2908
|
+
import React11 from "react";
|
|
2909
|
+
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2664
2910
|
function FadeCaption({
|
|
2665
2911
|
layer,
|
|
2666
2912
|
direction,
|
|
2667
2913
|
gesture
|
|
2668
2914
|
}) {
|
|
2669
2915
|
const tag = direction === "in" ? "Fade in" : "Fade out";
|
|
2670
|
-
return /* @__PURE__ */
|
|
2671
|
-
/* @__PURE__ */
|
|
2672
|
-
/* @__PURE__ */
|
|
2673
|
-
layer.soundLabel && /* @__PURE__ */
|
|
2916
|
+
return /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 min-w-0 px-2 py-0.5", children: [
|
|
2917
|
+
/* @__PURE__ */ jsx14("span", { className: "text-[9px] font-bold uppercase tracking-wide text-sas-accent flex-shrink-0", children: tag }),
|
|
2918
|
+
/* @__PURE__ */ jsx14("span", { className: "text-[11px] text-sas-text truncate", title: layer.sourceName ?? layer.name, children: layer.sourceName ?? layer.name }),
|
|
2919
|
+
layer.soundLabel && /* @__PURE__ */ jsxs10("span", { className: "text-[9px] text-sas-muted/60 truncate flex-shrink-0", title: layer.soundLabel, children: [
|
|
2674
2920
|
"\xB7 ",
|
|
2675
2921
|
layer.soundLabel
|
|
2676
2922
|
] }),
|
|
2677
|
-
/* @__PURE__ */
|
|
2923
|
+
/* @__PURE__ */ jsxs10("span", { className: "text-[9px] text-sas-muted/50 flex-shrink-0", title: `Fade gesture: ${gesture}`, children: [
|
|
2678
2924
|
"\xB7 ",
|
|
2679
2925
|
gesture
|
|
2680
2926
|
] })
|
|
@@ -2695,20 +2941,20 @@ function FadeTrackRow({
|
|
|
2695
2941
|
levels,
|
|
2696
2942
|
accentColor = "#9333EA"
|
|
2697
2943
|
}) {
|
|
2698
|
-
const [confirmDelete, setConfirmDelete] =
|
|
2944
|
+
const [confirmDelete, setConfirmDelete] = React11.useState(false);
|
|
2699
2945
|
const leftLabel = direction === "in" ? "(silent)" : layer.sourceName ?? layer.name;
|
|
2700
2946
|
const rightLabel = direction === "in" ? layer.sourceName ?? layer.name : "(silent)";
|
|
2701
2947
|
const verb = effect && effect !== "fade" ? effect.charAt(0).toUpperCase() + effect.slice(1) : "Fade";
|
|
2702
2948
|
const badge = direction === "in" ? `\u2197 ${verb} in` : `\u2198 ${verb} out`;
|
|
2703
|
-
return /* @__PURE__ */
|
|
2949
|
+
return /* @__PURE__ */ jsxs10(
|
|
2704
2950
|
"div",
|
|
2705
2951
|
{
|
|
2706
2952
|
"data-testid": "fade-track-row",
|
|
2707
2953
|
className: "w-full rounded-sm border border-sas-border bg-sas-panel/40 overflow-hidden",
|
|
2708
2954
|
style: { borderLeftColor: accentColor, borderLeftWidth: "3px" },
|
|
2709
2955
|
children: [
|
|
2710
|
-
/* @__PURE__ */
|
|
2711
|
-
/* @__PURE__ */
|
|
2956
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between px-2 py-1 bg-sas-panel-alt/60", children: [
|
|
2957
|
+
/* @__PURE__ */ jsx14(
|
|
2712
2958
|
"span",
|
|
2713
2959
|
{
|
|
2714
2960
|
"data-testid": "fade-direction-badge",
|
|
@@ -2717,7 +2963,7 @@ function FadeTrackRow({
|
|
|
2717
2963
|
children: badge
|
|
2718
2964
|
}
|
|
2719
2965
|
),
|
|
2720
|
-
/* @__PURE__ */
|
|
2966
|
+
/* @__PURE__ */ jsx14(
|
|
2721
2967
|
"button",
|
|
2722
2968
|
{
|
|
2723
2969
|
"data-testid": "fade-delete-button",
|
|
@@ -2729,7 +2975,7 @@ function FadeTrackRow({
|
|
|
2729
2975
|
}
|
|
2730
2976
|
)
|
|
2731
2977
|
] }),
|
|
2732
|
-
/* @__PURE__ */
|
|
2978
|
+
/* @__PURE__ */ jsx14(
|
|
2733
2979
|
TrackRow,
|
|
2734
2980
|
{
|
|
2735
2981
|
track: { id: layer.trackId, name: "", role: layer.role },
|
|
@@ -2739,15 +2985,15 @@ function FadeTrackRow({
|
|
|
2739
2985
|
drawerTab: "fx",
|
|
2740
2986
|
levels,
|
|
2741
2987
|
accentColor,
|
|
2742
|
-
contentSlot: /* @__PURE__ */
|
|
2988
|
+
contentSlot: /* @__PURE__ */ jsx14(FadeCaption, { layer, direction, gesture }),
|
|
2743
2989
|
onMuteToggle,
|
|
2744
2990
|
onSoloToggle,
|
|
2745
2991
|
onVolumeChange,
|
|
2746
2992
|
onPanChange
|
|
2747
2993
|
}
|
|
2748
2994
|
),
|
|
2749
|
-
/* @__PURE__ */
|
|
2750
|
-
/* @__PURE__ */
|
|
2995
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 px-3 py-1.5", "data-testid": "fade-slider-row", children: [
|
|
2996
|
+
/* @__PURE__ */ jsx14(
|
|
2751
2997
|
"span",
|
|
2752
2998
|
{
|
|
2753
2999
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] text-right flex-shrink-0",
|
|
@@ -2755,7 +3001,7 @@ function FadeTrackRow({
|
|
|
2755
3001
|
children: leftLabel
|
|
2756
3002
|
}
|
|
2757
3003
|
),
|
|
2758
|
-
/* @__PURE__ */
|
|
3004
|
+
/* @__PURE__ */ jsx14(
|
|
2759
3005
|
"input",
|
|
2760
3006
|
{
|
|
2761
3007
|
type: "range",
|
|
@@ -2771,7 +3017,7 @@ function FadeTrackRow({
|
|
|
2771
3017
|
"aria-label": "Fade position"
|
|
2772
3018
|
}
|
|
2773
3019
|
),
|
|
2774
|
-
/* @__PURE__ */
|
|
3020
|
+
/* @__PURE__ */ jsx14(
|
|
2775
3021
|
"span",
|
|
2776
3022
|
{
|
|
2777
3023
|
className: "text-[9px] text-sas-muted/60 truncate max-w-[70px] flex-shrink-0",
|
|
@@ -2780,12 +3026,12 @@ function FadeTrackRow({
|
|
|
2780
3026
|
}
|
|
2781
3027
|
)
|
|
2782
3028
|
] }),
|
|
2783
|
-
/* @__PURE__ */
|
|
3029
|
+
/* @__PURE__ */ jsx14(
|
|
2784
3030
|
ConfirmDialog,
|
|
2785
3031
|
{
|
|
2786
3032
|
open: confirmDelete,
|
|
2787
3033
|
title: "Delete fade?",
|
|
2788
|
-
message: /* @__PURE__ */
|
|
3034
|
+
message: /* @__PURE__ */ jsx14(Fragment3, { children: "This fade track will be permanently removed from this scene. This cannot be undone." }),
|
|
2789
3035
|
confirmLabel: "Delete",
|
|
2790
3036
|
onConfirm: () => {
|
|
2791
3037
|
setConfirmDelete(false);
|
|
@@ -2801,8 +3047,8 @@ function FadeTrackRow({
|
|
|
2801
3047
|
}
|
|
2802
3048
|
|
|
2803
3049
|
// src/components/FadeModal.tsx
|
|
2804
|
-
import { useCallback as
|
|
2805
|
-
import { Fragment as Fragment4, jsx as
|
|
3050
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef8, useState as useState9 } from "react";
|
|
3051
|
+
import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2806
3052
|
function shortId(dbId) {
|
|
2807
3053
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
2808
3054
|
}
|
|
@@ -2845,7 +3091,7 @@ function OrphanRow({
|
|
|
2845
3091
|
}) {
|
|
2846
3092
|
const primary = track.prompt?.trim() || track.name;
|
|
2847
3093
|
const meta = [track.role, shortId(track.dbId), gesture].filter(Boolean).join(" \xB7 ");
|
|
2848
|
-
return /* @__PURE__ */
|
|
3094
|
+
return /* @__PURE__ */ jsxs11(
|
|
2849
3095
|
"button",
|
|
2850
3096
|
{
|
|
2851
3097
|
type: "button",
|
|
@@ -2857,8 +3103,8 @@ function OrphanRow({
|
|
|
2857
3103
|
disabled,
|
|
2858
3104
|
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"}`,
|
|
2859
3105
|
children: [
|
|
2860
|
-
/* @__PURE__ */
|
|
2861
|
-
meta && /* @__PURE__ */
|
|
3106
|
+
/* @__PURE__ */ jsx15("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
|
|
3107
|
+
meta && /* @__PURE__ */ jsx15("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
|
|
2862
3108
|
]
|
|
2863
3109
|
}
|
|
2864
3110
|
);
|
|
@@ -2875,14 +3121,14 @@ function FadeModal({
|
|
|
2875
3121
|
onCreate,
|
|
2876
3122
|
testIdPrefix = "fade-modal"
|
|
2877
3123
|
}) {
|
|
2878
|
-
const [load, setLoad] =
|
|
2879
|
-
const [selectedDbId, setSelectedDbId] =
|
|
2880
|
-
const [isCreating, setIsCreating] =
|
|
2881
|
-
const [error, setError] =
|
|
2882
|
-
const [fromName, setFromName] =
|
|
2883
|
-
const [toName, setToName] =
|
|
2884
|
-
const cancelRef =
|
|
2885
|
-
const refresh =
|
|
3124
|
+
const [load, setLoad] = useState9({ status: "loading" });
|
|
3125
|
+
const [selectedDbId, setSelectedDbId] = useState9("");
|
|
3126
|
+
const [isCreating, setIsCreating] = useState9(false);
|
|
3127
|
+
const [error, setError] = useState9(null);
|
|
3128
|
+
const [fromName, setFromName] = useState9(null);
|
|
3129
|
+
const [toName, setToName] = useState9(null);
|
|
3130
|
+
const cancelRef = useRef8(null);
|
|
3131
|
+
const refresh = useCallback5(async () => {
|
|
2886
3132
|
if (!host.listSceneFamilyTracks) {
|
|
2887
3133
|
setLoad({ status: "error", message: "This host does not support fades." });
|
|
2888
3134
|
return;
|
|
@@ -2902,7 +3148,7 @@ function FadeModal({
|
|
|
2902
3148
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
2903
3149
|
}
|
|
2904
3150
|
}, [host, fromSceneId, toSceneId]);
|
|
2905
|
-
|
|
3151
|
+
useEffect7(() => {
|
|
2906
3152
|
if (open) {
|
|
2907
3153
|
setError(null);
|
|
2908
3154
|
setIsCreating(false);
|
|
@@ -2910,29 +3156,29 @@ function FadeModal({
|
|
|
2910
3156
|
void refresh();
|
|
2911
3157
|
}
|
|
2912
3158
|
}, [open, refresh]);
|
|
2913
|
-
const excludeSet =
|
|
2914
|
-
const { fadeOut, fadeIn } =
|
|
3159
|
+
const excludeSet = useMemo4(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
3160
|
+
const { fadeOut, fadeIn } = useMemo4(
|
|
2915
3161
|
() => load.status === "ready" ? computeOrphans(load.from, load.to, excludeSet) : { fadeOut: [], fadeIn: [] },
|
|
2916
3162
|
[load, excludeSet]
|
|
2917
3163
|
);
|
|
2918
|
-
const allOrphans =
|
|
3164
|
+
const allOrphans = useMemo4(
|
|
2919
3165
|
() => [
|
|
2920
3166
|
...fadeOut.map((t) => ({ track: t, direction: "out" })),
|
|
2921
3167
|
...fadeIn.map((t) => ({ track: t, direction: "in" }))
|
|
2922
3168
|
],
|
|
2923
3169
|
[fadeOut, fadeIn]
|
|
2924
3170
|
);
|
|
2925
|
-
|
|
3171
|
+
useEffect7(() => {
|
|
2926
3172
|
if (!allOrphans.some((o) => o.track.dbId === selectedDbId)) {
|
|
2927
3173
|
setSelectedDbId(allOrphans[0]?.track.dbId ?? "");
|
|
2928
3174
|
}
|
|
2929
3175
|
}, [allOrphans, selectedDbId]);
|
|
2930
3176
|
const selected = allOrphans.find((o) => o.track.dbId === selectedDbId) ?? null;
|
|
2931
3177
|
const canCreate = !isCreating && !!selected;
|
|
2932
|
-
const handleClose =
|
|
3178
|
+
const handleClose = useCallback5(() => {
|
|
2933
3179
|
if (!isCreating) onClose();
|
|
2934
3180
|
}, [isCreating, onClose]);
|
|
2935
|
-
const handleCreate =
|
|
3181
|
+
const handleCreate = useCallback5(async () => {
|
|
2936
3182
|
if (!selected) return;
|
|
2937
3183
|
setIsCreating(true);
|
|
2938
3184
|
setError(null);
|
|
@@ -2953,16 +3199,16 @@ function FadeModal({
|
|
|
2953
3199
|
if (!open) return null;
|
|
2954
3200
|
const renderSection = (heading, list, section) => {
|
|
2955
3201
|
if (list.length === 0) return null;
|
|
2956
|
-
return /* @__PURE__ */
|
|
2957
|
-
/* @__PURE__ */
|
|
2958
|
-
/* @__PURE__ */
|
|
3202
|
+
return /* @__PURE__ */ jsxs11("div", { className: "block", children: [
|
|
3203
|
+
/* @__PURE__ */ jsx15("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: heading }),
|
|
3204
|
+
/* @__PURE__ */ jsx15(
|
|
2959
3205
|
"div",
|
|
2960
3206
|
{
|
|
2961
3207
|
role: "radiogroup",
|
|
2962
3208
|
"aria-label": heading,
|
|
2963
3209
|
"data-testid": `${testIdPrefix}-${section === "out" ? "fade-out" : "fade-in"}-list`,
|
|
2964
3210
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
2965
|
-
children: list.map((t) => /* @__PURE__ */
|
|
3211
|
+
children: list.map((t) => /* @__PURE__ */ jsx15(
|
|
2966
3212
|
OrphanRow,
|
|
2967
3213
|
{
|
|
2968
3214
|
track: t,
|
|
@@ -2978,32 +3224,32 @@ function FadeModal({
|
|
|
2978
3224
|
)
|
|
2979
3225
|
] });
|
|
2980
3226
|
};
|
|
2981
|
-
return /* @__PURE__ */
|
|
3227
|
+
return /* @__PURE__ */ jsx15(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs11(
|
|
2982
3228
|
"div",
|
|
2983
3229
|
{
|
|
2984
3230
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
2985
3231
|
onClick: (e) => e.stopPropagation(),
|
|
2986
3232
|
"data-testid": `${testIdPrefix}-box`,
|
|
2987
3233
|
children: [
|
|
2988
|
-
/* @__PURE__ */
|
|
2989
|
-
/* @__PURE__ */
|
|
3234
|
+
/* @__PURE__ */ jsx15("h3", { className: "text-sm font-bold text-sas-text", children: "Add fade" }),
|
|
3235
|
+
/* @__PURE__ */ jsxs11("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
|
|
2990
3236
|
"Tracks with no counterpart between",
|
|
2991
3237
|
" ",
|
|
2992
|
-
/* @__PURE__ */
|
|
3238
|
+
/* @__PURE__ */ jsx15("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
2993
3239
|
" and",
|
|
2994
3240
|
" ",
|
|
2995
|
-
/* @__PURE__ */
|
|
3241
|
+
/* @__PURE__ */ jsx15("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
2996
3242
|
" can gracefully fade out (leaving) or fade in (entering) across this transition."
|
|
2997
3243
|
] }),
|
|
2998
|
-
load.status === "loading" && /* @__PURE__ */
|
|
2999
|
-
load.status === "error" && /* @__PURE__ */
|
|
3000
|
-
load.status === "ready" && (allOrphans.length === 0 ? /* @__PURE__ */
|
|
3244
|
+
load.status === "loading" && /* @__PURE__ */ jsx15("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
|
|
3245
|
+
load.status === "error" && /* @__PURE__ */ jsx15("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
|
|
3246
|
+
load.status === "ready" && (allOrphans.length === 0 ? /* @__PURE__ */ jsx15("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__ */ jsxs11(Fragment4, { children: [
|
|
3001
3247
|
renderSection(`Fade out${fromLabel ? ` (from ${fromLabel})` : ""}`, fadeOut, "out"),
|
|
3002
3248
|
renderSection(`Fade in${toLabel ? ` (to ${toLabel})` : ""}`, fadeIn, "in")
|
|
3003
3249
|
] })),
|
|
3004
|
-
error && /* @__PURE__ */
|
|
3005
|
-
/* @__PURE__ */
|
|
3006
|
-
/* @__PURE__ */
|
|
3250
|
+
error && /* @__PURE__ */ jsx15("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
|
|
3251
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
3252
|
+
/* @__PURE__ */ jsx15(
|
|
3007
3253
|
"button",
|
|
3008
3254
|
{
|
|
3009
3255
|
ref: cancelRef,
|
|
@@ -3014,7 +3260,7 @@ function FadeModal({
|
|
|
3014
3260
|
children: "Cancel"
|
|
3015
3261
|
}
|
|
3016
3262
|
),
|
|
3017
|
-
/* @__PURE__ */
|
|
3263
|
+
/* @__PURE__ */ jsx15(
|
|
3018
3264
|
"button",
|
|
3019
3265
|
{
|
|
3020
3266
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -3031,8 +3277,8 @@ function FadeModal({
|
|
|
3031
3277
|
}
|
|
3032
3278
|
|
|
3033
3279
|
// src/components/ImportTrackModal.tsx
|
|
3034
|
-
import { useCallback as
|
|
3035
|
-
import { jsx as
|
|
3280
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useState as useState10 } from "react";
|
|
3281
|
+
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3036
3282
|
function ImportTrackModal({
|
|
3037
3283
|
host,
|
|
3038
3284
|
open,
|
|
@@ -3044,10 +3290,10 @@ function ImportTrackModal({
|
|
|
3044
3290
|
onPick,
|
|
3045
3291
|
onPortTrack
|
|
3046
3292
|
}) {
|
|
3047
|
-
const [load, setLoad] =
|
|
3048
|
-
const [selectedSceneId, setSelectedSceneId] =
|
|
3049
|
-
const [importingTrackId, setImportingTrackId] =
|
|
3050
|
-
const refresh =
|
|
3293
|
+
const [load, setLoad] = useState10({ status: "loading" });
|
|
3294
|
+
const [selectedSceneId, setSelectedSceneId] = useState10(null);
|
|
3295
|
+
const [importingTrackId, setImportingTrackId] = useState10(null);
|
|
3296
|
+
const refresh = useCallback6(async () => {
|
|
3051
3297
|
if (!host.listImportableTracks) {
|
|
3052
3298
|
setLoad({ status: "error", message: "This host does not support importing tracks." });
|
|
3053
3299
|
return;
|
|
@@ -3063,14 +3309,14 @@ function ImportTrackModal({
|
|
|
3063
3309
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load scenes." });
|
|
3064
3310
|
}
|
|
3065
3311
|
}, [host, mode, onPortTrack]);
|
|
3066
|
-
|
|
3312
|
+
useEffect8(() => {
|
|
3067
3313
|
if (open) {
|
|
3068
3314
|
setSelectedSceneId(null);
|
|
3069
3315
|
setImportingTrackId(null);
|
|
3070
3316
|
void refresh();
|
|
3071
3317
|
}
|
|
3072
3318
|
}, [open, refresh]);
|
|
3073
|
-
const handleImport =
|
|
3319
|
+
const handleImport = useCallback6(
|
|
3074
3320
|
async (track, sourceSceneId, sceneName, isSameScene) => {
|
|
3075
3321
|
if (isSameScene && onPortTrack) {
|
|
3076
3322
|
if (!track.importable) return;
|
|
@@ -3111,16 +3357,16 @@ function ImportTrackModal({
|
|
|
3111
3357
|
if (!open) return null;
|
|
3112
3358
|
const scenes = load.status === "ready" ? load.scenes : [];
|
|
3113
3359
|
const selectedScene = scenes.find((s) => s.sceneId === selectedSceneId) ?? null;
|
|
3114
|
-
return /* @__PURE__ */
|
|
3360
|
+
return /* @__PURE__ */ jsx16(Modal, { open, onClose, testIdPrefix, children: /* @__PURE__ */ jsxs12(
|
|
3115
3361
|
"div",
|
|
3116
3362
|
{
|
|
3117
3363
|
className: "w-[420px] max-h-[70vh] overflow-hidden flex flex-col rounded-md border border-sas-border bg-sas-panel shadow-xl",
|
|
3118
3364
|
onClick: (e) => e.stopPropagation(),
|
|
3119
3365
|
"data-testid": `${testIdPrefix}-modal`,
|
|
3120
3366
|
children: [
|
|
3121
|
-
/* @__PURE__ */
|
|
3122
|
-
/* @__PURE__ */
|
|
3123
|
-
selectedScene && /* @__PURE__ */
|
|
3367
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between px-3 py-2 border-b border-sas-border", children: [
|
|
3368
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
3369
|
+
selectedScene && /* @__PURE__ */ jsx16(
|
|
3124
3370
|
"button",
|
|
3125
3371
|
{
|
|
3126
3372
|
className: "text-sas-muted hover:text-sas-accent text-xs",
|
|
@@ -3129,9 +3375,9 @@ function ImportTrackModal({
|
|
|
3129
3375
|
children: "\u2190"
|
|
3130
3376
|
}
|
|
3131
3377
|
),
|
|
3132
|
-
/* @__PURE__ */
|
|
3378
|
+
/* @__PURE__ */ jsx16("span", { className: "text-sm font-medium text-sas-text", children: selectedScene ? selectedScene.sceneName : title })
|
|
3133
3379
|
] }),
|
|
3134
|
-
/* @__PURE__ */
|
|
3380
|
+
/* @__PURE__ */ jsx16(
|
|
3135
3381
|
"button",
|
|
3136
3382
|
{
|
|
3137
3383
|
className: "text-sas-muted hover:text-sas-accent text-sm",
|
|
@@ -3141,30 +3387,30 @@ function ImportTrackModal({
|
|
|
3141
3387
|
}
|
|
3142
3388
|
)
|
|
3143
3389
|
] }),
|
|
3144
|
-
/* @__PURE__ */
|
|
3145
|
-
load.status === "loading" && /* @__PURE__ */
|
|
3146
|
-
load.status === "error" && /* @__PURE__ */
|
|
3147
|
-
load.status === "ready" && scenes.length === 0 && /* @__PURE__ */
|
|
3148
|
-
load.status === "ready" && scenes.length > 0 && !selectedScene && /* @__PURE__ */
|
|
3390
|
+
/* @__PURE__ */ jsxs12("div", { className: "overflow-y-auto p-2 flex-1", children: [
|
|
3391
|
+
load.status === "loading" && /* @__PURE__ */ jsx16("div", { className: "py-8 text-center text-xs text-sas-muted", "data-testid": `${testIdPrefix}-loading`, children: "Loading scenes\u2026" }),
|
|
3392
|
+
load.status === "error" && /* @__PURE__ */ jsx16("div", { className: "py-8 text-center text-xs text-red-400", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
3393
|
+
load.status === "ready" && scenes.length === 0 && /* @__PURE__ */ jsx16("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." }),
|
|
3394
|
+
load.status === "ready" && scenes.length > 0 && !selectedScene && /* @__PURE__ */ jsx16("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-scene-list`, children: scenes.map((scene) => /* @__PURE__ */ jsx16("li", { children: /* @__PURE__ */ jsxs12(
|
|
3149
3395
|
"button",
|
|
3150
3396
|
{
|
|
3151
3397
|
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",
|
|
3152
3398
|
onClick: () => setSelectedSceneId(scene.sceneId),
|
|
3153
3399
|
"data-testid": `${testIdPrefix}-scene`,
|
|
3154
3400
|
children: [
|
|
3155
|
-
/* @__PURE__ */
|
|
3156
|
-
/* @__PURE__ */
|
|
3401
|
+
/* @__PURE__ */ jsx16("span", { className: "truncate", children: scene.sceneName }),
|
|
3402
|
+
/* @__PURE__ */ jsxs12("span", { className: "text-sas-muted", children: [
|
|
3157
3403
|
scene.tracks.length,
|
|
3158
3404
|
" \u2192"
|
|
3159
3405
|
] })
|
|
3160
3406
|
]
|
|
3161
3407
|
}
|
|
3162
3408
|
) }, scene.sceneId)) }),
|
|
3163
|
-
selectedScene && /* @__PURE__ */
|
|
3409
|
+
selectedScene && /* @__PURE__ */ jsx16("ul", { className: "flex flex-col gap-1", "data-testid": `${testIdPrefix}-track-list`, children: selectedScene.tracks.map((track) => {
|
|
3164
3410
|
const busy = importingTrackId === track.trackId;
|
|
3165
3411
|
const gated = mode === "track" && !track.importable;
|
|
3166
3412
|
const disabled = gated || busy;
|
|
3167
|
-
return /* @__PURE__ */
|
|
3413
|
+
return /* @__PURE__ */ jsx16("li", { children: /* @__PURE__ */ jsxs12(
|
|
3168
3414
|
"button",
|
|
3169
3415
|
{
|
|
3170
3416
|
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"}`,
|
|
@@ -3174,14 +3420,14 @@ function ImportTrackModal({
|
|
|
3174
3420
|
"data-testid": `${testIdPrefix}-track`,
|
|
3175
3421
|
"data-importable": mode === "sound" || track.importable ? "true" : "false",
|
|
3176
3422
|
children: [
|
|
3177
|
-
/* @__PURE__ */
|
|
3423
|
+
/* @__PURE__ */ jsxs12("span", { className: "truncate", children: [
|
|
3178
3424
|
track.name,
|
|
3179
|
-
track.role ? /* @__PURE__ */
|
|
3425
|
+
track.role ? /* @__PURE__ */ jsxs12("span", { className: "text-sas-muted", children: [
|
|
3180
3426
|
" \xB7 ",
|
|
3181
3427
|
track.role
|
|
3182
3428
|
] }) : null
|
|
3183
3429
|
] }),
|
|
3184
|
-
busy ? /* @__PURE__ */
|
|
3430
|
+
busy ? /* @__PURE__ */ jsx16("span", { className: "text-sas-muted", children: "\u2026" }) : gated ? /* @__PURE__ */ jsx16("span", { className: "text-sas-muted", children: "\u2298" }) : null
|
|
3185
3431
|
]
|
|
3186
3432
|
}
|
|
3187
3433
|
) }, track.dbId);
|
|
@@ -3193,8 +3439,8 @@ function ImportTrackModal({
|
|
|
3193
3439
|
}
|
|
3194
3440
|
|
|
3195
3441
|
// src/components/CrossfadeModal.tsx
|
|
3196
|
-
import { useCallback as
|
|
3197
|
-
import { Fragment as Fragment5, jsx as
|
|
3442
|
+
import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef9, useState as useState11 } from "react";
|
|
3443
|
+
import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3198
3444
|
function shortId2(dbId) {
|
|
3199
3445
|
return dbId.length > 8 ? dbId.slice(0, 8) : dbId;
|
|
3200
3446
|
}
|
|
@@ -3207,7 +3453,7 @@ function CandidateRow({
|
|
|
3207
3453
|
}) {
|
|
3208
3454
|
const primary = track.prompt?.trim() || track.name;
|
|
3209
3455
|
const meta = [track.role, shortId2(track.dbId)].filter(Boolean).join(" \xB7 ");
|
|
3210
|
-
return /* @__PURE__ */
|
|
3456
|
+
return /* @__PURE__ */ jsxs13(
|
|
3211
3457
|
"button",
|
|
3212
3458
|
{
|
|
3213
3459
|
type: "button",
|
|
@@ -3219,8 +3465,8 @@ function CandidateRow({
|
|
|
3219
3465
|
disabled,
|
|
3220
3466
|
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"}`,
|
|
3221
3467
|
children: [
|
|
3222
|
-
/* @__PURE__ */
|
|
3223
|
-
meta && /* @__PURE__ */
|
|
3468
|
+
/* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-text truncate", title: primary, children: primary }),
|
|
3469
|
+
meta && /* @__PURE__ */ jsx17("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", title: track.dbId, children: meta })
|
|
3224
3470
|
]
|
|
3225
3471
|
}
|
|
3226
3472
|
);
|
|
@@ -3237,15 +3483,15 @@ function CrossfadeModal({
|
|
|
3237
3483
|
onCreate,
|
|
3238
3484
|
testIdPrefix = "crossfade-modal"
|
|
3239
3485
|
}) {
|
|
3240
|
-
const [load, setLoad] =
|
|
3241
|
-
const [originDbId, setOriginDbId] =
|
|
3242
|
-
const [targetDbId, setTargetDbId] =
|
|
3243
|
-
const [isCreating, setIsCreating] =
|
|
3244
|
-
const [error, setError] =
|
|
3245
|
-
const [fromName, setFromName] =
|
|
3246
|
-
const [toName, setToName] =
|
|
3247
|
-
const cancelRef =
|
|
3248
|
-
const refresh =
|
|
3486
|
+
const [load, setLoad] = useState11({ status: "loading" });
|
|
3487
|
+
const [originDbId, setOriginDbId] = useState11("");
|
|
3488
|
+
const [targetDbId, setTargetDbId] = useState11("");
|
|
3489
|
+
const [isCreating, setIsCreating] = useState11(false);
|
|
3490
|
+
const [error, setError] = useState11(null);
|
|
3491
|
+
const [fromName, setFromName] = useState11(null);
|
|
3492
|
+
const [toName, setToName] = useState11(null);
|
|
3493
|
+
const cancelRef = useRef9(null);
|
|
3494
|
+
const refresh = useCallback7(async () => {
|
|
3249
3495
|
if (!host.listSceneFamilyTracks) {
|
|
3250
3496
|
setLoad({ status: "error", message: "This host does not support crossfade tracks." });
|
|
3251
3497
|
return;
|
|
@@ -3265,7 +3511,7 @@ function CrossfadeModal({
|
|
|
3265
3511
|
setLoad({ status: "error", message: err instanceof Error ? err.message : "Failed to load tracks." });
|
|
3266
3512
|
}
|
|
3267
3513
|
}, [host, fromSceneId, toSceneId]);
|
|
3268
|
-
|
|
3514
|
+
useEffect9(() => {
|
|
3269
3515
|
if (open) {
|
|
3270
3516
|
setError(null);
|
|
3271
3517
|
setIsCreating(false);
|
|
@@ -3274,21 +3520,21 @@ function CrossfadeModal({
|
|
|
3274
3520
|
void refresh();
|
|
3275
3521
|
}
|
|
3276
3522
|
}, [open, refresh]);
|
|
3277
|
-
const excludeSet =
|
|
3278
|
-
const originCandidates =
|
|
3523
|
+
const excludeSet = useMemo5(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
3524
|
+
const originCandidates = useMemo5(
|
|
3279
3525
|
() => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3280
3526
|
[load, excludeSet]
|
|
3281
3527
|
);
|
|
3282
|
-
const targetCandidates =
|
|
3528
|
+
const targetCandidates = useMemo5(
|
|
3283
3529
|
() => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3284
3530
|
[load, excludeSet]
|
|
3285
3531
|
);
|
|
3286
|
-
|
|
3532
|
+
useEffect9(() => {
|
|
3287
3533
|
if (!originCandidates.some((t) => t.dbId === originDbId)) {
|
|
3288
3534
|
setOriginDbId(originCandidates[0]?.dbId ?? "");
|
|
3289
3535
|
}
|
|
3290
3536
|
}, [originCandidates, originDbId]);
|
|
3291
|
-
|
|
3537
|
+
useEffect9(() => {
|
|
3292
3538
|
if (!targetCandidates.some((t) => t.dbId === targetDbId)) {
|
|
3293
3539
|
setTargetDbId(targetCandidates[0]?.dbId ?? "");
|
|
3294
3540
|
}
|
|
@@ -3296,10 +3542,10 @@ function CrossfadeModal({
|
|
|
3296
3542
|
const originTrack = originCandidates.find((t) => t.dbId === originDbId) ?? null;
|
|
3297
3543
|
const targetTrack = targetCandidates.find((t) => t.dbId === targetDbId) ?? null;
|
|
3298
3544
|
const canCreate = !isCreating && !!originTrack && !!targetTrack;
|
|
3299
|
-
const handleClose =
|
|
3545
|
+
const handleClose = useCallback7(() => {
|
|
3300
3546
|
if (!isCreating) onClose();
|
|
3301
3547
|
}, [isCreating, onClose]);
|
|
3302
|
-
const handleCreate =
|
|
3548
|
+
const handleCreate = useCallback7(async () => {
|
|
3303
3549
|
if (!originTrack || !targetTrack) return;
|
|
3304
3550
|
setIsCreating(true);
|
|
3305
3551
|
setError(null);
|
|
@@ -3317,26 +3563,26 @@ function CrossfadeModal({
|
|
|
3317
3563
|
const fromLabel = fromName ?? fromSceneName ?? null;
|
|
3318
3564
|
const toLabel = toName ?? toSceneName ?? null;
|
|
3319
3565
|
if (!open) return null;
|
|
3320
|
-
return /* @__PURE__ */
|
|
3566
|
+
return /* @__PURE__ */ jsx17(Modal, { open, onClose: handleClose, testIdPrefix, initialFocusRef: cancelRef, children: /* @__PURE__ */ jsxs13(
|
|
3321
3567
|
"div",
|
|
3322
3568
|
{
|
|
3323
3569
|
className: "bg-sas-panel border border-sas-border rounded-md shadow-xl w-[420px] max-w-[92vw] p-4 space-y-3",
|
|
3324
3570
|
onClick: (e) => e.stopPropagation(),
|
|
3325
3571
|
"data-testid": `${testIdPrefix}-box`,
|
|
3326
3572
|
children: [
|
|
3327
|
-
/* @__PURE__ */
|
|
3328
|
-
/* @__PURE__ */
|
|
3573
|
+
/* @__PURE__ */ jsx17("h3", { className: "text-sm font-bold text-sas-text", children: "Add crossfade" }),
|
|
3574
|
+
/* @__PURE__ */ jsxs13("p", { className: "text-[11px] text-sas-muted leading-relaxed", children: [
|
|
3329
3575
|
"Bridge a track from",
|
|
3330
3576
|
" ",
|
|
3331
|
-
/* @__PURE__ */
|
|
3577
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sas-text", children: fromLabel ?? "the origin scene" }),
|
|
3332
3578
|
" into one from",
|
|
3333
3579
|
" ",
|
|
3334
|
-
/* @__PURE__ */
|
|
3580
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sas-text", children: toLabel ?? "the target scene" }),
|
|
3335
3581
|
". Both layers share one generated part; each keeps its own preset."
|
|
3336
3582
|
] }),
|
|
3337
|
-
load.status === "loading" && /* @__PURE__ */
|
|
3338
|
-
load.status === "error" && /* @__PURE__ */
|
|
3339
|
-
load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */
|
|
3583
|
+
load.status === "loading" && /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-muted py-4 text-center", children: "Loading tracks\u2026" }),
|
|
3584
|
+
load.status === "error" && /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-danger py-4 text-center", children: load.message }),
|
|
3585
|
+
load.status === "ready" && (originCandidates.length === 0 ? /* @__PURE__ */ jsxs13(
|
|
3340
3586
|
"div",
|
|
3341
3587
|
{
|
|
3342
3588
|
className: "text-xs text-sas-muted py-4 text-center",
|
|
@@ -3347,20 +3593,20 @@ function CrossfadeModal({
|
|
|
3347
3593
|
". Add one (or free one from another crossfade) first."
|
|
3348
3594
|
]
|
|
3349
3595
|
}
|
|
3350
|
-
) : /* @__PURE__ */
|
|
3351
|
-
/* @__PURE__ */
|
|
3352
|
-
/* @__PURE__ */
|
|
3596
|
+
) : /* @__PURE__ */ jsxs13(Fragment5, { children: [
|
|
3597
|
+
/* @__PURE__ */ jsxs13("div", { className: "block", children: [
|
|
3598
|
+
/* @__PURE__ */ jsxs13("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
3353
3599
|
"Origin ",
|
|
3354
3600
|
fromLabel ? `(${fromLabel})` : "(top)"
|
|
3355
3601
|
] }),
|
|
3356
|
-
/* @__PURE__ */
|
|
3602
|
+
/* @__PURE__ */ jsx17(
|
|
3357
3603
|
"div",
|
|
3358
3604
|
{
|
|
3359
3605
|
role: "radiogroup",
|
|
3360
3606
|
"aria-label": "Origin track",
|
|
3361
3607
|
"data-testid": `${testIdPrefix}-origin-list`,
|
|
3362
3608
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3363
|
-
children: originCandidates.map((t) => /* @__PURE__ */
|
|
3609
|
+
children: originCandidates.map((t) => /* @__PURE__ */ jsx17(
|
|
3364
3610
|
CandidateRow,
|
|
3365
3611
|
{
|
|
3366
3612
|
track: t,
|
|
@@ -3374,23 +3620,23 @@ function CrossfadeModal({
|
|
|
3374
3620
|
}
|
|
3375
3621
|
)
|
|
3376
3622
|
] }),
|
|
3377
|
-
/* @__PURE__ */
|
|
3378
|
-
/* @__PURE__ */
|
|
3623
|
+
/* @__PURE__ */ jsxs13("div", { className: "block", children: [
|
|
3624
|
+
/* @__PURE__ */ jsxs13("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: [
|
|
3379
3625
|
"Target ",
|
|
3380
3626
|
toLabel ? `(${toLabel})` : "(bottom)"
|
|
3381
3627
|
] }),
|
|
3382
|
-
targetCandidates.length === 0 ? /* @__PURE__ */
|
|
3628
|
+
targetCandidates.length === 0 ? /* @__PURE__ */ jsxs13("div", { className: "text-xs text-sas-danger mt-0.5", "data-testid": `${testIdPrefix}-empty-target`, children: [
|
|
3383
3629
|
"No available tracks in ",
|
|
3384
3630
|
toLabel ?? "the target scene",
|
|
3385
3631
|
" to crossfade into."
|
|
3386
|
-
] }) : /* @__PURE__ */
|
|
3632
|
+
] }) : /* @__PURE__ */ jsx17(
|
|
3387
3633
|
"div",
|
|
3388
3634
|
{
|
|
3389
3635
|
role: "radiogroup",
|
|
3390
3636
|
"aria-label": "Target track",
|
|
3391
3637
|
"data-testid": `${testIdPrefix}-target-list`,
|
|
3392
3638
|
className: "mt-1 space-y-1 max-h-40 overflow-y-auto pr-0.5",
|
|
3393
|
-
children: targetCandidates.map((t) => /* @__PURE__ */
|
|
3639
|
+
children: targetCandidates.map((t) => /* @__PURE__ */ jsx17(
|
|
3394
3640
|
CandidateRow,
|
|
3395
3641
|
{
|
|
3396
3642
|
track: t,
|
|
@@ -3405,9 +3651,9 @@ function CrossfadeModal({
|
|
|
3405
3651
|
)
|
|
3406
3652
|
] })
|
|
3407
3653
|
] })),
|
|
3408
|
-
error && /* @__PURE__ */
|
|
3409
|
-
/* @__PURE__ */
|
|
3410
|
-
/* @__PURE__ */
|
|
3654
|
+
error && /* @__PURE__ */ jsx17("div", { className: "text-xs text-sas-danger", "data-testid": `${testIdPrefix}-error`, children: error }),
|
|
3655
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
3656
|
+
/* @__PURE__ */ jsx17(
|
|
3411
3657
|
"button",
|
|
3412
3658
|
{
|
|
3413
3659
|
ref: cancelRef,
|
|
@@ -3418,7 +3664,7 @@ function CrossfadeModal({
|
|
|
3418
3664
|
children: "Cancel"
|
|
3419
3665
|
}
|
|
3420
3666
|
),
|
|
3421
|
-
/* @__PURE__ */
|
|
3667
|
+
/* @__PURE__ */ jsx17(
|
|
3422
3668
|
"button",
|
|
3423
3669
|
{
|
|
3424
3670
|
"data-testid": `${testIdPrefix}-confirm`,
|
|
@@ -3435,10 +3681,10 @@ function CrossfadeModal({
|
|
|
3435
3681
|
}
|
|
3436
3682
|
|
|
3437
3683
|
// src/components/TransitionDesigner.tsx
|
|
3438
|
-
import { useCallback as
|
|
3684
|
+
import { useCallback as useCallback9, useEffect as useEffect10, useMemo as useMemo6, useRef as useRef11, useState as useState13 } from "react";
|
|
3439
3685
|
|
|
3440
3686
|
// src/hooks/useTrackReorder.ts
|
|
3441
|
-
import { useCallback as
|
|
3687
|
+
import { useCallback as useCallback8, useRef as useRef10, useState as useState12 } from "react";
|
|
3442
3688
|
function moveItem(arr, from, to) {
|
|
3443
3689
|
const next = arr.slice();
|
|
3444
3690
|
if (from === to || from < 0 || to < 0 || from >= next.length || to >= next.length) {
|
|
@@ -3455,12 +3701,12 @@ function useTrackReorder({
|
|
|
3455
3701
|
getId,
|
|
3456
3702
|
onError
|
|
3457
3703
|
}) {
|
|
3458
|
-
const [draggingIndex, setDraggingIndex] =
|
|
3459
|
-
const [dragOverIndex, setDragOverIndex] =
|
|
3460
|
-
const fromRef =
|
|
3461
|
-
const itemsRef =
|
|
3704
|
+
const [draggingIndex, setDraggingIndex] = useState12(null);
|
|
3705
|
+
const [dragOverIndex, setDragOverIndex] = useState12(null);
|
|
3706
|
+
const fromRef = useRef10(null);
|
|
3707
|
+
const itemsRef = useRef10(items);
|
|
3462
3708
|
itemsRef.current = items;
|
|
3463
|
-
const dragPropsFor =
|
|
3709
|
+
const dragPropsFor = useCallback8(
|
|
3464
3710
|
(index) => ({
|
|
3465
3711
|
handleProps: {
|
|
3466
3712
|
draggable: true,
|
|
@@ -3642,7 +3888,7 @@ function dbIdsFromKeys(keys) {
|
|
|
3642
3888
|
}
|
|
3643
3889
|
|
|
3644
3890
|
// src/components/TransitionDesigner.tsx
|
|
3645
|
-
import { jsx as
|
|
3891
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3646
3892
|
var CROSSFADE_ESTIMATE_MS = 15e3;
|
|
3647
3893
|
var FADE_ESTIMATE_MS = 11e3;
|
|
3648
3894
|
var CREATE_ALL_CONCURRENCY = 5;
|
|
@@ -3666,44 +3912,44 @@ function TransitionDesigner({
|
|
|
3666
3912
|
familyLabel,
|
|
3667
3913
|
testIdPrefix = "transition-designer"
|
|
3668
3914
|
}) {
|
|
3669
|
-
const [load, setLoad] =
|
|
3670
|
-
const [fromName, setFromName] =
|
|
3671
|
-
const [toName, setToName] =
|
|
3672
|
-
const [originSlots, setOriginSlots] =
|
|
3673
|
-
const [targetSlots, setTargetSlots] =
|
|
3674
|
-
const [creatingKeys, setCreatingKeys] =
|
|
3675
|
-
const [rowErrors, setRowErrors] =
|
|
3676
|
-
const [rowEffects, setRowEffects] =
|
|
3677
|
-
const rowEffectsRef =
|
|
3915
|
+
const [load, setLoad] = useState13({ status: "loading" });
|
|
3916
|
+
const [fromName, setFromName] = useState13(null);
|
|
3917
|
+
const [toName, setToName] = useState13(null);
|
|
3918
|
+
const [originSlots, setOriginSlots] = useState13([]);
|
|
3919
|
+
const [targetSlots, setTargetSlots] = useState13([]);
|
|
3920
|
+
const [creatingKeys, setCreatingKeys] = useState13(() => /* @__PURE__ */ new Set());
|
|
3921
|
+
const [rowErrors, setRowErrors] = useState13({});
|
|
3922
|
+
const [rowEffects, setRowEffects] = useState13({});
|
|
3923
|
+
const rowEffectsRef = useRef11(rowEffects);
|
|
3678
3924
|
rowEffectsRef.current = rowEffects;
|
|
3679
3925
|
const audioEffectsEnabled = !!onCreateAudioTransition;
|
|
3680
|
-
const excludeRef =
|
|
3926
|
+
const excludeRef = useRef11(excludeSourceDbIds);
|
|
3681
3927
|
excludeRef.current = excludeSourceDbIds;
|
|
3682
|
-
const originSlotsRef =
|
|
3928
|
+
const originSlotsRef = useRef11(originSlots);
|
|
3683
3929
|
originSlotsRef.current = originSlots;
|
|
3684
|
-
const targetSlotsRef =
|
|
3930
|
+
const targetSlotsRef = useRef11(targetSlots);
|
|
3685
3931
|
targetSlotsRef.current = targetSlots;
|
|
3686
|
-
const creatingKeysRef =
|
|
3932
|
+
const creatingKeysRef = useRef11(creatingKeys);
|
|
3687
3933
|
creatingKeysRef.current = creatingKeys;
|
|
3688
|
-
const dragRef =
|
|
3689
|
-
const [dragging, setDragging] =
|
|
3690
|
-
const [dragOver, setDragOver] =
|
|
3691
|
-
const excludeSet =
|
|
3692
|
-
const originPool =
|
|
3934
|
+
const dragRef = useRef11(null);
|
|
3935
|
+
const [dragging, setDragging] = useState13(null);
|
|
3936
|
+
const [dragOver, setDragOver] = useState13(null);
|
|
3937
|
+
const excludeSet = useMemo6(() => new Set(excludeSourceDbIds ?? []), [excludeSourceDbIds]);
|
|
3938
|
+
const originPool = useMemo6(
|
|
3693
3939
|
() => load.status === "ready" ? load.origin.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3694
3940
|
[load, excludeSet]
|
|
3695
3941
|
);
|
|
3696
|
-
const targetPool =
|
|
3942
|
+
const targetPool = useMemo6(
|
|
3697
3943
|
() => load.status === "ready" ? load.target.filter((t) => !excludeSet.has(t.dbId)) : [],
|
|
3698
3944
|
[load, excludeSet]
|
|
3699
3945
|
);
|
|
3700
|
-
const originById =
|
|
3701
|
-
const targetById =
|
|
3702
|
-
const originByIdRef =
|
|
3946
|
+
const originById = useMemo6(() => new Map(originPool.map((t) => [t.dbId, t])), [originPool]);
|
|
3947
|
+
const targetById = useMemo6(() => new Map(targetPool.map((t) => [t.dbId, t])), [targetPool]);
|
|
3948
|
+
const originByIdRef = useRef11(originById);
|
|
3703
3949
|
originByIdRef.current = originById;
|
|
3704
|
-
const targetByIdRef =
|
|
3950
|
+
const targetByIdRef = useRef11(targetById);
|
|
3705
3951
|
targetByIdRef.current = targetById;
|
|
3706
|
-
const refresh =
|
|
3952
|
+
const refresh = useCallback9(async () => {
|
|
3707
3953
|
if (!host.listSceneFamilyTracks) {
|
|
3708
3954
|
setLoad({ status: "error", message: "This host does not support transition tracks." });
|
|
3709
3955
|
return;
|
|
@@ -3738,10 +3984,10 @@ function TransitionDesigner({
|
|
|
3738
3984
|
});
|
|
3739
3985
|
}
|
|
3740
3986
|
}, [host, fromSceneId, toSceneId, transitionSceneId]);
|
|
3741
|
-
|
|
3987
|
+
useEffect10(() => {
|
|
3742
3988
|
void refresh();
|
|
3743
3989
|
}, [refresh]);
|
|
3744
|
-
|
|
3990
|
+
useEffect10(() => {
|
|
3745
3991
|
if (load.status !== "ready") return;
|
|
3746
3992
|
const [po, pt] = padPair(
|
|
3747
3993
|
reconcileSlots(originSlotsRef.current, originPool.map((t) => t.dbId)),
|
|
@@ -3750,7 +3996,7 @@ function TransitionDesigner({
|
|
|
3750
3996
|
if (!slotsEqual(po, originSlotsRef.current)) setOriginSlots(po);
|
|
3751
3997
|
if (!slotsEqual(pt, targetSlotsRef.current)) setTargetSlots(pt);
|
|
3752
3998
|
}, [originPool, targetPool, load.status]);
|
|
3753
|
-
const mutate =
|
|
3999
|
+
const mutate = useCallback9(
|
|
3754
4000
|
(nextOrigin, nextTarget) => {
|
|
3755
4001
|
const norm = normalizeSlots(nextOrigin, nextTarget);
|
|
3756
4002
|
const [po, pt] = padPair(norm.originOrder, norm.targetOrder);
|
|
@@ -3763,7 +4009,7 @@ function TransitionDesigner({
|
|
|
3763
4009
|
},
|
|
3764
4010
|
[host, transitionSceneId]
|
|
3765
4011
|
);
|
|
3766
|
-
const setRowEffect =
|
|
4012
|
+
const setRowEffect = useCallback9(
|
|
3767
4013
|
(sourceDbId, effect) => {
|
|
3768
4014
|
setRowEffects((prev) => {
|
|
3769
4015
|
const next = { ...prev, [sourceDbId]: effect };
|
|
@@ -3777,7 +4023,7 @@ function TransitionDesigner({
|
|
|
3777
4023
|
},
|
|
3778
4024
|
[host, transitionSceneId]
|
|
3779
4025
|
);
|
|
3780
|
-
const insertGapAbove =
|
|
4026
|
+
const insertGapAbove = useCallback9(
|
|
3781
4027
|
(col, index) => {
|
|
3782
4028
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
3783
4029
|
const next = [...slots.slice(0, index), null, ...slots.slice(index)];
|
|
@@ -3786,7 +4032,7 @@ function TransitionDesigner({
|
|
|
3786
4032
|
},
|
|
3787
4033
|
[originSlots, targetSlots, mutate]
|
|
3788
4034
|
);
|
|
3789
|
-
const removeGap =
|
|
4035
|
+
const removeGap = useCallback9(
|
|
3790
4036
|
(col, index) => {
|
|
3791
4037
|
const slots = col === "origin" ? originSlots : targetSlots;
|
|
3792
4038
|
const next = slots.filter((_, i) => i !== index);
|
|
@@ -3795,7 +4041,7 @@ function TransitionDesigner({
|
|
|
3795
4041
|
},
|
|
3796
4042
|
[originSlots, targetSlots, mutate]
|
|
3797
4043
|
);
|
|
3798
|
-
const handleDrop =
|
|
4044
|
+
const handleDrop = useCallback9(
|
|
3799
4045
|
(col, to) => {
|
|
3800
4046
|
const from = dragRef.current;
|
|
3801
4047
|
dragRef.current = null;
|
|
@@ -3807,16 +4053,16 @@ function TransitionDesigner({
|
|
|
3807
4053
|
},
|
|
3808
4054
|
[originSlots, targetSlots, mutate]
|
|
3809
4055
|
);
|
|
3810
|
-
const rows =
|
|
3811
|
-
const creatingDbIds =
|
|
3812
|
-
const eligibleCount =
|
|
4056
|
+
const rows = useMemo6(() => buildRowSlots(originSlots, targetSlots), [originSlots, targetSlots]);
|
|
4057
|
+
const creatingDbIds = useMemo6(() => dbIdsFromKeys(creatingKeys), [creatingKeys]);
|
|
4058
|
+
const eligibleCount = useMemo6(
|
|
3813
4059
|
() => rows.filter((r) => {
|
|
3814
4060
|
const k = rowKey(r);
|
|
3815
4061
|
return k !== null && !creatingKeys.has(k);
|
|
3816
4062
|
}).length,
|
|
3817
4063
|
[rows, creatingKeys]
|
|
3818
4064
|
);
|
|
3819
|
-
const createRow =
|
|
4065
|
+
const createRow = useCallback9(
|
|
3820
4066
|
async (row) => {
|
|
3821
4067
|
const key = rowKey(row);
|
|
3822
4068
|
if (!key || !row.type || creatingKeysRef.current.has(key)) return;
|
|
@@ -3870,7 +4116,7 @@ function TransitionDesigner({
|
|
|
3870
4116
|
},
|
|
3871
4117
|
[onCreateCrossfade, onCreateFade, onCreateAudioTransition]
|
|
3872
4118
|
);
|
|
3873
|
-
const createAll =
|
|
4119
|
+
const createAll = useCallback9(async () => {
|
|
3874
4120
|
const eligible = buildRowSlots(originSlotsRef.current, targetSlotsRef.current).filter((r) => {
|
|
3875
4121
|
const k = rowKey(r);
|
|
3876
4122
|
return k !== null && !creatingKeysRef.current.has(k);
|
|
@@ -3938,15 +4184,15 @@ function TransitionDesigner({
|
|
|
3938
4184
|
const base = "group relative rounded-sm border px-2 py-1.5 text-left transition-colors select-none";
|
|
3939
4185
|
const tone = isDragTarget ? "border-sas-accent bg-sas-accent/10" : "border-sas-border bg-sas-panel";
|
|
3940
4186
|
if (slotId === null) {
|
|
3941
|
-
return /* @__PURE__ */
|
|
4187
|
+
return /* @__PURE__ */ jsxs14(
|
|
3942
4188
|
"div",
|
|
3943
4189
|
{
|
|
3944
4190
|
...cellDragProps(col, index, false),
|
|
3945
4191
|
"data-testid": `${testIdPrefix}-${col}-gap-${index}`,
|
|
3946
4192
|
className: `${base} ${tone} border-dashed flex items-center justify-between ${isDragging ? "opacity-40" : "opacity-70"}`,
|
|
3947
4193
|
children: [
|
|
3948
|
-
/* @__PURE__ */
|
|
3949
|
-
/* @__PURE__ */
|
|
4194
|
+
/* @__PURE__ */ jsx18("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted", children: "\u2014 gap \u2014" }),
|
|
4195
|
+
/* @__PURE__ */ jsx18(
|
|
3950
4196
|
"button",
|
|
3951
4197
|
{
|
|
3952
4198
|
type: "button",
|
|
@@ -3963,7 +4209,7 @@ function TransitionDesigner({
|
|
|
3963
4209
|
}
|
|
3964
4210
|
const primary = track ? track.prompt?.trim() || track.name : slotId;
|
|
3965
4211
|
const meta = track ? [track.role, shortId3(track.dbId)].filter(Boolean).join(" \xB7 ") : "missing";
|
|
3966
|
-
return /* @__PURE__ */
|
|
4212
|
+
return /* @__PURE__ */ jsx18(
|
|
3967
4213
|
"div",
|
|
3968
4214
|
{
|
|
3969
4215
|
...cellDragProps(col, index, locked),
|
|
@@ -3971,13 +4217,13 @@ function TransitionDesigner({
|
|
|
3971
4217
|
"data-value": slotId,
|
|
3972
4218
|
className: `${base} ${tone} ${isDragging ? "opacity-40" : ""} ${locked ? "opacity-60" : "cursor-grab active:cursor-grabbing"}`,
|
|
3973
4219
|
title: track ? track.dbId : "Track no longer available",
|
|
3974
|
-
children: /* @__PURE__ */
|
|
3975
|
-
/* @__PURE__ */
|
|
3976
|
-
/* @__PURE__ */
|
|
3977
|
-
/* @__PURE__ */
|
|
3978
|
-
meta && /* @__PURE__ */
|
|
4220
|
+
children: /* @__PURE__ */ jsxs14("div", { className: "flex items-start gap-1", children: [
|
|
4221
|
+
/* @__PURE__ */ jsx18("span", { className: "text-sas-muted/60 text-xs leading-tight pt-0.5", "aria-hidden": true, children: "\u283F" }),
|
|
4222
|
+
/* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1", children: [
|
|
4223
|
+
/* @__PURE__ */ jsx18("div", { className: "text-xs text-sas-text truncate", children: primary }),
|
|
4224
|
+
meta && /* @__PURE__ */ jsx18("div", { className: "text-[10px] text-sas-muted truncate mt-0.5", children: meta })
|
|
3979
4225
|
] }),
|
|
3980
|
-
/* @__PURE__ */
|
|
4226
|
+
/* @__PURE__ */ jsx18(
|
|
3981
4227
|
"button",
|
|
3982
4228
|
{
|
|
3983
4229
|
type: "button",
|
|
@@ -3993,22 +4239,22 @@ function TransitionDesigner({
|
|
|
3993
4239
|
}
|
|
3994
4240
|
);
|
|
3995
4241
|
};
|
|
3996
|
-
return /* @__PURE__ */
|
|
3997
|
-
/* @__PURE__ */
|
|
3998
|
-
/* @__PURE__ */
|
|
3999
|
-
/* @__PURE__ */
|
|
4242
|
+
return /* @__PURE__ */ jsxs14("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-box`, children: [
|
|
4243
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between gap-3 pb-1 border-b border-sas-border", children: [
|
|
4244
|
+
/* @__PURE__ */ jsxs14("p", { className: "text-[11px] text-sas-muted leading-snug min-w-0", children: [
|
|
4245
|
+
/* @__PURE__ */ jsx18("span", { className: "text-sas-text", children: fromLabel }),
|
|
4000
4246
|
" \u2192",
|
|
4001
4247
|
" ",
|
|
4002
|
-
/* @__PURE__ */
|
|
4248
|
+
/* @__PURE__ */ jsx18("span", { className: "text-sas-text", children: toLabel }),
|
|
4003
4249
|
familyLabel ? ` \xB7 ${familyLabel}` : "",
|
|
4004
4250
|
" \xB7 line up a track on each side to crossfade; leave one blank (or insert a gap) to fade."
|
|
4005
4251
|
] }),
|
|
4006
|
-
/* @__PURE__ */
|
|
4007
|
-
creatingKeys.size > 0 && /* @__PURE__ */
|
|
4252
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
4253
|
+
creatingKeys.size > 0 && /* @__PURE__ */ jsxs14("span", { className: "text-[10px] text-sas-accent whitespace-nowrap", "data-testid": `${testIdPrefix}-creating-count`, children: [
|
|
4008
4254
|
creatingKeys.size,
|
|
4009
4255
|
" creating\u2026"
|
|
4010
4256
|
] }),
|
|
4011
|
-
/* @__PURE__ */
|
|
4257
|
+
/* @__PURE__ */ jsxs14(
|
|
4012
4258
|
"button",
|
|
4013
4259
|
{
|
|
4014
4260
|
type: "button",
|
|
@@ -4025,49 +4271,49 @@ function TransitionDesigner({
|
|
|
4025
4271
|
)
|
|
4026
4272
|
] })
|
|
4027
4273
|
] }),
|
|
4028
|
-
/* @__PURE__ */
|
|
4029
|
-
/* @__PURE__ */
|
|
4274
|
+
/* @__PURE__ */ jsxs14("div", { className: "grid grid-cols-[1fr_auto_1fr] gap-2", children: [
|
|
4275
|
+
/* @__PURE__ */ jsxs14("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate", children: [
|
|
4030
4276
|
"Origin (",
|
|
4031
4277
|
fromLabel,
|
|
4032
4278
|
")"
|
|
4033
4279
|
] }),
|
|
4034
|
-
/* @__PURE__ */
|
|
4035
|
-
/* @__PURE__ */
|
|
4280
|
+
/* @__PURE__ */ jsx18("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted text-center px-2", children: "Transition" }),
|
|
4281
|
+
/* @__PURE__ */ jsxs14("span", { className: "text-[10px] uppercase tracking-wide text-sas-muted truncate text-right", children: [
|
|
4036
4282
|
"Target (",
|
|
4037
4283
|
toLabel,
|
|
4038
4284
|
")"
|
|
4039
4285
|
] })
|
|
4040
4286
|
] }),
|
|
4041
|
-
load.status === "loading" && /* @__PURE__ */
|
|
4042
|
-
load.status === "error" && /* @__PURE__ */
|
|
4043
|
-
load.status === "ready" && (rows.length === 0 ? /* @__PURE__ */
|
|
4287
|
+
load.status === "loading" && /* @__PURE__ */ jsx18("div", { className: "text-xs text-sas-muted py-6 text-center", children: "Loading tracks\u2026" }),
|
|
4288
|
+
load.status === "error" && /* @__PURE__ */ jsx18("div", { className: "text-xs text-sas-danger py-6 text-center", "data-testid": `${testIdPrefix}-error`, children: load.message }),
|
|
4289
|
+
load.status === "ready" && (rows.length === 0 ? /* @__PURE__ */ jsxs14("div", { className: "text-xs text-sas-muted py-6 text-center", "data-testid": `${testIdPrefix}-empty`, children: [
|
|
4044
4290
|
"No tracks to arrange in this panel for either scene. Add tracks to ",
|
|
4045
4291
|
fromLabel,
|
|
4046
4292
|
" or ",
|
|
4047
4293
|
toLabel,
|
|
4048
4294
|
" ",
|
|
4049
4295
|
"first (or free one by deleting an existing crossfade/fade)."
|
|
4050
|
-
] }) : /* @__PURE__ */
|
|
4296
|
+
] }) : /* @__PURE__ */ jsx18("div", { className: "space-y-2", children: rows.map((row, i) => {
|
|
4051
4297
|
const key = rowKey(row);
|
|
4052
4298
|
const isCreatingThis = key !== null && creatingKeys.has(key);
|
|
4053
4299
|
const errMsg = key !== null ? rowErrors[key] : void 0;
|
|
4054
|
-
return /* @__PURE__ */
|
|
4300
|
+
return /* @__PURE__ */ jsxs14(
|
|
4055
4301
|
"div",
|
|
4056
4302
|
{
|
|
4057
4303
|
"data-testid": `${testIdPrefix}-row-${i}`,
|
|
4058
4304
|
className: "grid grid-cols-[1fr_auto_1fr] gap-2 items-center",
|
|
4059
4305
|
children: [
|
|
4060
4306
|
renderCell("origin", i, row.originId),
|
|
4061
|
-
/* @__PURE__ */
|
|
4062
|
-
!row.type ? /* @__PURE__ */
|
|
4307
|
+
/* @__PURE__ */ jsxs14("div", { className: "w-[160px] flex flex-col items-center gap-1", children: [
|
|
4308
|
+
!row.type ? /* @__PURE__ */ jsx18("span", { className: "text-[10px] text-sas-muted/50", children: "\u2014" }) : row.type === "crossfade" ? /* @__PURE__ */ jsx18(
|
|
4063
4309
|
"span",
|
|
4064
4310
|
{
|
|
4065
4311
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
4066
4312
|
className: "text-[10px] font-medium px-1.5 py-0.5 rounded-sm border border-sas-accent/50 text-sas-accent",
|
|
4067
4313
|
children: TYPE_LABEL[row.type]
|
|
4068
4314
|
}
|
|
4069
|
-
) : audioEffectsEnabled ? /* @__PURE__ */
|
|
4070
|
-
/* @__PURE__ */
|
|
4315
|
+
) : audioEffectsEnabled ? /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1", "data-testid": `${testIdPrefix}-type-${i}`, children: [
|
|
4316
|
+
/* @__PURE__ */ jsx18(
|
|
4071
4317
|
"select",
|
|
4072
4318
|
{
|
|
4073
4319
|
"data-testid": `${testIdPrefix}-effect-${i}`,
|
|
@@ -4077,11 +4323,11 @@ function TransitionDesigner({
|
|
|
4077
4323
|
if (id) setRowEffect(id, e.target.value);
|
|
4078
4324
|
},
|
|
4079
4325
|
className: "text-[10px] bg-sas-panel border border-sas-border rounded-sm px-1 py-0.5 text-sas-text",
|
|
4080
|
-
children: AUDIO_EFFECTS.map((eff) => /* @__PURE__ */
|
|
4326
|
+
children: AUDIO_EFFECTS.map((eff) => /* @__PURE__ */ jsx18("option", { value: eff, children: AUDIO_EFFECT_LABEL[eff] }, eff))
|
|
4081
4327
|
}
|
|
4082
4328
|
),
|
|
4083
|
-
/* @__PURE__ */
|
|
4084
|
-
] }) : /* @__PURE__ */
|
|
4329
|
+
/* @__PURE__ */ jsx18("span", { className: "text-[9px] text-sas-muted", children: row.type === "fade-out" ? "out" : "in" })
|
|
4330
|
+
] }) : /* @__PURE__ */ jsx18(
|
|
4085
4331
|
"span",
|
|
4086
4332
|
{
|
|
4087
4333
|
"data-testid": `${testIdPrefix}-type-${i}`,
|
|
@@ -4089,7 +4335,7 @@ function TransitionDesigner({
|
|
|
4089
4335
|
children: TYPE_LABEL[row.type]
|
|
4090
4336
|
}
|
|
4091
4337
|
),
|
|
4092
|
-
isCreatingThis ? /* @__PURE__ */
|
|
4338
|
+
isCreatingThis ? /* @__PURE__ */ jsx18("div", { className: "w-full", children: /* @__PURE__ */ jsx18(
|
|
4093
4339
|
SorceryProgressBar,
|
|
4094
4340
|
{
|
|
4095
4341
|
isLoading: true,
|
|
@@ -4097,7 +4343,7 @@ function TransitionDesigner({
|
|
|
4097
4343
|
statusText: "CREATING",
|
|
4098
4344
|
estimatedDurationMs: row.type === "crossfade" ? CROSSFADE_ESTIMATE_MS : FADE_ESTIMATE_MS
|
|
4099
4345
|
}
|
|
4100
|
-
) }) : /* @__PURE__ */
|
|
4346
|
+
) }) : /* @__PURE__ */ jsx18(
|
|
4101
4347
|
"button",
|
|
4102
4348
|
{
|
|
4103
4349
|
type: "button",
|
|
@@ -4108,7 +4354,7 @@ function TransitionDesigner({
|
|
|
4108
4354
|
children: "Create"
|
|
4109
4355
|
}
|
|
4110
4356
|
),
|
|
4111
|
-
errMsg && /* @__PURE__ */
|
|
4357
|
+
errMsg && /* @__PURE__ */ jsx18(
|
|
4112
4358
|
"span",
|
|
4113
4359
|
{
|
|
4114
4360
|
"data-testid": `${testIdPrefix}-row-error-${i}`,
|
|
@@ -4126,9 +4372,325 @@ function TransitionDesigner({
|
|
|
4126
4372
|
] });
|
|
4127
4373
|
}
|
|
4128
4374
|
|
|
4375
|
+
// src/components/PanelMasterStrip.tsx
|
|
4376
|
+
import { useMemo as useMemo7, useState as useState14 } from "react";
|
|
4377
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4378
|
+
function PanelMasterStrip({
|
|
4379
|
+
bus,
|
|
4380
|
+
levels = null,
|
|
4381
|
+
availableFx = [],
|
|
4382
|
+
fxLoading = false,
|
|
4383
|
+
soloedOut = false,
|
|
4384
|
+
disabled = false,
|
|
4385
|
+
fxPickerOpen,
|
|
4386
|
+
onToggleFxPicker,
|
|
4387
|
+
onRefreshFx,
|
|
4388
|
+
onVolumeChange,
|
|
4389
|
+
onMuteToggle,
|
|
4390
|
+
onSoloToggle,
|
|
4391
|
+
onAddFx,
|
|
4392
|
+
onRemoveFx,
|
|
4393
|
+
onToggleFxEnabled,
|
|
4394
|
+
onShowFxEditor
|
|
4395
|
+
}) {
|
|
4396
|
+
const [search, setSearch] = useState14("");
|
|
4397
|
+
const filtered = useMemo7(() => {
|
|
4398
|
+
const q = search.trim().toLowerCase();
|
|
4399
|
+
if (!q) return availableFx;
|
|
4400
|
+
return availableFx.filter(
|
|
4401
|
+
(fx) => fx.name.toLowerCase().includes(q) || fx.manufacturer.toLowerCase().includes(q)
|
|
4402
|
+
);
|
|
4403
|
+
}, [availableFx, search]);
|
|
4404
|
+
return /* @__PURE__ */ jsxs15(
|
|
4405
|
+
"div",
|
|
4406
|
+
{
|
|
4407
|
+
"data-testid": "panel-master-strip",
|
|
4408
|
+
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" : ""}`,
|
|
4409
|
+
children: [
|
|
4410
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
|
|
4411
|
+
/* @__PURE__ */ jsx19(
|
|
4412
|
+
"span",
|
|
4413
|
+
{
|
|
4414
|
+
className: "text-[9px] font-bold tracking-widest text-sas-muted/70 select-none",
|
|
4415
|
+
title: "Panel mix bus \u2014 volume, mute/solo and FX applied to this panel's summed output",
|
|
4416
|
+
children: "BUS"
|
|
4417
|
+
}
|
|
4418
|
+
),
|
|
4419
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex-1 min-w-[8rem] flex flex-col gap-0.5", children: [
|
|
4420
|
+
/* @__PURE__ */ jsx19(
|
|
4421
|
+
VolumeSlider,
|
|
4422
|
+
{
|
|
4423
|
+
value: dbToSlider(bus.volume),
|
|
4424
|
+
onChange: (sliderValue) => onVolumeChange(sliderToDb(sliderValue)),
|
|
4425
|
+
disabled
|
|
4426
|
+
}
|
|
4427
|
+
),
|
|
4428
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-px", "data-testid": "bus-meter", children: [
|
|
4429
|
+
/* @__PURE__ */ jsx19(
|
|
4430
|
+
LevelMeter,
|
|
4431
|
+
{
|
|
4432
|
+
peakDb: levels?.leftDb ?? -120,
|
|
4433
|
+
active: levels != null,
|
|
4434
|
+
clipped: levels?.clipped,
|
|
4435
|
+
compact: true,
|
|
4436
|
+
"data-testid": "bus-meter-left"
|
|
4437
|
+
}
|
|
4438
|
+
),
|
|
4439
|
+
/* @__PURE__ */ jsx19(
|
|
4440
|
+
LevelMeter,
|
|
4441
|
+
{
|
|
4442
|
+
peakDb: levels?.rightDb ?? -120,
|
|
4443
|
+
active: levels != null,
|
|
4444
|
+
compact: true,
|
|
4445
|
+
"data-testid": "bus-meter-right"
|
|
4446
|
+
}
|
|
4447
|
+
)
|
|
4448
|
+
] })
|
|
4449
|
+
] }),
|
|
4450
|
+
/* @__PURE__ */ jsx19(
|
|
4451
|
+
"button",
|
|
4452
|
+
{
|
|
4453
|
+
"data-testid": "bus-mute-button",
|
|
4454
|
+
onClick: onMuteToggle,
|
|
4455
|
+
disabled,
|
|
4456
|
+
className: `px-1.5 py-0.5 text-xs font-bold rounded transition-colors ${bus.muted ? "bg-red-600 text-white" : "bg-sas-panel-alt text-sas-muted hover:bg-sas-border"} disabled:opacity-50`,
|
|
4457
|
+
title: bus.muted ? "Unmute panel bus" : "Mute panel bus (silences the whole panel)",
|
|
4458
|
+
children: "M"
|
|
4459
|
+
}
|
|
4460
|
+
),
|
|
4461
|
+
/* @__PURE__ */ jsx19(
|
|
4462
|
+
"button",
|
|
4463
|
+
{
|
|
4464
|
+
"data-testid": "bus-solo-button",
|
|
4465
|
+
onClick: onSoloToggle,
|
|
4466
|
+
disabled,
|
|
4467
|
+
className: `px-1.5 py-0.5 text-xs font-bold rounded transition-colors ${bus.soloed ? "bg-amber-500 text-black" : "bg-sas-panel-alt text-sas-muted hover:bg-sas-border"} disabled:opacity-50`,
|
|
4468
|
+
title: bus.soloed ? "Unsolo panel bus" : "Solo this panel (silences other panels/tracks in scope)",
|
|
4469
|
+
children: "S"
|
|
4470
|
+
}
|
|
4471
|
+
),
|
|
4472
|
+
/* @__PURE__ */ jsx19("div", { className: "flex items-center gap-1 max-w-[45%] min-w-0 overflow-x-auto", children: bus.fx.map((fx) => /* @__PURE__ */ jsxs15(
|
|
4473
|
+
"span",
|
|
4474
|
+
{
|
|
4475
|
+
"data-testid": `bus-fx-chip-${fx.index}`,
|
|
4476
|
+
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"}`,
|
|
4477
|
+
title: `${fx.name}${fx.enabled ? "" : " (bypassed)"}`,
|
|
4478
|
+
children: [
|
|
4479
|
+
/* @__PURE__ */ jsx19(
|
|
4480
|
+
"button",
|
|
4481
|
+
{
|
|
4482
|
+
"data-testid": `bus-fx-toggle-${fx.index}`,
|
|
4483
|
+
onClick: () => onToggleFxEnabled(fx.index, !fx.enabled),
|
|
4484
|
+
disabled,
|
|
4485
|
+
className: "hover:opacity-70 disabled:opacity-50",
|
|
4486
|
+
title: fx.enabled ? `Bypass ${fx.name}` : `Enable ${fx.name}`,
|
|
4487
|
+
children: fx.enabled ? "\u25CF" : "\u25CB"
|
|
4488
|
+
}
|
|
4489
|
+
),
|
|
4490
|
+
onShowFxEditor ? /* @__PURE__ */ jsx19(
|
|
4491
|
+
"button",
|
|
4492
|
+
{
|
|
4493
|
+
"data-testid": `bus-fx-edit-${fx.index}`,
|
|
4494
|
+
onClick: () => onShowFxEditor(fx.index),
|
|
4495
|
+
disabled,
|
|
4496
|
+
className: "max-w-[80px] truncate hover:underline disabled:opacity-50",
|
|
4497
|
+
title: `Open ${fx.name} editor`,
|
|
4498
|
+
children: fx.name
|
|
4499
|
+
}
|
|
4500
|
+
) : /* @__PURE__ */ jsx19("span", { className: "max-w-[80px] truncate", children: fx.name }),
|
|
4501
|
+
/* @__PURE__ */ jsx19(
|
|
4502
|
+
"button",
|
|
4503
|
+
{
|
|
4504
|
+
"data-testid": `bus-fx-remove-${fx.index}`,
|
|
4505
|
+
onClick: () => onRemoveFx(fx.index),
|
|
4506
|
+
disabled,
|
|
4507
|
+
className: "text-sas-muted/60 hover:text-sas-danger disabled:opacity-50",
|
|
4508
|
+
title: `Remove ${fx.name} from the bus`,
|
|
4509
|
+
children: "\u2715"
|
|
4510
|
+
}
|
|
4511
|
+
)
|
|
4512
|
+
]
|
|
4513
|
+
},
|
|
4514
|
+
`${fx.index}:${fx.pluginId}`
|
|
4515
|
+
)) }),
|
|
4516
|
+
/* @__PURE__ */ jsx19(
|
|
4517
|
+
"button",
|
|
4518
|
+
{
|
|
4519
|
+
"data-testid": "bus-fx-add-button",
|
|
4520
|
+
onClick: () => onToggleFxPicker(!fxPickerOpen),
|
|
4521
|
+
disabled,
|
|
4522
|
+
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`,
|
|
4523
|
+
title: fxPickerOpen ? "Close the FX picker" : "Add an FX plugin to the panel bus",
|
|
4524
|
+
children: fxPickerOpen ? "FX \u25B4" : "FX +"
|
|
4525
|
+
}
|
|
4526
|
+
)
|
|
4527
|
+
] }),
|
|
4528
|
+
fxPickerOpen && /* @__PURE__ */ jsxs15("div", { "data-testid": "bus-fx-picker", className: "flex flex-col gap-2 pt-1", children: [
|
|
4529
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
|
|
4530
|
+
/* @__PURE__ */ jsx19(
|
|
4531
|
+
"input",
|
|
4532
|
+
{
|
|
4533
|
+
type: "text",
|
|
4534
|
+
value: search,
|
|
4535
|
+
onChange: (e) => setSearch(e.target.value),
|
|
4536
|
+
placeholder: "Search FX...",
|
|
4537
|
+
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
4538
|
+
}
|
|
4539
|
+
),
|
|
4540
|
+
onRefreshFx && /* @__PURE__ */ jsx19(
|
|
4541
|
+
"button",
|
|
4542
|
+
{
|
|
4543
|
+
onClick: () => onRefreshFx(),
|
|
4544
|
+
disabled: fxLoading,
|
|
4545
|
+
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",
|
|
4546
|
+
title: "Re-scan plugins",
|
|
4547
|
+
children: fxLoading ? "..." : "Refresh"
|
|
4548
|
+
}
|
|
4549
|
+
)
|
|
4550
|
+
] }),
|
|
4551
|
+
fxLoading && availableFx.length === 0 ? /* @__PURE__ */ jsx19("div", { className: "text-xs text-sas-muted/60 text-center py-3", children: "Scanning plugins..." }) : /* @__PURE__ */ jsxs15("div", { className: "grid grid-cols-3 gap-1 max-h-[140px] overflow-y-auto", children: [
|
|
4552
|
+
filtered.map((fx) => /* @__PURE__ */ jsxs15(
|
|
4553
|
+
"button",
|
|
4554
|
+
{
|
|
4555
|
+
"data-testid": `bus-fx-pick-${fx.pluginId}`,
|
|
4556
|
+
onClick: () => onAddFx(fx.pluginId),
|
|
4557
|
+
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",
|
|
4558
|
+
title: `${fx.name} by ${fx.manufacturer} (${fx.type.toUpperCase()})`,
|
|
4559
|
+
children: [
|
|
4560
|
+
/* @__PURE__ */ jsx19("span", { className: "text-xs font-medium truncate w-full", children: fx.name }),
|
|
4561
|
+
/* @__PURE__ */ jsx19("span", { className: "text-[9px] text-sas-muted/50 truncate w-full", children: fx.manufacturer || fx.type.toUpperCase() })
|
|
4562
|
+
]
|
|
4563
|
+
},
|
|
4564
|
+
fx.pluginId
|
|
4565
|
+
)),
|
|
4566
|
+
filtered.length === 0 && /* @__PURE__ */ jsx19("div", { className: "col-span-3 text-xs text-sas-muted/60 text-center py-2", children: search.trim() ? "No matches" : "No FX plugins found" })
|
|
4567
|
+
] })
|
|
4568
|
+
] })
|
|
4569
|
+
]
|
|
4570
|
+
}
|
|
4571
|
+
);
|
|
4572
|
+
}
|
|
4573
|
+
|
|
4574
|
+
// src/hooks/usePanelBus.ts
|
|
4575
|
+
import { useCallback as useCallback10, useEffect as useEffect11, useRef as useRef12, useState as useState15 } from "react";
|
|
4576
|
+
var LEVELS_POLL_MS = 66;
|
|
4577
|
+
function usePanelBus(host, activeSceneId) {
|
|
4578
|
+
const supported = typeof host.getPanelBusState === "function";
|
|
4579
|
+
const [bus, setBus] = useState15(null);
|
|
4580
|
+
const [levels, setLevels] = useState15(null);
|
|
4581
|
+
const [availableFx, setAvailableFx] = useState15([]);
|
|
4582
|
+
const [fxLoading, setFxLoading] = useState15(false);
|
|
4583
|
+
const [fxPickerOpen, setFxPickerOpen] = useState15(false);
|
|
4584
|
+
const fxLoadedRef = useRef12(false);
|
|
4585
|
+
const loadSeqRef = useRef12(0);
|
|
4586
|
+
const reload = useCallback10(async () => {
|
|
4587
|
+
if (!supported || !activeSceneId || !host.getPanelBusState) {
|
|
4588
|
+
setBus(null);
|
|
4589
|
+
return;
|
|
4590
|
+
}
|
|
4591
|
+
const seq = ++loadSeqRef.current;
|
|
4592
|
+
try {
|
|
4593
|
+
const state = await host.getPanelBusState(activeSceneId);
|
|
4594
|
+
if (loadSeqRef.current === seq) setBus(state);
|
|
4595
|
+
} catch {
|
|
4596
|
+
}
|
|
4597
|
+
}, [host, activeSceneId, supported]);
|
|
4598
|
+
useEffect11(() => {
|
|
4599
|
+
setBus(null);
|
|
4600
|
+
setFxPickerOpen(false);
|
|
4601
|
+
void reload();
|
|
4602
|
+
}, [reload]);
|
|
4603
|
+
useEffect11(() => {
|
|
4604
|
+
if (!supported || !activeSceneId || !bus?.engaged || !host.getPanelBusLevels) {
|
|
4605
|
+
setLevels(null);
|
|
4606
|
+
return;
|
|
4607
|
+
}
|
|
4608
|
+
let cancelled = false;
|
|
4609
|
+
const tick = async () => {
|
|
4610
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
4611
|
+
try {
|
|
4612
|
+
const next = await host.getPanelBusLevels(activeSceneId);
|
|
4613
|
+
if (!cancelled) setLevels(next);
|
|
4614
|
+
} catch {
|
|
4615
|
+
if (!cancelled) setLevels(null);
|
|
4616
|
+
}
|
|
4617
|
+
};
|
|
4618
|
+
void tick();
|
|
4619
|
+
const id = setInterval(() => void tick(), LEVELS_POLL_MS);
|
|
4620
|
+
return () => {
|
|
4621
|
+
cancelled = true;
|
|
4622
|
+
clearInterval(id);
|
|
4623
|
+
};
|
|
4624
|
+
}, [supported, activeSceneId, bus?.engaged, host]);
|
|
4625
|
+
const loadFxList = useCallback10(
|
|
4626
|
+
async (force) => {
|
|
4627
|
+
if (!supported || !host.getAvailableFx) return;
|
|
4628
|
+
if (fxLoadedRef.current && !force) return;
|
|
4629
|
+
setFxLoading(true);
|
|
4630
|
+
try {
|
|
4631
|
+
const list = await host.getAvailableFx();
|
|
4632
|
+
setAvailableFx(list);
|
|
4633
|
+
fxLoadedRef.current = true;
|
|
4634
|
+
} catch {
|
|
4635
|
+
} finally {
|
|
4636
|
+
setFxLoading(false);
|
|
4637
|
+
}
|
|
4638
|
+
},
|
|
4639
|
+
[host, supported]
|
|
4640
|
+
);
|
|
4641
|
+
const openPicker = useCallback10(
|
|
4642
|
+
(open) => {
|
|
4643
|
+
setFxPickerOpen(open);
|
|
4644
|
+
if (open) void loadFxList(false);
|
|
4645
|
+
},
|
|
4646
|
+
[loadFxList]
|
|
4647
|
+
);
|
|
4648
|
+
const mutate = useCallback10(
|
|
4649
|
+
(fn) => {
|
|
4650
|
+
if (!fn || !activeSceneId) return;
|
|
4651
|
+
void (async () => {
|
|
4652
|
+
try {
|
|
4653
|
+
await fn();
|
|
4654
|
+
} catch {
|
|
4655
|
+
}
|
|
4656
|
+
await reload();
|
|
4657
|
+
})();
|
|
4658
|
+
},
|
|
4659
|
+
[activeSceneId, reload]
|
|
4660
|
+
);
|
|
4661
|
+
return {
|
|
4662
|
+
supported,
|
|
4663
|
+
bus,
|
|
4664
|
+
levels,
|
|
4665
|
+
availableFx,
|
|
4666
|
+
fxLoading,
|
|
4667
|
+
fxPickerOpen,
|
|
4668
|
+
setFxPickerOpen: openPicker,
|
|
4669
|
+
refreshFx: () => void loadFxList(true),
|
|
4670
|
+
reload,
|
|
4671
|
+
onVolumeChange: (volumeDb) => mutate(host.setPanelBusVolume && (() => host.setPanelBusVolume(activeSceneId, volumeDb))),
|
|
4672
|
+
onMuteToggle: () => mutate(
|
|
4673
|
+
host.setPanelBusMute && (() => host.setPanelBusMute(activeSceneId, !(bus?.muted ?? false)))
|
|
4674
|
+
),
|
|
4675
|
+
onSoloToggle: () => mutate(
|
|
4676
|
+
host.setPanelBusSolo && (() => host.setPanelBusSolo(activeSceneId, !(bus?.soloed ?? false)))
|
|
4677
|
+
),
|
|
4678
|
+
onAddFx: (pluginId) => mutate(host.loadPanelBusFx && (async () => {
|
|
4679
|
+
await host.loadPanelBusFx(activeSceneId, pluginId);
|
|
4680
|
+
})),
|
|
4681
|
+
onRemoveFx: (fxIndex) => mutate(host.removePanelBusFx && (() => host.removePanelBusFx(activeSceneId, fxIndex))),
|
|
4682
|
+
onToggleFxEnabled: (fxIndex, enabled) => mutate(
|
|
4683
|
+
host.setPanelBusFxEnabled && (() => host.setPanelBusFxEnabled(activeSceneId, fxIndex, enabled))
|
|
4684
|
+
),
|
|
4685
|
+
onShowFxEditor: (fxIndex) => mutate(
|
|
4686
|
+
host.showPanelBusFxEditor && (() => host.showPanelBusFxEditor(activeSceneId, fxIndex))
|
|
4687
|
+
)
|
|
4688
|
+
};
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4129
4691
|
// src/components/DownloadPackButton.tsx
|
|
4130
|
-
import { useCallback as
|
|
4131
|
-
import { jsx as
|
|
4692
|
+
import { useCallback as useCallback11, useEffect as useEffect12, useState as useState16 } from "react";
|
|
4693
|
+
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4132
4694
|
function formatSize(bytes) {
|
|
4133
4695
|
if (!bytes || bytes <= 0) return "";
|
|
4134
4696
|
const gb = bytes / 1024 ** 3;
|
|
@@ -4144,10 +4706,10 @@ var DownloadPackButton = ({
|
|
|
4144
4706
|
variant = "compact",
|
|
4145
4707
|
onDownloadComplete
|
|
4146
4708
|
}) => {
|
|
4147
|
-
const [status, setStatus] =
|
|
4148
|
-
const [progress, setProgress] =
|
|
4149
|
-
const [errorMessage, setErrorMessage] =
|
|
4150
|
-
|
|
4709
|
+
const [status, setStatus] = useState16("idle");
|
|
4710
|
+
const [progress, setProgress] = useState16(0);
|
|
4711
|
+
const [errorMessage, setErrorMessage] = useState16(null);
|
|
4712
|
+
useEffect12(() => {
|
|
4151
4713
|
const unsub = host.onSamplePackProgress(packId, (p) => {
|
|
4152
4714
|
setStatus(p.status);
|
|
4153
4715
|
setProgress(p.progress);
|
|
@@ -4162,7 +4724,7 @@ var DownloadPackButton = ({
|
|
|
4162
4724
|
});
|
|
4163
4725
|
return unsub;
|
|
4164
4726
|
}, [host, packId, onDownloadComplete]);
|
|
4165
|
-
const handleClick =
|
|
4727
|
+
const handleClick = useCallback11(async () => {
|
|
4166
4728
|
if (status !== "idle" && status !== "error") return;
|
|
4167
4729
|
try {
|
|
4168
4730
|
setStatus("downloading");
|
|
@@ -4216,8 +4778,8 @@ var DownloadPackButton = ({
|
|
|
4216
4778
|
} else {
|
|
4217
4779
|
className = `${baseClasses} text-sas-muted hover:text-sas-accent border-sas-border hover:border-sas-accent`;
|
|
4218
4780
|
}
|
|
4219
|
-
return /* @__PURE__ */
|
|
4220
|
-
/* @__PURE__ */
|
|
4781
|
+
return /* @__PURE__ */ jsxs16("div", { children: [
|
|
4782
|
+
/* @__PURE__ */ jsx20(
|
|
4221
4783
|
"button",
|
|
4222
4784
|
{
|
|
4223
4785
|
"data-testid": `download-pack-button-${packId}`,
|
|
@@ -4228,12 +4790,12 @@ var DownloadPackButton = ({
|
|
|
4228
4790
|
children: buttonLabel
|
|
4229
4791
|
}
|
|
4230
4792
|
),
|
|
4231
|
-
variant === "large" && status === "error" && errorMessage && /* @__PURE__ */
|
|
4793
|
+
variant === "large" && status === "error" && errorMessage && /* @__PURE__ */ jsx20("div", { className: "text-xs text-sas-danger mt-2", "data-testid": `download-pack-error-${packId}`, children: errorMessage })
|
|
4232
4794
|
] });
|
|
4233
4795
|
};
|
|
4234
4796
|
|
|
4235
4797
|
// src/components/SamplePackCTACard.tsx
|
|
4236
|
-
import { jsx as
|
|
4798
|
+
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4237
4799
|
var SamplePackCTACard = ({
|
|
4238
4800
|
host,
|
|
4239
4801
|
pack,
|
|
@@ -4241,7 +4803,7 @@ var SamplePackCTACard = ({
|
|
|
4241
4803
|
onDownloadComplete
|
|
4242
4804
|
}) => {
|
|
4243
4805
|
if (status === "checking") {
|
|
4244
|
-
return /* @__PURE__ */
|
|
4806
|
+
return /* @__PURE__ */ jsx21(
|
|
4245
4807
|
"div",
|
|
4246
4808
|
{
|
|
4247
4809
|
"data-testid": `sample-pack-cta-checking-${pack.packId}`,
|
|
@@ -4252,16 +4814,16 @@ var SamplePackCTACard = ({
|
|
|
4252
4814
|
}
|
|
4253
4815
|
const headline = status === "stale" ? `${pack.displayName} update available` : `${pack.displayName} not installed`;
|
|
4254
4816
|
const sublabel = status === "stale" ? `A newer version is available for download.` : pack.description;
|
|
4255
|
-
return /* @__PURE__ */
|
|
4817
|
+
return /* @__PURE__ */ jsxs17(
|
|
4256
4818
|
"div",
|
|
4257
4819
|
{
|
|
4258
4820
|
"data-testid": `sample-pack-cta-${pack.packId}`,
|
|
4259
4821
|
className: "flex flex-col items-center justify-center py-12 px-6 text-center",
|
|
4260
4822
|
children: [
|
|
4261
|
-
/* @__PURE__ */
|
|
4262
|
-
/* @__PURE__ */
|
|
4263
|
-
/* @__PURE__ */
|
|
4264
|
-
/* @__PURE__ */
|
|
4823
|
+
/* @__PURE__ */ jsx21("div", { className: "text-sm uppercase tracking-wide text-sas-muted mb-2", children: status === "stale" ? "Update available" : "Sample library not installed" }),
|
|
4824
|
+
/* @__PURE__ */ jsx21("div", { className: "text-base text-sas-text mb-1", children: headline }),
|
|
4825
|
+
/* @__PURE__ */ jsx21("div", { className: "text-xs text-sas-muted mb-6 max-w-md", children: sublabel }),
|
|
4826
|
+
/* @__PURE__ */ jsx21(
|
|
4265
4827
|
DownloadPackButton,
|
|
4266
4828
|
{
|
|
4267
4829
|
host,
|
|
@@ -4278,7 +4840,7 @@ var SamplePackCTACard = ({
|
|
|
4278
4840
|
};
|
|
4279
4841
|
|
|
4280
4842
|
// src/components/WaveformView.tsx
|
|
4281
|
-
import { useEffect as
|
|
4843
|
+
import { useEffect as useEffect13, useRef as useRef13, useState as useState17 } from "react";
|
|
4282
4844
|
|
|
4283
4845
|
// src/components/waveform.ts
|
|
4284
4846
|
function computePeaks(audioBuffer, bins, targetSamples) {
|
|
@@ -4341,7 +4903,7 @@ function drawWaveform(canvas, peaks, options = {}) {
|
|
|
4341
4903
|
}
|
|
4342
4904
|
|
|
4343
4905
|
// src/components/WaveformView.tsx
|
|
4344
|
-
import { jsx as
|
|
4906
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
4345
4907
|
var WaveformView = ({
|
|
4346
4908
|
host,
|
|
4347
4909
|
filePath,
|
|
@@ -4350,9 +4912,9 @@ var WaveformView = ({
|
|
|
4350
4912
|
fillStyle,
|
|
4351
4913
|
targetSamples
|
|
4352
4914
|
}) => {
|
|
4353
|
-
const canvasRef =
|
|
4354
|
-
const [peaks, setPeaks] =
|
|
4355
|
-
|
|
4915
|
+
const canvasRef = useRef13(null);
|
|
4916
|
+
const [peaks, setPeaks] = useState17(null);
|
|
4917
|
+
useEffect13(() => {
|
|
4356
4918
|
let cancelled = false;
|
|
4357
4919
|
let audioContext = null;
|
|
4358
4920
|
(async () => {
|
|
@@ -4378,7 +4940,7 @@ var WaveformView = ({
|
|
|
4378
4940
|
cancelled = true;
|
|
4379
4941
|
};
|
|
4380
4942
|
}, [host, filePath, bins, targetSamples]);
|
|
4381
|
-
|
|
4943
|
+
useEffect13(() => {
|
|
4382
4944
|
if (!peaks) return;
|
|
4383
4945
|
const canvas = canvasRef.current;
|
|
4384
4946
|
if (!canvas) return;
|
|
@@ -4389,7 +4951,7 @@ var WaveformView = ({
|
|
|
4389
4951
|
observer.observe(canvas);
|
|
4390
4952
|
return () => observer.disconnect();
|
|
4391
4953
|
}, [peaks, fillStyle]);
|
|
4392
|
-
return /* @__PURE__ */
|
|
4954
|
+
return /* @__PURE__ */ jsx22(
|
|
4393
4955
|
"canvas",
|
|
4394
4956
|
{
|
|
4395
4957
|
ref: canvasRef,
|
|
@@ -4400,8 +4962,8 @@ var WaveformView = ({
|
|
|
4400
4962
|
};
|
|
4401
4963
|
|
|
4402
4964
|
// src/components/ScrollingWaveform.tsx
|
|
4403
|
-
import { useEffect as
|
|
4404
|
-
import { jsx as
|
|
4965
|
+
import { useEffect as useEffect14, useRef as useRef14 } from "react";
|
|
4966
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
4405
4967
|
var ScrollingWaveform = ({
|
|
4406
4968
|
getPeakDb,
|
|
4407
4969
|
active,
|
|
@@ -4409,11 +4971,11 @@ var ScrollingWaveform = ({
|
|
|
4409
4971
|
className,
|
|
4410
4972
|
fillStyle
|
|
4411
4973
|
}) => {
|
|
4412
|
-
const canvasRef =
|
|
4413
|
-
const ringRef =
|
|
4414
|
-
const writeIdxRef =
|
|
4415
|
-
const rafRef =
|
|
4416
|
-
|
|
4974
|
+
const canvasRef = useRef14(null);
|
|
4975
|
+
const ringRef = useRef14(new Float32Array(columns));
|
|
4976
|
+
const writeIdxRef = useRef14(0);
|
|
4977
|
+
const rafRef = useRef14(null);
|
|
4978
|
+
useEffect14(() => {
|
|
4417
4979
|
if (ringRef.current.length !== columns) {
|
|
4418
4980
|
const next = new Float32Array(columns);
|
|
4419
4981
|
const prev = ringRef.current;
|
|
@@ -4425,7 +4987,7 @@ var ScrollingWaveform = ({
|
|
|
4425
4987
|
writeIdxRef.current = writeIdxRef.current % columns;
|
|
4426
4988
|
}
|
|
4427
4989
|
}, [columns]);
|
|
4428
|
-
|
|
4990
|
+
useEffect14(() => {
|
|
4429
4991
|
if (!active) {
|
|
4430
4992
|
if (rafRef.current !== null) {
|
|
4431
4993
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -4477,7 +5039,7 @@ var ScrollingWaveform = ({
|
|
|
4477
5039
|
}
|
|
4478
5040
|
};
|
|
4479
5041
|
}, [active, getPeakDb, fillStyle]);
|
|
4480
|
-
return /* @__PURE__ */
|
|
5042
|
+
return /* @__PURE__ */ jsx23(
|
|
4481
5043
|
"canvas",
|
|
4482
5044
|
{
|
|
4483
5045
|
ref: canvasRef,
|
|
@@ -4488,8 +5050,8 @@ var ScrollingWaveform = ({
|
|
|
4488
5050
|
};
|
|
4489
5051
|
|
|
4490
5052
|
// src/components/OffsetScrubber.tsx
|
|
4491
|
-
import { useCallback as
|
|
4492
|
-
import { jsx as
|
|
5053
|
+
import { useCallback as useCallback12, useEffect as useEffect15, useMemo as useMemo8, useRef as useRef15, useState as useState18 } from "react";
|
|
5054
|
+
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
4493
5055
|
var SLIDER_HEIGHT_PX = 28;
|
|
4494
5056
|
var TICK_HEIGHT_PX = 14;
|
|
4495
5057
|
var DOWNBEAT_TICK_HEIGHT_PX = 22;
|
|
@@ -4502,40 +5064,40 @@ function OffsetScrubber({
|
|
|
4502
5064
|
onChange,
|
|
4503
5065
|
disabled = false
|
|
4504
5066
|
}) {
|
|
4505
|
-
const trackRef =
|
|
4506
|
-
const [draftOffset, setDraftOffset] =
|
|
4507
|
-
const [isDragging, setIsDragging] =
|
|
4508
|
-
|
|
5067
|
+
const trackRef = useRef15(null);
|
|
5068
|
+
const [draftOffset, setDraftOffset] = useState18(offsetSamples);
|
|
5069
|
+
const [isDragging, setIsDragging] = useState18(false);
|
|
5070
|
+
useEffect15(() => {
|
|
4509
5071
|
if (!isDragging) setDraftOffset(offsetSamples);
|
|
4510
5072
|
}, [offsetSamples, isDragging]);
|
|
4511
5073
|
const sampleRate = cuePoints?.sample_rate ?? 44100;
|
|
4512
5074
|
const detectedBpm = cuePoints?.detected_bpm ?? projectBpm;
|
|
4513
|
-
const beatsForRange =
|
|
5075
|
+
const beatsForRange = useMemo8(() => {
|
|
4514
5076
|
return Math.round(60 / projectBpm * sampleRate);
|
|
4515
5077
|
}, [projectBpm, sampleRate]);
|
|
4516
5078
|
const rangeSamples = beatsForRange * meter;
|
|
4517
|
-
const sampleToFraction =
|
|
5079
|
+
const sampleToFraction = useCallback12(
|
|
4518
5080
|
(sample) => {
|
|
4519
5081
|
const clamped = Math.max(-rangeSamples, Math.min(rangeSamples, sample));
|
|
4520
5082
|
return (clamped + rangeSamples) / (2 * rangeSamples);
|
|
4521
5083
|
},
|
|
4522
5084
|
[rangeSamples]
|
|
4523
5085
|
);
|
|
4524
|
-
const fractionToSample =
|
|
5086
|
+
const fractionToSample = useCallback12(
|
|
4525
5087
|
(fraction) => {
|
|
4526
5088
|
const clamped = Math.max(0, Math.min(1, fraction));
|
|
4527
5089
|
return Math.round(clamped * 2 * rangeSamples - rangeSamples);
|
|
4528
5090
|
},
|
|
4529
5091
|
[rangeSamples]
|
|
4530
5092
|
);
|
|
4531
|
-
const snapTargets =
|
|
5093
|
+
const snapTargets = useMemo8(() => {
|
|
4532
5094
|
if (!cuePoints || cuePoints.beats.length === 0) return [];
|
|
4533
5095
|
const downbeat = cuePoints.beats[0];
|
|
4534
5096
|
const positives = cuePoints.beats.map((b) => b - downbeat);
|
|
4535
5097
|
const negatives = positives.slice(1).map((p) => -p);
|
|
4536
5098
|
return [...negatives, ...positives].sort((a, b) => a - b);
|
|
4537
5099
|
}, [cuePoints]);
|
|
4538
|
-
const snapToBeat =
|
|
5100
|
+
const snapToBeat = useCallback12(
|
|
4539
5101
|
(sample) => {
|
|
4540
5102
|
if (snapTargets.length === 0) return sample;
|
|
4541
5103
|
let best = snapTargets[0];
|
|
@@ -4551,7 +5113,7 @@ function OffsetScrubber({
|
|
|
4551
5113
|
},
|
|
4552
5114
|
[snapTargets]
|
|
4553
5115
|
);
|
|
4554
|
-
const handlePointerDown =
|
|
5116
|
+
const handlePointerDown = useCallback12(
|
|
4555
5117
|
(e) => {
|
|
4556
5118
|
if (disabled || !cuePoints) return;
|
|
4557
5119
|
e.preventDefault();
|
|
@@ -4585,7 +5147,7 @@ function OffsetScrubber({
|
|
|
4585
5147
|
},
|
|
4586
5148
|
[disabled, cuePoints, fractionToSample, onChange, snapToBeat]
|
|
4587
5149
|
);
|
|
4588
|
-
const handleResetToZero =
|
|
5150
|
+
const handleResetToZero = useCallback12(() => {
|
|
4589
5151
|
if (disabled) return;
|
|
4590
5152
|
setDraftOffset(0);
|
|
4591
5153
|
onChange(0);
|
|
@@ -4593,7 +5155,7 @@ function OffsetScrubber({
|
|
|
4593
5155
|
const thumbFraction = sampleToFraction(draftOffset);
|
|
4594
5156
|
const thumbLeftPct = `${(thumbFraction * 100).toFixed(2)}%`;
|
|
4595
5157
|
const bpmMismatch = cuePoints?.detected_bpm != null && Math.abs(cuePoints.detected_bpm - projectBpm) > 1;
|
|
4596
|
-
const ticks =
|
|
5158
|
+
const ticks = useMemo8(() => {
|
|
4597
5159
|
if (!cuePoints) return [];
|
|
4598
5160
|
const downbeat = cuePoints.beats[0] ?? 0;
|
|
4599
5161
|
return cuePoints.beats.map((b, i) => {
|
|
@@ -4604,9 +5166,9 @@ function OffsetScrubber({
|
|
|
4604
5166
|
});
|
|
4605
5167
|
}, [cuePoints, sampleToFraction]);
|
|
4606
5168
|
const isDisabled = disabled || !cuePoints || cuePoints.beats.length === 0;
|
|
4607
|
-
return /* @__PURE__ */
|
|
4608
|
-
/* @__PURE__ */
|
|
4609
|
-
/* @__PURE__ */
|
|
5169
|
+
return /* @__PURE__ */ jsxs18("div", { "data-testid": "offset-scrubber", className: "flex items-center gap-2 w-full", children: [
|
|
5170
|
+
/* @__PURE__ */ jsx24("span", { className: "text-[9px] text-sas-muted/60 uppercase tracking-wide flex-shrink-0", children: "Align" }),
|
|
5171
|
+
/* @__PURE__ */ jsxs18(
|
|
4610
5172
|
"div",
|
|
4611
5173
|
{
|
|
4612
5174
|
ref: trackRef,
|
|
@@ -4622,7 +5184,7 @@ function OffsetScrubber({
|
|
|
4622
5184
|
"aria-valuenow": draftOffset,
|
|
4623
5185
|
"aria-disabled": isDisabled,
|
|
4624
5186
|
children: [
|
|
4625
|
-
/* @__PURE__ */
|
|
5187
|
+
/* @__PURE__ */ jsx24(
|
|
4626
5188
|
"div",
|
|
4627
5189
|
{
|
|
4628
5190
|
"aria-hidden": "true",
|
|
@@ -4630,7 +5192,7 @@ function OffsetScrubber({
|
|
|
4630
5192
|
style: { left: "50%" }
|
|
4631
5193
|
}
|
|
4632
5194
|
),
|
|
4633
|
-
ticks.map((t) => /* @__PURE__ */
|
|
5195
|
+
ticks.map((t) => /* @__PURE__ */ jsx24(
|
|
4634
5196
|
"div",
|
|
4635
5197
|
{
|
|
4636
5198
|
"data-testid": t.isDownbeat ? "offset-tick-downbeat" : "offset-tick",
|
|
@@ -4645,7 +5207,7 @@ function OffsetScrubber({
|
|
|
4645
5207
|
},
|
|
4646
5208
|
t.i
|
|
4647
5209
|
)),
|
|
4648
|
-
/* @__PURE__ */
|
|
5210
|
+
/* @__PURE__ */ jsx24(
|
|
4649
5211
|
"div",
|
|
4650
5212
|
{
|
|
4651
5213
|
"data-testid": "offset-scrubber-thumb",
|
|
@@ -4662,7 +5224,7 @@ function OffsetScrubber({
|
|
|
4662
5224
|
]
|
|
4663
5225
|
}
|
|
4664
5226
|
),
|
|
4665
|
-
/* @__PURE__ */
|
|
5227
|
+
/* @__PURE__ */ jsx24(
|
|
4666
5228
|
"span",
|
|
4667
5229
|
{
|
|
4668
5230
|
"data-testid": "offset-scrubber-readout",
|
|
@@ -4670,7 +5232,7 @@ function OffsetScrubber({
|
|
|
4670
5232
|
children: formatOffset(draftOffset, sampleRate)
|
|
4671
5233
|
}
|
|
4672
5234
|
),
|
|
4673
|
-
/* @__PURE__ */
|
|
5235
|
+
/* @__PURE__ */ jsx24(
|
|
4674
5236
|
"button",
|
|
4675
5237
|
{
|
|
4676
5238
|
type: "button",
|
|
@@ -4682,7 +5244,7 @@ function OffsetScrubber({
|
|
|
4682
5244
|
children: "\u2316"
|
|
4683
5245
|
}
|
|
4684
5246
|
),
|
|
4685
|
-
bpmMismatch && /* @__PURE__ */
|
|
5247
|
+
bpmMismatch && /* @__PURE__ */ jsx24(
|
|
4686
5248
|
"span",
|
|
4687
5249
|
{
|
|
4688
5250
|
"data-testid": "offset-bpm-mismatch",
|
|
@@ -4754,16 +5316,16 @@ function synthesizeCuePoints({
|
|
|
4754
5316
|
}
|
|
4755
5317
|
|
|
4756
5318
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
4757
|
-
import { useState as
|
|
5319
|
+
import { useState as useState23, useEffect as useEffect18, useCallback as useCallback16, useRef as useRef19, useMemo as useMemo10 } from "react";
|
|
4758
5320
|
|
|
4759
5321
|
// src/hooks/useSceneState.ts
|
|
4760
|
-
import { useState as
|
|
5322
|
+
import { useState as useState19, useCallback as useCallback13, useRef as useRef16 } from "react";
|
|
4761
5323
|
function useSceneState(activeSceneId, initialValue) {
|
|
4762
|
-
const [stateMap, setStateMap] =
|
|
4763
|
-
const activeSceneIdRef =
|
|
5324
|
+
const [stateMap, setStateMap] = useState19(() => /* @__PURE__ */ new Map());
|
|
5325
|
+
const activeSceneIdRef = useRef16(activeSceneId);
|
|
4764
5326
|
activeSceneIdRef.current = activeSceneId;
|
|
4765
5327
|
const currentValue = activeSceneId !== null && stateMap.has(activeSceneId) ? stateMap.get(activeSceneId) : initialValue;
|
|
4766
|
-
const setForCurrentScene =
|
|
5328
|
+
const setForCurrentScene = useCallback13((value) => {
|
|
4767
5329
|
const sid = activeSceneIdRef.current;
|
|
4768
5330
|
if (sid === null) return;
|
|
4769
5331
|
setStateMap((prev) => {
|
|
@@ -4774,7 +5336,7 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
4774
5336
|
return newMap;
|
|
4775
5337
|
});
|
|
4776
5338
|
}, [initialValue]);
|
|
4777
|
-
const setForScene =
|
|
5339
|
+
const setForScene = useCallback13((sceneId, value) => {
|
|
4778
5340
|
setStateMap((prev) => {
|
|
4779
5341
|
const current = prev.has(sceneId) ? prev.get(sceneId) : initialValue;
|
|
4780
5342
|
const next = typeof value === "function" ? value(current) : value;
|
|
@@ -4787,10 +5349,10 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
4787
5349
|
}
|
|
4788
5350
|
|
|
4789
5351
|
// src/hooks/useAnySolo.ts
|
|
4790
|
-
import { useEffect as
|
|
5352
|
+
import { useEffect as useEffect16, useState as useState20 } from "react";
|
|
4791
5353
|
function useAnySolo(host) {
|
|
4792
|
-
const [anySolo, setAnySolo] =
|
|
4793
|
-
|
|
5354
|
+
const [anySolo, setAnySolo] = useState20(false);
|
|
5355
|
+
useEffect16(() => {
|
|
4794
5356
|
let active = true;
|
|
4795
5357
|
const refresh = () => {
|
|
4796
5358
|
host.isAnySoloActive().then((v) => {
|
|
@@ -4809,7 +5371,7 @@ function useAnySolo(host) {
|
|
|
4809
5371
|
}
|
|
4810
5372
|
|
|
4811
5373
|
// src/hooks/useSoundHistory.ts
|
|
4812
|
-
import { useCallback as
|
|
5374
|
+
import { useCallback as useCallback14, useMemo as useMemo9, useRef as useRef17, useState as useState21 } from "react";
|
|
4813
5375
|
var EMPTY = { entries: [], cursor: -1 };
|
|
4814
5376
|
function sameDescriptor(a, b) {
|
|
4815
5377
|
if (a === b) return true;
|
|
@@ -4821,14 +5383,14 @@ function sameDescriptor(a, b) {
|
|
|
4821
5383
|
}
|
|
4822
5384
|
function useSoundHistory(applySound, opts = {}) {
|
|
4823
5385
|
const max = Math.max(2, opts.max ?? 24);
|
|
4824
|
-
const applyRef =
|
|
5386
|
+
const applyRef = useRef17(applySound);
|
|
4825
5387
|
applyRef.current = applySound;
|
|
4826
|
-
const onChangeRef =
|
|
5388
|
+
const onChangeRef = useRef17(opts.onChange);
|
|
4827
5389
|
onChangeRef.current = opts.onChange;
|
|
4828
|
-
const dataRef =
|
|
4829
|
-
const [, setVersion] =
|
|
4830
|
-
const bump =
|
|
4831
|
-
const commit =
|
|
5390
|
+
const dataRef = useRef17({});
|
|
5391
|
+
const [, setVersion] = useState21(0);
|
|
5392
|
+
const bump = useCallback14(() => setVersion((v) => v + 1), []);
|
|
5393
|
+
const commit = useCallback14(
|
|
4832
5394
|
(trackId, next, notify) => {
|
|
4833
5395
|
dataRef.current = { ...dataRef.current, [trackId]: next };
|
|
4834
5396
|
bump();
|
|
@@ -4836,7 +5398,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
4836
5398
|
},
|
|
4837
5399
|
[bump]
|
|
4838
5400
|
);
|
|
4839
|
-
const record =
|
|
5401
|
+
const record = useCallback14(
|
|
4840
5402
|
(trackId, descriptor, label) => {
|
|
4841
5403
|
const h = dataRef.current[trackId];
|
|
4842
5404
|
const current = h && h.cursor >= 0 ? h.entries[h.cursor] : void 0;
|
|
@@ -4851,7 +5413,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
4851
5413
|
},
|
|
4852
5414
|
[max, commit]
|
|
4853
5415
|
);
|
|
4854
|
-
const restoreTo =
|
|
5416
|
+
const restoreTo = useCallback14(
|
|
4855
5417
|
async (trackId, index) => {
|
|
4856
5418
|
const h = dataRef.current[trackId];
|
|
4857
5419
|
if (!h || index < 0 || index >= h.entries.length || index === h.cursor) return false;
|
|
@@ -4861,7 +5423,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
4861
5423
|
},
|
|
4862
5424
|
[commit]
|
|
4863
5425
|
);
|
|
4864
|
-
const undo =
|
|
5426
|
+
const undo = useCallback14(
|
|
4865
5427
|
(trackId) => {
|
|
4866
5428
|
const h = dataRef.current[trackId];
|
|
4867
5429
|
if (!h || h.cursor <= 0) return Promise.resolve(false);
|
|
@@ -4869,7 +5431,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
4869
5431
|
},
|
|
4870
5432
|
[restoreTo]
|
|
4871
5433
|
);
|
|
4872
|
-
const toggleFavorite =
|
|
5434
|
+
const toggleFavorite = useCallback14(
|
|
4873
5435
|
(trackId, index) => {
|
|
4874
5436
|
const h = dataRef.current[trackId];
|
|
4875
5437
|
if (!h || index < 0 || index >= h.entries.length) return;
|
|
@@ -4878,7 +5440,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
4878
5440
|
},
|
|
4879
5441
|
[commit]
|
|
4880
5442
|
);
|
|
4881
|
-
const restore =
|
|
5443
|
+
const restore = useCallback14(
|
|
4882
5444
|
(trackId, state) => {
|
|
4883
5445
|
const entries = Array.isArray(state?.entries) ? [...state.entries] : [];
|
|
4884
5446
|
const raw = typeof state?.cursor === "number" ? state.cursor : entries.length - 1;
|
|
@@ -4887,15 +5449,15 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
4887
5449
|
},
|
|
4888
5450
|
[commit]
|
|
4889
5451
|
);
|
|
4890
|
-
const list =
|
|
5452
|
+
const list = useCallback14(
|
|
4891
5453
|
(trackId) => dataRef.current[trackId] ?? EMPTY,
|
|
4892
5454
|
[]
|
|
4893
5455
|
);
|
|
4894
|
-
const canUndo =
|
|
5456
|
+
const canUndo = useCallback14((trackId) => {
|
|
4895
5457
|
const h = dataRef.current[trackId];
|
|
4896
5458
|
return !!h && h.cursor > 0;
|
|
4897
5459
|
}, []);
|
|
4898
|
-
const clear =
|
|
5460
|
+
const clear = useCallback14(
|
|
4899
5461
|
(trackId) => {
|
|
4900
5462
|
if (dataRef.current[trackId]) {
|
|
4901
5463
|
const next = { ...dataRef.current };
|
|
@@ -4907,11 +5469,11 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
4907
5469
|
},
|
|
4908
5470
|
[bump]
|
|
4909
5471
|
);
|
|
4910
|
-
const reset =
|
|
5472
|
+
const reset = useCallback14(() => {
|
|
4911
5473
|
dataRef.current = {};
|
|
4912
5474
|
bump();
|
|
4913
5475
|
}, [bump]);
|
|
4914
|
-
return
|
|
5476
|
+
return useMemo9(
|
|
4915
5477
|
() => ({ record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite }),
|
|
4916
5478
|
[record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite]
|
|
4917
5479
|
);
|
|
@@ -5044,7 +5606,7 @@ function resolveTrackGroups(parsedGroups, tracks, getDbId, opts = {}) {
|
|
|
5044
5606
|
}
|
|
5045
5607
|
|
|
5046
5608
|
// src/panel-core/useTransitionOps.ts
|
|
5047
|
-
import { useCallback as
|
|
5609
|
+
import { useCallback as useCallback15, useEffect as useEffect17, useRef as useRef18, useState as useState22 } from "react";
|
|
5048
5610
|
function useTransitionOps({
|
|
5049
5611
|
host,
|
|
5050
5612
|
adapter,
|
|
@@ -5061,8 +5623,8 @@ function useTransitionOps({
|
|
|
5061
5623
|
resolvedFades
|
|
5062
5624
|
}) {
|
|
5063
5625
|
const { identity } = adapter;
|
|
5064
|
-
const appliedFadeAutomationRef =
|
|
5065
|
-
const applyCrossfadeAutomation =
|
|
5626
|
+
const appliedFadeAutomationRef = useRef18(/* @__PURE__ */ new Set());
|
|
5627
|
+
const applyCrossfadeAutomation = useCallback15(
|
|
5066
5628
|
async (originTrackId, targetTrackId, bars, bpm, sliderPos) => {
|
|
5067
5629
|
if (host.setTrackVolumeAutomation) {
|
|
5068
5630
|
const curves = buildCrossfadeVolumeCurves(bars, bpm, sliderPos);
|
|
@@ -5079,7 +5641,7 @@ function useTransitionOps({
|
|
|
5079
5641
|
},
|
|
5080
5642
|
[host]
|
|
5081
5643
|
);
|
|
5082
|
-
const applyFadeAutomation =
|
|
5644
|
+
const applyFadeAutomation = useCallback15(
|
|
5083
5645
|
async (trackId, direction, bars, bpm, sliderPos, gesture) => {
|
|
5084
5646
|
if (!host.setTrackVolumeAutomation) return;
|
|
5085
5647
|
const points = buildFadeVolumeCurve(bars, bpm, direction, sliderPos, gesture);
|
|
@@ -5088,8 +5650,8 @@ function useTransitionOps({
|
|
|
5088
5650
|
},
|
|
5089
5651
|
[host]
|
|
5090
5652
|
);
|
|
5091
|
-
const [isCreatingCrossfade, setIsCreatingCrossfade] =
|
|
5092
|
-
const handleCreateCrossfade =
|
|
5653
|
+
const [isCreatingCrossfade, setIsCreatingCrossfade] = useState22(false);
|
|
5654
|
+
const handleCreateCrossfade = useCallback15(
|
|
5093
5655
|
async (origin, target) => {
|
|
5094
5656
|
const scene = activeSceneId;
|
|
5095
5657
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -5217,8 +5779,8 @@ function useTransitionOps({
|
|
|
5217
5779
|
loadTracks
|
|
5218
5780
|
]
|
|
5219
5781
|
);
|
|
5220
|
-
const [isCreatingFade, setIsCreatingFade] =
|
|
5221
|
-
const handleCreateFade =
|
|
5782
|
+
const [isCreatingFade, setIsCreatingFade] = useState22(false);
|
|
5783
|
+
const handleCreateFade = useCallback15(
|
|
5222
5784
|
async (selection, direction, gesture) => {
|
|
5223
5785
|
const scene = activeSceneId;
|
|
5224
5786
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -5328,7 +5890,7 @@ function useTransitionOps({
|
|
|
5328
5890
|
loadTracks
|
|
5329
5891
|
]
|
|
5330
5892
|
);
|
|
5331
|
-
const handleCrossfadeMute =
|
|
5893
|
+
const handleCrossfadeMute = useCallback15(
|
|
5332
5894
|
(pair) => {
|
|
5333
5895
|
const newMuted = !pair.origin.runtimeState.muted;
|
|
5334
5896
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -5343,7 +5905,7 @@ function useTransitionOps({
|
|
|
5343
5905
|
},
|
|
5344
5906
|
[host, setTracks]
|
|
5345
5907
|
);
|
|
5346
|
-
const handleCrossfadeSolo =
|
|
5908
|
+
const handleCrossfadeSolo = useCallback15(
|
|
5347
5909
|
(pair) => {
|
|
5348
5910
|
const newSolo = !pair.origin.runtimeState.solo;
|
|
5349
5911
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -5358,7 +5920,7 @@ function useTransitionOps({
|
|
|
5358
5920
|
},
|
|
5359
5921
|
[host, setTracks]
|
|
5360
5922
|
);
|
|
5361
|
-
const handleCrossfadeDelete =
|
|
5923
|
+
const handleCrossfadeDelete = useCallback15(
|
|
5362
5924
|
async (pair) => {
|
|
5363
5925
|
try {
|
|
5364
5926
|
for (const member of [pair.origin, pair.target]) {
|
|
@@ -5384,8 +5946,8 @@ function useTransitionOps({
|
|
|
5384
5946
|
},
|
|
5385
5947
|
[host, activeSceneId, setCrossfadePairsMeta, setTracks]
|
|
5386
5948
|
);
|
|
5387
|
-
const crossfadeSliderTimers =
|
|
5388
|
-
const handleCrossfadeSlider =
|
|
5949
|
+
const crossfadeSliderTimers = useRef18({});
|
|
5950
|
+
const handleCrossfadeSlider = useCallback15(
|
|
5389
5951
|
(pair, pos) => {
|
|
5390
5952
|
setCrossfadePairsMeta(
|
|
5391
5953
|
(prev) => prev.map((p) => p.groupId === pair.groupId ? { ...p, sliderPos: pos } : p)
|
|
@@ -5418,7 +5980,7 @@ function useTransitionOps({
|
|
|
5418
5980
|
},
|
|
5419
5981
|
[host, activeSceneId, applyCrossfadeAutomation, setCrossfadePairsMeta]
|
|
5420
5982
|
);
|
|
5421
|
-
const handleFadeDelete =
|
|
5983
|
+
const handleFadeDelete = useCallback15(
|
|
5422
5984
|
async (fade) => {
|
|
5423
5985
|
try {
|
|
5424
5986
|
await host.deleteTrack(fade.track.handle.id);
|
|
@@ -5438,8 +6000,8 @@ function useTransitionOps({
|
|
|
5438
6000
|
},
|
|
5439
6001
|
[host, activeSceneId, setFadesMeta, setTracks]
|
|
5440
6002
|
);
|
|
5441
|
-
const fadeSliderTimers =
|
|
5442
|
-
const handleFadeSlider =
|
|
6003
|
+
const fadeSliderTimers = useRef18({});
|
|
6004
|
+
const handleFadeSlider = useCallback15(
|
|
5443
6005
|
(fade, pos) => {
|
|
5444
6006
|
setFadesMeta(
|
|
5445
6007
|
(prev) => prev.map((f) => f.dbId === fade.dbId ? { ...f, meta: { ...f.meta, sliderPos: pos } } : f)
|
|
@@ -5469,8 +6031,8 @@ function useTransitionOps({
|
|
|
5469
6031
|
},
|
|
5470
6032
|
[host, activeSceneId, applyFadeAutomation, setFadesMeta]
|
|
5471
6033
|
);
|
|
5472
|
-
const lastResyncKeyRef =
|
|
5473
|
-
|
|
6034
|
+
const lastResyncKeyRef = useRef18("");
|
|
6035
|
+
useEffect17(() => {
|
|
5474
6036
|
if (!host.getTrackSound || resolvedCrossfadePairs.length === 0 && resolvedFades.length === 0) {
|
|
5475
6037
|
return;
|
|
5476
6038
|
}
|
|
@@ -5511,7 +6073,7 @@ function useTransitionOps({
|
|
|
5511
6073
|
cancelled = true;
|
|
5512
6074
|
};
|
|
5513
6075
|
}, [resolvedCrossfadePairs, resolvedFades, host, adapter]);
|
|
5514
|
-
|
|
6076
|
+
useEffect17(() => {
|
|
5515
6077
|
if (!host.setTrackVolumeAutomation || resolvedFades.length === 0) return;
|
|
5516
6078
|
void (async () => {
|
|
5517
6079
|
const mc = await host.getMusicalContext();
|
|
@@ -5545,7 +6107,7 @@ function useTransitionOps({
|
|
|
5545
6107
|
}
|
|
5546
6108
|
|
|
5547
6109
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
5548
|
-
import { jsx as
|
|
6110
|
+
import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
5549
6111
|
var EMPTY_PLACEHOLDERS = [];
|
|
5550
6112
|
function useGeneratorPanelCore({
|
|
5551
6113
|
ui,
|
|
@@ -5565,8 +6127,8 @@ function useGeneratorPanelCore({
|
|
|
5565
6127
|
} = ui;
|
|
5566
6128
|
const { identity, features } = adapter;
|
|
5567
6129
|
const logTag = identity.logTag;
|
|
5568
|
-
const adapterRef =
|
|
5569
|
-
|
|
6130
|
+
const adapterRef = useRef19(adapter);
|
|
6131
|
+
useEffect18(() => {
|
|
5570
6132
|
if (adapterRef.current !== adapter) {
|
|
5571
6133
|
adapterRef.current = adapter;
|
|
5572
6134
|
console.warn(
|
|
@@ -5576,27 +6138,27 @@ function useGeneratorPanelCore({
|
|
|
5576
6138
|
}, [adapter, logTag]);
|
|
5577
6139
|
const supportsMeters = typeof host.getTrackLevels === "function";
|
|
5578
6140
|
const trackLevels = useTrackLevels(host, isExpanded);
|
|
5579
|
-
const [tracks, setTracks] =
|
|
5580
|
-
const [isLoadingTracks, setIsLoadingTracks] =
|
|
5581
|
-
const [importOpen, setImportOpen] =
|
|
5582
|
-
const [soundImportTarget, setSoundImportTarget] =
|
|
5583
|
-
const [designerView, setDesignerView] =
|
|
5584
|
-
const [transitionSourceTotal, setTransitionSourceTotal] =
|
|
5585
|
-
const [crossfadePairsMeta, setCrossfadePairsMeta] =
|
|
5586
|
-
const [fadesMeta, setFadesMeta] =
|
|
5587
|
-
const [genericGroupMetas, setGenericGroupMetas] =
|
|
6141
|
+
const [tracks, setTracks] = useState23([]);
|
|
6142
|
+
const [isLoadingTracks, setIsLoadingTracks] = useState23(false);
|
|
6143
|
+
const [importOpen, setImportOpen] = useState23(false);
|
|
6144
|
+
const [soundImportTarget, setSoundImportTarget] = useState23(null);
|
|
6145
|
+
const [designerView, setDesignerView] = useState23(false);
|
|
6146
|
+
const [transitionSourceTotal, setTransitionSourceTotal] = useState23(0);
|
|
6147
|
+
const [crossfadePairsMeta, setCrossfadePairsMeta] = useState23([]);
|
|
6148
|
+
const [fadesMeta, setFadesMeta] = useState23([]);
|
|
6149
|
+
const [genericGroupMetas, setGenericGroupMetas] = useState23({});
|
|
5588
6150
|
const [isComposing, , setIsComposingForScene] = useSceneState(activeSceneId, false);
|
|
5589
6151
|
const [placeholders, , setPlaceholdersForScene] = useSceneState(
|
|
5590
6152
|
activeSceneId,
|
|
5591
6153
|
EMPTY_PLACEHOLDERS
|
|
5592
6154
|
);
|
|
5593
|
-
const saveTimeoutRefs =
|
|
5594
|
-
const editLoadStartedRef =
|
|
5595
|
-
const [availableInstruments, setAvailableInstruments] =
|
|
5596
|
-
const [instrumentsLoading, setInstrumentsLoading] =
|
|
5597
|
-
const engineToDbIdRef =
|
|
5598
|
-
const tracksLoadedForSceneRef =
|
|
5599
|
-
const persistSoundHistory =
|
|
6155
|
+
const saveTimeoutRefs = useRef19({});
|
|
6156
|
+
const editLoadStartedRef = useRef19(/* @__PURE__ */ new Set());
|
|
6157
|
+
const [availableInstruments, setAvailableInstruments] = useState23([]);
|
|
6158
|
+
const [instrumentsLoading, setInstrumentsLoading] = useState23(false);
|
|
6159
|
+
const engineToDbIdRef = useRef19(/* @__PURE__ */ new Map());
|
|
6160
|
+
const tracksLoadedForSceneRef = useRef19(null);
|
|
6161
|
+
const persistSoundHistory = useCallback16(
|
|
5600
6162
|
(trackId, state) => {
|
|
5601
6163
|
if (!activeSceneId) return;
|
|
5602
6164
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -5616,7 +6178,7 @@ function useGeneratorPanelCore({
|
|
|
5616
6178
|
setItems: setTracks,
|
|
5617
6179
|
getId: (t) => t.handle.dbId
|
|
5618
6180
|
});
|
|
5619
|
-
const loadTracks =
|
|
6181
|
+
const loadTracks = useCallback16(
|
|
5620
6182
|
async (incremental = false) => {
|
|
5621
6183
|
const sceneAtStart = activeSceneId;
|
|
5622
6184
|
if (!sceneAtStart) {
|
|
@@ -5741,18 +6303,18 @@ function useGeneratorPanelCore({
|
|
|
5741
6303
|
},
|
|
5742
6304
|
[host, activeSceneId, soundHistory, adapter, logTag]
|
|
5743
6305
|
);
|
|
5744
|
-
|
|
6306
|
+
useEffect18(() => {
|
|
5745
6307
|
loadTracks();
|
|
5746
6308
|
}, [loadTracks]);
|
|
5747
|
-
|
|
6309
|
+
useEffect18(() => {
|
|
5748
6310
|
const map = /* @__PURE__ */ new Map();
|
|
5749
6311
|
for (const t of tracks) {
|
|
5750
6312
|
map.set(t.handle.id, t.handle.dbId);
|
|
5751
6313
|
}
|
|
5752
6314
|
engineToDbIdRef.current = map;
|
|
5753
6315
|
}, [tracks]);
|
|
5754
|
-
const loadedCompletedIdsRef =
|
|
5755
|
-
|
|
6316
|
+
const loadedCompletedIdsRef = useRef19(/* @__PURE__ */ new Set());
|
|
6317
|
+
useEffect18(() => {
|
|
5756
6318
|
if (placeholders.length === 0) {
|
|
5757
6319
|
loadedCompletedIdsRef.current.clear();
|
|
5758
6320
|
return;
|
|
@@ -5771,16 +6333,16 @@ function useGeneratorPanelCore({
|
|
|
5771
6333
|
loadTracks(true);
|
|
5772
6334
|
}
|
|
5773
6335
|
}, [placeholders, loadTracks, logTag]);
|
|
5774
|
-
const adoptAndLoad =
|
|
6336
|
+
const adoptAndLoad = useCallback16(() => {
|
|
5775
6337
|
loadTracks(true);
|
|
5776
6338
|
}, [loadTracks]);
|
|
5777
|
-
|
|
6339
|
+
useEffect18(() => {
|
|
5778
6340
|
const unsub = host.onEngineReady(() => {
|
|
5779
6341
|
adoptAndLoad();
|
|
5780
6342
|
});
|
|
5781
6343
|
return unsub;
|
|
5782
6344
|
}, [host, adoptAndLoad]);
|
|
5783
|
-
|
|
6345
|
+
useEffect18(() => {
|
|
5784
6346
|
if (typeof host.onAfterAgentMutation !== "function") return;
|
|
5785
6347
|
let timer = null;
|
|
5786
6348
|
const unsub = host.onAfterAgentMutation(() => {
|
|
@@ -5795,13 +6357,13 @@ function useGeneratorPanelCore({
|
|
|
5795
6357
|
if (timer) clearTimeout(timer);
|
|
5796
6358
|
};
|
|
5797
6359
|
}, [host, loadTracks]);
|
|
5798
|
-
|
|
6360
|
+
useEffect18(() => {
|
|
5799
6361
|
const unsub = host.onTrackStateChange((trackId, state) => {
|
|
5800
6362
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: state } : t));
|
|
5801
6363
|
});
|
|
5802
6364
|
return unsub;
|
|
5803
6365
|
}, [host]);
|
|
5804
|
-
|
|
6366
|
+
useEffect18(() => {
|
|
5805
6367
|
if (!features.bulkComposePlaceholders) return;
|
|
5806
6368
|
console.log(`[${logTag}] Subscribing to composeProgress`);
|
|
5807
6369
|
const unsub = host.onComposeProgress((event) => {
|
|
@@ -5835,7 +6397,7 @@ function useGeneratorPanelCore({
|
|
|
5835
6397
|
});
|
|
5836
6398
|
return unsub;
|
|
5837
6399
|
}, [host, setIsComposingForScene, setPlaceholdersForScene, features.bulkComposePlaceholders, logTag]);
|
|
5838
|
-
|
|
6400
|
+
useEffect18(() => {
|
|
5839
6401
|
const refs = saveTimeoutRefs;
|
|
5840
6402
|
return () => {
|
|
5841
6403
|
for (const timeout of Object.values(refs.current)) {
|
|
@@ -5843,9 +6405,9 @@ function useGeneratorPanelCore({
|
|
|
5843
6405
|
}
|
|
5844
6406
|
};
|
|
5845
6407
|
}, []);
|
|
5846
|
-
const isAddingTrackRef =
|
|
5847
|
-
const [isAddingTrack, setIsAddingTrack] =
|
|
5848
|
-
const handleAddTrack =
|
|
6408
|
+
const isAddingTrackRef = useRef19(false);
|
|
6409
|
+
const [isAddingTrack, setIsAddingTrack] = useState23(false);
|
|
6410
|
+
const handleAddTrack = useCallback16(async () => {
|
|
5849
6411
|
if (isAddingTrackRef.current) return;
|
|
5850
6412
|
if (!activeSceneId) {
|
|
5851
6413
|
host.showToast("warning", "Select SCENE");
|
|
@@ -5885,7 +6447,7 @@ function useGeneratorPanelCore({
|
|
|
5885
6447
|
setIsAddingTrack(false);
|
|
5886
6448
|
}
|
|
5887
6449
|
}, [host, adapter, identity, activeSceneId, isConnected, isAuthenticated, tracks.length, onExpandSelf]);
|
|
5888
|
-
const handlePortTrack =
|
|
6450
|
+
const handlePortTrack = useCallback16(
|
|
5889
6451
|
async (sel) => {
|
|
5890
6452
|
if (!activeSceneId) {
|
|
5891
6453
|
host.showToast("warning", "Select SCENE");
|
|
@@ -5942,7 +6504,7 @@ function useGeneratorPanelCore({
|
|
|
5942
6504
|
},
|
|
5943
6505
|
[host, adapter, identity, activeSceneId, isConnected, tracks.length, loadTracks]
|
|
5944
6506
|
);
|
|
5945
|
-
const handleSoundImportPick =
|
|
6507
|
+
const handleSoundImportPick = useCallback16(
|
|
5946
6508
|
async (sel) => {
|
|
5947
6509
|
const target = soundImportTarget;
|
|
5948
6510
|
if (!target || !host.getTrackSound) {
|
|
@@ -5973,8 +6535,8 @@ function useGeneratorPanelCore({
|
|
|
5973
6535
|
},
|
|
5974
6536
|
[soundImportTarget, host, adapter, identity.familyKey, soundHistory]
|
|
5975
6537
|
);
|
|
5976
|
-
const [isExportingMidi, setIsExportingMidi] =
|
|
5977
|
-
const handleExportMidi =
|
|
6538
|
+
const [isExportingMidi, setIsExportingMidi] = useState23(false);
|
|
6539
|
+
const handleExportMidi = useCallback16(async () => {
|
|
5978
6540
|
if (isExportingMidi) return;
|
|
5979
6541
|
setIsExportingMidi(true);
|
|
5980
6542
|
try {
|
|
@@ -6005,10 +6567,10 @@ function useGeneratorPanelCore({
|
|
|
6005
6567
|
const xfFromId = sceneContext?.transitionFromSceneId ?? null;
|
|
6006
6568
|
const xfToId = sceneContext?.transitionToSceneId ?? null;
|
|
6007
6569
|
const canCrossfade = features.transitionDesigner && sceneContext?.sceneType === "transition" && !!xfFromId && !!xfToId && !!host.listSceneFamilyTracks;
|
|
6008
|
-
|
|
6570
|
+
useEffect18(() => {
|
|
6009
6571
|
if (!canCrossfade) setDesignerView(false);
|
|
6010
6572
|
}, [canCrossfade]);
|
|
6011
|
-
|
|
6573
|
+
useEffect18(() => {
|
|
6012
6574
|
if (!canCrossfade || !xfFromId || !xfToId || !host.listSceneFamilyTracks) {
|
|
6013
6575
|
setTransitionSourceTotal(0);
|
|
6014
6576
|
return;
|
|
@@ -6024,12 +6586,12 @@ function useGeneratorPanelCore({
|
|
|
6024
6586
|
};
|
|
6025
6587
|
}, [canCrossfade, xfFromId, xfToId, host]);
|
|
6026
6588
|
const transitionDone = crossfadePairsMeta.length * 2 + fadesMeta.length;
|
|
6027
|
-
|
|
6589
|
+
useEffect18(() => {
|
|
6028
6590
|
if (!onHeaderContent) return;
|
|
6029
6591
|
const addDisabled = needsContract || !isConnected || !activeSceneId || tracks.length >= identity.maxTracks || isAddingTrack;
|
|
6030
6592
|
onHeaderContent(
|
|
6031
|
-
/* @__PURE__ */
|
|
6032
|
-
features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */
|
|
6593
|
+
/* @__PURE__ */ jsxs19("div", { className: "flex gap-1 items-center", children: [
|
|
6594
|
+
features.importTracks && (!canCrossfade || !designerView) && host.listImportableTracks && /* @__PURE__ */ jsx25(
|
|
6033
6595
|
"button",
|
|
6034
6596
|
{
|
|
6035
6597
|
"data-testid": `import-from-scene-${identity.familyKey}-button`,
|
|
@@ -6043,7 +6605,7 @@ function useGeneratorPanelCore({
|
|
|
6043
6605
|
children: identity.importTrackLabel ?? "Import Track"
|
|
6044
6606
|
}
|
|
6045
6607
|
),
|
|
6046
|
-
(!canCrossfade || !designerView) && /* @__PURE__ */
|
|
6608
|
+
(!canCrossfade || !designerView) && /* @__PURE__ */ jsx25(
|
|
6047
6609
|
"button",
|
|
6048
6610
|
{
|
|
6049
6611
|
"data-testid": `add-${identity.familyKey}-track-button`,
|
|
@@ -6059,7 +6621,7 @@ function useGeneratorPanelCore({
|
|
|
6059
6621
|
children: identity.addTrackLabel ?? "Add Track"
|
|
6060
6622
|
}
|
|
6061
6623
|
),
|
|
6062
|
-
canCrossfade && /* @__PURE__ */
|
|
6624
|
+
canCrossfade && /* @__PURE__ */ jsxs19(
|
|
6063
6625
|
"button",
|
|
6064
6626
|
{
|
|
6065
6627
|
"data-testid": `${identity.familyKey}-view-toggle`,
|
|
@@ -6078,7 +6640,7 @@ function useGeneratorPanelCore({
|
|
|
6078
6640
|
title: designerView ? "Back to the track list" : "Open the transition designer",
|
|
6079
6641
|
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",
|
|
6080
6642
|
children: [
|
|
6081
|
-
transitionSourceTotal > 0 && /* @__PURE__ */
|
|
6643
|
+
transitionSourceTotal > 0 && /* @__PURE__ */ jsx25(
|
|
6082
6644
|
"span",
|
|
6083
6645
|
{
|
|
6084
6646
|
className: "absolute inset-y-0 left-0 bg-sas-accent/25",
|
|
@@ -6086,7 +6648,7 @@ function useGeneratorPanelCore({
|
|
|
6086
6648
|
"aria-hidden": true
|
|
6087
6649
|
}
|
|
6088
6650
|
),
|
|
6089
|
-
/* @__PURE__ */
|
|
6651
|
+
/* @__PURE__ */ jsxs19("span", { className: "relative", children: [
|
|
6090
6652
|
"\u21C4 ",
|
|
6091
6653
|
designerView ? "Transition" : "Tracks",
|
|
6092
6654
|
transitionSourceTotal > 0 ? ` ${transitionDone}/${transitionSourceTotal}` : ""
|
|
@@ -6117,7 +6679,7 @@ function useGeneratorPanelCore({
|
|
|
6117
6679
|
identity,
|
|
6118
6680
|
features.importTracks
|
|
6119
6681
|
]);
|
|
6120
|
-
|
|
6682
|
+
useEffect18(() => {
|
|
6121
6683
|
if (!onLoading) return;
|
|
6122
6684
|
const anyGenerating = tracks.some((t) => t.isGenerating);
|
|
6123
6685
|
onLoading(isLoadingTracks || anyGenerating || isBulkActive);
|
|
@@ -6125,7 +6687,7 @@ function useGeneratorPanelCore({
|
|
|
6125
6687
|
onLoading(false);
|
|
6126
6688
|
};
|
|
6127
6689
|
}, [onLoading, isLoadingTracks, tracks, isBulkActive]);
|
|
6128
|
-
const handleDeleteTrack =
|
|
6690
|
+
const handleDeleteTrack = useCallback16(
|
|
6129
6691
|
async (trackId) => {
|
|
6130
6692
|
try {
|
|
6131
6693
|
await host.deleteTrack(trackId);
|
|
@@ -6141,7 +6703,7 @@ function useGeneratorPanelCore({
|
|
|
6141
6703
|
},
|
|
6142
6704
|
[host, activeSceneId]
|
|
6143
6705
|
);
|
|
6144
|
-
const handlePromptChange =
|
|
6706
|
+
const handlePromptChange = useCallback16(
|
|
6145
6707
|
(trackId, prompt) => {
|
|
6146
6708
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, prompt } : t));
|
|
6147
6709
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -6157,7 +6719,7 @@ function useGeneratorPanelCore({
|
|
|
6157
6719
|
},
|
|
6158
6720
|
[host, activeSceneId]
|
|
6159
6721
|
);
|
|
6160
|
-
const resolvedGenericGroups =
|
|
6722
|
+
const resolvedGenericGroups = useMemo10(() => {
|
|
6161
6723
|
const out = {};
|
|
6162
6724
|
for (const ext of adapter.groupExtensions ?? []) {
|
|
6163
6725
|
out[ext.metaKey] = resolveTrackGroups(
|
|
@@ -6171,18 +6733,18 @@ function useGeneratorPanelCore({
|
|
|
6171
6733
|
}
|
|
6172
6734
|
return out;
|
|
6173
6735
|
}, [adapter, genericGroupMetas, tracks]);
|
|
6174
|
-
const genericGroupMemberDbIds =
|
|
6736
|
+
const genericGroupMemberDbIds = useMemo10(() => {
|
|
6175
6737
|
const s = /* @__PURE__ */ new Set();
|
|
6176
6738
|
for (const r of Object.values(resolvedGenericGroups)) {
|
|
6177
6739
|
for (const dbId of r.memberDbIds) s.add(dbId);
|
|
6178
6740
|
}
|
|
6179
6741
|
return s;
|
|
6180
6742
|
}, [resolvedGenericGroups]);
|
|
6181
|
-
const engineToDbId =
|
|
6743
|
+
const engineToDbId = useCallback16(
|
|
6182
6744
|
(trackId) => engineToDbIdRef.current.get(trackId) ?? trackId,
|
|
6183
6745
|
[]
|
|
6184
6746
|
);
|
|
6185
|
-
const updateTrack =
|
|
6747
|
+
const updateTrack = useCallback16(
|
|
6186
6748
|
(trackId, patch) => {
|
|
6187
6749
|
setTracks(
|
|
6188
6750
|
(prev) => prev.map(
|
|
@@ -6192,18 +6754,18 @@ function useGeneratorPanelCore({
|
|
|
6192
6754
|
},
|
|
6193
6755
|
[]
|
|
6194
6756
|
);
|
|
6195
|
-
const markEditLoaded =
|
|
6757
|
+
const markEditLoaded = useCallback16((trackId) => {
|
|
6196
6758
|
editLoadStartedRef.current.add(trackId);
|
|
6197
6759
|
}, []);
|
|
6198
|
-
const tracksRef =
|
|
6199
|
-
|
|
6760
|
+
const tracksRef = useRef19(tracks);
|
|
6761
|
+
useEffect18(() => {
|
|
6200
6762
|
tracksRef.current = tracks;
|
|
6201
6763
|
}, [tracks]);
|
|
6202
|
-
const resolvedGenericGroupsRef =
|
|
6203
|
-
|
|
6764
|
+
const resolvedGenericGroupsRef = useRef19(resolvedGenericGroups);
|
|
6765
|
+
useEffect18(() => {
|
|
6204
6766
|
resolvedGenericGroupsRef.current = resolvedGenericGroups;
|
|
6205
6767
|
}, [resolvedGenericGroups]);
|
|
6206
|
-
const makeServices =
|
|
6768
|
+
const makeServices = useCallback16(() => {
|
|
6207
6769
|
return {
|
|
6208
6770
|
host,
|
|
6209
6771
|
activeSceneId,
|
|
@@ -6222,7 +6784,7 @@ function useGeneratorPanelCore({
|
|
|
6222
6784
|
resolvedGroups: (metaKey) => resolvedGenericGroupsRef.current[metaKey]?.resolved ?? []
|
|
6223
6785
|
};
|
|
6224
6786
|
}, [host, activeSceneId, updateTrack, loadTracks, soundHistory, engineToDbId, markEditLoaded, identity, adapter]);
|
|
6225
|
-
const handleGenerate =
|
|
6787
|
+
const handleGenerate = useCallback16(
|
|
6226
6788
|
async (trackId) => {
|
|
6227
6789
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6228
6790
|
if (!track || !track.prompt.trim()) return;
|
|
@@ -6249,7 +6811,7 @@ function useGeneratorPanelCore({
|
|
|
6249
6811
|
},
|
|
6250
6812
|
[host, adapter, tracks, isAuthenticated, makeServices]
|
|
6251
6813
|
);
|
|
6252
|
-
const handleMuteToggle =
|
|
6814
|
+
const handleMuteToggle = useCallback16(
|
|
6253
6815
|
(trackId) => {
|
|
6254
6816
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6255
6817
|
if (!track) return;
|
|
@@ -6269,7 +6831,7 @@ function useGeneratorPanelCore({
|
|
|
6269
6831
|
},
|
|
6270
6832
|
[host, tracks]
|
|
6271
6833
|
);
|
|
6272
|
-
const handleSoloToggle =
|
|
6834
|
+
const handleSoloToggle = useCallback16(
|
|
6273
6835
|
(trackId) => {
|
|
6274
6836
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6275
6837
|
if (!track) return;
|
|
@@ -6289,7 +6851,7 @@ function useGeneratorPanelCore({
|
|
|
6289
6851
|
},
|
|
6290
6852
|
[host, tracks]
|
|
6291
6853
|
);
|
|
6292
|
-
const handleVolumeChange =
|
|
6854
|
+
const handleVolumeChange = useCallback16(
|
|
6293
6855
|
(trackId, volume) => {
|
|
6294
6856
|
setTracks(
|
|
6295
6857
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, volume } } : t)
|
|
@@ -6299,7 +6861,7 @@ function useGeneratorPanelCore({
|
|
|
6299
6861
|
},
|
|
6300
6862
|
[host]
|
|
6301
6863
|
);
|
|
6302
|
-
const handlePanChange =
|
|
6864
|
+
const handlePanChange = useCallback16(
|
|
6303
6865
|
(trackId, pan) => {
|
|
6304
6866
|
setTracks(
|
|
6305
6867
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, pan } } : t)
|
|
@@ -6309,7 +6871,7 @@ function useGeneratorPanelCore({
|
|
|
6309
6871
|
},
|
|
6310
6872
|
[host]
|
|
6311
6873
|
);
|
|
6312
|
-
const handleShuffle =
|
|
6874
|
+
const handleShuffle = useCallback16(
|
|
6313
6875
|
async (trackId) => {
|
|
6314
6876
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6315
6877
|
if (!track) return;
|
|
@@ -6351,7 +6913,7 @@ function useGeneratorPanelCore({
|
|
|
6351
6913
|
},
|
|
6352
6914
|
[host, adapter, tracks, soundHistory, logTag]
|
|
6353
6915
|
);
|
|
6354
|
-
const handleCopy =
|
|
6916
|
+
const handleCopy = useCallback16(
|
|
6355
6917
|
async (trackId) => {
|
|
6356
6918
|
try {
|
|
6357
6919
|
const newHandle = await host.duplicateTrack(trackId);
|
|
@@ -6364,7 +6926,7 @@ function useGeneratorPanelCore({
|
|
|
6364
6926
|
},
|
|
6365
6927
|
[host, loadTracks]
|
|
6366
6928
|
);
|
|
6367
|
-
const handleFxToggle =
|
|
6929
|
+
const handleFxToggle = useCallback16(
|
|
6368
6930
|
(trackId, category, enabled) => {
|
|
6369
6931
|
setTracks(
|
|
6370
6932
|
(prev) => prev.map(
|
|
@@ -6387,7 +6949,7 @@ function useGeneratorPanelCore({
|
|
|
6387
6949
|
},
|
|
6388
6950
|
[host]
|
|
6389
6951
|
);
|
|
6390
|
-
const handleFxPresetChange =
|
|
6952
|
+
const handleFxPresetChange = useCallback16(
|
|
6391
6953
|
(trackId, category, presetIndex) => {
|
|
6392
6954
|
setTracks(
|
|
6393
6955
|
(prev) => prev.map(
|
|
@@ -6413,7 +6975,7 @@ function useGeneratorPanelCore({
|
|
|
6413
6975
|
},
|
|
6414
6976
|
[host]
|
|
6415
6977
|
);
|
|
6416
|
-
const handleFxDryWetChange =
|
|
6978
|
+
const handleFxDryWetChange = useCallback16(
|
|
6417
6979
|
(trackId, category, value) => {
|
|
6418
6980
|
setTracks(
|
|
6419
6981
|
(prev) => prev.map(
|
|
@@ -6425,7 +6987,7 @@ function useGeneratorPanelCore({
|
|
|
6425
6987
|
},
|
|
6426
6988
|
[host]
|
|
6427
6989
|
);
|
|
6428
|
-
const toggleFxDrawer =
|
|
6990
|
+
const toggleFxDrawer = useCallback16(
|
|
6429
6991
|
(trackId) => {
|
|
6430
6992
|
setTracks(
|
|
6431
6993
|
(prev) => prev.map((t) => {
|
|
@@ -6447,7 +7009,7 @@ function useGeneratorPanelCore({
|
|
|
6447
7009
|
},
|
|
6448
7010
|
[host, tracks]
|
|
6449
7011
|
);
|
|
6450
|
-
const loadEditNotes =
|
|
7012
|
+
const loadEditNotes = useCallback16(
|
|
6451
7013
|
async (trackId) => {
|
|
6452
7014
|
try {
|
|
6453
7015
|
const mc = await host.getMusicalContext();
|
|
@@ -6465,7 +7027,7 @@ function useGeneratorPanelCore({
|
|
|
6465
7027
|
},
|
|
6466
7028
|
[host, logTag]
|
|
6467
7029
|
);
|
|
6468
|
-
const handleNotesChange =
|
|
7030
|
+
const handleNotesChange = useCallback16(
|
|
6469
7031
|
(trackId, notes) => {
|
|
6470
7032
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editNotes: notes } : t));
|
|
6471
7033
|
const key = `edit:${trackId}`;
|
|
@@ -6495,7 +7057,7 @@ function useGeneratorPanelCore({
|
|
|
6495
7057
|
},
|
|
6496
7058
|
[host]
|
|
6497
7059
|
);
|
|
6498
|
-
const handleTabChange =
|
|
7060
|
+
const handleTabChange = useCallback16(
|
|
6499
7061
|
(trackId, tab) => {
|
|
6500
7062
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, drawerOpen: true, drawerTab: tab } : t));
|
|
6501
7063
|
if (tab === "fx") {
|
|
@@ -6520,10 +7082,10 @@ function useGeneratorPanelCore({
|
|
|
6520
7082
|
},
|
|
6521
7083
|
[host, availableInstruments.length, instrumentsLoading, loadEditNotes]
|
|
6522
7084
|
);
|
|
6523
|
-
const handleProgressChange =
|
|
7085
|
+
const handleProgressChange = useCallback16((trackId, pct) => {
|
|
6524
7086
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, generationProgress: pct } : t));
|
|
6525
7087
|
}, []);
|
|
6526
|
-
const handleToggleDrawer =
|
|
7088
|
+
const handleToggleDrawer = useCallback16((trackId) => {
|
|
6527
7089
|
setTracks(
|
|
6528
7090
|
(prev) => prev.map((t) => {
|
|
6529
7091
|
if (t.handle.id !== trackId) return t;
|
|
@@ -6532,7 +7094,7 @@ function useGeneratorPanelCore({
|
|
|
6532
7094
|
})
|
|
6533
7095
|
);
|
|
6534
7096
|
}, []);
|
|
6535
|
-
const handleInstrumentSelect =
|
|
7097
|
+
const handleInstrumentSelect = useCallback16(
|
|
6536
7098
|
async (trackId, pluginId) => {
|
|
6537
7099
|
const isDefaultInstrument = pluginId === (identity.defaultInstrumentPluginId ?? "Surge XT");
|
|
6538
7100
|
if (isDefaultInstrument) {
|
|
@@ -6585,7 +7147,7 @@ function useGeneratorPanelCore({
|
|
|
6585
7147
|
},
|
|
6586
7148
|
[host, identity.defaultInstrumentPluginId, logTag]
|
|
6587
7149
|
);
|
|
6588
|
-
const handleShowEditor =
|
|
7150
|
+
const handleShowEditor = useCallback16(
|
|
6589
7151
|
async (trackId) => {
|
|
6590
7152
|
try {
|
|
6591
7153
|
await host.showInstrumentEditor(trackId);
|
|
@@ -6596,12 +7158,12 @@ function useGeneratorPanelCore({
|
|
|
6596
7158
|
},
|
|
6597
7159
|
[host]
|
|
6598
7160
|
);
|
|
6599
|
-
const handleBackToInstruments =
|
|
7161
|
+
const handleBackToInstruments = useCallback16((trackId) => {
|
|
6600
7162
|
setTracks(
|
|
6601
7163
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editorStage: false } : t)
|
|
6602
7164
|
);
|
|
6603
7165
|
}, []);
|
|
6604
|
-
const handleRefreshInstruments =
|
|
7166
|
+
const handleRefreshInstruments = useCallback16(() => {
|
|
6605
7167
|
setInstrumentsLoading(true);
|
|
6606
7168
|
host.getAvailableInstruments().then((instruments) => {
|
|
6607
7169
|
setAvailableInstruments(instruments);
|
|
@@ -6610,13 +7172,13 @@ function useGeneratorPanelCore({
|
|
|
6610
7172
|
setInstrumentsLoading(false);
|
|
6611
7173
|
});
|
|
6612
7174
|
}, [host]);
|
|
6613
|
-
const onAuditionNote =
|
|
7175
|
+
const onAuditionNote = useCallback16(
|
|
6614
7176
|
(trackId, pitch, velocity, ms) => {
|
|
6615
7177
|
void host.auditionNote(trackId, pitch, velocity, ms);
|
|
6616
7178
|
},
|
|
6617
7179
|
[host]
|
|
6618
7180
|
);
|
|
6619
|
-
const { resolvedCrossfadePairs, crossfadeMemberDbIds } =
|
|
7181
|
+
const { resolvedCrossfadePairs, crossfadeMemberDbIds } = useMemo10(() => {
|
|
6620
7182
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
6621
7183
|
const pairs = [];
|
|
6622
7184
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -6631,7 +7193,7 @@ function useGeneratorPanelCore({
|
|
|
6631
7193
|
}
|
|
6632
7194
|
return { resolvedCrossfadePairs: pairs, crossfadeMemberDbIds: members };
|
|
6633
7195
|
}, [tracks, crossfadePairsMeta]);
|
|
6634
|
-
const { resolvedFades, fadeMemberDbIds } =
|
|
7196
|
+
const { resolvedFades, fadeMemberDbIds } = useMemo10(() => {
|
|
6635
7197
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
6636
7198
|
const list = [];
|
|
6637
7199
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -6659,7 +7221,7 @@ function useGeneratorPanelCore({
|
|
|
6659
7221
|
resolvedCrossfadePairs,
|
|
6660
7222
|
resolvedFades
|
|
6661
7223
|
});
|
|
6662
|
-
const setGroupMute =
|
|
7224
|
+
const setGroupMute = useCallback16(
|
|
6663
7225
|
(trackIds, muted) => {
|
|
6664
7226
|
for (const id of trackIds) {
|
|
6665
7227
|
setTracks(
|
|
@@ -6671,7 +7233,7 @@ function useGeneratorPanelCore({
|
|
|
6671
7233
|
},
|
|
6672
7234
|
[host]
|
|
6673
7235
|
);
|
|
6674
|
-
const setGroupSolo =
|
|
7236
|
+
const setGroupSolo = useCallback16(
|
|
6675
7237
|
(trackIds, solo) => {
|
|
6676
7238
|
for (const id of trackIds) {
|
|
6677
7239
|
setTracks(
|
|
@@ -6683,7 +7245,7 @@ function useGeneratorPanelCore({
|
|
|
6683
7245
|
},
|
|
6684
7246
|
[host]
|
|
6685
7247
|
);
|
|
6686
|
-
const deleteGroup =
|
|
7248
|
+
const deleteGroup = useCallback16(
|
|
6687
7249
|
async (members, cleanupKeySuffixes) => {
|
|
6688
7250
|
for (const member of members) {
|
|
6689
7251
|
try {
|
|
@@ -6703,7 +7265,7 @@ function useGeneratorPanelCore({
|
|
|
6703
7265
|
},
|
|
6704
7266
|
[host, activeSceneId, loadTracks]
|
|
6705
7267
|
);
|
|
6706
|
-
const handlers =
|
|
7268
|
+
const handlers = useMemo10(
|
|
6707
7269
|
() => ({
|
|
6708
7270
|
promptChange: handlePromptChange,
|
|
6709
7271
|
generate: (trackId) => {
|
|
@@ -6817,8 +7379,8 @@ function useGeneratorPanelCore({
|
|
|
6817
7379
|
}
|
|
6818
7380
|
|
|
6819
7381
|
// src/panel-core/GeneratorPanelShell.tsx
|
|
6820
|
-
import
|
|
6821
|
-
import { Fragment as Fragment6, jsx as
|
|
7382
|
+
import React22, { useCallback as useCallback17 } from "react";
|
|
7383
|
+
import { Fragment as Fragment6, jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
6822
7384
|
function GeneratorPanelShell({ core, slots }) {
|
|
6823
7385
|
const {
|
|
6824
7386
|
ui,
|
|
@@ -6871,8 +7433,9 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
6871
7433
|
deleteGroup
|
|
6872
7434
|
} = core;
|
|
6873
7435
|
const { host, activeSceneId, isAuthenticated, sceneContext, onSelectScene, onOpenContract } = ui;
|
|
7436
|
+
const panelBus = usePanelBus(host, activeSceneId);
|
|
6874
7437
|
const { identity, features } = adapter;
|
|
6875
|
-
const buildRowProps =
|
|
7438
|
+
const buildRowProps = useCallback17(
|
|
6876
7439
|
(track, drag) => {
|
|
6877
7440
|
const id = track.handle.id;
|
|
6878
7441
|
const pickerProps = features.instrumentPicker ? {
|
|
@@ -6924,6 +7487,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
6924
7487
|
onVolumeChange: (vol) => handlers.volumeChange(id, vol),
|
|
6925
7488
|
onPanChange: (pan) => handlers.panChange(id, pan),
|
|
6926
7489
|
onFxToggle: (cat, enabled) => handleFxToggle(id, cat, enabled),
|
|
7490
|
+
externalFxHost: host,
|
|
6927
7491
|
onFxPresetChange: (cat, idx) => handleFxPresetChange(id, cat, idx),
|
|
6928
7492
|
onFxDryWetChange: (cat, val) => handleFxDryWetChange(id, cat, val),
|
|
6929
7493
|
onToggleFxDrawer: () => handlers.toggleFxDrawer(id),
|
|
@@ -6971,12 +7535,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
6971
7535
|
]
|
|
6972
7536
|
);
|
|
6973
7537
|
if (!activeSceneId) {
|
|
6974
|
-
return /* @__PURE__ */
|
|
7538
|
+
return /* @__PURE__ */ jsx26(
|
|
6975
7539
|
"div",
|
|
6976
7540
|
{
|
|
6977
7541
|
"data-testid": `no-scene-placeholder-${identity.familyKey}`,
|
|
6978
7542
|
className: "flex items-center justify-center py-8",
|
|
6979
|
-
children: /* @__PURE__ */
|
|
7543
|
+
children: /* @__PURE__ */ jsx26(
|
|
6980
7544
|
"button",
|
|
6981
7545
|
{
|
|
6982
7546
|
onClick: () => onSelectScene?.(),
|
|
@@ -6988,12 +7552,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
6988
7552
|
);
|
|
6989
7553
|
}
|
|
6990
7554
|
if (!sceneContext?.hasContract) {
|
|
6991
|
-
return /* @__PURE__ */
|
|
7555
|
+
return /* @__PURE__ */ jsx26(
|
|
6992
7556
|
"div",
|
|
6993
7557
|
{
|
|
6994
7558
|
"data-testid": `no-contract-placeholder-${identity.familyKey}`,
|
|
6995
7559
|
className: "flex items-center justify-center py-8",
|
|
6996
|
-
children: /* @__PURE__ */
|
|
7560
|
+
children: /* @__PURE__ */ jsx26(
|
|
6997
7561
|
"button",
|
|
6998
7562
|
{
|
|
6999
7563
|
onClick: () => onOpenContract?.(),
|
|
@@ -7005,7 +7569,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7005
7569
|
);
|
|
7006
7570
|
}
|
|
7007
7571
|
if (features.bulkComposePlaceholders && isComposing) {
|
|
7008
|
-
return /* @__PURE__ */
|
|
7572
|
+
return /* @__PURE__ */ jsx26("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2", children: /* @__PURE__ */ jsx26(SorceryProgressBar, { isLoading: true, statusText: "COMPOSING...", heightClass: "h-10" }) });
|
|
7009
7573
|
}
|
|
7010
7574
|
const activePlaceholders = features.bulkComposePlaceholders ? placeholders : [];
|
|
7011
7575
|
if (activePlaceholders.length > 0) {
|
|
@@ -7016,18 +7580,18 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7016
7580
|
tracksByDbId.set(t.handle.id, t);
|
|
7017
7581
|
}
|
|
7018
7582
|
}
|
|
7019
|
-
return /* @__PURE__ */
|
|
7583
|
+
return /* @__PURE__ */ jsx26("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: activePlaceholders.map((ph) => {
|
|
7020
7584
|
const loadedTrack = ph.status === "completed" ? tracksByDbId.get(ph.id) : void 0;
|
|
7021
7585
|
if (loadedTrack) {
|
|
7022
|
-
return /* @__PURE__ */
|
|
7586
|
+
return /* @__PURE__ */ jsx26(TrackRow, { ...buildRowProps(loadedTrack) }, ph.id);
|
|
7023
7587
|
}
|
|
7024
|
-
return /* @__PURE__ */
|
|
7588
|
+
return /* @__PURE__ */ jsx26(
|
|
7025
7589
|
"div",
|
|
7026
7590
|
{
|
|
7027
7591
|
"data-testid": "bulk-placeholder-track",
|
|
7028
7592
|
className: "relative rounded-sm border w-full overflow-hidden border-sas-border bg-sas-panel-alt",
|
|
7029
7593
|
style: { borderLeftColor: identity.placeholderAccentColor, borderLeftWidth: "3px" },
|
|
7030
|
-
children: /* @__PURE__ */
|
|
7594
|
+
children: /* @__PURE__ */ jsx26(SorceryProgressBar, { isLoading: true, statusText: "CONJURING MIDI...", heightClass: "h-10" })
|
|
7031
7595
|
},
|
|
7032
7596
|
ph.id
|
|
7033
7597
|
);
|
|
@@ -7039,13 +7603,13 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7039
7603
|
supportsMeters,
|
|
7040
7604
|
levels: supportsMeters ? trackLevels : void 0,
|
|
7041
7605
|
handlers,
|
|
7042
|
-
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */
|
|
7606
|
+
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */ jsx26(TrackRow, { ...{ ...buildRowProps(track, drag), ...overrides ?? {} } }, track.handle.id),
|
|
7043
7607
|
setGroupMute,
|
|
7044
7608
|
setGroupSolo,
|
|
7045
7609
|
deleteGroup
|
|
7046
7610
|
};
|
|
7047
|
-
return /* @__PURE__ */
|
|
7048
|
-
features.importTracks && host.listImportableTracks && /* @__PURE__ */
|
|
7611
|
+
return /* @__PURE__ */ jsxs20("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: [
|
|
7612
|
+
features.importTracks && host.listImportableTracks && /* @__PURE__ */ jsx26(
|
|
7049
7613
|
ImportTrackModal,
|
|
7050
7614
|
{
|
|
7051
7615
|
host,
|
|
@@ -7058,7 +7622,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7058
7622
|
testIdPrefix: `${identity.familyKey}-import`
|
|
7059
7623
|
}
|
|
7060
7624
|
),
|
|
7061
|
-
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */
|
|
7625
|
+
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ jsx26(
|
|
7062
7626
|
ImportTrackModal,
|
|
7063
7627
|
{
|
|
7064
7628
|
host,
|
|
@@ -7073,7 +7637,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7073
7637
|
}
|
|
7074
7638
|
),
|
|
7075
7639
|
slots?.modals,
|
|
7076
|
-
canCrossfade && xfFromId && xfToId && /* @__PURE__ */
|
|
7640
|
+
canCrossfade && xfFromId && xfToId && /* @__PURE__ */ jsx26("div", { className: designerView ? "contents" : "hidden", children: /* @__PURE__ */ jsx26(
|
|
7077
7641
|
TransitionDesigner,
|
|
7078
7642
|
{
|
|
7079
7643
|
host,
|
|
@@ -7090,9 +7654,29 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7090
7654
|
testIdPrefix: `${identity.familyKey}-transition-designer`
|
|
7091
7655
|
}
|
|
7092
7656
|
) }),
|
|
7093
|
-
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */
|
|
7657
|
+
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */ jsx26("div", { className: "text-sas-muted text-xs text-center py-4", children: "Loading tracks..." }) : /* @__PURE__ */ jsxs20(Fragment6, { children: [
|
|
7658
|
+
panelBus.supported && panelBus.bus && /* @__PURE__ */ jsx26(
|
|
7659
|
+
PanelMasterStrip,
|
|
7660
|
+
{
|
|
7661
|
+
bus: panelBus.bus,
|
|
7662
|
+
levels: panelBus.levels,
|
|
7663
|
+
availableFx: panelBus.availableFx,
|
|
7664
|
+
fxLoading: panelBus.fxLoading,
|
|
7665
|
+
soloedOut: anySolo && !panelBus.bus.soloed,
|
|
7666
|
+
fxPickerOpen: panelBus.fxPickerOpen,
|
|
7667
|
+
onToggleFxPicker: panelBus.setFxPickerOpen,
|
|
7668
|
+
onRefreshFx: panelBus.refreshFx,
|
|
7669
|
+
onVolumeChange: panelBus.onVolumeChange,
|
|
7670
|
+
onMuteToggle: panelBus.onMuteToggle,
|
|
7671
|
+
onSoloToggle: panelBus.onSoloToggle,
|
|
7672
|
+
onAddFx: panelBus.onAddFx,
|
|
7673
|
+
onRemoveFx: panelBus.onRemoveFx,
|
|
7674
|
+
onToggleFxEnabled: panelBus.onToggleFxEnabled,
|
|
7675
|
+
onShowFxEditor: panelBus.onShowFxEditor
|
|
7676
|
+
}
|
|
7677
|
+
),
|
|
7094
7678
|
slots?.beforeRows,
|
|
7095
|
-
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */
|
|
7679
|
+
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ jsx26(
|
|
7096
7680
|
CrossfadeTrackRow,
|
|
7097
7681
|
{
|
|
7098
7682
|
accentColor: identity.transitionAccentColor,
|
|
@@ -7129,7 +7713,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7129
7713
|
},
|
|
7130
7714
|
pair.groupId
|
|
7131
7715
|
)),
|
|
7132
|
-
resolvedFades.map((fade) => /* @__PURE__ */
|
|
7716
|
+
resolvedFades.map((fade) => /* @__PURE__ */ jsx26(
|
|
7133
7717
|
FadeTrackRow,
|
|
7134
7718
|
{
|
|
7135
7719
|
accentColor: identity.transitionAccentColor,
|
|
@@ -7155,20 +7739,20 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7155
7739
|
fade.dbId
|
|
7156
7740
|
)),
|
|
7157
7741
|
(adapter.groupExtensions ?? []).flatMap(
|
|
7158
|
-
(ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).map((group) => /* @__PURE__ */
|
|
7742
|
+
(ext) => (resolvedGenericGroups[ext.metaKey]?.resolved ?? []).map((group) => /* @__PURE__ */ jsx26(React22.Fragment, { children: ext.renderGroup(group, groupCtx) }, `${ext.metaKey}:${group.groupId}`))
|
|
7159
7743
|
),
|
|
7160
7744
|
tracks.map((track, index) => {
|
|
7161
7745
|
if (crossfadeMemberDbIds.has(track.handle.dbId) || fadeMemberDbIds.has(track.handle.dbId) || genericGroupMemberDbIds.has(track.handle.dbId)) {
|
|
7162
7746
|
return null;
|
|
7163
7747
|
}
|
|
7164
|
-
return /* @__PURE__ */
|
|
7748
|
+
return /* @__PURE__ */ jsx26(TrackRow, { ...buildRowProps(track, reorder.dragPropsFor(index)) }, track.handle.id);
|
|
7165
7749
|
}),
|
|
7166
7750
|
slots?.afterRows
|
|
7167
7751
|
] })),
|
|
7168
7752
|
features.exportMidi && !designerView && !isLoadingTracks && tracks.length > 0 && (() => {
|
|
7169
7753
|
const hasAnyMidi = tracks.some((t) => t.hasMidi);
|
|
7170
7754
|
const exportDisabled = isExportingMidi || !hasAnyMidi;
|
|
7171
|
-
return /* @__PURE__ */
|
|
7755
|
+
return /* @__PURE__ */ jsx26("div", { className: "pt-2", children: /* @__PURE__ */ jsx26(
|
|
7172
7756
|
"button",
|
|
7173
7757
|
{
|
|
7174
7758
|
"data-testid": "export-midi-tracks-button",
|
|
@@ -7236,7 +7820,7 @@ function createSurgeSoundAdapter(host, overrides = {}) {
|
|
|
7236
7820
|
}
|
|
7237
7821
|
|
|
7238
7822
|
// src/constants/sdk-version.ts
|
|
7239
|
-
var PLUGIN_SDK_VERSION = "2.
|
|
7823
|
+
var PLUGIN_SDK_VERSION = "2.39.0";
|
|
7240
7824
|
|
|
7241
7825
|
// src/utils/format-concurrent-tracks.ts
|
|
7242
7826
|
function formatConcurrentTracks(ctx) {
|
|
@@ -7411,6 +7995,7 @@ export {
|
|
|
7411
7995
|
PLUGIN_SDK_VERSION,
|
|
7412
7996
|
PX_PER_BEAT,
|
|
7413
7997
|
PanSlider,
|
|
7998
|
+
PanelMasterStrip,
|
|
7414
7999
|
PianoRollEditor,
|
|
7415
8000
|
PluginError,
|
|
7416
8001
|
RESIZE_HANDLE_PX,
|
|
@@ -7422,6 +8007,7 @@ export {
|
|
|
7422
8007
|
TEXTURAL_ROLES,
|
|
7423
8008
|
TRANSITION_DESIGNER_DRAFT_KEY,
|
|
7424
8009
|
TrackDrawer,
|
|
8010
|
+
TrackExternalFxSection,
|
|
7425
8011
|
TrackMeterStrip,
|
|
7426
8012
|
TrackRow,
|
|
7427
8013
|
TransitionDesigner,
|
|
@@ -7475,8 +8061,10 @@ export {
|
|
|
7475
8061
|
transposeNotes,
|
|
7476
8062
|
useAnySolo,
|
|
7477
8063
|
useGeneratorPanelCore,
|
|
8064
|
+
usePanelBus,
|
|
7478
8065
|
useSceneState,
|
|
7479
8066
|
useSoundHistory,
|
|
8067
|
+
useTrackExternalFx,
|
|
7480
8068
|
useTrackLevel,
|
|
7481
8069
|
useTrackLevels,
|
|
7482
8070
|
useTrackMeter,
|