@waveform-playlist/ui-components 12.0.0 → 13.0.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 CHANGED
@@ -353,8 +353,20 @@ interface PlayheadProps {
353
353
  controlsOffset?: number;
354
354
  /** Function to get current audio context time - required for smooth animation */
355
355
  getAudioContextTime?: () => number;
356
- /** Returns current playback time (auto-wraps at loop boundaries). Preferred over manual elapsed calculation. */
356
+ /**
357
+ * Returns raw playback time from the engine (auto-wraps at loop boundaries).
358
+ * This is the scheduling position — for playhead display use `visualTimeRef`
359
+ * which already has `outputLatency` and `lookAhead` subtracted.
360
+ */
357
361
  getPlaybackTime?: () => number;
362
+ /**
363
+ * Ref to the visually-aligned playback time (raw time minus `outputLatency`
364
+ * and `engine.lookAhead`), kept current by the provider's animation loop
365
+ * during playback and by pause/stop/seek paths when stopped. Use this for
366
+ * playhead positioning so it lines up with audible output and matches the
367
+ * progress fill in `ChannelWithProgress`.
368
+ */
369
+ visualTimeRef?: react__default.RefObject<number>;
358
370
  }
359
371
  /**
360
372
  * Type for custom playhead render functions.
@@ -468,9 +480,14 @@ interface SelectionTimeInputsProps {
468
480
  }
469
481
  declare const SelectionTimeInputs: react__default.FC<SelectionTimeInputsProps>;
470
482
 
471
- interface SpectrogramWorkerCanvasApi {
472
- registerCanvas(canvasId: string, canvas: OffscreenCanvas): void;
473
- unregisterCanvas(canvasId: string): void;
483
+ interface SpectrogramCanvasRegistration {
484
+ canvasId: string;
485
+ canvas: OffscreenCanvas;
486
+ clipId: string;
487
+ channelIndex: number;
488
+ chunkIndex: number;
489
+ widthPx: number;
490
+ heightPx: number;
474
491
  }
475
492
  interface SpectrogramChannelProps {
476
493
  /** Visual position index — used for CSS positioning (top offset). */
@@ -485,12 +502,12 @@ interface SpectrogramChannelProps {
485
502
  devicePixelRatio?: number;
486
503
  /** Samples per pixel at current zoom level */
487
504
  samplesPerPixel: number;
488
- /** Worker API for transferring canvas ownership. Rendering is done in the worker. */
489
- workerApi: SpectrogramWorkerCanvasApi;
490
- /** Clip ID used to construct unique canvas IDs for worker registration */
505
+ /** Clip ID used to construct unique canvas IDs */
491
506
  clipId: string;
492
- /** Callback when canvases are registered with the worker, providing canvas IDs and widths */
493
- onCanvasesReady?: (canvasIds: string[], canvasWidths: number[]) => void;
507
+ /** Single-call canvas registration. Receives the transferred OffscreenCanvas + metadata. */
508
+ onCanvasRegister: (reg: SpectrogramCanvasRegistration) => void;
509
+ /** Counterpart for chunk unmount / component unmount. */
510
+ onCanvasUnregister: (canvasId: string) => void;
494
511
  }
495
512
  declare const SpectrogramChannel: FunctionComponent<SpectrogramChannelProps>;
496
513
 
@@ -507,12 +524,12 @@ interface SmartChannelProps {
507
524
  renderMode?: RenderMode;
508
525
  /** Samples per pixel at current zoom level */
509
526
  samplesPerPixel?: number;
510
- /** Worker API for OffscreenCanvas transfer */
511
- spectrogramWorkerApi?: SpectrogramWorkerCanvasApi;
512
- /** Clip ID for worker canvas registration */
527
+ /** Clip ID for spectrogram canvas registration */
513
528
  spectrogramClipId?: string;
514
- /** Callback when canvases are registered with the worker */
515
- spectrogramOnCanvasesReady?: (canvasIds: string[], canvasWidths: number[]) => void;
529
+ /** Single-call registration for spectrogram canvases (from SpectrogramIntegration). */
530
+ spectrogramOnCanvasRegister?: (reg: SpectrogramCanvasRegistration) => void;
531
+ /** Counterpart for chunk unmount / unmount. */
532
+ spectrogramOnCanvasUnregister?: (canvasId: string) => void;
516
533
  /** MIDI note data for piano-roll rendering */
517
534
  midiNotes?: MidiNoteData[];
518
535
  /** Sample rate for MIDI note time → pixel conversion */
@@ -921,4 +938,4 @@ declare const BaseSlider: styled_components_dist_types.IStyledComponentBase<"web
921
938
  ref?: ((instance: HTMLInputElement | null) => void | react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | react.RefObject<HTMLInputElement> | null | undefined;
922
939
  }>, never>, never>> & string;
923
940
 
924
- export { AudioPosition, type AudioPositionProps, AutomaticScrollCheckbox, type AutomaticScrollCheckboxProps, BaseButton, BaseCheckbox, BaseCheckboxLabel, BaseCheckboxWrapper, BaseControlButton, BaseInput, BaseInputSmall, BaseLabel, BaseSelect, BaseSelectSmall, BaseSlider, type BeatsAndBarsContextValue, BeatsAndBarsProvider, type BeatsAndBarsProviderProps, Button, ButtonGroup, CLIP_BOUNDARY_WIDTH, CLIP_BOUNDARY_WIDTH_TOUCH, CLIP_HEADER_HEIGHT, Channel, type ChannelProps, Clip, ClipBoundary, type ClipBoundaryProps, ClipHeader, ClipHeaderPresentational, type ClipHeaderPresentationalProps, type ClipHeaderProps, type ClipProps, ClipViewportOriginProvider, CloseButton, type ColorStop, Controls$1 as Controls, DevicePixelRatioProvider, DotsIcon, type DragHandleProps, FadeOverlay, type FadeOverlayProps, type GradientStop, Header, InlineLabel, LoopRegion, LoopRegionMarkers, type LoopRegionMarkersProps, type LoopRegionProps, MasterVolumeControl, type MasterVolumeControlProps, PianoRollChannel, type PianoRollChannelProps, Playhead, type PlayheadProps, PlayheadWithMarker, Playlist, PlaylistErrorBoundary, type PlaylistErrorBoundaryProps, PlaylistInfoContext, type PlaylistProps, PlayoutProvider, type PrecomputedTickData, type RenderPlayheadFunction, type ScaleMode, ScreenReaderOnly, type ScrollViewport, ScrollViewportProvider, SegmentedVUMeter, type SegmentedVUMeterProps, Selection, type SelectionProps, SelectionTimeInputs, type SelectionTimeInputsProps, Slider, SliderWrapper, SmartChannel, type SmartChannelProps, SmartScale, type SmartScaleProps, type SnapTo, SpectrogramChannel, type SpectrogramChannelProps, SpectrogramLabels, type SpectrogramLabelsProps, type SpectrogramWorkerCanvasApi, StyledPlaylist, StyledTimeScale, type TimeFormat, TimeFormatSelect, type TimeFormatSelectProps, TimeInput, type TimeInputProps, TimeScale, type TimeScaleProps, TimescaleLoopRegion, type TimescaleLoopRegionProps, Track, TrackControlsContext, TrackMenu, type TrackMenuItem, type TrackMenuProps, type TrackProps, TrashIcon, VolumeDownIcon, VolumeUpIcon, type WaveformColor, type WaveformDrawMode, type WaveformGradient, type WaveformPlaylistTheme, darkTheme, defaultTheme, formatTime, getScaleInfo, isWaveformGradient, parseTime, pixelsToSamples, pixelsToSeconds, samplesToPixels, samplesToSeconds, secondsToPixels, secondsToSamples, useBeatsAndBars, useClipViewportOrigin, useDevicePixelRatio, usePlaylistInfo, usePlayoutStatus, usePlayoutStatusUpdate, useScrollViewport, useScrollViewportSelector, useTheme, useTrackControls, useVisibleChunkIndices, waveformColorToCss };
941
+ export { AudioPosition, type AudioPositionProps, AutomaticScrollCheckbox, type AutomaticScrollCheckboxProps, BaseButton, BaseCheckbox, BaseCheckboxLabel, BaseCheckboxWrapper, BaseControlButton, BaseInput, BaseInputSmall, BaseLabel, BaseSelect, BaseSelectSmall, BaseSlider, type BeatsAndBarsContextValue, BeatsAndBarsProvider, type BeatsAndBarsProviderProps, Button, ButtonGroup, CLIP_BOUNDARY_WIDTH, CLIP_BOUNDARY_WIDTH_TOUCH, CLIP_HEADER_HEIGHT, Channel, type ChannelProps, Clip, ClipBoundary, type ClipBoundaryProps, ClipHeader, ClipHeaderPresentational, type ClipHeaderPresentationalProps, type ClipHeaderProps, type ClipProps, ClipViewportOriginProvider, CloseButton, type ColorStop, Controls$1 as Controls, DevicePixelRatioProvider, DotsIcon, type DragHandleProps, FadeOverlay, type FadeOverlayProps, type GradientStop, Header, InlineLabel, LoopRegion, LoopRegionMarkers, type LoopRegionMarkersProps, type LoopRegionProps, MasterVolumeControl, type MasterVolumeControlProps, PianoRollChannel, type PianoRollChannelProps, Playhead, type PlayheadProps, PlayheadWithMarker, Playlist, PlaylistErrorBoundary, type PlaylistErrorBoundaryProps, PlaylistInfoContext, type PlaylistProps, PlayoutProvider, type PrecomputedTickData, type RenderPlayheadFunction, type ScaleMode, ScreenReaderOnly, type ScrollViewport, ScrollViewportProvider, SegmentedVUMeter, type SegmentedVUMeterProps, Selection, type SelectionProps, SelectionTimeInputs, type SelectionTimeInputsProps, Slider, SliderWrapper, SmartChannel, type SmartChannelProps, SmartScale, type SmartScaleProps, type SnapTo, type SpectrogramCanvasRegistration, SpectrogramChannel, type SpectrogramChannelProps, SpectrogramLabels, type SpectrogramLabelsProps, StyledPlaylist, StyledTimeScale, type TimeFormat, TimeFormatSelect, type TimeFormatSelectProps, TimeInput, type TimeInputProps, TimeScale, type TimeScaleProps, TimescaleLoopRegion, type TimescaleLoopRegionProps, Track, TrackControlsContext, TrackMenu, type TrackMenuItem, type TrackMenuProps, type TrackProps, TrashIcon, VolumeDownIcon, VolumeUpIcon, type WaveformColor, type WaveformDrawMode, type WaveformGradient, type WaveformPlaylistTheme, darkTheme, defaultTheme, formatTime, getScaleInfo, isWaveformGradient, parseTime, pixelsToSamples, pixelsToSeconds, samplesToPixels, samplesToSeconds, secondsToPixels, secondsToSamples, useBeatsAndBars, useClipViewportOrigin, useDevicePixelRatio, usePlaylistInfo, usePlayoutStatus, usePlayoutStatusUpdate, useScrollViewport, useScrollViewportSelector, useTheme, useTrackControls, useVisibleChunkIndices, waveformColorToCss };
package/dist/index.d.ts CHANGED
@@ -353,8 +353,20 @@ interface PlayheadProps {
353
353
  controlsOffset?: number;
354
354
  /** Function to get current audio context time - required for smooth animation */
355
355
  getAudioContextTime?: () => number;
356
- /** Returns current playback time (auto-wraps at loop boundaries). Preferred over manual elapsed calculation. */
356
+ /**
357
+ * Returns raw playback time from the engine (auto-wraps at loop boundaries).
358
+ * This is the scheduling position — for playhead display use `visualTimeRef`
359
+ * which already has `outputLatency` and `lookAhead` subtracted.
360
+ */
357
361
  getPlaybackTime?: () => number;
362
+ /**
363
+ * Ref to the visually-aligned playback time (raw time minus `outputLatency`
364
+ * and `engine.lookAhead`), kept current by the provider's animation loop
365
+ * during playback and by pause/stop/seek paths when stopped. Use this for
366
+ * playhead positioning so it lines up with audible output and matches the
367
+ * progress fill in `ChannelWithProgress`.
368
+ */
369
+ visualTimeRef?: react__default.RefObject<number>;
358
370
  }
359
371
  /**
360
372
  * Type for custom playhead render functions.
@@ -468,9 +480,14 @@ interface SelectionTimeInputsProps {
468
480
  }
469
481
  declare const SelectionTimeInputs: react__default.FC<SelectionTimeInputsProps>;
470
482
 
471
- interface SpectrogramWorkerCanvasApi {
472
- registerCanvas(canvasId: string, canvas: OffscreenCanvas): void;
473
- unregisterCanvas(canvasId: string): void;
483
+ interface SpectrogramCanvasRegistration {
484
+ canvasId: string;
485
+ canvas: OffscreenCanvas;
486
+ clipId: string;
487
+ channelIndex: number;
488
+ chunkIndex: number;
489
+ widthPx: number;
490
+ heightPx: number;
474
491
  }
475
492
  interface SpectrogramChannelProps {
476
493
  /** Visual position index — used for CSS positioning (top offset). */
@@ -485,12 +502,12 @@ interface SpectrogramChannelProps {
485
502
  devicePixelRatio?: number;
486
503
  /** Samples per pixel at current zoom level */
487
504
  samplesPerPixel: number;
488
- /** Worker API for transferring canvas ownership. Rendering is done in the worker. */
489
- workerApi: SpectrogramWorkerCanvasApi;
490
- /** Clip ID used to construct unique canvas IDs for worker registration */
505
+ /** Clip ID used to construct unique canvas IDs */
491
506
  clipId: string;
492
- /** Callback when canvases are registered with the worker, providing canvas IDs and widths */
493
- onCanvasesReady?: (canvasIds: string[], canvasWidths: number[]) => void;
507
+ /** Single-call canvas registration. Receives the transferred OffscreenCanvas + metadata. */
508
+ onCanvasRegister: (reg: SpectrogramCanvasRegistration) => void;
509
+ /** Counterpart for chunk unmount / component unmount. */
510
+ onCanvasUnregister: (canvasId: string) => void;
494
511
  }
495
512
  declare const SpectrogramChannel: FunctionComponent<SpectrogramChannelProps>;
496
513
 
@@ -507,12 +524,12 @@ interface SmartChannelProps {
507
524
  renderMode?: RenderMode;
508
525
  /** Samples per pixel at current zoom level */
509
526
  samplesPerPixel?: number;
510
- /** Worker API for OffscreenCanvas transfer */
511
- spectrogramWorkerApi?: SpectrogramWorkerCanvasApi;
512
- /** Clip ID for worker canvas registration */
527
+ /** Clip ID for spectrogram canvas registration */
513
528
  spectrogramClipId?: string;
514
- /** Callback when canvases are registered with the worker */
515
- spectrogramOnCanvasesReady?: (canvasIds: string[], canvasWidths: number[]) => void;
529
+ /** Single-call registration for spectrogram canvases (from SpectrogramIntegration). */
530
+ spectrogramOnCanvasRegister?: (reg: SpectrogramCanvasRegistration) => void;
531
+ /** Counterpart for chunk unmount / unmount. */
532
+ spectrogramOnCanvasUnregister?: (canvasId: string) => void;
516
533
  /** MIDI note data for piano-roll rendering */
517
534
  midiNotes?: MidiNoteData[];
518
535
  /** Sample rate for MIDI note time → pixel conversion */
@@ -921,4 +938,4 @@ declare const BaseSlider: styled_components_dist_types.IStyledComponentBase<"web
921
938
  ref?: ((instance: HTMLInputElement | null) => void | react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | react.RefObject<HTMLInputElement> | null | undefined;
922
939
  }>, never>, never>> & string;
923
940
 
924
- export { AudioPosition, type AudioPositionProps, AutomaticScrollCheckbox, type AutomaticScrollCheckboxProps, BaseButton, BaseCheckbox, BaseCheckboxLabel, BaseCheckboxWrapper, BaseControlButton, BaseInput, BaseInputSmall, BaseLabel, BaseSelect, BaseSelectSmall, BaseSlider, type BeatsAndBarsContextValue, BeatsAndBarsProvider, type BeatsAndBarsProviderProps, Button, ButtonGroup, CLIP_BOUNDARY_WIDTH, CLIP_BOUNDARY_WIDTH_TOUCH, CLIP_HEADER_HEIGHT, Channel, type ChannelProps, Clip, ClipBoundary, type ClipBoundaryProps, ClipHeader, ClipHeaderPresentational, type ClipHeaderPresentationalProps, type ClipHeaderProps, type ClipProps, ClipViewportOriginProvider, CloseButton, type ColorStop, Controls$1 as Controls, DevicePixelRatioProvider, DotsIcon, type DragHandleProps, FadeOverlay, type FadeOverlayProps, type GradientStop, Header, InlineLabel, LoopRegion, LoopRegionMarkers, type LoopRegionMarkersProps, type LoopRegionProps, MasterVolumeControl, type MasterVolumeControlProps, PianoRollChannel, type PianoRollChannelProps, Playhead, type PlayheadProps, PlayheadWithMarker, Playlist, PlaylistErrorBoundary, type PlaylistErrorBoundaryProps, PlaylistInfoContext, type PlaylistProps, PlayoutProvider, type PrecomputedTickData, type RenderPlayheadFunction, type ScaleMode, ScreenReaderOnly, type ScrollViewport, ScrollViewportProvider, SegmentedVUMeter, type SegmentedVUMeterProps, Selection, type SelectionProps, SelectionTimeInputs, type SelectionTimeInputsProps, Slider, SliderWrapper, SmartChannel, type SmartChannelProps, SmartScale, type SmartScaleProps, type SnapTo, SpectrogramChannel, type SpectrogramChannelProps, SpectrogramLabels, type SpectrogramLabelsProps, type SpectrogramWorkerCanvasApi, StyledPlaylist, StyledTimeScale, type TimeFormat, TimeFormatSelect, type TimeFormatSelectProps, TimeInput, type TimeInputProps, TimeScale, type TimeScaleProps, TimescaleLoopRegion, type TimescaleLoopRegionProps, Track, TrackControlsContext, TrackMenu, type TrackMenuItem, type TrackMenuProps, type TrackProps, TrashIcon, VolumeDownIcon, VolumeUpIcon, type WaveformColor, type WaveformDrawMode, type WaveformGradient, type WaveformPlaylistTheme, darkTheme, defaultTheme, formatTime, getScaleInfo, isWaveformGradient, parseTime, pixelsToSamples, pixelsToSeconds, samplesToPixels, samplesToSeconds, secondsToPixels, secondsToSamples, useBeatsAndBars, useClipViewportOrigin, useDevicePixelRatio, usePlaylistInfo, usePlayoutStatus, usePlayoutStatusUpdate, useScrollViewport, useScrollViewportSelector, useTheme, useTrackControls, useVisibleChunkIndices, waveformColorToCss };
941
+ export { AudioPosition, type AudioPositionProps, AutomaticScrollCheckbox, type AutomaticScrollCheckboxProps, BaseButton, BaseCheckbox, BaseCheckboxLabel, BaseCheckboxWrapper, BaseControlButton, BaseInput, BaseInputSmall, BaseLabel, BaseSelect, BaseSelectSmall, BaseSlider, type BeatsAndBarsContextValue, BeatsAndBarsProvider, type BeatsAndBarsProviderProps, Button, ButtonGroup, CLIP_BOUNDARY_WIDTH, CLIP_BOUNDARY_WIDTH_TOUCH, CLIP_HEADER_HEIGHT, Channel, type ChannelProps, Clip, ClipBoundary, type ClipBoundaryProps, ClipHeader, ClipHeaderPresentational, type ClipHeaderPresentationalProps, type ClipHeaderProps, type ClipProps, ClipViewportOriginProvider, CloseButton, type ColorStop, Controls$1 as Controls, DevicePixelRatioProvider, DotsIcon, type DragHandleProps, FadeOverlay, type FadeOverlayProps, type GradientStop, Header, InlineLabel, LoopRegion, LoopRegionMarkers, type LoopRegionMarkersProps, type LoopRegionProps, MasterVolumeControl, type MasterVolumeControlProps, PianoRollChannel, type PianoRollChannelProps, Playhead, type PlayheadProps, PlayheadWithMarker, Playlist, PlaylistErrorBoundary, type PlaylistErrorBoundaryProps, PlaylistInfoContext, type PlaylistProps, PlayoutProvider, type PrecomputedTickData, type RenderPlayheadFunction, type ScaleMode, ScreenReaderOnly, type ScrollViewport, ScrollViewportProvider, SegmentedVUMeter, type SegmentedVUMeterProps, Selection, type SelectionProps, SelectionTimeInputs, type SelectionTimeInputsProps, Slider, SliderWrapper, SmartChannel, type SmartChannelProps, SmartScale, type SmartScaleProps, type SnapTo, type SpectrogramCanvasRegistration, SpectrogramChannel, type SpectrogramChannelProps, SpectrogramLabels, type SpectrogramLabelsProps, StyledPlaylist, StyledTimeScale, type TimeFormat, TimeFormatSelect, type TimeFormatSelectProps, TimeInput, type TimeInputProps, TimeScale, type TimeScaleProps, TimescaleLoopRegion, type TimescaleLoopRegionProps, Track, TrackControlsContext, TrackMenu, type TrackMenuItem, type TrackMenuProps, type TrackProps, TrashIcon, VolumeDownIcon, VolumeUpIcon, type WaveformColor, type WaveformDrawMode, type WaveformGradient, type WaveformPlaylistTheme, darkTheme, defaultTheme, formatTime, getScaleInfo, isWaveformGradient, parseTime, pixelsToSamples, pixelsToSeconds, samplesToPixels, samplesToSeconds, secondsToPixels, secondsToSamples, useBeatsAndBars, useClipViewportOrigin, useDevicePixelRatio, usePlaylistInfo, usePlayoutStatus, usePlayoutStatusUpdate, useScrollViewport, useScrollViewportSelector, useTheme, useTrackControls, useVisibleChunkIndices, waveformColorToCss };
package/dist/index.js CHANGED
@@ -1602,6 +1602,7 @@ var PlayheadWithMarker = ({
1602
1602
  color = "#ff0000",
1603
1603
  isPlaying,
1604
1604
  currentTimeRef,
1605
+ visualTimeRef,
1605
1606
  playbackStartTimeRef,
1606
1607
  audioStartPositionRef,
1607
1608
  samplesPerPixel,
@@ -1616,7 +1617,9 @@ var PlayheadWithMarker = ({
1616
1617
  const updatePosition = () => {
1617
1618
  if (containerRef.current) {
1618
1619
  let time;
1619
- if (isPlaying) {
1620
+ if (visualTimeRef?.current !== void 0 && visualTimeRef.current !== null) {
1621
+ time = visualTimeRef.current;
1622
+ } else if (isPlaying) {
1620
1623
  if (getPlaybackTime) {
1621
1624
  time = getPlaybackTime();
1622
1625
  } else if (getAudioContextTime) {
@@ -1652,6 +1655,7 @@ var PlayheadWithMarker = ({
1652
1655
  samplesPerPixel,
1653
1656
  controlsOffset,
1654
1657
  currentTimeRef,
1658
+ visualTimeRef,
1655
1659
  playbackStartTimeRef,
1656
1660
  audioStartPositionRef,
1657
1661
  getAudioContextTime,
@@ -1659,7 +1663,7 @@ var PlayheadWithMarker = ({
1659
1663
  ]);
1660
1664
  (0, import_react9.useEffect)(() => {
1661
1665
  if (!isPlaying && containerRef.current) {
1662
- const time = currentTimeRef.current ?? 0;
1666
+ const time = visualTimeRef?.current ?? currentTimeRef.current ?? 0;
1663
1667
  const pos = time * sampleRate / samplesPerPixel + controlsOffset;
1664
1668
  containerRef.current.style.transform = `translate3d(${pos}px, 0, 0)`;
1665
1669
  }
@@ -2457,28 +2461,28 @@ var SpectrogramChannel = ({
2457
2461
  waveHeight,
2458
2462
  devicePixelRatio = 1,
2459
2463
  samplesPerPixel: _samplesPerPixel,
2460
- workerApi,
2461
2464
  clipId,
2462
- onCanvasesReady
2465
+ onCanvasRegister,
2466
+ onCanvasUnregister
2463
2467
  }) => {
2464
2468
  const channelIndex = channelIndexProp ?? index;
2465
2469
  const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
2466
2470
  const registeredIdsRef = (0, import_react20.useRef)([]);
2467
2471
  const transferredCanvasesRef = (0, import_react20.useRef)(/* @__PURE__ */ new WeakSet());
2468
- const workerApiRef = (0, import_react20.useRef)(workerApi);
2469
- const onCanvasesReadyRef = (0, import_react20.useRef)(onCanvasesReady);
2472
+ const onCanvasRegisterRef = (0, import_react20.useRef)(onCanvasRegister);
2473
+ const onCanvasUnregisterRef = (0, import_react20.useRef)(onCanvasUnregister);
2470
2474
  const clipOriginX = useClipViewportOrigin();
2471
2475
  const visibleChunkIndices = useVisibleChunkIndices(length, import_core5.MAX_CANVAS_WIDTH, clipOriginX);
2472
2476
  (0, import_react20.useEffect)(() => {
2473
- workerApiRef.current = workerApi;
2474
- }, [workerApi]);
2477
+ onCanvasRegisterRef.current = onCanvasRegister;
2478
+ }, [onCanvasRegister]);
2475
2479
  (0, import_react20.useEffect)(() => {
2476
- onCanvasesReadyRef.current = onCanvasesReady;
2477
- }, [onCanvasesReady]);
2480
+ onCanvasUnregisterRef.current = onCanvasUnregister;
2481
+ }, [onCanvasUnregister]);
2478
2482
  (0, import_react20.useEffect)(() => {
2479
- const currentWorkerApi = workerApiRef.current;
2480
- if (!currentWorkerApi || !clipId) return;
2481
- const previousCount = registeredIdsRef.current.length;
2483
+ if (!clipId) return;
2484
+ const unregister = onCanvasUnregisterRef.current;
2485
+ const register = onCanvasRegisterRef.current;
2482
2486
  const remaining = [];
2483
2487
  for (const id of registeredIdsRef.current) {
2484
2488
  const match = id.match(/chunk(\d+)$/);
@@ -2492,14 +2496,13 @@ var SpectrogramChannel = ({
2492
2496
  remaining.push(id);
2493
2497
  } else {
2494
2498
  try {
2495
- currentWorkerApi.unregisterCanvas(id);
2499
+ unregister(id);
2496
2500
  } catch (err) {
2497
- console.warn(`[spectrogram] unregisterCanvas failed for ${id}:`, err);
2501
+ console.warn(`[spectrogram] unregister failed for ${id}:`, err);
2498
2502
  }
2499
2503
  }
2500
2504
  }
2501
2505
  registeredIdsRef.current = remaining;
2502
- const newIds = [];
2503
2506
  for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
2504
2507
  if (transferredCanvasesRef.current.has(canvas)) continue;
2505
2508
  const canvasId = `${clipId}-ch${channelIndex}-chunk${canvasIdx}`;
@@ -2511,41 +2514,31 @@ var SpectrogramChannel = ({
2511
2514
  continue;
2512
2515
  }
2513
2516
  transferredCanvasesRef.current.add(canvas);
2517
+ const widthPx = Math.min(length - canvasIdx * import_core5.MAX_CANVAS_WIDTH, import_core5.MAX_CANVAS_WIDTH);
2514
2518
  try {
2515
- currentWorkerApi.registerCanvas(canvasId, offscreen);
2516
- newIds.push(canvasId);
2519
+ register({
2520
+ canvasId,
2521
+ canvas: offscreen,
2522
+ clipId,
2523
+ channelIndex,
2524
+ chunkIndex: canvasIdx,
2525
+ widthPx,
2526
+ heightPx: waveHeight
2527
+ });
2528
+ registeredIdsRef.current.push(canvasId);
2517
2529
  } catch (err) {
2518
- console.warn(`[spectrogram] registerCanvas failed for ${canvasId}:`, err);
2519
- continue;
2530
+ console.warn(`[spectrogram] register failed for ${canvasId}:`, err);
2520
2531
  }
2521
2532
  }
2522
- if (newIds.length > 0) {
2523
- registeredIdsRef.current = [...registeredIdsRef.current, ...newIds];
2524
- }
2525
- const canvasSetChanged = newIds.length > 0 || remaining.length < previousCount;
2526
- if (canvasSetChanged) {
2527
- const allIds = registeredIdsRef.current;
2528
- const allWidths = allIds.map((id) => {
2529
- const match = id.match(/chunk(\d+)$/);
2530
- if (!match) {
2531
- console.warn(`[spectrogram] Unexpected canvas ID format: ${id}`);
2532
- return import_core5.MAX_CANVAS_WIDTH;
2533
- }
2534
- const chunkIdx = parseInt(match[1], 10);
2535
- return Math.min(length - chunkIdx * import_core5.MAX_CANVAS_WIDTH, import_core5.MAX_CANVAS_WIDTH);
2536
- });
2537
- onCanvasesReadyRef.current?.(allIds, allWidths);
2538
- }
2539
- }, [canvasMapRef, clipId, channelIndex, length, visibleChunkIndices]);
2533
+ }, [canvasMapRef, clipId, channelIndex, length, waveHeight, visibleChunkIndices]);
2540
2534
  (0, import_react20.useEffect)(() => {
2541
2535
  return () => {
2542
- const api = workerApiRef.current;
2543
- if (!api) return;
2536
+ const unregister = onCanvasUnregisterRef.current;
2544
2537
  for (const id of registeredIdsRef.current) {
2545
2538
  try {
2546
- api.unregisterCanvas(id);
2539
+ unregister(id);
2547
2540
  } catch (err) {
2548
- console.warn(`[spectrogram] unregisterCanvas failed for ${id}:`, err);
2541
+ console.warn(`[spectrogram] unregister failed for ${id}:`, err);
2549
2542
  }
2550
2543
  }
2551
2544
  registeredIdsRef.current = [];
@@ -2578,9 +2571,9 @@ var SmartChannel = ({
2578
2571
  transparentBackground,
2579
2572
  renderMode = "waveform",
2580
2573
  samplesPerPixel: sppProp,
2581
- spectrogramWorkerApi,
2582
2574
  spectrogramClipId,
2583
- spectrogramOnCanvasesReady,
2575
+ spectrogramOnCanvasRegister,
2576
+ spectrogramOnCanvasUnregister,
2584
2577
  midiNotes,
2585
2578
  sampleRate: sampleRateProp,
2586
2579
  clipOffsetSeconds,
@@ -2599,7 +2592,7 @@ var SmartChannel = ({
2599
2592
  const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;
2600
2593
  const waveFillColor = isSelected && theme ? theme.selectedWaveFillColor : theme?.waveFillColor;
2601
2594
  const drawMode = theme?.waveformDrawMode || "inverted";
2602
- const hasSpectrogram = spectrogramWorkerApi && spectrogramClipId;
2595
+ const hasSpectrogram = spectrogramClipId && spectrogramOnCanvasRegister && spectrogramOnCanvasUnregister;
2603
2596
  if (renderMode === "spectrogram" && hasSpectrogram) {
2604
2597
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2605
2598
  SpectrogramChannel,
@@ -2609,9 +2602,9 @@ var SmartChannel = ({
2609
2602
  waveHeight,
2610
2603
  devicePixelRatio,
2611
2604
  samplesPerPixel,
2612
- workerApi: spectrogramWorkerApi,
2613
2605
  clipId: spectrogramClipId,
2614
- onCanvasesReady: spectrogramOnCanvasesReady
2606
+ onCanvasRegister: spectrogramOnCanvasRegister,
2607
+ onCanvasUnregister: spectrogramOnCanvasUnregister
2615
2608
  }
2616
2609
  );
2617
2610
  }
@@ -2627,9 +2620,9 @@ var SmartChannel = ({
2627
2620
  waveHeight: halfHeight,
2628
2621
  devicePixelRatio,
2629
2622
  samplesPerPixel,
2630
- workerApi: spectrogramWorkerApi,
2631
2623
  clipId: spectrogramClipId,
2632
- onCanvasesReady: spectrogramOnCanvasesReady
2624
+ onCanvasRegister: spectrogramOnCanvasRegister,
2625
+ onCanvasUnregister: spectrogramOnCanvasUnregister
2633
2626
  }
2634
2627
  ),
2635
2628
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(