@waveform-playlist/ui-components 12.1.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
@@ -480,9 +480,14 @@ interface SelectionTimeInputsProps {
480
480
  }
481
481
  declare const SelectionTimeInputs: react__default.FC<SelectionTimeInputsProps>;
482
482
 
483
- interface SpectrogramWorkerCanvasApi {
484
- registerCanvas(canvasId: string, canvas: OffscreenCanvas): void;
485
- 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;
486
491
  }
487
492
  interface SpectrogramChannelProps {
488
493
  /** Visual position index — used for CSS positioning (top offset). */
@@ -497,12 +502,12 @@ interface SpectrogramChannelProps {
497
502
  devicePixelRatio?: number;
498
503
  /** Samples per pixel at current zoom level */
499
504
  samplesPerPixel: number;
500
- /** Worker API for transferring canvas ownership. Rendering is done in the worker. */
501
- workerApi: SpectrogramWorkerCanvasApi;
502
- /** Clip ID used to construct unique canvas IDs for worker registration */
505
+ /** Clip ID used to construct unique canvas IDs */
503
506
  clipId: string;
504
- /** Callback when canvases are registered with the worker, providing canvas IDs and widths */
505
- 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;
506
511
  }
507
512
  declare const SpectrogramChannel: FunctionComponent<SpectrogramChannelProps>;
508
513
 
@@ -519,12 +524,12 @@ interface SmartChannelProps {
519
524
  renderMode?: RenderMode;
520
525
  /** Samples per pixel at current zoom level */
521
526
  samplesPerPixel?: number;
522
- /** Worker API for OffscreenCanvas transfer */
523
- spectrogramWorkerApi?: SpectrogramWorkerCanvasApi;
524
- /** Clip ID for worker canvas registration */
527
+ /** Clip ID for spectrogram canvas registration */
525
528
  spectrogramClipId?: string;
526
- /** Callback when canvases are registered with the worker */
527
- 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;
528
533
  /** MIDI note data for piano-roll rendering */
529
534
  midiNotes?: MidiNoteData[];
530
535
  /** Sample rate for MIDI note time → pixel conversion */
@@ -933,4 +938,4 @@ declare const BaseSlider: styled_components_dist_types.IStyledComponentBase<"web
933
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;
934
939
  }>, never>, never>> & string;
935
940
 
936
- 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
@@ -480,9 +480,14 @@ interface SelectionTimeInputsProps {
480
480
  }
481
481
  declare const SelectionTimeInputs: react__default.FC<SelectionTimeInputsProps>;
482
482
 
483
- interface SpectrogramWorkerCanvasApi {
484
- registerCanvas(canvasId: string, canvas: OffscreenCanvas): void;
485
- 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;
486
491
  }
487
492
  interface SpectrogramChannelProps {
488
493
  /** Visual position index — used for CSS positioning (top offset). */
@@ -497,12 +502,12 @@ interface SpectrogramChannelProps {
497
502
  devicePixelRatio?: number;
498
503
  /** Samples per pixel at current zoom level */
499
504
  samplesPerPixel: number;
500
- /** Worker API for transferring canvas ownership. Rendering is done in the worker. */
501
- workerApi: SpectrogramWorkerCanvasApi;
502
- /** Clip ID used to construct unique canvas IDs for worker registration */
505
+ /** Clip ID used to construct unique canvas IDs */
503
506
  clipId: string;
504
- /** Callback when canvases are registered with the worker, providing canvas IDs and widths */
505
- 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;
506
511
  }
507
512
  declare const SpectrogramChannel: FunctionComponent<SpectrogramChannelProps>;
508
513
 
@@ -519,12 +524,12 @@ interface SmartChannelProps {
519
524
  renderMode?: RenderMode;
520
525
  /** Samples per pixel at current zoom level */
521
526
  samplesPerPixel?: number;
522
- /** Worker API for OffscreenCanvas transfer */
523
- spectrogramWorkerApi?: SpectrogramWorkerCanvasApi;
524
- /** Clip ID for worker canvas registration */
527
+ /** Clip ID for spectrogram canvas registration */
525
528
  spectrogramClipId?: string;
526
- /** Callback when canvases are registered with the worker */
527
- 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;
528
533
  /** MIDI note data for piano-roll rendering */
529
534
  midiNotes?: MidiNoteData[];
530
535
  /** Sample rate for MIDI note time → pixel conversion */
@@ -933,4 +938,4 @@ declare const BaseSlider: styled_components_dist_types.IStyledComponentBase<"web
933
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;
934
939
  }>, never>, never>> & string;
935
940
 
936
- 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
@@ -2461,28 +2461,28 @@ var SpectrogramChannel = ({
2461
2461
  waveHeight,
2462
2462
  devicePixelRatio = 1,
2463
2463
  samplesPerPixel: _samplesPerPixel,
2464
- workerApi,
2465
2464
  clipId,
2466
- onCanvasesReady
2465
+ onCanvasRegister,
2466
+ onCanvasUnregister
2467
2467
  }) => {
2468
2468
  const channelIndex = channelIndexProp ?? index;
2469
2469
  const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
2470
2470
  const registeredIdsRef = (0, import_react20.useRef)([]);
2471
2471
  const transferredCanvasesRef = (0, import_react20.useRef)(/* @__PURE__ */ new WeakSet());
2472
- const workerApiRef = (0, import_react20.useRef)(workerApi);
2473
- const onCanvasesReadyRef = (0, import_react20.useRef)(onCanvasesReady);
2472
+ const onCanvasRegisterRef = (0, import_react20.useRef)(onCanvasRegister);
2473
+ const onCanvasUnregisterRef = (0, import_react20.useRef)(onCanvasUnregister);
2474
2474
  const clipOriginX = useClipViewportOrigin();
2475
2475
  const visibleChunkIndices = useVisibleChunkIndices(length, import_core5.MAX_CANVAS_WIDTH, clipOriginX);
2476
2476
  (0, import_react20.useEffect)(() => {
2477
- workerApiRef.current = workerApi;
2478
- }, [workerApi]);
2477
+ onCanvasRegisterRef.current = onCanvasRegister;
2478
+ }, [onCanvasRegister]);
2479
2479
  (0, import_react20.useEffect)(() => {
2480
- onCanvasesReadyRef.current = onCanvasesReady;
2481
- }, [onCanvasesReady]);
2480
+ onCanvasUnregisterRef.current = onCanvasUnregister;
2481
+ }, [onCanvasUnregister]);
2482
2482
  (0, import_react20.useEffect)(() => {
2483
- const currentWorkerApi = workerApiRef.current;
2484
- if (!currentWorkerApi || !clipId) return;
2485
- const previousCount = registeredIdsRef.current.length;
2483
+ if (!clipId) return;
2484
+ const unregister = onCanvasUnregisterRef.current;
2485
+ const register = onCanvasRegisterRef.current;
2486
2486
  const remaining = [];
2487
2487
  for (const id of registeredIdsRef.current) {
2488
2488
  const match = id.match(/chunk(\d+)$/);
@@ -2496,14 +2496,13 @@ var SpectrogramChannel = ({
2496
2496
  remaining.push(id);
2497
2497
  } else {
2498
2498
  try {
2499
- currentWorkerApi.unregisterCanvas(id);
2499
+ unregister(id);
2500
2500
  } catch (err) {
2501
- console.warn(`[spectrogram] unregisterCanvas failed for ${id}:`, err);
2501
+ console.warn(`[spectrogram] unregister failed for ${id}:`, err);
2502
2502
  }
2503
2503
  }
2504
2504
  }
2505
2505
  registeredIdsRef.current = remaining;
2506
- const newIds = [];
2507
2506
  for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
2508
2507
  if (transferredCanvasesRef.current.has(canvas)) continue;
2509
2508
  const canvasId = `${clipId}-ch${channelIndex}-chunk${canvasIdx}`;
@@ -2515,41 +2514,31 @@ var SpectrogramChannel = ({
2515
2514
  continue;
2516
2515
  }
2517
2516
  transferredCanvasesRef.current.add(canvas);
2517
+ const widthPx = Math.min(length - canvasIdx * import_core5.MAX_CANVAS_WIDTH, import_core5.MAX_CANVAS_WIDTH);
2518
2518
  try {
2519
- currentWorkerApi.registerCanvas(canvasId, offscreen);
2520
- 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);
2521
2529
  } catch (err) {
2522
- console.warn(`[spectrogram] registerCanvas failed for ${canvasId}:`, err);
2523
- continue;
2530
+ console.warn(`[spectrogram] register failed for ${canvasId}:`, err);
2524
2531
  }
2525
2532
  }
2526
- if (newIds.length > 0) {
2527
- registeredIdsRef.current = [...registeredIdsRef.current, ...newIds];
2528
- }
2529
- const canvasSetChanged = newIds.length > 0 || remaining.length < previousCount;
2530
- if (canvasSetChanged) {
2531
- const allIds = registeredIdsRef.current;
2532
- const allWidths = allIds.map((id) => {
2533
- const match = id.match(/chunk(\d+)$/);
2534
- if (!match) {
2535
- console.warn(`[spectrogram] Unexpected canvas ID format: ${id}`);
2536
- return import_core5.MAX_CANVAS_WIDTH;
2537
- }
2538
- const chunkIdx = parseInt(match[1], 10);
2539
- return Math.min(length - chunkIdx * import_core5.MAX_CANVAS_WIDTH, import_core5.MAX_CANVAS_WIDTH);
2540
- });
2541
- onCanvasesReadyRef.current?.(allIds, allWidths);
2542
- }
2543
- }, [canvasMapRef, clipId, channelIndex, length, visibleChunkIndices]);
2533
+ }, [canvasMapRef, clipId, channelIndex, length, waveHeight, visibleChunkIndices]);
2544
2534
  (0, import_react20.useEffect)(() => {
2545
2535
  return () => {
2546
- const api = workerApiRef.current;
2547
- if (!api) return;
2536
+ const unregister = onCanvasUnregisterRef.current;
2548
2537
  for (const id of registeredIdsRef.current) {
2549
2538
  try {
2550
- api.unregisterCanvas(id);
2539
+ unregister(id);
2551
2540
  } catch (err) {
2552
- console.warn(`[spectrogram] unregisterCanvas failed for ${id}:`, err);
2541
+ console.warn(`[spectrogram] unregister failed for ${id}:`, err);
2553
2542
  }
2554
2543
  }
2555
2544
  registeredIdsRef.current = [];
@@ -2582,9 +2571,9 @@ var SmartChannel = ({
2582
2571
  transparentBackground,
2583
2572
  renderMode = "waveform",
2584
2573
  samplesPerPixel: sppProp,
2585
- spectrogramWorkerApi,
2586
2574
  spectrogramClipId,
2587
- spectrogramOnCanvasesReady,
2575
+ spectrogramOnCanvasRegister,
2576
+ spectrogramOnCanvasUnregister,
2588
2577
  midiNotes,
2589
2578
  sampleRate: sampleRateProp,
2590
2579
  clipOffsetSeconds,
@@ -2603,7 +2592,7 @@ var SmartChannel = ({
2603
2592
  const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;
2604
2593
  const waveFillColor = isSelected && theme ? theme.selectedWaveFillColor : theme?.waveFillColor;
2605
2594
  const drawMode = theme?.waveformDrawMode || "inverted";
2606
- const hasSpectrogram = spectrogramWorkerApi && spectrogramClipId;
2595
+ const hasSpectrogram = spectrogramClipId && spectrogramOnCanvasRegister && spectrogramOnCanvasUnregister;
2607
2596
  if (renderMode === "spectrogram" && hasSpectrogram) {
2608
2597
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2609
2598
  SpectrogramChannel,
@@ -2613,9 +2602,9 @@ var SmartChannel = ({
2613
2602
  waveHeight,
2614
2603
  devicePixelRatio,
2615
2604
  samplesPerPixel,
2616
- workerApi: spectrogramWorkerApi,
2617
2605
  clipId: spectrogramClipId,
2618
- onCanvasesReady: spectrogramOnCanvasesReady
2606
+ onCanvasRegister: spectrogramOnCanvasRegister,
2607
+ onCanvasUnregister: spectrogramOnCanvasUnregister
2619
2608
  }
2620
2609
  );
2621
2610
  }
@@ -2631,9 +2620,9 @@ var SmartChannel = ({
2631
2620
  waveHeight: halfHeight,
2632
2621
  devicePixelRatio,
2633
2622
  samplesPerPixel,
2634
- workerApi: spectrogramWorkerApi,
2635
2623
  clipId: spectrogramClipId,
2636
- onCanvasesReady: spectrogramOnCanvasesReady
2624
+ onCanvasRegister: spectrogramOnCanvasRegister,
2625
+ onCanvasUnregister: spectrogramOnCanvasUnregister
2637
2626
  }
2638
2627
  ),
2639
2628
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(