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