@waveform-playlist/ui-components 9.0.4 → 9.1.0
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 +35 -11
- package/dist/index.d.ts +35 -11
- package/dist/index.js +530 -349
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +473 -292
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -62,6 +62,7 @@ __export(index_exports, {
|
|
|
62
62
|
LoopRegion: () => LoopRegion,
|
|
63
63
|
LoopRegionMarkers: () => LoopRegionMarkers,
|
|
64
64
|
MasterVolumeControl: () => MasterVolumeControl,
|
|
65
|
+
PianoRollChannel: () => PianoRollChannel,
|
|
65
66
|
Playhead: () => Playhead,
|
|
66
67
|
PlayheadWithMarker: () => PlayheadWithMarker,
|
|
67
68
|
Playlist: () => Playlist,
|
|
@@ -527,6 +528,10 @@ var defaultTheme = {
|
|
|
527
528
|
annotationResizeHandleColor: "rgba(0, 0, 0, 0.4)",
|
|
528
529
|
annotationResizeHandleActiveColor: "rgba(0, 0, 0, 0.8)",
|
|
529
530
|
annotationTextItemHoverBackground: "rgba(0, 0, 0, 0.03)",
|
|
531
|
+
// Piano roll colors
|
|
532
|
+
pianoRollNoteColor: "#2a7070",
|
|
533
|
+
pianoRollSelectedNoteColor: "#3d9e9e",
|
|
534
|
+
pianoRollBackgroundColor: "#1a1a2e",
|
|
530
535
|
// Spacing and sizing
|
|
531
536
|
borderRadius: "4px",
|
|
532
537
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif',
|
|
@@ -604,6 +609,10 @@ var darkTheme = {
|
|
|
604
609
|
annotationResizeHandleColor: "rgba(200, 160, 120, 0.5)",
|
|
605
610
|
annotationResizeHandleActiveColor: "rgba(220, 180, 140, 0.8)",
|
|
606
611
|
annotationTextItemHoverBackground: "rgba(200, 160, 120, 0.08)",
|
|
612
|
+
// Piano roll colors
|
|
613
|
+
pianoRollNoteColor: "#c49a6c",
|
|
614
|
+
pianoRollSelectedNoteColor: "#e8c090",
|
|
615
|
+
pianoRollBackgroundColor: "#0d0d14",
|
|
607
616
|
// Spacing and sizing
|
|
608
617
|
borderRadius: "4px",
|
|
609
618
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif',
|
|
@@ -615,19 +624,32 @@ var darkTheme = {
|
|
|
615
624
|
var import_react = require("react");
|
|
616
625
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
617
626
|
var ViewportStore = class {
|
|
618
|
-
constructor() {
|
|
619
|
-
this._state = null;
|
|
627
|
+
constructor(containerEl) {
|
|
620
628
|
this._listeners = /* @__PURE__ */ new Set();
|
|
629
|
+
this._notifyRafId = null;
|
|
621
630
|
this.subscribe = (callback) => {
|
|
622
631
|
this._listeners.add(callback);
|
|
623
632
|
return () => this._listeners.delete(callback);
|
|
624
633
|
};
|
|
625
634
|
this.getSnapshot = () => this._state;
|
|
635
|
+
const width = containerEl?.clientWidth ?? (typeof window !== "undefined" ? window.innerWidth : 1024);
|
|
636
|
+
const buffer = width * 1.5;
|
|
637
|
+
this._state = {
|
|
638
|
+
scrollLeft: 0,
|
|
639
|
+
containerWidth: width,
|
|
640
|
+
visibleStart: 0,
|
|
641
|
+
visibleEnd: width + buffer
|
|
642
|
+
};
|
|
626
643
|
}
|
|
627
644
|
/**
|
|
628
645
|
* Update viewport state. Applies a 100px scroll threshold to skip updates
|
|
629
646
|
* that don't affect chunk visibility (1000px chunks with 1.5× overscan buffer).
|
|
630
647
|
* Only notifies listeners when the state actually changes.
|
|
648
|
+
*
|
|
649
|
+
* Listener notification is deferred by one frame via requestAnimationFrame
|
|
650
|
+
* to avoid conflicting with React 19's concurrent rendering. When React
|
|
651
|
+
* time-slices a render across frames, synchronous useSyncExternalStore
|
|
652
|
+
* notifications can trigger "Should not already be working" errors.
|
|
631
653
|
*/
|
|
632
654
|
update(scrollLeft, containerWidth) {
|
|
633
655
|
const buffer = containerWidth * 1.5;
|
|
@@ -637,8 +659,19 @@ var ViewportStore = class {
|
|
|
637
659
|
return;
|
|
638
660
|
}
|
|
639
661
|
this._state = { scrollLeft, containerWidth, visibleStart, visibleEnd };
|
|
640
|
-
|
|
641
|
-
|
|
662
|
+
if (this._notifyRafId === null) {
|
|
663
|
+
this._notifyRafId = requestAnimationFrame(() => {
|
|
664
|
+
this._notifyRafId = null;
|
|
665
|
+
for (const listener of this._listeners) {
|
|
666
|
+
listener();
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
cancelPendingNotification() {
|
|
672
|
+
if (this._notifyRafId !== null) {
|
|
673
|
+
cancelAnimationFrame(this._notifyRafId);
|
|
674
|
+
this._notifyRafId = null;
|
|
642
675
|
}
|
|
643
676
|
}
|
|
644
677
|
};
|
|
@@ -649,7 +682,7 @@ var NULL_SNAPSHOT = () => null;
|
|
|
649
682
|
var ScrollViewportProvider = ({ containerRef, children }) => {
|
|
650
683
|
const storeRef = (0, import_react.useRef)(null);
|
|
651
684
|
if (storeRef.current === null) {
|
|
652
|
-
storeRef.current = new ViewportStore();
|
|
685
|
+
storeRef.current = new ViewportStore(containerRef.current);
|
|
653
686
|
}
|
|
654
687
|
const store = storeRef.current;
|
|
655
688
|
const rafIdRef = (0, import_react.useRef)(null);
|
|
@@ -665,43 +698,27 @@ var ScrollViewportProvider = ({ containerRef, children }) => {
|
|
|
665
698
|
measure();
|
|
666
699
|
});
|
|
667
700
|
}, [measure]);
|
|
701
|
+
(0, import_react.useLayoutEffect)(() => {
|
|
702
|
+
measure();
|
|
703
|
+
}, [measure]);
|
|
668
704
|
(0, import_react.useEffect)(() => {
|
|
669
705
|
const el = containerRef.current;
|
|
670
706
|
if (!el) return;
|
|
671
|
-
measure();
|
|
672
707
|
el.addEventListener("scroll", scheduleUpdate, { passive: true });
|
|
673
|
-
let userHasInteracted = false;
|
|
674
|
-
const markInteracted = () => {
|
|
675
|
-
userHasInteracted = true;
|
|
676
|
-
};
|
|
677
|
-
el.addEventListener("pointerdown", markInteracted, { once: true });
|
|
678
|
-
el.addEventListener("keydown", markInteracted, { once: true });
|
|
679
|
-
el.addEventListener("wheel", markInteracted, { once: true, passive: true });
|
|
680
|
-
const resetHandler = () => {
|
|
681
|
-
if (!userHasInteracted && el.scrollLeft !== 0) {
|
|
682
|
-
el.scrollLeft = 0;
|
|
683
|
-
measure();
|
|
684
|
-
}
|
|
685
|
-
el.removeEventListener("scroll", resetHandler);
|
|
686
|
-
};
|
|
687
|
-
el.addEventListener("scroll", resetHandler);
|
|
688
708
|
const resizeObserver = new ResizeObserver(() => {
|
|
689
709
|
scheduleUpdate();
|
|
690
710
|
});
|
|
691
711
|
resizeObserver.observe(el);
|
|
692
712
|
return () => {
|
|
693
713
|
el.removeEventListener("scroll", scheduleUpdate);
|
|
694
|
-
el.removeEventListener("scroll", resetHandler);
|
|
695
|
-
el.removeEventListener("pointerdown", markInteracted);
|
|
696
|
-
el.removeEventListener("keydown", markInteracted);
|
|
697
|
-
el.removeEventListener("wheel", markInteracted);
|
|
698
714
|
resizeObserver.disconnect();
|
|
699
715
|
if (rafIdRef.current !== null) {
|
|
700
716
|
cancelAnimationFrame(rafIdRef.current);
|
|
701
717
|
rafIdRef.current = null;
|
|
702
718
|
}
|
|
719
|
+
store.cancelPendingNotification();
|
|
703
720
|
};
|
|
704
|
-
}, [containerRef,
|
|
721
|
+
}, [containerRef, scheduleUpdate, store]);
|
|
705
722
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ViewportStoreContext.Provider, { value: store, children });
|
|
706
723
|
};
|
|
707
724
|
var useScrollViewport = () => {
|
|
@@ -836,8 +853,6 @@ var Waveform = import_styled_components9.default.canvas.attrs((props) => ({
|
|
|
836
853
|
}))`
|
|
837
854
|
position: absolute;
|
|
838
855
|
top: 0;
|
|
839
|
-
/* Promote to own compositing layer for smoother scrolling */
|
|
840
|
-
will-change: transform;
|
|
841
856
|
/* Disable image rendering interpolation */
|
|
842
857
|
image-rendering: pixelated;
|
|
843
858
|
image-rendering: crisp-edges;
|
|
@@ -874,7 +889,8 @@ var Channel = (props) => {
|
|
|
874
889
|
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
875
890
|
const clipOriginX = useClipViewportOrigin();
|
|
876
891
|
const visibleChunkIndices = useVisibleChunkIndices(length, import_core.MAX_CANVAS_WIDTH, clipOriginX);
|
|
877
|
-
(0, import_react4.
|
|
892
|
+
(0, import_react4.useEffect)(() => {
|
|
893
|
+
const tDraw = performance.now();
|
|
878
894
|
const step = barWidth + barGap;
|
|
879
895
|
for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
|
|
880
896
|
const globalPixelOffset = canvasIdx * import_core.MAX_CANVAS_WIDTH;
|
|
@@ -910,6 +926,9 @@ var Channel = (props) => {
|
|
|
910
926
|
}
|
|
911
927
|
}
|
|
912
928
|
}
|
|
929
|
+
console.log(
|
|
930
|
+
`[waveform] draw ch${index}: ${canvasMapRef.current.size} chunks, ${(performance.now() - tDraw).toFixed(1)}ms`
|
|
931
|
+
);
|
|
913
932
|
}, [
|
|
914
933
|
canvasMapRef,
|
|
915
934
|
data,
|
|
@@ -922,7 +941,8 @@ var Channel = (props) => {
|
|
|
922
941
|
barWidth,
|
|
923
942
|
barGap,
|
|
924
943
|
drawMode,
|
|
925
|
-
visibleChunkIndices
|
|
944
|
+
visibleChunkIndices,
|
|
945
|
+
index
|
|
926
946
|
]);
|
|
927
947
|
const waveforms = visibleChunkIndices.map((i) => {
|
|
928
948
|
const chunkLeft = i * import_core.MAX_CANVAS_WIDTH;
|
|
@@ -1040,7 +1060,7 @@ var ClipHeaderPresentational = ({
|
|
|
1040
1060
|
trackName,
|
|
1041
1061
|
isSelected = false
|
|
1042
1062
|
}) => {
|
|
1043
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderContainer, { $interactive: false, $isSelected: isSelected, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TrackName, { children: trackName }) });
|
|
1063
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderContainer, { $interactive: false, $isSelected: isSelected, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TrackName, { title: trackName, children: trackName }) });
|
|
1044
1064
|
};
|
|
1045
1065
|
var ClipHeader = ({
|
|
1046
1066
|
clipId,
|
|
@@ -1062,7 +1082,7 @@ var ClipHeader = ({
|
|
|
1062
1082
|
"data-clip-id": clipId,
|
|
1063
1083
|
$interactive: true,
|
|
1064
1084
|
$isSelected: isSelected,
|
|
1065
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TrackName, { children: trackName })
|
|
1085
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TrackName, { title: trackName, children: trackName })
|
|
1066
1086
|
}
|
|
1067
1087
|
);
|
|
1068
1088
|
};
|
|
@@ -1283,7 +1303,7 @@ var Clip = ({
|
|
|
1283
1303
|
"data-clip-container": "true",
|
|
1284
1304
|
"data-track-id": trackId,
|
|
1285
1305
|
onMouseDown,
|
|
1286
|
-
|
|
1306
|
+
tabIndex: -1,
|
|
1287
1307
|
children: [
|
|
1288
1308
|
showHeader && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1289
1309
|
ClipHeader,
|
|
@@ -1393,11 +1413,140 @@ var MasterVolumeControl = ({
|
|
|
1393
1413
|
] });
|
|
1394
1414
|
};
|
|
1395
1415
|
|
|
1396
|
-
// src/components/
|
|
1416
|
+
// src/components/PianoRollChannel.tsx
|
|
1397
1417
|
var import_react8 = require("react");
|
|
1398
1418
|
var import_styled_components15 = __toESM(require("styled-components"));
|
|
1419
|
+
var import_core2 = require("@waveform-playlist/core");
|
|
1399
1420
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1400
|
-
var
|
|
1421
|
+
var NoteCanvas = import_styled_components15.default.canvas.attrs((props) => ({
|
|
1422
|
+
style: {
|
|
1423
|
+
width: `${props.$cssWidth}px`,
|
|
1424
|
+
height: `${props.$waveHeight}px`,
|
|
1425
|
+
left: `${props.$left}px`
|
|
1426
|
+
}
|
|
1427
|
+
}))`
|
|
1428
|
+
position: absolute;
|
|
1429
|
+
top: 0;
|
|
1430
|
+
image-rendering: pixelated;
|
|
1431
|
+
image-rendering: crisp-edges;
|
|
1432
|
+
`;
|
|
1433
|
+
var Wrapper2 = import_styled_components15.default.div.attrs((props) => ({
|
|
1434
|
+
style: {
|
|
1435
|
+
top: `${props.$waveHeight * props.$index}px`,
|
|
1436
|
+
width: `${props.$cssWidth}px`,
|
|
1437
|
+
height: `${props.$waveHeight}px`
|
|
1438
|
+
}
|
|
1439
|
+
}))`
|
|
1440
|
+
position: absolute;
|
|
1441
|
+
background: ${(props) => props.$backgroundColor};
|
|
1442
|
+
transform: translateZ(0);
|
|
1443
|
+
backface-visibility: hidden;
|
|
1444
|
+
`;
|
|
1445
|
+
var PianoRollChannel = ({
|
|
1446
|
+
index,
|
|
1447
|
+
midiNotes,
|
|
1448
|
+
length,
|
|
1449
|
+
waveHeight,
|
|
1450
|
+
devicePixelRatio,
|
|
1451
|
+
samplesPerPixel,
|
|
1452
|
+
sampleRate,
|
|
1453
|
+
clipOffsetSeconds,
|
|
1454
|
+
noteColor = "#2a7070",
|
|
1455
|
+
selectedNoteColor = "#3d9e9e",
|
|
1456
|
+
isSelected = false,
|
|
1457
|
+
transparentBackground = false,
|
|
1458
|
+
backgroundColor = "#1a1a2e"
|
|
1459
|
+
}) => {
|
|
1460
|
+
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
1461
|
+
const clipOriginX = useClipViewportOrigin();
|
|
1462
|
+
const visibleChunkIndices = useVisibleChunkIndices(length, import_core2.MAX_CANVAS_WIDTH, clipOriginX);
|
|
1463
|
+
const { minMidi, maxMidi } = (0, import_react8.useMemo)(() => {
|
|
1464
|
+
if (midiNotes.length === 0) return { minMidi: 0, maxMidi: 127 };
|
|
1465
|
+
let min = 127, max = 0;
|
|
1466
|
+
for (const note of midiNotes) {
|
|
1467
|
+
if (note.midi < min) min = note.midi;
|
|
1468
|
+
if (note.midi > max) max = note.midi;
|
|
1469
|
+
}
|
|
1470
|
+
return { minMidi: Math.max(0, min - 1), maxMidi: Math.min(127, max + 1) };
|
|
1471
|
+
}, [midiNotes]);
|
|
1472
|
+
const color = isSelected ? selectedNoteColor : noteColor;
|
|
1473
|
+
(0, import_react8.useEffect)(() => {
|
|
1474
|
+
const tDraw = performance.now();
|
|
1475
|
+
const noteRange = maxMidi - minMidi + 1;
|
|
1476
|
+
const noteHeight = Math.max(2, waveHeight / noteRange);
|
|
1477
|
+
const pixelsPerSecond = sampleRate / samplesPerPixel;
|
|
1478
|
+
for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
|
|
1479
|
+
const chunkPixelStart = canvasIdx * import_core2.MAX_CANVAS_WIDTH;
|
|
1480
|
+
const canvasWidth = canvas.width / devicePixelRatio;
|
|
1481
|
+
const ctx = canvas.getContext("2d");
|
|
1482
|
+
if (!ctx) continue;
|
|
1483
|
+
ctx.resetTransform();
|
|
1484
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1485
|
+
ctx.imageSmoothingEnabled = false;
|
|
1486
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
1487
|
+
const chunkStartTime = chunkPixelStart * samplesPerPixel / sampleRate;
|
|
1488
|
+
const chunkEndTime = (chunkPixelStart + canvasWidth) * samplesPerPixel / sampleRate;
|
|
1489
|
+
for (const note of midiNotes) {
|
|
1490
|
+
const noteStart = note.time - clipOffsetSeconds;
|
|
1491
|
+
const noteEnd = noteStart + note.duration;
|
|
1492
|
+
if (noteEnd <= chunkStartTime || noteStart >= chunkEndTime) continue;
|
|
1493
|
+
const x = noteStart * pixelsPerSecond - chunkPixelStart;
|
|
1494
|
+
const w = Math.max(2, note.duration * pixelsPerSecond);
|
|
1495
|
+
const y = (maxMidi - note.midi) / noteRange * waveHeight;
|
|
1496
|
+
const alpha = 0.3 + note.velocity * 0.7;
|
|
1497
|
+
ctx.fillStyle = color;
|
|
1498
|
+
ctx.globalAlpha = alpha;
|
|
1499
|
+
const r = 1;
|
|
1500
|
+
ctx.beginPath();
|
|
1501
|
+
ctx.roundRect(x, y, w, noteHeight, r);
|
|
1502
|
+
ctx.fill();
|
|
1503
|
+
}
|
|
1504
|
+
ctx.globalAlpha = 1;
|
|
1505
|
+
}
|
|
1506
|
+
console.log(
|
|
1507
|
+
`[piano-roll] draw ch${index}: ${canvasMapRef.current.size} chunks, ${midiNotes.length} notes, ${(performance.now() - tDraw).toFixed(1)}ms`
|
|
1508
|
+
);
|
|
1509
|
+
}, [
|
|
1510
|
+
canvasMapRef,
|
|
1511
|
+
midiNotes,
|
|
1512
|
+
waveHeight,
|
|
1513
|
+
devicePixelRatio,
|
|
1514
|
+
samplesPerPixel,
|
|
1515
|
+
sampleRate,
|
|
1516
|
+
clipOffsetSeconds,
|
|
1517
|
+
color,
|
|
1518
|
+
minMidi,
|
|
1519
|
+
maxMidi,
|
|
1520
|
+
length,
|
|
1521
|
+
visibleChunkIndices,
|
|
1522
|
+
index
|
|
1523
|
+
]);
|
|
1524
|
+
const canvases = visibleChunkIndices.map((i) => {
|
|
1525
|
+
const chunkLeft = i * import_core2.MAX_CANVAS_WIDTH;
|
|
1526
|
+
const currentWidth = Math.min(length - chunkLeft, import_core2.MAX_CANVAS_WIDTH);
|
|
1527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1528
|
+
NoteCanvas,
|
|
1529
|
+
{
|
|
1530
|
+
$cssWidth: currentWidth,
|
|
1531
|
+
$left: chunkLeft,
|
|
1532
|
+
width: currentWidth * devicePixelRatio,
|
|
1533
|
+
height: waveHeight * devicePixelRatio,
|
|
1534
|
+
$waveHeight: waveHeight,
|
|
1535
|
+
"data-index": i,
|
|
1536
|
+
ref: canvasRef
|
|
1537
|
+
},
|
|
1538
|
+
`${length}-${i}`
|
|
1539
|
+
);
|
|
1540
|
+
});
|
|
1541
|
+
const bgColor = transparentBackground ? "transparent" : backgroundColor;
|
|
1542
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Wrapper2, { $index: index, $cssWidth: length, $waveHeight: waveHeight, $backgroundColor: bgColor, children: canvases });
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
// src/components/Playhead.tsx
|
|
1546
|
+
var import_react9 = require("react");
|
|
1547
|
+
var import_styled_components16 = __toESM(require("styled-components"));
|
|
1548
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1549
|
+
var PlayheadLine = import_styled_components16.default.div.attrs((props) => ({
|
|
1401
1550
|
style: {
|
|
1402
1551
|
transform: `translate3d(${props.$position}px, 0, 0)`
|
|
1403
1552
|
}
|
|
@@ -1413,9 +1562,9 @@ var PlayheadLine = import_styled_components15.default.div.attrs((props) => ({
|
|
|
1413
1562
|
will-change: transform;
|
|
1414
1563
|
`;
|
|
1415
1564
|
var Playhead = ({ position, color = "#ff0000" }) => {
|
|
1416
|
-
return /* @__PURE__ */ (0,
|
|
1565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PlayheadLine, { $position: position, $color: color });
|
|
1417
1566
|
};
|
|
1418
|
-
var PlayheadWithMarkerContainer =
|
|
1567
|
+
var PlayheadWithMarkerContainer = import_styled_components16.default.div`
|
|
1419
1568
|
position: absolute;
|
|
1420
1569
|
top: 0;
|
|
1421
1570
|
left: 0;
|
|
@@ -1424,7 +1573,7 @@ var PlayheadWithMarkerContainer = import_styled_components15.default.div`
|
|
|
1424
1573
|
pointer-events: none;
|
|
1425
1574
|
will-change: transform;
|
|
1426
1575
|
`;
|
|
1427
|
-
var MarkerTriangle =
|
|
1576
|
+
var MarkerTriangle = import_styled_components16.default.div`
|
|
1428
1577
|
position: absolute;
|
|
1429
1578
|
top: -10px;
|
|
1430
1579
|
left: -6px;
|
|
@@ -1434,7 +1583,7 @@ var MarkerTriangle = import_styled_components15.default.div`
|
|
|
1434
1583
|
border-right: 7px solid transparent;
|
|
1435
1584
|
border-top: 10px solid ${(props) => props.$color};
|
|
1436
1585
|
`;
|
|
1437
|
-
var MarkerLine =
|
|
1586
|
+
var MarkerLine = import_styled_components16.default.div`
|
|
1438
1587
|
position: absolute;
|
|
1439
1588
|
top: 0;
|
|
1440
1589
|
left: 0;
|
|
@@ -1450,13 +1599,13 @@ var PlayheadWithMarker = ({
|
|
|
1450
1599
|
audioStartPositionRef,
|
|
1451
1600
|
samplesPerPixel,
|
|
1452
1601
|
sampleRate,
|
|
1453
|
-
controlsOffset,
|
|
1602
|
+
controlsOffset = 0,
|
|
1454
1603
|
getAudioContextTime,
|
|
1455
1604
|
getPlaybackTime
|
|
1456
1605
|
}) => {
|
|
1457
|
-
const containerRef = (0,
|
|
1458
|
-
const animationFrameRef = (0,
|
|
1459
|
-
(0,
|
|
1606
|
+
const containerRef = (0, import_react9.useRef)(null);
|
|
1607
|
+
const animationFrameRef = (0, import_react9.useRef)(null);
|
|
1608
|
+
(0, import_react9.useEffect)(() => {
|
|
1460
1609
|
const updatePosition = () => {
|
|
1461
1610
|
if (containerRef.current) {
|
|
1462
1611
|
let time;
|
|
@@ -1501,35 +1650,51 @@ var PlayheadWithMarker = ({
|
|
|
1501
1650
|
getAudioContextTime,
|
|
1502
1651
|
getPlaybackTime
|
|
1503
1652
|
]);
|
|
1504
|
-
(0,
|
|
1653
|
+
(0, import_react9.useEffect)(() => {
|
|
1505
1654
|
if (!isPlaying && containerRef.current) {
|
|
1506
1655
|
const time = currentTimeRef.current ?? 0;
|
|
1507
1656
|
const pos = time * sampleRate / samplesPerPixel + controlsOffset;
|
|
1508
1657
|
containerRef.current.style.transform = `translate3d(${pos}px, 0, 0)`;
|
|
1509
1658
|
}
|
|
1510
1659
|
});
|
|
1511
|
-
return /* @__PURE__ */ (0,
|
|
1512
|
-
/* @__PURE__ */ (0,
|
|
1513
|
-
/* @__PURE__ */ (0,
|
|
1660
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(PlayheadWithMarkerContainer, { ref: containerRef, $color: color, children: [
|
|
1661
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MarkerTriangle, { $color: color }),
|
|
1662
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MarkerLine, { $color: color })
|
|
1514
1663
|
] });
|
|
1515
1664
|
};
|
|
1516
1665
|
|
|
1517
1666
|
// src/components/Playlist.tsx
|
|
1518
|
-
var
|
|
1519
|
-
var
|
|
1520
|
-
var
|
|
1521
|
-
var
|
|
1667
|
+
var import_styled_components17 = __toESM(require("styled-components"));
|
|
1668
|
+
var import_react10 = require("react");
|
|
1669
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1670
|
+
var Wrapper3 = import_styled_components17.default.div`
|
|
1671
|
+
display: flex;
|
|
1522
1672
|
overflow-y: hidden;
|
|
1673
|
+
position: relative;
|
|
1674
|
+
`;
|
|
1675
|
+
var ControlsColumn = import_styled_components17.default.div.attrs((props) => ({
|
|
1676
|
+
style: { width: `${props.$width}px` }
|
|
1677
|
+
}))`
|
|
1678
|
+
flex-shrink: 0;
|
|
1679
|
+
overflow: hidden;
|
|
1680
|
+
`;
|
|
1681
|
+
var TimescaleGap = import_styled_components17.default.div.attrs((props) => ({
|
|
1682
|
+
style: { height: `${props.$height}px` }
|
|
1683
|
+
}))``;
|
|
1684
|
+
var ScrollArea = import_styled_components17.default.div`
|
|
1523
1685
|
overflow-x: auto;
|
|
1686
|
+
overflow-y: hidden;
|
|
1687
|
+
overflow-anchor: none;
|
|
1688
|
+
flex: 1;
|
|
1524
1689
|
position: relative;
|
|
1525
1690
|
`;
|
|
1526
|
-
var
|
|
1691
|
+
var ScrollContainerInner = import_styled_components17.default.div.attrs((props) => ({
|
|
1527
1692
|
style: props.$width !== void 0 ? { width: `${props.$width}px` } : {}
|
|
1528
1693
|
}))`
|
|
1529
1694
|
position: relative;
|
|
1530
1695
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
1531
1696
|
`;
|
|
1532
|
-
var TimescaleWrapper =
|
|
1697
|
+
var TimescaleWrapper = import_styled_components17.default.div.attrs((props) => ({
|
|
1533
1698
|
style: props.$width ? { minWidth: `${props.$width}px` } : {}
|
|
1534
1699
|
}))`
|
|
1535
1700
|
background: ${(props) => props.$backgroundColor || "white"};
|
|
@@ -1537,14 +1702,14 @@ var TimescaleWrapper = import_styled_components16.default.div.attrs((props) => (
|
|
|
1537
1702
|
position: relative;
|
|
1538
1703
|
overflow: hidden; /* Constrain loop region to timescale area */
|
|
1539
1704
|
`;
|
|
1540
|
-
var TracksContainer =
|
|
1705
|
+
var TracksContainer = import_styled_components17.default.div.attrs((props) => ({
|
|
1541
1706
|
style: props.$width !== void 0 ? { minWidth: `${props.$width}px` } : {}
|
|
1542
1707
|
}))`
|
|
1543
1708
|
position: relative;
|
|
1544
1709
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
1545
1710
|
width: 100%;
|
|
1546
1711
|
`;
|
|
1547
|
-
var ClickOverlay =
|
|
1712
|
+
var ClickOverlay = import_styled_components17.default.div`
|
|
1548
1713
|
position: absolute;
|
|
1549
1714
|
top: 0;
|
|
1550
1715
|
left: 0;
|
|
@@ -1561,7 +1726,6 @@ var Playlist = ({
|
|
|
1561
1726
|
timescale,
|
|
1562
1727
|
timescaleWidth,
|
|
1563
1728
|
tracksWidth,
|
|
1564
|
-
scrollContainerWidth,
|
|
1565
1729
|
controlsWidth,
|
|
1566
1730
|
onTracksClick,
|
|
1567
1731
|
onTracksMouseDown,
|
|
@@ -1569,40 +1733,48 @@ var Playlist = ({
|
|
|
1569
1733
|
onTracksMouseUp,
|
|
1570
1734
|
scrollContainerRef,
|
|
1571
1735
|
isSelecting,
|
|
1572
|
-
"data-playlist-state": playlistState
|
|
1736
|
+
"data-playlist-state": playlistState,
|
|
1737
|
+
trackControlsSlots,
|
|
1738
|
+
timescaleGapHeight = 0
|
|
1573
1739
|
}) => {
|
|
1574
|
-
const
|
|
1575
|
-
const handleRef = (0,
|
|
1740
|
+
const scrollAreaRef = (0, import_react10.useRef)(null);
|
|
1741
|
+
const handleRef = (0, import_react10.useCallback)(
|
|
1576
1742
|
(el) => {
|
|
1577
|
-
|
|
1743
|
+
scrollAreaRef.current = el;
|
|
1578
1744
|
scrollContainerRef?.(el);
|
|
1579
1745
|
},
|
|
1580
1746
|
[scrollContainerRef]
|
|
1581
1747
|
);
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
/* @__PURE__ */ (0,
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1748
|
+
const showControls = controlsWidth !== void 0 && controlsWidth > 0;
|
|
1749
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Wrapper3, { "data-playlist-state": playlistState, children: [
|
|
1750
|
+
showControls && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(ControlsColumn, { $width: controlsWidth, children: [
|
|
1751
|
+
timescaleGapHeight > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TimescaleGap, { $height: timescaleGapHeight }),
|
|
1752
|
+
trackControlsSlots
|
|
1753
|
+
] }),
|
|
1754
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ScrollArea, { "data-scroll-container": "true", ref: handleRef, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ScrollViewportProvider, { containerRef: scrollAreaRef, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(ScrollContainerInner, { $backgroundColor: backgroundColor, $width: tracksWidth, children: [
|
|
1755
|
+
timescale && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TimescaleWrapper, { $width: timescaleWidth, $backgroundColor: timescaleBackgroundColor, children: timescale }),
|
|
1756
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(TracksContainer, { $width: tracksWidth, $backgroundColor: backgroundColor, children: [
|
|
1757
|
+
children,
|
|
1758
|
+
(onTracksClick || onTracksMouseDown) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1759
|
+
ClickOverlay,
|
|
1760
|
+
{
|
|
1761
|
+
$isSelecting: isSelecting,
|
|
1762
|
+
onClick: onTracksClick,
|
|
1763
|
+
onMouseDown: onTracksMouseDown,
|
|
1764
|
+
onMouseMove: onTracksMouseMove,
|
|
1765
|
+
onMouseUp: onTracksMouseUp
|
|
1766
|
+
}
|
|
1767
|
+
)
|
|
1768
|
+
] })
|
|
1769
|
+
] }) }) })
|
|
1770
|
+
] });
|
|
1599
1771
|
};
|
|
1600
|
-
var StyledPlaylist = (0,
|
|
1772
|
+
var StyledPlaylist = (0, import_styled_components17.withTheme)(Playlist);
|
|
1601
1773
|
|
|
1602
1774
|
// src/components/Selection.tsx
|
|
1603
|
-
var
|
|
1604
|
-
var
|
|
1605
|
-
var SelectionOverlay =
|
|
1775
|
+
var import_styled_components18 = __toESM(require("styled-components"));
|
|
1776
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1777
|
+
var SelectionOverlay = import_styled_components18.default.div.attrs((props) => ({
|
|
1606
1778
|
style: {
|
|
1607
1779
|
left: `${props.$left}px`,
|
|
1608
1780
|
width: `${props.$width}px`
|
|
@@ -1625,14 +1797,14 @@ var Selection = ({
|
|
|
1625
1797
|
if (width <= 0) {
|
|
1626
1798
|
return null;
|
|
1627
1799
|
}
|
|
1628
|
-
return /* @__PURE__ */ (0,
|
|
1800
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SelectionOverlay, { $left: startPosition, $width: width, $color: color, "data-selection": true });
|
|
1629
1801
|
};
|
|
1630
1802
|
|
|
1631
1803
|
// src/components/LoopRegion.tsx
|
|
1632
|
-
var
|
|
1633
|
-
var
|
|
1634
|
-
var
|
|
1635
|
-
var LoopRegionOverlayDiv =
|
|
1804
|
+
var import_react11 = require("react");
|
|
1805
|
+
var import_styled_components19 = __toESM(require("styled-components"));
|
|
1806
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1807
|
+
var LoopRegionOverlayDiv = import_styled_components19.default.div.attrs((props) => ({
|
|
1636
1808
|
style: {
|
|
1637
1809
|
left: `${props.$left}px`,
|
|
1638
1810
|
width: `${props.$width}px`
|
|
@@ -1645,7 +1817,7 @@ var LoopRegionOverlayDiv = import_styled_components18.default.div.attrs((props)
|
|
|
1645
1817
|
z-index: 55; /* Between clips (z-index: 50) and selection (z-index: 60) */
|
|
1646
1818
|
pointer-events: none;
|
|
1647
1819
|
`;
|
|
1648
|
-
var LoopMarker =
|
|
1820
|
+
var LoopMarker = import_styled_components19.default.div.attrs((props) => ({
|
|
1649
1821
|
style: {
|
|
1650
1822
|
left: `${props.$left}px`
|
|
1651
1823
|
}
|
|
@@ -1680,8 +1852,8 @@ var LoopRegion = ({
|
|
|
1680
1852
|
if (width <= 0) {
|
|
1681
1853
|
return null;
|
|
1682
1854
|
}
|
|
1683
|
-
return /* @__PURE__ */ (0,
|
|
1684
|
-
/* @__PURE__ */ (0,
|
|
1855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
1856
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1685
1857
|
LoopRegionOverlayDiv,
|
|
1686
1858
|
{
|
|
1687
1859
|
$left: startPosition,
|
|
@@ -1690,7 +1862,7 @@ var LoopRegion = ({
|
|
|
1690
1862
|
"data-loop-region": true
|
|
1691
1863
|
}
|
|
1692
1864
|
),
|
|
1693
|
-
/* @__PURE__ */ (0,
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1694
1866
|
LoopMarker,
|
|
1695
1867
|
{
|
|
1696
1868
|
$left: startPosition,
|
|
@@ -1699,7 +1871,7 @@ var LoopRegion = ({
|
|
|
1699
1871
|
"data-loop-marker": "start"
|
|
1700
1872
|
}
|
|
1701
1873
|
),
|
|
1702
|
-
/* @__PURE__ */ (0,
|
|
1874
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1703
1875
|
LoopMarker,
|
|
1704
1876
|
{
|
|
1705
1877
|
$left: endPosition - 2,
|
|
@@ -1710,7 +1882,7 @@ var LoopRegion = ({
|
|
|
1710
1882
|
)
|
|
1711
1883
|
] });
|
|
1712
1884
|
};
|
|
1713
|
-
var DraggableMarkerHandle =
|
|
1885
|
+
var DraggableMarkerHandle = import_styled_components19.default.div.attrs((props) => ({
|
|
1714
1886
|
style: {
|
|
1715
1887
|
left: `${props.$left}px`
|
|
1716
1888
|
}
|
|
@@ -1752,7 +1924,7 @@ var DraggableMarkerHandle = import_styled_components18.default.div.attrs((props)
|
|
|
1752
1924
|
opacity: 1;
|
|
1753
1925
|
}
|
|
1754
1926
|
`;
|
|
1755
|
-
var TimescaleLoopShade =
|
|
1927
|
+
var TimescaleLoopShade = import_styled_components19.default.div.attrs((props) => ({
|
|
1756
1928
|
style: {
|
|
1757
1929
|
left: `${props.$left}px`,
|
|
1758
1930
|
width: `${props.$width}px`
|
|
@@ -1780,12 +1952,12 @@ var LoopRegionMarkers = ({
|
|
|
1780
1952
|
minPosition = 0,
|
|
1781
1953
|
maxPosition = Infinity
|
|
1782
1954
|
}) => {
|
|
1783
|
-
const [draggingMarker, setDraggingMarker] = (0,
|
|
1784
|
-
const dragStartX = (0,
|
|
1785
|
-
const dragStartPosition = (0,
|
|
1786
|
-
const dragStartEnd = (0,
|
|
1955
|
+
const [draggingMarker, setDraggingMarker] = (0, import_react11.useState)(null);
|
|
1956
|
+
const dragStartX = (0, import_react11.useRef)(0);
|
|
1957
|
+
const dragStartPosition = (0, import_react11.useRef)(0);
|
|
1958
|
+
const dragStartEnd = (0, import_react11.useRef)(0);
|
|
1787
1959
|
const width = Math.max(0, endPosition - startPosition);
|
|
1788
|
-
const handleMarkerMouseDown = (0,
|
|
1960
|
+
const handleMarkerMouseDown = (0, import_react11.useCallback)(
|
|
1789
1961
|
(e, marker) => {
|
|
1790
1962
|
e.preventDefault();
|
|
1791
1963
|
e.stopPropagation();
|
|
@@ -1813,7 +1985,7 @@ var LoopRegionMarkers = ({
|
|
|
1813
1985
|
},
|
|
1814
1986
|
[startPosition, endPosition, minPosition, maxPosition, onLoopStartChange, onLoopEndChange]
|
|
1815
1987
|
);
|
|
1816
|
-
const handleRegionMouseDown = (0,
|
|
1988
|
+
const handleRegionMouseDown = (0, import_react11.useCallback)(
|
|
1817
1989
|
(e) => {
|
|
1818
1990
|
e.preventDefault();
|
|
1819
1991
|
e.stopPropagation();
|
|
@@ -1849,8 +2021,8 @@ var LoopRegionMarkers = ({
|
|
|
1849
2021
|
if (width <= 0) {
|
|
1850
2022
|
return null;
|
|
1851
2023
|
}
|
|
1852
|
-
return /* @__PURE__ */ (0,
|
|
1853
|
-
/* @__PURE__ */ (0,
|
|
2024
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1854
2026
|
TimescaleLoopShade,
|
|
1855
2027
|
{
|
|
1856
2028
|
$left: startPosition,
|
|
@@ -1861,7 +2033,7 @@ var LoopRegionMarkers = ({
|
|
|
1861
2033
|
"data-loop-region-timescale": true
|
|
1862
2034
|
}
|
|
1863
2035
|
),
|
|
1864
|
-
/* @__PURE__ */ (0,
|
|
2036
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1865
2037
|
DraggableMarkerHandle,
|
|
1866
2038
|
{
|
|
1867
2039
|
$left: startPosition,
|
|
@@ -1872,7 +2044,7 @@ var LoopRegionMarkers = ({
|
|
|
1872
2044
|
"data-loop-marker-handle": "start"
|
|
1873
2045
|
}
|
|
1874
2046
|
),
|
|
1875
|
-
/* @__PURE__ */ (0,
|
|
2047
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1876
2048
|
DraggableMarkerHandle,
|
|
1877
2049
|
{
|
|
1878
2050
|
$left: endPosition,
|
|
@@ -1885,13 +2057,10 @@ var LoopRegionMarkers = ({
|
|
|
1885
2057
|
)
|
|
1886
2058
|
] });
|
|
1887
2059
|
};
|
|
1888
|
-
var TimescaleLoopCreator =
|
|
1889
|
-
style: {
|
|
1890
|
-
left: `${props.$leftOffset || 0}px`
|
|
1891
|
-
}
|
|
1892
|
-
}))`
|
|
2060
|
+
var TimescaleLoopCreator = import_styled_components19.default.div`
|
|
1893
2061
|
position: absolute;
|
|
1894
2062
|
top: 0;
|
|
2063
|
+
left: 0;
|
|
1895
2064
|
right: 0;
|
|
1896
2065
|
height: 100%; /* Stay within timescale bounds, don't extend into tracks */
|
|
1897
2066
|
cursor: crosshair;
|
|
@@ -1904,14 +2073,13 @@ var TimescaleLoopRegion = ({
|
|
|
1904
2073
|
regionColor = "rgba(59, 130, 246, 0.3)",
|
|
1905
2074
|
onLoopRegionChange,
|
|
1906
2075
|
minPosition = 0,
|
|
1907
|
-
maxPosition = Infinity
|
|
1908
|
-
controlsOffset = 0
|
|
2076
|
+
maxPosition = Infinity
|
|
1909
2077
|
}) => {
|
|
1910
|
-
const [, setIsCreating] = (0,
|
|
1911
|
-
const createStartX = (0,
|
|
1912
|
-
const containerRef = (0,
|
|
2078
|
+
const [, setIsCreating] = (0, import_react11.useState)(false);
|
|
2079
|
+
const createStartX = (0, import_react11.useRef)(0);
|
|
2080
|
+
const containerRef = (0, import_react11.useRef)(null);
|
|
1913
2081
|
const hasLoopRegion = endPosition > startPosition;
|
|
1914
|
-
const handleBackgroundMouseDown = (0,
|
|
2082
|
+
const handleBackgroundMouseDown = (0, import_react11.useCallback)(
|
|
1915
2083
|
(e) => {
|
|
1916
2084
|
const target = e.target;
|
|
1917
2085
|
if (target.closest("[data-loop-marker-handle]") || target.closest("[data-loop-region-timescale]")) {
|
|
@@ -1942,14 +2110,13 @@ var TimescaleLoopRegion = ({
|
|
|
1942
2110
|
},
|
|
1943
2111
|
[minPosition, maxPosition, onLoopRegionChange]
|
|
1944
2112
|
);
|
|
1945
|
-
return /* @__PURE__ */ (0,
|
|
2113
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1946
2114
|
TimescaleLoopCreator,
|
|
1947
2115
|
{
|
|
1948
2116
|
ref: containerRef,
|
|
1949
|
-
$leftOffset: controlsOffset,
|
|
1950
2117
|
onMouseDown: handleBackgroundMouseDown,
|
|
1951
2118
|
"data-timescale-loop-creator": true,
|
|
1952
|
-
children: hasLoopRegion && /* @__PURE__ */ (0,
|
|
2119
|
+
children: hasLoopRegion && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1953
2120
|
LoopRegionMarkers,
|
|
1954
2121
|
{
|
|
1955
2122
|
startPosition,
|
|
@@ -1968,10 +2135,10 @@ var TimescaleLoopRegion = ({
|
|
|
1968
2135
|
};
|
|
1969
2136
|
|
|
1970
2137
|
// src/components/SelectionTimeInputs.tsx
|
|
1971
|
-
var
|
|
2138
|
+
var import_react13 = require("react");
|
|
1972
2139
|
|
|
1973
2140
|
// src/components/TimeInput.tsx
|
|
1974
|
-
var
|
|
2141
|
+
var import_react12 = require("react");
|
|
1975
2142
|
|
|
1976
2143
|
// src/utils/timeFormat.ts
|
|
1977
2144
|
function clockFormat(seconds, decimals) {
|
|
@@ -2021,7 +2188,7 @@ function parseTime(timeStr, format) {
|
|
|
2021
2188
|
}
|
|
2022
2189
|
|
|
2023
2190
|
// src/components/TimeInput.tsx
|
|
2024
|
-
var
|
|
2191
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
2025
2192
|
var TimeInput = ({
|
|
2026
2193
|
id,
|
|
2027
2194
|
label,
|
|
@@ -2031,8 +2198,8 @@ var TimeInput = ({
|
|
|
2031
2198
|
onChange,
|
|
2032
2199
|
readOnly = false
|
|
2033
2200
|
}) => {
|
|
2034
|
-
const [displayValue, setDisplayValue] = (0,
|
|
2035
|
-
(0,
|
|
2201
|
+
const [displayValue, setDisplayValue] = (0, import_react12.useState)("");
|
|
2202
|
+
(0, import_react12.useEffect)(() => {
|
|
2036
2203
|
const formatted = formatTime(value, format);
|
|
2037
2204
|
setDisplayValue(formatted);
|
|
2038
2205
|
}, [value, format, id]);
|
|
@@ -2052,9 +2219,9 @@ var TimeInput = ({
|
|
|
2052
2219
|
e.currentTarget.blur();
|
|
2053
2220
|
}
|
|
2054
2221
|
};
|
|
2055
|
-
return /* @__PURE__ */ (0,
|
|
2056
|
-
/* @__PURE__ */ (0,
|
|
2057
|
-
/* @__PURE__ */ (0,
|
|
2222
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
2223
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ScreenReaderOnly, { as: "label", htmlFor: id, children: label }),
|
|
2224
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2058
2225
|
BaseInput,
|
|
2059
2226
|
{
|
|
2060
2227
|
type: "text",
|
|
@@ -2071,15 +2238,15 @@ var TimeInput = ({
|
|
|
2071
2238
|
};
|
|
2072
2239
|
|
|
2073
2240
|
// src/components/SelectionTimeInputs.tsx
|
|
2074
|
-
var
|
|
2241
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
2075
2242
|
var SelectionTimeInputs = ({
|
|
2076
2243
|
selectionStart,
|
|
2077
2244
|
selectionEnd,
|
|
2078
2245
|
onSelectionChange,
|
|
2079
2246
|
className
|
|
2080
2247
|
}) => {
|
|
2081
|
-
const [timeFormat, setTimeFormat] = (0,
|
|
2082
|
-
(0,
|
|
2248
|
+
const [timeFormat, setTimeFormat] = (0, import_react13.useState)("hh:mm:ss.uuu");
|
|
2249
|
+
(0, import_react13.useEffect)(() => {
|
|
2083
2250
|
const timeFormatSelect = document.querySelector(".time-format");
|
|
2084
2251
|
const handleFormatChange = () => {
|
|
2085
2252
|
if (timeFormatSelect) {
|
|
@@ -2104,8 +2271,8 @@ var SelectionTimeInputs = ({
|
|
|
2104
2271
|
onSelectionChange(selectionStart, value);
|
|
2105
2272
|
}
|
|
2106
2273
|
};
|
|
2107
|
-
return /* @__PURE__ */ (0,
|
|
2108
|
-
/* @__PURE__ */ (0,
|
|
2274
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className, children: [
|
|
2275
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2109
2276
|
TimeInput,
|
|
2110
2277
|
{
|
|
2111
2278
|
id: "audio_start",
|
|
@@ -2116,7 +2283,7 @@ var SelectionTimeInputs = ({
|
|
|
2116
2283
|
onChange: handleStartChange
|
|
2117
2284
|
}
|
|
2118
2285
|
),
|
|
2119
|
-
/* @__PURE__ */ (0,
|
|
2286
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2120
2287
|
TimeInput,
|
|
2121
2288
|
{
|
|
2122
2289
|
id: "audio_end",
|
|
@@ -2131,14 +2298,14 @@ var SelectionTimeInputs = ({
|
|
|
2131
2298
|
};
|
|
2132
2299
|
|
|
2133
2300
|
// src/contexts/DevicePixelRatio.tsx
|
|
2134
|
-
var
|
|
2135
|
-
var
|
|
2301
|
+
var import_react14 = require("react");
|
|
2302
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
2136
2303
|
function getScale() {
|
|
2137
2304
|
return window.devicePixelRatio;
|
|
2138
2305
|
}
|
|
2139
|
-
var DevicePixelRatioContext = (0,
|
|
2306
|
+
var DevicePixelRatioContext = (0, import_react14.createContext)(getScale());
|
|
2140
2307
|
var DevicePixelRatioProvider = ({ children }) => {
|
|
2141
|
-
const [scale, setScale] = (0,
|
|
2308
|
+
const [scale, setScale] = (0, import_react14.useState)(getScale());
|
|
2142
2309
|
matchMedia(`(resolution: ${getScale()}dppx)`).addEventListener(
|
|
2143
2310
|
"change",
|
|
2144
2311
|
() => {
|
|
@@ -2146,13 +2313,13 @@ var DevicePixelRatioProvider = ({ children }) => {
|
|
|
2146
2313
|
},
|
|
2147
2314
|
{ once: true }
|
|
2148
2315
|
);
|
|
2149
|
-
return /* @__PURE__ */ (0,
|
|
2316
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });
|
|
2150
2317
|
};
|
|
2151
|
-
var useDevicePixelRatio = () => (0,
|
|
2318
|
+
var useDevicePixelRatio = () => (0, import_react14.useContext)(DevicePixelRatioContext);
|
|
2152
2319
|
|
|
2153
2320
|
// src/contexts/PlaylistInfo.tsx
|
|
2154
|
-
var
|
|
2155
|
-
var PlaylistInfoContext = (0,
|
|
2321
|
+
var import_react15 = require("react");
|
|
2322
|
+
var PlaylistInfoContext = (0, import_react15.createContext)({
|
|
2156
2323
|
sampleRate: 48e3,
|
|
2157
2324
|
samplesPerPixel: 1e3,
|
|
2158
2325
|
zoomLevels: [1e3, 1500, 2e3, 2500],
|
|
@@ -2166,22 +2333,22 @@ var PlaylistInfoContext = (0, import_react14.createContext)({
|
|
|
2166
2333
|
barWidth: 1,
|
|
2167
2334
|
barGap: 0
|
|
2168
2335
|
});
|
|
2169
|
-
var usePlaylistInfo = () => (0,
|
|
2336
|
+
var usePlaylistInfo = () => (0, import_react15.useContext)(PlaylistInfoContext);
|
|
2170
2337
|
|
|
2171
2338
|
// src/contexts/Theme.tsx
|
|
2172
|
-
var import_react15 = require("react");
|
|
2173
|
-
var import_styled_components19 = require("styled-components");
|
|
2174
|
-
var useTheme2 = () => (0, import_react15.useContext)(import_styled_components19.ThemeContext);
|
|
2175
|
-
|
|
2176
|
-
// src/contexts/TrackControls.tsx
|
|
2177
2339
|
var import_react16 = require("react");
|
|
2178
|
-
var
|
|
2179
|
-
var
|
|
2180
|
-
var useTrackControls = () => (0, import_react16.useContext)(TrackControlsContext);
|
|
2340
|
+
var import_styled_components20 = require("styled-components");
|
|
2341
|
+
var useTheme2 = () => (0, import_react16.useContext)(import_styled_components20.ThemeContext);
|
|
2181
2342
|
|
|
2182
|
-
// src/contexts/
|
|
2343
|
+
// src/contexts/TrackControls.tsx
|
|
2183
2344
|
var import_react17 = require("react");
|
|
2184
2345
|
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
2346
|
+
var TrackControlsContext = (0, import_react17.createContext)(/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react17.Fragment, {}));
|
|
2347
|
+
var useTrackControls = () => (0, import_react17.useContext)(TrackControlsContext);
|
|
2348
|
+
|
|
2349
|
+
// src/contexts/Playout.tsx
|
|
2350
|
+
var import_react18 = require("react");
|
|
2351
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
2185
2352
|
var defaultProgress = 0;
|
|
2186
2353
|
var defaultIsPlaying = false;
|
|
2187
2354
|
var defaultSelectionStart = 0;
|
|
@@ -2192,8 +2359,8 @@ var defaultPlayout = {
|
|
|
2192
2359
|
selectionStart: defaultSelectionStart,
|
|
2193
2360
|
selectionEnd: defaultSelectionEnd
|
|
2194
2361
|
};
|
|
2195
|
-
var PlayoutStatusContext = (0,
|
|
2196
|
-
var PlayoutStatusUpdateContext = (0,
|
|
2362
|
+
var PlayoutStatusContext = (0, import_react18.createContext)(defaultPlayout);
|
|
2363
|
+
var PlayoutStatusUpdateContext = (0, import_react18.createContext)({
|
|
2197
2364
|
setIsPlaying: () => {
|
|
2198
2365
|
},
|
|
2199
2366
|
setProgress: () => {
|
|
@@ -2202,26 +2369,26 @@ var PlayoutStatusUpdateContext = (0, import_react17.createContext)({
|
|
|
2202
2369
|
}
|
|
2203
2370
|
});
|
|
2204
2371
|
var PlayoutProvider = ({ children }) => {
|
|
2205
|
-
const [isPlaying, setIsPlaying] = (0,
|
|
2206
|
-
const [progress, setProgress] = (0,
|
|
2207
|
-
const [selectionStart, setSelectionStart] = (0,
|
|
2208
|
-
const [selectionEnd, setSelectionEnd] = (0,
|
|
2372
|
+
const [isPlaying, setIsPlaying] = (0, import_react18.useState)(defaultIsPlaying);
|
|
2373
|
+
const [progress, setProgress] = (0, import_react18.useState)(defaultProgress);
|
|
2374
|
+
const [selectionStart, setSelectionStart] = (0, import_react18.useState)(defaultSelectionStart);
|
|
2375
|
+
const [selectionEnd, setSelectionEnd] = (0, import_react18.useState)(defaultSelectionEnd);
|
|
2209
2376
|
const setSelection = (start, end) => {
|
|
2210
2377
|
setSelectionStart(start);
|
|
2211
2378
|
setSelectionEnd(end);
|
|
2212
2379
|
};
|
|
2213
|
-
return /* @__PURE__ */ (0,
|
|
2380
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PlayoutStatusUpdateContext.Provider, { value: { setIsPlaying, setProgress, setSelection }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PlayoutStatusContext.Provider, { value: { isPlaying, progress, selectionStart, selectionEnd }, children }) });
|
|
2214
2381
|
};
|
|
2215
|
-
var usePlayoutStatus = () => (0,
|
|
2216
|
-
var usePlayoutStatusUpdate = () => (0,
|
|
2382
|
+
var usePlayoutStatus = () => (0, import_react18.useContext)(PlayoutStatusContext);
|
|
2383
|
+
var usePlayoutStatusUpdate = () => (0, import_react18.useContext)(PlayoutStatusUpdateContext);
|
|
2217
2384
|
|
|
2218
2385
|
// src/components/SpectrogramChannel.tsx
|
|
2219
|
-
var
|
|
2220
|
-
var
|
|
2221
|
-
var
|
|
2222
|
-
var
|
|
2386
|
+
var import_react19 = require("react");
|
|
2387
|
+
var import_styled_components21 = __toESM(require("styled-components"));
|
|
2388
|
+
var import_core3 = require("@waveform-playlist/core");
|
|
2389
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
2223
2390
|
var LINEAR_FREQUENCY_SCALE = (f, minF, maxF) => (f - minF) / (maxF - minF);
|
|
2224
|
-
var
|
|
2391
|
+
var Wrapper4 = import_styled_components21.default.div.attrs((props) => ({
|
|
2225
2392
|
style: {
|
|
2226
2393
|
top: `${props.$waveHeight * props.$index}px`,
|
|
2227
2394
|
width: `${props.$cssWidth}px`,
|
|
@@ -2233,7 +2400,7 @@ var Wrapper3 = import_styled_components20.default.div.attrs((props) => ({
|
|
|
2233
2400
|
transform: translateZ(0);
|
|
2234
2401
|
backface-visibility: hidden;
|
|
2235
2402
|
`;
|
|
2236
|
-
var SpectrogramCanvas =
|
|
2403
|
+
var SpectrogramCanvas = import_styled_components21.default.canvas.attrs((props) => ({
|
|
2237
2404
|
style: {
|
|
2238
2405
|
width: `${props.$cssWidth}px`,
|
|
2239
2406
|
height: `${props.$waveHeight}px`,
|
|
@@ -2242,8 +2409,6 @@ var SpectrogramCanvas = import_styled_components20.default.canvas.attrs((props)
|
|
|
2242
2409
|
}))`
|
|
2243
2410
|
position: absolute;
|
|
2244
2411
|
top: 0;
|
|
2245
|
-
/* Promote to own compositing layer for smoother scrolling */
|
|
2246
|
-
will-change: transform;
|
|
2247
2412
|
image-rendering: pixelated;
|
|
2248
2413
|
image-rendering: crisp-edges;
|
|
2249
2414
|
`;
|
|
@@ -2273,24 +2438,24 @@ var SpectrogramChannel = ({
|
|
|
2273
2438
|
}) => {
|
|
2274
2439
|
const channelIndex = channelIndexProp ?? index;
|
|
2275
2440
|
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
2276
|
-
const registeredIdsRef = (0,
|
|
2277
|
-
const transferredCanvasesRef = (0,
|
|
2278
|
-
const workerApiRef = (0,
|
|
2279
|
-
const onCanvasesReadyRef = (0,
|
|
2441
|
+
const registeredIdsRef = (0, import_react19.useRef)([]);
|
|
2442
|
+
const transferredCanvasesRef = (0, import_react19.useRef)(/* @__PURE__ */ new WeakSet());
|
|
2443
|
+
const workerApiRef = (0, import_react19.useRef)(workerApi);
|
|
2444
|
+
const onCanvasesReadyRef = (0, import_react19.useRef)(onCanvasesReady);
|
|
2280
2445
|
const isWorkerMode = !!(workerApi && clipId);
|
|
2281
2446
|
const clipOriginX = useClipViewportOrigin();
|
|
2282
|
-
const visibleChunkIndices = useVisibleChunkIndices(length,
|
|
2447
|
+
const visibleChunkIndices = useVisibleChunkIndices(length, import_core3.MAX_CANVAS_WIDTH, clipOriginX);
|
|
2283
2448
|
const lut = colorLUT ?? DEFAULT_COLOR_LUT;
|
|
2284
2449
|
const maxF = maxFrequency ?? (data ? data.sampleRate / 2 : 22050);
|
|
2285
2450
|
const scaleFn = frequencyScaleFn ?? LINEAR_FREQUENCY_SCALE;
|
|
2286
2451
|
const hasCustomFrequencyScale = Boolean(frequencyScaleFn);
|
|
2287
|
-
(0,
|
|
2452
|
+
(0, import_react19.useEffect)(() => {
|
|
2288
2453
|
workerApiRef.current = workerApi;
|
|
2289
2454
|
}, [workerApi]);
|
|
2290
|
-
(0,
|
|
2455
|
+
(0, import_react19.useEffect)(() => {
|
|
2291
2456
|
onCanvasesReadyRef.current = onCanvasesReady;
|
|
2292
2457
|
}, [onCanvasesReady]);
|
|
2293
|
-
(0,
|
|
2458
|
+
(0, import_react19.useEffect)(() => {
|
|
2294
2459
|
if (!isWorkerMode) return;
|
|
2295
2460
|
const currentWorkerApi = workerApiRef.current;
|
|
2296
2461
|
if (!currentWorkerApi || !clipId) return;
|
|
@@ -2345,15 +2510,15 @@ var SpectrogramChannel = ({
|
|
|
2345
2510
|
const match = id.match(/chunk(\d+)$/);
|
|
2346
2511
|
if (!match) {
|
|
2347
2512
|
console.warn(`[spectrogram] Unexpected canvas ID format: ${id}`);
|
|
2348
|
-
return
|
|
2513
|
+
return import_core3.MAX_CANVAS_WIDTH;
|
|
2349
2514
|
}
|
|
2350
2515
|
const chunkIdx = parseInt(match[1], 10);
|
|
2351
|
-
return Math.min(length - chunkIdx *
|
|
2516
|
+
return Math.min(length - chunkIdx * import_core3.MAX_CANVAS_WIDTH, import_core3.MAX_CANVAS_WIDTH);
|
|
2352
2517
|
});
|
|
2353
2518
|
onCanvasesReadyRef.current?.(allIds, allWidths);
|
|
2354
2519
|
}
|
|
2355
2520
|
}, [canvasMapRef, isWorkerMode, clipId, channelIndex, length, visibleChunkIndices]);
|
|
2356
|
-
(0,
|
|
2521
|
+
(0, import_react19.useEffect)(() => {
|
|
2357
2522
|
return () => {
|
|
2358
2523
|
const api = workerApiRef.current;
|
|
2359
2524
|
if (!api) return;
|
|
@@ -2367,7 +2532,7 @@ var SpectrogramChannel = ({
|
|
|
2367
2532
|
registeredIdsRef.current = [];
|
|
2368
2533
|
};
|
|
2369
2534
|
}, []);
|
|
2370
|
-
(0,
|
|
2535
|
+
(0, import_react19.useEffect)(() => {
|
|
2371
2536
|
if (isWorkerMode || !data) return;
|
|
2372
2537
|
const {
|
|
2373
2538
|
frequencyBinCount,
|
|
@@ -2380,7 +2545,7 @@ var SpectrogramChannel = ({
|
|
|
2380
2545
|
const rangeDb = rawRangeDb === 0 ? 1 : rawRangeDb;
|
|
2381
2546
|
const binToFreq = (bin) => bin / frequencyBinCount * (sampleRate / 2);
|
|
2382
2547
|
for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
|
|
2383
|
-
const globalPixelOffset = canvasIdx *
|
|
2548
|
+
const globalPixelOffset = canvasIdx * import_core3.MAX_CANVAS_WIDTH;
|
|
2384
2549
|
const ctx = canvas.getContext("2d");
|
|
2385
2550
|
if (!ctx) continue;
|
|
2386
2551
|
const canvasWidth = canvas.width / devicePixelRatio;
|
|
@@ -2456,9 +2621,9 @@ var SpectrogramChannel = ({
|
|
|
2456
2621
|
visibleChunkIndices
|
|
2457
2622
|
]);
|
|
2458
2623
|
const canvases = visibleChunkIndices.map((i) => {
|
|
2459
|
-
const chunkLeft = i *
|
|
2460
|
-
const currentWidth = Math.min(length - chunkLeft,
|
|
2461
|
-
return /* @__PURE__ */ (0,
|
|
2624
|
+
const chunkLeft = i * import_core3.MAX_CANVAS_WIDTH;
|
|
2625
|
+
const currentWidth = Math.min(length - chunkLeft, import_core3.MAX_CANVAS_WIDTH);
|
|
2626
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2462
2627
|
SpectrogramCanvas,
|
|
2463
2628
|
{
|
|
2464
2629
|
$cssWidth: currentWidth,
|
|
@@ -2472,11 +2637,11 @@ var SpectrogramChannel = ({
|
|
|
2472
2637
|
`${length}-${i}`
|
|
2473
2638
|
);
|
|
2474
2639
|
});
|
|
2475
|
-
return /* @__PURE__ */ (0,
|
|
2640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Wrapper4, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });
|
|
2476
2641
|
};
|
|
2477
2642
|
|
|
2478
2643
|
// src/components/SmartChannel.tsx
|
|
2479
|
-
var
|
|
2644
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
2480
2645
|
var SmartChannel = ({
|
|
2481
2646
|
isSelected,
|
|
2482
2647
|
transparentBackground,
|
|
@@ -2490,10 +2655,19 @@ var SmartChannel = ({
|
|
|
2490
2655
|
spectrogramWorkerApi,
|
|
2491
2656
|
spectrogramClipId,
|
|
2492
2657
|
spectrogramOnCanvasesReady,
|
|
2658
|
+
midiNotes,
|
|
2659
|
+
sampleRate: sampleRateProp,
|
|
2660
|
+
clipOffsetSeconds,
|
|
2493
2661
|
...props
|
|
2494
2662
|
}) => {
|
|
2495
2663
|
const theme = useTheme2();
|
|
2496
|
-
const {
|
|
2664
|
+
const {
|
|
2665
|
+
waveHeight,
|
|
2666
|
+
barWidth,
|
|
2667
|
+
barGap,
|
|
2668
|
+
samplesPerPixel: contextSpp,
|
|
2669
|
+
sampleRate: contextSampleRate
|
|
2670
|
+
} = usePlaylistInfo();
|
|
2497
2671
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2498
2672
|
const samplesPerPixel = sppProp ?? contextSpp;
|
|
2499
2673
|
const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;
|
|
@@ -2501,7 +2675,7 @@ var SmartChannel = ({
|
|
|
2501
2675
|
const drawMode = theme?.waveformDrawMode || "inverted";
|
|
2502
2676
|
const hasSpectrogram = spectrogramData || spectrogramWorkerApi;
|
|
2503
2677
|
if (renderMode === "spectrogram" && hasSpectrogram) {
|
|
2504
|
-
return /* @__PURE__ */ (0,
|
|
2678
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2505
2679
|
SpectrogramChannel,
|
|
2506
2680
|
{
|
|
2507
2681
|
index: props.index,
|
|
@@ -2522,8 +2696,8 @@ var SmartChannel = ({
|
|
|
2522
2696
|
}
|
|
2523
2697
|
if (renderMode === "both" && hasSpectrogram) {
|
|
2524
2698
|
const halfHeight = Math.floor(waveHeight / 2);
|
|
2525
|
-
return /* @__PURE__ */ (0,
|
|
2526
|
-
/* @__PURE__ */ (0,
|
|
2699
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
2700
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2527
2701
|
SpectrogramChannel,
|
|
2528
2702
|
{
|
|
2529
2703
|
index: props.index * 2,
|
|
@@ -2542,7 +2716,7 @@ var SmartChannel = ({
|
|
|
2542
2716
|
onCanvasesReady: spectrogramOnCanvasesReady
|
|
2543
2717
|
}
|
|
2544
2718
|
),
|
|
2545
|
-
/* @__PURE__ */ (0,
|
|
2719
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2546
2720
|
"div",
|
|
2547
2721
|
{
|
|
2548
2722
|
style: {
|
|
@@ -2551,7 +2725,7 @@ var SmartChannel = ({
|
|
|
2551
2725
|
width: props.length,
|
|
2552
2726
|
height: halfHeight
|
|
2553
2727
|
},
|
|
2554
|
-
children: /* @__PURE__ */ (0,
|
|
2728
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2555
2729
|
Channel,
|
|
2556
2730
|
{
|
|
2557
2731
|
...props,
|
|
@@ -2570,7 +2744,27 @@ var SmartChannel = ({
|
|
|
2570
2744
|
)
|
|
2571
2745
|
] });
|
|
2572
2746
|
}
|
|
2573
|
-
|
|
2747
|
+
if (renderMode === "piano-roll") {
|
|
2748
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2749
|
+
PianoRollChannel,
|
|
2750
|
+
{
|
|
2751
|
+
index: props.index,
|
|
2752
|
+
midiNotes: midiNotes ?? [],
|
|
2753
|
+
length: props.length,
|
|
2754
|
+
waveHeight,
|
|
2755
|
+
devicePixelRatio,
|
|
2756
|
+
samplesPerPixel,
|
|
2757
|
+
sampleRate: sampleRateProp ?? contextSampleRate,
|
|
2758
|
+
clipOffsetSeconds: clipOffsetSeconds ?? 0,
|
|
2759
|
+
noteColor: theme?.pianoRollNoteColor,
|
|
2760
|
+
selectedNoteColor: theme?.pianoRollSelectedNoteColor,
|
|
2761
|
+
isSelected,
|
|
2762
|
+
transparentBackground,
|
|
2763
|
+
backgroundColor: theme?.pianoRollBackgroundColor
|
|
2764
|
+
}
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2767
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2574
2768
|
Channel,
|
|
2575
2769
|
{
|
|
2576
2770
|
...props,
|
|
@@ -2587,11 +2781,11 @@ var SmartChannel = ({
|
|
|
2587
2781
|
};
|
|
2588
2782
|
|
|
2589
2783
|
// src/components/SpectrogramLabels.tsx
|
|
2590
|
-
var
|
|
2591
|
-
var
|
|
2592
|
-
var
|
|
2784
|
+
var import_react20 = require("react");
|
|
2785
|
+
var import_styled_components22 = __toESM(require("styled-components"));
|
|
2786
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2593
2787
|
var LABELS_WIDTH = 72;
|
|
2594
|
-
var LabelsStickyWrapper =
|
|
2788
|
+
var LabelsStickyWrapper = import_styled_components22.default.div`
|
|
2595
2789
|
position: sticky;
|
|
2596
2790
|
left: 0;
|
|
2597
2791
|
z-index: 101;
|
|
@@ -2639,12 +2833,12 @@ var SpectrogramLabels = ({
|
|
|
2639
2833
|
renderMode = "spectrogram",
|
|
2640
2834
|
hasClipHeaders = false
|
|
2641
2835
|
}) => {
|
|
2642
|
-
const canvasRef = (0,
|
|
2836
|
+
const canvasRef = (0, import_react20.useRef)(null);
|
|
2643
2837
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2644
2838
|
const spectrogramHeight = renderMode === "both" ? Math.floor(waveHeight / 2) : waveHeight;
|
|
2645
2839
|
const totalHeight = numChannels * waveHeight;
|
|
2646
2840
|
const clipHeaderOffset = hasClipHeaders ? 22 : 0;
|
|
2647
|
-
(0,
|
|
2841
|
+
(0, import_react20.useLayoutEffect)(() => {
|
|
2648
2842
|
const canvas = canvasRef.current;
|
|
2649
2843
|
if (!canvas) return;
|
|
2650
2844
|
const ctx = canvas.getContext("2d");
|
|
@@ -2682,7 +2876,7 @@ var SpectrogramLabels = ({
|
|
|
2682
2876
|
spectrogramHeight,
|
|
2683
2877
|
clipHeaderOffset
|
|
2684
2878
|
]);
|
|
2685
|
-
return /* @__PURE__ */ (0,
|
|
2879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2686
2880
|
"canvas",
|
|
2687
2881
|
{
|
|
2688
2882
|
ref: canvasRef,
|
|
@@ -2698,11 +2892,11 @@ var SpectrogramLabels = ({
|
|
|
2698
2892
|
};
|
|
2699
2893
|
|
|
2700
2894
|
// src/components/SmartScale.tsx
|
|
2701
|
-
var
|
|
2895
|
+
var import_react22 = require("react");
|
|
2702
2896
|
|
|
2703
2897
|
// src/components/TimeScale.tsx
|
|
2704
|
-
var
|
|
2705
|
-
var
|
|
2898
|
+
var import_react21 = __toESM(require("react"));
|
|
2899
|
+
var import_styled_components23 = __toESM(require("styled-components"));
|
|
2706
2900
|
|
|
2707
2901
|
// src/utils/conversions.ts
|
|
2708
2902
|
function samplesToSeconds(samples, sampleRate) {
|
|
@@ -2725,18 +2919,17 @@ function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
|
|
|
2725
2919
|
}
|
|
2726
2920
|
|
|
2727
2921
|
// src/components/TimeScale.tsx
|
|
2728
|
-
var
|
|
2729
|
-
var
|
|
2922
|
+
var import_core4 = require("@waveform-playlist/core");
|
|
2923
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2730
2924
|
function formatTime2(milliseconds) {
|
|
2731
2925
|
const seconds = Math.floor(milliseconds / 1e3);
|
|
2732
2926
|
const s = seconds % 60;
|
|
2733
2927
|
const m = (seconds - s) / 60;
|
|
2734
2928
|
return `${m}:${String(s).padStart(2, "0")}`;
|
|
2735
2929
|
}
|
|
2736
|
-
var PlaylistTimeScaleScroll =
|
|
2930
|
+
var PlaylistTimeScaleScroll = import_styled_components23.default.div.attrs((props) => ({
|
|
2737
2931
|
style: {
|
|
2738
2932
|
width: `${props.$cssWidth}px`,
|
|
2739
|
-
marginLeft: `${props.$controlWidth}px`,
|
|
2740
2933
|
height: `${props.$timeScaleHeight}px`
|
|
2741
2934
|
}
|
|
2742
2935
|
}))`
|
|
@@ -2745,7 +2938,7 @@ var PlaylistTimeScaleScroll = import_styled_components22.default.div.attrs((prop
|
|
|
2745
2938
|
border-bottom: 1px solid ${(props) => props.theme.timeColor};
|
|
2746
2939
|
box-sizing: border-box;
|
|
2747
2940
|
`;
|
|
2748
|
-
var TimeTickChunk =
|
|
2941
|
+
var TimeTickChunk = import_styled_components23.default.canvas.attrs((props) => ({
|
|
2749
2942
|
style: {
|
|
2750
2943
|
width: `${props.$cssWidth}px`,
|
|
2751
2944
|
height: `${props.$timeScaleHeight}px`,
|
|
@@ -2754,10 +2947,8 @@ var TimeTickChunk = import_styled_components22.default.canvas.attrs((props) => (
|
|
|
2754
2947
|
}))`
|
|
2755
2948
|
position: absolute;
|
|
2756
2949
|
bottom: 0;
|
|
2757
|
-
/* Promote to own compositing layer for smoother scrolling */
|
|
2758
|
-
will-change: transform;
|
|
2759
2950
|
`;
|
|
2760
|
-
var TimeStamp =
|
|
2951
|
+
var TimeStamp = import_styled_components23.default.div.attrs((props) => ({
|
|
2761
2952
|
style: {
|
|
2762
2953
|
left: `${props.$left + 4}px`
|
|
2763
2954
|
// Offset 4px to the right of the tick
|
|
@@ -2778,14 +2969,9 @@ var TimeScale = (props) => {
|
|
|
2778
2969
|
renderTimestamp
|
|
2779
2970
|
} = props;
|
|
2780
2971
|
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
2781
|
-
const {
|
|
2782
|
-
sampleRate,
|
|
2783
|
-
samplesPerPixel,
|
|
2784
|
-
timeScaleHeight,
|
|
2785
|
-
controls: { show: showControls, width: controlWidth }
|
|
2786
|
-
} = (0, import_react20.useContext)(PlaylistInfoContext);
|
|
2972
|
+
const { sampleRate, samplesPerPixel, timeScaleHeight } = (0, import_react21.useContext)(PlaylistInfoContext);
|
|
2787
2973
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2788
|
-
const { widthX, canvasInfo, timeMarkersWithPositions } = (0,
|
|
2974
|
+
const { widthX, canvasInfo, timeMarkersWithPositions } = (0, import_react21.useMemo)(() => {
|
|
2789
2975
|
const nextCanvasInfo = /* @__PURE__ */ new Map();
|
|
2790
2976
|
const nextMarkers = [];
|
|
2791
2977
|
const nextWidthX = secondsToPixels(duration / 1e3, samplesPerPixel, sampleRate);
|
|
@@ -2796,7 +2982,7 @@ var TimeScale = (props) => {
|
|
|
2796
2982
|
if (counter % marker === 0) {
|
|
2797
2983
|
const timeMs = counter;
|
|
2798
2984
|
const timestamp = formatTime2(timeMs);
|
|
2799
|
-
const element = renderTimestamp ? /* @__PURE__ */ (0,
|
|
2985
|
+
const element = renderTimestamp ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react21.default.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TimeStamp, { $left: pix, children: timestamp }, timestamp);
|
|
2800
2986
|
nextMarkers.push({ pix, element });
|
|
2801
2987
|
nextCanvasInfo.set(pix, timeScaleHeight);
|
|
2802
2988
|
} else if (counter % bigStep === 0) {
|
|
@@ -2821,11 +3007,11 @@ var TimeScale = (props) => {
|
|
|
2821
3007
|
renderTimestamp,
|
|
2822
3008
|
timeScaleHeight
|
|
2823
3009
|
]);
|
|
2824
|
-
const visibleChunkIndices = useVisibleChunkIndices(widthX,
|
|
3010
|
+
const visibleChunkIndices = useVisibleChunkIndices(widthX, import_core4.MAX_CANVAS_WIDTH);
|
|
2825
3011
|
const visibleChunks = visibleChunkIndices.map((i) => {
|
|
2826
|
-
const chunkLeft = i *
|
|
2827
|
-
const chunkWidth = Math.min(widthX - chunkLeft,
|
|
2828
|
-
return /* @__PURE__ */ (0,
|
|
3012
|
+
const chunkLeft = i * import_core4.MAX_CANVAS_WIDTH;
|
|
3013
|
+
const chunkWidth = Math.min(widthX - chunkLeft, import_core4.MAX_CANVAS_WIDTH);
|
|
3014
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2829
3015
|
TimeTickChunk,
|
|
2830
3016
|
{
|
|
2831
3017
|
$cssWidth: chunkWidth,
|
|
@@ -2839,14 +3025,14 @@ var TimeScale = (props) => {
|
|
|
2839
3025
|
`timescale-${i}`
|
|
2840
3026
|
);
|
|
2841
3027
|
});
|
|
2842
|
-
const firstChunkLeft = visibleChunkIndices.length > 0 ? visibleChunkIndices[0] *
|
|
2843
|
-
const lastChunkRight = visibleChunkIndices.length > 0 ? (visibleChunkIndices[visibleChunkIndices.length - 1] + 1) *
|
|
3028
|
+
const firstChunkLeft = visibleChunkIndices.length > 0 ? visibleChunkIndices[0] * import_core4.MAX_CANVAS_WIDTH : 0;
|
|
3029
|
+
const lastChunkRight = visibleChunkIndices.length > 0 ? (visibleChunkIndices[visibleChunkIndices.length - 1] + 1) * import_core4.MAX_CANVAS_WIDTH : Infinity;
|
|
2844
3030
|
const visibleMarkers = visibleChunkIndices.length > 0 ? timeMarkersWithPositions.filter(({ pix }) => pix >= firstChunkLeft && pix < lastChunkRight).map(({ element }) => element) : timeMarkersWithPositions.map(({ element }) => element);
|
|
2845
|
-
(0,
|
|
3031
|
+
(0, import_react21.useLayoutEffect)(() => {
|
|
2846
3032
|
for (const [chunkIdx, canvas] of canvasMapRef.current.entries()) {
|
|
2847
3033
|
const ctx = canvas.getContext("2d");
|
|
2848
3034
|
if (!ctx) continue;
|
|
2849
|
-
const chunkLeft = chunkIdx *
|
|
3035
|
+
const chunkLeft = chunkIdx * import_core4.MAX_CANVAS_WIDTH;
|
|
2850
3036
|
const chunkWidth = canvas.width / devicePixelRatio;
|
|
2851
3037
|
ctx.resetTransform();
|
|
2852
3038
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
@@ -2869,23 +3055,15 @@ var TimeScale = (props) => {
|
|
|
2869
3055
|
canvasInfo,
|
|
2870
3056
|
visibleChunkIndices
|
|
2871
3057
|
]);
|
|
2872
|
-
return /* @__PURE__ */ (0,
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
$controlWidth: showControls ? controlWidth : 0,
|
|
2877
|
-
$timeScaleHeight: timeScaleHeight,
|
|
2878
|
-
children: [
|
|
2879
|
-
visibleMarkers,
|
|
2880
|
-
visibleChunks
|
|
2881
|
-
]
|
|
2882
|
-
}
|
|
2883
|
-
);
|
|
3058
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(PlaylistTimeScaleScroll, { $cssWidth: widthX, $timeScaleHeight: timeScaleHeight, children: [
|
|
3059
|
+
visibleMarkers,
|
|
3060
|
+
visibleChunks
|
|
3061
|
+
] });
|
|
2884
3062
|
};
|
|
2885
|
-
var StyledTimeScale = (0,
|
|
3063
|
+
var StyledTimeScale = (0, import_styled_components23.withTheme)(TimeScale);
|
|
2886
3064
|
|
|
2887
3065
|
// src/components/SmartScale.tsx
|
|
2888
|
-
var
|
|
3066
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
2889
3067
|
var timeinfo = /* @__PURE__ */ new Map([
|
|
2890
3068
|
[
|
|
2891
3069
|
700,
|
|
@@ -2959,9 +3137,9 @@ function getScaleInfo(samplesPerPixel) {
|
|
|
2959
3137
|
return config;
|
|
2960
3138
|
}
|
|
2961
3139
|
var SmartScale = ({ renderTimestamp }) => {
|
|
2962
|
-
const { samplesPerPixel, duration } = (0,
|
|
3140
|
+
const { samplesPerPixel, duration } = (0, import_react22.useContext)(PlaylistInfoContext);
|
|
2963
3141
|
let config = getScaleInfo(samplesPerPixel);
|
|
2964
|
-
return /* @__PURE__ */ (0,
|
|
3142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2965
3143
|
StyledTimeScale,
|
|
2966
3144
|
{
|
|
2967
3145
|
marker: config.marker,
|
|
@@ -2974,9 +3152,9 @@ var SmartScale = ({ renderTimestamp }) => {
|
|
|
2974
3152
|
};
|
|
2975
3153
|
|
|
2976
3154
|
// src/components/TimeFormatSelect.tsx
|
|
2977
|
-
var
|
|
2978
|
-
var
|
|
2979
|
-
var SelectWrapper =
|
|
3155
|
+
var import_styled_components24 = __toESM(require("styled-components"));
|
|
3156
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
3157
|
+
var SelectWrapper = import_styled_components24.default.div`
|
|
2980
3158
|
display: inline-flex;
|
|
2981
3159
|
align-items: center;
|
|
2982
3160
|
gap: 0.5rem;
|
|
@@ -2998,7 +3176,7 @@ var TimeFormatSelect = ({
|
|
|
2998
3176
|
const handleChange = (e) => {
|
|
2999
3177
|
onChange(e.target.value);
|
|
3000
3178
|
};
|
|
3001
|
-
return /* @__PURE__ */ (0,
|
|
3179
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SelectWrapper, { className, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
3002
3180
|
BaseSelect,
|
|
3003
3181
|
{
|
|
3004
3182
|
className: "time-format",
|
|
@@ -3006,50 +3184,30 @@ var TimeFormatSelect = ({
|
|
|
3006
3184
|
onChange: handleChange,
|
|
3007
3185
|
disabled,
|
|
3008
3186
|
"aria-label": "Time format selection",
|
|
3009
|
-
children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ (0,
|
|
3187
|
+
children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: option.value, children: option.label }, option.value))
|
|
3010
3188
|
}
|
|
3011
3189
|
) });
|
|
3012
3190
|
};
|
|
3013
3191
|
|
|
3014
3192
|
// src/components/Track.tsx
|
|
3015
|
-
var
|
|
3016
|
-
var
|
|
3017
|
-
var Container =
|
|
3193
|
+
var import_styled_components25 = __toESM(require("styled-components"));
|
|
3194
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
3195
|
+
var Container = import_styled_components25.default.div.attrs((props) => ({
|
|
3018
3196
|
style: {
|
|
3019
3197
|
height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`
|
|
3020
3198
|
}
|
|
3021
3199
|
}))`
|
|
3022
3200
|
position: relative;
|
|
3023
|
-
display: flex;
|
|
3024
3201
|
${(props) => props.$width !== void 0 && `width: ${props.$width}px;`}
|
|
3025
3202
|
`;
|
|
3026
|
-
var ChannelContainer =
|
|
3203
|
+
var ChannelContainer = import_styled_components25.default.div.attrs((props) => ({
|
|
3027
3204
|
style: {
|
|
3028
3205
|
paddingLeft: `${props.$offset || 0}px`
|
|
3029
3206
|
}
|
|
3030
3207
|
}))`
|
|
3031
3208
|
position: relative;
|
|
3032
3209
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
3033
|
-
flex: 1;
|
|
3034
|
-
`;
|
|
3035
|
-
var ControlsWrapper = import_styled_components24.default.div.attrs((props) => ({
|
|
3036
|
-
style: {
|
|
3037
|
-
width: `${props.$controlWidth}px`
|
|
3038
|
-
}
|
|
3039
|
-
}))`
|
|
3040
|
-
position: sticky;
|
|
3041
|
-
z-index: 102; /* Above waveform content and spectrogram labels (101), below Docusaurus navbar (200) */
|
|
3042
|
-
left: 0;
|
|
3043
3210
|
height: 100%;
|
|
3044
|
-
flex-shrink: 0;
|
|
3045
|
-
pointer-events: auto;
|
|
3046
|
-
background: ${(props) => props.theme.surfaceColor};
|
|
3047
|
-
transition: background 0.15s ease-in-out;
|
|
3048
|
-
|
|
3049
|
-
/* Selected track: highlighted background */
|
|
3050
|
-
${(props) => props.$isSelected && `
|
|
3051
|
-
background: ${props.theme.selectedTrackControlsBackground};
|
|
3052
|
-
`}
|
|
3053
3211
|
`;
|
|
3054
3212
|
var Track = ({
|
|
3055
3213
|
numChannels,
|
|
@@ -3061,44 +3219,34 @@ var Track = ({
|
|
|
3061
3219
|
hasClipHeaders = false,
|
|
3062
3220
|
onClick,
|
|
3063
3221
|
trackId,
|
|
3064
|
-
isSelected = false
|
|
3222
|
+
isSelected: _isSelected = false
|
|
3065
3223
|
}) => {
|
|
3066
|
-
const {
|
|
3067
|
-
|
|
3068
|
-
controls: { show, width: controlWidth }
|
|
3069
|
-
} = usePlaylistInfo();
|
|
3070
|
-
const controls = useTrackControls();
|
|
3071
|
-
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
3224
|
+
const { waveHeight } = usePlaylistInfo();
|
|
3225
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3072
3226
|
Container,
|
|
3073
3227
|
{
|
|
3074
3228
|
$numChannels: numChannels,
|
|
3075
3229
|
className,
|
|
3076
3230
|
$waveHeight: waveHeight,
|
|
3077
|
-
$controlWidth: show ? controlWidth : 0,
|
|
3078
3231
|
$width: width,
|
|
3079
3232
|
$hasClipHeaders: hasClipHeaders,
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
"data-track-id": trackId,
|
|
3091
|
-
children
|
|
3092
|
-
}
|
|
3093
|
-
)
|
|
3094
|
-
]
|
|
3233
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3234
|
+
ChannelContainer,
|
|
3235
|
+
{
|
|
3236
|
+
$backgroundColor: backgroundColor,
|
|
3237
|
+
$offset: offset,
|
|
3238
|
+
onClick,
|
|
3239
|
+
"data-track-id": trackId,
|
|
3240
|
+
children
|
|
3241
|
+
}
|
|
3242
|
+
)
|
|
3095
3243
|
}
|
|
3096
3244
|
);
|
|
3097
3245
|
};
|
|
3098
3246
|
|
|
3099
3247
|
// src/components/TrackControls/Button.tsx
|
|
3100
|
-
var
|
|
3101
|
-
var Button =
|
|
3248
|
+
var import_styled_components26 = __toESM(require("styled-components"));
|
|
3249
|
+
var Button = import_styled_components26.default.button.attrs({
|
|
3102
3250
|
type: "button"
|
|
3103
3251
|
})`
|
|
3104
3252
|
display: inline-block;
|
|
@@ -3173,8 +3321,8 @@ var Button = import_styled_components25.default.button.attrs({
|
|
|
3173
3321
|
`;
|
|
3174
3322
|
|
|
3175
3323
|
// src/components/TrackControls/ButtonGroup.tsx
|
|
3176
|
-
var
|
|
3177
|
-
var ButtonGroup =
|
|
3324
|
+
var import_styled_components27 = __toESM(require("styled-components"));
|
|
3325
|
+
var ButtonGroup = import_styled_components27.default.div`
|
|
3178
3326
|
margin-bottom: 0.3rem;
|
|
3179
3327
|
|
|
3180
3328
|
button:not(:first-child) {
|
|
@@ -3189,10 +3337,10 @@ var ButtonGroup = import_styled_components26.default.div`
|
|
|
3189
3337
|
`;
|
|
3190
3338
|
|
|
3191
3339
|
// src/components/TrackControls/CloseButton.tsx
|
|
3192
|
-
var
|
|
3193
|
-
var
|
|
3194
|
-
var
|
|
3195
|
-
var StyledCloseButton =
|
|
3340
|
+
var import_styled_components28 = __toESM(require("styled-components"));
|
|
3341
|
+
var import_react23 = require("@phosphor-icons/react");
|
|
3342
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
3343
|
+
var StyledCloseButton = import_styled_components28.default.button`
|
|
3196
3344
|
position: absolute;
|
|
3197
3345
|
left: 0;
|
|
3198
3346
|
top: 0;
|
|
@@ -3215,11 +3363,11 @@ var StyledCloseButton = import_styled_components27.default.button`
|
|
|
3215
3363
|
color: #dc3545;
|
|
3216
3364
|
}
|
|
3217
3365
|
`;
|
|
3218
|
-
var CloseButton = ({ onClick, title = "Remove track" }) => /* @__PURE__ */ (0,
|
|
3366
|
+
var CloseButton = ({ onClick, title = "Remove track" }) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react23.X, { size: 12, weight: "bold" }) });
|
|
3219
3367
|
|
|
3220
3368
|
// src/components/TrackControls/Controls.tsx
|
|
3221
|
-
var
|
|
3222
|
-
var Controls =
|
|
3369
|
+
var import_styled_components29 = __toESM(require("styled-components"));
|
|
3370
|
+
var Controls = import_styled_components29.default.div`
|
|
3223
3371
|
background: transparent;
|
|
3224
3372
|
width: 100%;
|
|
3225
3373
|
height: 100%;
|
|
@@ -3235,8 +3383,8 @@ var Controls = import_styled_components28.default.div`
|
|
|
3235
3383
|
`;
|
|
3236
3384
|
|
|
3237
3385
|
// src/components/TrackControls/Header.tsx
|
|
3238
|
-
var
|
|
3239
|
-
var Header =
|
|
3386
|
+
var import_styled_components30 = __toESM(require("styled-components"));
|
|
3387
|
+
var Header = import_styled_components30.default.header`
|
|
3240
3388
|
overflow: hidden;
|
|
3241
3389
|
height: 26px;
|
|
3242
3390
|
width: 100%;
|
|
@@ -3250,28 +3398,28 @@ var Header = import_styled_components29.default.header`
|
|
|
3250
3398
|
`;
|
|
3251
3399
|
|
|
3252
3400
|
// src/components/TrackControls/VolumeDownIcon.tsx
|
|
3253
|
-
var import_react23 = require("@phosphor-icons/react");
|
|
3254
|
-
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
3255
|
-
var VolumeDownIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react23.SpeakerLowIcon, { weight: "light", ...props });
|
|
3256
|
-
|
|
3257
|
-
// src/components/TrackControls/VolumeUpIcon.tsx
|
|
3258
3401
|
var import_react24 = require("@phosphor-icons/react");
|
|
3259
3402
|
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
3260
|
-
var
|
|
3403
|
+
var VolumeDownIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react24.SpeakerLowIcon, { weight: "light", ...props });
|
|
3261
3404
|
|
|
3262
|
-
// src/components/TrackControls/
|
|
3405
|
+
// src/components/TrackControls/VolumeUpIcon.tsx
|
|
3263
3406
|
var import_react25 = require("@phosphor-icons/react");
|
|
3264
3407
|
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
3265
|
-
var
|
|
3408
|
+
var VolumeUpIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react25.SpeakerHighIcon, { weight: "light", ...props });
|
|
3266
3409
|
|
|
3267
|
-
// src/components/TrackControls/
|
|
3410
|
+
// src/components/TrackControls/TrashIcon.tsx
|
|
3268
3411
|
var import_react26 = require("@phosphor-icons/react");
|
|
3269
3412
|
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
3270
|
-
var
|
|
3413
|
+
var TrashIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react26.TrashIcon, { weight: "light", ...props });
|
|
3414
|
+
|
|
3415
|
+
// src/components/TrackControls/DotsIcon.tsx
|
|
3416
|
+
var import_react27 = require("@phosphor-icons/react");
|
|
3417
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
3418
|
+
var DotsIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react27.DotsThreeIcon, { weight: "bold", ...props });
|
|
3271
3419
|
|
|
3272
3420
|
// src/components/TrackControls/Slider.tsx
|
|
3273
|
-
var
|
|
3274
|
-
var Slider = (0,
|
|
3421
|
+
var import_styled_components31 = __toESM(require("styled-components"));
|
|
3422
|
+
var Slider = (0, import_styled_components31.default)(BaseSlider)`
|
|
3275
3423
|
width: 75%;
|
|
3276
3424
|
height: 5px;
|
|
3277
3425
|
background: ${(props) => props.theme.sliderTrackColor};
|
|
@@ -3323,8 +3471,8 @@ var Slider = (0, import_styled_components30.default)(BaseSlider)`
|
|
|
3323
3471
|
`;
|
|
3324
3472
|
|
|
3325
3473
|
// src/components/TrackControls/SliderWrapper.tsx
|
|
3326
|
-
var
|
|
3327
|
-
var SliderWrapper =
|
|
3474
|
+
var import_styled_components32 = __toESM(require("styled-components"));
|
|
3475
|
+
var SliderWrapper = import_styled_components32.default.label`
|
|
3328
3476
|
width: 100%;
|
|
3329
3477
|
display: flex;
|
|
3330
3478
|
justify-content: space-between;
|
|
@@ -3335,15 +3483,15 @@ var SliderWrapper = import_styled_components31.default.label`
|
|
|
3335
3483
|
`;
|
|
3336
3484
|
|
|
3337
3485
|
// src/components/TrackMenu.tsx
|
|
3338
|
-
var
|
|
3486
|
+
var import_react28 = __toESM(require("react"));
|
|
3339
3487
|
var import_react_dom = require("react-dom");
|
|
3340
|
-
var
|
|
3341
|
-
var
|
|
3342
|
-
var MenuContainer =
|
|
3488
|
+
var import_styled_components33 = __toESM(require("styled-components"));
|
|
3489
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
3490
|
+
var MenuContainer = import_styled_components33.default.div`
|
|
3343
3491
|
position: relative;
|
|
3344
3492
|
display: inline-block;
|
|
3345
3493
|
`;
|
|
3346
|
-
var MenuButton =
|
|
3494
|
+
var MenuButton = import_styled_components33.default.button`
|
|
3347
3495
|
background: none;
|
|
3348
3496
|
border: none;
|
|
3349
3497
|
cursor: pointer;
|
|
@@ -3358,7 +3506,8 @@ var MenuButton = import_styled_components32.default.button`
|
|
|
3358
3506
|
opacity: 1;
|
|
3359
3507
|
}
|
|
3360
3508
|
`;
|
|
3361
|
-
var
|
|
3509
|
+
var DROPDOWN_MIN_WIDTH = 180;
|
|
3510
|
+
var Dropdown = import_styled_components33.default.div`
|
|
3362
3511
|
position: fixed;
|
|
3363
3512
|
top: ${(p) => p.$top}px;
|
|
3364
3513
|
left: ${(p) => p.$left}px;
|
|
@@ -3368,31 +3517,53 @@ var Dropdown = import_styled_components32.default.div`
|
|
|
3368
3517
|
border: 1px solid rgba(128, 128, 128, 0.4);
|
|
3369
3518
|
border-radius: 6px;
|
|
3370
3519
|
padding: 0.5rem 0;
|
|
3371
|
-
min-width:
|
|
3520
|
+
min-width: ${DROPDOWN_MIN_WIDTH}px;
|
|
3372
3521
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
3373
3522
|
`;
|
|
3374
|
-
var Divider =
|
|
3523
|
+
var Divider = import_styled_components33.default.hr`
|
|
3375
3524
|
border: none;
|
|
3376
3525
|
border-top: 1px solid rgba(128, 128, 128, 0.3);
|
|
3377
3526
|
margin: 0.35rem 0;
|
|
3378
3527
|
`;
|
|
3379
3528
|
var TrackMenu = ({ items: itemsProp }) => {
|
|
3380
|
-
const [open, setOpen] = (0,
|
|
3381
|
-
const close = () => setOpen(false);
|
|
3529
|
+
const [open, setOpen] = (0, import_react28.useState)(false);
|
|
3530
|
+
const close = (0, import_react28.useCallback)(() => setOpen(false), []);
|
|
3382
3531
|
const items = typeof itemsProp === "function" ? itemsProp(close) : itemsProp;
|
|
3383
|
-
const [dropdownPos, setDropdownPos] = (0,
|
|
3384
|
-
const buttonRef = (0,
|
|
3385
|
-
const dropdownRef = (0,
|
|
3386
|
-
(0,
|
|
3387
|
-
if (
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3532
|
+
const [dropdownPos, setDropdownPos] = (0, import_react28.useState)({ top: 0, left: 0 });
|
|
3533
|
+
const buttonRef = (0, import_react28.useRef)(null);
|
|
3534
|
+
const dropdownRef = (0, import_react28.useRef)(null);
|
|
3535
|
+
const updatePosition = (0, import_react28.useCallback)(() => {
|
|
3536
|
+
if (!buttonRef.current) return;
|
|
3537
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
3538
|
+
const vw = window.innerWidth;
|
|
3539
|
+
const vh = window.innerHeight;
|
|
3540
|
+
const dropHeight = dropdownRef.current?.offsetHeight ?? 160;
|
|
3541
|
+
let left = rect.right + 4;
|
|
3542
|
+
if (left + DROPDOWN_MIN_WIDTH > vw) {
|
|
3543
|
+
left = rect.left - DROPDOWN_MIN_WIDTH - 4;
|
|
3393
3544
|
}
|
|
3394
|
-
|
|
3395
|
-
|
|
3545
|
+
left = Math.max(4, Math.min(left, vw - DROPDOWN_MIN_WIDTH - 4));
|
|
3546
|
+
let top = rect.top;
|
|
3547
|
+
if (top + dropHeight > vh - 4) {
|
|
3548
|
+
top = Math.max(4, rect.bottom - dropHeight);
|
|
3549
|
+
}
|
|
3550
|
+
setDropdownPos({ top, left });
|
|
3551
|
+
}, []);
|
|
3552
|
+
(0, import_react28.useEffect)(() => {
|
|
3553
|
+
if (!open) return;
|
|
3554
|
+
updatePosition();
|
|
3555
|
+
const rafId = requestAnimationFrame(() => updatePosition());
|
|
3556
|
+
const onScroll = () => updatePosition();
|
|
3557
|
+
const onResize = () => updatePosition();
|
|
3558
|
+
window.addEventListener("scroll", onScroll, true);
|
|
3559
|
+
window.addEventListener("resize", onResize);
|
|
3560
|
+
return () => {
|
|
3561
|
+
cancelAnimationFrame(rafId);
|
|
3562
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
3563
|
+
window.removeEventListener("resize", onResize);
|
|
3564
|
+
};
|
|
3565
|
+
}, [open, updatePosition]);
|
|
3566
|
+
(0, import_react28.useEffect)(() => {
|
|
3396
3567
|
if (!open) return;
|
|
3397
3568
|
const handleClick = (e) => {
|
|
3398
3569
|
const target = e.target;
|
|
@@ -3400,11 +3571,20 @@ var TrackMenu = ({ items: itemsProp }) => {
|
|
|
3400
3571
|
setOpen(false);
|
|
3401
3572
|
}
|
|
3402
3573
|
};
|
|
3574
|
+
const handleKeyDown = (e) => {
|
|
3575
|
+
if (e.key === "Escape") {
|
|
3576
|
+
setOpen(false);
|
|
3577
|
+
}
|
|
3578
|
+
};
|
|
3403
3579
|
document.addEventListener("mousedown", handleClick);
|
|
3404
|
-
|
|
3580
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
3581
|
+
return () => {
|
|
3582
|
+
document.removeEventListener("mousedown", handleClick);
|
|
3583
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
3584
|
+
};
|
|
3405
3585
|
}, [open]);
|
|
3406
|
-
return /* @__PURE__ */ (0,
|
|
3407
|
-
/* @__PURE__ */ (0,
|
|
3586
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(MenuContainer, { children: [
|
|
3587
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3408
3588
|
MenuButton,
|
|
3409
3589
|
{
|
|
3410
3590
|
ref: buttonRef,
|
|
@@ -3415,19 +3595,19 @@ var TrackMenu = ({ items: itemsProp }) => {
|
|
|
3415
3595
|
onMouseDown: (e) => e.stopPropagation(),
|
|
3416
3596
|
title: "Track menu",
|
|
3417
3597
|
"aria-label": "Track menu",
|
|
3418
|
-
children: /* @__PURE__ */ (0,
|
|
3598
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(DotsIcon, { size: 16 })
|
|
3419
3599
|
}
|
|
3420
3600
|
),
|
|
3421
3601
|
open && typeof document !== "undefined" && (0, import_react_dom.createPortal)(
|
|
3422
|
-
/* @__PURE__ */ (0,
|
|
3602
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3423
3603
|
Dropdown,
|
|
3424
3604
|
{
|
|
3425
3605
|
ref: dropdownRef,
|
|
3426
3606
|
$top: dropdownPos.top,
|
|
3427
3607
|
$left: dropdownPos.left,
|
|
3428
3608
|
onMouseDown: (e) => e.stopPropagation(),
|
|
3429
|
-
children: items.map((item, index) => /* @__PURE__ */ (0,
|
|
3430
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
3609
|
+
children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react28.default.Fragment, { children: [
|
|
3610
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Divider, {}),
|
|
3431
3611
|
item.content
|
|
3432
3612
|
] }, item.id))
|
|
3433
3613
|
}
|
|
@@ -3470,6 +3650,7 @@ var TrackMenu = ({ items: itemsProp }) => {
|
|
|
3470
3650
|
LoopRegion,
|
|
3471
3651
|
LoopRegionMarkers,
|
|
3472
3652
|
MasterVolumeControl,
|
|
3653
|
+
PianoRollChannel,
|
|
3473
3654
|
Playhead,
|
|
3474
3655
|
PlayheadWithMarker,
|
|
3475
3656
|
Playlist,
|