@waveform-playlist/ui-components 5.0.0-alpha.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/LICENSE.md +21 -0
- package/dist/index.d.mts +642 -0
- package/dist/index.d.ts +642 -0
- package/dist/index.js +2419 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2320 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +91 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2419 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.tsx
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AudioPosition: () => AudioPosition,
|
|
34
|
+
AutomaticScrollCheckbox: () => AutomaticScrollCheckbox,
|
|
35
|
+
BaseButton: () => BaseButton,
|
|
36
|
+
BaseCheckbox: () => BaseCheckbox,
|
|
37
|
+
BaseCheckboxLabel: () => BaseCheckboxLabel,
|
|
38
|
+
BaseCheckboxWrapper: () => BaseCheckboxWrapper,
|
|
39
|
+
BaseControlButton: () => BaseControlButton,
|
|
40
|
+
BaseInput: () => BaseInput,
|
|
41
|
+
BaseLabel: () => BaseLabel,
|
|
42
|
+
BaseSelect: () => BaseSelect,
|
|
43
|
+
BaseSlider: () => BaseSlider,
|
|
44
|
+
Button: () => Button,
|
|
45
|
+
ButtonGroup: () => ButtonGroup,
|
|
46
|
+
CLIP_BOUNDARY_WIDTH: () => CLIP_BOUNDARY_WIDTH,
|
|
47
|
+
CLIP_HEADER_HEIGHT: () => CLIP_HEADER_HEIGHT,
|
|
48
|
+
Channel: () => Channel,
|
|
49
|
+
Clip: () => Clip,
|
|
50
|
+
ClipBoundary: () => ClipBoundary,
|
|
51
|
+
ClipHeader: () => ClipHeader,
|
|
52
|
+
ClipHeaderPresentational: () => ClipHeaderPresentational,
|
|
53
|
+
Controls: () => Controls,
|
|
54
|
+
DevicePixelRatioProvider: () => DevicePixelRatioProvider,
|
|
55
|
+
FadeOverlay: () => FadeOverlay,
|
|
56
|
+
Header: () => Header,
|
|
57
|
+
InlineLabel: () => InlineLabel,
|
|
58
|
+
MasterVolumeControl: () => MasterVolumeControl,
|
|
59
|
+
Playhead: () => Playhead,
|
|
60
|
+
PlayheadWithMarker: () => PlayheadWithMarker,
|
|
61
|
+
Playlist: () => Playlist,
|
|
62
|
+
PlaylistInfoContext: () => PlaylistInfoContext,
|
|
63
|
+
PlayoutProvider: () => PlayoutProvider,
|
|
64
|
+
ScreenReaderOnly: () => ScreenReaderOnly,
|
|
65
|
+
Selection: () => Selection,
|
|
66
|
+
SelectionTimeInputs: () => SelectionTimeInputs,
|
|
67
|
+
Slider: () => Slider,
|
|
68
|
+
SliderWrapper: () => SliderWrapper,
|
|
69
|
+
SmartChannel: () => SmartChannel,
|
|
70
|
+
SmartScale: () => SmartScale,
|
|
71
|
+
StyledPlaylist: () => StyledPlaylist,
|
|
72
|
+
StyledTimeScale: () => StyledTimeScale,
|
|
73
|
+
TimeFormatSelect: () => TimeFormatSelect,
|
|
74
|
+
TimeInput: () => TimeInput,
|
|
75
|
+
TimeScale: () => TimeScale,
|
|
76
|
+
Track: () => Track,
|
|
77
|
+
TrackControlsContext: () => TrackControlsContext,
|
|
78
|
+
TrackControlsWithDelete: () => TrackControlsWithDelete,
|
|
79
|
+
TrashIcon: () => TrashIcon,
|
|
80
|
+
VolumeDownIcon: () => VolumeDownIcon,
|
|
81
|
+
VolumeUpIcon: () => VolumeUpIcon,
|
|
82
|
+
darkTheme: () => darkTheme,
|
|
83
|
+
defaultTheme: () => defaultTheme,
|
|
84
|
+
formatTime: () => formatTime,
|
|
85
|
+
isWaveformGradient: () => isWaveformGradient,
|
|
86
|
+
parseTime: () => parseTime,
|
|
87
|
+
pixelsToSamples: () => pixelsToSamples,
|
|
88
|
+
pixelsToSeconds: () => pixelsToSeconds,
|
|
89
|
+
samplesToPixels: () => samplesToPixels,
|
|
90
|
+
samplesToSeconds: () => samplesToSeconds,
|
|
91
|
+
secondsToPixels: () => secondsToPixels,
|
|
92
|
+
secondsToSamples: () => secondsToSamples,
|
|
93
|
+
useDevicePixelRatio: () => useDevicePixelRatio,
|
|
94
|
+
usePlaylistInfo: () => usePlaylistInfo,
|
|
95
|
+
usePlayoutStatus: () => usePlayoutStatus,
|
|
96
|
+
usePlayoutStatusUpdate: () => usePlayoutStatusUpdate,
|
|
97
|
+
useTheme: () => useTheme2,
|
|
98
|
+
useTrackControls: () => useTrackControls,
|
|
99
|
+
waveformColorToCss: () => waveformColorToCss
|
|
100
|
+
});
|
|
101
|
+
module.exports = __toCommonJS(index_exports);
|
|
102
|
+
|
|
103
|
+
// src/components/AudioPosition.tsx
|
|
104
|
+
var import_styled_components = __toESM(require("styled-components"));
|
|
105
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
106
|
+
var PositionDisplay = import_styled_components.default.span`
|
|
107
|
+
font-family: 'Courier New', Monaco, monospace;
|
|
108
|
+
font-size: 1rem;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
color: ${(props) => props.theme?.textColor || "#333"};
|
|
111
|
+
user-select: none;
|
|
112
|
+
`;
|
|
113
|
+
var AudioPosition = ({
|
|
114
|
+
formattedTime,
|
|
115
|
+
className
|
|
116
|
+
}) => {
|
|
117
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PositionDisplay, { className, "aria-label": "Audio position", children: formattedTime });
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/styled/BaseButton.tsx
|
|
121
|
+
var import_styled_components2 = __toESM(require("styled-components"));
|
|
122
|
+
var BaseButton = import_styled_components2.default.button`
|
|
123
|
+
display: inline-flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
justify-content: center;
|
|
126
|
+
padding: 0.5rem 1rem;
|
|
127
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
128
|
+
font-size: ${(props) => props.theme.fontSize};
|
|
129
|
+
font-weight: 500;
|
|
130
|
+
color: ${(props) => props.theme.buttonText};
|
|
131
|
+
background-color: ${(props) => props.theme.buttonBackground};
|
|
132
|
+
border: 1px solid ${(props) => props.theme.buttonBorder};
|
|
133
|
+
border-radius: ${(props) => props.theme.borderRadius};
|
|
134
|
+
cursor: pointer;
|
|
135
|
+
outline: none;
|
|
136
|
+
transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,
|
|
137
|
+
box-shadow 0.15s ease-in-out;
|
|
138
|
+
|
|
139
|
+
&:hover:not(:disabled) {
|
|
140
|
+
background-color: ${(props) => props.theme.buttonHoverBackground};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
&:focus {
|
|
144
|
+
box-shadow: 0 0 0 2px ${(props) => props.theme.inputFocusBorder}33;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
&:disabled {
|
|
148
|
+
opacity: 0.6;
|
|
149
|
+
cursor: not-allowed;
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
var BaseButtonSmall = (0, import_styled_components2.default)(BaseButton)`
|
|
153
|
+
padding: 0.25rem 0.5rem;
|
|
154
|
+
font-size: ${(props) => props.theme.fontSizeSmall};
|
|
155
|
+
`;
|
|
156
|
+
var IconButton = (0, import_styled_components2.default)(BaseButton)`
|
|
157
|
+
padding: 0.5rem;
|
|
158
|
+
min-width: 2.25rem;
|
|
159
|
+
min-height: 2.25rem;
|
|
160
|
+
`;
|
|
161
|
+
var IconButtonSmall = (0, import_styled_components2.default)(BaseButton)`
|
|
162
|
+
padding: 0.25rem;
|
|
163
|
+
min-width: 1.75rem;
|
|
164
|
+
min-height: 1.75rem;
|
|
165
|
+
font-size: ${(props) => props.theme.fontSizeSmall};
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
// src/styled/BaseCheckbox.tsx
|
|
169
|
+
var import_styled_components3 = __toESM(require("styled-components"));
|
|
170
|
+
var BaseCheckboxWrapper = import_styled_components3.default.div`
|
|
171
|
+
display: inline-flex;
|
|
172
|
+
align-items: center;
|
|
173
|
+
gap: 0.5rem;
|
|
174
|
+
`;
|
|
175
|
+
var BaseCheckbox = import_styled_components3.default.input`
|
|
176
|
+
cursor: pointer;
|
|
177
|
+
accent-color: ${(props) => props.theme.inputFocusBorder};
|
|
178
|
+
|
|
179
|
+
&:disabled {
|
|
180
|
+
cursor: not-allowed;
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
183
|
+
var BaseCheckboxLabel = import_styled_components3.default.label`
|
|
184
|
+
margin: 0;
|
|
185
|
+
cursor: pointer;
|
|
186
|
+
user-select: none;
|
|
187
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
188
|
+
font-size: ${(props) => props.theme.fontSize};
|
|
189
|
+
color: ${(props) => props.theme.textColor};
|
|
190
|
+
`;
|
|
191
|
+
|
|
192
|
+
// src/styled/BaseControlButton.tsx
|
|
193
|
+
var import_styled_components4 = __toESM(require("styled-components"));
|
|
194
|
+
var BaseControlButton = import_styled_components4.default.button`
|
|
195
|
+
padding: 0.5rem 1rem;
|
|
196
|
+
background: ${(props) => props.theme.buttonBackground || "#007bff"};
|
|
197
|
+
color: ${(props) => props.theme.buttonText || "white"};
|
|
198
|
+
border: none;
|
|
199
|
+
border-radius: ${(props) => props.theme.borderRadius};
|
|
200
|
+
cursor: pointer;
|
|
201
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
202
|
+
font-size: ${(props) => props.theme.fontSize};
|
|
203
|
+
font-weight: 500;
|
|
204
|
+
transition: background-color 0.15s ease-in-out;
|
|
205
|
+
|
|
206
|
+
&:hover:not(:disabled) {
|
|
207
|
+
background: ${(props) => props.theme.buttonHoverBackground || "#0056b3"};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
&:focus {
|
|
211
|
+
outline: none;
|
|
212
|
+
box-shadow: 0 0 0 2px ${(props) => props.theme.buttonBackground || "#007bff"}66;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
&:disabled {
|
|
216
|
+
background: #6c757d;
|
|
217
|
+
cursor: not-allowed;
|
|
218
|
+
opacity: 0.6;
|
|
219
|
+
}
|
|
220
|
+
`;
|
|
221
|
+
|
|
222
|
+
// src/styled/BaseInput.tsx
|
|
223
|
+
var import_styled_components5 = __toESM(require("styled-components"));
|
|
224
|
+
var BaseInput = import_styled_components5.default.input`
|
|
225
|
+
padding: 0.5rem 0.75rem;
|
|
226
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
227
|
+
font-size: ${(props) => props.theme.fontSize};
|
|
228
|
+
color: ${(props) => props.theme.inputText};
|
|
229
|
+
background-color: ${(props) => props.theme.inputBackground};
|
|
230
|
+
border: 1px solid ${(props) => props.theme.inputBorder};
|
|
231
|
+
border-radius: ${(props) => props.theme.borderRadius};
|
|
232
|
+
outline: none;
|
|
233
|
+
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
|
234
|
+
|
|
235
|
+
&::placeholder {
|
|
236
|
+
color: ${(props) => props.theme.inputPlaceholder};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
&:focus {
|
|
240
|
+
border-color: ${(props) => props.theme.inputFocusBorder};
|
|
241
|
+
box-shadow: 0 0 0 2px ${(props) => props.theme.inputFocusBorder}33;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
&:disabled {
|
|
245
|
+
opacity: 0.6;
|
|
246
|
+
cursor: not-allowed;
|
|
247
|
+
}
|
|
248
|
+
`;
|
|
249
|
+
var BaseInputSmall = (0, import_styled_components5.default)(BaseInput)`
|
|
250
|
+
padding: 0.25rem 0.5rem;
|
|
251
|
+
font-size: ${(props) => props.theme.fontSizeSmall};
|
|
252
|
+
`;
|
|
253
|
+
|
|
254
|
+
// src/styled/BaseLabel.tsx
|
|
255
|
+
var import_styled_components6 = __toESM(require("styled-components"));
|
|
256
|
+
var BaseLabel = import_styled_components6.default.label`
|
|
257
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
258
|
+
font-size: ${(props) => props.theme.fontSizeSmall};
|
|
259
|
+
font-weight: 500;
|
|
260
|
+
color: ${(props) => props.theme.textColorMuted};
|
|
261
|
+
margin-bottom: 0.25rem;
|
|
262
|
+
display: block;
|
|
263
|
+
`;
|
|
264
|
+
var InlineLabel = import_styled_components6.default.label`
|
|
265
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
266
|
+
font-size: ${(props) => props.theme.fontSize};
|
|
267
|
+
color: ${(props) => props.theme.textColor};
|
|
268
|
+
display: inline-flex;
|
|
269
|
+
align-items: center;
|
|
270
|
+
gap: 0.5rem;
|
|
271
|
+
cursor: pointer;
|
|
272
|
+
`;
|
|
273
|
+
var ScreenReaderOnly = import_styled_components6.default.span`
|
|
274
|
+
position: absolute;
|
|
275
|
+
width: 1px;
|
|
276
|
+
height: 1px;
|
|
277
|
+
padding: 0;
|
|
278
|
+
margin: -1px;
|
|
279
|
+
overflow: hidden;
|
|
280
|
+
clip: rect(0, 0, 0, 0);
|
|
281
|
+
white-space: nowrap;
|
|
282
|
+
border: 0;
|
|
283
|
+
`;
|
|
284
|
+
|
|
285
|
+
// src/styled/BaseSelect.tsx
|
|
286
|
+
var import_styled_components7 = __toESM(require("styled-components"));
|
|
287
|
+
var BaseSelect = import_styled_components7.default.select`
|
|
288
|
+
padding: 0.5rem 2rem 0.5rem 0.75rem;
|
|
289
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
290
|
+
font-size: ${(props) => props.theme.fontSize};
|
|
291
|
+
color: ${(props) => props.theme.inputText};
|
|
292
|
+
background-color: ${(props) => props.theme.inputBackground};
|
|
293
|
+
border: 1px solid ${(props) => props.theme.inputBorder};
|
|
294
|
+
border-radius: ${(props) => props.theme.borderRadius};
|
|
295
|
+
outline: none;
|
|
296
|
+
cursor: pointer;
|
|
297
|
+
appearance: none;
|
|
298
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
|
|
299
|
+
background-repeat: no-repeat;
|
|
300
|
+
background-position: right 0.75rem center;
|
|
301
|
+
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
|
302
|
+
|
|
303
|
+
&:focus {
|
|
304
|
+
border-color: ${(props) => props.theme.inputFocusBorder};
|
|
305
|
+
box-shadow: 0 0 0 2px ${(props) => props.theme.inputFocusBorder}33;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
&:disabled {
|
|
309
|
+
opacity: 0.6;
|
|
310
|
+
cursor: not-allowed;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Style native option elements for dark mode support */
|
|
314
|
+
option {
|
|
315
|
+
color: ${(props) => props.theme.inputText};
|
|
316
|
+
background-color: ${(props) => props.theme.inputBackground};
|
|
317
|
+
}
|
|
318
|
+
`;
|
|
319
|
+
var BaseSelectSmall = (0, import_styled_components7.default)(BaseSelect)`
|
|
320
|
+
padding: 0.25rem 1.75rem 0.25rem 0.5rem;
|
|
321
|
+
font-size: ${(props) => props.theme.fontSizeSmall};
|
|
322
|
+
`;
|
|
323
|
+
|
|
324
|
+
// src/styled/BaseSlider.tsx
|
|
325
|
+
var import_styled_components8 = __toESM(require("styled-components"));
|
|
326
|
+
var BaseSlider = import_styled_components8.default.input.attrs({ type: "range" })`
|
|
327
|
+
-webkit-appearance: none;
|
|
328
|
+
appearance: none;
|
|
329
|
+
width: 100%;
|
|
330
|
+
height: 6px;
|
|
331
|
+
background: ${(props) => props.theme.sliderTrackColor};
|
|
332
|
+
border-radius: 3px;
|
|
333
|
+
cursor: pointer;
|
|
334
|
+
outline: none;
|
|
335
|
+
|
|
336
|
+
/* WebKit (Chrome, Safari) */
|
|
337
|
+
&::-webkit-slider-thumb {
|
|
338
|
+
-webkit-appearance: none;
|
|
339
|
+
appearance: none;
|
|
340
|
+
width: 16px;
|
|
341
|
+
height: 16px;
|
|
342
|
+
background: ${(props) => props.theme.sliderThumbColor};
|
|
343
|
+
border: 2px solid ${(props) => props.theme.inputBackground};
|
|
344
|
+
border-radius: 50%;
|
|
345
|
+
cursor: pointer;
|
|
346
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
347
|
+
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
&::-webkit-slider-thumb:hover {
|
|
351
|
+
transform: scale(1.1);
|
|
352
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* Firefox */
|
|
356
|
+
&::-moz-range-thumb {
|
|
357
|
+
width: 16px;
|
|
358
|
+
height: 16px;
|
|
359
|
+
background: ${(props) => props.theme.sliderThumbColor};
|
|
360
|
+
border: 2px solid ${(props) => props.theme.inputBackground};
|
|
361
|
+
border-radius: 50%;
|
|
362
|
+
cursor: pointer;
|
|
363
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
364
|
+
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
&::-moz-range-thumb:hover {
|
|
368
|
+
transform: scale(1.1);
|
|
369
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
&::-moz-range-track {
|
|
373
|
+
background: ${(props) => props.theme.sliderTrackColor};
|
|
374
|
+
border-radius: 3px;
|
|
375
|
+
height: 6px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
&:focus {
|
|
379
|
+
outline: none;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
&:focus::-webkit-slider-thumb {
|
|
383
|
+
box-shadow: 0 0 0 3px ${(props) => props.theme.inputFocusBorder}33;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
&:focus::-moz-range-thumb {
|
|
387
|
+
box-shadow: 0 0 0 3px ${(props) => props.theme.inputFocusBorder}33;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
&:disabled {
|
|
391
|
+
cursor: not-allowed;
|
|
392
|
+
opacity: 0.5;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
&:disabled::-webkit-slider-thumb {
|
|
396
|
+
cursor: not-allowed;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
&:disabled::-moz-range-thumb {
|
|
400
|
+
cursor: not-allowed;
|
|
401
|
+
}
|
|
402
|
+
`;
|
|
403
|
+
|
|
404
|
+
// src/components/AutomaticScrollCheckbox.tsx
|
|
405
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
406
|
+
var AutomaticScrollCheckbox = ({
|
|
407
|
+
checked,
|
|
408
|
+
onChange,
|
|
409
|
+
disabled = false,
|
|
410
|
+
className
|
|
411
|
+
}) => {
|
|
412
|
+
const handleChange = (e) => {
|
|
413
|
+
onChange(e.target.checked);
|
|
414
|
+
};
|
|
415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(BaseCheckboxWrapper, { className, children: [
|
|
416
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
417
|
+
BaseCheckbox,
|
|
418
|
+
{
|
|
419
|
+
type: "checkbox",
|
|
420
|
+
id: "automatic-scroll",
|
|
421
|
+
className: "automatic-scroll",
|
|
422
|
+
checked,
|
|
423
|
+
onChange: handleChange,
|
|
424
|
+
disabled
|
|
425
|
+
}
|
|
426
|
+
),
|
|
427
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BaseCheckboxLabel, { htmlFor: "automatic-scroll", children: "Automatic Scroll" })
|
|
428
|
+
] });
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// src/components/Channel.tsx
|
|
432
|
+
var import_react = require("react");
|
|
433
|
+
var import_styled_components9 = __toESM(require("styled-components"));
|
|
434
|
+
|
|
435
|
+
// src/wfpl-theme.ts
|
|
436
|
+
function isWaveformGradient(color) {
|
|
437
|
+
return typeof color === "object" && color !== null && "type" in color;
|
|
438
|
+
}
|
|
439
|
+
function waveformColorToCss(color) {
|
|
440
|
+
if (!isWaveformGradient(color)) {
|
|
441
|
+
return color;
|
|
442
|
+
}
|
|
443
|
+
const direction = color.direction === "vertical" ? "to bottom" : "to right";
|
|
444
|
+
const stops = color.stops.map((stop) => `${stop.color} ${stop.offset * 100}%`).join(", ");
|
|
445
|
+
return `linear-gradient(${direction}, ${stops})`;
|
|
446
|
+
}
|
|
447
|
+
var defaultTheme = {
|
|
448
|
+
waveformDrawMode: "inverted",
|
|
449
|
+
waveOutlineColor: "#ffffff",
|
|
450
|
+
waveFillColor: "#1a7f8e",
|
|
451
|
+
// White background for crisp look
|
|
452
|
+
waveProgressColor: "rgba(0, 0, 0, 0.10)",
|
|
453
|
+
// Subtle dark overlay for light mode
|
|
454
|
+
selectedWaveOutlineColor: "#ffffff",
|
|
455
|
+
selectedWaveFillColor: "#00b4d8",
|
|
456
|
+
// Selected: brighter cyan
|
|
457
|
+
selectedTrackControlsBackground: "#d9e9ff",
|
|
458
|
+
// Light blue background for selected track controls
|
|
459
|
+
timeColor: "#000",
|
|
460
|
+
timescaleBackgroundColor: "#fff",
|
|
461
|
+
playheadColor: "#f00",
|
|
462
|
+
selectionColor: "rgba(255, 105, 180, 0.7)",
|
|
463
|
+
// hot pink - high contrast on light backgrounds
|
|
464
|
+
clipHeaderBackgroundColor: "rgba(0, 0, 0, 0.1)",
|
|
465
|
+
clipHeaderBorderColor: "rgba(0, 0, 0, 0.2)",
|
|
466
|
+
clipHeaderTextColor: "#333",
|
|
467
|
+
clipHeaderFontFamily: "inherit",
|
|
468
|
+
selectedClipHeaderBackgroundColor: "#b3d9ff",
|
|
469
|
+
// Brighter blue for selected track clip headers
|
|
470
|
+
// Fade overlay colors
|
|
471
|
+
fadeOverlayColor: "rgba(0, 0, 0, 0.4)",
|
|
472
|
+
// Semi-transparent overlay for fade regions
|
|
473
|
+
// UI component colors
|
|
474
|
+
backgroundColor: "#ffffff",
|
|
475
|
+
surfaceColor: "#f5f5f5",
|
|
476
|
+
borderColor: "#ddd",
|
|
477
|
+
textColor: "#333",
|
|
478
|
+
textColorMuted: "#666",
|
|
479
|
+
// Interactive element colors
|
|
480
|
+
inputBackground: "#ffffff",
|
|
481
|
+
inputBorder: "#ccc",
|
|
482
|
+
inputText: "#333",
|
|
483
|
+
inputPlaceholder: "#999",
|
|
484
|
+
inputFocusBorder: "#0066cc",
|
|
485
|
+
// Button colors - blue to match common UI patterns
|
|
486
|
+
buttonBackground: "#0091ff",
|
|
487
|
+
buttonText: "#ffffff",
|
|
488
|
+
buttonBorder: "#0081e6",
|
|
489
|
+
buttonHoverBackground: "#0081e6",
|
|
490
|
+
// Slider colors
|
|
491
|
+
sliderTrackColor: "#ddd",
|
|
492
|
+
sliderThumbColor: "#daa520",
|
|
493
|
+
// goldenrod
|
|
494
|
+
// Annotation colors
|
|
495
|
+
annotationBoxBackground: "rgba(255, 255, 255, 0.85)",
|
|
496
|
+
annotationBoxActiveBackground: "rgba(255, 255, 255, 0.95)",
|
|
497
|
+
annotationBoxHoverBackground: "rgba(255, 255, 255, 0.98)",
|
|
498
|
+
annotationBoxBorder: "#ff9800",
|
|
499
|
+
annotationBoxActiveBorder: "#d67600",
|
|
500
|
+
annotationLabelColor: "#2a2a2a",
|
|
501
|
+
annotationResizeHandleColor: "rgba(0, 0, 0, 0.4)",
|
|
502
|
+
annotationResizeHandleActiveColor: "rgba(0, 0, 0, 0.8)",
|
|
503
|
+
annotationTextItemHoverBackground: "rgba(0, 0, 0, 0.03)",
|
|
504
|
+
// Spacing and sizing
|
|
505
|
+
borderRadius: "4px",
|
|
506
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif',
|
|
507
|
+
fontSize: "14px",
|
|
508
|
+
fontSizeSmall: "12px"
|
|
509
|
+
};
|
|
510
|
+
var darkTheme = {
|
|
511
|
+
// Normal mode: waveOutlineColor = bars, waveFillColor = background
|
|
512
|
+
waveformDrawMode: "inverted",
|
|
513
|
+
// Dark bars on warm amber background
|
|
514
|
+
waveOutlineColor: "#c49a6c",
|
|
515
|
+
// Solid warm amber for background
|
|
516
|
+
waveFillColor: "#1a1612",
|
|
517
|
+
// Very dark warm brown for bars
|
|
518
|
+
waveProgressColor: "rgba(100, 70, 40, 0.6)",
|
|
519
|
+
// Warm brown progress overlay
|
|
520
|
+
// Selected: slightly lighter bars on brighter amber background
|
|
521
|
+
selectedWaveFillColor: "#241c14",
|
|
522
|
+
// Slightly lighter warm brown bars when selected
|
|
523
|
+
selectedWaveOutlineColor: "#e8c090",
|
|
524
|
+
// Brighter amber background when selected
|
|
525
|
+
selectedTrackControlsBackground: "#2a2218",
|
|
526
|
+
// Dark warm brown for selected track controls
|
|
527
|
+
timeColor: "#d8c0a8",
|
|
528
|
+
// Warm amber for timescale text
|
|
529
|
+
timescaleBackgroundColor: "#1a1612",
|
|
530
|
+
// Dark warm brown background
|
|
531
|
+
playheadColor: "#3a8838",
|
|
532
|
+
// Darker Ampelmännchen green playhead
|
|
533
|
+
selectionColor: "rgba(224, 160, 100, 0.5)",
|
|
534
|
+
// Warm amber selection
|
|
535
|
+
clipHeaderBackgroundColor: "rgba(20, 16, 12, 0.85)",
|
|
536
|
+
// Dark background for clip headers
|
|
537
|
+
clipHeaderBorderColor: "rgba(200, 160, 120, 0.25)",
|
|
538
|
+
clipHeaderTextColor: "#d8c0a8",
|
|
539
|
+
// Warm amber text
|
|
540
|
+
clipHeaderFontFamily: "inherit",
|
|
541
|
+
selectedClipHeaderBackgroundColor: "#3a2c20",
|
|
542
|
+
// Darker warm brown for selected clip headers
|
|
543
|
+
// Fade overlay colors
|
|
544
|
+
fadeOverlayColor: "rgba(200, 100, 80, 0.5)",
|
|
545
|
+
// Warm red-orange overlay visible on dark backgrounds
|
|
546
|
+
// UI component colors
|
|
547
|
+
backgroundColor: "#1e1e1e",
|
|
548
|
+
surfaceColor: "#2d2d2d",
|
|
549
|
+
borderColor: "#444",
|
|
550
|
+
textColor: "#e0e0e0",
|
|
551
|
+
textColorMuted: "#999",
|
|
552
|
+
// Interactive element colors
|
|
553
|
+
inputBackground: "#2d2d2d",
|
|
554
|
+
inputBorder: "#555",
|
|
555
|
+
inputText: "#e0e0e0",
|
|
556
|
+
inputPlaceholder: "#777",
|
|
557
|
+
inputFocusBorder: "#4A9EFF",
|
|
558
|
+
// Button colors - Ampelmännchen green (#63C75F) with black text
|
|
559
|
+
buttonBackground: "#63C75F",
|
|
560
|
+
buttonText: "#0a0a0f",
|
|
561
|
+
buttonBorder: "#52b84e",
|
|
562
|
+
buttonHoverBackground: "#78d074",
|
|
563
|
+
// Slider colors
|
|
564
|
+
sliderTrackColor: "#555",
|
|
565
|
+
sliderThumbColor: "#f0c040",
|
|
566
|
+
// brighter goldenrod for dark mode
|
|
567
|
+
// Annotation colors (dark mode - warm amber theme)
|
|
568
|
+
annotationBoxBackground: "rgba(40, 32, 24, 0.9)",
|
|
569
|
+
annotationBoxActiveBackground: "rgba(50, 40, 30, 0.95)",
|
|
570
|
+
annotationBoxHoverBackground: "rgba(60, 48, 36, 0.98)",
|
|
571
|
+
annotationBoxBorder: "#c49a6c",
|
|
572
|
+
annotationBoxActiveBorder: "#d4a87c",
|
|
573
|
+
annotationLabelColor: "#d8c0a8",
|
|
574
|
+
annotationResizeHandleColor: "rgba(200, 160, 120, 0.5)",
|
|
575
|
+
annotationResizeHandleActiveColor: "rgba(220, 180, 140, 0.8)",
|
|
576
|
+
annotationTextItemHoverBackground: "rgba(200, 160, 120, 0.08)",
|
|
577
|
+
// Spacing and sizing
|
|
578
|
+
borderRadius: "4px",
|
|
579
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif',
|
|
580
|
+
fontSize: "14px",
|
|
581
|
+
fontSizeSmall: "12px"
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
// src/components/Channel.tsx
|
|
585
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
586
|
+
var MAX_CANVAS_WIDTH = 1e3;
|
|
587
|
+
function createCanvasFillStyle(ctx, color, width, height) {
|
|
588
|
+
if (!isWaveformGradient(color)) {
|
|
589
|
+
return color;
|
|
590
|
+
}
|
|
591
|
+
let gradient;
|
|
592
|
+
if (color.direction === "vertical") {
|
|
593
|
+
gradient = ctx.createLinearGradient(0, 0, 0, height);
|
|
594
|
+
} else {
|
|
595
|
+
gradient = ctx.createLinearGradient(0, 0, width, 0);
|
|
596
|
+
}
|
|
597
|
+
for (const stop of color.stops) {
|
|
598
|
+
gradient.addColorStop(stop.offset, stop.color);
|
|
599
|
+
}
|
|
600
|
+
return gradient;
|
|
601
|
+
}
|
|
602
|
+
var Waveform = import_styled_components9.default.canvas.attrs((props) => ({
|
|
603
|
+
style: {
|
|
604
|
+
width: `${props.$cssWidth}px`,
|
|
605
|
+
height: `${props.$waveHeight}px`
|
|
606
|
+
}
|
|
607
|
+
}))`
|
|
608
|
+
float: left;
|
|
609
|
+
position: relative;
|
|
610
|
+
/* Promote to own compositing layer for smoother scrolling */
|
|
611
|
+
will-change: transform;
|
|
612
|
+
/* Disable image rendering interpolation */
|
|
613
|
+
image-rendering: pixelated;
|
|
614
|
+
image-rendering: crisp-edges;
|
|
615
|
+
`;
|
|
616
|
+
var Wrapper = import_styled_components9.default.div.attrs((props) => ({
|
|
617
|
+
style: {
|
|
618
|
+
top: `${props.$waveHeight * props.$index}px`,
|
|
619
|
+
width: `${props.$cssWidth}px`,
|
|
620
|
+
height: `${props.$waveHeight}px`
|
|
621
|
+
}
|
|
622
|
+
}))`
|
|
623
|
+
position: absolute;
|
|
624
|
+
background: ${(props) => props.$waveFillColor};
|
|
625
|
+
/* Force GPU compositing layer to reduce scroll flickering */
|
|
626
|
+
transform: translateZ(0);
|
|
627
|
+
backface-visibility: hidden;
|
|
628
|
+
`;
|
|
629
|
+
var Channel = (props) => {
|
|
630
|
+
const {
|
|
631
|
+
data,
|
|
632
|
+
bits,
|
|
633
|
+
length,
|
|
634
|
+
index,
|
|
635
|
+
className,
|
|
636
|
+
devicePixelRatio = 1,
|
|
637
|
+
waveHeight = 80,
|
|
638
|
+
waveOutlineColor = "#E0EFF1",
|
|
639
|
+
waveFillColor = "grey",
|
|
640
|
+
barWidth = 1,
|
|
641
|
+
barGap = 0,
|
|
642
|
+
transparentBackground = false,
|
|
643
|
+
drawMode = "inverted"
|
|
644
|
+
} = props;
|
|
645
|
+
const canvasesRef = (0, import_react.useRef)([]);
|
|
646
|
+
const canvasRef = (0, import_react.useCallback)(
|
|
647
|
+
(canvas) => {
|
|
648
|
+
if (canvas !== null) {
|
|
649
|
+
const index2 = parseInt(canvas.dataset.index, 10);
|
|
650
|
+
canvasesRef.current[index2] = canvas;
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
[]
|
|
654
|
+
);
|
|
655
|
+
(0, import_react.useLayoutEffect)(() => {
|
|
656
|
+
const canvases = canvasesRef.current;
|
|
657
|
+
const step = barWidth + barGap;
|
|
658
|
+
let globalPixelOffset = 0;
|
|
659
|
+
for (let i = 0; i < canvases.length; i++) {
|
|
660
|
+
const canvas = canvases[i];
|
|
661
|
+
const ctx = canvas.getContext("2d");
|
|
662
|
+
const h2 = Math.floor(waveHeight / 2);
|
|
663
|
+
const maxValue = 2 ** (bits - 1);
|
|
664
|
+
if (ctx) {
|
|
665
|
+
ctx.resetTransform();
|
|
666
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
667
|
+
ctx.imageSmoothingEnabled = false;
|
|
668
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
669
|
+
const canvasWidth = canvas.width / devicePixelRatio;
|
|
670
|
+
let fillColor;
|
|
671
|
+
if (drawMode === "normal") {
|
|
672
|
+
fillColor = waveFillColor;
|
|
673
|
+
} else {
|
|
674
|
+
fillColor = waveOutlineColor;
|
|
675
|
+
}
|
|
676
|
+
ctx.fillStyle = createCanvasFillStyle(
|
|
677
|
+
ctx,
|
|
678
|
+
fillColor,
|
|
679
|
+
canvasWidth,
|
|
680
|
+
waveHeight
|
|
681
|
+
);
|
|
682
|
+
const canvasStartGlobal = globalPixelOffset;
|
|
683
|
+
const canvasEndGlobal = globalPixelOffset + canvasWidth;
|
|
684
|
+
const firstBarGlobal = Math.floor((canvasStartGlobal - barWidth + step) / step) * step;
|
|
685
|
+
for (let barGlobal = Math.max(0, firstBarGlobal); barGlobal < canvasEndGlobal; barGlobal += step) {
|
|
686
|
+
const x = barGlobal - canvasStartGlobal;
|
|
687
|
+
if (x + barWidth <= 0) continue;
|
|
688
|
+
const peakIndex = barGlobal;
|
|
689
|
+
if (peakIndex * 2 + 1 < data.length) {
|
|
690
|
+
const minPeak = data[peakIndex * 2] / maxValue;
|
|
691
|
+
const maxPeak = data[peakIndex * 2 + 1] / maxValue;
|
|
692
|
+
const min = Math.abs(minPeak * h2);
|
|
693
|
+
const max = Math.abs(maxPeak * h2);
|
|
694
|
+
if (drawMode === "normal") {
|
|
695
|
+
ctx.fillRect(x, h2 - max, barWidth, max + min);
|
|
696
|
+
} else {
|
|
697
|
+
ctx.fillRect(x, 0, barWidth, h2 - max);
|
|
698
|
+
ctx.fillRect(x, h2 + min, barWidth, h2 - min);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
globalPixelOffset += canvas.width / devicePixelRatio;
|
|
704
|
+
}
|
|
705
|
+
}, [
|
|
706
|
+
data,
|
|
707
|
+
bits,
|
|
708
|
+
waveHeight,
|
|
709
|
+
waveOutlineColor,
|
|
710
|
+
waveFillColor,
|
|
711
|
+
devicePixelRatio,
|
|
712
|
+
length,
|
|
713
|
+
barWidth,
|
|
714
|
+
barGap,
|
|
715
|
+
drawMode
|
|
716
|
+
]);
|
|
717
|
+
let totalWidth = length;
|
|
718
|
+
let waveformCount = 0;
|
|
719
|
+
const waveforms = [];
|
|
720
|
+
while (totalWidth > 0) {
|
|
721
|
+
const currentWidth = Math.min(totalWidth, MAX_CANVAS_WIDTH);
|
|
722
|
+
const waveform = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
723
|
+
Waveform,
|
|
724
|
+
{
|
|
725
|
+
$cssWidth: currentWidth,
|
|
726
|
+
width: currentWidth * devicePixelRatio,
|
|
727
|
+
height: waveHeight * devicePixelRatio,
|
|
728
|
+
$waveHeight: waveHeight,
|
|
729
|
+
"data-index": waveformCount,
|
|
730
|
+
ref: canvasRef
|
|
731
|
+
},
|
|
732
|
+
`${length}-${waveformCount}`
|
|
733
|
+
);
|
|
734
|
+
waveforms.push(waveform);
|
|
735
|
+
totalWidth -= currentWidth;
|
|
736
|
+
waveformCount += 1;
|
|
737
|
+
}
|
|
738
|
+
const bgColor = waveFillColor;
|
|
739
|
+
const backgroundCss = transparentBackground ? "transparent" : waveformColorToCss(bgColor);
|
|
740
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
741
|
+
Wrapper,
|
|
742
|
+
{
|
|
743
|
+
$index: index,
|
|
744
|
+
$cssWidth: length,
|
|
745
|
+
className,
|
|
746
|
+
$waveHeight: waveHeight,
|
|
747
|
+
$waveFillColor: backgroundCss,
|
|
748
|
+
children: waveforms
|
|
749
|
+
}
|
|
750
|
+
);
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
// src/components/Clip.tsx
|
|
754
|
+
var import_styled_components13 = __toESM(require("styled-components"));
|
|
755
|
+
var import_core = require("@dnd-kit/core");
|
|
756
|
+
var import_utilities = require("@dnd-kit/utilities");
|
|
757
|
+
|
|
758
|
+
// src/components/ClipHeader.tsx
|
|
759
|
+
var import_styled_components10 = __toESM(require("styled-components"));
|
|
760
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
761
|
+
var CLIP_HEADER_HEIGHT = 22;
|
|
762
|
+
var HeaderContainer = import_styled_components10.default.div`
|
|
763
|
+
position: relative;
|
|
764
|
+
height: ${CLIP_HEADER_HEIGHT}px;
|
|
765
|
+
background: ${(props) => props.$isSelected ? props.theme.selectedClipHeaderBackgroundColor : props.theme.clipHeaderBackgroundColor};
|
|
766
|
+
border-bottom: 1px solid ${(props) => props.theme.clipHeaderBorderColor};
|
|
767
|
+
display: flex;
|
|
768
|
+
align-items: center;
|
|
769
|
+
padding: 0 8px;
|
|
770
|
+
cursor: ${(props) => props.$interactive ? props.$isDragging ? "grabbing" : "grab" : "default"};
|
|
771
|
+
user-select: none;
|
|
772
|
+
z-index: 110;
|
|
773
|
+
flex-shrink: 0;
|
|
774
|
+
pointer-events: auto; /* Re-enable pointer events (parent ClipContainer has pointer-events: none) */
|
|
775
|
+
|
|
776
|
+
${(props) => props.$interactive && `
|
|
777
|
+
&:hover {
|
|
778
|
+
background: ${props.theme.clipHeaderBackgroundColor}dd;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
&:active {
|
|
782
|
+
cursor: grabbing;
|
|
783
|
+
}
|
|
784
|
+
`}
|
|
785
|
+
`;
|
|
786
|
+
var TrackName = import_styled_components10.default.span`
|
|
787
|
+
font-size: 11px;
|
|
788
|
+
font-weight: 600;
|
|
789
|
+
font-family: ${(props) => props.theme.clipHeaderFontFamily};
|
|
790
|
+
color: ${(props) => props.theme.clipHeaderTextColor};
|
|
791
|
+
white-space: nowrap;
|
|
792
|
+
overflow: hidden;
|
|
793
|
+
text-overflow: ellipsis;
|
|
794
|
+
`;
|
|
795
|
+
var ClipHeaderPresentational = ({
|
|
796
|
+
trackName,
|
|
797
|
+
isSelected = false
|
|
798
|
+
}) => {
|
|
799
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
800
|
+
HeaderContainer,
|
|
801
|
+
{
|
|
802
|
+
$isDragging: false,
|
|
803
|
+
$interactive: false,
|
|
804
|
+
$isSelected: isSelected,
|
|
805
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TrackName, { children: trackName })
|
|
806
|
+
}
|
|
807
|
+
);
|
|
808
|
+
};
|
|
809
|
+
var ClipHeader = ({
|
|
810
|
+
clipId,
|
|
811
|
+
trackIndex,
|
|
812
|
+
clipIndex,
|
|
813
|
+
trackName,
|
|
814
|
+
isSelected = false,
|
|
815
|
+
disableDrag = false,
|
|
816
|
+
dragHandleProps
|
|
817
|
+
}) => {
|
|
818
|
+
if (disableDrag || !dragHandleProps) {
|
|
819
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
820
|
+
ClipHeaderPresentational,
|
|
821
|
+
{
|
|
822
|
+
trackName,
|
|
823
|
+
isSelected
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
const { attributes, listeners, setActivatorNodeRef } = dragHandleProps;
|
|
828
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
829
|
+
HeaderContainer,
|
|
830
|
+
{
|
|
831
|
+
ref: setActivatorNodeRef,
|
|
832
|
+
"data-clip-id": clipId,
|
|
833
|
+
$interactive: true,
|
|
834
|
+
$isSelected: isSelected,
|
|
835
|
+
...listeners,
|
|
836
|
+
...attributes,
|
|
837
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TrackName, { children: trackName })
|
|
838
|
+
}
|
|
839
|
+
);
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
// src/components/ClipBoundary.tsx
|
|
843
|
+
var import_react2 = __toESM(require("react"));
|
|
844
|
+
var import_styled_components11 = __toESM(require("styled-components"));
|
|
845
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
846
|
+
var CLIP_BOUNDARY_WIDTH = 8;
|
|
847
|
+
var BoundaryContainer = import_styled_components11.default.div`
|
|
848
|
+
position: absolute;
|
|
849
|
+
${(props) => props.$edge === "left" ? "left: 0;" : "right: 0;"}
|
|
850
|
+
top: 0;
|
|
851
|
+
bottom: 0;
|
|
852
|
+
width: ${CLIP_BOUNDARY_WIDTH}px;
|
|
853
|
+
cursor: col-resize;
|
|
854
|
+
user-select: none;
|
|
855
|
+
z-index: 105; /* Above waveform, below header */
|
|
856
|
+
pointer-events: auto; /* Re-enable pointer events (parent ClipContainer has pointer-events: none) */
|
|
857
|
+
|
|
858
|
+
/* Invisible by default, visible on hover */
|
|
859
|
+
background: ${(props) => props.$isDragging ? "rgba(255, 255, 255, 0.4)" : props.$isHovered ? "rgba(255, 255, 255, 0.2)" : "transparent"};
|
|
860
|
+
|
|
861
|
+
${(props) => props.$edge === "left" ? `border-left: 2px solid ${props.$isDragging ? "rgba(255, 255, 255, 0.8)" : props.$isHovered ? "rgba(255, 255, 255, 0.5)" : "transparent"};` : `border-right: 2px solid ${props.$isDragging ? "rgba(255, 255, 255, 0.8)" : props.$isHovered ? "rgba(255, 255, 255, 0.5)" : "transparent"};`}
|
|
862
|
+
|
|
863
|
+
transition: background 0.15s ease, border-color 0.15s ease;
|
|
864
|
+
|
|
865
|
+
&:hover {
|
|
866
|
+
background: rgba(255, 255, 255, 0.2);
|
|
867
|
+
${(props) => props.$edge === "left" ? "border-left: 2px solid rgba(255, 255, 255, 0.5);" : "border-right: 2px solid rgba(255, 255, 255, 0.5);"}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
&:active {
|
|
871
|
+
background: rgba(255, 255, 255, 0.4);
|
|
872
|
+
${(props) => props.$edge === "left" ? "border-left: 2px solid rgba(255, 255, 255, 0.8);" : "border-right: 2px solid rgba(255, 255, 255, 0.8);"}
|
|
873
|
+
}
|
|
874
|
+
`;
|
|
875
|
+
var ClipBoundary = ({
|
|
876
|
+
clipId,
|
|
877
|
+
trackIndex,
|
|
878
|
+
clipIndex,
|
|
879
|
+
edge,
|
|
880
|
+
dragHandleProps
|
|
881
|
+
}) => {
|
|
882
|
+
const [isHovered, setIsHovered] = import_react2.default.useState(false);
|
|
883
|
+
if (!dragHandleProps) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
const { attributes, listeners, setActivatorNodeRef, isDragging } = dragHandleProps;
|
|
887
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
888
|
+
BoundaryContainer,
|
|
889
|
+
{
|
|
890
|
+
ref: setActivatorNodeRef,
|
|
891
|
+
"data-clip-id": clipId,
|
|
892
|
+
"data-boundary-edge": edge,
|
|
893
|
+
$edge: edge,
|
|
894
|
+
$isDragging: isDragging,
|
|
895
|
+
$isHovered: isHovered,
|
|
896
|
+
onMouseEnter: () => setIsHovered(true),
|
|
897
|
+
onMouseLeave: () => setIsHovered(false),
|
|
898
|
+
...listeners,
|
|
899
|
+
...attributes
|
|
900
|
+
}
|
|
901
|
+
);
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
// src/components/FadeOverlay.tsx
|
|
905
|
+
var import_styled_components12 = __toESM(require("styled-components"));
|
|
906
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
907
|
+
var FadeContainer = import_styled_components12.default.div.attrs((props) => ({
|
|
908
|
+
style: {
|
|
909
|
+
left: `${props.$left}px`,
|
|
910
|
+
width: `${props.$width}px`
|
|
911
|
+
}
|
|
912
|
+
}))`
|
|
913
|
+
position: absolute;
|
|
914
|
+
top: 0;
|
|
915
|
+
bottom: 0;
|
|
916
|
+
pointer-events: none;
|
|
917
|
+
z-index: 50;
|
|
918
|
+
`;
|
|
919
|
+
var FadeSvg = import_styled_components12.default.svg`
|
|
920
|
+
width: 100%;
|
|
921
|
+
height: 100%;
|
|
922
|
+
display: block;
|
|
923
|
+
/* Flip horizontally for fadeOut - makes it mirror of fadeIn */
|
|
924
|
+
transform: ${(props) => props.$type === "fadeOut" ? "scaleX(-1)" : "none"};
|
|
925
|
+
`;
|
|
926
|
+
function generateFadePath(width, height, curveType = "logarithmic") {
|
|
927
|
+
const points = [];
|
|
928
|
+
const numPoints = Math.max(20, Math.min(width, 100));
|
|
929
|
+
for (let i = 0; i <= numPoints; i++) {
|
|
930
|
+
const x = i / numPoints * width;
|
|
931
|
+
const progress = i / numPoints;
|
|
932
|
+
let curvedProgress;
|
|
933
|
+
switch (curveType) {
|
|
934
|
+
case "linear":
|
|
935
|
+
curvedProgress = progress;
|
|
936
|
+
break;
|
|
937
|
+
case "exponential":
|
|
938
|
+
curvedProgress = progress * progress;
|
|
939
|
+
break;
|
|
940
|
+
case "sCurve":
|
|
941
|
+
curvedProgress = (1 - Math.cos(progress * Math.PI)) / 2;
|
|
942
|
+
break;
|
|
943
|
+
case "logarithmic":
|
|
944
|
+
default:
|
|
945
|
+
curvedProgress = Math.log10(1 + progress * 9) / Math.log10(10);
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
const y = (1 - curvedProgress) * height;
|
|
949
|
+
points.push(`${x},${y}`);
|
|
950
|
+
}
|
|
951
|
+
return `M 0,${height} L ${points.join(" L ")} L ${width},0 L 0,0 Z`;
|
|
952
|
+
}
|
|
953
|
+
var FadeOverlay = ({
|
|
954
|
+
left,
|
|
955
|
+
width,
|
|
956
|
+
type,
|
|
957
|
+
curveType = "logarithmic",
|
|
958
|
+
color
|
|
959
|
+
}) => {
|
|
960
|
+
const theme = (0, import_styled_components12.useTheme)();
|
|
961
|
+
if (width < 1) return null;
|
|
962
|
+
const fillColor = color || theme?.fadeOverlayColor || "rgba(0, 0, 0, 0.4)";
|
|
963
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FadeContainer, { $left: left, $width: width, $type: type, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FadeSvg, { $type: type, viewBox: `0 0 ${width} 100`, preserveAspectRatio: "none", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
964
|
+
"path",
|
|
965
|
+
{
|
|
966
|
+
d: generateFadePath(width, 100, curveType),
|
|
967
|
+
fill: fillColor
|
|
968
|
+
}
|
|
969
|
+
) }) });
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
// src/components/Clip.tsx
|
|
973
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
974
|
+
var ClipContainer = import_styled_components13.default.div.attrs((props) => ({
|
|
975
|
+
style: props.$isOverlay ? {} : {
|
|
976
|
+
left: `${props.$left}px`,
|
|
977
|
+
width: `${props.$width}px`
|
|
978
|
+
}
|
|
979
|
+
}))`
|
|
980
|
+
position: ${(props) => props.$isOverlay ? "relative" : "absolute"};
|
|
981
|
+
top: 0;
|
|
982
|
+
height: ${(props) => props.$isOverlay ? "auto" : "100%"};
|
|
983
|
+
width: ${(props) => props.$isOverlay ? `${props.$width}px` : "auto"};
|
|
984
|
+
display: flex;
|
|
985
|
+
flex-direction: column;
|
|
986
|
+
background: rgba(255, 255, 255, 0.05);
|
|
987
|
+
z-index: 10; /* Above progress overlay (z-index: 2) but below controls/playhead */
|
|
988
|
+
pointer-events: none; /* Let clicks pass through to ClickOverlay for playhead positioning */
|
|
989
|
+
|
|
990
|
+
&:hover {
|
|
991
|
+
background: rgba(255, 255, 255, 0.08);
|
|
992
|
+
}
|
|
993
|
+
`;
|
|
994
|
+
var ChannelsWrapper = import_styled_components13.default.div`
|
|
995
|
+
flex: 1;
|
|
996
|
+
position: relative;
|
|
997
|
+
overflow: ${(props) => props.$isOverlay ? "visible" : "hidden"};
|
|
998
|
+
`;
|
|
999
|
+
var Clip = ({
|
|
1000
|
+
children,
|
|
1001
|
+
className,
|
|
1002
|
+
clipId,
|
|
1003
|
+
trackIndex,
|
|
1004
|
+
clipIndex,
|
|
1005
|
+
trackName,
|
|
1006
|
+
startSample,
|
|
1007
|
+
durationSamples,
|
|
1008
|
+
samplesPerPixel,
|
|
1009
|
+
showHeader = false,
|
|
1010
|
+
disableHeaderDrag = false,
|
|
1011
|
+
isOverlay = false,
|
|
1012
|
+
isSelected = false,
|
|
1013
|
+
onMouseDown,
|
|
1014
|
+
trackId,
|
|
1015
|
+
fadeIn,
|
|
1016
|
+
fadeOut,
|
|
1017
|
+
sampleRate = 44100,
|
|
1018
|
+
showFades = false
|
|
1019
|
+
}) => {
|
|
1020
|
+
const left = Math.floor(startSample / samplesPerPixel);
|
|
1021
|
+
const endPixel = Math.floor((startSample + durationSamples) / samplesPerPixel);
|
|
1022
|
+
const width = endPixel - left;
|
|
1023
|
+
const enableDrag = showHeader && !disableHeaderDrag && !isOverlay;
|
|
1024
|
+
const draggableId = `clip-${trackIndex}-${clipIndex}`;
|
|
1025
|
+
const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, isDragging } = (0, import_core.useDraggable)({
|
|
1026
|
+
id: draggableId,
|
|
1027
|
+
data: { clipId, trackIndex, clipIndex },
|
|
1028
|
+
disabled: !enableDrag
|
|
1029
|
+
});
|
|
1030
|
+
const leftBoundaryId = `clip-boundary-left-${trackIndex}-${clipIndex}`;
|
|
1031
|
+
const {
|
|
1032
|
+
attributes: leftBoundaryAttributes,
|
|
1033
|
+
listeners: leftBoundaryListeners,
|
|
1034
|
+
setActivatorNodeRef: setLeftBoundaryActivatorRef,
|
|
1035
|
+
isDragging: isLeftBoundaryDragging
|
|
1036
|
+
} = (0, import_core.useDraggable)({
|
|
1037
|
+
id: leftBoundaryId,
|
|
1038
|
+
data: { clipId, trackIndex, clipIndex, boundary: "left" },
|
|
1039
|
+
disabled: !enableDrag
|
|
1040
|
+
});
|
|
1041
|
+
const rightBoundaryId = `clip-boundary-right-${trackIndex}-${clipIndex}`;
|
|
1042
|
+
const {
|
|
1043
|
+
attributes: rightBoundaryAttributes,
|
|
1044
|
+
listeners: rightBoundaryListeners,
|
|
1045
|
+
setActivatorNodeRef: setRightBoundaryActivatorRef,
|
|
1046
|
+
isDragging: isRightBoundaryDragging
|
|
1047
|
+
} = (0, import_core.useDraggable)({
|
|
1048
|
+
id: rightBoundaryId,
|
|
1049
|
+
data: { clipId, trackIndex, clipIndex, boundary: "right" },
|
|
1050
|
+
disabled: !enableDrag
|
|
1051
|
+
});
|
|
1052
|
+
const style = transform ? {
|
|
1053
|
+
transform: import_utilities.CSS.Translate.toString(transform),
|
|
1054
|
+
zIndex: isDragging ? 100 : void 0
|
|
1055
|
+
// Below controls (z-index: 999) but above other clips
|
|
1056
|
+
} : void 0;
|
|
1057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1058
|
+
ClipContainer,
|
|
1059
|
+
{
|
|
1060
|
+
ref: setNodeRef,
|
|
1061
|
+
style,
|
|
1062
|
+
className,
|
|
1063
|
+
$left: left,
|
|
1064
|
+
$width: width,
|
|
1065
|
+
$isOverlay: isOverlay,
|
|
1066
|
+
"data-clip-container": "true",
|
|
1067
|
+
"data-track-id": trackId,
|
|
1068
|
+
onMouseDown,
|
|
1069
|
+
children: [
|
|
1070
|
+
showHeader && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1071
|
+
ClipHeader,
|
|
1072
|
+
{
|
|
1073
|
+
clipId,
|
|
1074
|
+
trackIndex,
|
|
1075
|
+
clipIndex,
|
|
1076
|
+
trackName,
|
|
1077
|
+
isSelected,
|
|
1078
|
+
disableDrag: disableHeaderDrag,
|
|
1079
|
+
dragHandleProps: enableDrag ? { attributes, listeners, setActivatorNodeRef } : void 0
|
|
1080
|
+
}
|
|
1081
|
+
),
|
|
1082
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(ChannelsWrapper, { $isOverlay: isOverlay, children: [
|
|
1083
|
+
children,
|
|
1084
|
+
showFades && fadeIn && fadeIn.duration > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1085
|
+
FadeOverlay,
|
|
1086
|
+
{
|
|
1087
|
+
left: 0,
|
|
1088
|
+
width: Math.floor(fadeIn.duration * sampleRate / samplesPerPixel),
|
|
1089
|
+
type: "fadeIn",
|
|
1090
|
+
curveType: fadeIn.type
|
|
1091
|
+
}
|
|
1092
|
+
),
|
|
1093
|
+
showFades && fadeOut && fadeOut.duration > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1094
|
+
FadeOverlay,
|
|
1095
|
+
{
|
|
1096
|
+
left: width - Math.floor(fadeOut.duration * sampleRate / samplesPerPixel),
|
|
1097
|
+
width: Math.floor(fadeOut.duration * sampleRate / samplesPerPixel),
|
|
1098
|
+
type: "fadeOut",
|
|
1099
|
+
curveType: fadeOut.type
|
|
1100
|
+
}
|
|
1101
|
+
)
|
|
1102
|
+
] }),
|
|
1103
|
+
showHeader && !disableHeaderDrag && !isOverlay && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1104
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1105
|
+
ClipBoundary,
|
|
1106
|
+
{
|
|
1107
|
+
clipId,
|
|
1108
|
+
trackIndex,
|
|
1109
|
+
clipIndex,
|
|
1110
|
+
edge: "left",
|
|
1111
|
+
dragHandleProps: {
|
|
1112
|
+
attributes: leftBoundaryAttributes,
|
|
1113
|
+
listeners: leftBoundaryListeners,
|
|
1114
|
+
setActivatorNodeRef: setLeftBoundaryActivatorRef,
|
|
1115
|
+
isDragging: isLeftBoundaryDragging
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
),
|
|
1119
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1120
|
+
ClipBoundary,
|
|
1121
|
+
{
|
|
1122
|
+
clipId,
|
|
1123
|
+
trackIndex,
|
|
1124
|
+
clipIndex,
|
|
1125
|
+
edge: "right",
|
|
1126
|
+
dragHandleProps: {
|
|
1127
|
+
attributes: rightBoundaryAttributes,
|
|
1128
|
+
listeners: rightBoundaryListeners,
|
|
1129
|
+
setActivatorNodeRef: setRightBoundaryActivatorRef,
|
|
1130
|
+
isDragging: isRightBoundaryDragging
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
)
|
|
1134
|
+
] })
|
|
1135
|
+
]
|
|
1136
|
+
}
|
|
1137
|
+
);
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1140
|
+
// src/components/MasterVolumeControl.tsx
|
|
1141
|
+
var import_styled_components14 = __toESM(require("styled-components"));
|
|
1142
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1143
|
+
var VolumeContainer = import_styled_components14.default.div`
|
|
1144
|
+
display: inline-flex;
|
|
1145
|
+
align-items: center;
|
|
1146
|
+
gap: 0.5rem;
|
|
1147
|
+
`;
|
|
1148
|
+
var VolumeLabel = (0, import_styled_components14.default)(BaseLabel)`
|
|
1149
|
+
margin: 0;
|
|
1150
|
+
white-space: nowrap;
|
|
1151
|
+
`;
|
|
1152
|
+
var VolumeSlider = (0, import_styled_components14.default)(BaseSlider)`
|
|
1153
|
+
width: 120px;
|
|
1154
|
+
`;
|
|
1155
|
+
var MasterVolumeControl = ({
|
|
1156
|
+
volume,
|
|
1157
|
+
onChange,
|
|
1158
|
+
disabled = false,
|
|
1159
|
+
className
|
|
1160
|
+
}) => {
|
|
1161
|
+
const handleChange = (e) => {
|
|
1162
|
+
onChange(parseFloat(e.target.value) / 100);
|
|
1163
|
+
};
|
|
1164
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(VolumeContainer, { className, children: [
|
|
1165
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VolumeLabel, { htmlFor: "master-gain", children: "Master Volume" }),
|
|
1166
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1167
|
+
VolumeSlider,
|
|
1168
|
+
{
|
|
1169
|
+
min: "0",
|
|
1170
|
+
max: "100",
|
|
1171
|
+
value: volume * 100,
|
|
1172
|
+
onChange: handleChange,
|
|
1173
|
+
disabled,
|
|
1174
|
+
id: "master-gain"
|
|
1175
|
+
}
|
|
1176
|
+
)
|
|
1177
|
+
] });
|
|
1178
|
+
};
|
|
1179
|
+
|
|
1180
|
+
// src/components/Playhead.tsx
|
|
1181
|
+
var import_react3 = require("react");
|
|
1182
|
+
var import_styled_components15 = __toESM(require("styled-components"));
|
|
1183
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1184
|
+
var PlayheadLine = import_styled_components15.default.div.attrs((props) => ({
|
|
1185
|
+
style: {
|
|
1186
|
+
transform: `translate3d(${props.$position}px, 0, 0)`
|
|
1187
|
+
}
|
|
1188
|
+
}))`
|
|
1189
|
+
position: absolute;
|
|
1190
|
+
top: 0;
|
|
1191
|
+
left: 0;
|
|
1192
|
+
width: 2px;
|
|
1193
|
+
background: ${(props) => props.$color};
|
|
1194
|
+
height: 100%;
|
|
1195
|
+
z-index: 100; /* Below sticky controls (z-index: 101) so playhead is hidden when scrolled behind controls */
|
|
1196
|
+
pointer-events: none;
|
|
1197
|
+
will-change: transform;
|
|
1198
|
+
`;
|
|
1199
|
+
var Playhead = ({ position, color = "#ff0000" }) => {
|
|
1200
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PlayheadLine, { $position: position, $color: color });
|
|
1201
|
+
};
|
|
1202
|
+
var PlayheadWithMarkerContainer = import_styled_components15.default.div`
|
|
1203
|
+
position: absolute;
|
|
1204
|
+
top: 0;
|
|
1205
|
+
left: 0;
|
|
1206
|
+
height: 100%;
|
|
1207
|
+
z-index: 100; /* Below sticky controls (z-index: 101) so playhead is hidden when scrolled behind controls */
|
|
1208
|
+
pointer-events: none;
|
|
1209
|
+
will-change: transform;
|
|
1210
|
+
`;
|
|
1211
|
+
var MarkerTriangle = import_styled_components15.default.div`
|
|
1212
|
+
position: absolute;
|
|
1213
|
+
top: -10px;
|
|
1214
|
+
left: -6px;
|
|
1215
|
+
width: 0;
|
|
1216
|
+
height: 0;
|
|
1217
|
+
border-left: 7px solid transparent;
|
|
1218
|
+
border-right: 7px solid transparent;
|
|
1219
|
+
border-top: 10px solid ${(props) => props.$color};
|
|
1220
|
+
`;
|
|
1221
|
+
var MarkerLine = import_styled_components15.default.div`
|
|
1222
|
+
position: absolute;
|
|
1223
|
+
top: 0;
|
|
1224
|
+
left: 0;
|
|
1225
|
+
width: 2px;
|
|
1226
|
+
height: 100%;
|
|
1227
|
+
background: ${(props) => props.$color};
|
|
1228
|
+
`;
|
|
1229
|
+
var PlayheadWithMarker = ({
|
|
1230
|
+
color = "#ff0000",
|
|
1231
|
+
isPlaying,
|
|
1232
|
+
currentTimeRef,
|
|
1233
|
+
playbackStartTimeRef,
|
|
1234
|
+
audioStartPositionRef,
|
|
1235
|
+
samplesPerPixel,
|
|
1236
|
+
sampleRate,
|
|
1237
|
+
controlsOffset,
|
|
1238
|
+
getAudioContextTime
|
|
1239
|
+
}) => {
|
|
1240
|
+
const containerRef = (0, import_react3.useRef)(null);
|
|
1241
|
+
const animationFrameRef = (0, import_react3.useRef)(null);
|
|
1242
|
+
(0, import_react3.useEffect)(() => {
|
|
1243
|
+
const updatePosition = () => {
|
|
1244
|
+
if (containerRef.current) {
|
|
1245
|
+
let time;
|
|
1246
|
+
if (isPlaying && getAudioContextTime) {
|
|
1247
|
+
const elapsed = getAudioContextTime() - (playbackStartTimeRef.current ?? 0);
|
|
1248
|
+
time = (audioStartPositionRef.current ?? 0) + elapsed;
|
|
1249
|
+
} else {
|
|
1250
|
+
time = currentTimeRef.current ?? 0;
|
|
1251
|
+
}
|
|
1252
|
+
const pos = time * sampleRate / samplesPerPixel + controlsOffset;
|
|
1253
|
+
containerRef.current.style.transform = `translate3d(${pos}px, 0, 0)`;
|
|
1254
|
+
}
|
|
1255
|
+
if (isPlaying) {
|
|
1256
|
+
animationFrameRef.current = requestAnimationFrame(updatePosition);
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
if (isPlaying) {
|
|
1260
|
+
animationFrameRef.current = requestAnimationFrame(updatePosition);
|
|
1261
|
+
} else {
|
|
1262
|
+
updatePosition();
|
|
1263
|
+
}
|
|
1264
|
+
return () => {
|
|
1265
|
+
if (animationFrameRef.current) {
|
|
1266
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
1267
|
+
animationFrameRef.current = null;
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
}, [isPlaying, sampleRate, samplesPerPixel, controlsOffset, currentTimeRef, playbackStartTimeRef, audioStartPositionRef, getAudioContextTime]);
|
|
1271
|
+
(0, import_react3.useEffect)(() => {
|
|
1272
|
+
if (!isPlaying && containerRef.current) {
|
|
1273
|
+
const time = currentTimeRef.current ?? 0;
|
|
1274
|
+
const pos = time * sampleRate / samplesPerPixel + controlsOffset;
|
|
1275
|
+
containerRef.current.style.transform = `translate3d(${pos}px, 0, 0)`;
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(PlayheadWithMarkerContainer, { ref: containerRef, $color: color, children: [
|
|
1279
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MarkerTriangle, { $color: color }),
|
|
1280
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MarkerLine, { $color: color })
|
|
1281
|
+
] });
|
|
1282
|
+
};
|
|
1283
|
+
|
|
1284
|
+
// src/components/Playlist.tsx
|
|
1285
|
+
var import_styled_components16 = __toESM(require("styled-components"));
|
|
1286
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1287
|
+
var Wrapper2 = import_styled_components16.default.div`
|
|
1288
|
+
overflow-y: hidden;
|
|
1289
|
+
overflow-x: auto;
|
|
1290
|
+
position: relative;
|
|
1291
|
+
`;
|
|
1292
|
+
var ScrollContainer = import_styled_components16.default.div.attrs((props) => ({
|
|
1293
|
+
style: props.$width !== void 0 ? { width: `${props.$width}px` } : {}
|
|
1294
|
+
}))`
|
|
1295
|
+
position: relative;
|
|
1296
|
+
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
1297
|
+
`;
|
|
1298
|
+
var TimescaleWrapper = import_styled_components16.default.div.attrs((props) => ({
|
|
1299
|
+
style: props.$width ? { minWidth: `${props.$width}px` } : {}
|
|
1300
|
+
}))`
|
|
1301
|
+
background: ${(props) => props.$backgroundColor || "white"};
|
|
1302
|
+
width: 100%;
|
|
1303
|
+
overflow: visible;
|
|
1304
|
+
`;
|
|
1305
|
+
var TracksContainer = import_styled_components16.default.div.attrs((props) => ({
|
|
1306
|
+
style: props.$width !== void 0 ? { minWidth: `${props.$width}px` } : {}
|
|
1307
|
+
}))`
|
|
1308
|
+
position: relative;
|
|
1309
|
+
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
1310
|
+
width: 100%;
|
|
1311
|
+
`;
|
|
1312
|
+
var ClickOverlay = import_styled_components16.default.div`
|
|
1313
|
+
position: absolute;
|
|
1314
|
+
top: 0;
|
|
1315
|
+
left: 0;
|
|
1316
|
+
right: 0;
|
|
1317
|
+
bottom: 0;
|
|
1318
|
+
cursor: crosshair;
|
|
1319
|
+
z-index: 1; /* Low z-index - clip headers and boundaries have higher z-index */
|
|
1320
|
+
`;
|
|
1321
|
+
var Playlist = ({
|
|
1322
|
+
children,
|
|
1323
|
+
backgroundColor,
|
|
1324
|
+
timescaleBackgroundColor,
|
|
1325
|
+
timescale,
|
|
1326
|
+
timescaleWidth,
|
|
1327
|
+
tracksWidth,
|
|
1328
|
+
scrollContainerWidth,
|
|
1329
|
+
controlsWidth,
|
|
1330
|
+
onTracksClick,
|
|
1331
|
+
onTracksMouseDown,
|
|
1332
|
+
onTracksMouseMove,
|
|
1333
|
+
onTracksMouseUp,
|
|
1334
|
+
scrollContainerRef
|
|
1335
|
+
}) => {
|
|
1336
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Wrapper2, { "data-scroll-container": "true", ref: scrollContainerRef, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1337
|
+
ScrollContainer,
|
|
1338
|
+
{
|
|
1339
|
+
$backgroundColor: backgroundColor,
|
|
1340
|
+
$width: scrollContainerWidth,
|
|
1341
|
+
children: [
|
|
1342
|
+
timescale && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TimescaleWrapper, { $width: timescaleWidth, $backgroundColor: timescaleBackgroundColor, children: timescale }),
|
|
1343
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(TracksContainer, { $width: tracksWidth, $backgroundColor: backgroundColor, children: [
|
|
1344
|
+
children,
|
|
1345
|
+
(onTracksClick || onTracksMouseDown) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1346
|
+
ClickOverlay,
|
|
1347
|
+
{
|
|
1348
|
+
$controlsWidth: controlsWidth,
|
|
1349
|
+
onClick: onTracksClick,
|
|
1350
|
+
onMouseDown: onTracksMouseDown,
|
|
1351
|
+
onMouseMove: onTracksMouseMove,
|
|
1352
|
+
onMouseUp: onTracksMouseUp
|
|
1353
|
+
}
|
|
1354
|
+
)
|
|
1355
|
+
] })
|
|
1356
|
+
]
|
|
1357
|
+
}
|
|
1358
|
+
) });
|
|
1359
|
+
};
|
|
1360
|
+
var StyledPlaylist = (0, import_styled_components16.withTheme)(Playlist);
|
|
1361
|
+
|
|
1362
|
+
// src/components/Selection.tsx
|
|
1363
|
+
var import_styled_components17 = __toESM(require("styled-components"));
|
|
1364
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1365
|
+
var SelectionOverlay = import_styled_components17.default.div.attrs((props) => ({
|
|
1366
|
+
style: {
|
|
1367
|
+
left: `${props.$left}px`,
|
|
1368
|
+
width: `${props.$width}px`
|
|
1369
|
+
}
|
|
1370
|
+
}))`
|
|
1371
|
+
position: absolute;
|
|
1372
|
+
top: 0;
|
|
1373
|
+
background: ${(props) => props.$color};
|
|
1374
|
+
height: 100%;
|
|
1375
|
+
z-index: 5;
|
|
1376
|
+
pointer-events: none;
|
|
1377
|
+
opacity: 0.3;
|
|
1378
|
+
`;
|
|
1379
|
+
var Selection = ({
|
|
1380
|
+
startPosition,
|
|
1381
|
+
endPosition,
|
|
1382
|
+
color = "#00ff00"
|
|
1383
|
+
}) => {
|
|
1384
|
+
const width = Math.max(0, endPosition - startPosition);
|
|
1385
|
+
if (width <= 0) {
|
|
1386
|
+
return null;
|
|
1387
|
+
}
|
|
1388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SelectionOverlay, { $left: startPosition, $width: width, $color: color });
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
// src/components/SelectionTimeInputs.tsx
|
|
1392
|
+
var import_react5 = require("react");
|
|
1393
|
+
|
|
1394
|
+
// src/components/TimeInput.tsx
|
|
1395
|
+
var import_react4 = require("react");
|
|
1396
|
+
|
|
1397
|
+
// src/utils/timeFormat.ts
|
|
1398
|
+
function clockFormat(seconds, decimals) {
|
|
1399
|
+
const hours = Math.floor(seconds / 3600) % 24;
|
|
1400
|
+
const minutes = Math.floor(seconds / 60) % 60;
|
|
1401
|
+
const secs = (seconds % 60).toFixed(decimals);
|
|
1402
|
+
return String(hours).padStart(2, "0") + ":" + String(minutes).padStart(2, "0") + ":" + secs.padStart(decimals + 3, "0");
|
|
1403
|
+
}
|
|
1404
|
+
function formatTime(seconds, format) {
|
|
1405
|
+
switch (format) {
|
|
1406
|
+
case "seconds":
|
|
1407
|
+
return seconds.toFixed(0);
|
|
1408
|
+
case "thousandths":
|
|
1409
|
+
return seconds.toFixed(3);
|
|
1410
|
+
case "hh:mm:ss":
|
|
1411
|
+
return clockFormat(seconds, 0);
|
|
1412
|
+
case "hh:mm:ss.u":
|
|
1413
|
+
return clockFormat(seconds, 1);
|
|
1414
|
+
case "hh:mm:ss.uu":
|
|
1415
|
+
return clockFormat(seconds, 2);
|
|
1416
|
+
case "hh:mm:ss.uuu":
|
|
1417
|
+
return clockFormat(seconds, 3);
|
|
1418
|
+
default:
|
|
1419
|
+
return clockFormat(seconds, 3);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
function parseTime(timeStr, format) {
|
|
1423
|
+
if (!timeStr) return 0;
|
|
1424
|
+
switch (format) {
|
|
1425
|
+
case "seconds":
|
|
1426
|
+
case "thousandths":
|
|
1427
|
+
return parseFloat(timeStr) || 0;
|
|
1428
|
+
case "hh:mm:ss":
|
|
1429
|
+
case "hh:mm:ss.u":
|
|
1430
|
+
case "hh:mm:ss.uu":
|
|
1431
|
+
case "hh:mm:ss.uuu": {
|
|
1432
|
+
const parts = timeStr.split(":");
|
|
1433
|
+
if (parts.length !== 3) return 0;
|
|
1434
|
+
const hours = parseInt(parts[0], 10) || 0;
|
|
1435
|
+
const minutes = parseInt(parts[1], 10) || 0;
|
|
1436
|
+
const seconds = parseFloat(parts[2]) || 0;
|
|
1437
|
+
return hours * 3600 + minutes * 60 + seconds;
|
|
1438
|
+
}
|
|
1439
|
+
default:
|
|
1440
|
+
return 0;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
// src/components/TimeInput.tsx
|
|
1445
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1446
|
+
var TimeInput = ({
|
|
1447
|
+
id,
|
|
1448
|
+
label,
|
|
1449
|
+
value,
|
|
1450
|
+
format,
|
|
1451
|
+
className,
|
|
1452
|
+
onChange,
|
|
1453
|
+
readOnly = false
|
|
1454
|
+
}) => {
|
|
1455
|
+
const [displayValue, setDisplayValue] = (0, import_react4.useState)("");
|
|
1456
|
+
(0, import_react4.useEffect)(() => {
|
|
1457
|
+
const formatted = formatTime(value, format);
|
|
1458
|
+
setDisplayValue(formatted);
|
|
1459
|
+
}, [value, format, id]);
|
|
1460
|
+
const handleChange = (e) => {
|
|
1461
|
+
const newDisplayValue = e.target.value;
|
|
1462
|
+
setDisplayValue(newDisplayValue);
|
|
1463
|
+
};
|
|
1464
|
+
const handleBlur = () => {
|
|
1465
|
+
if (onChange) {
|
|
1466
|
+
const parsedValue = parseTime(displayValue, format);
|
|
1467
|
+
onChange(parsedValue);
|
|
1468
|
+
}
|
|
1469
|
+
setDisplayValue(formatTime(value, format));
|
|
1470
|
+
};
|
|
1471
|
+
const handleKeyDown = (e) => {
|
|
1472
|
+
if (e.key === "Enter") {
|
|
1473
|
+
e.currentTarget.blur();
|
|
1474
|
+
}
|
|
1475
|
+
};
|
|
1476
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
1477
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ScreenReaderOnly, { as: "label", htmlFor: id, children: label }),
|
|
1478
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1479
|
+
BaseInput,
|
|
1480
|
+
{
|
|
1481
|
+
type: "text",
|
|
1482
|
+
className,
|
|
1483
|
+
id,
|
|
1484
|
+
value: displayValue,
|
|
1485
|
+
onChange: handleChange,
|
|
1486
|
+
onBlur: handleBlur,
|
|
1487
|
+
onKeyDown: handleKeyDown,
|
|
1488
|
+
readOnly
|
|
1489
|
+
}
|
|
1490
|
+
)
|
|
1491
|
+
] });
|
|
1492
|
+
};
|
|
1493
|
+
|
|
1494
|
+
// src/components/SelectionTimeInputs.tsx
|
|
1495
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1496
|
+
var SelectionTimeInputs = ({
|
|
1497
|
+
selectionStart,
|
|
1498
|
+
selectionEnd,
|
|
1499
|
+
onSelectionChange,
|
|
1500
|
+
className
|
|
1501
|
+
}) => {
|
|
1502
|
+
const [timeFormat, setTimeFormat] = (0, import_react5.useState)("hh:mm:ss.uuu");
|
|
1503
|
+
(0, import_react5.useEffect)(() => {
|
|
1504
|
+
const timeFormatSelect = document.querySelector(".time-format");
|
|
1505
|
+
const handleFormatChange = () => {
|
|
1506
|
+
if (timeFormatSelect) {
|
|
1507
|
+
setTimeFormat(timeFormatSelect.value);
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
if (timeFormatSelect) {
|
|
1511
|
+
setTimeFormat(timeFormatSelect.value);
|
|
1512
|
+
timeFormatSelect.addEventListener("change", handleFormatChange);
|
|
1513
|
+
}
|
|
1514
|
+
return () => {
|
|
1515
|
+
timeFormatSelect?.removeEventListener("change", handleFormatChange);
|
|
1516
|
+
};
|
|
1517
|
+
}, []);
|
|
1518
|
+
const handleStartChange = (value) => {
|
|
1519
|
+
if (onSelectionChange) {
|
|
1520
|
+
onSelectionChange(value, selectionEnd);
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
const handleEndChange = (value) => {
|
|
1524
|
+
if (onSelectionChange) {
|
|
1525
|
+
onSelectionChange(selectionStart, value);
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
1529
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1530
|
+
TimeInput,
|
|
1531
|
+
{
|
|
1532
|
+
id: "audio_start",
|
|
1533
|
+
label: "Start of audio selection",
|
|
1534
|
+
value: selectionStart,
|
|
1535
|
+
format: timeFormat,
|
|
1536
|
+
className: "audio-start form-control mr-sm-2",
|
|
1537
|
+
onChange: handleStartChange
|
|
1538
|
+
}
|
|
1539
|
+
),
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1541
|
+
TimeInput,
|
|
1542
|
+
{
|
|
1543
|
+
id: "audio_end",
|
|
1544
|
+
label: "End of audio selection",
|
|
1545
|
+
value: selectionEnd,
|
|
1546
|
+
format: timeFormat,
|
|
1547
|
+
className: "audio-end form-control mr-sm-2",
|
|
1548
|
+
onChange: handleEndChange
|
|
1549
|
+
}
|
|
1550
|
+
)
|
|
1551
|
+
] });
|
|
1552
|
+
};
|
|
1553
|
+
|
|
1554
|
+
// src/contexts/DevicePixelRatio.tsx
|
|
1555
|
+
var import_react6 = require("react");
|
|
1556
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1557
|
+
function getScale() {
|
|
1558
|
+
return window.devicePixelRatio;
|
|
1559
|
+
}
|
|
1560
|
+
var DevicePixelRatioContext = (0, import_react6.createContext)(getScale());
|
|
1561
|
+
var DevicePixelRatioProvider = ({ children }) => {
|
|
1562
|
+
const [scale, setScale] = (0, import_react6.useState)(getScale());
|
|
1563
|
+
matchMedia(`(resolution: ${getScale()}dppx)`).addEventListener(
|
|
1564
|
+
"change",
|
|
1565
|
+
() => {
|
|
1566
|
+
setScale(getScale());
|
|
1567
|
+
},
|
|
1568
|
+
{ once: true }
|
|
1569
|
+
);
|
|
1570
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });
|
|
1571
|
+
};
|
|
1572
|
+
var useDevicePixelRatio = () => (0, import_react6.useContext)(DevicePixelRatioContext);
|
|
1573
|
+
|
|
1574
|
+
// src/contexts/PlaylistInfo.tsx
|
|
1575
|
+
var import_react7 = require("react");
|
|
1576
|
+
var PlaylistInfoContext = (0, import_react7.createContext)({
|
|
1577
|
+
sampleRate: 48e3,
|
|
1578
|
+
samplesPerPixel: 1e3,
|
|
1579
|
+
zoomLevels: [1e3, 1500, 2e3, 2500],
|
|
1580
|
+
waveHeight: 80,
|
|
1581
|
+
timeScaleHeight: 15,
|
|
1582
|
+
controls: {
|
|
1583
|
+
show: false,
|
|
1584
|
+
width: 150
|
|
1585
|
+
},
|
|
1586
|
+
duration: 3e4,
|
|
1587
|
+
barWidth: 1,
|
|
1588
|
+
barGap: 0
|
|
1589
|
+
});
|
|
1590
|
+
var usePlaylistInfo = () => (0, import_react7.useContext)(PlaylistInfoContext);
|
|
1591
|
+
|
|
1592
|
+
// src/contexts/Theme.tsx
|
|
1593
|
+
var import_react8 = require("react");
|
|
1594
|
+
var import_styled_components18 = require("styled-components");
|
|
1595
|
+
var useTheme2 = () => (0, import_react8.useContext)(import_styled_components18.ThemeContext);
|
|
1596
|
+
|
|
1597
|
+
// src/contexts/TrackControls.tsx
|
|
1598
|
+
var import_react9 = require("react");
|
|
1599
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1600
|
+
var TrackControlsContext = (0, import_react9.createContext)(/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react9.Fragment, {}));
|
|
1601
|
+
var useTrackControls = () => (0, import_react9.useContext)(TrackControlsContext);
|
|
1602
|
+
|
|
1603
|
+
// src/contexts/Playout.tsx
|
|
1604
|
+
var import_react10 = require("react");
|
|
1605
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1606
|
+
var defaultProgress = 0;
|
|
1607
|
+
var defaultIsPlaying = false;
|
|
1608
|
+
var defaultSelectionStart = 0;
|
|
1609
|
+
var defaultSelectionEnd = 0;
|
|
1610
|
+
var defaultPlayout = {
|
|
1611
|
+
progress: defaultProgress,
|
|
1612
|
+
isPlaying: defaultIsPlaying,
|
|
1613
|
+
selectionStart: defaultSelectionStart,
|
|
1614
|
+
selectionEnd: defaultSelectionEnd
|
|
1615
|
+
};
|
|
1616
|
+
var PlayoutStatusContext = (0, import_react10.createContext)(defaultPlayout);
|
|
1617
|
+
var PlayoutStatusUpdateContext = (0, import_react10.createContext)({
|
|
1618
|
+
setIsPlaying: () => {
|
|
1619
|
+
},
|
|
1620
|
+
setProgress: () => {
|
|
1621
|
+
},
|
|
1622
|
+
setSelection: () => {
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
var PlayoutProvider = ({ children }) => {
|
|
1626
|
+
const [isPlaying, setIsPlaying] = (0, import_react10.useState)(defaultIsPlaying);
|
|
1627
|
+
const [progress, setProgress] = (0, import_react10.useState)(defaultProgress);
|
|
1628
|
+
const [selectionStart, setSelectionStart] = (0, import_react10.useState)(defaultSelectionStart);
|
|
1629
|
+
const [selectionEnd, setSelectionEnd] = (0, import_react10.useState)(defaultSelectionEnd);
|
|
1630
|
+
const setSelection = (start, end) => {
|
|
1631
|
+
setSelectionStart(start);
|
|
1632
|
+
setSelectionEnd(end);
|
|
1633
|
+
};
|
|
1634
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PlayoutStatusUpdateContext.Provider, { value: { setIsPlaying, setProgress, setSelection }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PlayoutStatusContext.Provider, { value: { isPlaying, progress, selectionStart, selectionEnd }, children }) });
|
|
1635
|
+
};
|
|
1636
|
+
var usePlayoutStatus = () => (0, import_react10.useContext)(PlayoutStatusContext);
|
|
1637
|
+
var usePlayoutStatusUpdate = () => (0, import_react10.useContext)(PlayoutStatusUpdateContext);
|
|
1638
|
+
|
|
1639
|
+
// src/components/SmartChannel.tsx
|
|
1640
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1641
|
+
var SmartChannel = ({ isSelected, transparentBackground, ...props }) => {
|
|
1642
|
+
const theme = useTheme2();
|
|
1643
|
+
const { waveHeight, barWidth, barGap } = usePlaylistInfo();
|
|
1644
|
+
const devicePixelRatio = useDevicePixelRatio();
|
|
1645
|
+
const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;
|
|
1646
|
+
const waveFillColor = isSelected && theme ? theme.selectedWaveFillColor : theme?.waveFillColor;
|
|
1647
|
+
const drawMode = theme?.waveformDrawMode || "inverted";
|
|
1648
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1649
|
+
Channel,
|
|
1650
|
+
{
|
|
1651
|
+
...props,
|
|
1652
|
+
...theme,
|
|
1653
|
+
waveOutlineColor,
|
|
1654
|
+
waveFillColor,
|
|
1655
|
+
waveHeight,
|
|
1656
|
+
devicePixelRatio,
|
|
1657
|
+
barWidth,
|
|
1658
|
+
barGap,
|
|
1659
|
+
transparentBackground,
|
|
1660
|
+
drawMode
|
|
1661
|
+
}
|
|
1662
|
+
);
|
|
1663
|
+
};
|
|
1664
|
+
|
|
1665
|
+
// src/components/SmartScale.tsx
|
|
1666
|
+
var import_react12 = require("react");
|
|
1667
|
+
|
|
1668
|
+
// src/components/TimeScale.tsx
|
|
1669
|
+
var import_react11 = __toESM(require("react"));
|
|
1670
|
+
var import_styled_components19 = __toESM(require("styled-components"));
|
|
1671
|
+
|
|
1672
|
+
// src/utils/conversions.ts
|
|
1673
|
+
function samplesToSeconds(samples, sampleRate) {
|
|
1674
|
+
return samples / sampleRate;
|
|
1675
|
+
}
|
|
1676
|
+
function secondsToSamples(seconds, sampleRate) {
|
|
1677
|
+
return Math.ceil(seconds * sampleRate);
|
|
1678
|
+
}
|
|
1679
|
+
function samplesToPixels(samples, samplesPerPixel) {
|
|
1680
|
+
return Math.floor(samples / samplesPerPixel);
|
|
1681
|
+
}
|
|
1682
|
+
function pixelsToSamples(pixels, samplesPerPixel) {
|
|
1683
|
+
return Math.floor(pixels * samplesPerPixel);
|
|
1684
|
+
}
|
|
1685
|
+
function pixelsToSeconds(pixels, samplesPerPixel, sampleRate) {
|
|
1686
|
+
return pixels * samplesPerPixel / sampleRate;
|
|
1687
|
+
}
|
|
1688
|
+
function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
|
|
1689
|
+
return Math.ceil(seconds * sampleRate / samplesPerPixel);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
// src/components/TimeScale.tsx
|
|
1693
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1694
|
+
function formatTime2(milliseconds) {
|
|
1695
|
+
const seconds = Math.floor(milliseconds / 1e3);
|
|
1696
|
+
const s = seconds % 60;
|
|
1697
|
+
const m = (seconds - s) / 60;
|
|
1698
|
+
return `${m}:${String(s).padStart(2, "0")}`;
|
|
1699
|
+
}
|
|
1700
|
+
var PlaylistTimeScaleScroll = import_styled_components19.default.div.attrs((props) => ({
|
|
1701
|
+
style: {
|
|
1702
|
+
width: `${props.$cssWidth}px`,
|
|
1703
|
+
marginLeft: `${props.$controlWidth}px`,
|
|
1704
|
+
height: `${props.$timeScaleHeight}px`
|
|
1705
|
+
}
|
|
1706
|
+
}))`
|
|
1707
|
+
position: relative;
|
|
1708
|
+
overflow: visible; /* Allow time labels to render above the container */
|
|
1709
|
+
border-bottom: 1px solid ${(props) => props.theme.timeColor};
|
|
1710
|
+
box-sizing: border-box;
|
|
1711
|
+
`;
|
|
1712
|
+
var TimeTicks = import_styled_components19.default.canvas.attrs((props) => ({
|
|
1713
|
+
style: {
|
|
1714
|
+
width: `${props.$cssWidth}px`,
|
|
1715
|
+
height: `${props.$timeScaleHeight}px`
|
|
1716
|
+
}
|
|
1717
|
+
}))`
|
|
1718
|
+
position: absolute;
|
|
1719
|
+
left: 0;
|
|
1720
|
+
right: 0;
|
|
1721
|
+
bottom: 0;
|
|
1722
|
+
`;
|
|
1723
|
+
var TimeStamp = import_styled_components19.default.div.attrs((props) => ({
|
|
1724
|
+
style: {
|
|
1725
|
+
left: `${props.$left + 4}px`
|
|
1726
|
+
// Offset 4px to the right of the tick
|
|
1727
|
+
}
|
|
1728
|
+
}))`
|
|
1729
|
+
position: absolute;
|
|
1730
|
+
font-size: 0.75rem; /* Smaller font to prevent overflow */
|
|
1731
|
+
white-space: nowrap; /* Prevent text wrapping */
|
|
1732
|
+
color: ${(props) => props.theme.timeColor}; /* Use theme color instead of inheriting */
|
|
1733
|
+
`;
|
|
1734
|
+
var TimeScale = (props) => {
|
|
1735
|
+
const {
|
|
1736
|
+
theme: { timeColor },
|
|
1737
|
+
duration,
|
|
1738
|
+
marker,
|
|
1739
|
+
bigStep,
|
|
1740
|
+
secondStep,
|
|
1741
|
+
renderTimestamp
|
|
1742
|
+
} = props;
|
|
1743
|
+
const canvasInfo = /* @__PURE__ */ new Map();
|
|
1744
|
+
const timeMarkers = [];
|
|
1745
|
+
const canvasRef = (0, import_react11.useRef)(null);
|
|
1746
|
+
const {
|
|
1747
|
+
sampleRate,
|
|
1748
|
+
samplesPerPixel,
|
|
1749
|
+
timeScaleHeight,
|
|
1750
|
+
controls: { show: showControls, width: controlWidth }
|
|
1751
|
+
} = (0, import_react11.useContext)(PlaylistInfoContext);
|
|
1752
|
+
const devicePixelRatio = useDevicePixelRatio();
|
|
1753
|
+
(0, import_react11.useEffect)(() => {
|
|
1754
|
+
if (canvasRef.current !== null) {
|
|
1755
|
+
const canvas = canvasRef.current;
|
|
1756
|
+
const ctx = canvas.getContext("2d");
|
|
1757
|
+
if (ctx) {
|
|
1758
|
+
ctx.resetTransform();
|
|
1759
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1760
|
+
ctx.imageSmoothingEnabled = false;
|
|
1761
|
+
ctx.fillStyle = timeColor;
|
|
1762
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
1763
|
+
for (const [pixLeft, scaleHeight] of canvasInfo.entries()) {
|
|
1764
|
+
const scaleY = timeScaleHeight - scaleHeight;
|
|
1765
|
+
ctx.fillRect(pixLeft, scaleY, 1, scaleHeight);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
}, [
|
|
1770
|
+
duration,
|
|
1771
|
+
devicePixelRatio,
|
|
1772
|
+
timeColor,
|
|
1773
|
+
timeScaleHeight,
|
|
1774
|
+
bigStep,
|
|
1775
|
+
secondStep,
|
|
1776
|
+
marker,
|
|
1777
|
+
canvasInfo
|
|
1778
|
+
]);
|
|
1779
|
+
const widthX = secondsToPixels(duration / 1e3, samplesPerPixel, sampleRate);
|
|
1780
|
+
const pixPerSec = sampleRate / samplesPerPixel;
|
|
1781
|
+
let counter = 0;
|
|
1782
|
+
for (let i = 0; i < widthX; i += pixPerSec * secondStep / 1e3) {
|
|
1783
|
+
const pix = Math.floor(i);
|
|
1784
|
+
if (counter % marker === 0) {
|
|
1785
|
+
const timeMs = counter;
|
|
1786
|
+
const timestamp = formatTime2(timeMs);
|
|
1787
|
+
const timestampContent = renderTimestamp ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react11.default.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TimeStamp, { $left: pix, children: timestamp }, timestamp);
|
|
1788
|
+
timeMarkers.push(timestampContent);
|
|
1789
|
+
canvasInfo.set(pix, timeScaleHeight);
|
|
1790
|
+
} else if (counter % bigStep === 0) {
|
|
1791
|
+
canvasInfo.set(pix, Math.floor(timeScaleHeight / 2));
|
|
1792
|
+
} else if (counter % secondStep === 0) {
|
|
1793
|
+
canvasInfo.set(pix, Math.floor(timeScaleHeight / 5));
|
|
1794
|
+
}
|
|
1795
|
+
counter += secondStep;
|
|
1796
|
+
}
|
|
1797
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
1798
|
+
PlaylistTimeScaleScroll,
|
|
1799
|
+
{
|
|
1800
|
+
$cssWidth: widthX,
|
|
1801
|
+
$controlWidth: showControls ? controlWidth : 0,
|
|
1802
|
+
$timeScaleHeight: timeScaleHeight,
|
|
1803
|
+
children: [
|
|
1804
|
+
timeMarkers,
|
|
1805
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1806
|
+
TimeTicks,
|
|
1807
|
+
{
|
|
1808
|
+
$cssWidth: widthX,
|
|
1809
|
+
$timeScaleHeight: timeScaleHeight,
|
|
1810
|
+
width: widthX * devicePixelRatio,
|
|
1811
|
+
height: timeScaleHeight * devicePixelRatio,
|
|
1812
|
+
ref: canvasRef
|
|
1813
|
+
}
|
|
1814
|
+
)
|
|
1815
|
+
]
|
|
1816
|
+
}
|
|
1817
|
+
);
|
|
1818
|
+
};
|
|
1819
|
+
var StyledTimeScale = (0, import_styled_components19.withTheme)(TimeScale);
|
|
1820
|
+
|
|
1821
|
+
// src/components/SmartScale.tsx
|
|
1822
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1823
|
+
var timeinfo = /* @__PURE__ */ new Map([
|
|
1824
|
+
[
|
|
1825
|
+
700,
|
|
1826
|
+
{
|
|
1827
|
+
marker: 1e3,
|
|
1828
|
+
bigStep: 500,
|
|
1829
|
+
smallStep: 100
|
|
1830
|
+
}
|
|
1831
|
+
],
|
|
1832
|
+
[
|
|
1833
|
+
1500,
|
|
1834
|
+
{
|
|
1835
|
+
marker: 2e3,
|
|
1836
|
+
bigStep: 1e3,
|
|
1837
|
+
smallStep: 200
|
|
1838
|
+
}
|
|
1839
|
+
],
|
|
1840
|
+
[
|
|
1841
|
+
2500,
|
|
1842
|
+
{
|
|
1843
|
+
marker: 2e3,
|
|
1844
|
+
bigStep: 1e3,
|
|
1845
|
+
smallStep: 500
|
|
1846
|
+
}
|
|
1847
|
+
],
|
|
1848
|
+
[
|
|
1849
|
+
5e3,
|
|
1850
|
+
{
|
|
1851
|
+
marker: 5e3,
|
|
1852
|
+
bigStep: 1e3,
|
|
1853
|
+
smallStep: 500
|
|
1854
|
+
}
|
|
1855
|
+
],
|
|
1856
|
+
[
|
|
1857
|
+
1e4,
|
|
1858
|
+
{
|
|
1859
|
+
marker: 1e4,
|
|
1860
|
+
bigStep: 5e3,
|
|
1861
|
+
smallStep: 1e3
|
|
1862
|
+
}
|
|
1863
|
+
],
|
|
1864
|
+
[
|
|
1865
|
+
12e3,
|
|
1866
|
+
{
|
|
1867
|
+
marker: 15e3,
|
|
1868
|
+
bigStep: 5e3,
|
|
1869
|
+
smallStep: 1e3
|
|
1870
|
+
}
|
|
1871
|
+
],
|
|
1872
|
+
[
|
|
1873
|
+
Infinity,
|
|
1874
|
+
{
|
|
1875
|
+
marker: 3e4,
|
|
1876
|
+
bigStep: 1e4,
|
|
1877
|
+
smallStep: 5e3
|
|
1878
|
+
}
|
|
1879
|
+
]
|
|
1880
|
+
]);
|
|
1881
|
+
function getScaleInfo(samplesPerPixel) {
|
|
1882
|
+
const keys = timeinfo.keys();
|
|
1883
|
+
let config;
|
|
1884
|
+
for (const resolution of keys) {
|
|
1885
|
+
if (samplesPerPixel < resolution) {
|
|
1886
|
+
config = timeinfo.get(resolution);
|
|
1887
|
+
break;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
if (config === void 0) {
|
|
1891
|
+
config = { marker: 3e4, bigStep: 1e4, smallStep: 5e3 };
|
|
1892
|
+
}
|
|
1893
|
+
return config;
|
|
1894
|
+
}
|
|
1895
|
+
var SmartScale = () => {
|
|
1896
|
+
const { samplesPerPixel, duration } = (0, import_react12.useContext)(PlaylistInfoContext);
|
|
1897
|
+
let config = getScaleInfo(samplesPerPixel);
|
|
1898
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1899
|
+
StyledTimeScale,
|
|
1900
|
+
{
|
|
1901
|
+
marker: config.marker,
|
|
1902
|
+
bigStep: config.bigStep,
|
|
1903
|
+
secondStep: config.smallStep,
|
|
1904
|
+
duration
|
|
1905
|
+
}
|
|
1906
|
+
);
|
|
1907
|
+
};
|
|
1908
|
+
|
|
1909
|
+
// src/components/TimeFormatSelect.tsx
|
|
1910
|
+
var import_styled_components20 = __toESM(require("styled-components"));
|
|
1911
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1912
|
+
var SelectWrapper = import_styled_components20.default.div`
|
|
1913
|
+
display: inline-flex;
|
|
1914
|
+
align-items: center;
|
|
1915
|
+
gap: 0.5rem;
|
|
1916
|
+
`;
|
|
1917
|
+
var TIME_FORMAT_OPTIONS = [
|
|
1918
|
+
{ value: "seconds", label: "seconds" },
|
|
1919
|
+
{ value: "thousandths", label: "thousandths" },
|
|
1920
|
+
{ value: "hh:mm:ss", label: "hh:mm:ss" },
|
|
1921
|
+
{ value: "hh:mm:ss.u", label: "hh:mm:ss + tenths" },
|
|
1922
|
+
{ value: "hh:mm:ss.uu", label: "hh:mm:ss + hundredths" },
|
|
1923
|
+
{ value: "hh:mm:ss.uuu", label: "hh:mm:ss + milliseconds" }
|
|
1924
|
+
];
|
|
1925
|
+
var TimeFormatSelect = ({
|
|
1926
|
+
value,
|
|
1927
|
+
onChange,
|
|
1928
|
+
disabled = false,
|
|
1929
|
+
className
|
|
1930
|
+
}) => {
|
|
1931
|
+
const handleChange = (e) => {
|
|
1932
|
+
onChange(e.target.value);
|
|
1933
|
+
};
|
|
1934
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SelectWrapper, { className, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
1935
|
+
BaseSelect,
|
|
1936
|
+
{
|
|
1937
|
+
className: "time-format",
|
|
1938
|
+
value,
|
|
1939
|
+
onChange: handleChange,
|
|
1940
|
+
disabled,
|
|
1941
|
+
"aria-label": "Time format selection",
|
|
1942
|
+
children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: option.value, children: option.label }, option.value))
|
|
1943
|
+
}
|
|
1944
|
+
) });
|
|
1945
|
+
};
|
|
1946
|
+
|
|
1947
|
+
// src/components/Track.tsx
|
|
1948
|
+
var import_styled_components21 = __toESM(require("styled-components"));
|
|
1949
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1950
|
+
var Container = import_styled_components21.default.div.attrs((props) => ({
|
|
1951
|
+
style: {
|
|
1952
|
+
height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`
|
|
1953
|
+
}
|
|
1954
|
+
}))`
|
|
1955
|
+
position: relative;
|
|
1956
|
+
display: flex;
|
|
1957
|
+
${(props) => props.$width !== void 0 && `width: ${props.$width}px;`}
|
|
1958
|
+
`;
|
|
1959
|
+
var ChannelContainer = import_styled_components21.default.div.attrs((props) => ({
|
|
1960
|
+
style: {
|
|
1961
|
+
paddingLeft: `${props.$offset || 0}px`
|
|
1962
|
+
}
|
|
1963
|
+
}))`
|
|
1964
|
+
position: relative;
|
|
1965
|
+
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
1966
|
+
flex: 1;
|
|
1967
|
+
`;
|
|
1968
|
+
var ControlsWrapper = import_styled_components21.default.div.attrs((props) => ({
|
|
1969
|
+
style: {
|
|
1970
|
+
width: `${props.$controlWidth}px`
|
|
1971
|
+
}
|
|
1972
|
+
}))`
|
|
1973
|
+
position: sticky;
|
|
1974
|
+
z-index: 101; /* Above waveform content, below Docusaurus navbar (z-index: 200) */
|
|
1975
|
+
left: 0;
|
|
1976
|
+
height: 100%;
|
|
1977
|
+
flex-shrink: 0;
|
|
1978
|
+
pointer-events: auto;
|
|
1979
|
+
background: ${(props) => props.theme.surfaceColor};
|
|
1980
|
+
transition: background 0.15s ease-in-out;
|
|
1981
|
+
|
|
1982
|
+
/* Selected track: highlighted background */
|
|
1983
|
+
${(props) => props.$isSelected && `
|
|
1984
|
+
background: ${props.theme.selectedTrackControlsBackground};
|
|
1985
|
+
`}
|
|
1986
|
+
`;
|
|
1987
|
+
var Track = ({
|
|
1988
|
+
numChannels,
|
|
1989
|
+
children,
|
|
1990
|
+
className,
|
|
1991
|
+
backgroundColor,
|
|
1992
|
+
offset = 0,
|
|
1993
|
+
width,
|
|
1994
|
+
hasClipHeaders = false,
|
|
1995
|
+
onClick,
|
|
1996
|
+
trackId,
|
|
1997
|
+
isSelected = false
|
|
1998
|
+
}) => {
|
|
1999
|
+
const {
|
|
2000
|
+
waveHeight,
|
|
2001
|
+
controls: { show, width: controlWidth }
|
|
2002
|
+
} = usePlaylistInfo();
|
|
2003
|
+
const controls = useTrackControls();
|
|
2004
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
2005
|
+
Container,
|
|
2006
|
+
{
|
|
2007
|
+
$numChannels: numChannels,
|
|
2008
|
+
className,
|
|
2009
|
+
$waveHeight: waveHeight,
|
|
2010
|
+
$controlWidth: show ? controlWidth : 0,
|
|
2011
|
+
$width: width,
|
|
2012
|
+
$hasClipHeaders: hasClipHeaders,
|
|
2013
|
+
$isSelected: isSelected,
|
|
2014
|
+
children: [
|
|
2015
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2016
|
+
ControlsWrapper,
|
|
2017
|
+
{
|
|
2018
|
+
$controlWidth: show ? controlWidth : 0,
|
|
2019
|
+
$isSelected: isSelected,
|
|
2020
|
+
children: controls
|
|
2021
|
+
}
|
|
2022
|
+
),
|
|
2023
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2024
|
+
ChannelContainer,
|
|
2025
|
+
{
|
|
2026
|
+
$controlWidth: show ? controlWidth : 0,
|
|
2027
|
+
$backgroundColor: backgroundColor,
|
|
2028
|
+
$offset: offset,
|
|
2029
|
+
onClick,
|
|
2030
|
+
"data-track-id": trackId,
|
|
2031
|
+
children
|
|
2032
|
+
}
|
|
2033
|
+
)
|
|
2034
|
+
]
|
|
2035
|
+
}
|
|
2036
|
+
);
|
|
2037
|
+
};
|
|
2038
|
+
|
|
2039
|
+
// src/components/TrackControls/Button.tsx
|
|
2040
|
+
var import_styled_components22 = __toESM(require("styled-components"));
|
|
2041
|
+
var Button = import_styled_components22.default.button.attrs({
|
|
2042
|
+
type: "button"
|
|
2043
|
+
})`
|
|
2044
|
+
display: inline-block;
|
|
2045
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
2046
|
+
font-weight: 500;
|
|
2047
|
+
text-align: center;
|
|
2048
|
+
vertical-align: middle;
|
|
2049
|
+
user-select: none;
|
|
2050
|
+
padding: 0.25rem 0.4rem;
|
|
2051
|
+
font-size: ${(props) => props.theme.fontSizeSmall};
|
|
2052
|
+
line-height: 1;
|
|
2053
|
+
border-radius: ${(props) => props.theme.borderRadius};
|
|
2054
|
+
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
|
|
2055
|
+
border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
|
2056
|
+
cursor: pointer;
|
|
2057
|
+
|
|
2058
|
+
${(props) => {
|
|
2059
|
+
if (props.$variant === "danger") {
|
|
2060
|
+
return `
|
|
2061
|
+
color: #fff;
|
|
2062
|
+
background-color: #dc3545;
|
|
2063
|
+
border: 1px solid #dc3545;
|
|
2064
|
+
|
|
2065
|
+
&:hover {
|
|
2066
|
+
background-color: #c82333;
|
|
2067
|
+
border-color: #bd2130;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
&:focus {
|
|
2071
|
+
outline: none;
|
|
2072
|
+
box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);
|
|
2073
|
+
}
|
|
2074
|
+
`;
|
|
2075
|
+
} else if (props.$variant === "info") {
|
|
2076
|
+
return `
|
|
2077
|
+
color: #fff;
|
|
2078
|
+
background-color: #17a2b8;
|
|
2079
|
+
border: 1px solid #17a2b8;
|
|
2080
|
+
|
|
2081
|
+
&:hover {
|
|
2082
|
+
background-color: #138496;
|
|
2083
|
+
border-color: #117a8b;
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
&:focus {
|
|
2087
|
+
outline: none;
|
|
2088
|
+
box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);
|
|
2089
|
+
}
|
|
2090
|
+
`;
|
|
2091
|
+
} else {
|
|
2092
|
+
return `
|
|
2093
|
+
color: ${props.theme.textColor};
|
|
2094
|
+
background-color: transparent;
|
|
2095
|
+
border: 1px solid ${props.theme.borderColor};
|
|
2096
|
+
|
|
2097
|
+
&:hover {
|
|
2098
|
+
color: #fff;
|
|
2099
|
+
background-color: ${props.theme.textColor};
|
|
2100
|
+
border-color: ${props.theme.textColor};
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
&:focus {
|
|
2104
|
+
outline: none;
|
|
2105
|
+
box-shadow: 0 0 0 0.2rem ${props.theme.inputFocusBorder}33;
|
|
2106
|
+
}
|
|
2107
|
+
`;
|
|
2108
|
+
}
|
|
2109
|
+
}}
|
|
2110
|
+
`;
|
|
2111
|
+
|
|
2112
|
+
// src/components/TrackControls/ButtonGroup.tsx
|
|
2113
|
+
var import_styled_components23 = __toESM(require("styled-components"));
|
|
2114
|
+
var ButtonGroup = import_styled_components23.default.div`
|
|
2115
|
+
margin-bottom: 0.3rem;
|
|
2116
|
+
|
|
2117
|
+
button:not(:first-child) {
|
|
2118
|
+
border-top-left-radius: 0;
|
|
2119
|
+
border-bottom-left-radius: 0;
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
button:not(:last-child) {
|
|
2123
|
+
border-top-right-radius: 0;
|
|
2124
|
+
border-bottom-right-radius: 0;
|
|
2125
|
+
}
|
|
2126
|
+
`;
|
|
2127
|
+
|
|
2128
|
+
// src/components/TrackControls/Controls.tsx
|
|
2129
|
+
var import_styled_components24 = __toESM(require("styled-components"));
|
|
2130
|
+
var Controls = import_styled_components24.default.div`
|
|
2131
|
+
background: transparent;
|
|
2132
|
+
width: 100%;
|
|
2133
|
+
height: 100%;
|
|
2134
|
+
display: flex;
|
|
2135
|
+
flex-direction: column;
|
|
2136
|
+
align-items: center;
|
|
2137
|
+
justify-content: flex-start;
|
|
2138
|
+
overflow: hidden;
|
|
2139
|
+
box-sizing: border-box;
|
|
2140
|
+
text-align: center;
|
|
2141
|
+
border: 1px solid ${(props) => props.theme.borderColor};
|
|
2142
|
+
border-radius: ${(props) => props.theme.borderRadius};
|
|
2143
|
+
`;
|
|
2144
|
+
|
|
2145
|
+
// src/components/TrackControls/Header.tsx
|
|
2146
|
+
var import_styled_components25 = __toESM(require("styled-components"));
|
|
2147
|
+
var Header = import_styled_components25.default.header`
|
|
2148
|
+
overflow: hidden;
|
|
2149
|
+
height: 26px;
|
|
2150
|
+
width: 100%;
|
|
2151
|
+
display: flex;
|
|
2152
|
+
align-items: center;
|
|
2153
|
+
justify-content: space-between;
|
|
2154
|
+
padding: 0 0.2rem;
|
|
2155
|
+
font-size: ${(props) => props.theme.fontSizeSmall};
|
|
2156
|
+
color: ${(props) => props.theme.textColor};
|
|
2157
|
+
background-color: transparent;
|
|
2158
|
+
`;
|
|
2159
|
+
|
|
2160
|
+
// src/components/TrackControls/VolumeDownIcon.tsx
|
|
2161
|
+
var import_react13 = require("@phosphor-icons/react");
|
|
2162
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
2163
|
+
var VolumeDownIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react13.SpeakerLowIcon, { weight: "light", ...props });
|
|
2164
|
+
|
|
2165
|
+
// src/components/TrackControls/VolumeUpIcon.tsx
|
|
2166
|
+
var import_react14 = require("@phosphor-icons/react");
|
|
2167
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
2168
|
+
var VolumeUpIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react14.SpeakerHighIcon, { weight: "light", ...props });
|
|
2169
|
+
|
|
2170
|
+
// src/components/TrackControls/TrashIcon.tsx
|
|
2171
|
+
var import_react15 = require("@phosphor-icons/react");
|
|
2172
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2173
|
+
var TrashIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react15.TrashIcon, { weight: "light", ...props });
|
|
2174
|
+
|
|
2175
|
+
// src/components/TrackControls/Slider.tsx
|
|
2176
|
+
var import_styled_components26 = __toESM(require("styled-components"));
|
|
2177
|
+
var Slider = (0, import_styled_components26.default)(BaseSlider)`
|
|
2178
|
+
width: 75%;
|
|
2179
|
+
height: 5px;
|
|
2180
|
+
background: ${(props) => props.theme.sliderTrackColor};
|
|
2181
|
+
|
|
2182
|
+
&::-webkit-slider-thumb {
|
|
2183
|
+
width: 12px;
|
|
2184
|
+
height: 12px;
|
|
2185
|
+
background: ${(props) => props.theme.sliderThumbColor};
|
|
2186
|
+
border: none;
|
|
2187
|
+
margin-top: -4px;
|
|
2188
|
+
cursor: ew-resize;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
&::-moz-range-thumb {
|
|
2192
|
+
width: 12px;
|
|
2193
|
+
height: 12px;
|
|
2194
|
+
background: ${(props) => props.theme.sliderThumbColor};
|
|
2195
|
+
border: none;
|
|
2196
|
+
cursor: ew-resize;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
&::-webkit-slider-runnable-track {
|
|
2200
|
+
height: 5px;
|
|
2201
|
+
background: ${(props) => props.theme.sliderTrackColor};
|
|
2202
|
+
border-radius: 3px;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
&::-moz-range-track {
|
|
2206
|
+
height: 5px;
|
|
2207
|
+
background: ${(props) => props.theme.sliderTrackColor};
|
|
2208
|
+
border-radius: 3px;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
&:focus::-webkit-slider-runnable-track {
|
|
2212
|
+
background: ${(props) => props.theme.inputBorder};
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
&:focus::-moz-range-track {
|
|
2216
|
+
background: ${(props) => props.theme.inputBorder};
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
&:focus::-webkit-slider-thumb {
|
|
2220
|
+
border: 2px solid ${(props) => props.theme.textColor};
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
&:focus::-moz-range-thumb {
|
|
2224
|
+
border: 2px solid ${(props) => props.theme.textColor};
|
|
2225
|
+
}
|
|
2226
|
+
`;
|
|
2227
|
+
|
|
2228
|
+
// src/components/TrackControls/SliderWrapper.tsx
|
|
2229
|
+
var import_styled_components27 = __toESM(require("styled-components"));
|
|
2230
|
+
var SliderWrapper = import_styled_components27.default.label`
|
|
2231
|
+
width: 100%;
|
|
2232
|
+
display: flex;
|
|
2233
|
+
justify-content: space-between;
|
|
2234
|
+
align-items: center;
|
|
2235
|
+
padding: 0 1rem;
|
|
2236
|
+
margin-bottom: 0.2rem;
|
|
2237
|
+
font-size: 14px;
|
|
2238
|
+
`;
|
|
2239
|
+
|
|
2240
|
+
// src/components/TrackControlsWithDelete.tsx
|
|
2241
|
+
var import_styled_components28 = __toESM(require("styled-components"));
|
|
2242
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2243
|
+
var HeaderContainer2 = import_styled_components28.default.div`
|
|
2244
|
+
display: flex;
|
|
2245
|
+
align-items: center;
|
|
2246
|
+
gap: 0.25rem;
|
|
2247
|
+
padding: 0.5rem 0.5rem 0.25rem 0.5rem;
|
|
2248
|
+
`;
|
|
2249
|
+
var TrackNameSpan = import_styled_components28.default.span`
|
|
2250
|
+
flex: 1;
|
|
2251
|
+
font-weight: 600;
|
|
2252
|
+
font-size: 0.875rem;
|
|
2253
|
+
overflow: hidden;
|
|
2254
|
+
text-overflow: ellipsis;
|
|
2255
|
+
white-space: nowrap;
|
|
2256
|
+
margin: 0 0.25rem;
|
|
2257
|
+
`;
|
|
2258
|
+
var DeleteIconButton = import_styled_components28.default.button`
|
|
2259
|
+
display: flex;
|
|
2260
|
+
align-items: center;
|
|
2261
|
+
justify-content: center;
|
|
2262
|
+
width: 20px;
|
|
2263
|
+
height: 20px;
|
|
2264
|
+
padding: 0;
|
|
2265
|
+
border: none;
|
|
2266
|
+
background: transparent;
|
|
2267
|
+
color: #999;
|
|
2268
|
+
cursor: pointer;
|
|
2269
|
+
font-size: 16px;
|
|
2270
|
+
line-height: 1;
|
|
2271
|
+
border-radius: 3px;
|
|
2272
|
+
transition: all 0.2s ease-in-out;
|
|
2273
|
+
flex-shrink: 0;
|
|
2274
|
+
|
|
2275
|
+
&:hover {
|
|
2276
|
+
background: #dc3545;
|
|
2277
|
+
color: white;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
&:active {
|
|
2281
|
+
transform: scale(0.9);
|
|
2282
|
+
}
|
|
2283
|
+
`;
|
|
2284
|
+
var TrackControlsWithDelete = ({
|
|
2285
|
+
trackName,
|
|
2286
|
+
muted,
|
|
2287
|
+
soloed,
|
|
2288
|
+
volume,
|
|
2289
|
+
pan,
|
|
2290
|
+
onMuteChange,
|
|
2291
|
+
onSoloChange,
|
|
2292
|
+
onVolumeChange,
|
|
2293
|
+
onPanChange,
|
|
2294
|
+
onDelete
|
|
2295
|
+
}) => {
|
|
2296
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Controls, { children: [
|
|
2297
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(HeaderContainer2, { children: [
|
|
2298
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DeleteIconButton, { onClick: onDelete, title: "Delete track", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TrashIcon, {}) }),
|
|
2299
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TrackNameSpan, { children: trackName })
|
|
2300
|
+
] }),
|
|
2301
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(ButtonGroup, { children: [
|
|
2302
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2303
|
+
Button,
|
|
2304
|
+
{
|
|
2305
|
+
$variant: muted ? "danger" : "outline",
|
|
2306
|
+
onClick: () => onMuteChange(!muted),
|
|
2307
|
+
children: "Mute"
|
|
2308
|
+
}
|
|
2309
|
+
),
|
|
2310
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2311
|
+
Button,
|
|
2312
|
+
{
|
|
2313
|
+
$variant: soloed ? "info" : "outline",
|
|
2314
|
+
onClick: () => onSoloChange(!soloed),
|
|
2315
|
+
children: "Solo"
|
|
2316
|
+
}
|
|
2317
|
+
)
|
|
2318
|
+
] }),
|
|
2319
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(SliderWrapper, { children: [
|
|
2320
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(VolumeDownIcon, {}),
|
|
2321
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2322
|
+
Slider,
|
|
2323
|
+
{
|
|
2324
|
+
min: "0",
|
|
2325
|
+
max: "1",
|
|
2326
|
+
step: "0.01",
|
|
2327
|
+
value: volume,
|
|
2328
|
+
onChange: (e) => onVolumeChange(parseFloat(e.target.value))
|
|
2329
|
+
}
|
|
2330
|
+
),
|
|
2331
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(VolumeUpIcon, {})
|
|
2332
|
+
] }),
|
|
2333
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(SliderWrapper, { children: [
|
|
2334
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "L" }),
|
|
2335
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2336
|
+
Slider,
|
|
2337
|
+
{
|
|
2338
|
+
min: "-1",
|
|
2339
|
+
max: "1",
|
|
2340
|
+
step: "0.01",
|
|
2341
|
+
value: pan,
|
|
2342
|
+
onChange: (e) => onPanChange(parseFloat(e.target.value))
|
|
2343
|
+
}
|
|
2344
|
+
),
|
|
2345
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "R" })
|
|
2346
|
+
] })
|
|
2347
|
+
] });
|
|
2348
|
+
};
|
|
2349
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2350
|
+
0 && (module.exports = {
|
|
2351
|
+
AudioPosition,
|
|
2352
|
+
AutomaticScrollCheckbox,
|
|
2353
|
+
BaseButton,
|
|
2354
|
+
BaseCheckbox,
|
|
2355
|
+
BaseCheckboxLabel,
|
|
2356
|
+
BaseCheckboxWrapper,
|
|
2357
|
+
BaseControlButton,
|
|
2358
|
+
BaseInput,
|
|
2359
|
+
BaseLabel,
|
|
2360
|
+
BaseSelect,
|
|
2361
|
+
BaseSlider,
|
|
2362
|
+
Button,
|
|
2363
|
+
ButtonGroup,
|
|
2364
|
+
CLIP_BOUNDARY_WIDTH,
|
|
2365
|
+
CLIP_HEADER_HEIGHT,
|
|
2366
|
+
Channel,
|
|
2367
|
+
Clip,
|
|
2368
|
+
ClipBoundary,
|
|
2369
|
+
ClipHeader,
|
|
2370
|
+
ClipHeaderPresentational,
|
|
2371
|
+
Controls,
|
|
2372
|
+
DevicePixelRatioProvider,
|
|
2373
|
+
FadeOverlay,
|
|
2374
|
+
Header,
|
|
2375
|
+
InlineLabel,
|
|
2376
|
+
MasterVolumeControl,
|
|
2377
|
+
Playhead,
|
|
2378
|
+
PlayheadWithMarker,
|
|
2379
|
+
Playlist,
|
|
2380
|
+
PlaylistInfoContext,
|
|
2381
|
+
PlayoutProvider,
|
|
2382
|
+
ScreenReaderOnly,
|
|
2383
|
+
Selection,
|
|
2384
|
+
SelectionTimeInputs,
|
|
2385
|
+
Slider,
|
|
2386
|
+
SliderWrapper,
|
|
2387
|
+
SmartChannel,
|
|
2388
|
+
SmartScale,
|
|
2389
|
+
StyledPlaylist,
|
|
2390
|
+
StyledTimeScale,
|
|
2391
|
+
TimeFormatSelect,
|
|
2392
|
+
TimeInput,
|
|
2393
|
+
TimeScale,
|
|
2394
|
+
Track,
|
|
2395
|
+
TrackControlsContext,
|
|
2396
|
+
TrackControlsWithDelete,
|
|
2397
|
+
TrashIcon,
|
|
2398
|
+
VolumeDownIcon,
|
|
2399
|
+
VolumeUpIcon,
|
|
2400
|
+
darkTheme,
|
|
2401
|
+
defaultTheme,
|
|
2402
|
+
formatTime,
|
|
2403
|
+
isWaveformGradient,
|
|
2404
|
+
parseTime,
|
|
2405
|
+
pixelsToSamples,
|
|
2406
|
+
pixelsToSeconds,
|
|
2407
|
+
samplesToPixels,
|
|
2408
|
+
samplesToSeconds,
|
|
2409
|
+
secondsToPixels,
|
|
2410
|
+
secondsToSamples,
|
|
2411
|
+
useDevicePixelRatio,
|
|
2412
|
+
usePlaylistInfo,
|
|
2413
|
+
usePlayoutStatus,
|
|
2414
|
+
usePlayoutStatusUpdate,
|
|
2415
|
+
useTheme,
|
|
2416
|
+
useTrackControls,
|
|
2417
|
+
waveformColorToCss
|
|
2418
|
+
});
|
|
2419
|
+
//# sourceMappingURL=index.js.map
|