@waveform-playlist/ui-components 9.1.2 → 9.2.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.mjs CHANGED
@@ -781,7 +781,6 @@ var Channel = (props) => {
781
781
  const clipOriginX = useClipViewportOrigin();
782
782
  const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH, clipOriginX);
783
783
  useEffect3(() => {
784
- const tDraw = performance.now();
785
784
  const step = barWidth + barGap;
786
785
  for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
787
786
  const globalPixelOffset = canvasIdx * MAX_CANVAS_WIDTH;
@@ -817,9 +816,6 @@ var Channel = (props) => {
817
816
  }
818
817
  }
819
818
  }
820
- console.log(
821
- `[waveform] draw ch${index}: ${canvasMapRef.current.size} chunks, ${(performance.now() - tDraw).toFixed(1)}ms`
822
- );
823
819
  }, [
824
820
  canvasMapRef,
825
821
  data,
@@ -1159,20 +1155,20 @@ var Clip = ({
1159
1155
  isDragSource
1160
1156
  } = useDraggable({
1161
1157
  id: draggableId,
1162
- data: { clipId, trackIndex, clipIndex },
1158
+ data: { clipId, trackIndex, clipIndex, startSample, durationSamples },
1163
1159
  disabled: !enableDrag
1164
1160
  });
1165
1161
  const leftBoundaryId = `clip-boundary-left-${trackIndex}-${clipIndex}`;
1166
1162
  const { ref: leftBoundaryRef, isDragSource: isLeftBoundaryDragging } = useDraggable({
1167
1163
  id: leftBoundaryId,
1168
- data: { clipId, trackIndex, clipIndex, boundary: "left" },
1164
+ data: { clipId, trackIndex, clipIndex, boundary: "left", startSample, durationSamples },
1169
1165
  disabled: !enableDrag,
1170
1166
  feedback: "none"
1171
1167
  });
1172
1168
  const rightBoundaryId = `clip-boundary-right-${trackIndex}-${clipIndex}`;
1173
1169
  const { ref: rightBoundaryRef, isDragSource: isRightBoundaryDragging } = useDraggable({
1174
1170
  id: rightBoundaryId,
1175
- data: { clipId, trackIndex, clipIndex, boundary: "right" },
1171
+ data: { clipId, trackIndex, clipIndex, boundary: "right", startSample, durationSamples },
1176
1172
  disabled: !enableDrag,
1177
1173
  feedback: "none"
1178
1174
  });
@@ -1357,7 +1353,6 @@ var PianoRollChannel = ({
1357
1353
  }, [midiNotes]);
1358
1354
  const color = isSelected ? selectedNoteColor : noteColor;
1359
1355
  useEffect4(() => {
1360
- const tDraw = performance.now();
1361
1356
  const noteRange = maxMidi - minMidi + 1;
1362
1357
  const noteHeight = Math.max(2, waveHeight / noteRange);
1363
1358
  const pixelsPerSecond = sampleRate / samplesPerPixel;
@@ -1389,9 +1384,6 @@ var PianoRollChannel = ({
1389
1384
  }
1390
1385
  ctx.globalAlpha = 1;
1391
1386
  }
1392
- console.log(
1393
- `[piano-roll] draw ch${index}: ${canvasMapRef.current.size} chunks, ${midiNotes.length} notes, ${(performance.now() - tDraw).toFixed(1)}ms`
1394
- );
1395
1387
  }, [
1396
1388
  canvasMapRef,
1397
1389
  midiNotes,
@@ -2183,13 +2175,43 @@ var SelectionTimeInputs = ({
2183
2175
  ] });
2184
2176
  };
2185
2177
 
2186
- // src/contexts/DevicePixelRatio.tsx
2187
- import { useState as useState4, createContext as createContext3, useContext as useContext3 } from "react";
2178
+ // src/contexts/BeatsAndBars.tsx
2179
+ import { createContext as createContext3, useContext as useContext3, useMemo as useMemo3 } from "react";
2180
+ import { ticksPerBeat, ticksPerBar } from "@waveform-playlist/core";
2188
2181
  import { jsx as jsx19 } from "react/jsx-runtime";
2182
+ var BeatsAndBarsContext = createContext3(null);
2183
+ function BeatsAndBarsProvider({
2184
+ bpm,
2185
+ timeSignature,
2186
+ snapTo,
2187
+ children
2188
+ }) {
2189
+ const [numerator, denominator] = timeSignature;
2190
+ const value = useMemo3(() => {
2191
+ const ts = [numerator, denominator];
2192
+ const tpBeat = ticksPerBeat(ts);
2193
+ const tpBar = ticksPerBar(ts);
2194
+ return {
2195
+ bpm,
2196
+ timeSignature: ts,
2197
+ snapTo,
2198
+ ticksPerBeat: tpBeat,
2199
+ ticksPerBar: tpBar
2200
+ };
2201
+ }, [bpm, numerator, denominator, snapTo]);
2202
+ return /* @__PURE__ */ jsx19(BeatsAndBarsContext.Provider, { value, children });
2203
+ }
2204
+ function useBeatsAndBars() {
2205
+ return useContext3(BeatsAndBarsContext);
2206
+ }
2207
+
2208
+ // src/contexts/DevicePixelRatio.tsx
2209
+ import { useState as useState4, createContext as createContext4, useContext as useContext4 } from "react";
2210
+ import { jsx as jsx20 } from "react/jsx-runtime";
2189
2211
  function getScale() {
2190
2212
  return window.devicePixelRatio;
2191
2213
  }
2192
- var DevicePixelRatioContext = createContext3(getScale());
2214
+ var DevicePixelRatioContext = createContext4(getScale());
2193
2215
  var DevicePixelRatioProvider = ({ children }) => {
2194
2216
  const [scale, setScale] = useState4(getScale());
2195
2217
  matchMedia(`(resolution: ${getScale()}dppx)`).addEventListener(
@@ -2199,13 +2221,13 @@ var DevicePixelRatioProvider = ({ children }) => {
2199
2221
  },
2200
2222
  { once: true }
2201
2223
  );
2202
- return /* @__PURE__ */ jsx19(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });
2224
+ return /* @__PURE__ */ jsx20(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });
2203
2225
  };
2204
- var useDevicePixelRatio = () => useContext3(DevicePixelRatioContext);
2226
+ var useDevicePixelRatio = () => useContext4(DevicePixelRatioContext);
2205
2227
 
2206
2228
  // src/contexts/PlaylistInfo.tsx
2207
- import { createContext as createContext4, useContext as useContext4 } from "react";
2208
- var PlaylistInfoContext = createContext4({
2229
+ import { createContext as createContext5, useContext as useContext5 } from "react";
2230
+ var PlaylistInfoContext = createContext5({
2209
2231
  sampleRate: 48e3,
2210
2232
  samplesPerPixel: 1e3,
2211
2233
  zoomLevels: [1e3, 1500, 2e3, 2500],
@@ -2219,26 +2241,26 @@ var PlaylistInfoContext = createContext4({
2219
2241
  barWidth: 1,
2220
2242
  barGap: 0
2221
2243
  });
2222
- var usePlaylistInfo = () => useContext4(PlaylistInfoContext);
2244
+ var usePlaylistInfo = () => useContext5(PlaylistInfoContext);
2223
2245
 
2224
2246
  // src/contexts/Theme.tsx
2225
- import { useContext as useContext5 } from "react";
2247
+ import { useContext as useContext6 } from "react";
2226
2248
  import { ThemeContext } from "styled-components";
2227
- var useTheme2 = () => useContext5(ThemeContext);
2249
+ var useTheme2 = () => useContext6(ThemeContext);
2228
2250
 
2229
2251
  // src/contexts/TrackControls.tsx
2230
- import { createContext as createContext5, useContext as useContext6, Fragment as Fragment4 } from "react";
2231
- import { jsx as jsx20 } from "react/jsx-runtime";
2232
- var TrackControlsContext = createContext5(/* @__PURE__ */ jsx20(Fragment4, {}));
2233
- var useTrackControls = () => useContext6(TrackControlsContext);
2252
+ import { createContext as createContext6, useContext as useContext7, Fragment as Fragment4 } from "react";
2253
+ import { jsx as jsx21 } from "react/jsx-runtime";
2254
+ var TrackControlsContext = createContext6(/* @__PURE__ */ jsx21(Fragment4, {}));
2255
+ var useTrackControls = () => useContext7(TrackControlsContext);
2234
2256
 
2235
2257
  // src/contexts/Playout.tsx
2236
2258
  import {
2237
2259
  useState as useState5,
2238
- createContext as createContext6,
2239
- useContext as useContext7
2260
+ createContext as createContext7,
2261
+ useContext as useContext8
2240
2262
  } from "react";
2241
- import { jsx as jsx21 } from "react/jsx-runtime";
2263
+ import { jsx as jsx22 } from "react/jsx-runtime";
2242
2264
  var defaultProgress = 0;
2243
2265
  var defaultIsPlaying = false;
2244
2266
  var defaultSelectionStart = 0;
@@ -2249,8 +2271,8 @@ var defaultPlayout = {
2249
2271
  selectionStart: defaultSelectionStart,
2250
2272
  selectionEnd: defaultSelectionEnd
2251
2273
  };
2252
- var PlayoutStatusContext = createContext6(defaultPlayout);
2253
- var PlayoutStatusUpdateContext = createContext6({
2274
+ var PlayoutStatusContext = createContext7(defaultPlayout);
2275
+ var PlayoutStatusUpdateContext = createContext7({
2254
2276
  setIsPlaying: () => {
2255
2277
  },
2256
2278
  setProgress: () => {
@@ -2267,16 +2289,16 @@ var PlayoutProvider = ({ children }) => {
2267
2289
  setSelectionStart(start);
2268
2290
  setSelectionEnd(end);
2269
2291
  };
2270
- return /* @__PURE__ */ jsx21(PlayoutStatusUpdateContext.Provider, { value: { setIsPlaying, setProgress, setSelection }, children: /* @__PURE__ */ jsx21(PlayoutStatusContext.Provider, { value: { isPlaying, progress, selectionStart, selectionEnd }, children }) });
2292
+ return /* @__PURE__ */ jsx22(PlayoutStatusUpdateContext.Provider, { value: { setIsPlaying, setProgress, setSelection }, children: /* @__PURE__ */ jsx22(PlayoutStatusContext.Provider, { value: { isPlaying, progress, selectionStart, selectionEnd }, children }) });
2271
2293
  };
2272
- var usePlayoutStatus = () => useContext7(PlayoutStatusContext);
2273
- var usePlayoutStatusUpdate = () => useContext7(PlayoutStatusUpdateContext);
2294
+ var usePlayoutStatus = () => useContext8(PlayoutStatusContext);
2295
+ var usePlayoutStatusUpdate = () => useContext8(PlayoutStatusUpdateContext);
2274
2296
 
2275
2297
  // src/components/SpectrogramChannel.tsx
2276
2298
  import { useRef as useRef6, useEffect as useEffect8 } from "react";
2277
2299
  import styled20 from "styled-components";
2278
2300
  import { MAX_CANVAS_WIDTH as MAX_CANVAS_WIDTH3 } from "@waveform-playlist/core";
2279
- import { jsx as jsx22 } from "react/jsx-runtime";
2301
+ import { jsx as jsx23 } from "react/jsx-runtime";
2280
2302
  var LINEAR_FREQUENCY_SCALE = (f, minF, maxF) => (f - minF) / (maxF - minF);
2281
2303
  var Wrapper4 = styled20.div.attrs((props) => ({
2282
2304
  style: {
@@ -2513,7 +2535,7 @@ var SpectrogramChannel = ({
2513
2535
  const canvases = visibleChunkIndices.map((i) => {
2514
2536
  const chunkLeft = i * MAX_CANVAS_WIDTH3;
2515
2537
  const currentWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH3);
2516
- return /* @__PURE__ */ jsx22(
2538
+ return /* @__PURE__ */ jsx23(
2517
2539
  SpectrogramCanvas,
2518
2540
  {
2519
2541
  $cssWidth: currentWidth,
@@ -2527,11 +2549,11 @@ var SpectrogramChannel = ({
2527
2549
  `${length}-${i}`
2528
2550
  );
2529
2551
  });
2530
- return /* @__PURE__ */ jsx22(Wrapper4, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });
2552
+ return /* @__PURE__ */ jsx23(Wrapper4, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });
2531
2553
  };
2532
2554
 
2533
2555
  // src/components/SmartChannel.tsx
2534
- import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs9 } from "react/jsx-runtime";
2556
+ import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs9 } from "react/jsx-runtime";
2535
2557
  var SmartChannel = ({
2536
2558
  isSelected,
2537
2559
  transparentBackground,
@@ -2565,7 +2587,7 @@ var SmartChannel = ({
2565
2587
  const drawMode = theme?.waveformDrawMode || "inverted";
2566
2588
  const hasSpectrogram = spectrogramData || spectrogramWorkerApi;
2567
2589
  if (renderMode === "spectrogram" && hasSpectrogram) {
2568
- return /* @__PURE__ */ jsx23(
2590
+ return /* @__PURE__ */ jsx24(
2569
2591
  SpectrogramChannel,
2570
2592
  {
2571
2593
  index: props.index,
@@ -2587,7 +2609,7 @@ var SmartChannel = ({
2587
2609
  if (renderMode === "both" && hasSpectrogram) {
2588
2610
  const halfHeight = Math.floor(waveHeight / 2);
2589
2611
  return /* @__PURE__ */ jsxs9(Fragment5, { children: [
2590
- /* @__PURE__ */ jsx23(
2612
+ /* @__PURE__ */ jsx24(
2591
2613
  SpectrogramChannel,
2592
2614
  {
2593
2615
  index: props.index * 2,
@@ -2606,7 +2628,7 @@ var SmartChannel = ({
2606
2628
  onCanvasesReady: spectrogramOnCanvasesReady
2607
2629
  }
2608
2630
  ),
2609
- /* @__PURE__ */ jsx23(
2631
+ /* @__PURE__ */ jsx24(
2610
2632
  "div",
2611
2633
  {
2612
2634
  style: {
@@ -2615,7 +2637,7 @@ var SmartChannel = ({
2615
2637
  width: props.length,
2616
2638
  height: halfHeight
2617
2639
  },
2618
- children: /* @__PURE__ */ jsx23(
2640
+ children: /* @__PURE__ */ jsx24(
2619
2641
  Channel,
2620
2642
  {
2621
2643
  ...props,
@@ -2635,7 +2657,7 @@ var SmartChannel = ({
2635
2657
  ] });
2636
2658
  }
2637
2659
  if (renderMode === "piano-roll") {
2638
- return /* @__PURE__ */ jsx23(
2660
+ return /* @__PURE__ */ jsx24(
2639
2661
  PianoRollChannel,
2640
2662
  {
2641
2663
  index: props.index,
@@ -2654,7 +2676,7 @@ var SmartChannel = ({
2654
2676
  }
2655
2677
  );
2656
2678
  }
2657
- return /* @__PURE__ */ jsx23(
2679
+ return /* @__PURE__ */ jsx24(
2658
2680
  Channel,
2659
2681
  {
2660
2682
  ...props,
@@ -2673,7 +2695,7 @@ var SmartChannel = ({
2673
2695
  // src/components/SpectrogramLabels.tsx
2674
2696
  import { useRef as useRef7, useLayoutEffect as useLayoutEffect2 } from "react";
2675
2697
  import styled21 from "styled-components";
2676
- import { jsx as jsx24 } from "react/jsx-runtime";
2698
+ import { jsx as jsx25 } from "react/jsx-runtime";
2677
2699
  var LABELS_WIDTH = 72;
2678
2700
  var LabelsStickyWrapper = styled21.div`
2679
2701
  position: sticky;
@@ -2766,7 +2788,7 @@ var SpectrogramLabels = ({
2766
2788
  spectrogramHeight,
2767
2789
  clipHeaderOffset
2768
2790
  ]);
2769
- return /* @__PURE__ */ jsx24(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ jsx24(
2791
+ return /* @__PURE__ */ jsx25(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ jsx25(
2770
2792
  "canvas",
2771
2793
  {
2772
2794
  ref: canvasRef,
@@ -2782,41 +2804,14 @@ var SpectrogramLabels = ({
2782
2804
  };
2783
2805
 
2784
2806
  // src/components/SmartScale.tsx
2785
- import { useContext as useContext9 } from "react";
2807
+ import React19, { useContext as useContext10, useMemo as useMemo4 } from "react";
2808
+ import styled23 from "styled-components";
2786
2809
 
2787
2810
  // src/components/TimeScale.tsx
2788
- import React17, { useLayoutEffect as useLayoutEffect3, useContext as useContext8, useMemo as useMemo3 } from "react";
2811
+ import { useLayoutEffect as useLayoutEffect3, useContext as useContext9 } from "react";
2789
2812
  import styled22, { withTheme as withTheme2 } from "styled-components";
2790
-
2791
- // src/utils/conversions.ts
2792
- function samplesToSeconds(samples, sampleRate) {
2793
- return samples / sampleRate;
2794
- }
2795
- function secondsToSamples(seconds, sampleRate) {
2796
- return Math.ceil(seconds * sampleRate);
2797
- }
2798
- function samplesToPixels(samples, samplesPerPixel) {
2799
- return Math.floor(samples / samplesPerPixel);
2800
- }
2801
- function pixelsToSamples(pixels, samplesPerPixel) {
2802
- return Math.floor(pixels * samplesPerPixel);
2803
- }
2804
- function pixelsToSeconds(pixels, samplesPerPixel, sampleRate) {
2805
- return pixels * samplesPerPixel / sampleRate;
2806
- }
2807
- function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
2808
- return Math.ceil(seconds * sampleRate / samplesPerPixel);
2809
- }
2810
-
2811
- // src/components/TimeScale.tsx
2812
2813
  import { MAX_CANVAS_WIDTH as MAX_CANVAS_WIDTH4 } from "@waveform-playlist/core";
2813
- import { jsx as jsx25, jsxs as jsxs10 } from "react/jsx-runtime";
2814
- function formatTime2(milliseconds) {
2815
- const seconds = Math.floor(milliseconds / 1e3);
2816
- const s = seconds % 60;
2817
- const m = (seconds - s) / 60;
2818
- return `${m}:${String(s).padStart(2, "0")}`;
2819
- }
2814
+ import { jsx as jsx26, jsxs as jsxs10 } from "react/jsx-runtime";
2820
2815
  var PlaylistTimeScaleScroll = styled22.div.attrs((props) => ({
2821
2816
  style: {
2822
2817
  width: `${props.$cssWidth}px`,
@@ -2838,70 +2833,20 @@ var TimeTickChunk = styled22.canvas.attrs((props) => ({
2838
2833
  position: absolute;
2839
2834
  bottom: 0;
2840
2835
  `;
2841
- var TimeStamp = styled22.div.attrs((props) => ({
2842
- style: {
2843
- left: `${props.$left + 4}px`
2844
- // Offset 4px to the right of the tick
2845
- }
2846
- }))`
2847
- position: absolute;
2848
- font-size: 0.75rem; /* Smaller font to prevent overflow */
2849
- white-space: nowrap; /* Prevent text wrapping */
2850
- color: ${(props) => props.theme.timeColor}; /* Use theme color instead of inheriting */
2851
- `;
2852
2836
  var TimeScale = (props) => {
2853
2837
  const {
2854
2838
  theme: { timeColor },
2855
- duration,
2856
- marker,
2857
- bigStep,
2858
- secondStep,
2859
- renderTimestamp
2839
+ tickData
2860
2840
  } = props;
2861
2841
  const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
2862
- const { sampleRate, samplesPerPixel, timeScaleHeight } = useContext8(PlaylistInfoContext);
2842
+ const { timeScaleHeight } = useContext9(PlaylistInfoContext);
2863
2843
  const devicePixelRatio = useDevicePixelRatio();
2864
- const { widthX, canvasInfo, timeMarkersWithPositions } = useMemo3(() => {
2865
- const nextCanvasInfo = /* @__PURE__ */ new Map();
2866
- const nextMarkers = [];
2867
- const nextWidthX = secondsToPixels(duration / 1e3, samplesPerPixel, sampleRate);
2868
- const pixPerSec = sampleRate / samplesPerPixel;
2869
- let counter = 0;
2870
- for (let i = 0; i < nextWidthX; i += pixPerSec * secondStep / 1e3) {
2871
- const pix = Math.floor(i);
2872
- if (counter % marker === 0) {
2873
- const timeMs = counter;
2874
- const timestamp = formatTime2(timeMs);
2875
- const element = renderTimestamp ? /* @__PURE__ */ jsx25(React17.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ jsx25(TimeStamp, { $left: pix, children: timestamp }, timestamp);
2876
- nextMarkers.push({ pix, element });
2877
- nextCanvasInfo.set(pix, timeScaleHeight);
2878
- } else if (counter % bigStep === 0) {
2879
- nextCanvasInfo.set(pix, Math.floor(timeScaleHeight / 2));
2880
- } else if (counter % secondStep === 0) {
2881
- nextCanvasInfo.set(pix, Math.floor(timeScaleHeight / 5));
2882
- }
2883
- counter += secondStep;
2884
- }
2885
- return {
2886
- widthX: nextWidthX,
2887
- canvasInfo: nextCanvasInfo,
2888
- timeMarkersWithPositions: nextMarkers
2889
- };
2890
- }, [
2891
- duration,
2892
- samplesPerPixel,
2893
- sampleRate,
2894
- marker,
2895
- bigStep,
2896
- secondStep,
2897
- renderTimestamp,
2898
- timeScaleHeight
2899
- ]);
2844
+ const { widthX, canvasInfo, timeMarkersWithPositions } = tickData;
2900
2845
  const visibleChunkIndices = useVisibleChunkIndices(widthX, MAX_CANVAS_WIDTH4);
2901
2846
  const visibleChunks = visibleChunkIndices.map((i) => {
2902
2847
  const chunkLeft = i * MAX_CANVAS_WIDTH4;
2903
2848
  const chunkWidth = Math.min(widthX - chunkLeft, MAX_CANVAS_WIDTH4);
2904
- return /* @__PURE__ */ jsx25(
2849
+ return /* @__PURE__ */ jsx26(
2905
2850
  TimeTickChunk,
2906
2851
  {
2907
2852
  $cssWidth: chunkWidth,
@@ -2936,15 +2881,7 @@ var TimeScale = (props) => {
2936
2881
  ctx.fillRect(localX, scaleY, 1, scaleHeight);
2937
2882
  }
2938
2883
  }
2939
- }, [
2940
- canvasMapRef,
2941
- duration,
2942
- devicePixelRatio,
2943
- timeColor,
2944
- timeScaleHeight,
2945
- canvasInfo,
2946
- visibleChunkIndices
2947
- ]);
2884
+ }, [canvasMapRef, devicePixelRatio, timeColor, timeScaleHeight, canvasInfo, visibleChunkIndices]);
2948
2885
  return /* @__PURE__ */ jsxs10(PlaylistTimeScaleScroll, { $cssWidth: widthX, $timeScaleHeight: timeScaleHeight, children: [
2949
2886
  visibleMarkers,
2950
2887
  visibleChunks
@@ -2953,64 +2890,22 @@ var TimeScale = (props) => {
2953
2890
  var StyledTimeScale = withTheme2(TimeScale);
2954
2891
 
2955
2892
  // src/components/SmartScale.tsx
2956
- import { jsx as jsx26 } from "react/jsx-runtime";
2893
+ import {
2894
+ PPQN,
2895
+ ticksToSamples,
2896
+ ticksToBarBeatLabel,
2897
+ samplesToPixels,
2898
+ secondsToPixels
2899
+ } from "@waveform-playlist/core";
2900
+ import { jsx as jsx27 } from "react/jsx-runtime";
2957
2901
  var timeinfo = /* @__PURE__ */ new Map([
2958
- [
2959
- 700,
2960
- {
2961
- marker: 1e3,
2962
- bigStep: 500,
2963
- smallStep: 100
2964
- }
2965
- ],
2966
- [
2967
- 1500,
2968
- {
2969
- marker: 2e3,
2970
- bigStep: 1e3,
2971
- smallStep: 200
2972
- }
2973
- ],
2974
- [
2975
- 2500,
2976
- {
2977
- marker: 2e3,
2978
- bigStep: 1e3,
2979
- smallStep: 500
2980
- }
2981
- ],
2982
- [
2983
- 5e3,
2984
- {
2985
- marker: 5e3,
2986
- bigStep: 1e3,
2987
- smallStep: 500
2988
- }
2989
- ],
2990
- [
2991
- 1e4,
2992
- {
2993
- marker: 1e4,
2994
- bigStep: 5e3,
2995
- smallStep: 1e3
2996
- }
2997
- ],
2998
- [
2999
- 12e3,
3000
- {
3001
- marker: 15e3,
3002
- bigStep: 5e3,
3003
- smallStep: 1e3
3004
- }
3005
- ],
3006
- [
3007
- Infinity,
3008
- {
3009
- marker: 3e4,
3010
- bigStep: 1e4,
3011
- smallStep: 5e3
3012
- }
3013
- ]
2902
+ [700, { marker: 1e3, bigStep: 500, smallStep: 100 }],
2903
+ [1500, { marker: 2e3, bigStep: 1e3, smallStep: 200 }],
2904
+ [2500, { marker: 2e3, bigStep: 1e3, smallStep: 500 }],
2905
+ [5e3, { marker: 5e3, bigStep: 1e3, smallStep: 500 }],
2906
+ [1e4, { marker: 1e4, bigStep: 5e3, smallStep: 1e3 }],
2907
+ [12e3, { marker: 15e3, bigStep: 5e3, smallStep: 1e3 }],
2908
+ [Infinity, { marker: 3e4, bigStep: 1e4, smallStep: 5e3 }]
3014
2909
  ]);
3015
2910
  function getScaleInfo(samplesPerPixel) {
3016
2911
  const keys = timeinfo.keys();
@@ -3026,25 +2921,113 @@ function getScaleInfo(samplesPerPixel) {
3026
2921
  }
3027
2922
  return config;
3028
2923
  }
3029
- var SmartScale = ({ renderTimestamp }) => {
3030
- const { samplesPerPixel, duration } = useContext9(PlaylistInfoContext);
3031
- let config = getScaleInfo(samplesPerPixel);
3032
- return /* @__PURE__ */ jsx26(
3033
- StyledTimeScale,
3034
- {
3035
- marker: config.marker,
3036
- bigStep: config.bigStep,
3037
- secondStep: config.smallStep,
3038
- duration,
3039
- renderTimestamp
2924
+ function formatTime2(milliseconds) {
2925
+ const seconds = Math.floor(milliseconds / 1e3);
2926
+ const s = seconds % 60;
2927
+ const m = (seconds - s) / 60;
2928
+ return `${m}:${String(s).padStart(2, "0")}`;
2929
+ }
2930
+ var TimeStamp = styled23.div.attrs((props) => ({
2931
+ style: {
2932
+ left: `${props.$left + 4}px`
2933
+ // Offset 4px to the right of the tick
2934
+ }
2935
+ }))`
2936
+ position: absolute;
2937
+ font-size: 0.75rem; /* Smaller font to prevent overflow */
2938
+ white-space: nowrap; /* Prevent text wrapping */
2939
+ color: ${(props) => props.theme.timeColor}; /* Use theme color instead of inheriting */
2940
+ `;
2941
+ var SmartScale = ({ renderTick }) => {
2942
+ const { samplesPerPixel, sampleRate, duration, timeScaleHeight } = useContext10(PlaylistInfoContext);
2943
+ const beatsAndBars = useBeatsAndBars();
2944
+ const tickData = useMemo4(() => {
2945
+ const widthX = secondsToPixels(duration / 1e3, samplesPerPixel, sampleRate);
2946
+ if (beatsAndBars) {
2947
+ const { bpm, timeSignature, ticksPerBar: tpBar, ticksPerBeat: tpBeat } = beatsAndBars;
2948
+ const canvasInfo2 = /* @__PURE__ */ new Map();
2949
+ const timeMarkersWithPositions2 = [];
2950
+ const durationSeconds = duration / 1e3;
2951
+ const totalTicks = Math.ceil(durationSeconds * bpm * PPQN / 60);
2952
+ const pixelsPerBeat = ticksToSamples(tpBeat, bpm, sampleRate) / samplesPerPixel;
2953
+ const pixelsPerBar = ticksToSamples(tpBar, bpm, sampleRate) / samplesPerPixel;
2954
+ const MIN_TICK_PX = 10;
2955
+ const MIN_LABEL_PX = 30;
2956
+ let tickStep;
2957
+ if (pixelsPerBeat >= MIN_TICK_PX) {
2958
+ tickStep = tpBeat;
2959
+ } else if (pixelsPerBar >= MIN_TICK_PX) {
2960
+ tickStep = tpBar;
2961
+ } else {
2962
+ const barsPerTick = Math.ceil(MIN_TICK_PX / pixelsPerBar);
2963
+ tickStep = tpBar * barsPerTick;
2964
+ }
2965
+ let labelStep;
2966
+ if (pixelsPerBeat >= MIN_LABEL_PX) {
2967
+ labelStep = tpBeat;
2968
+ } else if (pixelsPerBar >= MIN_LABEL_PX) {
2969
+ labelStep = tpBar;
2970
+ } else {
2971
+ const barsPerLabel = Math.ceil(MIN_LABEL_PX / pixelsPerBar);
2972
+ labelStep = tpBar * barsPerLabel;
2973
+ }
2974
+ for (let tick = 0; tick <= totalTicks; tick += tickStep) {
2975
+ const samples = ticksToSamples(tick, bpm, sampleRate);
2976
+ const pix = samplesToPixels(samples, samplesPerPixel);
2977
+ if (pix >= widthX) break;
2978
+ const isBarLine = tick % tpBar === 0;
2979
+ const isLabelTick = tick % labelStep === 0;
2980
+ const tickHeight = isBarLine ? timeScaleHeight : isLabelTick ? Math.floor(timeScaleHeight / 2) : Math.floor(timeScaleHeight / 5);
2981
+ canvasInfo2.set(pix, tickHeight);
2982
+ if (isLabelTick) {
2983
+ const label = ticksToBarBeatLabel(tick, timeSignature);
2984
+ const element = renderTick ? /* @__PURE__ */ jsx27(React19.Fragment, { children: renderTick(label, pix) }, `bb-${tick}`) : /* @__PURE__ */ jsx27(
2985
+ "div",
2986
+ {
2987
+ style: {
2988
+ position: "absolute",
2989
+ left: `${pix + 4}px`,
2990
+ fontSize: "0.75rem",
2991
+ whiteSpace: "nowrap"
2992
+ },
2993
+ children: label
2994
+ },
2995
+ `bb-${tick}`
2996
+ );
2997
+ timeMarkersWithPositions2.push({ pix, element });
2998
+ }
2999
+ }
3000
+ return { widthX, canvasInfo: canvasInfo2, timeMarkersWithPositions: timeMarkersWithPositions2 };
3040
3001
  }
3041
- );
3002
+ const config = getScaleInfo(samplesPerPixel);
3003
+ const { marker, bigStep, smallStep } = config;
3004
+ const canvasInfo = /* @__PURE__ */ new Map();
3005
+ const timeMarkersWithPositions = [];
3006
+ const pixPerSec = sampleRate / samplesPerPixel;
3007
+ let counter = 0;
3008
+ for (let i = 0; i < widthX; i += pixPerSec * smallStep / 1e3) {
3009
+ const pix = Math.floor(i);
3010
+ if (counter % marker === 0) {
3011
+ const timestamp = formatTime2(counter);
3012
+ const element = renderTick ? /* @__PURE__ */ jsx27(React19.Fragment, { children: renderTick(timestamp, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ jsx27(TimeStamp, { $left: pix, children: timestamp }, timestamp);
3013
+ timeMarkersWithPositions.push({ pix, element });
3014
+ canvasInfo.set(pix, timeScaleHeight);
3015
+ } else if (counter % bigStep === 0) {
3016
+ canvasInfo.set(pix, Math.floor(timeScaleHeight / 2));
3017
+ } else if (counter % smallStep === 0) {
3018
+ canvasInfo.set(pix, Math.floor(timeScaleHeight / 5));
3019
+ }
3020
+ counter += smallStep;
3021
+ }
3022
+ return { widthX, canvasInfo, timeMarkersWithPositions };
3023
+ }, [beatsAndBars, duration, samplesPerPixel, sampleRate, timeScaleHeight, renderTick]);
3024
+ return /* @__PURE__ */ jsx27(StyledTimeScale, { tickData });
3042
3025
  };
3043
3026
 
3044
3027
  // src/components/TimeFormatSelect.tsx
3045
- import styled23 from "styled-components";
3046
- import { jsx as jsx27 } from "react/jsx-runtime";
3047
- var SelectWrapper = styled23.div`
3028
+ import styled24 from "styled-components";
3029
+ import { jsx as jsx28 } from "react/jsx-runtime";
3030
+ var SelectWrapper = styled24.div`
3048
3031
  display: inline-flex;
3049
3032
  align-items: center;
3050
3033
  gap: 0.5rem;
@@ -3066,7 +3049,7 @@ var TimeFormatSelect = ({
3066
3049
  const handleChange = (e) => {
3067
3050
  onChange(e.target.value);
3068
3051
  };
3069
- return /* @__PURE__ */ jsx27(SelectWrapper, { className, children: /* @__PURE__ */ jsx27(
3052
+ return /* @__PURE__ */ jsx28(SelectWrapper, { className, children: /* @__PURE__ */ jsx28(
3070
3053
  BaseSelect,
3071
3054
  {
3072
3055
  className: "time-format",
@@ -3074,15 +3057,15 @@ var TimeFormatSelect = ({
3074
3057
  onChange: handleChange,
3075
3058
  disabled,
3076
3059
  "aria-label": "Time format selection",
3077
- children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ jsx27("option", { value: option.value, children: option.label }, option.value))
3060
+ children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ jsx28("option", { value: option.value, children: option.label }, option.value))
3078
3061
  }
3079
3062
  ) });
3080
3063
  };
3081
3064
 
3082
3065
  // src/components/Track.tsx
3083
- import styled24 from "styled-components";
3084
- import { jsx as jsx28 } from "react/jsx-runtime";
3085
- var Container = styled24.div.attrs((props) => ({
3066
+ import styled25 from "styled-components";
3067
+ import { jsx as jsx29 } from "react/jsx-runtime";
3068
+ var Container = styled25.div.attrs((props) => ({
3086
3069
  style: {
3087
3070
  height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`
3088
3071
  }
@@ -3090,7 +3073,7 @@ var Container = styled24.div.attrs((props) => ({
3090
3073
  position: relative;
3091
3074
  ${(props) => props.$width !== void 0 && `width: ${props.$width}px;`}
3092
3075
  `;
3093
- var ChannelContainer = styled24.div.attrs((props) => ({
3076
+ var ChannelContainer = styled25.div.attrs((props) => ({
3094
3077
  style: {
3095
3078
  paddingLeft: `${props.$offset || 0}px`
3096
3079
  }
@@ -3112,7 +3095,7 @@ var Track = ({
3112
3095
  isSelected: _isSelected = false
3113
3096
  }) => {
3114
3097
  const { waveHeight } = usePlaylistInfo();
3115
- return /* @__PURE__ */ jsx28(
3098
+ return /* @__PURE__ */ jsx29(
3116
3099
  Container,
3117
3100
  {
3118
3101
  $numChannels: numChannels,
@@ -3120,7 +3103,7 @@ var Track = ({
3120
3103
  $waveHeight: waveHeight,
3121
3104
  $width: width,
3122
3105
  $hasClipHeaders: hasClipHeaders,
3123
- children: /* @__PURE__ */ jsx28(
3106
+ children: /* @__PURE__ */ jsx29(
3124
3107
  ChannelContainer,
3125
3108
  {
3126
3109
  $backgroundColor: backgroundColor,
@@ -3135,8 +3118,8 @@ var Track = ({
3135
3118
  };
3136
3119
 
3137
3120
  // src/components/TrackControls/Button.tsx
3138
- import styled25 from "styled-components";
3139
- var Button = styled25.button.attrs({
3121
+ import styled26 from "styled-components";
3122
+ var Button = styled26.button.attrs({
3140
3123
  type: "button"
3141
3124
  })`
3142
3125
  display: inline-block;
@@ -3211,8 +3194,8 @@ var Button = styled25.button.attrs({
3211
3194
  `;
3212
3195
 
3213
3196
  // src/components/TrackControls/ButtonGroup.tsx
3214
- import styled26 from "styled-components";
3215
- var ButtonGroup = styled26.div`
3197
+ import styled27 from "styled-components";
3198
+ var ButtonGroup = styled27.div`
3216
3199
  margin-bottom: 0.3rem;
3217
3200
 
3218
3201
  button:not(:first-child) {
@@ -3227,10 +3210,10 @@ var ButtonGroup = styled26.div`
3227
3210
  `;
3228
3211
 
3229
3212
  // src/components/TrackControls/CloseButton.tsx
3230
- import styled27 from "styled-components";
3213
+ import styled28 from "styled-components";
3231
3214
  import { X as XIcon } from "@phosphor-icons/react";
3232
- import { jsx as jsx29 } from "react/jsx-runtime";
3233
- var StyledCloseButton = styled27.button`
3215
+ import { jsx as jsx30 } from "react/jsx-runtime";
3216
+ var StyledCloseButton = styled28.button`
3234
3217
  position: absolute;
3235
3218
  left: 0;
3236
3219
  top: 0;
@@ -3253,11 +3236,11 @@ var StyledCloseButton = styled27.button`
3253
3236
  color: #dc3545;
3254
3237
  }
3255
3238
  `;
3256
- var CloseButton = ({ onClick, title = "Remove track" }) => /* @__PURE__ */ jsx29(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ jsx29(XIcon, { size: 12, weight: "bold" }) });
3239
+ var CloseButton = ({ onClick, title = "Remove track" }) => /* @__PURE__ */ jsx30(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ jsx30(XIcon, { size: 12, weight: "bold" }) });
3257
3240
 
3258
3241
  // src/components/TrackControls/Controls.tsx
3259
- import styled28 from "styled-components";
3260
- var Controls = styled28.div`
3242
+ import styled29 from "styled-components";
3243
+ var Controls = styled29.div`
3261
3244
  background: transparent;
3262
3245
  width: 100%;
3263
3246
  height: 100%;
@@ -3273,8 +3256,8 @@ var Controls = styled28.div`
3273
3256
  `;
3274
3257
 
3275
3258
  // src/components/TrackControls/Header.tsx
3276
- import styled29 from "styled-components";
3277
- var Header = styled29.header`
3259
+ import styled30 from "styled-components";
3260
+ var Header = styled30.header`
3278
3261
  overflow: hidden;
3279
3262
  height: 26px;
3280
3263
  width: 100%;
@@ -3289,27 +3272,27 @@ var Header = styled29.header`
3289
3272
 
3290
3273
  // src/components/TrackControls/VolumeDownIcon.tsx
3291
3274
  import { SpeakerLowIcon } from "@phosphor-icons/react";
3292
- import { jsx as jsx30 } from "react/jsx-runtime";
3293
- var VolumeDownIcon = (props) => /* @__PURE__ */ jsx30(SpeakerLowIcon, { weight: "light", ...props });
3275
+ import { jsx as jsx31 } from "react/jsx-runtime";
3276
+ var VolumeDownIcon = (props) => /* @__PURE__ */ jsx31(SpeakerLowIcon, { weight: "light", ...props });
3294
3277
 
3295
3278
  // src/components/TrackControls/VolumeUpIcon.tsx
3296
3279
  import { SpeakerHighIcon } from "@phosphor-icons/react";
3297
- import { jsx as jsx31 } from "react/jsx-runtime";
3298
- var VolumeUpIcon = (props) => /* @__PURE__ */ jsx31(SpeakerHighIcon, { weight: "light", ...props });
3280
+ import { jsx as jsx32 } from "react/jsx-runtime";
3281
+ var VolumeUpIcon = (props) => /* @__PURE__ */ jsx32(SpeakerHighIcon, { weight: "light", ...props });
3299
3282
 
3300
3283
  // src/components/TrackControls/TrashIcon.tsx
3301
3284
  import { TrashIcon as PhosphorTrashIcon } from "@phosphor-icons/react";
3302
- import { jsx as jsx32 } from "react/jsx-runtime";
3303
- var TrashIcon = (props) => /* @__PURE__ */ jsx32(PhosphorTrashIcon, { weight: "light", ...props });
3285
+ import { jsx as jsx33 } from "react/jsx-runtime";
3286
+ var TrashIcon = (props) => /* @__PURE__ */ jsx33(PhosphorTrashIcon, { weight: "light", ...props });
3304
3287
 
3305
3288
  // src/components/TrackControls/DotsIcon.tsx
3306
3289
  import { DotsThreeIcon } from "@phosphor-icons/react";
3307
- import { jsx as jsx33 } from "react/jsx-runtime";
3308
- var DotsIcon = (props) => /* @__PURE__ */ jsx33(DotsThreeIcon, { weight: "bold", ...props });
3290
+ import { jsx as jsx34 } from "react/jsx-runtime";
3291
+ var DotsIcon = (props) => /* @__PURE__ */ jsx34(DotsThreeIcon, { weight: "bold", ...props });
3309
3292
 
3310
3293
  // src/components/TrackControls/Slider.tsx
3311
- import styled30 from "styled-components";
3312
- var Slider = styled30(BaseSlider)`
3294
+ import styled31 from "styled-components";
3295
+ var Slider = styled31(BaseSlider)`
3313
3296
  width: 75%;
3314
3297
  height: 5px;
3315
3298
  background: ${(props) => props.theme.sliderTrackColor};
@@ -3361,8 +3344,8 @@ var Slider = styled30(BaseSlider)`
3361
3344
  `;
3362
3345
 
3363
3346
  // src/components/TrackControls/SliderWrapper.tsx
3364
- import styled31 from "styled-components";
3365
- var SliderWrapper = styled31.label`
3347
+ import styled32 from "styled-components";
3348
+ var SliderWrapper = styled32.label`
3366
3349
  width: 100%;
3367
3350
  display: flex;
3368
3351
  justify-content: space-between;
@@ -3373,15 +3356,15 @@ var SliderWrapper = styled31.label`
3373
3356
  `;
3374
3357
 
3375
3358
  // src/components/TrackMenu.tsx
3376
- import React19, { useState as useState6, useEffect as useEffect9, useRef as useRef8, useCallback as useCallback5 } from "react";
3359
+ import React20, { useState as useState6, useEffect as useEffect9, useRef as useRef8, useCallback as useCallback5 } from "react";
3377
3360
  import { createPortal } from "react-dom";
3378
- import styled32 from "styled-components";
3379
- import { jsx as jsx34, jsxs as jsxs11 } from "react/jsx-runtime";
3380
- var MenuContainer = styled32.div`
3361
+ import styled33 from "styled-components";
3362
+ import { jsx as jsx35, jsxs as jsxs11 } from "react/jsx-runtime";
3363
+ var MenuContainer = styled33.div`
3381
3364
  position: relative;
3382
3365
  display: inline-block;
3383
3366
  `;
3384
- var MenuButton = styled32.button`
3367
+ var MenuButton = styled33.button`
3385
3368
  background: none;
3386
3369
  border: none;
3387
3370
  cursor: pointer;
@@ -3397,7 +3380,7 @@ var MenuButton = styled32.button`
3397
3380
  }
3398
3381
  `;
3399
3382
  var DROPDOWN_MIN_WIDTH = 180;
3400
- var Dropdown = styled32.div`
3383
+ var Dropdown = styled33.div`
3401
3384
  position: fixed;
3402
3385
  top: ${(p) => p.$top}px;
3403
3386
  left: ${(p) => p.$left}px;
@@ -3410,7 +3393,7 @@ var Dropdown = styled32.div`
3410
3393
  min-width: ${DROPDOWN_MIN_WIDTH}px;
3411
3394
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
3412
3395
  `;
3413
- var Divider = styled32.hr`
3396
+ var Divider = styled33.hr`
3414
3397
  border: none;
3415
3398
  border-top: 1px solid rgba(128, 128, 128, 0.3);
3416
3399
  margin: 0.35rem 0;
@@ -3474,7 +3457,7 @@ var TrackMenu = ({ items: itemsProp }) => {
3474
3457
  };
3475
3458
  }, [open]);
3476
3459
  return /* @__PURE__ */ jsxs11(MenuContainer, { children: [
3477
- /* @__PURE__ */ jsx34(
3460
+ /* @__PURE__ */ jsx35(
3478
3461
  MenuButton,
3479
3462
  {
3480
3463
  ref: buttonRef,
@@ -3485,19 +3468,19 @@ var TrackMenu = ({ items: itemsProp }) => {
3485
3468
  onMouseDown: (e) => e.stopPropagation(),
3486
3469
  title: "Track menu",
3487
3470
  "aria-label": "Track menu",
3488
- children: /* @__PURE__ */ jsx34(DotsIcon, { size: 16 })
3471
+ children: /* @__PURE__ */ jsx35(DotsIcon, { size: 16 })
3489
3472
  }
3490
3473
  ),
3491
3474
  open && typeof document !== "undefined" && createPortal(
3492
- /* @__PURE__ */ jsx34(
3475
+ /* @__PURE__ */ jsx35(
3493
3476
  Dropdown,
3494
3477
  {
3495
3478
  ref: dropdownRef,
3496
3479
  $top: dropdownPos.top,
3497
3480
  $left: dropdownPos.left,
3498
3481
  onMouseDown: (e) => e.stopPropagation(),
3499
- children: items.map((item, index) => /* @__PURE__ */ jsxs11(React19.Fragment, { children: [
3500
- index > 0 && /* @__PURE__ */ jsx34(Divider, {}),
3482
+ children: items.map((item, index) => /* @__PURE__ */ jsxs11(React20.Fragment, { children: [
3483
+ index > 0 && /* @__PURE__ */ jsx35(Divider, {}),
3501
3484
  item.content
3502
3485
  ] }, item.id))
3503
3486
  }
@@ -3506,6 +3489,26 @@ var TrackMenu = ({ items: itemsProp }) => {
3506
3489
  )
3507
3490
  ] });
3508
3491
  };
3492
+
3493
+ // src/utils/conversions.ts
3494
+ function samplesToSeconds(samples, sampleRate) {
3495
+ return samples / sampleRate;
3496
+ }
3497
+ function secondsToSamples(seconds, sampleRate) {
3498
+ return Math.ceil(seconds * sampleRate);
3499
+ }
3500
+ function samplesToPixels2(samples, samplesPerPixel) {
3501
+ return Math.floor(samples / samplesPerPixel);
3502
+ }
3503
+ function pixelsToSamples(pixels, samplesPerPixel) {
3504
+ return Math.floor(pixels * samplesPerPixel);
3505
+ }
3506
+ function pixelsToSeconds(pixels, samplesPerPixel, sampleRate) {
3507
+ return pixels * samplesPerPixel / sampleRate;
3508
+ }
3509
+ function secondsToPixels2(seconds, samplesPerPixel, sampleRate) {
3510
+ return Math.ceil(seconds * sampleRate / samplesPerPixel);
3511
+ }
3509
3512
  export {
3510
3513
  AudioPosition,
3511
3514
  AutomaticScrollCheckbox,
@@ -3515,9 +3518,12 @@ export {
3515
3518
  BaseCheckboxWrapper,
3516
3519
  BaseControlButton,
3517
3520
  BaseInput,
3521
+ BaseInputSmall,
3518
3522
  BaseLabel,
3519
3523
  BaseSelect,
3524
+ BaseSelectSmall,
3520
3525
  BaseSlider,
3526
+ BeatsAndBarsProvider,
3521
3527
  Button,
3522
3528
  ButtonGroup,
3523
3529
  CLIP_BOUNDARY_WIDTH,
@@ -3571,14 +3577,16 @@ export {
3571
3577
  darkTheme,
3572
3578
  defaultTheme,
3573
3579
  formatTime,
3580
+ getScaleInfo,
3574
3581
  isWaveformGradient,
3575
3582
  parseTime,
3576
3583
  pixelsToSamples,
3577
3584
  pixelsToSeconds,
3578
- samplesToPixels,
3585
+ samplesToPixels2 as samplesToPixels,
3579
3586
  samplesToSeconds,
3580
- secondsToPixels,
3587
+ secondsToPixels2 as secondsToPixels,
3581
3588
  secondsToSamples,
3589
+ useBeatsAndBars,
3582
3590
  useClipViewportOrigin,
3583
3591
  useDevicePixelRatio,
3584
3592
  usePlaylistInfo,