@waveform-playlist/ui-components 9.0.3 → 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 -329
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +473 -272
- 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,10 +698,12 @@ 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
708
|
const resizeObserver = new ResizeObserver(() => {
|
|
674
709
|
scheduleUpdate();
|
|
@@ -681,8 +716,9 @@ var ScrollViewportProvider = ({ containerRef, children }) => {
|
|
|
681
716
|
cancelAnimationFrame(rafIdRef.current);
|
|
682
717
|
rafIdRef.current = null;
|
|
683
718
|
}
|
|
719
|
+
store.cancelPendingNotification();
|
|
684
720
|
};
|
|
685
|
-
}, [containerRef,
|
|
721
|
+
}, [containerRef, scheduleUpdate, store]);
|
|
686
722
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ViewportStoreContext.Provider, { value: store, children });
|
|
687
723
|
};
|
|
688
724
|
var useScrollViewport = () => {
|
|
@@ -817,8 +853,6 @@ var Waveform = import_styled_components9.default.canvas.attrs((props) => ({
|
|
|
817
853
|
}))`
|
|
818
854
|
position: absolute;
|
|
819
855
|
top: 0;
|
|
820
|
-
/* Promote to own compositing layer for smoother scrolling */
|
|
821
|
-
will-change: transform;
|
|
822
856
|
/* Disable image rendering interpolation */
|
|
823
857
|
image-rendering: pixelated;
|
|
824
858
|
image-rendering: crisp-edges;
|
|
@@ -855,7 +889,8 @@ var Channel = (props) => {
|
|
|
855
889
|
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
856
890
|
const clipOriginX = useClipViewportOrigin();
|
|
857
891
|
const visibleChunkIndices = useVisibleChunkIndices(length, import_core.MAX_CANVAS_WIDTH, clipOriginX);
|
|
858
|
-
(0, import_react4.
|
|
892
|
+
(0, import_react4.useEffect)(() => {
|
|
893
|
+
const tDraw = performance.now();
|
|
859
894
|
const step = barWidth + barGap;
|
|
860
895
|
for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
|
|
861
896
|
const globalPixelOffset = canvasIdx * import_core.MAX_CANVAS_WIDTH;
|
|
@@ -891,6 +926,9 @@ var Channel = (props) => {
|
|
|
891
926
|
}
|
|
892
927
|
}
|
|
893
928
|
}
|
|
929
|
+
console.log(
|
|
930
|
+
`[waveform] draw ch${index}: ${canvasMapRef.current.size} chunks, ${(performance.now() - tDraw).toFixed(1)}ms`
|
|
931
|
+
);
|
|
894
932
|
}, [
|
|
895
933
|
canvasMapRef,
|
|
896
934
|
data,
|
|
@@ -903,7 +941,8 @@ var Channel = (props) => {
|
|
|
903
941
|
barWidth,
|
|
904
942
|
barGap,
|
|
905
943
|
drawMode,
|
|
906
|
-
visibleChunkIndices
|
|
944
|
+
visibleChunkIndices,
|
|
945
|
+
index
|
|
907
946
|
]);
|
|
908
947
|
const waveforms = visibleChunkIndices.map((i) => {
|
|
909
948
|
const chunkLeft = i * import_core.MAX_CANVAS_WIDTH;
|
|
@@ -1021,7 +1060,7 @@ var ClipHeaderPresentational = ({
|
|
|
1021
1060
|
trackName,
|
|
1022
1061
|
isSelected = false
|
|
1023
1062
|
}) => {
|
|
1024
|
-
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 }) });
|
|
1025
1064
|
};
|
|
1026
1065
|
var ClipHeader = ({
|
|
1027
1066
|
clipId,
|
|
@@ -1043,7 +1082,7 @@ var ClipHeader = ({
|
|
|
1043
1082
|
"data-clip-id": clipId,
|
|
1044
1083
|
$interactive: true,
|
|
1045
1084
|
$isSelected: isSelected,
|
|
1046
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TrackName, { children: trackName })
|
|
1085
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TrackName, { title: trackName, children: trackName })
|
|
1047
1086
|
}
|
|
1048
1087
|
);
|
|
1049
1088
|
};
|
|
@@ -1264,6 +1303,7 @@ var Clip = ({
|
|
|
1264
1303
|
"data-clip-container": "true",
|
|
1265
1304
|
"data-track-id": trackId,
|
|
1266
1305
|
onMouseDown,
|
|
1306
|
+
tabIndex: -1,
|
|
1267
1307
|
children: [
|
|
1268
1308
|
showHeader && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1269
1309
|
ClipHeader,
|
|
@@ -1373,11 +1413,140 @@ var MasterVolumeControl = ({
|
|
|
1373
1413
|
] });
|
|
1374
1414
|
};
|
|
1375
1415
|
|
|
1376
|
-
// src/components/
|
|
1416
|
+
// src/components/PianoRollChannel.tsx
|
|
1377
1417
|
var import_react8 = require("react");
|
|
1378
1418
|
var import_styled_components15 = __toESM(require("styled-components"));
|
|
1419
|
+
var import_core2 = require("@waveform-playlist/core");
|
|
1379
1420
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1380
|
-
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) => ({
|
|
1381
1550
|
style: {
|
|
1382
1551
|
transform: `translate3d(${props.$position}px, 0, 0)`
|
|
1383
1552
|
}
|
|
@@ -1393,9 +1562,9 @@ var PlayheadLine = import_styled_components15.default.div.attrs((props) => ({
|
|
|
1393
1562
|
will-change: transform;
|
|
1394
1563
|
`;
|
|
1395
1564
|
var Playhead = ({ position, color = "#ff0000" }) => {
|
|
1396
|
-
return /* @__PURE__ */ (0,
|
|
1565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PlayheadLine, { $position: position, $color: color });
|
|
1397
1566
|
};
|
|
1398
|
-
var PlayheadWithMarkerContainer =
|
|
1567
|
+
var PlayheadWithMarkerContainer = import_styled_components16.default.div`
|
|
1399
1568
|
position: absolute;
|
|
1400
1569
|
top: 0;
|
|
1401
1570
|
left: 0;
|
|
@@ -1404,7 +1573,7 @@ var PlayheadWithMarkerContainer = import_styled_components15.default.div`
|
|
|
1404
1573
|
pointer-events: none;
|
|
1405
1574
|
will-change: transform;
|
|
1406
1575
|
`;
|
|
1407
|
-
var MarkerTriangle =
|
|
1576
|
+
var MarkerTriangle = import_styled_components16.default.div`
|
|
1408
1577
|
position: absolute;
|
|
1409
1578
|
top: -10px;
|
|
1410
1579
|
left: -6px;
|
|
@@ -1414,7 +1583,7 @@ var MarkerTriangle = import_styled_components15.default.div`
|
|
|
1414
1583
|
border-right: 7px solid transparent;
|
|
1415
1584
|
border-top: 10px solid ${(props) => props.$color};
|
|
1416
1585
|
`;
|
|
1417
|
-
var MarkerLine =
|
|
1586
|
+
var MarkerLine = import_styled_components16.default.div`
|
|
1418
1587
|
position: absolute;
|
|
1419
1588
|
top: 0;
|
|
1420
1589
|
left: 0;
|
|
@@ -1430,13 +1599,13 @@ var PlayheadWithMarker = ({
|
|
|
1430
1599
|
audioStartPositionRef,
|
|
1431
1600
|
samplesPerPixel,
|
|
1432
1601
|
sampleRate,
|
|
1433
|
-
controlsOffset,
|
|
1602
|
+
controlsOffset = 0,
|
|
1434
1603
|
getAudioContextTime,
|
|
1435
1604
|
getPlaybackTime
|
|
1436
1605
|
}) => {
|
|
1437
|
-
const containerRef = (0,
|
|
1438
|
-
const animationFrameRef = (0,
|
|
1439
|
-
(0,
|
|
1606
|
+
const containerRef = (0, import_react9.useRef)(null);
|
|
1607
|
+
const animationFrameRef = (0, import_react9.useRef)(null);
|
|
1608
|
+
(0, import_react9.useEffect)(() => {
|
|
1440
1609
|
const updatePosition = () => {
|
|
1441
1610
|
if (containerRef.current) {
|
|
1442
1611
|
let time;
|
|
@@ -1481,35 +1650,51 @@ var PlayheadWithMarker = ({
|
|
|
1481
1650
|
getAudioContextTime,
|
|
1482
1651
|
getPlaybackTime
|
|
1483
1652
|
]);
|
|
1484
|
-
(0,
|
|
1653
|
+
(0, import_react9.useEffect)(() => {
|
|
1485
1654
|
if (!isPlaying && containerRef.current) {
|
|
1486
1655
|
const time = currentTimeRef.current ?? 0;
|
|
1487
1656
|
const pos = time * sampleRate / samplesPerPixel + controlsOffset;
|
|
1488
1657
|
containerRef.current.style.transform = `translate3d(${pos}px, 0, 0)`;
|
|
1489
1658
|
}
|
|
1490
1659
|
});
|
|
1491
|
-
return /* @__PURE__ */ (0,
|
|
1492
|
-
/* @__PURE__ */ (0,
|
|
1493
|
-
/* @__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 })
|
|
1494
1663
|
] });
|
|
1495
1664
|
};
|
|
1496
1665
|
|
|
1497
1666
|
// src/components/Playlist.tsx
|
|
1498
|
-
var
|
|
1499
|
-
var
|
|
1500
|
-
var
|
|
1501
|
-
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;
|
|
1502
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`
|
|
1503
1685
|
overflow-x: auto;
|
|
1686
|
+
overflow-y: hidden;
|
|
1687
|
+
overflow-anchor: none;
|
|
1688
|
+
flex: 1;
|
|
1504
1689
|
position: relative;
|
|
1505
1690
|
`;
|
|
1506
|
-
var
|
|
1691
|
+
var ScrollContainerInner = import_styled_components17.default.div.attrs((props) => ({
|
|
1507
1692
|
style: props.$width !== void 0 ? { width: `${props.$width}px` } : {}
|
|
1508
1693
|
}))`
|
|
1509
1694
|
position: relative;
|
|
1510
1695
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
1511
1696
|
`;
|
|
1512
|
-
var TimescaleWrapper =
|
|
1697
|
+
var TimescaleWrapper = import_styled_components17.default.div.attrs((props) => ({
|
|
1513
1698
|
style: props.$width ? { minWidth: `${props.$width}px` } : {}
|
|
1514
1699
|
}))`
|
|
1515
1700
|
background: ${(props) => props.$backgroundColor || "white"};
|
|
@@ -1517,14 +1702,14 @@ var TimescaleWrapper = import_styled_components16.default.div.attrs((props) => (
|
|
|
1517
1702
|
position: relative;
|
|
1518
1703
|
overflow: hidden; /* Constrain loop region to timescale area */
|
|
1519
1704
|
`;
|
|
1520
|
-
var TracksContainer =
|
|
1705
|
+
var TracksContainer = import_styled_components17.default.div.attrs((props) => ({
|
|
1521
1706
|
style: props.$width !== void 0 ? { minWidth: `${props.$width}px` } : {}
|
|
1522
1707
|
}))`
|
|
1523
1708
|
position: relative;
|
|
1524
1709
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
1525
1710
|
width: 100%;
|
|
1526
1711
|
`;
|
|
1527
|
-
var ClickOverlay =
|
|
1712
|
+
var ClickOverlay = import_styled_components17.default.div`
|
|
1528
1713
|
position: absolute;
|
|
1529
1714
|
top: 0;
|
|
1530
1715
|
left: 0;
|
|
@@ -1541,7 +1726,6 @@ var Playlist = ({
|
|
|
1541
1726
|
timescale,
|
|
1542
1727
|
timescaleWidth,
|
|
1543
1728
|
tracksWidth,
|
|
1544
|
-
scrollContainerWidth,
|
|
1545
1729
|
controlsWidth,
|
|
1546
1730
|
onTracksClick,
|
|
1547
1731
|
onTracksMouseDown,
|
|
@@ -1549,40 +1733,48 @@ var Playlist = ({
|
|
|
1549
1733
|
onTracksMouseUp,
|
|
1550
1734
|
scrollContainerRef,
|
|
1551
1735
|
isSelecting,
|
|
1552
|
-
"data-playlist-state": playlistState
|
|
1736
|
+
"data-playlist-state": playlistState,
|
|
1737
|
+
trackControlsSlots,
|
|
1738
|
+
timescaleGapHeight = 0
|
|
1553
1739
|
}) => {
|
|
1554
|
-
const
|
|
1555
|
-
const handleRef = (0,
|
|
1740
|
+
const scrollAreaRef = (0, import_react10.useRef)(null);
|
|
1741
|
+
const handleRef = (0, import_react10.useCallback)(
|
|
1556
1742
|
(el) => {
|
|
1557
|
-
|
|
1743
|
+
scrollAreaRef.current = el;
|
|
1558
1744
|
scrollContainerRef?.(el);
|
|
1559
1745
|
},
|
|
1560
1746
|
[scrollContainerRef]
|
|
1561
1747
|
);
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
/* @__PURE__ */ (0,
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
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
|
+
] });
|
|
1579
1771
|
};
|
|
1580
|
-
var StyledPlaylist = (0,
|
|
1772
|
+
var StyledPlaylist = (0, import_styled_components17.withTheme)(Playlist);
|
|
1581
1773
|
|
|
1582
1774
|
// src/components/Selection.tsx
|
|
1583
|
-
var
|
|
1584
|
-
var
|
|
1585
|
-
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) => ({
|
|
1586
1778
|
style: {
|
|
1587
1779
|
left: `${props.$left}px`,
|
|
1588
1780
|
width: `${props.$width}px`
|
|
@@ -1605,14 +1797,14 @@ var Selection = ({
|
|
|
1605
1797
|
if (width <= 0) {
|
|
1606
1798
|
return null;
|
|
1607
1799
|
}
|
|
1608
|
-
return /* @__PURE__ */ (0,
|
|
1800
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SelectionOverlay, { $left: startPosition, $width: width, $color: color, "data-selection": true });
|
|
1609
1801
|
};
|
|
1610
1802
|
|
|
1611
1803
|
// src/components/LoopRegion.tsx
|
|
1612
|
-
var
|
|
1613
|
-
var
|
|
1614
|
-
var
|
|
1615
|
-
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) => ({
|
|
1616
1808
|
style: {
|
|
1617
1809
|
left: `${props.$left}px`,
|
|
1618
1810
|
width: `${props.$width}px`
|
|
@@ -1625,7 +1817,7 @@ var LoopRegionOverlayDiv = import_styled_components18.default.div.attrs((props)
|
|
|
1625
1817
|
z-index: 55; /* Between clips (z-index: 50) and selection (z-index: 60) */
|
|
1626
1818
|
pointer-events: none;
|
|
1627
1819
|
`;
|
|
1628
|
-
var LoopMarker =
|
|
1820
|
+
var LoopMarker = import_styled_components19.default.div.attrs((props) => ({
|
|
1629
1821
|
style: {
|
|
1630
1822
|
left: `${props.$left}px`
|
|
1631
1823
|
}
|
|
@@ -1660,8 +1852,8 @@ var LoopRegion = ({
|
|
|
1660
1852
|
if (width <= 0) {
|
|
1661
1853
|
return null;
|
|
1662
1854
|
}
|
|
1663
|
-
return /* @__PURE__ */ (0,
|
|
1664
|
-
/* @__PURE__ */ (0,
|
|
1855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
1856
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1665
1857
|
LoopRegionOverlayDiv,
|
|
1666
1858
|
{
|
|
1667
1859
|
$left: startPosition,
|
|
@@ -1670,7 +1862,7 @@ var LoopRegion = ({
|
|
|
1670
1862
|
"data-loop-region": true
|
|
1671
1863
|
}
|
|
1672
1864
|
),
|
|
1673
|
-
/* @__PURE__ */ (0,
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1674
1866
|
LoopMarker,
|
|
1675
1867
|
{
|
|
1676
1868
|
$left: startPosition,
|
|
@@ -1679,7 +1871,7 @@ var LoopRegion = ({
|
|
|
1679
1871
|
"data-loop-marker": "start"
|
|
1680
1872
|
}
|
|
1681
1873
|
),
|
|
1682
|
-
/* @__PURE__ */ (0,
|
|
1874
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1683
1875
|
LoopMarker,
|
|
1684
1876
|
{
|
|
1685
1877
|
$left: endPosition - 2,
|
|
@@ -1690,7 +1882,7 @@ var LoopRegion = ({
|
|
|
1690
1882
|
)
|
|
1691
1883
|
] });
|
|
1692
1884
|
};
|
|
1693
|
-
var DraggableMarkerHandle =
|
|
1885
|
+
var DraggableMarkerHandle = import_styled_components19.default.div.attrs((props) => ({
|
|
1694
1886
|
style: {
|
|
1695
1887
|
left: `${props.$left}px`
|
|
1696
1888
|
}
|
|
@@ -1732,7 +1924,7 @@ var DraggableMarkerHandle = import_styled_components18.default.div.attrs((props)
|
|
|
1732
1924
|
opacity: 1;
|
|
1733
1925
|
}
|
|
1734
1926
|
`;
|
|
1735
|
-
var TimescaleLoopShade =
|
|
1927
|
+
var TimescaleLoopShade = import_styled_components19.default.div.attrs((props) => ({
|
|
1736
1928
|
style: {
|
|
1737
1929
|
left: `${props.$left}px`,
|
|
1738
1930
|
width: `${props.$width}px`
|
|
@@ -1760,12 +1952,12 @@ var LoopRegionMarkers = ({
|
|
|
1760
1952
|
minPosition = 0,
|
|
1761
1953
|
maxPosition = Infinity
|
|
1762
1954
|
}) => {
|
|
1763
|
-
const [draggingMarker, setDraggingMarker] = (0,
|
|
1764
|
-
const dragStartX = (0,
|
|
1765
|
-
const dragStartPosition = (0,
|
|
1766
|
-
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);
|
|
1767
1959
|
const width = Math.max(0, endPosition - startPosition);
|
|
1768
|
-
const handleMarkerMouseDown = (0,
|
|
1960
|
+
const handleMarkerMouseDown = (0, import_react11.useCallback)(
|
|
1769
1961
|
(e, marker) => {
|
|
1770
1962
|
e.preventDefault();
|
|
1771
1963
|
e.stopPropagation();
|
|
@@ -1793,7 +1985,7 @@ var LoopRegionMarkers = ({
|
|
|
1793
1985
|
},
|
|
1794
1986
|
[startPosition, endPosition, minPosition, maxPosition, onLoopStartChange, onLoopEndChange]
|
|
1795
1987
|
);
|
|
1796
|
-
const handleRegionMouseDown = (0,
|
|
1988
|
+
const handleRegionMouseDown = (0, import_react11.useCallback)(
|
|
1797
1989
|
(e) => {
|
|
1798
1990
|
e.preventDefault();
|
|
1799
1991
|
e.stopPropagation();
|
|
@@ -1829,8 +2021,8 @@ var LoopRegionMarkers = ({
|
|
|
1829
2021
|
if (width <= 0) {
|
|
1830
2022
|
return null;
|
|
1831
2023
|
}
|
|
1832
|
-
return /* @__PURE__ */ (0,
|
|
1833
|
-
/* @__PURE__ */ (0,
|
|
2024
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1834
2026
|
TimescaleLoopShade,
|
|
1835
2027
|
{
|
|
1836
2028
|
$left: startPosition,
|
|
@@ -1841,7 +2033,7 @@ var LoopRegionMarkers = ({
|
|
|
1841
2033
|
"data-loop-region-timescale": true
|
|
1842
2034
|
}
|
|
1843
2035
|
),
|
|
1844
|
-
/* @__PURE__ */ (0,
|
|
2036
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1845
2037
|
DraggableMarkerHandle,
|
|
1846
2038
|
{
|
|
1847
2039
|
$left: startPosition,
|
|
@@ -1852,7 +2044,7 @@ var LoopRegionMarkers = ({
|
|
|
1852
2044
|
"data-loop-marker-handle": "start"
|
|
1853
2045
|
}
|
|
1854
2046
|
),
|
|
1855
|
-
/* @__PURE__ */ (0,
|
|
2047
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1856
2048
|
DraggableMarkerHandle,
|
|
1857
2049
|
{
|
|
1858
2050
|
$left: endPosition,
|
|
@@ -1865,13 +2057,10 @@ var LoopRegionMarkers = ({
|
|
|
1865
2057
|
)
|
|
1866
2058
|
] });
|
|
1867
2059
|
};
|
|
1868
|
-
var TimescaleLoopCreator =
|
|
1869
|
-
style: {
|
|
1870
|
-
left: `${props.$leftOffset || 0}px`
|
|
1871
|
-
}
|
|
1872
|
-
}))`
|
|
2060
|
+
var TimescaleLoopCreator = import_styled_components19.default.div`
|
|
1873
2061
|
position: absolute;
|
|
1874
2062
|
top: 0;
|
|
2063
|
+
left: 0;
|
|
1875
2064
|
right: 0;
|
|
1876
2065
|
height: 100%; /* Stay within timescale bounds, don't extend into tracks */
|
|
1877
2066
|
cursor: crosshair;
|
|
@@ -1884,14 +2073,13 @@ var TimescaleLoopRegion = ({
|
|
|
1884
2073
|
regionColor = "rgba(59, 130, 246, 0.3)",
|
|
1885
2074
|
onLoopRegionChange,
|
|
1886
2075
|
minPosition = 0,
|
|
1887
|
-
maxPosition = Infinity
|
|
1888
|
-
controlsOffset = 0
|
|
2076
|
+
maxPosition = Infinity
|
|
1889
2077
|
}) => {
|
|
1890
|
-
const [, setIsCreating] = (0,
|
|
1891
|
-
const createStartX = (0,
|
|
1892
|
-
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);
|
|
1893
2081
|
const hasLoopRegion = endPosition > startPosition;
|
|
1894
|
-
const handleBackgroundMouseDown = (0,
|
|
2082
|
+
const handleBackgroundMouseDown = (0, import_react11.useCallback)(
|
|
1895
2083
|
(e) => {
|
|
1896
2084
|
const target = e.target;
|
|
1897
2085
|
if (target.closest("[data-loop-marker-handle]") || target.closest("[data-loop-region-timescale]")) {
|
|
@@ -1922,14 +2110,13 @@ var TimescaleLoopRegion = ({
|
|
|
1922
2110
|
},
|
|
1923
2111
|
[minPosition, maxPosition, onLoopRegionChange]
|
|
1924
2112
|
);
|
|
1925
|
-
return /* @__PURE__ */ (0,
|
|
2113
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1926
2114
|
TimescaleLoopCreator,
|
|
1927
2115
|
{
|
|
1928
2116
|
ref: containerRef,
|
|
1929
|
-
$leftOffset: controlsOffset,
|
|
1930
2117
|
onMouseDown: handleBackgroundMouseDown,
|
|
1931
2118
|
"data-timescale-loop-creator": true,
|
|
1932
|
-
children: hasLoopRegion && /* @__PURE__ */ (0,
|
|
2119
|
+
children: hasLoopRegion && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1933
2120
|
LoopRegionMarkers,
|
|
1934
2121
|
{
|
|
1935
2122
|
startPosition,
|
|
@@ -1948,10 +2135,10 @@ var TimescaleLoopRegion = ({
|
|
|
1948
2135
|
};
|
|
1949
2136
|
|
|
1950
2137
|
// src/components/SelectionTimeInputs.tsx
|
|
1951
|
-
var
|
|
2138
|
+
var import_react13 = require("react");
|
|
1952
2139
|
|
|
1953
2140
|
// src/components/TimeInput.tsx
|
|
1954
|
-
var
|
|
2141
|
+
var import_react12 = require("react");
|
|
1955
2142
|
|
|
1956
2143
|
// src/utils/timeFormat.ts
|
|
1957
2144
|
function clockFormat(seconds, decimals) {
|
|
@@ -2001,7 +2188,7 @@ function parseTime(timeStr, format) {
|
|
|
2001
2188
|
}
|
|
2002
2189
|
|
|
2003
2190
|
// src/components/TimeInput.tsx
|
|
2004
|
-
var
|
|
2191
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
2005
2192
|
var TimeInput = ({
|
|
2006
2193
|
id,
|
|
2007
2194
|
label,
|
|
@@ -2011,8 +2198,8 @@ var TimeInput = ({
|
|
|
2011
2198
|
onChange,
|
|
2012
2199
|
readOnly = false
|
|
2013
2200
|
}) => {
|
|
2014
|
-
const [displayValue, setDisplayValue] = (0,
|
|
2015
|
-
(0,
|
|
2201
|
+
const [displayValue, setDisplayValue] = (0, import_react12.useState)("");
|
|
2202
|
+
(0, import_react12.useEffect)(() => {
|
|
2016
2203
|
const formatted = formatTime(value, format);
|
|
2017
2204
|
setDisplayValue(formatted);
|
|
2018
2205
|
}, [value, format, id]);
|
|
@@ -2032,9 +2219,9 @@ var TimeInput = ({
|
|
|
2032
2219
|
e.currentTarget.blur();
|
|
2033
2220
|
}
|
|
2034
2221
|
};
|
|
2035
|
-
return /* @__PURE__ */ (0,
|
|
2036
|
-
/* @__PURE__ */ (0,
|
|
2037
|
-
/* @__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)(
|
|
2038
2225
|
BaseInput,
|
|
2039
2226
|
{
|
|
2040
2227
|
type: "text",
|
|
@@ -2051,15 +2238,15 @@ var TimeInput = ({
|
|
|
2051
2238
|
};
|
|
2052
2239
|
|
|
2053
2240
|
// src/components/SelectionTimeInputs.tsx
|
|
2054
|
-
var
|
|
2241
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
2055
2242
|
var SelectionTimeInputs = ({
|
|
2056
2243
|
selectionStart,
|
|
2057
2244
|
selectionEnd,
|
|
2058
2245
|
onSelectionChange,
|
|
2059
2246
|
className
|
|
2060
2247
|
}) => {
|
|
2061
|
-
const [timeFormat, setTimeFormat] = (0,
|
|
2062
|
-
(0,
|
|
2248
|
+
const [timeFormat, setTimeFormat] = (0, import_react13.useState)("hh:mm:ss.uuu");
|
|
2249
|
+
(0, import_react13.useEffect)(() => {
|
|
2063
2250
|
const timeFormatSelect = document.querySelector(".time-format");
|
|
2064
2251
|
const handleFormatChange = () => {
|
|
2065
2252
|
if (timeFormatSelect) {
|
|
@@ -2084,8 +2271,8 @@ var SelectionTimeInputs = ({
|
|
|
2084
2271
|
onSelectionChange(selectionStart, value);
|
|
2085
2272
|
}
|
|
2086
2273
|
};
|
|
2087
|
-
return /* @__PURE__ */ (0,
|
|
2088
|
-
/* @__PURE__ */ (0,
|
|
2274
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className, children: [
|
|
2275
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2089
2276
|
TimeInput,
|
|
2090
2277
|
{
|
|
2091
2278
|
id: "audio_start",
|
|
@@ -2096,7 +2283,7 @@ var SelectionTimeInputs = ({
|
|
|
2096
2283
|
onChange: handleStartChange
|
|
2097
2284
|
}
|
|
2098
2285
|
),
|
|
2099
|
-
/* @__PURE__ */ (0,
|
|
2286
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2100
2287
|
TimeInput,
|
|
2101
2288
|
{
|
|
2102
2289
|
id: "audio_end",
|
|
@@ -2111,14 +2298,14 @@ var SelectionTimeInputs = ({
|
|
|
2111
2298
|
};
|
|
2112
2299
|
|
|
2113
2300
|
// src/contexts/DevicePixelRatio.tsx
|
|
2114
|
-
var
|
|
2115
|
-
var
|
|
2301
|
+
var import_react14 = require("react");
|
|
2302
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
2116
2303
|
function getScale() {
|
|
2117
2304
|
return window.devicePixelRatio;
|
|
2118
2305
|
}
|
|
2119
|
-
var DevicePixelRatioContext = (0,
|
|
2306
|
+
var DevicePixelRatioContext = (0, import_react14.createContext)(getScale());
|
|
2120
2307
|
var DevicePixelRatioProvider = ({ children }) => {
|
|
2121
|
-
const [scale, setScale] = (0,
|
|
2308
|
+
const [scale, setScale] = (0, import_react14.useState)(getScale());
|
|
2122
2309
|
matchMedia(`(resolution: ${getScale()}dppx)`).addEventListener(
|
|
2123
2310
|
"change",
|
|
2124
2311
|
() => {
|
|
@@ -2126,13 +2313,13 @@ var DevicePixelRatioProvider = ({ children }) => {
|
|
|
2126
2313
|
},
|
|
2127
2314
|
{ once: true }
|
|
2128
2315
|
);
|
|
2129
|
-
return /* @__PURE__ */ (0,
|
|
2316
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });
|
|
2130
2317
|
};
|
|
2131
|
-
var useDevicePixelRatio = () => (0,
|
|
2318
|
+
var useDevicePixelRatio = () => (0, import_react14.useContext)(DevicePixelRatioContext);
|
|
2132
2319
|
|
|
2133
2320
|
// src/contexts/PlaylistInfo.tsx
|
|
2134
|
-
var
|
|
2135
|
-
var PlaylistInfoContext = (0,
|
|
2321
|
+
var import_react15 = require("react");
|
|
2322
|
+
var PlaylistInfoContext = (0, import_react15.createContext)({
|
|
2136
2323
|
sampleRate: 48e3,
|
|
2137
2324
|
samplesPerPixel: 1e3,
|
|
2138
2325
|
zoomLevels: [1e3, 1500, 2e3, 2500],
|
|
@@ -2146,22 +2333,22 @@ var PlaylistInfoContext = (0, import_react14.createContext)({
|
|
|
2146
2333
|
barWidth: 1,
|
|
2147
2334
|
barGap: 0
|
|
2148
2335
|
});
|
|
2149
|
-
var usePlaylistInfo = () => (0,
|
|
2336
|
+
var usePlaylistInfo = () => (0, import_react15.useContext)(PlaylistInfoContext);
|
|
2150
2337
|
|
|
2151
2338
|
// src/contexts/Theme.tsx
|
|
2152
|
-
var import_react15 = require("react");
|
|
2153
|
-
var import_styled_components19 = require("styled-components");
|
|
2154
|
-
var useTheme2 = () => (0, import_react15.useContext)(import_styled_components19.ThemeContext);
|
|
2155
|
-
|
|
2156
|
-
// src/contexts/TrackControls.tsx
|
|
2157
2339
|
var import_react16 = require("react");
|
|
2158
|
-
var
|
|
2159
|
-
var
|
|
2160
|
-
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);
|
|
2161
2342
|
|
|
2162
|
-
// src/contexts/
|
|
2343
|
+
// src/contexts/TrackControls.tsx
|
|
2163
2344
|
var import_react17 = require("react");
|
|
2164
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");
|
|
2165
2352
|
var defaultProgress = 0;
|
|
2166
2353
|
var defaultIsPlaying = false;
|
|
2167
2354
|
var defaultSelectionStart = 0;
|
|
@@ -2172,8 +2359,8 @@ var defaultPlayout = {
|
|
|
2172
2359
|
selectionStart: defaultSelectionStart,
|
|
2173
2360
|
selectionEnd: defaultSelectionEnd
|
|
2174
2361
|
};
|
|
2175
|
-
var PlayoutStatusContext = (0,
|
|
2176
|
-
var PlayoutStatusUpdateContext = (0,
|
|
2362
|
+
var PlayoutStatusContext = (0, import_react18.createContext)(defaultPlayout);
|
|
2363
|
+
var PlayoutStatusUpdateContext = (0, import_react18.createContext)({
|
|
2177
2364
|
setIsPlaying: () => {
|
|
2178
2365
|
},
|
|
2179
2366
|
setProgress: () => {
|
|
@@ -2182,26 +2369,26 @@ var PlayoutStatusUpdateContext = (0, import_react17.createContext)({
|
|
|
2182
2369
|
}
|
|
2183
2370
|
});
|
|
2184
2371
|
var PlayoutProvider = ({ children }) => {
|
|
2185
|
-
const [isPlaying, setIsPlaying] = (0,
|
|
2186
|
-
const [progress, setProgress] = (0,
|
|
2187
|
-
const [selectionStart, setSelectionStart] = (0,
|
|
2188
|
-
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);
|
|
2189
2376
|
const setSelection = (start, end) => {
|
|
2190
2377
|
setSelectionStart(start);
|
|
2191
2378
|
setSelectionEnd(end);
|
|
2192
2379
|
};
|
|
2193
|
-
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 }) });
|
|
2194
2381
|
};
|
|
2195
|
-
var usePlayoutStatus = () => (0,
|
|
2196
|
-
var usePlayoutStatusUpdate = () => (0,
|
|
2382
|
+
var usePlayoutStatus = () => (0, import_react18.useContext)(PlayoutStatusContext);
|
|
2383
|
+
var usePlayoutStatusUpdate = () => (0, import_react18.useContext)(PlayoutStatusUpdateContext);
|
|
2197
2384
|
|
|
2198
2385
|
// src/components/SpectrogramChannel.tsx
|
|
2199
|
-
var
|
|
2200
|
-
var
|
|
2201
|
-
var
|
|
2202
|
-
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");
|
|
2203
2390
|
var LINEAR_FREQUENCY_SCALE = (f, minF, maxF) => (f - minF) / (maxF - minF);
|
|
2204
|
-
var
|
|
2391
|
+
var Wrapper4 = import_styled_components21.default.div.attrs((props) => ({
|
|
2205
2392
|
style: {
|
|
2206
2393
|
top: `${props.$waveHeight * props.$index}px`,
|
|
2207
2394
|
width: `${props.$cssWidth}px`,
|
|
@@ -2213,7 +2400,7 @@ var Wrapper3 = import_styled_components20.default.div.attrs((props) => ({
|
|
|
2213
2400
|
transform: translateZ(0);
|
|
2214
2401
|
backface-visibility: hidden;
|
|
2215
2402
|
`;
|
|
2216
|
-
var SpectrogramCanvas =
|
|
2403
|
+
var SpectrogramCanvas = import_styled_components21.default.canvas.attrs((props) => ({
|
|
2217
2404
|
style: {
|
|
2218
2405
|
width: `${props.$cssWidth}px`,
|
|
2219
2406
|
height: `${props.$waveHeight}px`,
|
|
@@ -2222,8 +2409,6 @@ var SpectrogramCanvas = import_styled_components20.default.canvas.attrs((props)
|
|
|
2222
2409
|
}))`
|
|
2223
2410
|
position: absolute;
|
|
2224
2411
|
top: 0;
|
|
2225
|
-
/* Promote to own compositing layer for smoother scrolling */
|
|
2226
|
-
will-change: transform;
|
|
2227
2412
|
image-rendering: pixelated;
|
|
2228
2413
|
image-rendering: crisp-edges;
|
|
2229
2414
|
`;
|
|
@@ -2253,24 +2438,24 @@ var SpectrogramChannel = ({
|
|
|
2253
2438
|
}) => {
|
|
2254
2439
|
const channelIndex = channelIndexProp ?? index;
|
|
2255
2440
|
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
2256
|
-
const registeredIdsRef = (0,
|
|
2257
|
-
const transferredCanvasesRef = (0,
|
|
2258
|
-
const workerApiRef = (0,
|
|
2259
|
-
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);
|
|
2260
2445
|
const isWorkerMode = !!(workerApi && clipId);
|
|
2261
2446
|
const clipOriginX = useClipViewportOrigin();
|
|
2262
|
-
const visibleChunkIndices = useVisibleChunkIndices(length,
|
|
2447
|
+
const visibleChunkIndices = useVisibleChunkIndices(length, import_core3.MAX_CANVAS_WIDTH, clipOriginX);
|
|
2263
2448
|
const lut = colorLUT ?? DEFAULT_COLOR_LUT;
|
|
2264
2449
|
const maxF = maxFrequency ?? (data ? data.sampleRate / 2 : 22050);
|
|
2265
2450
|
const scaleFn = frequencyScaleFn ?? LINEAR_FREQUENCY_SCALE;
|
|
2266
2451
|
const hasCustomFrequencyScale = Boolean(frequencyScaleFn);
|
|
2267
|
-
(0,
|
|
2452
|
+
(0, import_react19.useEffect)(() => {
|
|
2268
2453
|
workerApiRef.current = workerApi;
|
|
2269
2454
|
}, [workerApi]);
|
|
2270
|
-
(0,
|
|
2455
|
+
(0, import_react19.useEffect)(() => {
|
|
2271
2456
|
onCanvasesReadyRef.current = onCanvasesReady;
|
|
2272
2457
|
}, [onCanvasesReady]);
|
|
2273
|
-
(0,
|
|
2458
|
+
(0, import_react19.useEffect)(() => {
|
|
2274
2459
|
if (!isWorkerMode) return;
|
|
2275
2460
|
const currentWorkerApi = workerApiRef.current;
|
|
2276
2461
|
if (!currentWorkerApi || !clipId) return;
|
|
@@ -2325,15 +2510,15 @@ var SpectrogramChannel = ({
|
|
|
2325
2510
|
const match = id.match(/chunk(\d+)$/);
|
|
2326
2511
|
if (!match) {
|
|
2327
2512
|
console.warn(`[spectrogram] Unexpected canvas ID format: ${id}`);
|
|
2328
|
-
return
|
|
2513
|
+
return import_core3.MAX_CANVAS_WIDTH;
|
|
2329
2514
|
}
|
|
2330
2515
|
const chunkIdx = parseInt(match[1], 10);
|
|
2331
|
-
return Math.min(length - chunkIdx *
|
|
2516
|
+
return Math.min(length - chunkIdx * import_core3.MAX_CANVAS_WIDTH, import_core3.MAX_CANVAS_WIDTH);
|
|
2332
2517
|
});
|
|
2333
2518
|
onCanvasesReadyRef.current?.(allIds, allWidths);
|
|
2334
2519
|
}
|
|
2335
2520
|
}, [canvasMapRef, isWorkerMode, clipId, channelIndex, length, visibleChunkIndices]);
|
|
2336
|
-
(0,
|
|
2521
|
+
(0, import_react19.useEffect)(() => {
|
|
2337
2522
|
return () => {
|
|
2338
2523
|
const api = workerApiRef.current;
|
|
2339
2524
|
if (!api) return;
|
|
@@ -2347,7 +2532,7 @@ var SpectrogramChannel = ({
|
|
|
2347
2532
|
registeredIdsRef.current = [];
|
|
2348
2533
|
};
|
|
2349
2534
|
}, []);
|
|
2350
|
-
(0,
|
|
2535
|
+
(0, import_react19.useEffect)(() => {
|
|
2351
2536
|
if (isWorkerMode || !data) return;
|
|
2352
2537
|
const {
|
|
2353
2538
|
frequencyBinCount,
|
|
@@ -2360,7 +2545,7 @@ var SpectrogramChannel = ({
|
|
|
2360
2545
|
const rangeDb = rawRangeDb === 0 ? 1 : rawRangeDb;
|
|
2361
2546
|
const binToFreq = (bin) => bin / frequencyBinCount * (sampleRate / 2);
|
|
2362
2547
|
for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
|
|
2363
|
-
const globalPixelOffset = canvasIdx *
|
|
2548
|
+
const globalPixelOffset = canvasIdx * import_core3.MAX_CANVAS_WIDTH;
|
|
2364
2549
|
const ctx = canvas.getContext("2d");
|
|
2365
2550
|
if (!ctx) continue;
|
|
2366
2551
|
const canvasWidth = canvas.width / devicePixelRatio;
|
|
@@ -2436,9 +2621,9 @@ var SpectrogramChannel = ({
|
|
|
2436
2621
|
visibleChunkIndices
|
|
2437
2622
|
]);
|
|
2438
2623
|
const canvases = visibleChunkIndices.map((i) => {
|
|
2439
|
-
const chunkLeft = i *
|
|
2440
|
-
const currentWidth = Math.min(length - chunkLeft,
|
|
2441
|
-
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)(
|
|
2442
2627
|
SpectrogramCanvas,
|
|
2443
2628
|
{
|
|
2444
2629
|
$cssWidth: currentWidth,
|
|
@@ -2452,11 +2637,11 @@ var SpectrogramChannel = ({
|
|
|
2452
2637
|
`${length}-${i}`
|
|
2453
2638
|
);
|
|
2454
2639
|
});
|
|
2455
|
-
return /* @__PURE__ */ (0,
|
|
2640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Wrapper4, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });
|
|
2456
2641
|
};
|
|
2457
2642
|
|
|
2458
2643
|
// src/components/SmartChannel.tsx
|
|
2459
|
-
var
|
|
2644
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
2460
2645
|
var SmartChannel = ({
|
|
2461
2646
|
isSelected,
|
|
2462
2647
|
transparentBackground,
|
|
@@ -2470,10 +2655,19 @@ var SmartChannel = ({
|
|
|
2470
2655
|
spectrogramWorkerApi,
|
|
2471
2656
|
spectrogramClipId,
|
|
2472
2657
|
spectrogramOnCanvasesReady,
|
|
2658
|
+
midiNotes,
|
|
2659
|
+
sampleRate: sampleRateProp,
|
|
2660
|
+
clipOffsetSeconds,
|
|
2473
2661
|
...props
|
|
2474
2662
|
}) => {
|
|
2475
2663
|
const theme = useTheme2();
|
|
2476
|
-
const {
|
|
2664
|
+
const {
|
|
2665
|
+
waveHeight,
|
|
2666
|
+
barWidth,
|
|
2667
|
+
barGap,
|
|
2668
|
+
samplesPerPixel: contextSpp,
|
|
2669
|
+
sampleRate: contextSampleRate
|
|
2670
|
+
} = usePlaylistInfo();
|
|
2477
2671
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2478
2672
|
const samplesPerPixel = sppProp ?? contextSpp;
|
|
2479
2673
|
const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;
|
|
@@ -2481,7 +2675,7 @@ var SmartChannel = ({
|
|
|
2481
2675
|
const drawMode = theme?.waveformDrawMode || "inverted";
|
|
2482
2676
|
const hasSpectrogram = spectrogramData || spectrogramWorkerApi;
|
|
2483
2677
|
if (renderMode === "spectrogram" && hasSpectrogram) {
|
|
2484
|
-
return /* @__PURE__ */ (0,
|
|
2678
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2485
2679
|
SpectrogramChannel,
|
|
2486
2680
|
{
|
|
2487
2681
|
index: props.index,
|
|
@@ -2502,8 +2696,8 @@ var SmartChannel = ({
|
|
|
2502
2696
|
}
|
|
2503
2697
|
if (renderMode === "both" && hasSpectrogram) {
|
|
2504
2698
|
const halfHeight = Math.floor(waveHeight / 2);
|
|
2505
|
-
return /* @__PURE__ */ (0,
|
|
2506
|
-
/* @__PURE__ */ (0,
|
|
2699
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
2700
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2507
2701
|
SpectrogramChannel,
|
|
2508
2702
|
{
|
|
2509
2703
|
index: props.index * 2,
|
|
@@ -2522,7 +2716,7 @@ var SmartChannel = ({
|
|
|
2522
2716
|
onCanvasesReady: spectrogramOnCanvasesReady
|
|
2523
2717
|
}
|
|
2524
2718
|
),
|
|
2525
|
-
/* @__PURE__ */ (0,
|
|
2719
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2526
2720
|
"div",
|
|
2527
2721
|
{
|
|
2528
2722
|
style: {
|
|
@@ -2531,7 +2725,7 @@ var SmartChannel = ({
|
|
|
2531
2725
|
width: props.length,
|
|
2532
2726
|
height: halfHeight
|
|
2533
2727
|
},
|
|
2534
|
-
children: /* @__PURE__ */ (0,
|
|
2728
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2535
2729
|
Channel,
|
|
2536
2730
|
{
|
|
2537
2731
|
...props,
|
|
@@ -2550,7 +2744,27 @@ var SmartChannel = ({
|
|
|
2550
2744
|
)
|
|
2551
2745
|
] });
|
|
2552
2746
|
}
|
|
2553
|
-
|
|
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)(
|
|
2554
2768
|
Channel,
|
|
2555
2769
|
{
|
|
2556
2770
|
...props,
|
|
@@ -2567,11 +2781,11 @@ var SmartChannel = ({
|
|
|
2567
2781
|
};
|
|
2568
2782
|
|
|
2569
2783
|
// src/components/SpectrogramLabels.tsx
|
|
2570
|
-
var
|
|
2571
|
-
var
|
|
2572
|
-
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");
|
|
2573
2787
|
var LABELS_WIDTH = 72;
|
|
2574
|
-
var LabelsStickyWrapper =
|
|
2788
|
+
var LabelsStickyWrapper = import_styled_components22.default.div`
|
|
2575
2789
|
position: sticky;
|
|
2576
2790
|
left: 0;
|
|
2577
2791
|
z-index: 101;
|
|
@@ -2619,12 +2833,12 @@ var SpectrogramLabels = ({
|
|
|
2619
2833
|
renderMode = "spectrogram",
|
|
2620
2834
|
hasClipHeaders = false
|
|
2621
2835
|
}) => {
|
|
2622
|
-
const canvasRef = (0,
|
|
2836
|
+
const canvasRef = (0, import_react20.useRef)(null);
|
|
2623
2837
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2624
2838
|
const spectrogramHeight = renderMode === "both" ? Math.floor(waveHeight / 2) : waveHeight;
|
|
2625
2839
|
const totalHeight = numChannels * waveHeight;
|
|
2626
2840
|
const clipHeaderOffset = hasClipHeaders ? 22 : 0;
|
|
2627
|
-
(0,
|
|
2841
|
+
(0, import_react20.useLayoutEffect)(() => {
|
|
2628
2842
|
const canvas = canvasRef.current;
|
|
2629
2843
|
if (!canvas) return;
|
|
2630
2844
|
const ctx = canvas.getContext("2d");
|
|
@@ -2662,7 +2876,7 @@ var SpectrogramLabels = ({
|
|
|
2662
2876
|
spectrogramHeight,
|
|
2663
2877
|
clipHeaderOffset
|
|
2664
2878
|
]);
|
|
2665
|
-
return /* @__PURE__ */ (0,
|
|
2879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2666
2880
|
"canvas",
|
|
2667
2881
|
{
|
|
2668
2882
|
ref: canvasRef,
|
|
@@ -2678,11 +2892,11 @@ var SpectrogramLabels = ({
|
|
|
2678
2892
|
};
|
|
2679
2893
|
|
|
2680
2894
|
// src/components/SmartScale.tsx
|
|
2681
|
-
var
|
|
2895
|
+
var import_react22 = require("react");
|
|
2682
2896
|
|
|
2683
2897
|
// src/components/TimeScale.tsx
|
|
2684
|
-
var
|
|
2685
|
-
var
|
|
2898
|
+
var import_react21 = __toESM(require("react"));
|
|
2899
|
+
var import_styled_components23 = __toESM(require("styled-components"));
|
|
2686
2900
|
|
|
2687
2901
|
// src/utils/conversions.ts
|
|
2688
2902
|
function samplesToSeconds(samples, sampleRate) {
|
|
@@ -2705,18 +2919,17 @@ function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
|
|
|
2705
2919
|
}
|
|
2706
2920
|
|
|
2707
2921
|
// src/components/TimeScale.tsx
|
|
2708
|
-
var
|
|
2709
|
-
var
|
|
2922
|
+
var import_core4 = require("@waveform-playlist/core");
|
|
2923
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2710
2924
|
function formatTime2(milliseconds) {
|
|
2711
2925
|
const seconds = Math.floor(milliseconds / 1e3);
|
|
2712
2926
|
const s = seconds % 60;
|
|
2713
2927
|
const m = (seconds - s) / 60;
|
|
2714
2928
|
return `${m}:${String(s).padStart(2, "0")}`;
|
|
2715
2929
|
}
|
|
2716
|
-
var PlaylistTimeScaleScroll =
|
|
2930
|
+
var PlaylistTimeScaleScroll = import_styled_components23.default.div.attrs((props) => ({
|
|
2717
2931
|
style: {
|
|
2718
2932
|
width: `${props.$cssWidth}px`,
|
|
2719
|
-
marginLeft: `${props.$controlWidth}px`,
|
|
2720
2933
|
height: `${props.$timeScaleHeight}px`
|
|
2721
2934
|
}
|
|
2722
2935
|
}))`
|
|
@@ -2725,7 +2938,7 @@ var PlaylistTimeScaleScroll = import_styled_components22.default.div.attrs((prop
|
|
|
2725
2938
|
border-bottom: 1px solid ${(props) => props.theme.timeColor};
|
|
2726
2939
|
box-sizing: border-box;
|
|
2727
2940
|
`;
|
|
2728
|
-
var TimeTickChunk =
|
|
2941
|
+
var TimeTickChunk = import_styled_components23.default.canvas.attrs((props) => ({
|
|
2729
2942
|
style: {
|
|
2730
2943
|
width: `${props.$cssWidth}px`,
|
|
2731
2944
|
height: `${props.$timeScaleHeight}px`,
|
|
@@ -2734,10 +2947,8 @@ var TimeTickChunk = import_styled_components22.default.canvas.attrs((props) => (
|
|
|
2734
2947
|
}))`
|
|
2735
2948
|
position: absolute;
|
|
2736
2949
|
bottom: 0;
|
|
2737
|
-
/* Promote to own compositing layer for smoother scrolling */
|
|
2738
|
-
will-change: transform;
|
|
2739
2950
|
`;
|
|
2740
|
-
var TimeStamp =
|
|
2951
|
+
var TimeStamp = import_styled_components23.default.div.attrs((props) => ({
|
|
2741
2952
|
style: {
|
|
2742
2953
|
left: `${props.$left + 4}px`
|
|
2743
2954
|
// Offset 4px to the right of the tick
|
|
@@ -2758,14 +2969,9 @@ var TimeScale = (props) => {
|
|
|
2758
2969
|
renderTimestamp
|
|
2759
2970
|
} = props;
|
|
2760
2971
|
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
2761
|
-
const {
|
|
2762
|
-
sampleRate,
|
|
2763
|
-
samplesPerPixel,
|
|
2764
|
-
timeScaleHeight,
|
|
2765
|
-
controls: { show: showControls, width: controlWidth }
|
|
2766
|
-
} = (0, import_react20.useContext)(PlaylistInfoContext);
|
|
2972
|
+
const { sampleRate, samplesPerPixel, timeScaleHeight } = (0, import_react21.useContext)(PlaylistInfoContext);
|
|
2767
2973
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2768
|
-
const { widthX, canvasInfo, timeMarkersWithPositions } = (0,
|
|
2974
|
+
const { widthX, canvasInfo, timeMarkersWithPositions } = (0, import_react21.useMemo)(() => {
|
|
2769
2975
|
const nextCanvasInfo = /* @__PURE__ */ new Map();
|
|
2770
2976
|
const nextMarkers = [];
|
|
2771
2977
|
const nextWidthX = secondsToPixels(duration / 1e3, samplesPerPixel, sampleRate);
|
|
@@ -2776,7 +2982,7 @@ var TimeScale = (props) => {
|
|
|
2776
2982
|
if (counter % marker === 0) {
|
|
2777
2983
|
const timeMs = counter;
|
|
2778
2984
|
const timestamp = formatTime2(timeMs);
|
|
2779
|
-
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);
|
|
2780
2986
|
nextMarkers.push({ pix, element });
|
|
2781
2987
|
nextCanvasInfo.set(pix, timeScaleHeight);
|
|
2782
2988
|
} else if (counter % bigStep === 0) {
|
|
@@ -2801,11 +3007,11 @@ var TimeScale = (props) => {
|
|
|
2801
3007
|
renderTimestamp,
|
|
2802
3008
|
timeScaleHeight
|
|
2803
3009
|
]);
|
|
2804
|
-
const visibleChunkIndices = useVisibleChunkIndices(widthX,
|
|
3010
|
+
const visibleChunkIndices = useVisibleChunkIndices(widthX, import_core4.MAX_CANVAS_WIDTH);
|
|
2805
3011
|
const visibleChunks = visibleChunkIndices.map((i) => {
|
|
2806
|
-
const chunkLeft = i *
|
|
2807
|
-
const chunkWidth = Math.min(widthX - chunkLeft,
|
|
2808
|
-
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)(
|
|
2809
3015
|
TimeTickChunk,
|
|
2810
3016
|
{
|
|
2811
3017
|
$cssWidth: chunkWidth,
|
|
@@ -2819,14 +3025,14 @@ var TimeScale = (props) => {
|
|
|
2819
3025
|
`timescale-${i}`
|
|
2820
3026
|
);
|
|
2821
3027
|
});
|
|
2822
|
-
const firstChunkLeft = visibleChunkIndices.length > 0 ? visibleChunkIndices[0] *
|
|
2823
|
-
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;
|
|
2824
3030
|
const visibleMarkers = visibleChunkIndices.length > 0 ? timeMarkersWithPositions.filter(({ pix }) => pix >= firstChunkLeft && pix < lastChunkRight).map(({ element }) => element) : timeMarkersWithPositions.map(({ element }) => element);
|
|
2825
|
-
(0,
|
|
3031
|
+
(0, import_react21.useLayoutEffect)(() => {
|
|
2826
3032
|
for (const [chunkIdx, canvas] of canvasMapRef.current.entries()) {
|
|
2827
3033
|
const ctx = canvas.getContext("2d");
|
|
2828
3034
|
if (!ctx) continue;
|
|
2829
|
-
const chunkLeft = chunkIdx *
|
|
3035
|
+
const chunkLeft = chunkIdx * import_core4.MAX_CANVAS_WIDTH;
|
|
2830
3036
|
const chunkWidth = canvas.width / devicePixelRatio;
|
|
2831
3037
|
ctx.resetTransform();
|
|
2832
3038
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
@@ -2849,23 +3055,15 @@ var TimeScale = (props) => {
|
|
|
2849
3055
|
canvasInfo,
|
|
2850
3056
|
visibleChunkIndices
|
|
2851
3057
|
]);
|
|
2852
|
-
return /* @__PURE__ */ (0,
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
$controlWidth: showControls ? controlWidth : 0,
|
|
2857
|
-
$timeScaleHeight: timeScaleHeight,
|
|
2858
|
-
children: [
|
|
2859
|
-
visibleMarkers,
|
|
2860
|
-
visibleChunks
|
|
2861
|
-
]
|
|
2862
|
-
}
|
|
2863
|
-
);
|
|
3058
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(PlaylistTimeScaleScroll, { $cssWidth: widthX, $timeScaleHeight: timeScaleHeight, children: [
|
|
3059
|
+
visibleMarkers,
|
|
3060
|
+
visibleChunks
|
|
3061
|
+
] });
|
|
2864
3062
|
};
|
|
2865
|
-
var StyledTimeScale = (0,
|
|
3063
|
+
var StyledTimeScale = (0, import_styled_components23.withTheme)(TimeScale);
|
|
2866
3064
|
|
|
2867
3065
|
// src/components/SmartScale.tsx
|
|
2868
|
-
var
|
|
3066
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
2869
3067
|
var timeinfo = /* @__PURE__ */ new Map([
|
|
2870
3068
|
[
|
|
2871
3069
|
700,
|
|
@@ -2939,9 +3137,9 @@ function getScaleInfo(samplesPerPixel) {
|
|
|
2939
3137
|
return config;
|
|
2940
3138
|
}
|
|
2941
3139
|
var SmartScale = ({ renderTimestamp }) => {
|
|
2942
|
-
const { samplesPerPixel, duration } = (0,
|
|
3140
|
+
const { samplesPerPixel, duration } = (0, import_react22.useContext)(PlaylistInfoContext);
|
|
2943
3141
|
let config = getScaleInfo(samplesPerPixel);
|
|
2944
|
-
return /* @__PURE__ */ (0,
|
|
3142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2945
3143
|
StyledTimeScale,
|
|
2946
3144
|
{
|
|
2947
3145
|
marker: config.marker,
|
|
@@ -2954,9 +3152,9 @@ var SmartScale = ({ renderTimestamp }) => {
|
|
|
2954
3152
|
};
|
|
2955
3153
|
|
|
2956
3154
|
// src/components/TimeFormatSelect.tsx
|
|
2957
|
-
var
|
|
2958
|
-
var
|
|
2959
|
-
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`
|
|
2960
3158
|
display: inline-flex;
|
|
2961
3159
|
align-items: center;
|
|
2962
3160
|
gap: 0.5rem;
|
|
@@ -2978,7 +3176,7 @@ var TimeFormatSelect = ({
|
|
|
2978
3176
|
const handleChange = (e) => {
|
|
2979
3177
|
onChange(e.target.value);
|
|
2980
3178
|
};
|
|
2981
|
-
return /* @__PURE__ */ (0,
|
|
3179
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SelectWrapper, { className, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2982
3180
|
BaseSelect,
|
|
2983
3181
|
{
|
|
2984
3182
|
className: "time-format",
|
|
@@ -2986,50 +3184,30 @@ var TimeFormatSelect = ({
|
|
|
2986
3184
|
onChange: handleChange,
|
|
2987
3185
|
disabled,
|
|
2988
3186
|
"aria-label": "Time format selection",
|
|
2989
|
-
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))
|
|
2990
3188
|
}
|
|
2991
3189
|
) });
|
|
2992
3190
|
};
|
|
2993
3191
|
|
|
2994
3192
|
// src/components/Track.tsx
|
|
2995
|
-
var
|
|
2996
|
-
var
|
|
2997
|
-
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) => ({
|
|
2998
3196
|
style: {
|
|
2999
3197
|
height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`
|
|
3000
3198
|
}
|
|
3001
3199
|
}))`
|
|
3002
3200
|
position: relative;
|
|
3003
|
-
display: flex;
|
|
3004
3201
|
${(props) => props.$width !== void 0 && `width: ${props.$width}px;`}
|
|
3005
3202
|
`;
|
|
3006
|
-
var ChannelContainer =
|
|
3203
|
+
var ChannelContainer = import_styled_components25.default.div.attrs((props) => ({
|
|
3007
3204
|
style: {
|
|
3008
3205
|
paddingLeft: `${props.$offset || 0}px`
|
|
3009
3206
|
}
|
|
3010
3207
|
}))`
|
|
3011
3208
|
position: relative;
|
|
3012
3209
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
3013
|
-
flex: 1;
|
|
3014
|
-
`;
|
|
3015
|
-
var ControlsWrapper = import_styled_components24.default.div.attrs((props) => ({
|
|
3016
|
-
style: {
|
|
3017
|
-
width: `${props.$controlWidth}px`
|
|
3018
|
-
}
|
|
3019
|
-
}))`
|
|
3020
|
-
position: sticky;
|
|
3021
|
-
z-index: 102; /* Above waveform content and spectrogram labels (101), below Docusaurus navbar (200) */
|
|
3022
|
-
left: 0;
|
|
3023
3210
|
height: 100%;
|
|
3024
|
-
flex-shrink: 0;
|
|
3025
|
-
pointer-events: auto;
|
|
3026
|
-
background: ${(props) => props.theme.surfaceColor};
|
|
3027
|
-
transition: background 0.15s ease-in-out;
|
|
3028
|
-
|
|
3029
|
-
/* Selected track: highlighted background */
|
|
3030
|
-
${(props) => props.$isSelected && `
|
|
3031
|
-
background: ${props.theme.selectedTrackControlsBackground};
|
|
3032
|
-
`}
|
|
3033
3211
|
`;
|
|
3034
3212
|
var Track = ({
|
|
3035
3213
|
numChannels,
|
|
@@ -3041,44 +3219,34 @@ var Track = ({
|
|
|
3041
3219
|
hasClipHeaders = false,
|
|
3042
3220
|
onClick,
|
|
3043
3221
|
trackId,
|
|
3044
|
-
isSelected = false
|
|
3222
|
+
isSelected: _isSelected = false
|
|
3045
3223
|
}) => {
|
|
3046
|
-
const {
|
|
3047
|
-
|
|
3048
|
-
controls: { show, width: controlWidth }
|
|
3049
|
-
} = usePlaylistInfo();
|
|
3050
|
-
const controls = useTrackControls();
|
|
3051
|
-
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
3224
|
+
const { waveHeight } = usePlaylistInfo();
|
|
3225
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3052
3226
|
Container,
|
|
3053
3227
|
{
|
|
3054
3228
|
$numChannels: numChannels,
|
|
3055
3229
|
className,
|
|
3056
3230
|
$waveHeight: waveHeight,
|
|
3057
|
-
$controlWidth: show ? controlWidth : 0,
|
|
3058
3231
|
$width: width,
|
|
3059
3232
|
$hasClipHeaders: hasClipHeaders,
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
"data-track-id": trackId,
|
|
3071
|
-
children
|
|
3072
|
-
}
|
|
3073
|
-
)
|
|
3074
|
-
]
|
|
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
|
+
)
|
|
3075
3243
|
}
|
|
3076
3244
|
);
|
|
3077
3245
|
};
|
|
3078
3246
|
|
|
3079
3247
|
// src/components/TrackControls/Button.tsx
|
|
3080
|
-
var
|
|
3081
|
-
var Button =
|
|
3248
|
+
var import_styled_components26 = __toESM(require("styled-components"));
|
|
3249
|
+
var Button = import_styled_components26.default.button.attrs({
|
|
3082
3250
|
type: "button"
|
|
3083
3251
|
})`
|
|
3084
3252
|
display: inline-block;
|
|
@@ -3153,8 +3321,8 @@ var Button = import_styled_components25.default.button.attrs({
|
|
|
3153
3321
|
`;
|
|
3154
3322
|
|
|
3155
3323
|
// src/components/TrackControls/ButtonGroup.tsx
|
|
3156
|
-
var
|
|
3157
|
-
var ButtonGroup =
|
|
3324
|
+
var import_styled_components27 = __toESM(require("styled-components"));
|
|
3325
|
+
var ButtonGroup = import_styled_components27.default.div`
|
|
3158
3326
|
margin-bottom: 0.3rem;
|
|
3159
3327
|
|
|
3160
3328
|
button:not(:first-child) {
|
|
@@ -3169,10 +3337,10 @@ var ButtonGroup = import_styled_components26.default.div`
|
|
|
3169
3337
|
`;
|
|
3170
3338
|
|
|
3171
3339
|
// src/components/TrackControls/CloseButton.tsx
|
|
3172
|
-
var
|
|
3173
|
-
var
|
|
3174
|
-
var
|
|
3175
|
-
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`
|
|
3176
3344
|
position: absolute;
|
|
3177
3345
|
left: 0;
|
|
3178
3346
|
top: 0;
|
|
@@ -3195,11 +3363,11 @@ var StyledCloseButton = import_styled_components27.default.button`
|
|
|
3195
3363
|
color: #dc3545;
|
|
3196
3364
|
}
|
|
3197
3365
|
`;
|
|
3198
|
-
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" }) });
|
|
3199
3367
|
|
|
3200
3368
|
// src/components/TrackControls/Controls.tsx
|
|
3201
|
-
var
|
|
3202
|
-
var Controls =
|
|
3369
|
+
var import_styled_components29 = __toESM(require("styled-components"));
|
|
3370
|
+
var Controls = import_styled_components29.default.div`
|
|
3203
3371
|
background: transparent;
|
|
3204
3372
|
width: 100%;
|
|
3205
3373
|
height: 100%;
|
|
@@ -3215,8 +3383,8 @@ var Controls = import_styled_components28.default.div`
|
|
|
3215
3383
|
`;
|
|
3216
3384
|
|
|
3217
3385
|
// src/components/TrackControls/Header.tsx
|
|
3218
|
-
var
|
|
3219
|
-
var Header =
|
|
3386
|
+
var import_styled_components30 = __toESM(require("styled-components"));
|
|
3387
|
+
var Header = import_styled_components30.default.header`
|
|
3220
3388
|
overflow: hidden;
|
|
3221
3389
|
height: 26px;
|
|
3222
3390
|
width: 100%;
|
|
@@ -3230,28 +3398,28 @@ var Header = import_styled_components29.default.header`
|
|
|
3230
3398
|
`;
|
|
3231
3399
|
|
|
3232
3400
|
// src/components/TrackControls/VolumeDownIcon.tsx
|
|
3233
|
-
var import_react23 = require("@phosphor-icons/react");
|
|
3234
|
-
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
3235
|
-
var VolumeDownIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react23.SpeakerLowIcon, { weight: "light", ...props });
|
|
3236
|
-
|
|
3237
|
-
// src/components/TrackControls/VolumeUpIcon.tsx
|
|
3238
3401
|
var import_react24 = require("@phosphor-icons/react");
|
|
3239
3402
|
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
3240
|
-
var
|
|
3403
|
+
var VolumeDownIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react24.SpeakerLowIcon, { weight: "light", ...props });
|
|
3241
3404
|
|
|
3242
|
-
// src/components/TrackControls/
|
|
3405
|
+
// src/components/TrackControls/VolumeUpIcon.tsx
|
|
3243
3406
|
var import_react25 = require("@phosphor-icons/react");
|
|
3244
3407
|
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
3245
|
-
var
|
|
3408
|
+
var VolumeUpIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react25.SpeakerHighIcon, { weight: "light", ...props });
|
|
3246
3409
|
|
|
3247
|
-
// src/components/TrackControls/
|
|
3410
|
+
// src/components/TrackControls/TrashIcon.tsx
|
|
3248
3411
|
var import_react26 = require("@phosphor-icons/react");
|
|
3249
3412
|
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
3250
|
-
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 });
|
|
3251
3419
|
|
|
3252
3420
|
// src/components/TrackControls/Slider.tsx
|
|
3253
|
-
var
|
|
3254
|
-
var Slider = (0,
|
|
3421
|
+
var import_styled_components31 = __toESM(require("styled-components"));
|
|
3422
|
+
var Slider = (0, import_styled_components31.default)(BaseSlider)`
|
|
3255
3423
|
width: 75%;
|
|
3256
3424
|
height: 5px;
|
|
3257
3425
|
background: ${(props) => props.theme.sliderTrackColor};
|
|
@@ -3303,8 +3471,8 @@ var Slider = (0, import_styled_components30.default)(BaseSlider)`
|
|
|
3303
3471
|
`;
|
|
3304
3472
|
|
|
3305
3473
|
// src/components/TrackControls/SliderWrapper.tsx
|
|
3306
|
-
var
|
|
3307
|
-
var SliderWrapper =
|
|
3474
|
+
var import_styled_components32 = __toESM(require("styled-components"));
|
|
3475
|
+
var SliderWrapper = import_styled_components32.default.label`
|
|
3308
3476
|
width: 100%;
|
|
3309
3477
|
display: flex;
|
|
3310
3478
|
justify-content: space-between;
|
|
@@ -3315,15 +3483,15 @@ var SliderWrapper = import_styled_components31.default.label`
|
|
|
3315
3483
|
`;
|
|
3316
3484
|
|
|
3317
3485
|
// src/components/TrackMenu.tsx
|
|
3318
|
-
var
|
|
3486
|
+
var import_react28 = __toESM(require("react"));
|
|
3319
3487
|
var import_react_dom = require("react-dom");
|
|
3320
|
-
var
|
|
3321
|
-
var
|
|
3322
|
-
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`
|
|
3323
3491
|
position: relative;
|
|
3324
3492
|
display: inline-block;
|
|
3325
3493
|
`;
|
|
3326
|
-
var MenuButton =
|
|
3494
|
+
var MenuButton = import_styled_components33.default.button`
|
|
3327
3495
|
background: none;
|
|
3328
3496
|
border: none;
|
|
3329
3497
|
cursor: pointer;
|
|
@@ -3338,7 +3506,8 @@ var MenuButton = import_styled_components32.default.button`
|
|
|
3338
3506
|
opacity: 1;
|
|
3339
3507
|
}
|
|
3340
3508
|
`;
|
|
3341
|
-
var
|
|
3509
|
+
var DROPDOWN_MIN_WIDTH = 180;
|
|
3510
|
+
var Dropdown = import_styled_components33.default.div`
|
|
3342
3511
|
position: fixed;
|
|
3343
3512
|
top: ${(p) => p.$top}px;
|
|
3344
3513
|
left: ${(p) => p.$left}px;
|
|
@@ -3348,31 +3517,53 @@ var Dropdown = import_styled_components32.default.div`
|
|
|
3348
3517
|
border: 1px solid rgba(128, 128, 128, 0.4);
|
|
3349
3518
|
border-radius: 6px;
|
|
3350
3519
|
padding: 0.5rem 0;
|
|
3351
|
-
min-width:
|
|
3520
|
+
min-width: ${DROPDOWN_MIN_WIDTH}px;
|
|
3352
3521
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
3353
3522
|
`;
|
|
3354
|
-
var Divider =
|
|
3523
|
+
var Divider = import_styled_components33.default.hr`
|
|
3355
3524
|
border: none;
|
|
3356
3525
|
border-top: 1px solid rgba(128, 128, 128, 0.3);
|
|
3357
3526
|
margin: 0.35rem 0;
|
|
3358
3527
|
`;
|
|
3359
3528
|
var TrackMenu = ({ items: itemsProp }) => {
|
|
3360
|
-
const [open, setOpen] = (0,
|
|
3361
|
-
const close = () => setOpen(false);
|
|
3529
|
+
const [open, setOpen] = (0, import_react28.useState)(false);
|
|
3530
|
+
const close = (0, import_react28.useCallback)(() => setOpen(false), []);
|
|
3362
3531
|
const items = typeof itemsProp === "function" ? itemsProp(close) : itemsProp;
|
|
3363
|
-
const [dropdownPos, setDropdownPos] = (0,
|
|
3364
|
-
const buttonRef = (0,
|
|
3365
|
-
const dropdownRef = (0,
|
|
3366
|
-
(0,
|
|
3367
|
-
if (
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
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;
|
|
3373
3544
|
}
|
|
3374
|
-
|
|
3375
|
-
|
|
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)(() => {
|
|
3376
3567
|
if (!open) return;
|
|
3377
3568
|
const handleClick = (e) => {
|
|
3378
3569
|
const target = e.target;
|
|
@@ -3380,11 +3571,20 @@ var TrackMenu = ({ items: itemsProp }) => {
|
|
|
3380
3571
|
setOpen(false);
|
|
3381
3572
|
}
|
|
3382
3573
|
};
|
|
3574
|
+
const handleKeyDown = (e) => {
|
|
3575
|
+
if (e.key === "Escape") {
|
|
3576
|
+
setOpen(false);
|
|
3577
|
+
}
|
|
3578
|
+
};
|
|
3383
3579
|
document.addEventListener("mousedown", handleClick);
|
|
3384
|
-
|
|
3580
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
3581
|
+
return () => {
|
|
3582
|
+
document.removeEventListener("mousedown", handleClick);
|
|
3583
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
3584
|
+
};
|
|
3385
3585
|
}, [open]);
|
|
3386
|
-
return /* @__PURE__ */ (0,
|
|
3387
|
-
/* @__PURE__ */ (0,
|
|
3586
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(MenuContainer, { children: [
|
|
3587
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3388
3588
|
MenuButton,
|
|
3389
3589
|
{
|
|
3390
3590
|
ref: buttonRef,
|
|
@@ -3395,19 +3595,19 @@ var TrackMenu = ({ items: itemsProp }) => {
|
|
|
3395
3595
|
onMouseDown: (e) => e.stopPropagation(),
|
|
3396
3596
|
title: "Track menu",
|
|
3397
3597
|
"aria-label": "Track menu",
|
|
3398
|
-
children: /* @__PURE__ */ (0,
|
|
3598
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(DotsIcon, { size: 16 })
|
|
3399
3599
|
}
|
|
3400
3600
|
),
|
|
3401
3601
|
open && typeof document !== "undefined" && (0, import_react_dom.createPortal)(
|
|
3402
|
-
/* @__PURE__ */ (0,
|
|
3602
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3403
3603
|
Dropdown,
|
|
3404
3604
|
{
|
|
3405
3605
|
ref: dropdownRef,
|
|
3406
3606
|
$top: dropdownPos.top,
|
|
3407
3607
|
$left: dropdownPos.left,
|
|
3408
3608
|
onMouseDown: (e) => e.stopPropagation(),
|
|
3409
|
-
children: items.map((item, index) => /* @__PURE__ */ (0,
|
|
3410
|
-
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, {}),
|
|
3411
3611
|
item.content
|
|
3412
3612
|
] }, item.id))
|
|
3413
3613
|
}
|
|
@@ -3450,6 +3650,7 @@ var TrackMenu = ({ items: itemsProp }) => {
|
|
|
3450
3650
|
LoopRegion,
|
|
3451
3651
|
LoopRegionMarkers,
|
|
3452
3652
|
MasterVolumeControl,
|
|
3653
|
+
PianoRollChannel,
|
|
3453
3654
|
Playhead,
|
|
3454
3655
|
PlayheadWithMarker,
|
|
3455
3656
|
Playlist,
|