@signalsandsorcery/plugin-sdk 2.35.7 → 2.36.1
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 +59 -11
- package/dist/index.d.ts +59 -11
- package/dist/index.js +107 -80
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +105 -80
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -443,25 +443,31 @@ function clamp(v, lo, hi) {
|
|
|
443
443
|
function snapLabel(s) {
|
|
444
444
|
return SNAP_LABELS[String(s)] ?? `${s}`;
|
|
445
445
|
}
|
|
446
|
+
function effectivePxPerBeat(containerWidth, totalBeats) {
|
|
447
|
+
if (totalBeats <= 0) return PX_PER_BEAT;
|
|
448
|
+
const available = containerWidth - GUTTER_W;
|
|
449
|
+
if (available <= 0) return PX_PER_BEAT;
|
|
450
|
+
return Math.max(PX_PER_BEAT, available / totalBeats);
|
|
451
|
+
}
|
|
446
452
|
function pitchToName(pitch) {
|
|
447
453
|
const name = NOTE_NAMES[(pitch % 12 + 12) % 12];
|
|
448
454
|
const octave = Math.floor(pitch / 12) - 1;
|
|
449
455
|
return `${name}${octave}`;
|
|
450
456
|
}
|
|
451
|
-
function cellToPx(pitch, startBeat, hi) {
|
|
452
|
-
return { left: startBeat *
|
|
457
|
+
function cellToPx(pitch, startBeat, hi, pxPerBeat = PX_PER_BEAT) {
|
|
458
|
+
return { left: startBeat * pxPerBeat, top: (hi - pitch) * ROW_HEIGHT };
|
|
453
459
|
}
|
|
454
|
-
function pxToCell(localX, localY, hi, snap, bars, beatsPerBar) {
|
|
460
|
+
function pxToCell(localX, localY, hi, snap, bars, beatsPerBar, pxPerBeat = PX_PER_BEAT) {
|
|
455
461
|
const totalBeats = bars * beatsPerBar;
|
|
456
462
|
const pitch = clamp(hi - Math.floor(localY / ROW_HEIGHT), 0, 127);
|
|
457
|
-
const rawBeat = localX /
|
|
463
|
+
const rawBeat = localX / pxPerBeat;
|
|
458
464
|
const snapped = Math.round(rawBeat / snap) * snap;
|
|
459
465
|
const startBeat = clamp(snapped, 0, Math.max(0, totalBeats - snap));
|
|
460
466
|
return { pitch, startBeat };
|
|
461
467
|
}
|
|
462
|
-
function resizeNoteDuration(startBeat, localX, snap, bars, beatsPerBar) {
|
|
468
|
+
function resizeNoteDuration(startBeat, localX, snap, bars, beatsPerBar, pxPerBeat = PX_PER_BEAT) {
|
|
463
469
|
const totalBeats = bars * beatsPerBar;
|
|
464
|
-
const snappedEnd = Math.round(localX /
|
|
470
|
+
const snappedEnd = Math.round(localX / pxPerBeat / snap) * snap;
|
|
465
471
|
const end = clamp(snappedEnd, startBeat + snap, totalBeats);
|
|
466
472
|
return end - startBeat;
|
|
467
473
|
}
|
|
@@ -512,7 +518,19 @@ function PianoRollEditor({
|
|
|
512
518
|
}, [autoFit, notes, minPitch, maxPitch]);
|
|
513
519
|
const rowCount = hi - lo + 1;
|
|
514
520
|
const totalBeats = bars * beatsPerBar;
|
|
515
|
-
const
|
|
521
|
+
const [containerW, setContainerW] = useState(0);
|
|
522
|
+
useLayoutEffect(() => {
|
|
523
|
+
const el = scrollRef.current;
|
|
524
|
+
if (!el) return;
|
|
525
|
+
const measure = () => setContainerW(el.clientWidth);
|
|
526
|
+
measure();
|
|
527
|
+
if (typeof ResizeObserver === "undefined") return void 0;
|
|
528
|
+
const ro = new ResizeObserver(measure);
|
|
529
|
+
ro.observe(el);
|
|
530
|
+
return () => ro.disconnect();
|
|
531
|
+
}, []);
|
|
532
|
+
const pxPerBeat = effectivePxPerBeat(containerW, totalBeats);
|
|
533
|
+
const gridWidth = totalBeats * pxPerBeat;
|
|
516
534
|
const gridHeight = rowCount * ROW_HEIGHT;
|
|
517
535
|
const stateRef = useRef({
|
|
518
536
|
notes,
|
|
@@ -524,7 +542,8 @@ function PianoRollEditor({
|
|
|
524
542
|
defaultVelocity,
|
|
525
543
|
bpm,
|
|
526
544
|
onAuditionNote,
|
|
527
|
-
disabled
|
|
545
|
+
disabled,
|
|
546
|
+
pxPerBeat
|
|
528
547
|
});
|
|
529
548
|
stateRef.current = {
|
|
530
549
|
notes,
|
|
@@ -536,7 +555,8 @@ function PianoRollEditor({
|
|
|
536
555
|
defaultVelocity,
|
|
537
556
|
bpm,
|
|
538
557
|
onAuditionNote,
|
|
539
|
-
disabled
|
|
558
|
+
disabled,
|
|
559
|
+
pxPerBeat
|
|
540
560
|
};
|
|
541
561
|
const localCoords = useCallback((clientX, clientY) => {
|
|
542
562
|
const rect = gridRef.current?.getBoundingClientRect();
|
|
@@ -572,14 +592,14 @@ function PianoRollEditor({
|
|
|
572
592
|
if (drag.mode === "resize") {
|
|
573
593
|
const note = s.notes[drag.index];
|
|
574
594
|
if (!note) return;
|
|
575
|
-
const durationBeats = resizeNoteDuration(note.startBeat, x, s.snapState, s.bars, s.beatsPerBar);
|
|
595
|
+
const durationBeats = resizeNoteDuration(note.startBeat, x, s.snapState, s.bars, s.beatsPerBar, s.pxPerBeat);
|
|
576
596
|
if (durationBeats === note.durationBeats) return;
|
|
577
597
|
const next2 = s.notes.map((n, i) => i === drag.index ? { ...n, durationBeats } : n);
|
|
578
598
|
s.onChange(next2);
|
|
579
599
|
return;
|
|
580
600
|
}
|
|
581
601
|
if (drag.mode !== "drag") return;
|
|
582
|
-
const { pitch, startBeat } = pxToCell(x, y, s.hi, s.snapState, s.bars, s.beatsPerBar);
|
|
602
|
+
const { pitch, startBeat } = pxToCell(x, y, s.hi, s.snapState, s.bars, s.beatsPerBar, s.pxPerBeat);
|
|
583
603
|
const next = s.notes.map((n, i) => i === drag.index ? { ...n, pitch, startBeat } : n);
|
|
584
604
|
s.onChange(next);
|
|
585
605
|
}, [localCoords]);
|
|
@@ -595,7 +615,7 @@ function PianoRollEditor({
|
|
|
595
615
|
}
|
|
596
616
|
if (drag.mode === "pending-add") {
|
|
597
617
|
const { x, y } = localCoords(e.clientX, e.clientY);
|
|
598
|
-
const { pitch, startBeat } = pxToCell(x, y, s.hi, s.snapState, s.bars, s.beatsPerBar);
|
|
618
|
+
const { pitch, startBeat } = pxToCell(x, y, s.hi, s.snapState, s.bars, s.beatsPerBar, s.pxPerBeat);
|
|
599
619
|
const note = {
|
|
600
620
|
pitch,
|
|
601
621
|
startBeat,
|
|
@@ -644,14 +664,14 @@ function PianoRollEditor({
|
|
|
644
664
|
return out;
|
|
645
665
|
}, [hi, lo]);
|
|
646
666
|
const gridBg = useMemo(() => {
|
|
647
|
-
const beatPx =
|
|
648
|
-
const barPx =
|
|
667
|
+
const beatPx = pxPerBeat;
|
|
668
|
+
const barPx = pxPerBeat * beatsPerBar;
|
|
649
669
|
return [
|
|
650
670
|
`repeating-linear-gradient(to right, transparent 0 ${beatPx - 1}px, rgba(255,255,255,0.06) ${beatPx - 1}px ${beatPx}px)`,
|
|
651
671
|
`repeating-linear-gradient(to right, transparent 0 ${barPx - 1}px, rgba(255,255,255,0.16) ${barPx - 1}px ${barPx}px)`,
|
|
652
672
|
`repeating-linear-gradient(to bottom, transparent 0 ${ROW_HEIGHT - 1}px, rgba(255,255,255,0.04) ${ROW_HEIGHT - 1}px ${ROW_HEIGHT}px)`
|
|
653
673
|
].join(", ");
|
|
654
|
-
}, [beatsPerBar]);
|
|
674
|
+
}, [beatsPerBar, pxPerBeat]);
|
|
655
675
|
const octaveDisabled = disabled || notes.length === 0;
|
|
656
676
|
return /* @__PURE__ */ jsxs2("div", { className: `flex flex-col gap-1 ${className ?? ""}`, "data-testid": testId, children: [
|
|
657
677
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1", "data-testid": "sdk-pr-toolbar", children: [
|
|
@@ -745,8 +765,8 @@ function PianoRollEditor({
|
|
|
745
765
|
onPointerCancel: handlePointerCancel,
|
|
746
766
|
children: [
|
|
747
767
|
notes.map((n, i) => {
|
|
748
|
-
const { left, top } = cellToPx(n.pitch, n.startBeat, hi);
|
|
749
|
-
const width = Math.max(3, n.durationBeats *
|
|
768
|
+
const { left, top } = cellToPx(n.pitch, n.startBeat, hi, pxPerBeat);
|
|
769
|
+
const width = Math.max(3, n.durationBeats * pxPerBeat);
|
|
750
770
|
const handleW = Math.min(RESIZE_HANDLE_PX, width / 2);
|
|
751
771
|
return /* @__PURE__ */ jsx2(
|
|
752
772
|
"div",
|
|
@@ -2224,6 +2244,71 @@ function SorceryProgressBar({
|
|
|
2224
2244
|
);
|
|
2225
2245
|
}
|
|
2226
2246
|
|
|
2247
|
+
// src/panel-core/panel-helpers.ts
|
|
2248
|
+
function promptEnterToGenerate(generate, disabled = false) {
|
|
2249
|
+
return (e) => {
|
|
2250
|
+
if (e.key === "Enter" && !e.shiftKey && !disabled) {
|
|
2251
|
+
e.preventDefault();
|
|
2252
|
+
generate();
|
|
2253
|
+
}
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
function trackDataKey(dbId, suffix) {
|
|
2257
|
+
return `track:${dbId}:${suffix}`;
|
|
2258
|
+
}
|
|
2259
|
+
function pluginFxToToggleFx(sdkState) {
|
|
2260
|
+
const result = { ...EMPTY_FX_DETAIL_STATE };
|
|
2261
|
+
for (const category of ["eq", "compressor", "chorus", "phaser", "delay", "reverb"]) {
|
|
2262
|
+
const sdkCat = sdkState[category];
|
|
2263
|
+
if (sdkCat) {
|
|
2264
|
+
result[category] = {
|
|
2265
|
+
enabled: sdkCat.enabled,
|
|
2266
|
+
presetIndex: sdkCat.presetIndex,
|
|
2267
|
+
dryWet: sdkCat.dryWet
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
return result;
|
|
2272
|
+
}
|
|
2273
|
+
function parseLLMNoteResponse(content) {
|
|
2274
|
+
try {
|
|
2275
|
+
let jsonStr = content.trim();
|
|
2276
|
+
const fenceMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
|
|
2277
|
+
if (fenceMatch) {
|
|
2278
|
+
jsonStr = fenceMatch[1].trim();
|
|
2279
|
+
}
|
|
2280
|
+
const parsed = JSON.parse(jsonStr);
|
|
2281
|
+
if (typeof parsed !== "object" || parsed === null || !("notes" in parsed)) {
|
|
2282
|
+
return null;
|
|
2283
|
+
}
|
|
2284
|
+
const obj = parsed;
|
|
2285
|
+
if (!Array.isArray(obj.notes)) {
|
|
2286
|
+
return null;
|
|
2287
|
+
}
|
|
2288
|
+
const validNotes = [];
|
|
2289
|
+
for (const raw of obj.notes) {
|
|
2290
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
2291
|
+
const note = raw;
|
|
2292
|
+
const pitch = typeof note.pitch === "number" ? note.pitch : NaN;
|
|
2293
|
+
const startBeat = typeof note.startBeat === "number" ? note.startBeat : NaN;
|
|
2294
|
+
const durationBeats = typeof note.durationBeats === "number" ? note.durationBeats : NaN;
|
|
2295
|
+
const velocity = typeof note.velocity === "number" ? note.velocity : NaN;
|
|
2296
|
+
if (!isNaN(pitch) && pitch >= 0 && pitch <= 127 && !isNaN(startBeat) && startBeat >= 0 && !isNaN(durationBeats) && durationBeats > 0 && !isNaN(velocity) && velocity >= 1 && velocity <= 127) {
|
|
2297
|
+
validNotes.push({
|
|
2298
|
+
pitch: Math.round(pitch),
|
|
2299
|
+
startBeat,
|
|
2300
|
+
durationBeats,
|
|
2301
|
+
velocity: Math.round(velocity)
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
const role = typeof obj.role === "string" ? obj.role : void 0;
|
|
2306
|
+
return { notes: validNotes, role };
|
|
2307
|
+
} catch {
|
|
2308
|
+
return null;
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2227
2312
|
// src/components/TrackRow.tsx
|
|
2228
2313
|
import { Fragment, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2229
2314
|
function TrackRow({
|
|
@@ -2292,12 +2377,7 @@ function TrackRow({
|
|
|
2292
2377
|
);
|
|
2293
2378
|
const fxTabOpen = drawerOpen && drawerTab === "fx";
|
|
2294
2379
|
const soundTabOpen = drawerOpen && drawerTab !== "fx";
|
|
2295
|
-
const handleKeyDown = (
|
|
2296
|
-
if (e.key === "Enter" && !e.shiftKey && onGenerate) {
|
|
2297
|
-
e.preventDefault();
|
|
2298
|
-
onGenerate();
|
|
2299
|
-
}
|
|
2300
|
-
};
|
|
2380
|
+
const handleKeyDown = promptEnterToGenerate(() => onGenerate?.(), !onGenerate);
|
|
2301
2381
|
const borderColorStyle = needsGeneration ? void 0 : accentColor;
|
|
2302
2382
|
const borderClass = needsGeneration ? "border-amber-400 animate-pulse" : "border-sas-border";
|
|
2303
2383
|
return /* @__PURE__ */ jsxs8("div", { "data-testid": "sdk-track-row-wrapper", className: "w-full", ...drag?.rowProps ?? {}, children: [
|
|
@@ -5885,63 +5965,6 @@ function newTrackState(handle, overrides = {}) {
|
|
|
5885
5965
|
};
|
|
5886
5966
|
}
|
|
5887
5967
|
|
|
5888
|
-
// src/panel-core/panel-helpers.ts
|
|
5889
|
-
function trackDataKey(dbId, suffix) {
|
|
5890
|
-
return `track:${dbId}:${suffix}`;
|
|
5891
|
-
}
|
|
5892
|
-
function pluginFxToToggleFx(sdkState) {
|
|
5893
|
-
const result = { ...EMPTY_FX_DETAIL_STATE };
|
|
5894
|
-
for (const category of ["eq", "compressor", "chorus", "phaser", "delay", "reverb"]) {
|
|
5895
|
-
const sdkCat = sdkState[category];
|
|
5896
|
-
if (sdkCat) {
|
|
5897
|
-
result[category] = {
|
|
5898
|
-
enabled: sdkCat.enabled,
|
|
5899
|
-
presetIndex: sdkCat.presetIndex,
|
|
5900
|
-
dryWet: sdkCat.dryWet
|
|
5901
|
-
};
|
|
5902
|
-
}
|
|
5903
|
-
}
|
|
5904
|
-
return result;
|
|
5905
|
-
}
|
|
5906
|
-
function parseLLMNoteResponse(content) {
|
|
5907
|
-
try {
|
|
5908
|
-
let jsonStr = content.trim();
|
|
5909
|
-
const fenceMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
|
|
5910
|
-
if (fenceMatch) {
|
|
5911
|
-
jsonStr = fenceMatch[1].trim();
|
|
5912
|
-
}
|
|
5913
|
-
const parsed = JSON.parse(jsonStr);
|
|
5914
|
-
if (typeof parsed !== "object" || parsed === null || !("notes" in parsed)) {
|
|
5915
|
-
return null;
|
|
5916
|
-
}
|
|
5917
|
-
const obj = parsed;
|
|
5918
|
-
if (!Array.isArray(obj.notes)) {
|
|
5919
|
-
return null;
|
|
5920
|
-
}
|
|
5921
|
-
const validNotes = [];
|
|
5922
|
-
for (const raw of obj.notes) {
|
|
5923
|
-
if (typeof raw !== "object" || raw === null) continue;
|
|
5924
|
-
const note = raw;
|
|
5925
|
-
const pitch = typeof note.pitch === "number" ? note.pitch : NaN;
|
|
5926
|
-
const startBeat = typeof note.startBeat === "number" ? note.startBeat : NaN;
|
|
5927
|
-
const durationBeats = typeof note.durationBeats === "number" ? note.durationBeats : NaN;
|
|
5928
|
-
const velocity = typeof note.velocity === "number" ? note.velocity : NaN;
|
|
5929
|
-
if (!isNaN(pitch) && pitch >= 0 && pitch <= 127 && !isNaN(startBeat) && startBeat >= 0 && !isNaN(durationBeats) && durationBeats > 0 && !isNaN(velocity) && velocity >= 1 && velocity <= 127) {
|
|
5930
|
-
validNotes.push({
|
|
5931
|
-
pitch: Math.round(pitch),
|
|
5932
|
-
startBeat,
|
|
5933
|
-
durationBeats,
|
|
5934
|
-
velocity: Math.round(velocity)
|
|
5935
|
-
});
|
|
5936
|
-
}
|
|
5937
|
-
}
|
|
5938
|
-
const role = typeof obj.role === "string" ? obj.role : void 0;
|
|
5939
|
-
return { notes: validNotes, role };
|
|
5940
|
-
} catch {
|
|
5941
|
-
return null;
|
|
5942
|
-
}
|
|
5943
|
-
}
|
|
5944
|
-
|
|
5945
5968
|
// src/panel-core/group-meta.ts
|
|
5946
5969
|
function parseTrackGroups(sceneData, spec) {
|
|
5947
5970
|
const pattern = new RegExp(`^track:(.+):${spec.metaKey}$`);
|
|
@@ -9124,6 +9147,7 @@ export {
|
|
|
9124
9147
|
defaultVoiceSpecs,
|
|
9125
9148
|
describeViolations,
|
|
9126
9149
|
drawWaveform,
|
|
9150
|
+
effectivePxPerBeat,
|
|
9127
9151
|
enforceVoice,
|
|
9128
9152
|
foldPitchToRegister,
|
|
9129
9153
|
formatConcurrentTracks,
|
|
@@ -9142,6 +9166,7 @@ export {
|
|
|
9142
9166
|
pickTopKWeighted,
|
|
9143
9167
|
pitchToName,
|
|
9144
9168
|
pluginFxToToggleFx,
|
|
9169
|
+
promptEnterToGenerate,
|
|
9145
9170
|
pxToCell,
|
|
9146
9171
|
reconcileSlots,
|
|
9147
9172
|
resizeNoteDuration,
|