@waveform-playlist/ui-components 9.5.1 → 10.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 +25 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +290 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +289 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -15
- package/LICENSE.md +0 -21
package/dist/index.d.mts
CHANGED
|
@@ -77,6 +77,7 @@ interface WaveformPlaylistTheme {
|
|
|
77
77
|
selectedWaveOutlineColor: WaveformColor;
|
|
78
78
|
selectedWaveFillColor: WaveformColor;
|
|
79
79
|
selectedTrackControlsBackground: string;
|
|
80
|
+
selectedTrackBackground: string;
|
|
80
81
|
timeColor: string;
|
|
81
82
|
timescaleBackgroundColor: string;
|
|
82
83
|
playheadColor: string;
|
|
@@ -681,6 +682,29 @@ interface TrackMenuProps {
|
|
|
681
682
|
}
|
|
682
683
|
declare const TrackMenu: react__default.FC<TrackMenuProps>;
|
|
683
684
|
|
|
685
|
+
interface ColorStop {
|
|
686
|
+
dB: number;
|
|
687
|
+
color: string;
|
|
688
|
+
}
|
|
689
|
+
interface SegmentedVUMeterProps {
|
|
690
|
+
levels: number[];
|
|
691
|
+
peakLevels?: number[];
|
|
692
|
+
channelLabels?: string[];
|
|
693
|
+
orientation?: 'vertical' | 'horizontal';
|
|
694
|
+
segmentCount?: number;
|
|
695
|
+
dBRange?: [number, number];
|
|
696
|
+
showScale?: boolean;
|
|
697
|
+
colorStops?: ColorStop[];
|
|
698
|
+
segmentWidth?: number;
|
|
699
|
+
segmentHeight?: number;
|
|
700
|
+
segmentGap?: number;
|
|
701
|
+
coloredInactive?: boolean;
|
|
702
|
+
/** Color for scale labels and channel labels. Defaults to '#888'. */
|
|
703
|
+
labelColor?: string;
|
|
704
|
+
className?: string;
|
|
705
|
+
}
|
|
706
|
+
declare const SegmentedVUMeter: react__default.NamedExoticComponent<SegmentedVUMeterProps>;
|
|
707
|
+
|
|
684
708
|
type SnapTo = 'bar' | 'beat' | 'off';
|
|
685
709
|
type ScaleMode = 'beats' | 'temporal';
|
|
686
710
|
interface BeatsAndBarsContextValue {
|
|
@@ -897,4 +921,4 @@ declare const BaseSlider: styled_components_dist_types.IStyledComponentBase<"web
|
|
|
897
921
|
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;
|
|
898
922
|
}>, never>, never>> & string;
|
|
899
923
|
|
|
900
|
-
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, 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, 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 };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -77,6 +77,7 @@ interface WaveformPlaylistTheme {
|
|
|
77
77
|
selectedWaveOutlineColor: WaveformColor;
|
|
78
78
|
selectedWaveFillColor: WaveformColor;
|
|
79
79
|
selectedTrackControlsBackground: string;
|
|
80
|
+
selectedTrackBackground: string;
|
|
80
81
|
timeColor: string;
|
|
81
82
|
timescaleBackgroundColor: string;
|
|
82
83
|
playheadColor: string;
|
|
@@ -681,6 +682,29 @@ interface TrackMenuProps {
|
|
|
681
682
|
}
|
|
682
683
|
declare const TrackMenu: react__default.FC<TrackMenuProps>;
|
|
683
684
|
|
|
685
|
+
interface ColorStop {
|
|
686
|
+
dB: number;
|
|
687
|
+
color: string;
|
|
688
|
+
}
|
|
689
|
+
interface SegmentedVUMeterProps {
|
|
690
|
+
levels: number[];
|
|
691
|
+
peakLevels?: number[];
|
|
692
|
+
channelLabels?: string[];
|
|
693
|
+
orientation?: 'vertical' | 'horizontal';
|
|
694
|
+
segmentCount?: number;
|
|
695
|
+
dBRange?: [number, number];
|
|
696
|
+
showScale?: boolean;
|
|
697
|
+
colorStops?: ColorStop[];
|
|
698
|
+
segmentWidth?: number;
|
|
699
|
+
segmentHeight?: number;
|
|
700
|
+
segmentGap?: number;
|
|
701
|
+
coloredInactive?: boolean;
|
|
702
|
+
/** Color for scale labels and channel labels. Defaults to '#888'. */
|
|
703
|
+
labelColor?: string;
|
|
704
|
+
className?: string;
|
|
705
|
+
}
|
|
706
|
+
declare const SegmentedVUMeter: react__default.NamedExoticComponent<SegmentedVUMeterProps>;
|
|
707
|
+
|
|
684
708
|
type SnapTo = 'bar' | 'beat' | 'off';
|
|
685
709
|
type ScaleMode = 'beats' | 'temporal';
|
|
686
710
|
interface BeatsAndBarsContextValue {
|
|
@@ -897,4 +921,4 @@ declare const BaseSlider: styled_components_dist_types.IStyledComponentBase<"web
|
|
|
897
921
|
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;
|
|
898
922
|
}>, never>, never>> & string;
|
|
899
923
|
|
|
900
|
-
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, 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, 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,7 @@ __export(index_exports, {
|
|
|
74
74
|
PlayoutProvider: () => PlayoutProvider,
|
|
75
75
|
ScreenReaderOnly: () => ScreenReaderOnly,
|
|
76
76
|
ScrollViewportProvider: () => ScrollViewportProvider,
|
|
77
|
+
SegmentedVUMeter: () => SegmentedVUMeter,
|
|
77
78
|
Selection: () => Selection,
|
|
78
79
|
SelectionTimeInputs: () => SelectionTimeInputs,
|
|
79
80
|
Slider: () => Slider,
|
|
@@ -484,6 +485,8 @@ var defaultTheme = {
|
|
|
484
485
|
// Selected: brighter cyan
|
|
485
486
|
selectedTrackControlsBackground: "#d9e9ff",
|
|
486
487
|
// Light blue background for selected track controls
|
|
488
|
+
selectedTrackBackground: "#e8f0fe",
|
|
489
|
+
// Light blue tint for selected track waveform area
|
|
487
490
|
timeColor: "#000",
|
|
488
491
|
timescaleBackgroundColor: "#fff",
|
|
489
492
|
playheadColor: "#f00",
|
|
@@ -560,6 +563,8 @@ var darkTheme = {
|
|
|
560
563
|
// Brighter amber background when selected
|
|
561
564
|
selectedTrackControlsBackground: "#2a2218",
|
|
562
565
|
// Dark warm brown for selected track controls
|
|
566
|
+
selectedTrackBackground: "#e8c090",
|
|
567
|
+
// Amber for selected track waveform area (matches selected clip)
|
|
563
568
|
timeColor: "#d8c0a8",
|
|
564
569
|
// Warm amber for timescale text
|
|
565
570
|
timescaleBackgroundColor: "#1a1612",
|
|
@@ -3062,7 +3067,12 @@ var ChannelContainer = import_styled_components26.default.div.attrs((props) => (
|
|
|
3062
3067
|
}
|
|
3063
3068
|
}))`
|
|
3064
3069
|
position: relative;
|
|
3065
|
-
background: ${(props) =>
|
|
3070
|
+
background: ${(props) => {
|
|
3071
|
+
if (props.$isSelected) {
|
|
3072
|
+
return props.theme.selectedTrackBackground || props.$backgroundColor || "transparent";
|
|
3073
|
+
}
|
|
3074
|
+
return props.$backgroundColor || "transparent";
|
|
3075
|
+
}};
|
|
3066
3076
|
height: 100%;
|
|
3067
3077
|
`;
|
|
3068
3078
|
var Track = ({
|
|
@@ -3075,7 +3085,7 @@ var Track = ({
|
|
|
3075
3085
|
hasClipHeaders = false,
|
|
3076
3086
|
onClick,
|
|
3077
3087
|
trackId,
|
|
3078
|
-
isSelected
|
|
3088
|
+
isSelected = false
|
|
3079
3089
|
}) => {
|
|
3080
3090
|
const { waveHeight } = usePlaylistInfo();
|
|
3081
3091
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
@@ -3091,6 +3101,7 @@ var Track = ({
|
|
|
3091
3101
|
{
|
|
3092
3102
|
$backgroundColor: backgroundColor,
|
|
3093
3103
|
$offset: offset,
|
|
3104
|
+
$isSelected: isSelected,
|
|
3094
3105
|
onClick,
|
|
3095
3106
|
"data-track-id": trackId,
|
|
3096
3107
|
children
|
|
@@ -3473,6 +3484,282 @@ var TrackMenu = ({ items: itemsProp }) => {
|
|
|
3473
3484
|
] });
|
|
3474
3485
|
};
|
|
3475
3486
|
|
|
3487
|
+
// src/components/SegmentedVUMeter.tsx
|
|
3488
|
+
var import_react30 = __toESM(require("react"));
|
|
3489
|
+
var import_styled_components35 = __toESM(require("styled-components"));
|
|
3490
|
+
var import_core8 = require("@waveform-playlist/core");
|
|
3491
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
3492
|
+
var DEFAULT_COLOR_STOPS = [
|
|
3493
|
+
{ dB: 2, color: "#ff0000" },
|
|
3494
|
+
{ dB: -1, color: "#e74c3c" },
|
|
3495
|
+
{ dB: -3, color: "#e67e22" },
|
|
3496
|
+
{ dB: -6, color: "#f1c40f" },
|
|
3497
|
+
{ dB: -12, color: "#2ecc71" },
|
|
3498
|
+
{ dB: -20, color: "#27ae60" },
|
|
3499
|
+
{ dB: -30, color: "#5dade2" },
|
|
3500
|
+
{ dB: -50, color: "#85c1e9" }
|
|
3501
|
+
];
|
|
3502
|
+
var INACTIVE_OPACITY = 0.15;
|
|
3503
|
+
var INACTIVE_COLOR = "rgba(128, 128, 128, 0.2)";
|
|
3504
|
+
var PEAK_COLOR = "#ffffff";
|
|
3505
|
+
function getDefaultLabels(channelCount) {
|
|
3506
|
+
if (channelCount === 1) return ["M"];
|
|
3507
|
+
if (channelCount === 2) return ["L", "R"];
|
|
3508
|
+
return Array.from({ length: channelCount }, (_, i) => String(i + 1));
|
|
3509
|
+
}
|
|
3510
|
+
function getColorForDb(dB, colorStops) {
|
|
3511
|
+
if (colorStops.length === 0) return INACTIVE_COLOR;
|
|
3512
|
+
for (const stop of colorStops) {
|
|
3513
|
+
if (dB >= stop.dB) {
|
|
3514
|
+
return stop.color;
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
return colorStops[colorStops.length - 1].color;
|
|
3518
|
+
}
|
|
3519
|
+
function computeThresholds(segmentCount, dBRange) {
|
|
3520
|
+
const safeCount = Math.max(2, segmentCount);
|
|
3521
|
+
const [minDb, maxDb] = dBRange;
|
|
3522
|
+
const step = (maxDb - minDb) / (safeCount - 1);
|
|
3523
|
+
return Array.from({ length: safeCount }, (_, i) => maxDb - i * step);
|
|
3524
|
+
}
|
|
3525
|
+
function formatDbLabel(dB) {
|
|
3526
|
+
return Math.round(dB).toString();
|
|
3527
|
+
}
|
|
3528
|
+
var MeterContainer = import_styled_components35.default.div`
|
|
3529
|
+
display: inline-flex;
|
|
3530
|
+
flex-direction: ${(props) => props.$orientation === "horizontal" ? "column" : "row"};
|
|
3531
|
+
gap: 4px;
|
|
3532
|
+
font-family: 'Courier New', monospace;
|
|
3533
|
+
`;
|
|
3534
|
+
var ChannelColumn = import_styled_components35.default.div`
|
|
3535
|
+
display: flex;
|
|
3536
|
+
flex-direction: ${(props) => props.$orientation === "horizontal" ? "row" : "column"};
|
|
3537
|
+
align-items: center;
|
|
3538
|
+
gap: 4px;
|
|
3539
|
+
`;
|
|
3540
|
+
var SegmentStack = import_styled_components35.default.div`
|
|
3541
|
+
display: flex;
|
|
3542
|
+
flex-direction: ${(props) => props.$orientation === "horizontal" ? "row" : "column"};
|
|
3543
|
+
`;
|
|
3544
|
+
var Segment = import_styled_components35.default.div.attrs((props) => ({
|
|
3545
|
+
style: {
|
|
3546
|
+
width: `${props.$width}px`,
|
|
3547
|
+
height: `${props.$height}px`,
|
|
3548
|
+
...props.$orientation === "horizontal" ? { marginRight: `${props.$gap}px` } : { marginBottom: `${props.$gap}px` },
|
|
3549
|
+
backgroundColor: props.$isPeak ? PEAK_COLOR : props.$active || props.$coloredInactive ? props.$color : INACTIVE_COLOR,
|
|
3550
|
+
opacity: props.$isPeak || props.$active ? 1 : props.$coloredInactive ? INACTIVE_OPACITY : 1,
|
|
3551
|
+
boxShadow: props.$active || props.$isPeak ? `0 0 4px ${props.$isPeak ? PEAK_COLOR : props.$color}40` : "none"
|
|
3552
|
+
}
|
|
3553
|
+
}))`
|
|
3554
|
+
border-radius: 1px;
|
|
3555
|
+
`;
|
|
3556
|
+
var DEFAULT_LABEL_COLOR = "#888";
|
|
3557
|
+
var ChannelLabel = import_styled_components35.default.div`
|
|
3558
|
+
color: ${(props) => props.$labelColor};
|
|
3559
|
+
font-size: 10px;
|
|
3560
|
+
text-align: center;
|
|
3561
|
+
user-select: none;
|
|
3562
|
+
`;
|
|
3563
|
+
var ScaleColumn = import_styled_components35.default.div`
|
|
3564
|
+
display: flex;
|
|
3565
|
+
flex-direction: column;
|
|
3566
|
+
position: relative;
|
|
3567
|
+
min-width: 28px;
|
|
3568
|
+
`;
|
|
3569
|
+
var ScaleLabel = import_styled_components35.default.div.attrs((props) => ({
|
|
3570
|
+
style: {
|
|
3571
|
+
top: `${props.$top}px`,
|
|
3572
|
+
color: props.$labelColor
|
|
3573
|
+
}
|
|
3574
|
+
}))`
|
|
3575
|
+
position: absolute;
|
|
3576
|
+
left: 50%;
|
|
3577
|
+
font-size: 9px;
|
|
3578
|
+
font-family: 'Courier New', monospace;
|
|
3579
|
+
white-space: nowrap;
|
|
3580
|
+
transform: translate(-50%, -50%);
|
|
3581
|
+
user-select: none;
|
|
3582
|
+
`;
|
|
3583
|
+
var HorizontalScaleWrapper = import_styled_components35.default.div`
|
|
3584
|
+
display: flex;
|
|
3585
|
+
flex-direction: row;
|
|
3586
|
+
align-items: center;
|
|
3587
|
+
gap: 4px;
|
|
3588
|
+
`;
|
|
3589
|
+
var ScaleRow = import_styled_components35.default.div`
|
|
3590
|
+
display: flex;
|
|
3591
|
+
flex-direction: row;
|
|
3592
|
+
position: relative;
|
|
3593
|
+
min-height: 16px;
|
|
3594
|
+
`;
|
|
3595
|
+
var ScaleLabelHorizontal = import_styled_components35.default.div.attrs((props) => ({
|
|
3596
|
+
style: {
|
|
3597
|
+
left: `${props.$left}px`,
|
|
3598
|
+
color: props.$labelColor
|
|
3599
|
+
}
|
|
3600
|
+
}))`
|
|
3601
|
+
position: absolute;
|
|
3602
|
+
top: 50%;
|
|
3603
|
+
font-size: 9px;
|
|
3604
|
+
font-family: 'Courier New', monospace;
|
|
3605
|
+
white-space: nowrap;
|
|
3606
|
+
transform: translate(-50%, -50%);
|
|
3607
|
+
user-select: none;
|
|
3608
|
+
`;
|
|
3609
|
+
var SegmentedVUMeterInner = ({
|
|
3610
|
+
levels,
|
|
3611
|
+
peakLevels,
|
|
3612
|
+
channelLabels,
|
|
3613
|
+
orientation = "vertical",
|
|
3614
|
+
segmentCount = 24,
|
|
3615
|
+
dBRange = [-50, 5],
|
|
3616
|
+
showScale = true,
|
|
3617
|
+
colorStops = DEFAULT_COLOR_STOPS,
|
|
3618
|
+
segmentWidth = 20,
|
|
3619
|
+
segmentHeight = 8,
|
|
3620
|
+
segmentGap = 2,
|
|
3621
|
+
coloredInactive = false,
|
|
3622
|
+
labelColor,
|
|
3623
|
+
className
|
|
3624
|
+
}) => {
|
|
3625
|
+
const labels = channelLabels ?? getDefaultLabels(levels.length);
|
|
3626
|
+
const resolvedLabelColor = labelColor ?? DEFAULT_LABEL_COLOR;
|
|
3627
|
+
const channelCount = levels.length;
|
|
3628
|
+
if (process.env.NODE_ENV !== "production" && peakLevels != null && peakLevels.length !== channelCount) {
|
|
3629
|
+
console.warn(
|
|
3630
|
+
`[waveform-playlist] SegmentedVUMeter: peakLevels length (${peakLevels.length}) does not match levels length (${channelCount})`
|
|
3631
|
+
);
|
|
3632
|
+
}
|
|
3633
|
+
const isMultiChannel = channelCount >= 2;
|
|
3634
|
+
const segmentTotalHeight = segmentHeight + segmentGap;
|
|
3635
|
+
const [dBMin, dBMax] = dBRange;
|
|
3636
|
+
const thresholds = (0, import_react30.useMemo)(
|
|
3637
|
+
() => computeThresholds(segmentCount, [dBMin, dBMax]),
|
|
3638
|
+
[segmentCount, dBMin, dBMax]
|
|
3639
|
+
);
|
|
3640
|
+
const scaleLabels = (0, import_react30.useMemo)(() => {
|
|
3641
|
+
const totalSize = segmentCount * segmentTotalHeight - segmentGap;
|
|
3642
|
+
const minDb = dBMin;
|
|
3643
|
+
const maxDb = dBMax;
|
|
3644
|
+
let minSpacing;
|
|
3645
|
+
if (orientation === "horizontal") {
|
|
3646
|
+
minSpacing = 35;
|
|
3647
|
+
} else {
|
|
3648
|
+
minSpacing = Math.max(14, segmentTotalHeight * 2);
|
|
3649
|
+
}
|
|
3650
|
+
const labelCount = Math.max(2, Math.floor(totalSize / minSpacing));
|
|
3651
|
+
const labels2 = [];
|
|
3652
|
+
for (let i = 0; i < labelCount; i++) {
|
|
3653
|
+
const t = i / (labelCount - 1);
|
|
3654
|
+
const position = t * totalSize;
|
|
3655
|
+
const db = orientation === "horizontal" ? minDb + t * (maxDb - minDb) : maxDb - t * (maxDb - minDb);
|
|
3656
|
+
labels2.push({ position, label: formatDbLabel(db) });
|
|
3657
|
+
}
|
|
3658
|
+
return labels2;
|
|
3659
|
+
}, [orientation, segmentCount, segmentTotalHeight, segmentGap, dBMin, dBMax]);
|
|
3660
|
+
const renderThresholds = (0, import_react30.useMemo)(
|
|
3661
|
+
() => orientation === "horizontal" ? [...thresholds].reverse() : thresholds,
|
|
3662
|
+
[thresholds, orientation]
|
|
3663
|
+
);
|
|
3664
|
+
const renderChannel = (channelIndex) => {
|
|
3665
|
+
const level = levels[channelIndex];
|
|
3666
|
+
const levelDb = (0, import_core8.normalizedToDb)(level);
|
|
3667
|
+
const peakDb = peakLevels != null ? (0, import_core8.normalizedToDb)(peakLevels[channelIndex]) : null;
|
|
3668
|
+
let peakSegmentIndex = -1;
|
|
3669
|
+
if (peakDb != null) {
|
|
3670
|
+
let minDist = Infinity;
|
|
3671
|
+
for (let i = 0; i < renderThresholds.length; i++) {
|
|
3672
|
+
const dist = Math.abs(renderThresholds[i] - peakDb);
|
|
3673
|
+
if (dist < minDist) {
|
|
3674
|
+
minDist = dist;
|
|
3675
|
+
peakSegmentIndex = i;
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(ChannelColumn, { $orientation: orientation, "data-channel": true, children: [
|
|
3680
|
+
orientation === "horizontal" && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ChannelLabel, { $labelColor: resolvedLabelColor, children: labels[channelIndex] }),
|
|
3681
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(SegmentStack, { $orientation: orientation, children: renderThresholds.map((threshold, segIdx) => {
|
|
3682
|
+
const active = levelDb >= threshold;
|
|
3683
|
+
const isPeak = segIdx === peakSegmentIndex;
|
|
3684
|
+
const color = getColorForDb(threshold, colorStops);
|
|
3685
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
3686
|
+
Segment,
|
|
3687
|
+
{
|
|
3688
|
+
$width: orientation === "horizontal" ? segmentHeight : segmentWidth,
|
|
3689
|
+
$height: orientation === "horizontal" ? segmentWidth : segmentHeight,
|
|
3690
|
+
$gap: segmentGap,
|
|
3691
|
+
$active: active,
|
|
3692
|
+
$color: color,
|
|
3693
|
+
$isPeak: isPeak,
|
|
3694
|
+
$orientation: orientation,
|
|
3695
|
+
$coloredInactive: coloredInactive,
|
|
3696
|
+
"data-segment": true,
|
|
3697
|
+
...isPeak ? { "data-peak": true } : {}
|
|
3698
|
+
},
|
|
3699
|
+
segIdx
|
|
3700
|
+
);
|
|
3701
|
+
}) }),
|
|
3702
|
+
orientation === "vertical" && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ChannelLabel, { $labelColor: resolvedLabelColor, children: labels[channelIndex] })
|
|
3703
|
+
] }, channelIndex);
|
|
3704
|
+
};
|
|
3705
|
+
const renderScale = () => {
|
|
3706
|
+
if (orientation === "horizontal") {
|
|
3707
|
+
const totalWidth = segmentCount * segmentTotalHeight - segmentGap;
|
|
3708
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(HorizontalScaleWrapper, { children: [
|
|
3709
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ChannelLabel, { $labelColor: resolvedLabelColor, style: { visibility: "hidden" }, children: "L" }),
|
|
3710
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ScaleRow, { style: { width: `${totalWidth}px` }, children: scaleLabels.map(({ position, label }, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ScaleLabelHorizontal, { $left: position, $labelColor: resolvedLabelColor, children: label }, i)) })
|
|
3711
|
+
] });
|
|
3712
|
+
}
|
|
3713
|
+
const totalHeight = segmentCount * segmentTotalHeight - segmentGap;
|
|
3714
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ScaleColumn, { style: { height: `${totalHeight}px` }, children: scaleLabels.map(({ position, label }, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ScaleLabel, { $top: position, $labelColor: resolvedLabelColor, children: label }, i)) });
|
|
3715
|
+
};
|
|
3716
|
+
if (isMultiChannel) {
|
|
3717
|
+
if (orientation === "horizontal") {
|
|
3718
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
3719
|
+
MeterContainer,
|
|
3720
|
+
{
|
|
3721
|
+
className,
|
|
3722
|
+
$orientation: orientation,
|
|
3723
|
+
"data-meter-orientation": orientation,
|
|
3724
|
+
children: [
|
|
3725
|
+
Array.from({ length: channelCount }, (_, i) => renderChannel(i)),
|
|
3726
|
+
showScale && renderScale()
|
|
3727
|
+
]
|
|
3728
|
+
}
|
|
3729
|
+
);
|
|
3730
|
+
}
|
|
3731
|
+
const midpoint = Math.ceil(channelCount / 2);
|
|
3732
|
+
const leftChannels = Array.from({ length: midpoint }, (_, i) => i);
|
|
3733
|
+
const rightChannels = Array.from({ length: channelCount - midpoint }, (_, i) => midpoint + i);
|
|
3734
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
3735
|
+
MeterContainer,
|
|
3736
|
+
{
|
|
3737
|
+
className,
|
|
3738
|
+
$orientation: orientation,
|
|
3739
|
+
"data-meter-orientation": orientation,
|
|
3740
|
+
children: [
|
|
3741
|
+
leftChannels.map(renderChannel),
|
|
3742
|
+
showScale && renderScale(),
|
|
3743
|
+
rightChannels.map(renderChannel)
|
|
3744
|
+
]
|
|
3745
|
+
}
|
|
3746
|
+
);
|
|
3747
|
+
}
|
|
3748
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
3749
|
+
MeterContainer,
|
|
3750
|
+
{
|
|
3751
|
+
className,
|
|
3752
|
+
$orientation: orientation,
|
|
3753
|
+
"data-meter-orientation": orientation,
|
|
3754
|
+
children: [
|
|
3755
|
+
renderChannel(0),
|
|
3756
|
+
showScale && renderScale()
|
|
3757
|
+
]
|
|
3758
|
+
}
|
|
3759
|
+
);
|
|
3760
|
+
};
|
|
3761
|
+
var SegmentedVUMeter = import_react30.default.memo(SegmentedVUMeterInner);
|
|
3762
|
+
|
|
3476
3763
|
// src/utils/conversions.ts
|
|
3477
3764
|
function samplesToSeconds(samples, sampleRate) {
|
|
3478
3765
|
return samples / sampleRate;
|
|
@@ -3538,6 +3825,7 @@ function secondsToPixels2(seconds, samplesPerPixel, sampleRate) {
|
|
|
3538
3825
|
PlayoutProvider,
|
|
3539
3826
|
ScreenReaderOnly,
|
|
3540
3827
|
ScrollViewportProvider,
|
|
3828
|
+
SegmentedVUMeter,
|
|
3541
3829
|
Selection,
|
|
3542
3830
|
SelectionTimeInputs,
|
|
3543
3831
|
Slider,
|