@signalsandsorcery/plugin-sdk 2.35.2 → 2.35.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +139 -5
- package/dist/index.d.ts +139 -5
- package/dist/index.js +953 -652
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +951 -652
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.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}`,
|
|
@@ -4127,10 +4373,11 @@ function TransitionDesigner({
|
|
|
4127
4373
|
}
|
|
4128
4374
|
|
|
4129
4375
|
// src/components/PanelMasterStrip.tsx
|
|
4130
|
-
import { useMemo as
|
|
4131
|
-
import { jsx as
|
|
4376
|
+
import { useMemo as useMemo7, useState as useState14 } from "react";
|
|
4377
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4132
4378
|
function PanelMasterStrip({
|
|
4133
4379
|
bus,
|
|
4380
|
+
levels = null,
|
|
4134
4381
|
availableFx = [],
|
|
4135
4382
|
fxLoading = false,
|
|
4136
4383
|
soloedOut = false,
|
|
@@ -4146,22 +4393,22 @@ function PanelMasterStrip({
|
|
|
4146
4393
|
onToggleFxEnabled,
|
|
4147
4394
|
onShowFxEditor
|
|
4148
4395
|
}) {
|
|
4149
|
-
const [search, setSearch] =
|
|
4150
|
-
const filtered =
|
|
4396
|
+
const [search, setSearch] = useState14("");
|
|
4397
|
+
const filtered = useMemo7(() => {
|
|
4151
4398
|
const q = search.trim().toLowerCase();
|
|
4152
4399
|
if (!q) return availableFx;
|
|
4153
4400
|
return availableFx.filter(
|
|
4154
4401
|
(fx) => fx.name.toLowerCase().includes(q) || fx.manufacturer.toLowerCase().includes(q)
|
|
4155
4402
|
);
|
|
4156
4403
|
}, [availableFx, search]);
|
|
4157
|
-
return /* @__PURE__ */
|
|
4404
|
+
return /* @__PURE__ */ jsxs15(
|
|
4158
4405
|
"div",
|
|
4159
4406
|
{
|
|
4160
4407
|
"data-testid": "panel-master-strip",
|
|
4161
|
-
className: `flex flex-col gap-1 px-2 py-1.5 rounded-sm border border-sas-border
|
|
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" : ""}`,
|
|
4162
4409
|
children: [
|
|
4163
|
-
/* @__PURE__ */
|
|
4164
|
-
/* @__PURE__ */
|
|
4410
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
|
|
4411
|
+
/* @__PURE__ */ jsx19(
|
|
4165
4412
|
"span",
|
|
4166
4413
|
{
|
|
4167
4414
|
className: "text-[9px] font-bold tracking-widest text-sas-muted/70 select-none",
|
|
@@ -4169,15 +4416,38 @@ function PanelMasterStrip({
|
|
|
4169
4416
|
children: "BUS"
|
|
4170
4417
|
}
|
|
4171
4418
|
),
|
|
4172
|
-
/* @__PURE__ */
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
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(
|
|
4181
4451
|
"button",
|
|
4182
4452
|
{
|
|
4183
4453
|
"data-testid": "bus-mute-button",
|
|
@@ -4188,7 +4458,7 @@ function PanelMasterStrip({
|
|
|
4188
4458
|
children: "M"
|
|
4189
4459
|
}
|
|
4190
4460
|
),
|
|
4191
|
-
/* @__PURE__ */
|
|
4461
|
+
/* @__PURE__ */ jsx19(
|
|
4192
4462
|
"button",
|
|
4193
4463
|
{
|
|
4194
4464
|
"data-testid": "bus-solo-button",
|
|
@@ -4199,14 +4469,14 @@ function PanelMasterStrip({
|
|
|
4199
4469
|
children: "S"
|
|
4200
4470
|
}
|
|
4201
4471
|
),
|
|
4202
|
-
/* @__PURE__ */
|
|
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(
|
|
4203
4473
|
"span",
|
|
4204
4474
|
{
|
|
4205
4475
|
"data-testid": `bus-fx-chip-${fx.index}`,
|
|
4206
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"}`,
|
|
4207
4477
|
title: `${fx.name}${fx.enabled ? "" : " (bypassed)"}`,
|
|
4208
4478
|
children: [
|
|
4209
|
-
/* @__PURE__ */
|
|
4479
|
+
/* @__PURE__ */ jsx19(
|
|
4210
4480
|
"button",
|
|
4211
4481
|
{
|
|
4212
4482
|
"data-testid": `bus-fx-toggle-${fx.index}`,
|
|
@@ -4217,7 +4487,7 @@ function PanelMasterStrip({
|
|
|
4217
4487
|
children: fx.enabled ? "\u25CF" : "\u25CB"
|
|
4218
4488
|
}
|
|
4219
4489
|
),
|
|
4220
|
-
onShowFxEditor ? /* @__PURE__ */
|
|
4490
|
+
onShowFxEditor ? /* @__PURE__ */ jsx19(
|
|
4221
4491
|
"button",
|
|
4222
4492
|
{
|
|
4223
4493
|
"data-testid": `bus-fx-edit-${fx.index}`,
|
|
@@ -4227,8 +4497,8 @@ function PanelMasterStrip({
|
|
|
4227
4497
|
title: `Open ${fx.name} editor`,
|
|
4228
4498
|
children: fx.name
|
|
4229
4499
|
}
|
|
4230
|
-
) : /* @__PURE__ */
|
|
4231
|
-
/* @__PURE__ */
|
|
4500
|
+
) : /* @__PURE__ */ jsx19("span", { className: "max-w-[80px] truncate", children: fx.name }),
|
|
4501
|
+
/* @__PURE__ */ jsx19(
|
|
4232
4502
|
"button",
|
|
4233
4503
|
{
|
|
4234
4504
|
"data-testid": `bus-fx-remove-${fx.index}`,
|
|
@@ -4243,21 +4513,21 @@ function PanelMasterStrip({
|
|
|
4243
4513
|
},
|
|
4244
4514
|
`${fx.index}:${fx.pluginId}`
|
|
4245
4515
|
)) }),
|
|
4246
|
-
/* @__PURE__ */
|
|
4516
|
+
/* @__PURE__ */ jsx19(
|
|
4247
4517
|
"button",
|
|
4248
4518
|
{
|
|
4249
4519
|
"data-testid": "bus-fx-add-button",
|
|
4250
4520
|
onClick: () => onToggleFxPicker(!fxPickerOpen),
|
|
4251
4521
|
disabled,
|
|
4252
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`,
|
|
4253
|
-
title: "Add an FX plugin to the panel bus",
|
|
4254
|
-
children: "FX +"
|
|
4523
|
+
title: fxPickerOpen ? "Close the FX picker" : "Add an FX plugin to the panel bus",
|
|
4524
|
+
children: fxPickerOpen ? "FX \u25B4" : "FX +"
|
|
4255
4525
|
}
|
|
4256
4526
|
)
|
|
4257
4527
|
] }),
|
|
4258
|
-
fxPickerOpen && /* @__PURE__ */
|
|
4259
|
-
/* @__PURE__ */
|
|
4260
|
-
/* @__PURE__ */
|
|
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(
|
|
4261
4531
|
"input",
|
|
4262
4532
|
{
|
|
4263
4533
|
type: "text",
|
|
@@ -4267,7 +4537,7 @@ function PanelMasterStrip({
|
|
|
4267
4537
|
className: "sas-input flex-1 px-2 py-1 text-xs"
|
|
4268
4538
|
}
|
|
4269
4539
|
),
|
|
4270
|
-
onRefreshFx && /* @__PURE__ */
|
|
4540
|
+
onRefreshFx && /* @__PURE__ */ jsx19(
|
|
4271
4541
|
"button",
|
|
4272
4542
|
{
|
|
4273
4543
|
onClick: () => onRefreshFx(),
|
|
@@ -4278,8 +4548,8 @@ function PanelMasterStrip({
|
|
|
4278
4548
|
}
|
|
4279
4549
|
)
|
|
4280
4550
|
] }),
|
|
4281
|
-
fxLoading && availableFx.length === 0 ? /* @__PURE__ */
|
|
4282
|
-
filtered.map((fx) => /* @__PURE__ */
|
|
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(
|
|
4283
4553
|
"button",
|
|
4284
4554
|
{
|
|
4285
4555
|
"data-testid": `bus-fx-pick-${fx.pluginId}`,
|
|
@@ -4287,13 +4557,13 @@ function PanelMasterStrip({
|
|
|
4287
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",
|
|
4288
4558
|
title: `${fx.name} by ${fx.manufacturer} (${fx.type.toUpperCase()})`,
|
|
4289
4559
|
children: [
|
|
4290
|
-
/* @__PURE__ */
|
|
4291
|
-
/* @__PURE__ */
|
|
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() })
|
|
4292
4562
|
]
|
|
4293
4563
|
},
|
|
4294
4564
|
fx.pluginId
|
|
4295
4565
|
)),
|
|
4296
|
-
filtered.length === 0 && /* @__PURE__ */
|
|
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" })
|
|
4297
4567
|
] })
|
|
4298
4568
|
] })
|
|
4299
4569
|
]
|
|
@@ -4302,16 +4572,18 @@ function PanelMasterStrip({
|
|
|
4302
4572
|
}
|
|
4303
4573
|
|
|
4304
4574
|
// src/hooks/usePanelBus.ts
|
|
4305
|
-
import { useCallback as
|
|
4575
|
+
import { useCallback as useCallback10, useEffect as useEffect11, useRef as useRef12, useState as useState15 } from "react";
|
|
4576
|
+
var LEVELS_POLL_MS = 66;
|
|
4306
4577
|
function usePanelBus(host, activeSceneId) {
|
|
4307
4578
|
const supported = typeof host.getPanelBusState === "function";
|
|
4308
|
-
const [bus, setBus] =
|
|
4309
|
-
const [
|
|
4310
|
-
const [
|
|
4311
|
-
const [
|
|
4312
|
-
const
|
|
4313
|
-
const
|
|
4314
|
-
const
|
|
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 () => {
|
|
4315
4587
|
if (!supported || !activeSceneId || !host.getPanelBusState) {
|
|
4316
4588
|
setBus(null);
|
|
4317
4589
|
return;
|
|
@@ -4323,12 +4595,34 @@ function usePanelBus(host, activeSceneId) {
|
|
|
4323
4595
|
} catch {
|
|
4324
4596
|
}
|
|
4325
4597
|
}, [host, activeSceneId, supported]);
|
|
4326
|
-
|
|
4598
|
+
useEffect11(() => {
|
|
4327
4599
|
setBus(null);
|
|
4328
4600
|
setFxPickerOpen(false);
|
|
4329
4601
|
void reload();
|
|
4330
4602
|
}, [reload]);
|
|
4331
|
-
|
|
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(
|
|
4332
4626
|
async (force) => {
|
|
4333
4627
|
if (!supported || !host.getAvailableFx) return;
|
|
4334
4628
|
if (fxLoadedRef.current && !force) return;
|
|
@@ -4344,14 +4638,14 @@ function usePanelBus(host, activeSceneId) {
|
|
|
4344
4638
|
},
|
|
4345
4639
|
[host, supported]
|
|
4346
4640
|
);
|
|
4347
|
-
const openPicker =
|
|
4641
|
+
const openPicker = useCallback10(
|
|
4348
4642
|
(open) => {
|
|
4349
4643
|
setFxPickerOpen(open);
|
|
4350
4644
|
if (open) void loadFxList(false);
|
|
4351
4645
|
},
|
|
4352
4646
|
[loadFxList]
|
|
4353
4647
|
);
|
|
4354
|
-
const mutate =
|
|
4648
|
+
const mutate = useCallback10(
|
|
4355
4649
|
(fn) => {
|
|
4356
4650
|
if (!fn || !activeSceneId) return;
|
|
4357
4651
|
void (async () => {
|
|
@@ -4367,6 +4661,7 @@ function usePanelBus(host, activeSceneId) {
|
|
|
4367
4661
|
return {
|
|
4368
4662
|
supported,
|
|
4369
4663
|
bus,
|
|
4664
|
+
levels,
|
|
4370
4665
|
availableFx,
|
|
4371
4666
|
fxLoading,
|
|
4372
4667
|
fxPickerOpen,
|
|
@@ -4394,8 +4689,8 @@ function usePanelBus(host, activeSceneId) {
|
|
|
4394
4689
|
}
|
|
4395
4690
|
|
|
4396
4691
|
// src/components/DownloadPackButton.tsx
|
|
4397
|
-
import { useCallback as
|
|
4398
|
-
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";
|
|
4399
4694
|
function formatSize(bytes) {
|
|
4400
4695
|
if (!bytes || bytes <= 0) return "";
|
|
4401
4696
|
const gb = bytes / 1024 ** 3;
|
|
@@ -4411,10 +4706,10 @@ var DownloadPackButton = ({
|
|
|
4411
4706
|
variant = "compact",
|
|
4412
4707
|
onDownloadComplete
|
|
4413
4708
|
}) => {
|
|
4414
|
-
const [status, setStatus] =
|
|
4415
|
-
const [progress, setProgress] =
|
|
4416
|
-
const [errorMessage, setErrorMessage] =
|
|
4417
|
-
|
|
4709
|
+
const [status, setStatus] = useState16("idle");
|
|
4710
|
+
const [progress, setProgress] = useState16(0);
|
|
4711
|
+
const [errorMessage, setErrorMessage] = useState16(null);
|
|
4712
|
+
useEffect12(() => {
|
|
4418
4713
|
const unsub = host.onSamplePackProgress(packId, (p) => {
|
|
4419
4714
|
setStatus(p.status);
|
|
4420
4715
|
setProgress(p.progress);
|
|
@@ -4429,7 +4724,7 @@ var DownloadPackButton = ({
|
|
|
4429
4724
|
});
|
|
4430
4725
|
return unsub;
|
|
4431
4726
|
}, [host, packId, onDownloadComplete]);
|
|
4432
|
-
const handleClick =
|
|
4727
|
+
const handleClick = useCallback11(async () => {
|
|
4433
4728
|
if (status !== "idle" && status !== "error") return;
|
|
4434
4729
|
try {
|
|
4435
4730
|
setStatus("downloading");
|
|
@@ -4483,8 +4778,8 @@ var DownloadPackButton = ({
|
|
|
4483
4778
|
} else {
|
|
4484
4779
|
className = `${baseClasses} text-sas-muted hover:text-sas-accent border-sas-border hover:border-sas-accent`;
|
|
4485
4780
|
}
|
|
4486
|
-
return /* @__PURE__ */
|
|
4487
|
-
/* @__PURE__ */
|
|
4781
|
+
return /* @__PURE__ */ jsxs16("div", { children: [
|
|
4782
|
+
/* @__PURE__ */ jsx20(
|
|
4488
4783
|
"button",
|
|
4489
4784
|
{
|
|
4490
4785
|
"data-testid": `download-pack-button-${packId}`,
|
|
@@ -4495,12 +4790,12 @@ var DownloadPackButton = ({
|
|
|
4495
4790
|
children: buttonLabel
|
|
4496
4791
|
}
|
|
4497
4792
|
),
|
|
4498
|
-
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 })
|
|
4499
4794
|
] });
|
|
4500
4795
|
};
|
|
4501
4796
|
|
|
4502
4797
|
// src/components/SamplePackCTACard.tsx
|
|
4503
|
-
import { jsx as
|
|
4798
|
+
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4504
4799
|
var SamplePackCTACard = ({
|
|
4505
4800
|
host,
|
|
4506
4801
|
pack,
|
|
@@ -4508,7 +4803,7 @@ var SamplePackCTACard = ({
|
|
|
4508
4803
|
onDownloadComplete
|
|
4509
4804
|
}) => {
|
|
4510
4805
|
if (status === "checking") {
|
|
4511
|
-
return /* @__PURE__ */
|
|
4806
|
+
return /* @__PURE__ */ jsx21(
|
|
4512
4807
|
"div",
|
|
4513
4808
|
{
|
|
4514
4809
|
"data-testid": `sample-pack-cta-checking-${pack.packId}`,
|
|
@@ -4519,16 +4814,16 @@ var SamplePackCTACard = ({
|
|
|
4519
4814
|
}
|
|
4520
4815
|
const headline = status === "stale" ? `${pack.displayName} update available` : `${pack.displayName} not installed`;
|
|
4521
4816
|
const sublabel = status === "stale" ? `A newer version is available for download.` : pack.description;
|
|
4522
|
-
return /* @__PURE__ */
|
|
4817
|
+
return /* @__PURE__ */ jsxs17(
|
|
4523
4818
|
"div",
|
|
4524
4819
|
{
|
|
4525
4820
|
"data-testid": `sample-pack-cta-${pack.packId}`,
|
|
4526
4821
|
className: "flex flex-col items-center justify-center py-12 px-6 text-center",
|
|
4527
4822
|
children: [
|
|
4528
|
-
/* @__PURE__ */
|
|
4529
|
-
/* @__PURE__ */
|
|
4530
|
-
/* @__PURE__ */
|
|
4531
|
-
/* @__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(
|
|
4532
4827
|
DownloadPackButton,
|
|
4533
4828
|
{
|
|
4534
4829
|
host,
|
|
@@ -4545,7 +4840,7 @@ var SamplePackCTACard = ({
|
|
|
4545
4840
|
};
|
|
4546
4841
|
|
|
4547
4842
|
// src/components/WaveformView.tsx
|
|
4548
|
-
import { useEffect as
|
|
4843
|
+
import { useEffect as useEffect13, useRef as useRef13, useState as useState17 } from "react";
|
|
4549
4844
|
|
|
4550
4845
|
// src/components/waveform.ts
|
|
4551
4846
|
function computePeaks(audioBuffer, bins, targetSamples) {
|
|
@@ -4608,7 +4903,7 @@ function drawWaveform(canvas, peaks, options = {}) {
|
|
|
4608
4903
|
}
|
|
4609
4904
|
|
|
4610
4905
|
// src/components/WaveformView.tsx
|
|
4611
|
-
import { jsx as
|
|
4906
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
4612
4907
|
var WaveformView = ({
|
|
4613
4908
|
host,
|
|
4614
4909
|
filePath,
|
|
@@ -4617,9 +4912,9 @@ var WaveformView = ({
|
|
|
4617
4912
|
fillStyle,
|
|
4618
4913
|
targetSamples
|
|
4619
4914
|
}) => {
|
|
4620
|
-
const canvasRef =
|
|
4621
|
-
const [peaks, setPeaks] =
|
|
4622
|
-
|
|
4915
|
+
const canvasRef = useRef13(null);
|
|
4916
|
+
const [peaks, setPeaks] = useState17(null);
|
|
4917
|
+
useEffect13(() => {
|
|
4623
4918
|
let cancelled = false;
|
|
4624
4919
|
let audioContext = null;
|
|
4625
4920
|
(async () => {
|
|
@@ -4645,7 +4940,7 @@ var WaveformView = ({
|
|
|
4645
4940
|
cancelled = true;
|
|
4646
4941
|
};
|
|
4647
4942
|
}, [host, filePath, bins, targetSamples]);
|
|
4648
|
-
|
|
4943
|
+
useEffect13(() => {
|
|
4649
4944
|
if (!peaks) return;
|
|
4650
4945
|
const canvas = canvasRef.current;
|
|
4651
4946
|
if (!canvas) return;
|
|
@@ -4656,7 +4951,7 @@ var WaveformView = ({
|
|
|
4656
4951
|
observer.observe(canvas);
|
|
4657
4952
|
return () => observer.disconnect();
|
|
4658
4953
|
}, [peaks, fillStyle]);
|
|
4659
|
-
return /* @__PURE__ */
|
|
4954
|
+
return /* @__PURE__ */ jsx22(
|
|
4660
4955
|
"canvas",
|
|
4661
4956
|
{
|
|
4662
4957
|
ref: canvasRef,
|
|
@@ -4667,8 +4962,8 @@ var WaveformView = ({
|
|
|
4667
4962
|
};
|
|
4668
4963
|
|
|
4669
4964
|
// src/components/ScrollingWaveform.tsx
|
|
4670
|
-
import { useEffect as
|
|
4671
|
-
import { jsx as
|
|
4965
|
+
import { useEffect as useEffect14, useRef as useRef14 } from "react";
|
|
4966
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
4672
4967
|
var ScrollingWaveform = ({
|
|
4673
4968
|
getPeakDb,
|
|
4674
4969
|
active,
|
|
@@ -4676,11 +4971,11 @@ var ScrollingWaveform = ({
|
|
|
4676
4971
|
className,
|
|
4677
4972
|
fillStyle
|
|
4678
4973
|
}) => {
|
|
4679
|
-
const canvasRef =
|
|
4680
|
-
const ringRef =
|
|
4681
|
-
const writeIdxRef =
|
|
4682
|
-
const rafRef =
|
|
4683
|
-
|
|
4974
|
+
const canvasRef = useRef14(null);
|
|
4975
|
+
const ringRef = useRef14(new Float32Array(columns));
|
|
4976
|
+
const writeIdxRef = useRef14(0);
|
|
4977
|
+
const rafRef = useRef14(null);
|
|
4978
|
+
useEffect14(() => {
|
|
4684
4979
|
if (ringRef.current.length !== columns) {
|
|
4685
4980
|
const next = new Float32Array(columns);
|
|
4686
4981
|
const prev = ringRef.current;
|
|
@@ -4692,7 +4987,7 @@ var ScrollingWaveform = ({
|
|
|
4692
4987
|
writeIdxRef.current = writeIdxRef.current % columns;
|
|
4693
4988
|
}
|
|
4694
4989
|
}, [columns]);
|
|
4695
|
-
|
|
4990
|
+
useEffect14(() => {
|
|
4696
4991
|
if (!active) {
|
|
4697
4992
|
if (rafRef.current !== null) {
|
|
4698
4993
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -4744,7 +5039,7 @@ var ScrollingWaveform = ({
|
|
|
4744
5039
|
}
|
|
4745
5040
|
};
|
|
4746
5041
|
}, [active, getPeakDb, fillStyle]);
|
|
4747
|
-
return /* @__PURE__ */
|
|
5042
|
+
return /* @__PURE__ */ jsx23(
|
|
4748
5043
|
"canvas",
|
|
4749
5044
|
{
|
|
4750
5045
|
ref: canvasRef,
|
|
@@ -4755,8 +5050,8 @@ var ScrollingWaveform = ({
|
|
|
4755
5050
|
};
|
|
4756
5051
|
|
|
4757
5052
|
// src/components/OffsetScrubber.tsx
|
|
4758
|
-
import { useCallback as
|
|
4759
|
-
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";
|
|
4760
5055
|
var SLIDER_HEIGHT_PX = 28;
|
|
4761
5056
|
var TICK_HEIGHT_PX = 14;
|
|
4762
5057
|
var DOWNBEAT_TICK_HEIGHT_PX = 22;
|
|
@@ -4769,40 +5064,40 @@ function OffsetScrubber({
|
|
|
4769
5064
|
onChange,
|
|
4770
5065
|
disabled = false
|
|
4771
5066
|
}) {
|
|
4772
|
-
const trackRef =
|
|
4773
|
-
const [draftOffset, setDraftOffset] =
|
|
4774
|
-
const [isDragging, setIsDragging] =
|
|
4775
|
-
|
|
5067
|
+
const trackRef = useRef15(null);
|
|
5068
|
+
const [draftOffset, setDraftOffset] = useState18(offsetSamples);
|
|
5069
|
+
const [isDragging, setIsDragging] = useState18(false);
|
|
5070
|
+
useEffect15(() => {
|
|
4776
5071
|
if (!isDragging) setDraftOffset(offsetSamples);
|
|
4777
5072
|
}, [offsetSamples, isDragging]);
|
|
4778
5073
|
const sampleRate = cuePoints?.sample_rate ?? 44100;
|
|
4779
5074
|
const detectedBpm = cuePoints?.detected_bpm ?? projectBpm;
|
|
4780
|
-
const beatsForRange =
|
|
5075
|
+
const beatsForRange = useMemo8(() => {
|
|
4781
5076
|
return Math.round(60 / projectBpm * sampleRate);
|
|
4782
5077
|
}, [projectBpm, sampleRate]);
|
|
4783
5078
|
const rangeSamples = beatsForRange * meter;
|
|
4784
|
-
const sampleToFraction =
|
|
5079
|
+
const sampleToFraction = useCallback12(
|
|
4785
5080
|
(sample) => {
|
|
4786
5081
|
const clamped = Math.max(-rangeSamples, Math.min(rangeSamples, sample));
|
|
4787
5082
|
return (clamped + rangeSamples) / (2 * rangeSamples);
|
|
4788
5083
|
},
|
|
4789
5084
|
[rangeSamples]
|
|
4790
5085
|
);
|
|
4791
|
-
const fractionToSample =
|
|
5086
|
+
const fractionToSample = useCallback12(
|
|
4792
5087
|
(fraction) => {
|
|
4793
5088
|
const clamped = Math.max(0, Math.min(1, fraction));
|
|
4794
5089
|
return Math.round(clamped * 2 * rangeSamples - rangeSamples);
|
|
4795
5090
|
},
|
|
4796
5091
|
[rangeSamples]
|
|
4797
5092
|
);
|
|
4798
|
-
const snapTargets =
|
|
5093
|
+
const snapTargets = useMemo8(() => {
|
|
4799
5094
|
if (!cuePoints || cuePoints.beats.length === 0) return [];
|
|
4800
5095
|
const downbeat = cuePoints.beats[0];
|
|
4801
5096
|
const positives = cuePoints.beats.map((b) => b - downbeat);
|
|
4802
5097
|
const negatives = positives.slice(1).map((p) => -p);
|
|
4803
5098
|
return [...negatives, ...positives].sort((a, b) => a - b);
|
|
4804
5099
|
}, [cuePoints]);
|
|
4805
|
-
const snapToBeat =
|
|
5100
|
+
const snapToBeat = useCallback12(
|
|
4806
5101
|
(sample) => {
|
|
4807
5102
|
if (snapTargets.length === 0) return sample;
|
|
4808
5103
|
let best = snapTargets[0];
|
|
@@ -4818,7 +5113,7 @@ function OffsetScrubber({
|
|
|
4818
5113
|
},
|
|
4819
5114
|
[snapTargets]
|
|
4820
5115
|
);
|
|
4821
|
-
const handlePointerDown =
|
|
5116
|
+
const handlePointerDown = useCallback12(
|
|
4822
5117
|
(e) => {
|
|
4823
5118
|
if (disabled || !cuePoints) return;
|
|
4824
5119
|
e.preventDefault();
|
|
@@ -4852,7 +5147,7 @@ function OffsetScrubber({
|
|
|
4852
5147
|
},
|
|
4853
5148
|
[disabled, cuePoints, fractionToSample, onChange, snapToBeat]
|
|
4854
5149
|
);
|
|
4855
|
-
const handleResetToZero =
|
|
5150
|
+
const handleResetToZero = useCallback12(() => {
|
|
4856
5151
|
if (disabled) return;
|
|
4857
5152
|
setDraftOffset(0);
|
|
4858
5153
|
onChange(0);
|
|
@@ -4860,7 +5155,7 @@ function OffsetScrubber({
|
|
|
4860
5155
|
const thumbFraction = sampleToFraction(draftOffset);
|
|
4861
5156
|
const thumbLeftPct = `${(thumbFraction * 100).toFixed(2)}%`;
|
|
4862
5157
|
const bpmMismatch = cuePoints?.detected_bpm != null && Math.abs(cuePoints.detected_bpm - projectBpm) > 1;
|
|
4863
|
-
const ticks =
|
|
5158
|
+
const ticks = useMemo8(() => {
|
|
4864
5159
|
if (!cuePoints) return [];
|
|
4865
5160
|
const downbeat = cuePoints.beats[0] ?? 0;
|
|
4866
5161
|
return cuePoints.beats.map((b, i) => {
|
|
@@ -4871,9 +5166,9 @@ function OffsetScrubber({
|
|
|
4871
5166
|
});
|
|
4872
5167
|
}, [cuePoints, sampleToFraction]);
|
|
4873
5168
|
const isDisabled = disabled || !cuePoints || cuePoints.beats.length === 0;
|
|
4874
|
-
return /* @__PURE__ */
|
|
4875
|
-
/* @__PURE__ */
|
|
4876
|
-
/* @__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(
|
|
4877
5172
|
"div",
|
|
4878
5173
|
{
|
|
4879
5174
|
ref: trackRef,
|
|
@@ -4889,7 +5184,7 @@ function OffsetScrubber({
|
|
|
4889
5184
|
"aria-valuenow": draftOffset,
|
|
4890
5185
|
"aria-disabled": isDisabled,
|
|
4891
5186
|
children: [
|
|
4892
|
-
/* @__PURE__ */
|
|
5187
|
+
/* @__PURE__ */ jsx24(
|
|
4893
5188
|
"div",
|
|
4894
5189
|
{
|
|
4895
5190
|
"aria-hidden": "true",
|
|
@@ -4897,7 +5192,7 @@ function OffsetScrubber({
|
|
|
4897
5192
|
style: { left: "50%" }
|
|
4898
5193
|
}
|
|
4899
5194
|
),
|
|
4900
|
-
ticks.map((t) => /* @__PURE__ */
|
|
5195
|
+
ticks.map((t) => /* @__PURE__ */ jsx24(
|
|
4901
5196
|
"div",
|
|
4902
5197
|
{
|
|
4903
5198
|
"data-testid": t.isDownbeat ? "offset-tick-downbeat" : "offset-tick",
|
|
@@ -4912,7 +5207,7 @@ function OffsetScrubber({
|
|
|
4912
5207
|
},
|
|
4913
5208
|
t.i
|
|
4914
5209
|
)),
|
|
4915
|
-
/* @__PURE__ */
|
|
5210
|
+
/* @__PURE__ */ jsx24(
|
|
4916
5211
|
"div",
|
|
4917
5212
|
{
|
|
4918
5213
|
"data-testid": "offset-scrubber-thumb",
|
|
@@ -4929,7 +5224,7 @@ function OffsetScrubber({
|
|
|
4929
5224
|
]
|
|
4930
5225
|
}
|
|
4931
5226
|
),
|
|
4932
|
-
/* @__PURE__ */
|
|
5227
|
+
/* @__PURE__ */ jsx24(
|
|
4933
5228
|
"span",
|
|
4934
5229
|
{
|
|
4935
5230
|
"data-testid": "offset-scrubber-readout",
|
|
@@ -4937,7 +5232,7 @@ function OffsetScrubber({
|
|
|
4937
5232
|
children: formatOffset(draftOffset, sampleRate)
|
|
4938
5233
|
}
|
|
4939
5234
|
),
|
|
4940
|
-
/* @__PURE__ */
|
|
5235
|
+
/* @__PURE__ */ jsx24(
|
|
4941
5236
|
"button",
|
|
4942
5237
|
{
|
|
4943
5238
|
type: "button",
|
|
@@ -4949,7 +5244,7 @@ function OffsetScrubber({
|
|
|
4949
5244
|
children: "\u2316"
|
|
4950
5245
|
}
|
|
4951
5246
|
),
|
|
4952
|
-
bpmMismatch && /* @__PURE__ */
|
|
5247
|
+
bpmMismatch && /* @__PURE__ */ jsx24(
|
|
4953
5248
|
"span",
|
|
4954
5249
|
{
|
|
4955
5250
|
"data-testid": "offset-bpm-mismatch",
|
|
@@ -5021,16 +5316,16 @@ function synthesizeCuePoints({
|
|
|
5021
5316
|
}
|
|
5022
5317
|
|
|
5023
5318
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
5024
|
-
import { useState as
|
|
5319
|
+
import { useState as useState23, useEffect as useEffect18, useCallback as useCallback16, useRef as useRef19, useMemo as useMemo10 } from "react";
|
|
5025
5320
|
|
|
5026
5321
|
// src/hooks/useSceneState.ts
|
|
5027
|
-
import { useState as
|
|
5322
|
+
import { useState as useState19, useCallback as useCallback13, useRef as useRef16 } from "react";
|
|
5028
5323
|
function useSceneState(activeSceneId, initialValue) {
|
|
5029
|
-
const [stateMap, setStateMap] =
|
|
5030
|
-
const activeSceneIdRef =
|
|
5324
|
+
const [stateMap, setStateMap] = useState19(() => /* @__PURE__ */ new Map());
|
|
5325
|
+
const activeSceneIdRef = useRef16(activeSceneId);
|
|
5031
5326
|
activeSceneIdRef.current = activeSceneId;
|
|
5032
5327
|
const currentValue = activeSceneId !== null && stateMap.has(activeSceneId) ? stateMap.get(activeSceneId) : initialValue;
|
|
5033
|
-
const setForCurrentScene =
|
|
5328
|
+
const setForCurrentScene = useCallback13((value) => {
|
|
5034
5329
|
const sid = activeSceneIdRef.current;
|
|
5035
5330
|
if (sid === null) return;
|
|
5036
5331
|
setStateMap((prev) => {
|
|
@@ -5041,7 +5336,7 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
5041
5336
|
return newMap;
|
|
5042
5337
|
});
|
|
5043
5338
|
}, [initialValue]);
|
|
5044
|
-
const setForScene =
|
|
5339
|
+
const setForScene = useCallback13((sceneId, value) => {
|
|
5045
5340
|
setStateMap((prev) => {
|
|
5046
5341
|
const current = prev.has(sceneId) ? prev.get(sceneId) : initialValue;
|
|
5047
5342
|
const next = typeof value === "function" ? value(current) : value;
|
|
@@ -5054,10 +5349,10 @@ function useSceneState(activeSceneId, initialValue) {
|
|
|
5054
5349
|
}
|
|
5055
5350
|
|
|
5056
5351
|
// src/hooks/useAnySolo.ts
|
|
5057
|
-
import { useEffect as
|
|
5352
|
+
import { useEffect as useEffect16, useState as useState20 } from "react";
|
|
5058
5353
|
function useAnySolo(host) {
|
|
5059
|
-
const [anySolo, setAnySolo] =
|
|
5060
|
-
|
|
5354
|
+
const [anySolo, setAnySolo] = useState20(false);
|
|
5355
|
+
useEffect16(() => {
|
|
5061
5356
|
let active = true;
|
|
5062
5357
|
const refresh = () => {
|
|
5063
5358
|
host.isAnySoloActive().then((v) => {
|
|
@@ -5076,7 +5371,7 @@ function useAnySolo(host) {
|
|
|
5076
5371
|
}
|
|
5077
5372
|
|
|
5078
5373
|
// src/hooks/useSoundHistory.ts
|
|
5079
|
-
import { useCallback as
|
|
5374
|
+
import { useCallback as useCallback14, useMemo as useMemo9, useRef as useRef17, useState as useState21 } from "react";
|
|
5080
5375
|
var EMPTY = { entries: [], cursor: -1 };
|
|
5081
5376
|
function sameDescriptor(a, b) {
|
|
5082
5377
|
if (a === b) return true;
|
|
@@ -5088,14 +5383,14 @@ function sameDescriptor(a, b) {
|
|
|
5088
5383
|
}
|
|
5089
5384
|
function useSoundHistory(applySound, opts = {}) {
|
|
5090
5385
|
const max = Math.max(2, opts.max ?? 24);
|
|
5091
|
-
const applyRef =
|
|
5386
|
+
const applyRef = useRef17(applySound);
|
|
5092
5387
|
applyRef.current = applySound;
|
|
5093
|
-
const onChangeRef =
|
|
5388
|
+
const onChangeRef = useRef17(opts.onChange);
|
|
5094
5389
|
onChangeRef.current = opts.onChange;
|
|
5095
|
-
const dataRef =
|
|
5096
|
-
const [, setVersion] =
|
|
5097
|
-
const bump =
|
|
5098
|
-
const commit =
|
|
5390
|
+
const dataRef = useRef17({});
|
|
5391
|
+
const [, setVersion] = useState21(0);
|
|
5392
|
+
const bump = useCallback14(() => setVersion((v) => v + 1), []);
|
|
5393
|
+
const commit = useCallback14(
|
|
5099
5394
|
(trackId, next, notify) => {
|
|
5100
5395
|
dataRef.current = { ...dataRef.current, [trackId]: next };
|
|
5101
5396
|
bump();
|
|
@@ -5103,7 +5398,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5103
5398
|
},
|
|
5104
5399
|
[bump]
|
|
5105
5400
|
);
|
|
5106
|
-
const record =
|
|
5401
|
+
const record = useCallback14(
|
|
5107
5402
|
(trackId, descriptor, label) => {
|
|
5108
5403
|
const h = dataRef.current[trackId];
|
|
5109
5404
|
const current = h && h.cursor >= 0 ? h.entries[h.cursor] : void 0;
|
|
@@ -5118,7 +5413,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5118
5413
|
},
|
|
5119
5414
|
[max, commit]
|
|
5120
5415
|
);
|
|
5121
|
-
const restoreTo =
|
|
5416
|
+
const restoreTo = useCallback14(
|
|
5122
5417
|
async (trackId, index) => {
|
|
5123
5418
|
const h = dataRef.current[trackId];
|
|
5124
5419
|
if (!h || index < 0 || index >= h.entries.length || index === h.cursor) return false;
|
|
@@ -5128,7 +5423,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5128
5423
|
},
|
|
5129
5424
|
[commit]
|
|
5130
5425
|
);
|
|
5131
|
-
const undo =
|
|
5426
|
+
const undo = useCallback14(
|
|
5132
5427
|
(trackId) => {
|
|
5133
5428
|
const h = dataRef.current[trackId];
|
|
5134
5429
|
if (!h || h.cursor <= 0) return Promise.resolve(false);
|
|
@@ -5136,7 +5431,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5136
5431
|
},
|
|
5137
5432
|
[restoreTo]
|
|
5138
5433
|
);
|
|
5139
|
-
const toggleFavorite =
|
|
5434
|
+
const toggleFavorite = useCallback14(
|
|
5140
5435
|
(trackId, index) => {
|
|
5141
5436
|
const h = dataRef.current[trackId];
|
|
5142
5437
|
if (!h || index < 0 || index >= h.entries.length) return;
|
|
@@ -5145,7 +5440,7 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5145
5440
|
},
|
|
5146
5441
|
[commit]
|
|
5147
5442
|
);
|
|
5148
|
-
const restore =
|
|
5443
|
+
const restore = useCallback14(
|
|
5149
5444
|
(trackId, state) => {
|
|
5150
5445
|
const entries = Array.isArray(state?.entries) ? [...state.entries] : [];
|
|
5151
5446
|
const raw = typeof state?.cursor === "number" ? state.cursor : entries.length - 1;
|
|
@@ -5154,15 +5449,15 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5154
5449
|
},
|
|
5155
5450
|
[commit]
|
|
5156
5451
|
);
|
|
5157
|
-
const list =
|
|
5452
|
+
const list = useCallback14(
|
|
5158
5453
|
(trackId) => dataRef.current[trackId] ?? EMPTY,
|
|
5159
5454
|
[]
|
|
5160
5455
|
);
|
|
5161
|
-
const canUndo =
|
|
5456
|
+
const canUndo = useCallback14((trackId) => {
|
|
5162
5457
|
const h = dataRef.current[trackId];
|
|
5163
5458
|
return !!h && h.cursor > 0;
|
|
5164
5459
|
}, []);
|
|
5165
|
-
const clear =
|
|
5460
|
+
const clear = useCallback14(
|
|
5166
5461
|
(trackId) => {
|
|
5167
5462
|
if (dataRef.current[trackId]) {
|
|
5168
5463
|
const next = { ...dataRef.current };
|
|
@@ -5174,11 +5469,11 @@ function useSoundHistory(applySound, opts = {}) {
|
|
|
5174
5469
|
},
|
|
5175
5470
|
[bump]
|
|
5176
5471
|
);
|
|
5177
|
-
const reset =
|
|
5472
|
+
const reset = useCallback14(() => {
|
|
5178
5473
|
dataRef.current = {};
|
|
5179
5474
|
bump();
|
|
5180
5475
|
}, [bump]);
|
|
5181
|
-
return
|
|
5476
|
+
return useMemo9(
|
|
5182
5477
|
() => ({ record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite }),
|
|
5183
5478
|
[record, undo, restoreTo, list, canUndo, clear, reset, restore, toggleFavorite]
|
|
5184
5479
|
);
|
|
@@ -5311,7 +5606,7 @@ function resolveTrackGroups(parsedGroups, tracks, getDbId, opts = {}) {
|
|
|
5311
5606
|
}
|
|
5312
5607
|
|
|
5313
5608
|
// src/panel-core/useTransitionOps.ts
|
|
5314
|
-
import { useCallback as
|
|
5609
|
+
import { useCallback as useCallback15, useEffect as useEffect17, useRef as useRef18, useState as useState22 } from "react";
|
|
5315
5610
|
function useTransitionOps({
|
|
5316
5611
|
host,
|
|
5317
5612
|
adapter,
|
|
@@ -5328,8 +5623,8 @@ function useTransitionOps({
|
|
|
5328
5623
|
resolvedFades
|
|
5329
5624
|
}) {
|
|
5330
5625
|
const { identity } = adapter;
|
|
5331
|
-
const appliedFadeAutomationRef =
|
|
5332
|
-
const applyCrossfadeAutomation =
|
|
5626
|
+
const appliedFadeAutomationRef = useRef18(/* @__PURE__ */ new Set());
|
|
5627
|
+
const applyCrossfadeAutomation = useCallback15(
|
|
5333
5628
|
async (originTrackId, targetTrackId, bars, bpm, sliderPos) => {
|
|
5334
5629
|
if (host.setTrackVolumeAutomation) {
|
|
5335
5630
|
const curves = buildCrossfadeVolumeCurves(bars, bpm, sliderPos);
|
|
@@ -5346,7 +5641,7 @@ function useTransitionOps({
|
|
|
5346
5641
|
},
|
|
5347
5642
|
[host]
|
|
5348
5643
|
);
|
|
5349
|
-
const applyFadeAutomation =
|
|
5644
|
+
const applyFadeAutomation = useCallback15(
|
|
5350
5645
|
async (trackId, direction, bars, bpm, sliderPos, gesture) => {
|
|
5351
5646
|
if (!host.setTrackVolumeAutomation) return;
|
|
5352
5647
|
const points = buildFadeVolumeCurve(bars, bpm, direction, sliderPos, gesture);
|
|
@@ -5355,8 +5650,8 @@ function useTransitionOps({
|
|
|
5355
5650
|
},
|
|
5356
5651
|
[host]
|
|
5357
5652
|
);
|
|
5358
|
-
const [isCreatingCrossfade, setIsCreatingCrossfade] =
|
|
5359
|
-
const handleCreateCrossfade =
|
|
5653
|
+
const [isCreatingCrossfade, setIsCreatingCrossfade] = useState22(false);
|
|
5654
|
+
const handleCreateCrossfade = useCallback15(
|
|
5360
5655
|
async (origin, target) => {
|
|
5361
5656
|
const scene = activeSceneId;
|
|
5362
5657
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -5484,8 +5779,8 @@ function useTransitionOps({
|
|
|
5484
5779
|
loadTracks
|
|
5485
5780
|
]
|
|
5486
5781
|
);
|
|
5487
|
-
const [isCreatingFade, setIsCreatingFade] =
|
|
5488
|
-
const handleCreateFade =
|
|
5782
|
+
const [isCreatingFade, setIsCreatingFade] = useState22(false);
|
|
5783
|
+
const handleCreateFade = useCallback15(
|
|
5489
5784
|
async (selection, direction, gesture) => {
|
|
5490
5785
|
const scene = activeSceneId;
|
|
5491
5786
|
const fromSceneId = sceneContext?.transitionFromSceneId ?? "";
|
|
@@ -5595,7 +5890,7 @@ function useTransitionOps({
|
|
|
5595
5890
|
loadTracks
|
|
5596
5891
|
]
|
|
5597
5892
|
);
|
|
5598
|
-
const handleCrossfadeMute =
|
|
5893
|
+
const handleCrossfadeMute = useCallback15(
|
|
5599
5894
|
(pair) => {
|
|
5600
5895
|
const newMuted = !pair.origin.runtimeState.muted;
|
|
5601
5896
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -5610,7 +5905,7 @@ function useTransitionOps({
|
|
|
5610
5905
|
},
|
|
5611
5906
|
[host, setTracks]
|
|
5612
5907
|
);
|
|
5613
|
-
const handleCrossfadeSolo =
|
|
5908
|
+
const handleCrossfadeSolo = useCallback15(
|
|
5614
5909
|
(pair) => {
|
|
5615
5910
|
const newSolo = !pair.origin.runtimeState.solo;
|
|
5616
5911
|
for (const id of [pair.origin.handle.id, pair.target.handle.id]) {
|
|
@@ -5625,7 +5920,7 @@ function useTransitionOps({
|
|
|
5625
5920
|
},
|
|
5626
5921
|
[host, setTracks]
|
|
5627
5922
|
);
|
|
5628
|
-
const handleCrossfadeDelete =
|
|
5923
|
+
const handleCrossfadeDelete = useCallback15(
|
|
5629
5924
|
async (pair) => {
|
|
5630
5925
|
try {
|
|
5631
5926
|
for (const member of [pair.origin, pair.target]) {
|
|
@@ -5651,8 +5946,8 @@ function useTransitionOps({
|
|
|
5651
5946
|
},
|
|
5652
5947
|
[host, activeSceneId, setCrossfadePairsMeta, setTracks]
|
|
5653
5948
|
);
|
|
5654
|
-
const crossfadeSliderTimers =
|
|
5655
|
-
const handleCrossfadeSlider =
|
|
5949
|
+
const crossfadeSliderTimers = useRef18({});
|
|
5950
|
+
const handleCrossfadeSlider = useCallback15(
|
|
5656
5951
|
(pair, pos) => {
|
|
5657
5952
|
setCrossfadePairsMeta(
|
|
5658
5953
|
(prev) => prev.map((p) => p.groupId === pair.groupId ? { ...p, sliderPos: pos } : p)
|
|
@@ -5685,7 +5980,7 @@ function useTransitionOps({
|
|
|
5685
5980
|
},
|
|
5686
5981
|
[host, activeSceneId, applyCrossfadeAutomation, setCrossfadePairsMeta]
|
|
5687
5982
|
);
|
|
5688
|
-
const handleFadeDelete =
|
|
5983
|
+
const handleFadeDelete = useCallback15(
|
|
5689
5984
|
async (fade) => {
|
|
5690
5985
|
try {
|
|
5691
5986
|
await host.deleteTrack(fade.track.handle.id);
|
|
@@ -5705,8 +6000,8 @@ function useTransitionOps({
|
|
|
5705
6000
|
},
|
|
5706
6001
|
[host, activeSceneId, setFadesMeta, setTracks]
|
|
5707
6002
|
);
|
|
5708
|
-
const fadeSliderTimers =
|
|
5709
|
-
const handleFadeSlider =
|
|
6003
|
+
const fadeSliderTimers = useRef18({});
|
|
6004
|
+
const handleFadeSlider = useCallback15(
|
|
5710
6005
|
(fade, pos) => {
|
|
5711
6006
|
setFadesMeta(
|
|
5712
6007
|
(prev) => prev.map((f) => f.dbId === fade.dbId ? { ...f, meta: { ...f.meta, sliderPos: pos } } : f)
|
|
@@ -5736,8 +6031,8 @@ function useTransitionOps({
|
|
|
5736
6031
|
},
|
|
5737
6032
|
[host, activeSceneId, applyFadeAutomation, setFadesMeta]
|
|
5738
6033
|
);
|
|
5739
|
-
const lastResyncKeyRef =
|
|
5740
|
-
|
|
6034
|
+
const lastResyncKeyRef = useRef18("");
|
|
6035
|
+
useEffect17(() => {
|
|
5741
6036
|
if (!host.getTrackSound || resolvedCrossfadePairs.length === 0 && resolvedFades.length === 0) {
|
|
5742
6037
|
return;
|
|
5743
6038
|
}
|
|
@@ -5778,7 +6073,7 @@ function useTransitionOps({
|
|
|
5778
6073
|
cancelled = true;
|
|
5779
6074
|
};
|
|
5780
6075
|
}, [resolvedCrossfadePairs, resolvedFades, host, adapter]);
|
|
5781
|
-
|
|
6076
|
+
useEffect17(() => {
|
|
5782
6077
|
if (!host.setTrackVolumeAutomation || resolvedFades.length === 0) return;
|
|
5783
6078
|
void (async () => {
|
|
5784
6079
|
const mc = await host.getMusicalContext();
|
|
@@ -5812,7 +6107,7 @@ function useTransitionOps({
|
|
|
5812
6107
|
}
|
|
5813
6108
|
|
|
5814
6109
|
// src/panel-core/useGeneratorPanelCore.tsx
|
|
5815
|
-
import { jsx as
|
|
6110
|
+
import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
5816
6111
|
var EMPTY_PLACEHOLDERS = [];
|
|
5817
6112
|
function useGeneratorPanelCore({
|
|
5818
6113
|
ui,
|
|
@@ -5832,8 +6127,8 @@ function useGeneratorPanelCore({
|
|
|
5832
6127
|
} = ui;
|
|
5833
6128
|
const { identity, features } = adapter;
|
|
5834
6129
|
const logTag = identity.logTag;
|
|
5835
|
-
const adapterRef =
|
|
5836
|
-
|
|
6130
|
+
const adapterRef = useRef19(adapter);
|
|
6131
|
+
useEffect18(() => {
|
|
5837
6132
|
if (adapterRef.current !== adapter) {
|
|
5838
6133
|
adapterRef.current = adapter;
|
|
5839
6134
|
console.warn(
|
|
@@ -5843,27 +6138,27 @@ function useGeneratorPanelCore({
|
|
|
5843
6138
|
}, [adapter, logTag]);
|
|
5844
6139
|
const supportsMeters = typeof host.getTrackLevels === "function";
|
|
5845
6140
|
const trackLevels = useTrackLevels(host, isExpanded);
|
|
5846
|
-
const [tracks, setTracks] =
|
|
5847
|
-
const [isLoadingTracks, setIsLoadingTracks] =
|
|
5848
|
-
const [importOpen, setImportOpen] =
|
|
5849
|
-
const [soundImportTarget, setSoundImportTarget] =
|
|
5850
|
-
const [designerView, setDesignerView] =
|
|
5851
|
-
const [transitionSourceTotal, setTransitionSourceTotal] =
|
|
5852
|
-
const [crossfadePairsMeta, setCrossfadePairsMeta] =
|
|
5853
|
-
const [fadesMeta, setFadesMeta] =
|
|
5854
|
-
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({});
|
|
5855
6150
|
const [isComposing, , setIsComposingForScene] = useSceneState(activeSceneId, false);
|
|
5856
6151
|
const [placeholders, , setPlaceholdersForScene] = useSceneState(
|
|
5857
6152
|
activeSceneId,
|
|
5858
6153
|
EMPTY_PLACEHOLDERS
|
|
5859
6154
|
);
|
|
5860
|
-
const saveTimeoutRefs =
|
|
5861
|
-
const editLoadStartedRef =
|
|
5862
|
-
const [availableInstruments, setAvailableInstruments] =
|
|
5863
|
-
const [instrumentsLoading, setInstrumentsLoading] =
|
|
5864
|
-
const engineToDbIdRef =
|
|
5865
|
-
const tracksLoadedForSceneRef =
|
|
5866
|
-
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(
|
|
5867
6162
|
(trackId, state) => {
|
|
5868
6163
|
if (!activeSceneId) return;
|
|
5869
6164
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -5883,7 +6178,7 @@ function useGeneratorPanelCore({
|
|
|
5883
6178
|
setItems: setTracks,
|
|
5884
6179
|
getId: (t) => t.handle.dbId
|
|
5885
6180
|
});
|
|
5886
|
-
const loadTracks =
|
|
6181
|
+
const loadTracks = useCallback16(
|
|
5887
6182
|
async (incremental = false) => {
|
|
5888
6183
|
const sceneAtStart = activeSceneId;
|
|
5889
6184
|
if (!sceneAtStart) {
|
|
@@ -6008,18 +6303,18 @@ function useGeneratorPanelCore({
|
|
|
6008
6303
|
},
|
|
6009
6304
|
[host, activeSceneId, soundHistory, adapter, logTag]
|
|
6010
6305
|
);
|
|
6011
|
-
|
|
6306
|
+
useEffect18(() => {
|
|
6012
6307
|
loadTracks();
|
|
6013
6308
|
}, [loadTracks]);
|
|
6014
|
-
|
|
6309
|
+
useEffect18(() => {
|
|
6015
6310
|
const map = /* @__PURE__ */ new Map();
|
|
6016
6311
|
for (const t of tracks) {
|
|
6017
6312
|
map.set(t.handle.id, t.handle.dbId);
|
|
6018
6313
|
}
|
|
6019
6314
|
engineToDbIdRef.current = map;
|
|
6020
6315
|
}, [tracks]);
|
|
6021
|
-
const loadedCompletedIdsRef =
|
|
6022
|
-
|
|
6316
|
+
const loadedCompletedIdsRef = useRef19(/* @__PURE__ */ new Set());
|
|
6317
|
+
useEffect18(() => {
|
|
6023
6318
|
if (placeholders.length === 0) {
|
|
6024
6319
|
loadedCompletedIdsRef.current.clear();
|
|
6025
6320
|
return;
|
|
@@ -6038,16 +6333,16 @@ function useGeneratorPanelCore({
|
|
|
6038
6333
|
loadTracks(true);
|
|
6039
6334
|
}
|
|
6040
6335
|
}, [placeholders, loadTracks, logTag]);
|
|
6041
|
-
const adoptAndLoad =
|
|
6336
|
+
const adoptAndLoad = useCallback16(() => {
|
|
6042
6337
|
loadTracks(true);
|
|
6043
6338
|
}, [loadTracks]);
|
|
6044
|
-
|
|
6339
|
+
useEffect18(() => {
|
|
6045
6340
|
const unsub = host.onEngineReady(() => {
|
|
6046
6341
|
adoptAndLoad();
|
|
6047
6342
|
});
|
|
6048
6343
|
return unsub;
|
|
6049
6344
|
}, [host, adoptAndLoad]);
|
|
6050
|
-
|
|
6345
|
+
useEffect18(() => {
|
|
6051
6346
|
if (typeof host.onAfterAgentMutation !== "function") return;
|
|
6052
6347
|
let timer = null;
|
|
6053
6348
|
const unsub = host.onAfterAgentMutation(() => {
|
|
@@ -6062,13 +6357,13 @@ function useGeneratorPanelCore({
|
|
|
6062
6357
|
if (timer) clearTimeout(timer);
|
|
6063
6358
|
};
|
|
6064
6359
|
}, [host, loadTracks]);
|
|
6065
|
-
|
|
6360
|
+
useEffect18(() => {
|
|
6066
6361
|
const unsub = host.onTrackStateChange((trackId, state) => {
|
|
6067
6362
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: state } : t));
|
|
6068
6363
|
});
|
|
6069
6364
|
return unsub;
|
|
6070
6365
|
}, [host]);
|
|
6071
|
-
|
|
6366
|
+
useEffect18(() => {
|
|
6072
6367
|
if (!features.bulkComposePlaceholders) return;
|
|
6073
6368
|
console.log(`[${logTag}] Subscribing to composeProgress`);
|
|
6074
6369
|
const unsub = host.onComposeProgress((event) => {
|
|
@@ -6102,7 +6397,7 @@ function useGeneratorPanelCore({
|
|
|
6102
6397
|
});
|
|
6103
6398
|
return unsub;
|
|
6104
6399
|
}, [host, setIsComposingForScene, setPlaceholdersForScene, features.bulkComposePlaceholders, logTag]);
|
|
6105
|
-
|
|
6400
|
+
useEffect18(() => {
|
|
6106
6401
|
const refs = saveTimeoutRefs;
|
|
6107
6402
|
return () => {
|
|
6108
6403
|
for (const timeout of Object.values(refs.current)) {
|
|
@@ -6110,9 +6405,9 @@ function useGeneratorPanelCore({
|
|
|
6110
6405
|
}
|
|
6111
6406
|
};
|
|
6112
6407
|
}, []);
|
|
6113
|
-
const isAddingTrackRef =
|
|
6114
|
-
const [isAddingTrack, setIsAddingTrack] =
|
|
6115
|
-
const handleAddTrack =
|
|
6408
|
+
const isAddingTrackRef = useRef19(false);
|
|
6409
|
+
const [isAddingTrack, setIsAddingTrack] = useState23(false);
|
|
6410
|
+
const handleAddTrack = useCallback16(async () => {
|
|
6116
6411
|
if (isAddingTrackRef.current) return;
|
|
6117
6412
|
if (!activeSceneId) {
|
|
6118
6413
|
host.showToast("warning", "Select SCENE");
|
|
@@ -6152,7 +6447,7 @@ function useGeneratorPanelCore({
|
|
|
6152
6447
|
setIsAddingTrack(false);
|
|
6153
6448
|
}
|
|
6154
6449
|
}, [host, adapter, identity, activeSceneId, isConnected, isAuthenticated, tracks.length, onExpandSelf]);
|
|
6155
|
-
const handlePortTrack =
|
|
6450
|
+
const handlePortTrack = useCallback16(
|
|
6156
6451
|
async (sel) => {
|
|
6157
6452
|
if (!activeSceneId) {
|
|
6158
6453
|
host.showToast("warning", "Select SCENE");
|
|
@@ -6209,7 +6504,7 @@ function useGeneratorPanelCore({
|
|
|
6209
6504
|
},
|
|
6210
6505
|
[host, adapter, identity, activeSceneId, isConnected, tracks.length, loadTracks]
|
|
6211
6506
|
);
|
|
6212
|
-
const handleSoundImportPick =
|
|
6507
|
+
const handleSoundImportPick = useCallback16(
|
|
6213
6508
|
async (sel) => {
|
|
6214
6509
|
const target = soundImportTarget;
|
|
6215
6510
|
if (!target || !host.getTrackSound) {
|
|
@@ -6240,8 +6535,8 @@ function useGeneratorPanelCore({
|
|
|
6240
6535
|
},
|
|
6241
6536
|
[soundImportTarget, host, adapter, identity.familyKey, soundHistory]
|
|
6242
6537
|
);
|
|
6243
|
-
const [isExportingMidi, setIsExportingMidi] =
|
|
6244
|
-
const handleExportMidi =
|
|
6538
|
+
const [isExportingMidi, setIsExportingMidi] = useState23(false);
|
|
6539
|
+
const handleExportMidi = useCallback16(async () => {
|
|
6245
6540
|
if (isExportingMidi) return;
|
|
6246
6541
|
setIsExportingMidi(true);
|
|
6247
6542
|
try {
|
|
@@ -6272,10 +6567,10 @@ function useGeneratorPanelCore({
|
|
|
6272
6567
|
const xfFromId = sceneContext?.transitionFromSceneId ?? null;
|
|
6273
6568
|
const xfToId = sceneContext?.transitionToSceneId ?? null;
|
|
6274
6569
|
const canCrossfade = features.transitionDesigner && sceneContext?.sceneType === "transition" && !!xfFromId && !!xfToId && !!host.listSceneFamilyTracks;
|
|
6275
|
-
|
|
6570
|
+
useEffect18(() => {
|
|
6276
6571
|
if (!canCrossfade) setDesignerView(false);
|
|
6277
6572
|
}, [canCrossfade]);
|
|
6278
|
-
|
|
6573
|
+
useEffect18(() => {
|
|
6279
6574
|
if (!canCrossfade || !xfFromId || !xfToId || !host.listSceneFamilyTracks) {
|
|
6280
6575
|
setTransitionSourceTotal(0);
|
|
6281
6576
|
return;
|
|
@@ -6291,12 +6586,12 @@ function useGeneratorPanelCore({
|
|
|
6291
6586
|
};
|
|
6292
6587
|
}, [canCrossfade, xfFromId, xfToId, host]);
|
|
6293
6588
|
const transitionDone = crossfadePairsMeta.length * 2 + fadesMeta.length;
|
|
6294
|
-
|
|
6589
|
+
useEffect18(() => {
|
|
6295
6590
|
if (!onHeaderContent) return;
|
|
6296
6591
|
const addDisabled = needsContract || !isConnected || !activeSceneId || tracks.length >= identity.maxTracks || isAddingTrack;
|
|
6297
6592
|
onHeaderContent(
|
|
6298
|
-
/* @__PURE__ */
|
|
6299
|
-
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(
|
|
6300
6595
|
"button",
|
|
6301
6596
|
{
|
|
6302
6597
|
"data-testid": `import-from-scene-${identity.familyKey}-button`,
|
|
@@ -6310,7 +6605,7 @@ function useGeneratorPanelCore({
|
|
|
6310
6605
|
children: identity.importTrackLabel ?? "Import Track"
|
|
6311
6606
|
}
|
|
6312
6607
|
),
|
|
6313
|
-
(!canCrossfade || !designerView) && /* @__PURE__ */
|
|
6608
|
+
(!canCrossfade || !designerView) && /* @__PURE__ */ jsx25(
|
|
6314
6609
|
"button",
|
|
6315
6610
|
{
|
|
6316
6611
|
"data-testid": `add-${identity.familyKey}-track-button`,
|
|
@@ -6326,7 +6621,7 @@ function useGeneratorPanelCore({
|
|
|
6326
6621
|
children: identity.addTrackLabel ?? "Add Track"
|
|
6327
6622
|
}
|
|
6328
6623
|
),
|
|
6329
|
-
canCrossfade && /* @__PURE__ */
|
|
6624
|
+
canCrossfade && /* @__PURE__ */ jsxs19(
|
|
6330
6625
|
"button",
|
|
6331
6626
|
{
|
|
6332
6627
|
"data-testid": `${identity.familyKey}-view-toggle`,
|
|
@@ -6345,7 +6640,7 @@ function useGeneratorPanelCore({
|
|
|
6345
6640
|
title: designerView ? "Back to the track list" : "Open the transition designer",
|
|
6346
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",
|
|
6347
6642
|
children: [
|
|
6348
|
-
transitionSourceTotal > 0 && /* @__PURE__ */
|
|
6643
|
+
transitionSourceTotal > 0 && /* @__PURE__ */ jsx25(
|
|
6349
6644
|
"span",
|
|
6350
6645
|
{
|
|
6351
6646
|
className: "absolute inset-y-0 left-0 bg-sas-accent/25",
|
|
@@ -6353,7 +6648,7 @@ function useGeneratorPanelCore({
|
|
|
6353
6648
|
"aria-hidden": true
|
|
6354
6649
|
}
|
|
6355
6650
|
),
|
|
6356
|
-
/* @__PURE__ */
|
|
6651
|
+
/* @__PURE__ */ jsxs19("span", { className: "relative", children: [
|
|
6357
6652
|
"\u21C4 ",
|
|
6358
6653
|
designerView ? "Transition" : "Tracks",
|
|
6359
6654
|
transitionSourceTotal > 0 ? ` ${transitionDone}/${transitionSourceTotal}` : ""
|
|
@@ -6384,7 +6679,7 @@ function useGeneratorPanelCore({
|
|
|
6384
6679
|
identity,
|
|
6385
6680
|
features.importTracks
|
|
6386
6681
|
]);
|
|
6387
|
-
|
|
6682
|
+
useEffect18(() => {
|
|
6388
6683
|
if (!onLoading) return;
|
|
6389
6684
|
const anyGenerating = tracks.some((t) => t.isGenerating);
|
|
6390
6685
|
onLoading(isLoadingTracks || anyGenerating || isBulkActive);
|
|
@@ -6392,7 +6687,7 @@ function useGeneratorPanelCore({
|
|
|
6392
6687
|
onLoading(false);
|
|
6393
6688
|
};
|
|
6394
6689
|
}, [onLoading, isLoadingTracks, tracks, isBulkActive]);
|
|
6395
|
-
const handleDeleteTrack =
|
|
6690
|
+
const handleDeleteTrack = useCallback16(
|
|
6396
6691
|
async (trackId) => {
|
|
6397
6692
|
try {
|
|
6398
6693
|
await host.deleteTrack(trackId);
|
|
@@ -6408,7 +6703,7 @@ function useGeneratorPanelCore({
|
|
|
6408
6703
|
},
|
|
6409
6704
|
[host, activeSceneId]
|
|
6410
6705
|
);
|
|
6411
|
-
const handlePromptChange =
|
|
6706
|
+
const handlePromptChange = useCallback16(
|
|
6412
6707
|
(trackId, prompt) => {
|
|
6413
6708
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, prompt } : t));
|
|
6414
6709
|
const dbId = engineToDbIdRef.current.get(trackId) ?? trackId;
|
|
@@ -6424,7 +6719,7 @@ function useGeneratorPanelCore({
|
|
|
6424
6719
|
},
|
|
6425
6720
|
[host, activeSceneId]
|
|
6426
6721
|
);
|
|
6427
|
-
const resolvedGenericGroups =
|
|
6722
|
+
const resolvedGenericGroups = useMemo10(() => {
|
|
6428
6723
|
const out = {};
|
|
6429
6724
|
for (const ext of adapter.groupExtensions ?? []) {
|
|
6430
6725
|
out[ext.metaKey] = resolveTrackGroups(
|
|
@@ -6438,18 +6733,18 @@ function useGeneratorPanelCore({
|
|
|
6438
6733
|
}
|
|
6439
6734
|
return out;
|
|
6440
6735
|
}, [adapter, genericGroupMetas, tracks]);
|
|
6441
|
-
const genericGroupMemberDbIds =
|
|
6736
|
+
const genericGroupMemberDbIds = useMemo10(() => {
|
|
6442
6737
|
const s = /* @__PURE__ */ new Set();
|
|
6443
6738
|
for (const r of Object.values(resolvedGenericGroups)) {
|
|
6444
6739
|
for (const dbId of r.memberDbIds) s.add(dbId);
|
|
6445
6740
|
}
|
|
6446
6741
|
return s;
|
|
6447
6742
|
}, [resolvedGenericGroups]);
|
|
6448
|
-
const engineToDbId =
|
|
6743
|
+
const engineToDbId = useCallback16(
|
|
6449
6744
|
(trackId) => engineToDbIdRef.current.get(trackId) ?? trackId,
|
|
6450
6745
|
[]
|
|
6451
6746
|
);
|
|
6452
|
-
const updateTrack =
|
|
6747
|
+
const updateTrack = useCallback16(
|
|
6453
6748
|
(trackId, patch) => {
|
|
6454
6749
|
setTracks(
|
|
6455
6750
|
(prev) => prev.map(
|
|
@@ -6459,18 +6754,18 @@ function useGeneratorPanelCore({
|
|
|
6459
6754
|
},
|
|
6460
6755
|
[]
|
|
6461
6756
|
);
|
|
6462
|
-
const markEditLoaded =
|
|
6757
|
+
const markEditLoaded = useCallback16((trackId) => {
|
|
6463
6758
|
editLoadStartedRef.current.add(trackId);
|
|
6464
6759
|
}, []);
|
|
6465
|
-
const tracksRef =
|
|
6466
|
-
|
|
6760
|
+
const tracksRef = useRef19(tracks);
|
|
6761
|
+
useEffect18(() => {
|
|
6467
6762
|
tracksRef.current = tracks;
|
|
6468
6763
|
}, [tracks]);
|
|
6469
|
-
const resolvedGenericGroupsRef =
|
|
6470
|
-
|
|
6764
|
+
const resolvedGenericGroupsRef = useRef19(resolvedGenericGroups);
|
|
6765
|
+
useEffect18(() => {
|
|
6471
6766
|
resolvedGenericGroupsRef.current = resolvedGenericGroups;
|
|
6472
6767
|
}, [resolvedGenericGroups]);
|
|
6473
|
-
const makeServices =
|
|
6768
|
+
const makeServices = useCallback16(() => {
|
|
6474
6769
|
return {
|
|
6475
6770
|
host,
|
|
6476
6771
|
activeSceneId,
|
|
@@ -6489,7 +6784,7 @@ function useGeneratorPanelCore({
|
|
|
6489
6784
|
resolvedGroups: (metaKey) => resolvedGenericGroupsRef.current[metaKey]?.resolved ?? []
|
|
6490
6785
|
};
|
|
6491
6786
|
}, [host, activeSceneId, updateTrack, loadTracks, soundHistory, engineToDbId, markEditLoaded, identity, adapter]);
|
|
6492
|
-
const handleGenerate =
|
|
6787
|
+
const handleGenerate = useCallback16(
|
|
6493
6788
|
async (trackId) => {
|
|
6494
6789
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6495
6790
|
if (!track || !track.prompt.trim()) return;
|
|
@@ -6516,7 +6811,7 @@ function useGeneratorPanelCore({
|
|
|
6516
6811
|
},
|
|
6517
6812
|
[host, adapter, tracks, isAuthenticated, makeServices]
|
|
6518
6813
|
);
|
|
6519
|
-
const handleMuteToggle =
|
|
6814
|
+
const handleMuteToggle = useCallback16(
|
|
6520
6815
|
(trackId) => {
|
|
6521
6816
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6522
6817
|
if (!track) return;
|
|
@@ -6536,7 +6831,7 @@ function useGeneratorPanelCore({
|
|
|
6536
6831
|
},
|
|
6537
6832
|
[host, tracks]
|
|
6538
6833
|
);
|
|
6539
|
-
const handleSoloToggle =
|
|
6834
|
+
const handleSoloToggle = useCallback16(
|
|
6540
6835
|
(trackId) => {
|
|
6541
6836
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6542
6837
|
if (!track) return;
|
|
@@ -6556,7 +6851,7 @@ function useGeneratorPanelCore({
|
|
|
6556
6851
|
},
|
|
6557
6852
|
[host, tracks]
|
|
6558
6853
|
);
|
|
6559
|
-
const handleVolumeChange =
|
|
6854
|
+
const handleVolumeChange = useCallback16(
|
|
6560
6855
|
(trackId, volume) => {
|
|
6561
6856
|
setTracks(
|
|
6562
6857
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, volume } } : t)
|
|
@@ -6566,7 +6861,7 @@ function useGeneratorPanelCore({
|
|
|
6566
6861
|
},
|
|
6567
6862
|
[host]
|
|
6568
6863
|
);
|
|
6569
|
-
const handlePanChange =
|
|
6864
|
+
const handlePanChange = useCallback16(
|
|
6570
6865
|
(trackId, pan) => {
|
|
6571
6866
|
setTracks(
|
|
6572
6867
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, runtimeState: { ...t.runtimeState, pan } } : t)
|
|
@@ -6576,7 +6871,7 @@ function useGeneratorPanelCore({
|
|
|
6576
6871
|
},
|
|
6577
6872
|
[host]
|
|
6578
6873
|
);
|
|
6579
|
-
const handleShuffle =
|
|
6874
|
+
const handleShuffle = useCallback16(
|
|
6580
6875
|
async (trackId) => {
|
|
6581
6876
|
const track = tracks.find((t) => t.handle.id === trackId);
|
|
6582
6877
|
if (!track) return;
|
|
@@ -6618,7 +6913,7 @@ function useGeneratorPanelCore({
|
|
|
6618
6913
|
},
|
|
6619
6914
|
[host, adapter, tracks, soundHistory, logTag]
|
|
6620
6915
|
);
|
|
6621
|
-
const handleCopy =
|
|
6916
|
+
const handleCopy = useCallback16(
|
|
6622
6917
|
async (trackId) => {
|
|
6623
6918
|
try {
|
|
6624
6919
|
const newHandle = await host.duplicateTrack(trackId);
|
|
@@ -6631,7 +6926,7 @@ function useGeneratorPanelCore({
|
|
|
6631
6926
|
},
|
|
6632
6927
|
[host, loadTracks]
|
|
6633
6928
|
);
|
|
6634
|
-
const handleFxToggle =
|
|
6929
|
+
const handleFxToggle = useCallback16(
|
|
6635
6930
|
(trackId, category, enabled) => {
|
|
6636
6931
|
setTracks(
|
|
6637
6932
|
(prev) => prev.map(
|
|
@@ -6654,7 +6949,7 @@ function useGeneratorPanelCore({
|
|
|
6654
6949
|
},
|
|
6655
6950
|
[host]
|
|
6656
6951
|
);
|
|
6657
|
-
const handleFxPresetChange =
|
|
6952
|
+
const handleFxPresetChange = useCallback16(
|
|
6658
6953
|
(trackId, category, presetIndex) => {
|
|
6659
6954
|
setTracks(
|
|
6660
6955
|
(prev) => prev.map(
|
|
@@ -6680,7 +6975,7 @@ function useGeneratorPanelCore({
|
|
|
6680
6975
|
},
|
|
6681
6976
|
[host]
|
|
6682
6977
|
);
|
|
6683
|
-
const handleFxDryWetChange =
|
|
6978
|
+
const handleFxDryWetChange = useCallback16(
|
|
6684
6979
|
(trackId, category, value) => {
|
|
6685
6980
|
setTracks(
|
|
6686
6981
|
(prev) => prev.map(
|
|
@@ -6692,7 +6987,7 @@ function useGeneratorPanelCore({
|
|
|
6692
6987
|
},
|
|
6693
6988
|
[host]
|
|
6694
6989
|
);
|
|
6695
|
-
const toggleFxDrawer =
|
|
6990
|
+
const toggleFxDrawer = useCallback16(
|
|
6696
6991
|
(trackId) => {
|
|
6697
6992
|
setTracks(
|
|
6698
6993
|
(prev) => prev.map((t) => {
|
|
@@ -6714,7 +7009,7 @@ function useGeneratorPanelCore({
|
|
|
6714
7009
|
},
|
|
6715
7010
|
[host, tracks]
|
|
6716
7011
|
);
|
|
6717
|
-
const loadEditNotes =
|
|
7012
|
+
const loadEditNotes = useCallback16(
|
|
6718
7013
|
async (trackId) => {
|
|
6719
7014
|
try {
|
|
6720
7015
|
const mc = await host.getMusicalContext();
|
|
@@ -6732,7 +7027,7 @@ function useGeneratorPanelCore({
|
|
|
6732
7027
|
},
|
|
6733
7028
|
[host, logTag]
|
|
6734
7029
|
);
|
|
6735
|
-
const handleNotesChange =
|
|
7030
|
+
const handleNotesChange = useCallback16(
|
|
6736
7031
|
(trackId, notes) => {
|
|
6737
7032
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editNotes: notes } : t));
|
|
6738
7033
|
const key = `edit:${trackId}`;
|
|
@@ -6762,7 +7057,7 @@ function useGeneratorPanelCore({
|
|
|
6762
7057
|
},
|
|
6763
7058
|
[host]
|
|
6764
7059
|
);
|
|
6765
|
-
const handleTabChange =
|
|
7060
|
+
const handleTabChange = useCallback16(
|
|
6766
7061
|
(trackId, tab) => {
|
|
6767
7062
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, drawerOpen: true, drawerTab: tab } : t));
|
|
6768
7063
|
if (tab === "fx") {
|
|
@@ -6787,10 +7082,10 @@ function useGeneratorPanelCore({
|
|
|
6787
7082
|
},
|
|
6788
7083
|
[host, availableInstruments.length, instrumentsLoading, loadEditNotes]
|
|
6789
7084
|
);
|
|
6790
|
-
const handleProgressChange =
|
|
7085
|
+
const handleProgressChange = useCallback16((trackId, pct) => {
|
|
6791
7086
|
setTracks((prev) => prev.map((t) => t.handle.id === trackId ? { ...t, generationProgress: pct } : t));
|
|
6792
7087
|
}, []);
|
|
6793
|
-
const handleToggleDrawer =
|
|
7088
|
+
const handleToggleDrawer = useCallback16((trackId) => {
|
|
6794
7089
|
setTracks(
|
|
6795
7090
|
(prev) => prev.map((t) => {
|
|
6796
7091
|
if (t.handle.id !== trackId) return t;
|
|
@@ -6799,7 +7094,7 @@ function useGeneratorPanelCore({
|
|
|
6799
7094
|
})
|
|
6800
7095
|
);
|
|
6801
7096
|
}, []);
|
|
6802
|
-
const handleInstrumentSelect =
|
|
7097
|
+
const handleInstrumentSelect = useCallback16(
|
|
6803
7098
|
async (trackId, pluginId) => {
|
|
6804
7099
|
const isDefaultInstrument = pluginId === (identity.defaultInstrumentPluginId ?? "Surge XT");
|
|
6805
7100
|
if (isDefaultInstrument) {
|
|
@@ -6852,7 +7147,7 @@ function useGeneratorPanelCore({
|
|
|
6852
7147
|
},
|
|
6853
7148
|
[host, identity.defaultInstrumentPluginId, logTag]
|
|
6854
7149
|
);
|
|
6855
|
-
const handleShowEditor =
|
|
7150
|
+
const handleShowEditor = useCallback16(
|
|
6856
7151
|
async (trackId) => {
|
|
6857
7152
|
try {
|
|
6858
7153
|
await host.showInstrumentEditor(trackId);
|
|
@@ -6863,12 +7158,12 @@ function useGeneratorPanelCore({
|
|
|
6863
7158
|
},
|
|
6864
7159
|
[host]
|
|
6865
7160
|
);
|
|
6866
|
-
const handleBackToInstruments =
|
|
7161
|
+
const handleBackToInstruments = useCallback16((trackId) => {
|
|
6867
7162
|
setTracks(
|
|
6868
7163
|
(prev) => prev.map((t) => t.handle.id === trackId ? { ...t, editorStage: false } : t)
|
|
6869
7164
|
);
|
|
6870
7165
|
}, []);
|
|
6871
|
-
const handleRefreshInstruments =
|
|
7166
|
+
const handleRefreshInstruments = useCallback16(() => {
|
|
6872
7167
|
setInstrumentsLoading(true);
|
|
6873
7168
|
host.getAvailableInstruments().then((instruments) => {
|
|
6874
7169
|
setAvailableInstruments(instruments);
|
|
@@ -6877,13 +7172,13 @@ function useGeneratorPanelCore({
|
|
|
6877
7172
|
setInstrumentsLoading(false);
|
|
6878
7173
|
});
|
|
6879
7174
|
}, [host]);
|
|
6880
|
-
const onAuditionNote =
|
|
7175
|
+
const onAuditionNote = useCallback16(
|
|
6881
7176
|
(trackId, pitch, velocity, ms) => {
|
|
6882
7177
|
void host.auditionNote(trackId, pitch, velocity, ms);
|
|
6883
7178
|
},
|
|
6884
7179
|
[host]
|
|
6885
7180
|
);
|
|
6886
|
-
const { resolvedCrossfadePairs, crossfadeMemberDbIds } =
|
|
7181
|
+
const { resolvedCrossfadePairs, crossfadeMemberDbIds } = useMemo10(() => {
|
|
6887
7182
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
6888
7183
|
const pairs = [];
|
|
6889
7184
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -6898,7 +7193,7 @@ function useGeneratorPanelCore({
|
|
|
6898
7193
|
}
|
|
6899
7194
|
return { resolvedCrossfadePairs: pairs, crossfadeMemberDbIds: members };
|
|
6900
7195
|
}, [tracks, crossfadePairsMeta]);
|
|
6901
|
-
const { resolvedFades, fadeMemberDbIds } =
|
|
7196
|
+
const { resolvedFades, fadeMemberDbIds } = useMemo10(() => {
|
|
6902
7197
|
const byDbId = new Map(tracks.map((t) => [t.handle.dbId, t]));
|
|
6903
7198
|
const list = [];
|
|
6904
7199
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -6926,7 +7221,7 @@ function useGeneratorPanelCore({
|
|
|
6926
7221
|
resolvedCrossfadePairs,
|
|
6927
7222
|
resolvedFades
|
|
6928
7223
|
});
|
|
6929
|
-
const setGroupMute =
|
|
7224
|
+
const setGroupMute = useCallback16(
|
|
6930
7225
|
(trackIds, muted) => {
|
|
6931
7226
|
for (const id of trackIds) {
|
|
6932
7227
|
setTracks(
|
|
@@ -6938,7 +7233,7 @@ function useGeneratorPanelCore({
|
|
|
6938
7233
|
},
|
|
6939
7234
|
[host]
|
|
6940
7235
|
);
|
|
6941
|
-
const setGroupSolo =
|
|
7236
|
+
const setGroupSolo = useCallback16(
|
|
6942
7237
|
(trackIds, solo) => {
|
|
6943
7238
|
for (const id of trackIds) {
|
|
6944
7239
|
setTracks(
|
|
@@ -6950,7 +7245,7 @@ function useGeneratorPanelCore({
|
|
|
6950
7245
|
},
|
|
6951
7246
|
[host]
|
|
6952
7247
|
);
|
|
6953
|
-
const deleteGroup =
|
|
7248
|
+
const deleteGroup = useCallback16(
|
|
6954
7249
|
async (members, cleanupKeySuffixes) => {
|
|
6955
7250
|
for (const member of members) {
|
|
6956
7251
|
try {
|
|
@@ -6970,7 +7265,7 @@ function useGeneratorPanelCore({
|
|
|
6970
7265
|
},
|
|
6971
7266
|
[host, activeSceneId, loadTracks]
|
|
6972
7267
|
);
|
|
6973
|
-
const handlers =
|
|
7268
|
+
const handlers = useMemo10(
|
|
6974
7269
|
() => ({
|
|
6975
7270
|
promptChange: handlePromptChange,
|
|
6976
7271
|
generate: (trackId) => {
|
|
@@ -7084,8 +7379,8 @@ function useGeneratorPanelCore({
|
|
|
7084
7379
|
}
|
|
7085
7380
|
|
|
7086
7381
|
// src/panel-core/GeneratorPanelShell.tsx
|
|
7087
|
-
import
|
|
7088
|
-
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";
|
|
7089
7384
|
function GeneratorPanelShell({ core, slots }) {
|
|
7090
7385
|
const {
|
|
7091
7386
|
ui,
|
|
@@ -7140,7 +7435,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7140
7435
|
const { host, activeSceneId, isAuthenticated, sceneContext, onSelectScene, onOpenContract } = ui;
|
|
7141
7436
|
const panelBus = usePanelBus(host, activeSceneId);
|
|
7142
7437
|
const { identity, features } = adapter;
|
|
7143
|
-
const buildRowProps =
|
|
7438
|
+
const buildRowProps = useCallback17(
|
|
7144
7439
|
(track, drag) => {
|
|
7145
7440
|
const id = track.handle.id;
|
|
7146
7441
|
const pickerProps = features.instrumentPicker ? {
|
|
@@ -7192,6 +7487,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7192
7487
|
onVolumeChange: (vol) => handlers.volumeChange(id, vol),
|
|
7193
7488
|
onPanChange: (pan) => handlers.panChange(id, pan),
|
|
7194
7489
|
onFxToggle: (cat, enabled) => handleFxToggle(id, cat, enabled),
|
|
7490
|
+
externalFxHost: host,
|
|
7195
7491
|
onFxPresetChange: (cat, idx) => handleFxPresetChange(id, cat, idx),
|
|
7196
7492
|
onFxDryWetChange: (cat, val) => handleFxDryWetChange(id, cat, val),
|
|
7197
7493
|
onToggleFxDrawer: () => handlers.toggleFxDrawer(id),
|
|
@@ -7239,12 +7535,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7239
7535
|
]
|
|
7240
7536
|
);
|
|
7241
7537
|
if (!activeSceneId) {
|
|
7242
|
-
return /* @__PURE__ */
|
|
7538
|
+
return /* @__PURE__ */ jsx26(
|
|
7243
7539
|
"div",
|
|
7244
7540
|
{
|
|
7245
7541
|
"data-testid": `no-scene-placeholder-${identity.familyKey}`,
|
|
7246
7542
|
className: "flex items-center justify-center py-8",
|
|
7247
|
-
children: /* @__PURE__ */
|
|
7543
|
+
children: /* @__PURE__ */ jsx26(
|
|
7248
7544
|
"button",
|
|
7249
7545
|
{
|
|
7250
7546
|
onClick: () => onSelectScene?.(),
|
|
@@ -7256,12 +7552,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7256
7552
|
);
|
|
7257
7553
|
}
|
|
7258
7554
|
if (!sceneContext?.hasContract) {
|
|
7259
|
-
return /* @__PURE__ */
|
|
7555
|
+
return /* @__PURE__ */ jsx26(
|
|
7260
7556
|
"div",
|
|
7261
7557
|
{
|
|
7262
7558
|
"data-testid": `no-contract-placeholder-${identity.familyKey}`,
|
|
7263
7559
|
className: "flex items-center justify-center py-8",
|
|
7264
|
-
children: /* @__PURE__ */
|
|
7560
|
+
children: /* @__PURE__ */ jsx26(
|
|
7265
7561
|
"button",
|
|
7266
7562
|
{
|
|
7267
7563
|
onClick: () => onOpenContract?.(),
|
|
@@ -7273,7 +7569,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7273
7569
|
);
|
|
7274
7570
|
}
|
|
7275
7571
|
if (features.bulkComposePlaceholders && isComposing) {
|
|
7276
|
-
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" }) });
|
|
7277
7573
|
}
|
|
7278
7574
|
const activePlaceholders = features.bulkComposePlaceholders ? placeholders : [];
|
|
7279
7575
|
if (activePlaceholders.length > 0) {
|
|
@@ -7284,18 +7580,18 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7284
7580
|
tracksByDbId.set(t.handle.id, t);
|
|
7285
7581
|
}
|
|
7286
7582
|
}
|
|
7287
|
-
return /* @__PURE__ */
|
|
7583
|
+
return /* @__PURE__ */ jsx26("div", { "data-testid": `${identity.familyKey}-section`, className: "p-2 space-y-2", children: activePlaceholders.map((ph) => {
|
|
7288
7584
|
const loadedTrack = ph.status === "completed" ? tracksByDbId.get(ph.id) : void 0;
|
|
7289
7585
|
if (loadedTrack) {
|
|
7290
|
-
return /* @__PURE__ */
|
|
7586
|
+
return /* @__PURE__ */ jsx26(TrackRow, { ...buildRowProps(loadedTrack) }, ph.id);
|
|
7291
7587
|
}
|
|
7292
|
-
return /* @__PURE__ */
|
|
7588
|
+
return /* @__PURE__ */ jsx26(
|
|
7293
7589
|
"div",
|
|
7294
7590
|
{
|
|
7295
7591
|
"data-testid": "bulk-placeholder-track",
|
|
7296
7592
|
className: "relative rounded-sm border w-full overflow-hidden border-sas-border bg-sas-panel-alt",
|
|
7297
7593
|
style: { borderLeftColor: identity.placeholderAccentColor, borderLeftWidth: "3px" },
|
|
7298
|
-
children: /* @__PURE__ */
|
|
7594
|
+
children: /* @__PURE__ */ jsx26(SorceryProgressBar, { isLoading: true, statusText: "CONJURING MIDI...", heightClass: "h-10" })
|
|
7299
7595
|
},
|
|
7300
7596
|
ph.id
|
|
7301
7597
|
);
|
|
@@ -7307,13 +7603,13 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7307
7603
|
supportsMeters,
|
|
7308
7604
|
levels: supportsMeters ? trackLevels : void 0,
|
|
7309
7605
|
handlers,
|
|
7310
|
-
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */
|
|
7606
|
+
renderDefaultTrackRow: (track, overrides, drag) => /* @__PURE__ */ jsx26(TrackRow, { ...{ ...buildRowProps(track, drag), ...overrides ?? {} } }, track.handle.id),
|
|
7311
7607
|
setGroupMute,
|
|
7312
7608
|
setGroupSolo,
|
|
7313
7609
|
deleteGroup
|
|
7314
7610
|
};
|
|
7315
|
-
return /* @__PURE__ */
|
|
7316
|
-
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(
|
|
7317
7613
|
ImportTrackModal,
|
|
7318
7614
|
{
|
|
7319
7615
|
host,
|
|
@@ -7326,7 +7622,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7326
7622
|
testIdPrefix: `${identity.familyKey}-import`
|
|
7327
7623
|
}
|
|
7328
7624
|
),
|
|
7329
|
-
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */
|
|
7625
|
+
features.importTracks && host.listImportableTracks && host.getTrackSound && /* @__PURE__ */ jsx26(
|
|
7330
7626
|
ImportTrackModal,
|
|
7331
7627
|
{
|
|
7332
7628
|
host,
|
|
@@ -7341,7 +7637,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7341
7637
|
}
|
|
7342
7638
|
),
|
|
7343
7639
|
slots?.modals,
|
|
7344
|
-
canCrossfade && xfFromId && xfToId && /* @__PURE__ */
|
|
7640
|
+
canCrossfade && xfFromId && xfToId && /* @__PURE__ */ jsx26("div", { className: designerView ? "contents" : "hidden", children: /* @__PURE__ */ jsx26(
|
|
7345
7641
|
TransitionDesigner,
|
|
7346
7642
|
{
|
|
7347
7643
|
host,
|
|
@@ -7358,11 +7654,12 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7358
7654
|
testIdPrefix: `${identity.familyKey}-transition-designer`
|
|
7359
7655
|
}
|
|
7360
7656
|
) }),
|
|
7361
|
-
!(designerView && canCrossfade) && (isLoadingTracks ? /* @__PURE__ */
|
|
7362
|
-
panelBus.supported && panelBus.bus && /* @__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(
|
|
7363
7659
|
PanelMasterStrip,
|
|
7364
7660
|
{
|
|
7365
7661
|
bus: panelBus.bus,
|
|
7662
|
+
levels: panelBus.levels,
|
|
7366
7663
|
availableFx: panelBus.availableFx,
|
|
7367
7664
|
fxLoading: panelBus.fxLoading,
|
|
7368
7665
|
soloedOut: anySolo && !panelBus.bus.soloed,
|
|
@@ -7379,7 +7676,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7379
7676
|
}
|
|
7380
7677
|
),
|
|
7381
7678
|
slots?.beforeRows,
|
|
7382
|
-
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */
|
|
7679
|
+
resolvedCrossfadePairs.map((pair) => /* @__PURE__ */ jsx26(
|
|
7383
7680
|
CrossfadeTrackRow,
|
|
7384
7681
|
{
|
|
7385
7682
|
accentColor: identity.transitionAccentColor,
|
|
@@ -7416,7 +7713,7 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7416
7713
|
},
|
|
7417
7714
|
pair.groupId
|
|
7418
7715
|
)),
|
|
7419
|
-
resolvedFades.map((fade) => /* @__PURE__ */
|
|
7716
|
+
resolvedFades.map((fade) => /* @__PURE__ */ jsx26(
|
|
7420
7717
|
FadeTrackRow,
|
|
7421
7718
|
{
|
|
7422
7719
|
accentColor: identity.transitionAccentColor,
|
|
@@ -7442,20 +7739,20 @@ function GeneratorPanelShell({ core, slots }) {
|
|
|
7442
7739
|
fade.dbId
|
|
7443
7740
|
)),
|
|
7444
7741
|
(adapter.groupExtensions ?? []).flatMap(
|
|
7445
|
-
(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}`))
|
|
7446
7743
|
),
|
|
7447
7744
|
tracks.map((track, index) => {
|
|
7448
7745
|
if (crossfadeMemberDbIds.has(track.handle.dbId) || fadeMemberDbIds.has(track.handle.dbId) || genericGroupMemberDbIds.has(track.handle.dbId)) {
|
|
7449
7746
|
return null;
|
|
7450
7747
|
}
|
|
7451
|
-
return /* @__PURE__ */
|
|
7748
|
+
return /* @__PURE__ */ jsx26(TrackRow, { ...buildRowProps(track, reorder.dragPropsFor(index)) }, track.handle.id);
|
|
7452
7749
|
}),
|
|
7453
7750
|
slots?.afterRows
|
|
7454
7751
|
] })),
|
|
7455
7752
|
features.exportMidi && !designerView && !isLoadingTracks && tracks.length > 0 && (() => {
|
|
7456
7753
|
const hasAnyMidi = tracks.some((t) => t.hasMidi);
|
|
7457
7754
|
const exportDisabled = isExportingMidi || !hasAnyMidi;
|
|
7458
|
-
return /* @__PURE__ */
|
|
7755
|
+
return /* @__PURE__ */ jsx26("div", { className: "pt-2", children: /* @__PURE__ */ jsx26(
|
|
7459
7756
|
"button",
|
|
7460
7757
|
{
|
|
7461
7758
|
"data-testid": "export-midi-tracks-button",
|
|
@@ -7523,7 +7820,7 @@ function createSurgeSoundAdapter(host, overrides = {}) {
|
|
|
7523
7820
|
}
|
|
7524
7821
|
|
|
7525
7822
|
// src/constants/sdk-version.ts
|
|
7526
|
-
var PLUGIN_SDK_VERSION = "2.
|
|
7823
|
+
var PLUGIN_SDK_VERSION = "2.39.0";
|
|
7527
7824
|
|
|
7528
7825
|
// src/utils/format-concurrent-tracks.ts
|
|
7529
7826
|
function formatConcurrentTracks(ctx) {
|
|
@@ -7710,6 +8007,7 @@ export {
|
|
|
7710
8007
|
TEXTURAL_ROLES,
|
|
7711
8008
|
TRANSITION_DESIGNER_DRAFT_KEY,
|
|
7712
8009
|
TrackDrawer,
|
|
8010
|
+
TrackExternalFxSection,
|
|
7713
8011
|
TrackMeterStrip,
|
|
7714
8012
|
TrackRow,
|
|
7715
8013
|
TransitionDesigner,
|
|
@@ -7766,6 +8064,7 @@ export {
|
|
|
7766
8064
|
usePanelBus,
|
|
7767
8065
|
useSceneState,
|
|
7768
8066
|
useSoundHistory,
|
|
8067
|
+
useTrackExternalFx,
|
|
7769
8068
|
useTrackLevel,
|
|
7770
8069
|
useTrackLevels,
|
|
7771
8070
|
useTrackMeter,
|