@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.mjs
CHANGED
|
@@ -334,7 +334,7 @@ var AutomaticScrollCheckbox = ({
|
|
|
334
334
|
};
|
|
335
335
|
|
|
336
336
|
// src/components/Channel.tsx
|
|
337
|
-
import {
|
|
337
|
+
import { useEffect as useEffect3 } from "react";
|
|
338
338
|
import styled9 from "styled-components";
|
|
339
339
|
|
|
340
340
|
// src/wfpl-theme.ts
|
|
@@ -410,6 +410,10 @@ var defaultTheme = {
|
|
|
410
410
|
annotationResizeHandleColor: "rgba(0, 0, 0, 0.4)",
|
|
411
411
|
annotationResizeHandleActiveColor: "rgba(0, 0, 0, 0.8)",
|
|
412
412
|
annotationTextItemHoverBackground: "rgba(0, 0, 0, 0.03)",
|
|
413
|
+
// Piano roll colors
|
|
414
|
+
pianoRollNoteColor: "#2a7070",
|
|
415
|
+
pianoRollSelectedNoteColor: "#3d9e9e",
|
|
416
|
+
pianoRollBackgroundColor: "#1a1a2e",
|
|
413
417
|
// Spacing and sizing
|
|
414
418
|
borderRadius: "4px",
|
|
415
419
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif',
|
|
@@ -487,6 +491,10 @@ var darkTheme = {
|
|
|
487
491
|
annotationResizeHandleColor: "rgba(200, 160, 120, 0.5)",
|
|
488
492
|
annotationResizeHandleActiveColor: "rgba(220, 180, 140, 0.8)",
|
|
489
493
|
annotationTextItemHoverBackground: "rgba(200, 160, 120, 0.08)",
|
|
494
|
+
// Piano roll colors
|
|
495
|
+
pianoRollNoteColor: "#c49a6c",
|
|
496
|
+
pianoRollSelectedNoteColor: "#e8c090",
|
|
497
|
+
pianoRollBackgroundColor: "#0d0d14",
|
|
490
498
|
// Spacing and sizing
|
|
491
499
|
borderRadius: "4px",
|
|
492
500
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif',
|
|
@@ -499,6 +507,7 @@ import {
|
|
|
499
507
|
createContext,
|
|
500
508
|
useContext,
|
|
501
509
|
useEffect,
|
|
510
|
+
useLayoutEffect,
|
|
502
511
|
useCallback,
|
|
503
512
|
useMemo,
|
|
504
513
|
useRef,
|
|
@@ -506,19 +515,32 @@ import {
|
|
|
506
515
|
} from "react";
|
|
507
516
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
508
517
|
var ViewportStore = class {
|
|
509
|
-
constructor() {
|
|
510
|
-
this._state = null;
|
|
518
|
+
constructor(containerEl) {
|
|
511
519
|
this._listeners = /* @__PURE__ */ new Set();
|
|
520
|
+
this._notifyRafId = null;
|
|
512
521
|
this.subscribe = (callback) => {
|
|
513
522
|
this._listeners.add(callback);
|
|
514
523
|
return () => this._listeners.delete(callback);
|
|
515
524
|
};
|
|
516
525
|
this.getSnapshot = () => this._state;
|
|
526
|
+
const width = containerEl?.clientWidth ?? (typeof window !== "undefined" ? window.innerWidth : 1024);
|
|
527
|
+
const buffer = width * 1.5;
|
|
528
|
+
this._state = {
|
|
529
|
+
scrollLeft: 0,
|
|
530
|
+
containerWidth: width,
|
|
531
|
+
visibleStart: 0,
|
|
532
|
+
visibleEnd: width + buffer
|
|
533
|
+
};
|
|
517
534
|
}
|
|
518
535
|
/**
|
|
519
536
|
* Update viewport state. Applies a 100px scroll threshold to skip updates
|
|
520
537
|
* that don't affect chunk visibility (1000px chunks with 1.5× overscan buffer).
|
|
521
538
|
* Only notifies listeners when the state actually changes.
|
|
539
|
+
*
|
|
540
|
+
* Listener notification is deferred by one frame via requestAnimationFrame
|
|
541
|
+
* to avoid conflicting with React 19's concurrent rendering. When React
|
|
542
|
+
* time-slices a render across frames, synchronous useSyncExternalStore
|
|
543
|
+
* notifications can trigger "Should not already be working" errors.
|
|
522
544
|
*/
|
|
523
545
|
update(scrollLeft, containerWidth) {
|
|
524
546
|
const buffer = containerWidth * 1.5;
|
|
@@ -528,8 +550,19 @@ var ViewportStore = class {
|
|
|
528
550
|
return;
|
|
529
551
|
}
|
|
530
552
|
this._state = { scrollLeft, containerWidth, visibleStart, visibleEnd };
|
|
531
|
-
|
|
532
|
-
|
|
553
|
+
if (this._notifyRafId === null) {
|
|
554
|
+
this._notifyRafId = requestAnimationFrame(() => {
|
|
555
|
+
this._notifyRafId = null;
|
|
556
|
+
for (const listener of this._listeners) {
|
|
557
|
+
listener();
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
cancelPendingNotification() {
|
|
563
|
+
if (this._notifyRafId !== null) {
|
|
564
|
+
cancelAnimationFrame(this._notifyRafId);
|
|
565
|
+
this._notifyRafId = null;
|
|
533
566
|
}
|
|
534
567
|
}
|
|
535
568
|
};
|
|
@@ -540,7 +573,7 @@ var NULL_SNAPSHOT = () => null;
|
|
|
540
573
|
var ScrollViewportProvider = ({ containerRef, children }) => {
|
|
541
574
|
const storeRef = useRef(null);
|
|
542
575
|
if (storeRef.current === null) {
|
|
543
|
-
storeRef.current = new ViewportStore();
|
|
576
|
+
storeRef.current = new ViewportStore(containerRef.current);
|
|
544
577
|
}
|
|
545
578
|
const store = storeRef.current;
|
|
546
579
|
const rafIdRef = useRef(null);
|
|
@@ -556,10 +589,12 @@ var ScrollViewportProvider = ({ containerRef, children }) => {
|
|
|
556
589
|
measure();
|
|
557
590
|
});
|
|
558
591
|
}, [measure]);
|
|
592
|
+
useLayoutEffect(() => {
|
|
593
|
+
measure();
|
|
594
|
+
}, [measure]);
|
|
559
595
|
useEffect(() => {
|
|
560
596
|
const el = containerRef.current;
|
|
561
597
|
if (!el) return;
|
|
562
|
-
measure();
|
|
563
598
|
el.addEventListener("scroll", scheduleUpdate, { passive: true });
|
|
564
599
|
const resizeObserver = new ResizeObserver(() => {
|
|
565
600
|
scheduleUpdate();
|
|
@@ -572,8 +607,9 @@ var ScrollViewportProvider = ({ containerRef, children }) => {
|
|
|
572
607
|
cancelAnimationFrame(rafIdRef.current);
|
|
573
608
|
rafIdRef.current = null;
|
|
574
609
|
}
|
|
610
|
+
store.cancelPendingNotification();
|
|
575
611
|
};
|
|
576
|
-
}, [containerRef,
|
|
612
|
+
}, [containerRef, scheduleUpdate, store]);
|
|
577
613
|
return /* @__PURE__ */ jsx3(ViewportStoreContext.Provider, { value: store, children });
|
|
578
614
|
};
|
|
579
615
|
var useScrollViewport = () => {
|
|
@@ -708,8 +744,6 @@ var Waveform = styled9.canvas.attrs((props) => ({
|
|
|
708
744
|
}))`
|
|
709
745
|
position: absolute;
|
|
710
746
|
top: 0;
|
|
711
|
-
/* Promote to own compositing layer for smoother scrolling */
|
|
712
|
-
will-change: transform;
|
|
713
747
|
/* Disable image rendering interpolation */
|
|
714
748
|
image-rendering: pixelated;
|
|
715
749
|
image-rendering: crisp-edges;
|
|
@@ -746,7 +780,8 @@ var Channel = (props) => {
|
|
|
746
780
|
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
747
781
|
const clipOriginX = useClipViewportOrigin();
|
|
748
782
|
const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH, clipOriginX);
|
|
749
|
-
|
|
783
|
+
useEffect3(() => {
|
|
784
|
+
const tDraw = performance.now();
|
|
750
785
|
const step = barWidth + barGap;
|
|
751
786
|
for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
|
|
752
787
|
const globalPixelOffset = canvasIdx * MAX_CANVAS_WIDTH;
|
|
@@ -782,6 +817,9 @@ var Channel = (props) => {
|
|
|
782
817
|
}
|
|
783
818
|
}
|
|
784
819
|
}
|
|
820
|
+
console.log(
|
|
821
|
+
`[waveform] draw ch${index}: ${canvasMapRef.current.size} chunks, ${(performance.now() - tDraw).toFixed(1)}ms`
|
|
822
|
+
);
|
|
785
823
|
}, [
|
|
786
824
|
canvasMapRef,
|
|
787
825
|
data,
|
|
@@ -794,7 +832,8 @@ var Channel = (props) => {
|
|
|
794
832
|
barWidth,
|
|
795
833
|
barGap,
|
|
796
834
|
drawMode,
|
|
797
|
-
visibleChunkIndices
|
|
835
|
+
visibleChunkIndices,
|
|
836
|
+
index
|
|
798
837
|
]);
|
|
799
838
|
const waveforms = visibleChunkIndices.map((i) => {
|
|
800
839
|
const chunkLeft = i * MAX_CANVAS_WIDTH;
|
|
@@ -912,7 +951,7 @@ var ClipHeaderPresentational = ({
|
|
|
912
951
|
trackName,
|
|
913
952
|
isSelected = false
|
|
914
953
|
}) => {
|
|
915
|
-
return /* @__PURE__ */ jsx7(HeaderContainer, { $interactive: false, $isSelected: isSelected, children: /* @__PURE__ */ jsx7(TrackName, { children: trackName }) });
|
|
954
|
+
return /* @__PURE__ */ jsx7(HeaderContainer, { $interactive: false, $isSelected: isSelected, children: /* @__PURE__ */ jsx7(TrackName, { title: trackName, children: trackName }) });
|
|
916
955
|
};
|
|
917
956
|
var ClipHeader = ({
|
|
918
957
|
clipId,
|
|
@@ -934,7 +973,7 @@ var ClipHeader = ({
|
|
|
934
973
|
"data-clip-id": clipId,
|
|
935
974
|
$interactive: true,
|
|
936
975
|
$isSelected: isSelected,
|
|
937
|
-
children: /* @__PURE__ */ jsx7(TrackName, { children: trackName })
|
|
976
|
+
children: /* @__PURE__ */ jsx7(TrackName, { title: trackName, children: trackName })
|
|
938
977
|
}
|
|
939
978
|
);
|
|
940
979
|
};
|
|
@@ -1155,6 +1194,7 @@ var Clip = ({
|
|
|
1155
1194
|
"data-clip-container": "true",
|
|
1156
1195
|
"data-track-id": trackId,
|
|
1157
1196
|
onMouseDown,
|
|
1197
|
+
tabIndex: -1,
|
|
1158
1198
|
children: [
|
|
1159
1199
|
showHeader && /* @__PURE__ */ jsx10(
|
|
1160
1200
|
ClipHeader,
|
|
@@ -1264,11 +1304,140 @@ var MasterVolumeControl = ({
|
|
|
1264
1304
|
] });
|
|
1265
1305
|
};
|
|
1266
1306
|
|
|
1267
|
-
// src/components/
|
|
1268
|
-
import {
|
|
1307
|
+
// src/components/PianoRollChannel.tsx
|
|
1308
|
+
import { useEffect as useEffect4, useMemo as useMemo2 } from "react";
|
|
1269
1309
|
import styled15 from "styled-components";
|
|
1270
|
-
import {
|
|
1271
|
-
|
|
1310
|
+
import { MAX_CANVAS_WIDTH as MAX_CANVAS_WIDTH2 } from "@waveform-playlist/core";
|
|
1311
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1312
|
+
var NoteCanvas = styled15.canvas.attrs((props) => ({
|
|
1313
|
+
style: {
|
|
1314
|
+
width: `${props.$cssWidth}px`,
|
|
1315
|
+
height: `${props.$waveHeight}px`,
|
|
1316
|
+
left: `${props.$left}px`
|
|
1317
|
+
}
|
|
1318
|
+
}))`
|
|
1319
|
+
position: absolute;
|
|
1320
|
+
top: 0;
|
|
1321
|
+
image-rendering: pixelated;
|
|
1322
|
+
image-rendering: crisp-edges;
|
|
1323
|
+
`;
|
|
1324
|
+
var Wrapper2 = styled15.div.attrs((props) => ({
|
|
1325
|
+
style: {
|
|
1326
|
+
top: `${props.$waveHeight * props.$index}px`,
|
|
1327
|
+
width: `${props.$cssWidth}px`,
|
|
1328
|
+
height: `${props.$waveHeight}px`
|
|
1329
|
+
}
|
|
1330
|
+
}))`
|
|
1331
|
+
position: absolute;
|
|
1332
|
+
background: ${(props) => props.$backgroundColor};
|
|
1333
|
+
transform: translateZ(0);
|
|
1334
|
+
backface-visibility: hidden;
|
|
1335
|
+
`;
|
|
1336
|
+
var PianoRollChannel = ({
|
|
1337
|
+
index,
|
|
1338
|
+
midiNotes,
|
|
1339
|
+
length,
|
|
1340
|
+
waveHeight,
|
|
1341
|
+
devicePixelRatio,
|
|
1342
|
+
samplesPerPixel,
|
|
1343
|
+
sampleRate,
|
|
1344
|
+
clipOffsetSeconds,
|
|
1345
|
+
noteColor = "#2a7070",
|
|
1346
|
+
selectedNoteColor = "#3d9e9e",
|
|
1347
|
+
isSelected = false,
|
|
1348
|
+
transparentBackground = false,
|
|
1349
|
+
backgroundColor = "#1a1a2e"
|
|
1350
|
+
}) => {
|
|
1351
|
+
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
1352
|
+
const clipOriginX = useClipViewportOrigin();
|
|
1353
|
+
const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH2, clipOriginX);
|
|
1354
|
+
const { minMidi, maxMidi } = useMemo2(() => {
|
|
1355
|
+
if (midiNotes.length === 0) return { minMidi: 0, maxMidi: 127 };
|
|
1356
|
+
let min = 127, max = 0;
|
|
1357
|
+
for (const note of midiNotes) {
|
|
1358
|
+
if (note.midi < min) min = note.midi;
|
|
1359
|
+
if (note.midi > max) max = note.midi;
|
|
1360
|
+
}
|
|
1361
|
+
return { minMidi: Math.max(0, min - 1), maxMidi: Math.min(127, max + 1) };
|
|
1362
|
+
}, [midiNotes]);
|
|
1363
|
+
const color = isSelected ? selectedNoteColor : noteColor;
|
|
1364
|
+
useEffect4(() => {
|
|
1365
|
+
const tDraw = performance.now();
|
|
1366
|
+
const noteRange = maxMidi - minMidi + 1;
|
|
1367
|
+
const noteHeight = Math.max(2, waveHeight / noteRange);
|
|
1368
|
+
const pixelsPerSecond = sampleRate / samplesPerPixel;
|
|
1369
|
+
for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
|
|
1370
|
+
const chunkPixelStart = canvasIdx * MAX_CANVAS_WIDTH2;
|
|
1371
|
+
const canvasWidth = canvas.width / devicePixelRatio;
|
|
1372
|
+
const ctx = canvas.getContext("2d");
|
|
1373
|
+
if (!ctx) continue;
|
|
1374
|
+
ctx.resetTransform();
|
|
1375
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1376
|
+
ctx.imageSmoothingEnabled = false;
|
|
1377
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
1378
|
+
const chunkStartTime = chunkPixelStart * samplesPerPixel / sampleRate;
|
|
1379
|
+
const chunkEndTime = (chunkPixelStart + canvasWidth) * samplesPerPixel / sampleRate;
|
|
1380
|
+
for (const note of midiNotes) {
|
|
1381
|
+
const noteStart = note.time - clipOffsetSeconds;
|
|
1382
|
+
const noteEnd = noteStart + note.duration;
|
|
1383
|
+
if (noteEnd <= chunkStartTime || noteStart >= chunkEndTime) continue;
|
|
1384
|
+
const x = noteStart * pixelsPerSecond - chunkPixelStart;
|
|
1385
|
+
const w = Math.max(2, note.duration * pixelsPerSecond);
|
|
1386
|
+
const y = (maxMidi - note.midi) / noteRange * waveHeight;
|
|
1387
|
+
const alpha = 0.3 + note.velocity * 0.7;
|
|
1388
|
+
ctx.fillStyle = color;
|
|
1389
|
+
ctx.globalAlpha = alpha;
|
|
1390
|
+
const r = 1;
|
|
1391
|
+
ctx.beginPath();
|
|
1392
|
+
ctx.roundRect(x, y, w, noteHeight, r);
|
|
1393
|
+
ctx.fill();
|
|
1394
|
+
}
|
|
1395
|
+
ctx.globalAlpha = 1;
|
|
1396
|
+
}
|
|
1397
|
+
console.log(
|
|
1398
|
+
`[piano-roll] draw ch${index}: ${canvasMapRef.current.size} chunks, ${midiNotes.length} notes, ${(performance.now() - tDraw).toFixed(1)}ms`
|
|
1399
|
+
);
|
|
1400
|
+
}, [
|
|
1401
|
+
canvasMapRef,
|
|
1402
|
+
midiNotes,
|
|
1403
|
+
waveHeight,
|
|
1404
|
+
devicePixelRatio,
|
|
1405
|
+
samplesPerPixel,
|
|
1406
|
+
sampleRate,
|
|
1407
|
+
clipOffsetSeconds,
|
|
1408
|
+
color,
|
|
1409
|
+
minMidi,
|
|
1410
|
+
maxMidi,
|
|
1411
|
+
length,
|
|
1412
|
+
visibleChunkIndices,
|
|
1413
|
+
index
|
|
1414
|
+
]);
|
|
1415
|
+
const canvases = visibleChunkIndices.map((i) => {
|
|
1416
|
+
const chunkLeft = i * MAX_CANVAS_WIDTH2;
|
|
1417
|
+
const currentWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH2);
|
|
1418
|
+
return /* @__PURE__ */ jsx12(
|
|
1419
|
+
NoteCanvas,
|
|
1420
|
+
{
|
|
1421
|
+
$cssWidth: currentWidth,
|
|
1422
|
+
$left: chunkLeft,
|
|
1423
|
+
width: currentWidth * devicePixelRatio,
|
|
1424
|
+
height: waveHeight * devicePixelRatio,
|
|
1425
|
+
$waveHeight: waveHeight,
|
|
1426
|
+
"data-index": i,
|
|
1427
|
+
ref: canvasRef
|
|
1428
|
+
},
|
|
1429
|
+
`${length}-${i}`
|
|
1430
|
+
);
|
|
1431
|
+
});
|
|
1432
|
+
const bgColor = transparentBackground ? "transparent" : backgroundColor;
|
|
1433
|
+
return /* @__PURE__ */ jsx12(Wrapper2, { $index: index, $cssWidth: length, $waveHeight: waveHeight, $backgroundColor: bgColor, children: canvases });
|
|
1434
|
+
};
|
|
1435
|
+
|
|
1436
|
+
// src/components/Playhead.tsx
|
|
1437
|
+
import { useRef as useRef3, useEffect as useEffect5 } from "react";
|
|
1438
|
+
import styled16 from "styled-components";
|
|
1439
|
+
import { jsx as jsx13, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1440
|
+
var PlayheadLine = styled16.div.attrs((props) => ({
|
|
1272
1441
|
style: {
|
|
1273
1442
|
transform: `translate3d(${props.$position}px, 0, 0)`
|
|
1274
1443
|
}
|
|
@@ -1284,9 +1453,9 @@ var PlayheadLine = styled15.div.attrs((props) => ({
|
|
|
1284
1453
|
will-change: transform;
|
|
1285
1454
|
`;
|
|
1286
1455
|
var Playhead = ({ position, color = "#ff0000" }) => {
|
|
1287
|
-
return /* @__PURE__ */
|
|
1456
|
+
return /* @__PURE__ */ jsx13(PlayheadLine, { $position: position, $color: color });
|
|
1288
1457
|
};
|
|
1289
|
-
var PlayheadWithMarkerContainer =
|
|
1458
|
+
var PlayheadWithMarkerContainer = styled16.div`
|
|
1290
1459
|
position: absolute;
|
|
1291
1460
|
top: 0;
|
|
1292
1461
|
left: 0;
|
|
@@ -1295,7 +1464,7 @@ var PlayheadWithMarkerContainer = styled15.div`
|
|
|
1295
1464
|
pointer-events: none;
|
|
1296
1465
|
will-change: transform;
|
|
1297
1466
|
`;
|
|
1298
|
-
var MarkerTriangle =
|
|
1467
|
+
var MarkerTriangle = styled16.div`
|
|
1299
1468
|
position: absolute;
|
|
1300
1469
|
top: -10px;
|
|
1301
1470
|
left: -6px;
|
|
@@ -1305,7 +1474,7 @@ var MarkerTriangle = styled15.div`
|
|
|
1305
1474
|
border-right: 7px solid transparent;
|
|
1306
1475
|
border-top: 10px solid ${(props) => props.$color};
|
|
1307
1476
|
`;
|
|
1308
|
-
var MarkerLine =
|
|
1477
|
+
var MarkerLine = styled16.div`
|
|
1309
1478
|
position: absolute;
|
|
1310
1479
|
top: 0;
|
|
1311
1480
|
left: 0;
|
|
@@ -1321,13 +1490,13 @@ var PlayheadWithMarker = ({
|
|
|
1321
1490
|
audioStartPositionRef,
|
|
1322
1491
|
samplesPerPixel,
|
|
1323
1492
|
sampleRate,
|
|
1324
|
-
controlsOffset,
|
|
1493
|
+
controlsOffset = 0,
|
|
1325
1494
|
getAudioContextTime,
|
|
1326
1495
|
getPlaybackTime
|
|
1327
1496
|
}) => {
|
|
1328
1497
|
const containerRef = useRef3(null);
|
|
1329
1498
|
const animationFrameRef = useRef3(null);
|
|
1330
|
-
|
|
1499
|
+
useEffect5(() => {
|
|
1331
1500
|
const updatePosition = () => {
|
|
1332
1501
|
if (containerRef.current) {
|
|
1333
1502
|
let time;
|
|
@@ -1372,7 +1541,7 @@ var PlayheadWithMarker = ({
|
|
|
1372
1541
|
getAudioContextTime,
|
|
1373
1542
|
getPlaybackTime
|
|
1374
1543
|
]);
|
|
1375
|
-
|
|
1544
|
+
useEffect5(() => {
|
|
1376
1545
|
if (!isPlaying && containerRef.current) {
|
|
1377
1546
|
const time = currentTimeRef.current ?? 0;
|
|
1378
1547
|
const pos = time * sampleRate / samplesPerPixel + controlsOffset;
|
|
@@ -1380,27 +1549,43 @@ var PlayheadWithMarker = ({
|
|
|
1380
1549
|
}
|
|
1381
1550
|
});
|
|
1382
1551
|
return /* @__PURE__ */ jsxs4(PlayheadWithMarkerContainer, { ref: containerRef, $color: color, children: [
|
|
1383
|
-
/* @__PURE__ */
|
|
1384
|
-
/* @__PURE__ */
|
|
1552
|
+
/* @__PURE__ */ jsx13(MarkerTriangle, { $color: color }),
|
|
1553
|
+
/* @__PURE__ */ jsx13(MarkerLine, { $color: color })
|
|
1385
1554
|
] });
|
|
1386
1555
|
};
|
|
1387
1556
|
|
|
1388
1557
|
// src/components/Playlist.tsx
|
|
1389
|
-
import
|
|
1558
|
+
import styled17, { withTheme } from "styled-components";
|
|
1390
1559
|
import { useRef as useRef4, useCallback as useCallback3 } from "react";
|
|
1391
|
-
import { jsx as
|
|
1392
|
-
var
|
|
1560
|
+
import { jsx as jsx14, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1561
|
+
var Wrapper3 = styled17.div`
|
|
1562
|
+
display: flex;
|
|
1393
1563
|
overflow-y: hidden;
|
|
1564
|
+
position: relative;
|
|
1565
|
+
`;
|
|
1566
|
+
var ControlsColumn = styled17.div.attrs((props) => ({
|
|
1567
|
+
style: { width: `${props.$width}px` }
|
|
1568
|
+
}))`
|
|
1569
|
+
flex-shrink: 0;
|
|
1570
|
+
overflow: hidden;
|
|
1571
|
+
`;
|
|
1572
|
+
var TimescaleGap = styled17.div.attrs((props) => ({
|
|
1573
|
+
style: { height: `${props.$height}px` }
|
|
1574
|
+
}))``;
|
|
1575
|
+
var ScrollArea = styled17.div`
|
|
1394
1576
|
overflow-x: auto;
|
|
1577
|
+
overflow-y: hidden;
|
|
1578
|
+
overflow-anchor: none;
|
|
1579
|
+
flex: 1;
|
|
1395
1580
|
position: relative;
|
|
1396
1581
|
`;
|
|
1397
|
-
var
|
|
1582
|
+
var ScrollContainerInner = styled17.div.attrs((props) => ({
|
|
1398
1583
|
style: props.$width !== void 0 ? { width: `${props.$width}px` } : {}
|
|
1399
1584
|
}))`
|
|
1400
1585
|
position: relative;
|
|
1401
1586
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
1402
1587
|
`;
|
|
1403
|
-
var TimescaleWrapper =
|
|
1588
|
+
var TimescaleWrapper = styled17.div.attrs((props) => ({
|
|
1404
1589
|
style: props.$width ? { minWidth: `${props.$width}px` } : {}
|
|
1405
1590
|
}))`
|
|
1406
1591
|
background: ${(props) => props.$backgroundColor || "white"};
|
|
@@ -1408,14 +1593,14 @@ var TimescaleWrapper = styled16.div.attrs((props) => ({
|
|
|
1408
1593
|
position: relative;
|
|
1409
1594
|
overflow: hidden; /* Constrain loop region to timescale area */
|
|
1410
1595
|
`;
|
|
1411
|
-
var TracksContainer =
|
|
1596
|
+
var TracksContainer = styled17.div.attrs((props) => ({
|
|
1412
1597
|
style: props.$width !== void 0 ? { minWidth: `${props.$width}px` } : {}
|
|
1413
1598
|
}))`
|
|
1414
1599
|
position: relative;
|
|
1415
1600
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
1416
1601
|
width: 100%;
|
|
1417
1602
|
`;
|
|
1418
|
-
var ClickOverlay =
|
|
1603
|
+
var ClickOverlay = styled17.div`
|
|
1419
1604
|
position: absolute;
|
|
1420
1605
|
top: 0;
|
|
1421
1606
|
left: 0;
|
|
@@ -1432,7 +1617,6 @@ var Playlist = ({
|
|
|
1432
1617
|
timescale,
|
|
1433
1618
|
timescaleWidth,
|
|
1434
1619
|
tracksWidth,
|
|
1435
|
-
scrollContainerWidth,
|
|
1436
1620
|
controlsWidth,
|
|
1437
1621
|
onTracksClick,
|
|
1438
1622
|
onTracksMouseDown,
|
|
@@ -1440,40 +1624,48 @@ var Playlist = ({
|
|
|
1440
1624
|
onTracksMouseUp,
|
|
1441
1625
|
scrollContainerRef,
|
|
1442
1626
|
isSelecting,
|
|
1443
|
-
"data-playlist-state": playlistState
|
|
1627
|
+
"data-playlist-state": playlistState,
|
|
1628
|
+
trackControlsSlots,
|
|
1629
|
+
timescaleGapHeight = 0
|
|
1444
1630
|
}) => {
|
|
1445
|
-
const
|
|
1631
|
+
const scrollAreaRef = useRef4(null);
|
|
1446
1632
|
const handleRef = useCallback3(
|
|
1447
1633
|
(el) => {
|
|
1448
|
-
|
|
1634
|
+
scrollAreaRef.current = el;
|
|
1449
1635
|
scrollContainerRef?.(el);
|
|
1450
1636
|
},
|
|
1451
1637
|
[scrollContainerRef]
|
|
1452
1638
|
);
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
/* @__PURE__ */ jsxs5(
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1639
|
+
const showControls = controlsWidth !== void 0 && controlsWidth > 0;
|
|
1640
|
+
return /* @__PURE__ */ jsxs5(Wrapper3, { "data-playlist-state": playlistState, children: [
|
|
1641
|
+
showControls && /* @__PURE__ */ jsxs5(ControlsColumn, { $width: controlsWidth, children: [
|
|
1642
|
+
timescaleGapHeight > 0 && /* @__PURE__ */ jsx14(TimescaleGap, { $height: timescaleGapHeight }),
|
|
1643
|
+
trackControlsSlots
|
|
1644
|
+
] }),
|
|
1645
|
+
/* @__PURE__ */ jsx14(ScrollArea, { "data-scroll-container": "true", ref: handleRef, children: /* @__PURE__ */ jsx14(ScrollViewportProvider, { containerRef: scrollAreaRef, children: /* @__PURE__ */ jsxs5(ScrollContainerInner, { $backgroundColor: backgroundColor, $width: tracksWidth, children: [
|
|
1646
|
+
timescale && /* @__PURE__ */ jsx14(TimescaleWrapper, { $width: timescaleWidth, $backgroundColor: timescaleBackgroundColor, children: timescale }),
|
|
1647
|
+
/* @__PURE__ */ jsxs5(TracksContainer, { $width: tracksWidth, $backgroundColor: backgroundColor, children: [
|
|
1648
|
+
children,
|
|
1649
|
+
(onTracksClick || onTracksMouseDown) && /* @__PURE__ */ jsx14(
|
|
1650
|
+
ClickOverlay,
|
|
1651
|
+
{
|
|
1652
|
+
$isSelecting: isSelecting,
|
|
1653
|
+
onClick: onTracksClick,
|
|
1654
|
+
onMouseDown: onTracksMouseDown,
|
|
1655
|
+
onMouseMove: onTracksMouseMove,
|
|
1656
|
+
onMouseUp: onTracksMouseUp
|
|
1657
|
+
}
|
|
1658
|
+
)
|
|
1659
|
+
] })
|
|
1660
|
+
] }) }) })
|
|
1661
|
+
] });
|
|
1470
1662
|
};
|
|
1471
1663
|
var StyledPlaylist = withTheme(Playlist);
|
|
1472
1664
|
|
|
1473
1665
|
// src/components/Selection.tsx
|
|
1474
|
-
import
|
|
1475
|
-
import { jsx as
|
|
1476
|
-
var SelectionOverlay =
|
|
1666
|
+
import styled18 from "styled-components";
|
|
1667
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
1668
|
+
var SelectionOverlay = styled18.div.attrs((props) => ({
|
|
1477
1669
|
style: {
|
|
1478
1670
|
left: `${props.$left}px`,
|
|
1479
1671
|
width: `${props.$width}px`
|
|
@@ -1496,14 +1688,14 @@ var Selection = ({
|
|
|
1496
1688
|
if (width <= 0) {
|
|
1497
1689
|
return null;
|
|
1498
1690
|
}
|
|
1499
|
-
return /* @__PURE__ */
|
|
1691
|
+
return /* @__PURE__ */ jsx15(SelectionOverlay, { $left: startPosition, $width: width, $color: color, "data-selection": true });
|
|
1500
1692
|
};
|
|
1501
1693
|
|
|
1502
1694
|
// src/components/LoopRegion.tsx
|
|
1503
1695
|
import { useCallback as useCallback4, useRef as useRef5, useState } from "react";
|
|
1504
|
-
import
|
|
1505
|
-
import { Fragment as Fragment2, jsx as
|
|
1506
|
-
var LoopRegionOverlayDiv =
|
|
1696
|
+
import styled19 from "styled-components";
|
|
1697
|
+
import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1698
|
+
var LoopRegionOverlayDiv = styled19.div.attrs((props) => ({
|
|
1507
1699
|
style: {
|
|
1508
1700
|
left: `${props.$left}px`,
|
|
1509
1701
|
width: `${props.$width}px`
|
|
@@ -1516,7 +1708,7 @@ var LoopRegionOverlayDiv = styled18.div.attrs((props) => ({
|
|
|
1516
1708
|
z-index: 55; /* Between clips (z-index: 50) and selection (z-index: 60) */
|
|
1517
1709
|
pointer-events: none;
|
|
1518
1710
|
`;
|
|
1519
|
-
var LoopMarker =
|
|
1711
|
+
var LoopMarker = styled19.div.attrs((props) => ({
|
|
1520
1712
|
style: {
|
|
1521
1713
|
left: `${props.$left}px`
|
|
1522
1714
|
}
|
|
@@ -1552,7 +1744,7 @@ var LoopRegion = ({
|
|
|
1552
1744
|
return null;
|
|
1553
1745
|
}
|
|
1554
1746
|
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1555
|
-
/* @__PURE__ */
|
|
1747
|
+
/* @__PURE__ */ jsx16(
|
|
1556
1748
|
LoopRegionOverlayDiv,
|
|
1557
1749
|
{
|
|
1558
1750
|
$left: startPosition,
|
|
@@ -1561,7 +1753,7 @@ var LoopRegion = ({
|
|
|
1561
1753
|
"data-loop-region": true
|
|
1562
1754
|
}
|
|
1563
1755
|
),
|
|
1564
|
-
/* @__PURE__ */
|
|
1756
|
+
/* @__PURE__ */ jsx16(
|
|
1565
1757
|
LoopMarker,
|
|
1566
1758
|
{
|
|
1567
1759
|
$left: startPosition,
|
|
@@ -1570,7 +1762,7 @@ var LoopRegion = ({
|
|
|
1570
1762
|
"data-loop-marker": "start"
|
|
1571
1763
|
}
|
|
1572
1764
|
),
|
|
1573
|
-
/* @__PURE__ */
|
|
1765
|
+
/* @__PURE__ */ jsx16(
|
|
1574
1766
|
LoopMarker,
|
|
1575
1767
|
{
|
|
1576
1768
|
$left: endPosition - 2,
|
|
@@ -1581,7 +1773,7 @@ var LoopRegion = ({
|
|
|
1581
1773
|
)
|
|
1582
1774
|
] });
|
|
1583
1775
|
};
|
|
1584
|
-
var DraggableMarkerHandle =
|
|
1776
|
+
var DraggableMarkerHandle = styled19.div.attrs((props) => ({
|
|
1585
1777
|
style: {
|
|
1586
1778
|
left: `${props.$left}px`
|
|
1587
1779
|
}
|
|
@@ -1623,7 +1815,7 @@ var DraggableMarkerHandle = styled18.div.attrs((props) => ({
|
|
|
1623
1815
|
opacity: 1;
|
|
1624
1816
|
}
|
|
1625
1817
|
`;
|
|
1626
|
-
var TimescaleLoopShade =
|
|
1818
|
+
var TimescaleLoopShade = styled19.div.attrs((props) => ({
|
|
1627
1819
|
style: {
|
|
1628
1820
|
left: `${props.$left}px`,
|
|
1629
1821
|
width: `${props.$width}px`
|
|
@@ -1721,7 +1913,7 @@ var LoopRegionMarkers = ({
|
|
|
1721
1913
|
return null;
|
|
1722
1914
|
}
|
|
1723
1915
|
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1724
|
-
/* @__PURE__ */
|
|
1916
|
+
/* @__PURE__ */ jsx16(
|
|
1725
1917
|
TimescaleLoopShade,
|
|
1726
1918
|
{
|
|
1727
1919
|
$left: startPosition,
|
|
@@ -1732,7 +1924,7 @@ var LoopRegionMarkers = ({
|
|
|
1732
1924
|
"data-loop-region-timescale": true
|
|
1733
1925
|
}
|
|
1734
1926
|
),
|
|
1735
|
-
/* @__PURE__ */
|
|
1927
|
+
/* @__PURE__ */ jsx16(
|
|
1736
1928
|
DraggableMarkerHandle,
|
|
1737
1929
|
{
|
|
1738
1930
|
$left: startPosition,
|
|
@@ -1743,7 +1935,7 @@ var LoopRegionMarkers = ({
|
|
|
1743
1935
|
"data-loop-marker-handle": "start"
|
|
1744
1936
|
}
|
|
1745
1937
|
),
|
|
1746
|
-
/* @__PURE__ */
|
|
1938
|
+
/* @__PURE__ */ jsx16(
|
|
1747
1939
|
DraggableMarkerHandle,
|
|
1748
1940
|
{
|
|
1749
1941
|
$left: endPosition,
|
|
@@ -1756,13 +1948,10 @@ var LoopRegionMarkers = ({
|
|
|
1756
1948
|
)
|
|
1757
1949
|
] });
|
|
1758
1950
|
};
|
|
1759
|
-
var TimescaleLoopCreator =
|
|
1760
|
-
style: {
|
|
1761
|
-
left: `${props.$leftOffset || 0}px`
|
|
1762
|
-
}
|
|
1763
|
-
}))`
|
|
1951
|
+
var TimescaleLoopCreator = styled19.div`
|
|
1764
1952
|
position: absolute;
|
|
1765
1953
|
top: 0;
|
|
1954
|
+
left: 0;
|
|
1766
1955
|
right: 0;
|
|
1767
1956
|
height: 100%; /* Stay within timescale bounds, don't extend into tracks */
|
|
1768
1957
|
cursor: crosshair;
|
|
@@ -1775,8 +1964,7 @@ var TimescaleLoopRegion = ({
|
|
|
1775
1964
|
regionColor = "rgba(59, 130, 246, 0.3)",
|
|
1776
1965
|
onLoopRegionChange,
|
|
1777
1966
|
minPosition = 0,
|
|
1778
|
-
maxPosition = Infinity
|
|
1779
|
-
controlsOffset = 0
|
|
1967
|
+
maxPosition = Infinity
|
|
1780
1968
|
}) => {
|
|
1781
1969
|
const [, setIsCreating] = useState(false);
|
|
1782
1970
|
const createStartX = useRef5(0);
|
|
@@ -1813,14 +2001,13 @@ var TimescaleLoopRegion = ({
|
|
|
1813
2001
|
},
|
|
1814
2002
|
[minPosition, maxPosition, onLoopRegionChange]
|
|
1815
2003
|
);
|
|
1816
|
-
return /* @__PURE__ */
|
|
2004
|
+
return /* @__PURE__ */ jsx16(
|
|
1817
2005
|
TimescaleLoopCreator,
|
|
1818
2006
|
{
|
|
1819
2007
|
ref: containerRef,
|
|
1820
|
-
$leftOffset: controlsOffset,
|
|
1821
2008
|
onMouseDown: handleBackgroundMouseDown,
|
|
1822
2009
|
"data-timescale-loop-creator": true,
|
|
1823
|
-
children: hasLoopRegion && /* @__PURE__ */
|
|
2010
|
+
children: hasLoopRegion && /* @__PURE__ */ jsx16(
|
|
1824
2011
|
LoopRegionMarkers,
|
|
1825
2012
|
{
|
|
1826
2013
|
startPosition,
|
|
@@ -1839,10 +2026,10 @@ var TimescaleLoopRegion = ({
|
|
|
1839
2026
|
};
|
|
1840
2027
|
|
|
1841
2028
|
// src/components/SelectionTimeInputs.tsx
|
|
1842
|
-
import { useEffect as
|
|
2029
|
+
import { useEffect as useEffect7, useState as useState3 } from "react";
|
|
1843
2030
|
|
|
1844
2031
|
// src/components/TimeInput.tsx
|
|
1845
|
-
import { useEffect as
|
|
2032
|
+
import { useEffect as useEffect6, useState as useState2 } from "react";
|
|
1846
2033
|
|
|
1847
2034
|
// src/utils/timeFormat.ts
|
|
1848
2035
|
function clockFormat(seconds, decimals) {
|
|
@@ -1892,7 +2079,7 @@ function parseTime(timeStr, format) {
|
|
|
1892
2079
|
}
|
|
1893
2080
|
|
|
1894
2081
|
// src/components/TimeInput.tsx
|
|
1895
|
-
import { Fragment as Fragment3, jsx as
|
|
2082
|
+
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1896
2083
|
var TimeInput = ({
|
|
1897
2084
|
id,
|
|
1898
2085
|
label,
|
|
@@ -1903,7 +2090,7 @@ var TimeInput = ({
|
|
|
1903
2090
|
readOnly = false
|
|
1904
2091
|
}) => {
|
|
1905
2092
|
const [displayValue, setDisplayValue] = useState2("");
|
|
1906
|
-
|
|
2093
|
+
useEffect6(() => {
|
|
1907
2094
|
const formatted = formatTime(value, format);
|
|
1908
2095
|
setDisplayValue(formatted);
|
|
1909
2096
|
}, [value, format, id]);
|
|
@@ -1924,8 +2111,8 @@ var TimeInput = ({
|
|
|
1924
2111
|
}
|
|
1925
2112
|
};
|
|
1926
2113
|
return /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1927
|
-
/* @__PURE__ */
|
|
1928
|
-
/* @__PURE__ */
|
|
2114
|
+
/* @__PURE__ */ jsx17(ScreenReaderOnly, { as: "label", htmlFor: id, children: label }),
|
|
2115
|
+
/* @__PURE__ */ jsx17(
|
|
1929
2116
|
BaseInput,
|
|
1930
2117
|
{
|
|
1931
2118
|
type: "text",
|
|
@@ -1942,7 +2129,7 @@ var TimeInput = ({
|
|
|
1942
2129
|
};
|
|
1943
2130
|
|
|
1944
2131
|
// src/components/SelectionTimeInputs.tsx
|
|
1945
|
-
import { jsx as
|
|
2132
|
+
import { jsx as jsx18, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1946
2133
|
var SelectionTimeInputs = ({
|
|
1947
2134
|
selectionStart,
|
|
1948
2135
|
selectionEnd,
|
|
@@ -1950,7 +2137,7 @@ var SelectionTimeInputs = ({
|
|
|
1950
2137
|
className
|
|
1951
2138
|
}) => {
|
|
1952
2139
|
const [timeFormat, setTimeFormat] = useState3("hh:mm:ss.uuu");
|
|
1953
|
-
|
|
2140
|
+
useEffect7(() => {
|
|
1954
2141
|
const timeFormatSelect = document.querySelector(".time-format");
|
|
1955
2142
|
const handleFormatChange = () => {
|
|
1956
2143
|
if (timeFormatSelect) {
|
|
@@ -1976,7 +2163,7 @@ var SelectionTimeInputs = ({
|
|
|
1976
2163
|
}
|
|
1977
2164
|
};
|
|
1978
2165
|
return /* @__PURE__ */ jsxs8("div", { className, children: [
|
|
1979
|
-
/* @__PURE__ */
|
|
2166
|
+
/* @__PURE__ */ jsx18(
|
|
1980
2167
|
TimeInput,
|
|
1981
2168
|
{
|
|
1982
2169
|
id: "audio_start",
|
|
@@ -1987,7 +2174,7 @@ var SelectionTimeInputs = ({
|
|
|
1987
2174
|
onChange: handleStartChange
|
|
1988
2175
|
}
|
|
1989
2176
|
),
|
|
1990
|
-
/* @__PURE__ */
|
|
2177
|
+
/* @__PURE__ */ jsx18(
|
|
1991
2178
|
TimeInput,
|
|
1992
2179
|
{
|
|
1993
2180
|
id: "audio_end",
|
|
@@ -2003,7 +2190,7 @@ var SelectionTimeInputs = ({
|
|
|
2003
2190
|
|
|
2004
2191
|
// src/contexts/DevicePixelRatio.tsx
|
|
2005
2192
|
import { useState as useState4, createContext as createContext3, useContext as useContext3 } from "react";
|
|
2006
|
-
import { jsx as
|
|
2193
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
2007
2194
|
function getScale() {
|
|
2008
2195
|
return window.devicePixelRatio;
|
|
2009
2196
|
}
|
|
@@ -2017,7 +2204,7 @@ var DevicePixelRatioProvider = ({ children }) => {
|
|
|
2017
2204
|
},
|
|
2018
2205
|
{ once: true }
|
|
2019
2206
|
);
|
|
2020
|
-
return /* @__PURE__ */
|
|
2207
|
+
return /* @__PURE__ */ jsx19(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });
|
|
2021
2208
|
};
|
|
2022
2209
|
var useDevicePixelRatio = () => useContext3(DevicePixelRatioContext);
|
|
2023
2210
|
|
|
@@ -2046,8 +2233,8 @@ var useTheme2 = () => useContext5(ThemeContext);
|
|
|
2046
2233
|
|
|
2047
2234
|
// src/contexts/TrackControls.tsx
|
|
2048
2235
|
import { createContext as createContext5, useContext as useContext6, Fragment as Fragment4 } from "react";
|
|
2049
|
-
import { jsx as
|
|
2050
|
-
var TrackControlsContext = createContext5(/* @__PURE__ */
|
|
2236
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
2237
|
+
var TrackControlsContext = createContext5(/* @__PURE__ */ jsx20(Fragment4, {}));
|
|
2051
2238
|
var useTrackControls = () => useContext6(TrackControlsContext);
|
|
2052
2239
|
|
|
2053
2240
|
// src/contexts/Playout.tsx
|
|
@@ -2056,7 +2243,7 @@ import {
|
|
|
2056
2243
|
createContext as createContext6,
|
|
2057
2244
|
useContext as useContext7
|
|
2058
2245
|
} from "react";
|
|
2059
|
-
import { jsx as
|
|
2246
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
2060
2247
|
var defaultProgress = 0;
|
|
2061
2248
|
var defaultIsPlaying = false;
|
|
2062
2249
|
var defaultSelectionStart = 0;
|
|
@@ -2085,18 +2272,18 @@ var PlayoutProvider = ({ children }) => {
|
|
|
2085
2272
|
setSelectionStart(start);
|
|
2086
2273
|
setSelectionEnd(end);
|
|
2087
2274
|
};
|
|
2088
|
-
return /* @__PURE__ */
|
|
2275
|
+
return /* @__PURE__ */ jsx21(PlayoutStatusUpdateContext.Provider, { value: { setIsPlaying, setProgress, setSelection }, children: /* @__PURE__ */ jsx21(PlayoutStatusContext.Provider, { value: { isPlaying, progress, selectionStart, selectionEnd }, children }) });
|
|
2089
2276
|
};
|
|
2090
2277
|
var usePlayoutStatus = () => useContext7(PlayoutStatusContext);
|
|
2091
2278
|
var usePlayoutStatusUpdate = () => useContext7(PlayoutStatusUpdateContext);
|
|
2092
2279
|
|
|
2093
2280
|
// src/components/SpectrogramChannel.tsx
|
|
2094
|
-
import {
|
|
2095
|
-
import
|
|
2096
|
-
import { MAX_CANVAS_WIDTH as
|
|
2097
|
-
import { jsx as
|
|
2281
|
+
import { useRef as useRef6, useEffect as useEffect8 } from "react";
|
|
2282
|
+
import styled20 from "styled-components";
|
|
2283
|
+
import { MAX_CANVAS_WIDTH as MAX_CANVAS_WIDTH3 } from "@waveform-playlist/core";
|
|
2284
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
2098
2285
|
var LINEAR_FREQUENCY_SCALE = (f, minF, maxF) => (f - minF) / (maxF - minF);
|
|
2099
|
-
var
|
|
2286
|
+
var Wrapper4 = styled20.div.attrs((props) => ({
|
|
2100
2287
|
style: {
|
|
2101
2288
|
top: `${props.$waveHeight * props.$index}px`,
|
|
2102
2289
|
width: `${props.$cssWidth}px`,
|
|
@@ -2108,7 +2295,7 @@ var Wrapper3 = styled19.div.attrs((props) => ({
|
|
|
2108
2295
|
transform: translateZ(0);
|
|
2109
2296
|
backface-visibility: hidden;
|
|
2110
2297
|
`;
|
|
2111
|
-
var SpectrogramCanvas =
|
|
2298
|
+
var SpectrogramCanvas = styled20.canvas.attrs((props) => ({
|
|
2112
2299
|
style: {
|
|
2113
2300
|
width: `${props.$cssWidth}px`,
|
|
2114
2301
|
height: `${props.$waveHeight}px`,
|
|
@@ -2117,8 +2304,6 @@ var SpectrogramCanvas = styled19.canvas.attrs((props) => ({
|
|
|
2117
2304
|
}))`
|
|
2118
2305
|
position: absolute;
|
|
2119
2306
|
top: 0;
|
|
2120
|
-
/* Promote to own compositing layer for smoother scrolling */
|
|
2121
|
-
will-change: transform;
|
|
2122
2307
|
image-rendering: pixelated;
|
|
2123
2308
|
image-rendering: crisp-edges;
|
|
2124
2309
|
`;
|
|
@@ -2154,18 +2339,18 @@ var SpectrogramChannel = ({
|
|
|
2154
2339
|
const onCanvasesReadyRef = useRef6(onCanvasesReady);
|
|
2155
2340
|
const isWorkerMode = !!(workerApi && clipId);
|
|
2156
2341
|
const clipOriginX = useClipViewportOrigin();
|
|
2157
|
-
const visibleChunkIndices = useVisibleChunkIndices(length,
|
|
2342
|
+
const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH3, clipOriginX);
|
|
2158
2343
|
const lut = colorLUT ?? DEFAULT_COLOR_LUT;
|
|
2159
2344
|
const maxF = maxFrequency ?? (data ? data.sampleRate / 2 : 22050);
|
|
2160
2345
|
const scaleFn = frequencyScaleFn ?? LINEAR_FREQUENCY_SCALE;
|
|
2161
2346
|
const hasCustomFrequencyScale = Boolean(frequencyScaleFn);
|
|
2162
|
-
|
|
2347
|
+
useEffect8(() => {
|
|
2163
2348
|
workerApiRef.current = workerApi;
|
|
2164
2349
|
}, [workerApi]);
|
|
2165
|
-
|
|
2350
|
+
useEffect8(() => {
|
|
2166
2351
|
onCanvasesReadyRef.current = onCanvasesReady;
|
|
2167
2352
|
}, [onCanvasesReady]);
|
|
2168
|
-
|
|
2353
|
+
useEffect8(() => {
|
|
2169
2354
|
if (!isWorkerMode) return;
|
|
2170
2355
|
const currentWorkerApi = workerApiRef.current;
|
|
2171
2356
|
if (!currentWorkerApi || !clipId) return;
|
|
@@ -2220,15 +2405,15 @@ var SpectrogramChannel = ({
|
|
|
2220
2405
|
const match = id.match(/chunk(\d+)$/);
|
|
2221
2406
|
if (!match) {
|
|
2222
2407
|
console.warn(`[spectrogram] Unexpected canvas ID format: ${id}`);
|
|
2223
|
-
return
|
|
2408
|
+
return MAX_CANVAS_WIDTH3;
|
|
2224
2409
|
}
|
|
2225
2410
|
const chunkIdx = parseInt(match[1], 10);
|
|
2226
|
-
return Math.min(length - chunkIdx *
|
|
2411
|
+
return Math.min(length - chunkIdx * MAX_CANVAS_WIDTH3, MAX_CANVAS_WIDTH3);
|
|
2227
2412
|
});
|
|
2228
2413
|
onCanvasesReadyRef.current?.(allIds, allWidths);
|
|
2229
2414
|
}
|
|
2230
2415
|
}, [canvasMapRef, isWorkerMode, clipId, channelIndex, length, visibleChunkIndices]);
|
|
2231
|
-
|
|
2416
|
+
useEffect8(() => {
|
|
2232
2417
|
return () => {
|
|
2233
2418
|
const api = workerApiRef.current;
|
|
2234
2419
|
if (!api) return;
|
|
@@ -2242,7 +2427,7 @@ var SpectrogramChannel = ({
|
|
|
2242
2427
|
registeredIdsRef.current = [];
|
|
2243
2428
|
};
|
|
2244
2429
|
}, []);
|
|
2245
|
-
|
|
2430
|
+
useEffect8(() => {
|
|
2246
2431
|
if (isWorkerMode || !data) return;
|
|
2247
2432
|
const {
|
|
2248
2433
|
frequencyBinCount,
|
|
@@ -2255,7 +2440,7 @@ var SpectrogramChannel = ({
|
|
|
2255
2440
|
const rangeDb = rawRangeDb === 0 ? 1 : rawRangeDb;
|
|
2256
2441
|
const binToFreq = (bin) => bin / frequencyBinCount * (sampleRate / 2);
|
|
2257
2442
|
for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
|
|
2258
|
-
const globalPixelOffset = canvasIdx *
|
|
2443
|
+
const globalPixelOffset = canvasIdx * MAX_CANVAS_WIDTH3;
|
|
2259
2444
|
const ctx = canvas.getContext("2d");
|
|
2260
2445
|
if (!ctx) continue;
|
|
2261
2446
|
const canvasWidth = canvas.width / devicePixelRatio;
|
|
@@ -2331,9 +2516,9 @@ var SpectrogramChannel = ({
|
|
|
2331
2516
|
visibleChunkIndices
|
|
2332
2517
|
]);
|
|
2333
2518
|
const canvases = visibleChunkIndices.map((i) => {
|
|
2334
|
-
const chunkLeft = i *
|
|
2335
|
-
const currentWidth = Math.min(length - chunkLeft,
|
|
2336
|
-
return /* @__PURE__ */
|
|
2519
|
+
const chunkLeft = i * MAX_CANVAS_WIDTH3;
|
|
2520
|
+
const currentWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH3);
|
|
2521
|
+
return /* @__PURE__ */ jsx22(
|
|
2337
2522
|
SpectrogramCanvas,
|
|
2338
2523
|
{
|
|
2339
2524
|
$cssWidth: currentWidth,
|
|
@@ -2347,11 +2532,11 @@ var SpectrogramChannel = ({
|
|
|
2347
2532
|
`${length}-${i}`
|
|
2348
2533
|
);
|
|
2349
2534
|
});
|
|
2350
|
-
return /* @__PURE__ */
|
|
2535
|
+
return /* @__PURE__ */ jsx22(Wrapper4, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });
|
|
2351
2536
|
};
|
|
2352
2537
|
|
|
2353
2538
|
// src/components/SmartChannel.tsx
|
|
2354
|
-
import { Fragment as Fragment5, jsx as
|
|
2539
|
+
import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2355
2540
|
var SmartChannel = ({
|
|
2356
2541
|
isSelected,
|
|
2357
2542
|
transparentBackground,
|
|
@@ -2365,10 +2550,19 @@ var SmartChannel = ({
|
|
|
2365
2550
|
spectrogramWorkerApi,
|
|
2366
2551
|
spectrogramClipId,
|
|
2367
2552
|
spectrogramOnCanvasesReady,
|
|
2553
|
+
midiNotes,
|
|
2554
|
+
sampleRate: sampleRateProp,
|
|
2555
|
+
clipOffsetSeconds,
|
|
2368
2556
|
...props
|
|
2369
2557
|
}) => {
|
|
2370
2558
|
const theme = useTheme2();
|
|
2371
|
-
const {
|
|
2559
|
+
const {
|
|
2560
|
+
waveHeight,
|
|
2561
|
+
barWidth,
|
|
2562
|
+
barGap,
|
|
2563
|
+
samplesPerPixel: contextSpp,
|
|
2564
|
+
sampleRate: contextSampleRate
|
|
2565
|
+
} = usePlaylistInfo();
|
|
2372
2566
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2373
2567
|
const samplesPerPixel = sppProp ?? contextSpp;
|
|
2374
2568
|
const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;
|
|
@@ -2376,7 +2570,7 @@ var SmartChannel = ({
|
|
|
2376
2570
|
const drawMode = theme?.waveformDrawMode || "inverted";
|
|
2377
2571
|
const hasSpectrogram = spectrogramData || spectrogramWorkerApi;
|
|
2378
2572
|
if (renderMode === "spectrogram" && hasSpectrogram) {
|
|
2379
|
-
return /* @__PURE__ */
|
|
2573
|
+
return /* @__PURE__ */ jsx23(
|
|
2380
2574
|
SpectrogramChannel,
|
|
2381
2575
|
{
|
|
2382
2576
|
index: props.index,
|
|
@@ -2398,7 +2592,7 @@ var SmartChannel = ({
|
|
|
2398
2592
|
if (renderMode === "both" && hasSpectrogram) {
|
|
2399
2593
|
const halfHeight = Math.floor(waveHeight / 2);
|
|
2400
2594
|
return /* @__PURE__ */ jsxs9(Fragment5, { children: [
|
|
2401
|
-
/* @__PURE__ */
|
|
2595
|
+
/* @__PURE__ */ jsx23(
|
|
2402
2596
|
SpectrogramChannel,
|
|
2403
2597
|
{
|
|
2404
2598
|
index: props.index * 2,
|
|
@@ -2417,7 +2611,7 @@ var SmartChannel = ({
|
|
|
2417
2611
|
onCanvasesReady: spectrogramOnCanvasesReady
|
|
2418
2612
|
}
|
|
2419
2613
|
),
|
|
2420
|
-
/* @__PURE__ */
|
|
2614
|
+
/* @__PURE__ */ jsx23(
|
|
2421
2615
|
"div",
|
|
2422
2616
|
{
|
|
2423
2617
|
style: {
|
|
@@ -2426,7 +2620,7 @@ var SmartChannel = ({
|
|
|
2426
2620
|
width: props.length,
|
|
2427
2621
|
height: halfHeight
|
|
2428
2622
|
},
|
|
2429
|
-
children: /* @__PURE__ */
|
|
2623
|
+
children: /* @__PURE__ */ jsx23(
|
|
2430
2624
|
Channel,
|
|
2431
2625
|
{
|
|
2432
2626
|
...props,
|
|
@@ -2445,7 +2639,27 @@ var SmartChannel = ({
|
|
|
2445
2639
|
)
|
|
2446
2640
|
] });
|
|
2447
2641
|
}
|
|
2448
|
-
|
|
2642
|
+
if (renderMode === "piano-roll") {
|
|
2643
|
+
return /* @__PURE__ */ jsx23(
|
|
2644
|
+
PianoRollChannel,
|
|
2645
|
+
{
|
|
2646
|
+
index: props.index,
|
|
2647
|
+
midiNotes: midiNotes ?? [],
|
|
2648
|
+
length: props.length,
|
|
2649
|
+
waveHeight,
|
|
2650
|
+
devicePixelRatio,
|
|
2651
|
+
samplesPerPixel,
|
|
2652
|
+
sampleRate: sampleRateProp ?? contextSampleRate,
|
|
2653
|
+
clipOffsetSeconds: clipOffsetSeconds ?? 0,
|
|
2654
|
+
noteColor: theme?.pianoRollNoteColor,
|
|
2655
|
+
selectedNoteColor: theme?.pianoRollSelectedNoteColor,
|
|
2656
|
+
isSelected,
|
|
2657
|
+
transparentBackground,
|
|
2658
|
+
backgroundColor: theme?.pianoRollBackgroundColor
|
|
2659
|
+
}
|
|
2660
|
+
);
|
|
2661
|
+
}
|
|
2662
|
+
return /* @__PURE__ */ jsx23(
|
|
2449
2663
|
Channel,
|
|
2450
2664
|
{
|
|
2451
2665
|
...props,
|
|
@@ -2462,11 +2676,11 @@ var SmartChannel = ({
|
|
|
2462
2676
|
};
|
|
2463
2677
|
|
|
2464
2678
|
// src/components/SpectrogramLabels.tsx
|
|
2465
|
-
import { useRef as useRef7, useLayoutEffect as
|
|
2466
|
-
import
|
|
2467
|
-
import { jsx as
|
|
2679
|
+
import { useRef as useRef7, useLayoutEffect as useLayoutEffect2 } from "react";
|
|
2680
|
+
import styled21 from "styled-components";
|
|
2681
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
2468
2682
|
var LABELS_WIDTH = 72;
|
|
2469
|
-
var LabelsStickyWrapper =
|
|
2683
|
+
var LabelsStickyWrapper = styled21.div`
|
|
2470
2684
|
position: sticky;
|
|
2471
2685
|
left: 0;
|
|
2472
2686
|
z-index: 101;
|
|
@@ -2519,7 +2733,7 @@ var SpectrogramLabels = ({
|
|
|
2519
2733
|
const spectrogramHeight = renderMode === "both" ? Math.floor(waveHeight / 2) : waveHeight;
|
|
2520
2734
|
const totalHeight = numChannels * waveHeight;
|
|
2521
2735
|
const clipHeaderOffset = hasClipHeaders ? 22 : 0;
|
|
2522
|
-
|
|
2736
|
+
useLayoutEffect2(() => {
|
|
2523
2737
|
const canvas = canvasRef.current;
|
|
2524
2738
|
if (!canvas) return;
|
|
2525
2739
|
const ctx = canvas.getContext("2d");
|
|
@@ -2557,7 +2771,7 @@ var SpectrogramLabels = ({
|
|
|
2557
2771
|
spectrogramHeight,
|
|
2558
2772
|
clipHeaderOffset
|
|
2559
2773
|
]);
|
|
2560
|
-
return /* @__PURE__ */
|
|
2774
|
+
return /* @__PURE__ */ jsx24(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ jsx24(
|
|
2561
2775
|
"canvas",
|
|
2562
2776
|
{
|
|
2563
2777
|
ref: canvasRef,
|
|
@@ -2576,8 +2790,8 @@ var SpectrogramLabels = ({
|
|
|
2576
2790
|
import { useContext as useContext9 } from "react";
|
|
2577
2791
|
|
|
2578
2792
|
// src/components/TimeScale.tsx
|
|
2579
|
-
import
|
|
2580
|
-
import
|
|
2793
|
+
import React17, { useLayoutEffect as useLayoutEffect3, useContext as useContext8, useMemo as useMemo3 } from "react";
|
|
2794
|
+
import styled22, { withTheme as withTheme2 } from "styled-components";
|
|
2581
2795
|
|
|
2582
2796
|
// src/utils/conversions.ts
|
|
2583
2797
|
function samplesToSeconds(samples, sampleRate) {
|
|
@@ -2600,18 +2814,17 @@ function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
|
|
|
2600
2814
|
}
|
|
2601
2815
|
|
|
2602
2816
|
// src/components/TimeScale.tsx
|
|
2603
|
-
import { MAX_CANVAS_WIDTH as
|
|
2604
|
-
import { jsx as
|
|
2817
|
+
import { MAX_CANVAS_WIDTH as MAX_CANVAS_WIDTH4 } from "@waveform-playlist/core";
|
|
2818
|
+
import { jsx as jsx25, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2605
2819
|
function formatTime2(milliseconds) {
|
|
2606
2820
|
const seconds = Math.floor(milliseconds / 1e3);
|
|
2607
2821
|
const s = seconds % 60;
|
|
2608
2822
|
const m = (seconds - s) / 60;
|
|
2609
2823
|
return `${m}:${String(s).padStart(2, "0")}`;
|
|
2610
2824
|
}
|
|
2611
|
-
var PlaylistTimeScaleScroll =
|
|
2825
|
+
var PlaylistTimeScaleScroll = styled22.div.attrs((props) => ({
|
|
2612
2826
|
style: {
|
|
2613
2827
|
width: `${props.$cssWidth}px`,
|
|
2614
|
-
marginLeft: `${props.$controlWidth}px`,
|
|
2615
2828
|
height: `${props.$timeScaleHeight}px`
|
|
2616
2829
|
}
|
|
2617
2830
|
}))`
|
|
@@ -2620,7 +2833,7 @@ var PlaylistTimeScaleScroll = styled21.div.attrs((props) => ({
|
|
|
2620
2833
|
border-bottom: 1px solid ${(props) => props.theme.timeColor};
|
|
2621
2834
|
box-sizing: border-box;
|
|
2622
2835
|
`;
|
|
2623
|
-
var TimeTickChunk =
|
|
2836
|
+
var TimeTickChunk = styled22.canvas.attrs((props) => ({
|
|
2624
2837
|
style: {
|
|
2625
2838
|
width: `${props.$cssWidth}px`,
|
|
2626
2839
|
height: `${props.$timeScaleHeight}px`,
|
|
@@ -2629,10 +2842,8 @@ var TimeTickChunk = styled21.canvas.attrs((props) => ({
|
|
|
2629
2842
|
}))`
|
|
2630
2843
|
position: absolute;
|
|
2631
2844
|
bottom: 0;
|
|
2632
|
-
/* Promote to own compositing layer for smoother scrolling */
|
|
2633
|
-
will-change: transform;
|
|
2634
2845
|
`;
|
|
2635
|
-
var TimeStamp =
|
|
2846
|
+
var TimeStamp = styled22.div.attrs((props) => ({
|
|
2636
2847
|
style: {
|
|
2637
2848
|
left: `${props.$left + 4}px`
|
|
2638
2849
|
// Offset 4px to the right of the tick
|
|
@@ -2653,14 +2864,9 @@ var TimeScale = (props) => {
|
|
|
2653
2864
|
renderTimestamp
|
|
2654
2865
|
} = props;
|
|
2655
2866
|
const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
|
|
2656
|
-
const {
|
|
2657
|
-
sampleRate,
|
|
2658
|
-
samplesPerPixel,
|
|
2659
|
-
timeScaleHeight,
|
|
2660
|
-
controls: { show: showControls, width: controlWidth }
|
|
2661
|
-
} = useContext8(PlaylistInfoContext);
|
|
2867
|
+
const { sampleRate, samplesPerPixel, timeScaleHeight } = useContext8(PlaylistInfoContext);
|
|
2662
2868
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2663
|
-
const { widthX, canvasInfo, timeMarkersWithPositions } =
|
|
2869
|
+
const { widthX, canvasInfo, timeMarkersWithPositions } = useMemo3(() => {
|
|
2664
2870
|
const nextCanvasInfo = /* @__PURE__ */ new Map();
|
|
2665
2871
|
const nextMarkers = [];
|
|
2666
2872
|
const nextWidthX = secondsToPixels(duration / 1e3, samplesPerPixel, sampleRate);
|
|
@@ -2671,7 +2877,7 @@ var TimeScale = (props) => {
|
|
|
2671
2877
|
if (counter % marker === 0) {
|
|
2672
2878
|
const timeMs = counter;
|
|
2673
2879
|
const timestamp = formatTime2(timeMs);
|
|
2674
|
-
const element = renderTimestamp ? /* @__PURE__ */
|
|
2880
|
+
const element = renderTimestamp ? /* @__PURE__ */ jsx25(React17.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ jsx25(TimeStamp, { $left: pix, children: timestamp }, timestamp);
|
|
2675
2881
|
nextMarkers.push({ pix, element });
|
|
2676
2882
|
nextCanvasInfo.set(pix, timeScaleHeight);
|
|
2677
2883
|
} else if (counter % bigStep === 0) {
|
|
@@ -2696,11 +2902,11 @@ var TimeScale = (props) => {
|
|
|
2696
2902
|
renderTimestamp,
|
|
2697
2903
|
timeScaleHeight
|
|
2698
2904
|
]);
|
|
2699
|
-
const visibleChunkIndices = useVisibleChunkIndices(widthX,
|
|
2905
|
+
const visibleChunkIndices = useVisibleChunkIndices(widthX, MAX_CANVAS_WIDTH4);
|
|
2700
2906
|
const visibleChunks = visibleChunkIndices.map((i) => {
|
|
2701
|
-
const chunkLeft = i *
|
|
2702
|
-
const chunkWidth = Math.min(widthX - chunkLeft,
|
|
2703
|
-
return /* @__PURE__ */
|
|
2907
|
+
const chunkLeft = i * MAX_CANVAS_WIDTH4;
|
|
2908
|
+
const chunkWidth = Math.min(widthX - chunkLeft, MAX_CANVAS_WIDTH4);
|
|
2909
|
+
return /* @__PURE__ */ jsx25(
|
|
2704
2910
|
TimeTickChunk,
|
|
2705
2911
|
{
|
|
2706
2912
|
$cssWidth: chunkWidth,
|
|
@@ -2714,14 +2920,14 @@ var TimeScale = (props) => {
|
|
|
2714
2920
|
`timescale-${i}`
|
|
2715
2921
|
);
|
|
2716
2922
|
});
|
|
2717
|
-
const firstChunkLeft = visibleChunkIndices.length > 0 ? visibleChunkIndices[0] *
|
|
2718
|
-
const lastChunkRight = visibleChunkIndices.length > 0 ? (visibleChunkIndices[visibleChunkIndices.length - 1] + 1) *
|
|
2923
|
+
const firstChunkLeft = visibleChunkIndices.length > 0 ? visibleChunkIndices[0] * MAX_CANVAS_WIDTH4 : 0;
|
|
2924
|
+
const lastChunkRight = visibleChunkIndices.length > 0 ? (visibleChunkIndices[visibleChunkIndices.length - 1] + 1) * MAX_CANVAS_WIDTH4 : Infinity;
|
|
2719
2925
|
const visibleMarkers = visibleChunkIndices.length > 0 ? timeMarkersWithPositions.filter(({ pix }) => pix >= firstChunkLeft && pix < lastChunkRight).map(({ element }) => element) : timeMarkersWithPositions.map(({ element }) => element);
|
|
2720
|
-
|
|
2926
|
+
useLayoutEffect3(() => {
|
|
2721
2927
|
for (const [chunkIdx, canvas] of canvasMapRef.current.entries()) {
|
|
2722
2928
|
const ctx = canvas.getContext("2d");
|
|
2723
2929
|
if (!ctx) continue;
|
|
2724
|
-
const chunkLeft = chunkIdx *
|
|
2930
|
+
const chunkLeft = chunkIdx * MAX_CANVAS_WIDTH4;
|
|
2725
2931
|
const chunkWidth = canvas.width / devicePixelRatio;
|
|
2726
2932
|
ctx.resetTransform();
|
|
2727
2933
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
@@ -2744,23 +2950,15 @@ var TimeScale = (props) => {
|
|
|
2744
2950
|
canvasInfo,
|
|
2745
2951
|
visibleChunkIndices
|
|
2746
2952
|
]);
|
|
2747
|
-
return /* @__PURE__ */ jsxs10(
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
$controlWidth: showControls ? controlWidth : 0,
|
|
2752
|
-
$timeScaleHeight: timeScaleHeight,
|
|
2753
|
-
children: [
|
|
2754
|
-
visibleMarkers,
|
|
2755
|
-
visibleChunks
|
|
2756
|
-
]
|
|
2757
|
-
}
|
|
2758
|
-
);
|
|
2953
|
+
return /* @__PURE__ */ jsxs10(PlaylistTimeScaleScroll, { $cssWidth: widthX, $timeScaleHeight: timeScaleHeight, children: [
|
|
2954
|
+
visibleMarkers,
|
|
2955
|
+
visibleChunks
|
|
2956
|
+
] });
|
|
2759
2957
|
};
|
|
2760
2958
|
var StyledTimeScale = withTheme2(TimeScale);
|
|
2761
2959
|
|
|
2762
2960
|
// src/components/SmartScale.tsx
|
|
2763
|
-
import { jsx as
|
|
2961
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
2764
2962
|
var timeinfo = /* @__PURE__ */ new Map([
|
|
2765
2963
|
[
|
|
2766
2964
|
700,
|
|
@@ -2836,7 +3034,7 @@ function getScaleInfo(samplesPerPixel) {
|
|
|
2836
3034
|
var SmartScale = ({ renderTimestamp }) => {
|
|
2837
3035
|
const { samplesPerPixel, duration } = useContext9(PlaylistInfoContext);
|
|
2838
3036
|
let config = getScaleInfo(samplesPerPixel);
|
|
2839
|
-
return /* @__PURE__ */
|
|
3037
|
+
return /* @__PURE__ */ jsx26(
|
|
2840
3038
|
StyledTimeScale,
|
|
2841
3039
|
{
|
|
2842
3040
|
marker: config.marker,
|
|
@@ -2849,9 +3047,9 @@ var SmartScale = ({ renderTimestamp }) => {
|
|
|
2849
3047
|
};
|
|
2850
3048
|
|
|
2851
3049
|
// src/components/TimeFormatSelect.tsx
|
|
2852
|
-
import
|
|
2853
|
-
import { jsx as
|
|
2854
|
-
var SelectWrapper =
|
|
3050
|
+
import styled23 from "styled-components";
|
|
3051
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
3052
|
+
var SelectWrapper = styled23.div`
|
|
2855
3053
|
display: inline-flex;
|
|
2856
3054
|
align-items: center;
|
|
2857
3055
|
gap: 0.5rem;
|
|
@@ -2873,7 +3071,7 @@ var TimeFormatSelect = ({
|
|
|
2873
3071
|
const handleChange = (e) => {
|
|
2874
3072
|
onChange(e.target.value);
|
|
2875
3073
|
};
|
|
2876
|
-
return /* @__PURE__ */
|
|
3074
|
+
return /* @__PURE__ */ jsx27(SelectWrapper, { className, children: /* @__PURE__ */ jsx27(
|
|
2877
3075
|
BaseSelect,
|
|
2878
3076
|
{
|
|
2879
3077
|
className: "time-format",
|
|
@@ -2881,50 +3079,30 @@ var TimeFormatSelect = ({
|
|
|
2881
3079
|
onChange: handleChange,
|
|
2882
3080
|
disabled,
|
|
2883
3081
|
"aria-label": "Time format selection",
|
|
2884
|
-
children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */
|
|
3082
|
+
children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ jsx27("option", { value: option.value, children: option.label }, option.value))
|
|
2885
3083
|
}
|
|
2886
3084
|
) });
|
|
2887
3085
|
};
|
|
2888
3086
|
|
|
2889
3087
|
// src/components/Track.tsx
|
|
2890
|
-
import
|
|
2891
|
-
import { jsx as
|
|
2892
|
-
var Container =
|
|
3088
|
+
import styled24 from "styled-components";
|
|
3089
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
3090
|
+
var Container = styled24.div.attrs((props) => ({
|
|
2893
3091
|
style: {
|
|
2894
3092
|
height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`
|
|
2895
3093
|
}
|
|
2896
3094
|
}))`
|
|
2897
3095
|
position: relative;
|
|
2898
|
-
display: flex;
|
|
2899
3096
|
${(props) => props.$width !== void 0 && `width: ${props.$width}px;`}
|
|
2900
3097
|
`;
|
|
2901
|
-
var ChannelContainer =
|
|
3098
|
+
var ChannelContainer = styled24.div.attrs((props) => ({
|
|
2902
3099
|
style: {
|
|
2903
3100
|
paddingLeft: `${props.$offset || 0}px`
|
|
2904
3101
|
}
|
|
2905
3102
|
}))`
|
|
2906
3103
|
position: relative;
|
|
2907
3104
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
2908
|
-
flex: 1;
|
|
2909
|
-
`;
|
|
2910
|
-
var ControlsWrapper = styled23.div.attrs((props) => ({
|
|
2911
|
-
style: {
|
|
2912
|
-
width: `${props.$controlWidth}px`
|
|
2913
|
-
}
|
|
2914
|
-
}))`
|
|
2915
|
-
position: sticky;
|
|
2916
|
-
z-index: 102; /* Above waveform content and spectrogram labels (101), below Docusaurus navbar (200) */
|
|
2917
|
-
left: 0;
|
|
2918
3105
|
height: 100%;
|
|
2919
|
-
flex-shrink: 0;
|
|
2920
|
-
pointer-events: auto;
|
|
2921
|
-
background: ${(props) => props.theme.surfaceColor};
|
|
2922
|
-
transition: background 0.15s ease-in-out;
|
|
2923
|
-
|
|
2924
|
-
/* Selected track: highlighted background */
|
|
2925
|
-
${(props) => props.$isSelected && `
|
|
2926
|
-
background: ${props.theme.selectedTrackControlsBackground};
|
|
2927
|
-
`}
|
|
2928
3106
|
`;
|
|
2929
3107
|
var Track = ({
|
|
2930
3108
|
numChannels,
|
|
@@ -2936,44 +3114,34 @@ var Track = ({
|
|
|
2936
3114
|
hasClipHeaders = false,
|
|
2937
3115
|
onClick,
|
|
2938
3116
|
trackId,
|
|
2939
|
-
isSelected = false
|
|
3117
|
+
isSelected: _isSelected = false
|
|
2940
3118
|
}) => {
|
|
2941
|
-
const {
|
|
2942
|
-
|
|
2943
|
-
controls: { show, width: controlWidth }
|
|
2944
|
-
} = usePlaylistInfo();
|
|
2945
|
-
const controls = useTrackControls();
|
|
2946
|
-
return /* @__PURE__ */ jsxs11(
|
|
3119
|
+
const { waveHeight } = usePlaylistInfo();
|
|
3120
|
+
return /* @__PURE__ */ jsx28(
|
|
2947
3121
|
Container,
|
|
2948
3122
|
{
|
|
2949
3123
|
$numChannels: numChannels,
|
|
2950
3124
|
className,
|
|
2951
3125
|
$waveHeight: waveHeight,
|
|
2952
|
-
$controlWidth: show ? controlWidth : 0,
|
|
2953
3126
|
$width: width,
|
|
2954
3127
|
$hasClipHeaders: hasClipHeaders,
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
"data-track-id": trackId,
|
|
2966
|
-
children
|
|
2967
|
-
}
|
|
2968
|
-
)
|
|
2969
|
-
]
|
|
3128
|
+
children: /* @__PURE__ */ jsx28(
|
|
3129
|
+
ChannelContainer,
|
|
3130
|
+
{
|
|
3131
|
+
$backgroundColor: backgroundColor,
|
|
3132
|
+
$offset: offset,
|
|
3133
|
+
onClick,
|
|
3134
|
+
"data-track-id": trackId,
|
|
3135
|
+
children
|
|
3136
|
+
}
|
|
3137
|
+
)
|
|
2970
3138
|
}
|
|
2971
3139
|
);
|
|
2972
3140
|
};
|
|
2973
3141
|
|
|
2974
3142
|
// src/components/TrackControls/Button.tsx
|
|
2975
|
-
import
|
|
2976
|
-
var Button =
|
|
3143
|
+
import styled25 from "styled-components";
|
|
3144
|
+
var Button = styled25.button.attrs({
|
|
2977
3145
|
type: "button"
|
|
2978
3146
|
})`
|
|
2979
3147
|
display: inline-block;
|
|
@@ -3048,8 +3216,8 @@ var Button = styled24.button.attrs({
|
|
|
3048
3216
|
`;
|
|
3049
3217
|
|
|
3050
3218
|
// src/components/TrackControls/ButtonGroup.tsx
|
|
3051
|
-
import
|
|
3052
|
-
var ButtonGroup =
|
|
3219
|
+
import styled26 from "styled-components";
|
|
3220
|
+
var ButtonGroup = styled26.div`
|
|
3053
3221
|
margin-bottom: 0.3rem;
|
|
3054
3222
|
|
|
3055
3223
|
button:not(:first-child) {
|
|
@@ -3064,10 +3232,10 @@ var ButtonGroup = styled25.div`
|
|
|
3064
3232
|
`;
|
|
3065
3233
|
|
|
3066
3234
|
// src/components/TrackControls/CloseButton.tsx
|
|
3067
|
-
import
|
|
3235
|
+
import styled27 from "styled-components";
|
|
3068
3236
|
import { X as XIcon } from "@phosphor-icons/react";
|
|
3069
|
-
import { jsx as
|
|
3070
|
-
var StyledCloseButton =
|
|
3237
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
3238
|
+
var StyledCloseButton = styled27.button`
|
|
3071
3239
|
position: absolute;
|
|
3072
3240
|
left: 0;
|
|
3073
3241
|
top: 0;
|
|
@@ -3090,11 +3258,11 @@ var StyledCloseButton = styled26.button`
|
|
|
3090
3258
|
color: #dc3545;
|
|
3091
3259
|
}
|
|
3092
3260
|
`;
|
|
3093
|
-
var CloseButton = ({ onClick, title = "Remove track" }) => /* @__PURE__ */
|
|
3261
|
+
var CloseButton = ({ onClick, title = "Remove track" }) => /* @__PURE__ */ jsx29(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ jsx29(XIcon, { size: 12, weight: "bold" }) });
|
|
3094
3262
|
|
|
3095
3263
|
// src/components/TrackControls/Controls.tsx
|
|
3096
|
-
import
|
|
3097
|
-
var Controls =
|
|
3264
|
+
import styled28 from "styled-components";
|
|
3265
|
+
var Controls = styled28.div`
|
|
3098
3266
|
background: transparent;
|
|
3099
3267
|
width: 100%;
|
|
3100
3268
|
height: 100%;
|
|
@@ -3110,8 +3278,8 @@ var Controls = styled27.div`
|
|
|
3110
3278
|
`;
|
|
3111
3279
|
|
|
3112
3280
|
// src/components/TrackControls/Header.tsx
|
|
3113
|
-
import
|
|
3114
|
-
var Header =
|
|
3281
|
+
import styled29 from "styled-components";
|
|
3282
|
+
var Header = styled29.header`
|
|
3115
3283
|
overflow: hidden;
|
|
3116
3284
|
height: 26px;
|
|
3117
3285
|
width: 100%;
|
|
@@ -3126,27 +3294,27 @@ var Header = styled28.header`
|
|
|
3126
3294
|
|
|
3127
3295
|
// src/components/TrackControls/VolumeDownIcon.tsx
|
|
3128
3296
|
import { SpeakerLowIcon } from "@phosphor-icons/react";
|
|
3129
|
-
import { jsx as
|
|
3130
|
-
var VolumeDownIcon = (props) => /* @__PURE__ */
|
|
3297
|
+
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
3298
|
+
var VolumeDownIcon = (props) => /* @__PURE__ */ jsx30(SpeakerLowIcon, { weight: "light", ...props });
|
|
3131
3299
|
|
|
3132
3300
|
// src/components/TrackControls/VolumeUpIcon.tsx
|
|
3133
3301
|
import { SpeakerHighIcon } from "@phosphor-icons/react";
|
|
3134
|
-
import { jsx as
|
|
3135
|
-
var VolumeUpIcon = (props) => /* @__PURE__ */
|
|
3302
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
3303
|
+
var VolumeUpIcon = (props) => /* @__PURE__ */ jsx31(SpeakerHighIcon, { weight: "light", ...props });
|
|
3136
3304
|
|
|
3137
3305
|
// src/components/TrackControls/TrashIcon.tsx
|
|
3138
3306
|
import { TrashIcon as PhosphorTrashIcon } from "@phosphor-icons/react";
|
|
3139
|
-
import { jsx as
|
|
3140
|
-
var TrashIcon = (props) => /* @__PURE__ */
|
|
3307
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
3308
|
+
var TrashIcon = (props) => /* @__PURE__ */ jsx32(PhosphorTrashIcon, { weight: "light", ...props });
|
|
3141
3309
|
|
|
3142
3310
|
// src/components/TrackControls/DotsIcon.tsx
|
|
3143
3311
|
import { DotsThreeIcon } from "@phosphor-icons/react";
|
|
3144
|
-
import { jsx as
|
|
3145
|
-
var DotsIcon = (props) => /* @__PURE__ */
|
|
3312
|
+
import { jsx as jsx33 } from "react/jsx-runtime";
|
|
3313
|
+
var DotsIcon = (props) => /* @__PURE__ */ jsx33(DotsThreeIcon, { weight: "bold", ...props });
|
|
3146
3314
|
|
|
3147
3315
|
// src/components/TrackControls/Slider.tsx
|
|
3148
|
-
import
|
|
3149
|
-
var Slider =
|
|
3316
|
+
import styled30 from "styled-components";
|
|
3317
|
+
var Slider = styled30(BaseSlider)`
|
|
3150
3318
|
width: 75%;
|
|
3151
3319
|
height: 5px;
|
|
3152
3320
|
background: ${(props) => props.theme.sliderTrackColor};
|
|
@@ -3198,8 +3366,8 @@ var Slider = styled29(BaseSlider)`
|
|
|
3198
3366
|
`;
|
|
3199
3367
|
|
|
3200
3368
|
// src/components/TrackControls/SliderWrapper.tsx
|
|
3201
|
-
import
|
|
3202
|
-
var SliderWrapper =
|
|
3369
|
+
import styled31 from "styled-components";
|
|
3370
|
+
var SliderWrapper = styled31.label`
|
|
3203
3371
|
width: 100%;
|
|
3204
3372
|
display: flex;
|
|
3205
3373
|
justify-content: space-between;
|
|
@@ -3210,15 +3378,15 @@ var SliderWrapper = styled30.label`
|
|
|
3210
3378
|
`;
|
|
3211
3379
|
|
|
3212
3380
|
// src/components/TrackMenu.tsx
|
|
3213
|
-
import
|
|
3381
|
+
import React19, { useState as useState6, useEffect as useEffect9, useRef as useRef8, useCallback as useCallback5 } from "react";
|
|
3214
3382
|
import { createPortal } from "react-dom";
|
|
3215
|
-
import
|
|
3216
|
-
import { jsx as
|
|
3217
|
-
var MenuContainer =
|
|
3383
|
+
import styled32 from "styled-components";
|
|
3384
|
+
import { jsx as jsx34, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3385
|
+
var MenuContainer = styled32.div`
|
|
3218
3386
|
position: relative;
|
|
3219
3387
|
display: inline-block;
|
|
3220
3388
|
`;
|
|
3221
|
-
var MenuButton =
|
|
3389
|
+
var MenuButton = styled32.button`
|
|
3222
3390
|
background: none;
|
|
3223
3391
|
border: none;
|
|
3224
3392
|
cursor: pointer;
|
|
@@ -3233,7 +3401,8 @@ var MenuButton = styled31.button`
|
|
|
3233
3401
|
opacity: 1;
|
|
3234
3402
|
}
|
|
3235
3403
|
`;
|
|
3236
|
-
var
|
|
3404
|
+
var DROPDOWN_MIN_WIDTH = 180;
|
|
3405
|
+
var Dropdown = styled32.div`
|
|
3237
3406
|
position: fixed;
|
|
3238
3407
|
top: ${(p) => p.$top}px;
|
|
3239
3408
|
left: ${(p) => p.$left}px;
|
|
@@ -3243,31 +3412,53 @@ var Dropdown = styled31.div`
|
|
|
3243
3412
|
border: 1px solid rgba(128, 128, 128, 0.4);
|
|
3244
3413
|
border-radius: 6px;
|
|
3245
3414
|
padding: 0.5rem 0;
|
|
3246
|
-
min-width:
|
|
3415
|
+
min-width: ${DROPDOWN_MIN_WIDTH}px;
|
|
3247
3416
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
3248
3417
|
`;
|
|
3249
|
-
var Divider =
|
|
3418
|
+
var Divider = styled32.hr`
|
|
3250
3419
|
border: none;
|
|
3251
3420
|
border-top: 1px solid rgba(128, 128, 128, 0.3);
|
|
3252
3421
|
margin: 0.35rem 0;
|
|
3253
3422
|
`;
|
|
3254
3423
|
var TrackMenu = ({ items: itemsProp }) => {
|
|
3255
3424
|
const [open, setOpen] = useState6(false);
|
|
3256
|
-
const close = () => setOpen(false);
|
|
3425
|
+
const close = useCallback5(() => setOpen(false), []);
|
|
3257
3426
|
const items = typeof itemsProp === "function" ? itemsProp(close) : itemsProp;
|
|
3258
3427
|
const [dropdownPos, setDropdownPos] = useState6({ top: 0, left: 0 });
|
|
3259
3428
|
const buttonRef = useRef8(null);
|
|
3260
3429
|
const dropdownRef = useRef8(null);
|
|
3261
|
-
|
|
3262
|
-
if (
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3430
|
+
const updatePosition = useCallback5(() => {
|
|
3431
|
+
if (!buttonRef.current) return;
|
|
3432
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
3433
|
+
const vw = window.innerWidth;
|
|
3434
|
+
const vh = window.innerHeight;
|
|
3435
|
+
const dropHeight = dropdownRef.current?.offsetHeight ?? 160;
|
|
3436
|
+
let left = rect.right + 4;
|
|
3437
|
+
if (left + DROPDOWN_MIN_WIDTH > vw) {
|
|
3438
|
+
left = rect.left - DROPDOWN_MIN_WIDTH - 4;
|
|
3268
3439
|
}
|
|
3269
|
-
|
|
3270
|
-
|
|
3440
|
+
left = Math.max(4, Math.min(left, vw - DROPDOWN_MIN_WIDTH - 4));
|
|
3441
|
+
let top = rect.top;
|
|
3442
|
+
if (top + dropHeight > vh - 4) {
|
|
3443
|
+
top = Math.max(4, rect.bottom - dropHeight);
|
|
3444
|
+
}
|
|
3445
|
+
setDropdownPos({ top, left });
|
|
3446
|
+
}, []);
|
|
3447
|
+
useEffect9(() => {
|
|
3448
|
+
if (!open) return;
|
|
3449
|
+
updatePosition();
|
|
3450
|
+
const rafId = requestAnimationFrame(() => updatePosition());
|
|
3451
|
+
const onScroll = () => updatePosition();
|
|
3452
|
+
const onResize = () => updatePosition();
|
|
3453
|
+
window.addEventListener("scroll", onScroll, true);
|
|
3454
|
+
window.addEventListener("resize", onResize);
|
|
3455
|
+
return () => {
|
|
3456
|
+
cancelAnimationFrame(rafId);
|
|
3457
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
3458
|
+
window.removeEventListener("resize", onResize);
|
|
3459
|
+
};
|
|
3460
|
+
}, [open, updatePosition]);
|
|
3461
|
+
useEffect9(() => {
|
|
3271
3462
|
if (!open) return;
|
|
3272
3463
|
const handleClick = (e) => {
|
|
3273
3464
|
const target = e.target;
|
|
@@ -3275,11 +3466,20 @@ var TrackMenu = ({ items: itemsProp }) => {
|
|
|
3275
3466
|
setOpen(false);
|
|
3276
3467
|
}
|
|
3277
3468
|
};
|
|
3469
|
+
const handleKeyDown = (e) => {
|
|
3470
|
+
if (e.key === "Escape") {
|
|
3471
|
+
setOpen(false);
|
|
3472
|
+
}
|
|
3473
|
+
};
|
|
3278
3474
|
document.addEventListener("mousedown", handleClick);
|
|
3279
|
-
|
|
3475
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
3476
|
+
return () => {
|
|
3477
|
+
document.removeEventListener("mousedown", handleClick);
|
|
3478
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
3479
|
+
};
|
|
3280
3480
|
}, [open]);
|
|
3281
|
-
return /* @__PURE__ */
|
|
3282
|
-
/* @__PURE__ */
|
|
3481
|
+
return /* @__PURE__ */ jsxs11(MenuContainer, { children: [
|
|
3482
|
+
/* @__PURE__ */ jsx34(
|
|
3283
3483
|
MenuButton,
|
|
3284
3484
|
{
|
|
3285
3485
|
ref: buttonRef,
|
|
@@ -3290,19 +3490,19 @@ var TrackMenu = ({ items: itemsProp }) => {
|
|
|
3290
3490
|
onMouseDown: (e) => e.stopPropagation(),
|
|
3291
3491
|
title: "Track menu",
|
|
3292
3492
|
"aria-label": "Track menu",
|
|
3293
|
-
children: /* @__PURE__ */
|
|
3493
|
+
children: /* @__PURE__ */ jsx34(DotsIcon, { size: 16 })
|
|
3294
3494
|
}
|
|
3295
3495
|
),
|
|
3296
3496
|
open && typeof document !== "undefined" && createPortal(
|
|
3297
|
-
/* @__PURE__ */
|
|
3497
|
+
/* @__PURE__ */ jsx34(
|
|
3298
3498
|
Dropdown,
|
|
3299
3499
|
{
|
|
3300
3500
|
ref: dropdownRef,
|
|
3301
3501
|
$top: dropdownPos.top,
|
|
3302
3502
|
$left: dropdownPos.left,
|
|
3303
3503
|
onMouseDown: (e) => e.stopPropagation(),
|
|
3304
|
-
children: items.map((item, index) => /* @__PURE__ */
|
|
3305
|
-
index > 0 && /* @__PURE__ */
|
|
3504
|
+
children: items.map((item, index) => /* @__PURE__ */ jsxs11(React19.Fragment, { children: [
|
|
3505
|
+
index > 0 && /* @__PURE__ */ jsx34(Divider, {}),
|
|
3306
3506
|
item.content
|
|
3307
3507
|
] }, item.id))
|
|
3308
3508
|
}
|
|
@@ -3344,6 +3544,7 @@ export {
|
|
|
3344
3544
|
LoopRegion,
|
|
3345
3545
|
LoopRegionMarkers,
|
|
3346
3546
|
MasterVolumeControl,
|
|
3547
|
+
PianoRollChannel,
|
|
3347
3548
|
Playhead,
|
|
3348
3549
|
PlayheadWithMarker,
|
|
3349
3550
|
Playlist,
|