@signalsandsorcery/plugin-sdk 2.35.8 → 2.36.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.mts +75 -10
- package/dist/index.d.ts +75 -10
- package/dist/index.js +108 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +106 -81
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -111,6 +111,7 @@ __export(index_exports, {
|
|
|
111
111
|
defaultVoiceSpecs: () => defaultVoiceSpecs,
|
|
112
112
|
describeViolations: () => describeViolations,
|
|
113
113
|
drawWaveform: () => drawWaveform,
|
|
114
|
+
effectivePxPerBeat: () => effectivePxPerBeat,
|
|
114
115
|
enforceVoice: () => enforceVoice,
|
|
115
116
|
foldPitchToRegister: () => foldPitchToRegister,
|
|
116
117
|
formatConcurrentTracks: () => formatConcurrentTracks,
|
|
@@ -129,6 +130,7 @@ __export(index_exports, {
|
|
|
129
130
|
pickTopKWeighted: () => pickTopKWeighted,
|
|
130
131
|
pitchToName: () => pitchToName,
|
|
131
132
|
pluginFxToToggleFx: () => pluginFxToToggleFx,
|
|
133
|
+
promptEnterToGenerate: () => promptEnterToGenerate,
|
|
132
134
|
pxToCell: () => pxToCell,
|
|
133
135
|
reconcileSlots: () => reconcileSlots,
|
|
134
136
|
resizeNoteDuration: () => resizeNoteDuration,
|
|
@@ -604,25 +606,31 @@ function clamp(v, lo, hi) {
|
|
|
604
606
|
function snapLabel(s) {
|
|
605
607
|
return SNAP_LABELS[String(s)] ?? `${s}`;
|
|
606
608
|
}
|
|
609
|
+
function effectivePxPerBeat(containerWidth, totalBeats) {
|
|
610
|
+
if (totalBeats <= 0) return PX_PER_BEAT;
|
|
611
|
+
const available = containerWidth - GUTTER_W;
|
|
612
|
+
if (available <= 0) return PX_PER_BEAT;
|
|
613
|
+
return Math.max(PX_PER_BEAT, available / totalBeats);
|
|
614
|
+
}
|
|
607
615
|
function pitchToName(pitch) {
|
|
608
616
|
const name = NOTE_NAMES[(pitch % 12 + 12) % 12];
|
|
609
617
|
const octave = Math.floor(pitch / 12) - 1;
|
|
610
618
|
return `${name}${octave}`;
|
|
611
619
|
}
|
|
612
|
-
function cellToPx(pitch, startBeat, hi) {
|
|
613
|
-
return { left: startBeat *
|
|
620
|
+
function cellToPx(pitch, startBeat, hi, pxPerBeat = PX_PER_BEAT) {
|
|
621
|
+
return { left: startBeat * pxPerBeat, top: (hi - pitch) * ROW_HEIGHT };
|
|
614
622
|
}
|
|
615
|
-
function pxToCell(localX, localY, hi, snap, bars, beatsPerBar) {
|
|
623
|
+
function pxToCell(localX, localY, hi, snap, bars, beatsPerBar, pxPerBeat = PX_PER_BEAT) {
|
|
616
624
|
const totalBeats = bars * beatsPerBar;
|
|
617
625
|
const pitch = clamp(hi - Math.floor(localY / ROW_HEIGHT), 0, 127);
|
|
618
|
-
const rawBeat = localX /
|
|
626
|
+
const rawBeat = localX / pxPerBeat;
|
|
619
627
|
const snapped = Math.round(rawBeat / snap) * snap;
|
|
620
628
|
const startBeat = clamp(snapped, 0, Math.max(0, totalBeats - snap));
|
|
621
629
|
return { pitch, startBeat };
|
|
622
630
|
}
|
|
623
|
-
function resizeNoteDuration(startBeat, localX, snap, bars, beatsPerBar) {
|
|
631
|
+
function resizeNoteDuration(startBeat, localX, snap, bars, beatsPerBar, pxPerBeat = PX_PER_BEAT) {
|
|
624
632
|
const totalBeats = bars * beatsPerBar;
|
|
625
|
-
const snappedEnd = Math.round(localX /
|
|
633
|
+
const snappedEnd = Math.round(localX / pxPerBeat / snap) * snap;
|
|
626
634
|
const end = clamp(snappedEnd, startBeat + snap, totalBeats);
|
|
627
635
|
return end - startBeat;
|
|
628
636
|
}
|
|
@@ -673,7 +681,19 @@ function PianoRollEditor({
|
|
|
673
681
|
}, [autoFit, notes, minPitch, maxPitch]);
|
|
674
682
|
const rowCount = hi - lo + 1;
|
|
675
683
|
const totalBeats = bars * beatsPerBar;
|
|
676
|
-
const
|
|
684
|
+
const [containerW, setContainerW] = (0, import_react.useState)(0);
|
|
685
|
+
(0, import_react.useLayoutEffect)(() => {
|
|
686
|
+
const el = scrollRef.current;
|
|
687
|
+
if (!el) return;
|
|
688
|
+
const measure = () => setContainerW(el.clientWidth);
|
|
689
|
+
measure();
|
|
690
|
+
if (typeof ResizeObserver === "undefined") return void 0;
|
|
691
|
+
const ro = new ResizeObserver(measure);
|
|
692
|
+
ro.observe(el);
|
|
693
|
+
return () => ro.disconnect();
|
|
694
|
+
}, []);
|
|
695
|
+
const pxPerBeat = effectivePxPerBeat(containerW, totalBeats);
|
|
696
|
+
const gridWidth = totalBeats * pxPerBeat;
|
|
677
697
|
const gridHeight = rowCount * ROW_HEIGHT;
|
|
678
698
|
const stateRef = (0, import_react.useRef)({
|
|
679
699
|
notes,
|
|
@@ -685,7 +705,8 @@ function PianoRollEditor({
|
|
|
685
705
|
defaultVelocity,
|
|
686
706
|
bpm,
|
|
687
707
|
onAuditionNote,
|
|
688
|
-
disabled
|
|
708
|
+
disabled,
|
|
709
|
+
pxPerBeat
|
|
689
710
|
});
|
|
690
711
|
stateRef.current = {
|
|
691
712
|
notes,
|
|
@@ -697,7 +718,8 @@ function PianoRollEditor({
|
|
|
697
718
|
defaultVelocity,
|
|
698
719
|
bpm,
|
|
699
720
|
onAuditionNote,
|
|
700
|
-
disabled
|
|
721
|
+
disabled,
|
|
722
|
+
pxPerBeat
|
|
701
723
|
};
|
|
702
724
|
const localCoords = (0, import_react.useCallback)((clientX, clientY) => {
|
|
703
725
|
const rect = gridRef.current?.getBoundingClientRect();
|
|
@@ -733,14 +755,14 @@ function PianoRollEditor({
|
|
|
733
755
|
if (drag.mode === "resize") {
|
|
734
756
|
const note = s.notes[drag.index];
|
|
735
757
|
if (!note) return;
|
|
736
|
-
const durationBeats = resizeNoteDuration(note.startBeat, x, s.snapState, s.bars, s.beatsPerBar);
|
|
758
|
+
const durationBeats = resizeNoteDuration(note.startBeat, x, s.snapState, s.bars, s.beatsPerBar, s.pxPerBeat);
|
|
737
759
|
if (durationBeats === note.durationBeats) return;
|
|
738
760
|
const next2 = s.notes.map((n, i) => i === drag.index ? { ...n, durationBeats } : n);
|
|
739
761
|
s.onChange(next2);
|
|
740
762
|
return;
|
|
741
763
|
}
|
|
742
764
|
if (drag.mode !== "drag") return;
|
|
743
|
-
const { pitch, startBeat } = pxToCell(x, y, s.hi, s.snapState, s.bars, s.beatsPerBar);
|
|
765
|
+
const { pitch, startBeat } = pxToCell(x, y, s.hi, s.snapState, s.bars, s.beatsPerBar, s.pxPerBeat);
|
|
744
766
|
const next = s.notes.map((n, i) => i === drag.index ? { ...n, pitch, startBeat } : n);
|
|
745
767
|
s.onChange(next);
|
|
746
768
|
}, [localCoords]);
|
|
@@ -756,7 +778,7 @@ function PianoRollEditor({
|
|
|
756
778
|
}
|
|
757
779
|
if (drag.mode === "pending-add") {
|
|
758
780
|
const { x, y } = localCoords(e.clientX, e.clientY);
|
|
759
|
-
const { pitch, startBeat } = pxToCell(x, y, s.hi, s.snapState, s.bars, s.beatsPerBar);
|
|
781
|
+
const { pitch, startBeat } = pxToCell(x, y, s.hi, s.snapState, s.bars, s.beatsPerBar, s.pxPerBeat);
|
|
760
782
|
const note = {
|
|
761
783
|
pitch,
|
|
762
784
|
startBeat,
|
|
@@ -805,14 +827,14 @@ function PianoRollEditor({
|
|
|
805
827
|
return out;
|
|
806
828
|
}, [hi, lo]);
|
|
807
829
|
const gridBg = (0, import_react.useMemo)(() => {
|
|
808
|
-
const beatPx =
|
|
809
|
-
const barPx =
|
|
830
|
+
const beatPx = pxPerBeat;
|
|
831
|
+
const barPx = pxPerBeat * beatsPerBar;
|
|
810
832
|
return [
|
|
811
833
|
`repeating-linear-gradient(to right, transparent 0 ${beatPx - 1}px, rgba(255,255,255,0.06) ${beatPx - 1}px ${beatPx}px)`,
|
|
812
834
|
`repeating-linear-gradient(to right, transparent 0 ${barPx - 1}px, rgba(255,255,255,0.16) ${barPx - 1}px ${barPx}px)`,
|
|
813
835
|
`repeating-linear-gradient(to bottom, transparent 0 ${ROW_HEIGHT - 1}px, rgba(255,255,255,0.04) ${ROW_HEIGHT - 1}px ${ROW_HEIGHT}px)`
|
|
814
836
|
].join(", ");
|
|
815
|
-
}, [beatsPerBar]);
|
|
837
|
+
}, [beatsPerBar, pxPerBeat]);
|
|
816
838
|
const octaveDisabled = disabled || notes.length === 0;
|
|
817
839
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `flex flex-col gap-1 ${className ?? ""}`, "data-testid": testId, children: [
|
|
818
840
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-1", "data-testid": "sdk-pr-toolbar", children: [
|
|
@@ -906,8 +928,8 @@ function PianoRollEditor({
|
|
|
906
928
|
onPointerCancel: handlePointerCancel,
|
|
907
929
|
children: [
|
|
908
930
|
notes.map((n, i) => {
|
|
909
|
-
const { left, top } = cellToPx(n.pitch, n.startBeat, hi);
|
|
910
|
-
const width = Math.max(3, n.durationBeats *
|
|
931
|
+
const { left, top } = cellToPx(n.pitch, n.startBeat, hi, pxPerBeat);
|
|
932
|
+
const width = Math.max(3, n.durationBeats * pxPerBeat);
|
|
911
933
|
const handleW = Math.min(RESIZE_HANDLE_PX, width / 2);
|
|
912
934
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
913
935
|
"div",
|
|
@@ -2385,6 +2407,71 @@ function SorceryProgressBar({
|
|
|
2385
2407
|
);
|
|
2386
2408
|
}
|
|
2387
2409
|
|
|
2410
|
+
// src/panel-core/panel-helpers.ts
|
|
2411
|
+
function promptEnterToGenerate(generate, disabled = false) {
|
|
2412
|
+
return (e) => {
|
|
2413
|
+
if (e.key === "Enter" && !e.shiftKey && !disabled) {
|
|
2414
|
+
e.preventDefault();
|
|
2415
|
+
generate();
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
function trackDataKey(dbId, suffix) {
|
|
2420
|
+
return `track:${dbId}:${suffix}`;
|
|
2421
|
+
}
|
|
2422
|
+
function pluginFxToToggleFx(sdkState) {
|
|
2423
|
+
const result = { ...EMPTY_FX_DETAIL_STATE };
|
|
2424
|
+
for (const category of ["eq", "compressor", "chorus", "phaser", "delay", "reverb"]) {
|
|
2425
|
+
const sdkCat = sdkState[category];
|
|
2426
|
+
if (sdkCat) {
|
|
2427
|
+
result[category] = {
|
|
2428
|
+
enabled: sdkCat.enabled,
|
|
2429
|
+
presetIndex: sdkCat.presetIndex,
|
|
2430
|
+
dryWet: sdkCat.dryWet
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
return result;
|
|
2435
|
+
}
|
|
2436
|
+
function parseLLMNoteResponse(content) {
|
|
2437
|
+
try {
|
|
2438
|
+
let jsonStr = content.trim();
|
|
2439
|
+
const fenceMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
|
|
2440
|
+
if (fenceMatch) {
|
|
2441
|
+
jsonStr = fenceMatch[1].trim();
|
|
2442
|
+
}
|
|
2443
|
+
const parsed = JSON.parse(jsonStr);
|
|
2444
|
+
if (typeof parsed !== "object" || parsed === null || !("notes" in parsed)) {
|
|
2445
|
+
return null;
|
|
2446
|
+
}
|
|
2447
|
+
const obj = parsed;
|
|
2448
|
+
if (!Array.isArray(obj.notes)) {
|
|
2449
|
+
return null;
|
|
2450
|
+
}
|
|
2451
|
+
const validNotes = [];
|
|
2452
|
+
for (const raw of obj.notes) {
|
|
2453
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
2454
|
+
const note = raw;
|
|
2455
|
+
const pitch = typeof note.pitch === "number" ? note.pitch : NaN;
|
|
2456
|
+
const startBeat = typeof note.startBeat === "number" ? note.startBeat : NaN;
|
|
2457
|
+
const durationBeats = typeof note.durationBeats === "number" ? note.durationBeats : NaN;
|
|
2458
|
+
const velocity = typeof note.velocity === "number" ? note.velocity : NaN;
|
|
2459
|
+
if (!isNaN(pitch) && pitch >= 0 && pitch <= 127 && !isNaN(startBeat) && startBeat >= 0 && !isNaN(durationBeats) && durationBeats > 0 && !isNaN(velocity) && velocity >= 1 && velocity <= 127) {
|
|
2460
|
+
validNotes.push({
|
|
2461
|
+
pitch: Math.round(pitch),
|
|
2462
|
+
startBeat,
|
|
2463
|
+
durationBeats,
|
|
2464
|
+
velocity: Math.round(velocity)
|
|
2465
|
+
});
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
const role = typeof obj.role === "string" ? obj.role : void 0;
|
|
2469
|
+
return { notes: validNotes, role };
|
|
2470
|
+
} catch {
|
|
2471
|
+
return null;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2388
2475
|
// src/components/TrackRow.tsx
|
|
2389
2476
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2390
2477
|
function TrackRow({
|
|
@@ -2453,12 +2540,7 @@ function TrackRow({
|
|
|
2453
2540
|
);
|
|
2454
2541
|
const fxTabOpen = drawerOpen && drawerTab === "fx";
|
|
2455
2542
|
const soundTabOpen = drawerOpen && drawerTab !== "fx";
|
|
2456
|
-
const handleKeyDown = (
|
|
2457
|
-
if (e.key === "Enter" && !e.shiftKey && onGenerate) {
|
|
2458
|
-
e.preventDefault();
|
|
2459
|
-
onGenerate();
|
|
2460
|
-
}
|
|
2461
|
-
};
|
|
2543
|
+
const handleKeyDown = promptEnterToGenerate(() => onGenerate?.(), !onGenerate);
|
|
2462
2544
|
const borderColorStyle = needsGeneration ? void 0 : accentColor;
|
|
2463
2545
|
const borderClass = needsGeneration ? "border-amber-400 animate-pulse" : "border-sas-border";
|
|
2464
2546
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { "data-testid": "sdk-track-row-wrapper", className: "w-full", ...drag?.rowProps ?? {}, children: [
|
|
@@ -6046,63 +6128,6 @@ function newTrackState(handle, overrides = {}) {
|
|
|
6046
6128
|
};
|
|
6047
6129
|
}
|
|
6048
6130
|
|
|
6049
|
-
// src/panel-core/panel-helpers.ts
|
|
6050
|
-
function trackDataKey(dbId, suffix) {
|
|
6051
|
-
return `track:${dbId}:${suffix}`;
|
|
6052
|
-
}
|
|
6053
|
-
function pluginFxToToggleFx(sdkState) {
|
|
6054
|
-
const result = { ...EMPTY_FX_DETAIL_STATE };
|
|
6055
|
-
for (const category of ["eq", "compressor", "chorus", "phaser", "delay", "reverb"]) {
|
|
6056
|
-
const sdkCat = sdkState[category];
|
|
6057
|
-
if (sdkCat) {
|
|
6058
|
-
result[category] = {
|
|
6059
|
-
enabled: sdkCat.enabled,
|
|
6060
|
-
presetIndex: sdkCat.presetIndex,
|
|
6061
|
-
dryWet: sdkCat.dryWet
|
|
6062
|
-
};
|
|
6063
|
-
}
|
|
6064
|
-
}
|
|
6065
|
-
return result;
|
|
6066
|
-
}
|
|
6067
|
-
function parseLLMNoteResponse(content) {
|
|
6068
|
-
try {
|
|
6069
|
-
let jsonStr = content.trim();
|
|
6070
|
-
const fenceMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
|
|
6071
|
-
if (fenceMatch) {
|
|
6072
|
-
jsonStr = fenceMatch[1].trim();
|
|
6073
|
-
}
|
|
6074
|
-
const parsed = JSON.parse(jsonStr);
|
|
6075
|
-
if (typeof parsed !== "object" || parsed === null || !("notes" in parsed)) {
|
|
6076
|
-
return null;
|
|
6077
|
-
}
|
|
6078
|
-
const obj = parsed;
|
|
6079
|
-
if (!Array.isArray(obj.notes)) {
|
|
6080
|
-
return null;
|
|
6081
|
-
}
|
|
6082
|
-
const validNotes = [];
|
|
6083
|
-
for (const raw of obj.notes) {
|
|
6084
|
-
if (typeof raw !== "object" || raw === null) continue;
|
|
6085
|
-
const note = raw;
|
|
6086
|
-
const pitch = typeof note.pitch === "number" ? note.pitch : NaN;
|
|
6087
|
-
const startBeat = typeof note.startBeat === "number" ? note.startBeat : NaN;
|
|
6088
|
-
const durationBeats = typeof note.durationBeats === "number" ? note.durationBeats : NaN;
|
|
6089
|
-
const velocity = typeof note.velocity === "number" ? note.velocity : NaN;
|
|
6090
|
-
if (!isNaN(pitch) && pitch >= 0 && pitch <= 127 && !isNaN(startBeat) && startBeat >= 0 && !isNaN(durationBeats) && durationBeats > 0 && !isNaN(velocity) && velocity >= 1 && velocity <= 127) {
|
|
6091
|
-
validNotes.push({
|
|
6092
|
-
pitch: Math.round(pitch),
|
|
6093
|
-
startBeat,
|
|
6094
|
-
durationBeats,
|
|
6095
|
-
velocity: Math.round(velocity)
|
|
6096
|
-
});
|
|
6097
|
-
}
|
|
6098
|
-
}
|
|
6099
|
-
const role = typeof obj.role === "string" ? obj.role : void 0;
|
|
6100
|
-
return { notes: validNotes, role };
|
|
6101
|
-
} catch {
|
|
6102
|
-
return null;
|
|
6103
|
-
}
|
|
6104
|
-
}
|
|
6105
|
-
|
|
6106
6131
|
// src/panel-core/group-meta.ts
|
|
6107
6132
|
function parseTrackGroups(sceneData, spec) {
|
|
6108
6133
|
const pattern = new RegExp(`^track:(.+):${spec.metaKey}$`);
|
|
@@ -9048,7 +9073,7 @@ Your previous ensemble had these problems \u2014 fix them while keeping everythi
|
|
|
9048
9073
|
}
|
|
9049
9074
|
|
|
9050
9075
|
// src/constants/sdk-version.ts
|
|
9051
|
-
var PLUGIN_SDK_VERSION = "2.
|
|
9076
|
+
var PLUGIN_SDK_VERSION = "2.45.0";
|
|
9052
9077
|
|
|
9053
9078
|
// src/utils/format-concurrent-tracks.ts
|
|
9054
9079
|
function formatConcurrentTracks(ctx) {
|
|
@@ -9286,6 +9311,7 @@ function pickTopKWeighted(scored, options = {}) {
|
|
|
9286
9311
|
defaultVoiceSpecs,
|
|
9287
9312
|
describeViolations,
|
|
9288
9313
|
drawWaveform,
|
|
9314
|
+
effectivePxPerBeat,
|
|
9289
9315
|
enforceVoice,
|
|
9290
9316
|
foldPitchToRegister,
|
|
9291
9317
|
formatConcurrentTracks,
|
|
@@ -9304,6 +9330,7 @@ function pickTopKWeighted(scored, options = {}) {
|
|
|
9304
9330
|
pickTopKWeighted,
|
|
9305
9331
|
pitchToName,
|
|
9306
9332
|
pluginFxToToggleFx,
|
|
9333
|
+
promptEnterToGenerate,
|
|
9307
9334
|
pxToCell,
|
|
9308
9335
|
reconcileSlots,
|
|
9309
9336
|
resizeNoteDuration,
|