@waveform-playlist/ui-components 5.2.0 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +98 -43
- package/dist/index.d.ts +98 -43
- package/dist/index.js +566 -155
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +554 -147
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -51,8 +51,10 @@ __export(index_exports, {
|
|
|
51
51
|
ClipBoundary: () => ClipBoundary,
|
|
52
52
|
ClipHeader: () => ClipHeader,
|
|
53
53
|
ClipHeaderPresentational: () => ClipHeaderPresentational,
|
|
54
|
+
CloseButton: () => CloseButton,
|
|
54
55
|
Controls: () => Controls,
|
|
55
56
|
DevicePixelRatioProvider: () => DevicePixelRatioProvider,
|
|
57
|
+
DotsIcon: () => DotsIcon,
|
|
56
58
|
FadeOverlay: () => FadeOverlay,
|
|
57
59
|
Header: () => Header,
|
|
58
60
|
InlineLabel: () => InlineLabel,
|
|
@@ -71,6 +73,8 @@ __export(index_exports, {
|
|
|
71
73
|
SliderWrapper: () => SliderWrapper,
|
|
72
74
|
SmartChannel: () => SmartChannel,
|
|
73
75
|
SmartScale: () => SmartScale,
|
|
76
|
+
SpectrogramChannel: () => SpectrogramChannel,
|
|
77
|
+
SpectrogramLabels: () => SpectrogramLabels,
|
|
74
78
|
StyledPlaylist: () => StyledPlaylist,
|
|
75
79
|
StyledTimeScale: () => StyledTimeScale,
|
|
76
80
|
TimeFormatSelect: () => TimeFormatSelect,
|
|
@@ -79,7 +83,7 @@ __export(index_exports, {
|
|
|
79
83
|
TimescaleLoopRegion: () => TimescaleLoopRegion,
|
|
80
84
|
Track: () => Track,
|
|
81
85
|
TrackControlsContext: () => TrackControlsContext,
|
|
82
|
-
|
|
86
|
+
TrackMenu: () => TrackMenu,
|
|
83
87
|
TrashIcon: () => TrashIcon,
|
|
84
88
|
VolumeDownIcon: () => VolumeDownIcon,
|
|
85
89
|
VolumeUpIcon: () => VolumeUpIcon,
|
|
@@ -1991,16 +1995,288 @@ var PlayoutProvider = ({ children }) => {
|
|
|
1991
1995
|
var usePlayoutStatus = () => (0, import_react11.useContext)(PlayoutStatusContext);
|
|
1992
1996
|
var usePlayoutStatusUpdate = () => (0, import_react11.useContext)(PlayoutStatusUpdateContext);
|
|
1993
1997
|
|
|
1994
|
-
// src/components/
|
|
1998
|
+
// src/components/SpectrogramChannel.tsx
|
|
1999
|
+
var import_react12 = require("react");
|
|
2000
|
+
var import_styled_components20 = __toESM(require("styled-components"));
|
|
1995
2001
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1996
|
-
var
|
|
2002
|
+
var MAX_CANVAS_WIDTH2 = 1e3;
|
|
2003
|
+
var Wrapper3 = import_styled_components20.default.div.attrs((props) => ({
|
|
2004
|
+
style: {
|
|
2005
|
+
top: `${props.$waveHeight * props.$index}px`,
|
|
2006
|
+
width: `${props.$cssWidth}px`,
|
|
2007
|
+
height: `${props.$waveHeight}px`
|
|
2008
|
+
}
|
|
2009
|
+
}))`
|
|
2010
|
+
position: absolute;
|
|
2011
|
+
background: #000;
|
|
2012
|
+
transform: translateZ(0);
|
|
2013
|
+
backface-visibility: hidden;
|
|
2014
|
+
`;
|
|
2015
|
+
var SpectrogramCanvas = import_styled_components20.default.canvas.attrs((props) => ({
|
|
2016
|
+
style: {
|
|
2017
|
+
width: `${props.$cssWidth}px`,
|
|
2018
|
+
height: `${props.$waveHeight}px`
|
|
2019
|
+
}
|
|
2020
|
+
}))`
|
|
2021
|
+
float: left;
|
|
2022
|
+
position: relative;
|
|
2023
|
+
will-change: transform;
|
|
2024
|
+
image-rendering: pixelated;
|
|
2025
|
+
image-rendering: crisp-edges;
|
|
2026
|
+
`;
|
|
2027
|
+
function defaultGetColorMap() {
|
|
2028
|
+
const lut = new Uint8Array(256 * 3);
|
|
2029
|
+
for (let i = 0; i < 256; i++) {
|
|
2030
|
+
lut[i * 3] = lut[i * 3 + 1] = lut[i * 3 + 2] = i;
|
|
2031
|
+
}
|
|
2032
|
+
return lut;
|
|
2033
|
+
}
|
|
2034
|
+
var SpectrogramChannel = ({
|
|
2035
|
+
index,
|
|
2036
|
+
data,
|
|
2037
|
+
length,
|
|
2038
|
+
waveHeight,
|
|
2039
|
+
devicePixelRatio = 1,
|
|
2040
|
+
samplesPerPixel,
|
|
2041
|
+
colorLUT,
|
|
2042
|
+
frequencyScaleFn,
|
|
2043
|
+
minFrequency = 0,
|
|
2044
|
+
maxFrequency,
|
|
2045
|
+
workerApi,
|
|
2046
|
+
clipId,
|
|
2047
|
+
onCanvasesReady
|
|
2048
|
+
}) => {
|
|
2049
|
+
const canvasesRef = (0, import_react12.useRef)([]);
|
|
2050
|
+
const registeredIdsRef = (0, import_react12.useRef)([]);
|
|
2051
|
+
const transferredCanvasesRef = (0, import_react12.useRef)(/* @__PURE__ */ new WeakSet());
|
|
2052
|
+
const isWorkerMode = !!(workerApi && clipId);
|
|
2053
|
+
const canvasRef = (0, import_react12.useCallback)(
|
|
2054
|
+
(canvas) => {
|
|
2055
|
+
if (canvas !== null) {
|
|
2056
|
+
const idx = parseInt(canvas.dataset.index, 10);
|
|
2057
|
+
canvasesRef.current[idx] = canvas;
|
|
2058
|
+
}
|
|
2059
|
+
},
|
|
2060
|
+
[]
|
|
2061
|
+
);
|
|
2062
|
+
(0, import_react12.useEffect)(() => {
|
|
2063
|
+
if (!isWorkerMode) return;
|
|
2064
|
+
const canvasCount2 = Math.ceil(length / MAX_CANVAS_WIDTH2);
|
|
2065
|
+
canvasesRef.current.length = canvasCount2;
|
|
2066
|
+
const canvases2 = canvasesRef.current;
|
|
2067
|
+
const ids = [];
|
|
2068
|
+
const widths = [];
|
|
2069
|
+
for (let i = 0; i < canvases2.length; i++) {
|
|
2070
|
+
const canvas = canvases2[i];
|
|
2071
|
+
if (!canvas) continue;
|
|
2072
|
+
if (transferredCanvasesRef.current.has(canvas)) continue;
|
|
2073
|
+
const canvasId = `${clipId}-ch${index}-chunk${i}`;
|
|
2074
|
+
try {
|
|
2075
|
+
const offscreen = canvas.transferControlToOffscreen();
|
|
2076
|
+
workerApi.registerCanvas(canvasId, offscreen);
|
|
2077
|
+
transferredCanvasesRef.current.add(canvas);
|
|
2078
|
+
ids.push(canvasId);
|
|
2079
|
+
widths.push(Math.min(length - i * MAX_CANVAS_WIDTH2, MAX_CANVAS_WIDTH2));
|
|
2080
|
+
} catch (err) {
|
|
2081
|
+
console.warn(`[spectrogram] transferControlToOffscreen failed for ${canvasId}:`, err);
|
|
2082
|
+
continue;
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
registeredIdsRef.current = ids;
|
|
2086
|
+
if (ids.length > 0 && onCanvasesReady) {
|
|
2087
|
+
onCanvasesReady(ids, widths);
|
|
2088
|
+
}
|
|
2089
|
+
return () => {
|
|
2090
|
+
for (const id of registeredIdsRef.current) {
|
|
2091
|
+
workerApi.unregisterCanvas(id);
|
|
2092
|
+
}
|
|
2093
|
+
registeredIdsRef.current = [];
|
|
2094
|
+
};
|
|
2095
|
+
}, [isWorkerMode, clipId, index, length]);
|
|
2096
|
+
const lut = colorLUT ?? defaultGetColorMap();
|
|
2097
|
+
const maxF = maxFrequency ?? (data ? data.sampleRate / 2 : 22050);
|
|
2098
|
+
const scaleFn = frequencyScaleFn ?? ((f, minF, maxF2) => (f - minF) / (maxF2 - minF));
|
|
2099
|
+
(0, import_react12.useLayoutEffect)(() => {
|
|
2100
|
+
if (isWorkerMode || !data) return;
|
|
2101
|
+
const canvases2 = canvasesRef.current;
|
|
2102
|
+
const { frequencyBinCount, frameCount, hopSize, sampleRate, gainDb, rangeDb: rawRangeDb } = data;
|
|
2103
|
+
const rangeDb = rawRangeDb === 0 ? 1 : rawRangeDb;
|
|
2104
|
+
let globalPixelOffset = 0;
|
|
2105
|
+
const binToFreq = (bin) => bin / frequencyBinCount * (sampleRate / 2);
|
|
2106
|
+
for (let canvasIdx = 0; canvasIdx < canvases2.length; canvasIdx++) {
|
|
2107
|
+
const canvas = canvases2[canvasIdx];
|
|
2108
|
+
if (!canvas) continue;
|
|
2109
|
+
const ctx = canvas.getContext("2d");
|
|
2110
|
+
if (!ctx) continue;
|
|
2111
|
+
const canvasWidth = canvas.width / devicePixelRatio;
|
|
2112
|
+
const canvasHeight = waveHeight;
|
|
2113
|
+
ctx.resetTransform();
|
|
2114
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2115
|
+
ctx.imageSmoothingEnabled = false;
|
|
2116
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
2117
|
+
const imgData = ctx.createImageData(canvasWidth, canvasHeight);
|
|
2118
|
+
const pixels = imgData.data;
|
|
2119
|
+
for (let x = 0; x < canvasWidth; x++) {
|
|
2120
|
+
const globalX = globalPixelOffset + x;
|
|
2121
|
+
const samplePos = globalX * samplesPerPixel;
|
|
2122
|
+
const frame = Math.floor(samplePos / hopSize);
|
|
2123
|
+
if (frame < 0 || frame >= frameCount) continue;
|
|
2124
|
+
const frameOffset = frame * frequencyBinCount;
|
|
2125
|
+
for (let y = 0; y < canvasHeight; y++) {
|
|
2126
|
+
const normalizedY = 1 - y / canvasHeight;
|
|
2127
|
+
let bin = Math.floor(normalizedY * frequencyBinCount);
|
|
2128
|
+
if (frequencyScaleFn) {
|
|
2129
|
+
let lo = 0;
|
|
2130
|
+
let hi = frequencyBinCount - 1;
|
|
2131
|
+
while (lo < hi) {
|
|
2132
|
+
const mid = lo + hi >> 1;
|
|
2133
|
+
const freq = binToFreq(mid);
|
|
2134
|
+
const scaled = scaleFn(freq, minFrequency, maxF);
|
|
2135
|
+
if (scaled < normalizedY) {
|
|
2136
|
+
lo = mid + 1;
|
|
2137
|
+
} else {
|
|
2138
|
+
hi = mid;
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
bin = lo;
|
|
2142
|
+
}
|
|
2143
|
+
if (bin < 0 || bin >= frequencyBinCount) continue;
|
|
2144
|
+
const db = data.data[frameOffset + bin];
|
|
2145
|
+
const normalized = Math.max(0, Math.min(1, (db + rangeDb + gainDb) / rangeDb));
|
|
2146
|
+
const colorIdx = Math.floor(normalized * 255);
|
|
2147
|
+
const pixelIdx = (y * canvasWidth + x) * 4;
|
|
2148
|
+
pixels[pixelIdx] = lut[colorIdx * 3];
|
|
2149
|
+
pixels[pixelIdx + 1] = lut[colorIdx * 3 + 1];
|
|
2150
|
+
pixels[pixelIdx + 2] = lut[colorIdx * 3 + 2];
|
|
2151
|
+
pixels[pixelIdx + 3] = 255;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
ctx.resetTransform();
|
|
2155
|
+
ctx.putImageData(imgData, 0, 0);
|
|
2156
|
+
if (devicePixelRatio !== 1) {
|
|
2157
|
+
const tmpCanvas = document.createElement("canvas");
|
|
2158
|
+
tmpCanvas.width = canvasWidth;
|
|
2159
|
+
tmpCanvas.height = canvasHeight;
|
|
2160
|
+
const tmpCtx = tmpCanvas.getContext("2d");
|
|
2161
|
+
if (!tmpCtx) continue;
|
|
2162
|
+
tmpCtx.putImageData(imgData, 0, 0);
|
|
2163
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2164
|
+
ctx.imageSmoothingEnabled = false;
|
|
2165
|
+
ctx.drawImage(tmpCanvas, 0, 0, canvas.width, canvas.height);
|
|
2166
|
+
}
|
|
2167
|
+
globalPixelOffset += canvasWidth;
|
|
2168
|
+
}
|
|
2169
|
+
}, [isWorkerMode, data, length, waveHeight, devicePixelRatio, samplesPerPixel, lut, frequencyScaleFn, minFrequency, maxF, scaleFn]);
|
|
2170
|
+
let totalWidth = length;
|
|
2171
|
+
let canvasCount = 0;
|
|
2172
|
+
const canvases = [];
|
|
2173
|
+
while (totalWidth > 0) {
|
|
2174
|
+
const currentWidth = Math.min(totalWidth, MAX_CANVAS_WIDTH2);
|
|
2175
|
+
canvases.push(
|
|
2176
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2177
|
+
SpectrogramCanvas,
|
|
2178
|
+
{
|
|
2179
|
+
$cssWidth: currentWidth,
|
|
2180
|
+
width: currentWidth * devicePixelRatio,
|
|
2181
|
+
height: waveHeight * devicePixelRatio,
|
|
2182
|
+
$waveHeight: waveHeight,
|
|
2183
|
+
"data-index": canvasCount,
|
|
2184
|
+
ref: canvasRef
|
|
2185
|
+
},
|
|
2186
|
+
`${length}-${canvasCount}`
|
|
2187
|
+
)
|
|
2188
|
+
);
|
|
2189
|
+
totalWidth -= currentWidth;
|
|
2190
|
+
canvasCount++;
|
|
2191
|
+
}
|
|
2192
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Wrapper3, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });
|
|
2193
|
+
};
|
|
2194
|
+
|
|
2195
|
+
// src/components/SmartChannel.tsx
|
|
2196
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
2197
|
+
var SmartChannel = ({
|
|
2198
|
+
isSelected,
|
|
2199
|
+
transparentBackground,
|
|
2200
|
+
renderMode = "waveform",
|
|
2201
|
+
spectrogramData,
|
|
2202
|
+
spectrogramColorLUT,
|
|
2203
|
+
samplesPerPixel: sppProp,
|
|
2204
|
+
spectrogramFrequencyScaleFn,
|
|
2205
|
+
spectrogramMinFrequency,
|
|
2206
|
+
spectrogramMaxFrequency,
|
|
2207
|
+
spectrogramWorkerApi,
|
|
2208
|
+
spectrogramClipId,
|
|
2209
|
+
spectrogramOnCanvasesReady,
|
|
2210
|
+
...props
|
|
2211
|
+
}) => {
|
|
1997
2212
|
const theme = useTheme2();
|
|
1998
|
-
const { waveHeight, barWidth, barGap } = usePlaylistInfo();
|
|
2213
|
+
const { waveHeight, barWidth, barGap, samplesPerPixel: contextSpp } = usePlaylistInfo();
|
|
1999
2214
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2215
|
+
const samplesPerPixel = sppProp ?? contextSpp;
|
|
2000
2216
|
const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;
|
|
2001
2217
|
const waveFillColor = isSelected && theme ? theme.selectedWaveFillColor : theme?.waveFillColor;
|
|
2002
2218
|
const drawMode = theme?.waveformDrawMode || "inverted";
|
|
2003
|
-
|
|
2219
|
+
const hasSpectrogram = spectrogramData || spectrogramWorkerApi;
|
|
2220
|
+
if (renderMode === "spectrogram" && hasSpectrogram) {
|
|
2221
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2222
|
+
SpectrogramChannel,
|
|
2223
|
+
{
|
|
2224
|
+
index: props.index,
|
|
2225
|
+
data: spectrogramData,
|
|
2226
|
+
length: props.length,
|
|
2227
|
+
waveHeight,
|
|
2228
|
+
devicePixelRatio,
|
|
2229
|
+
samplesPerPixel,
|
|
2230
|
+
colorLUT: spectrogramColorLUT,
|
|
2231
|
+
frequencyScaleFn: spectrogramFrequencyScaleFn,
|
|
2232
|
+
minFrequency: spectrogramMinFrequency,
|
|
2233
|
+
maxFrequency: spectrogramMaxFrequency,
|
|
2234
|
+
workerApi: spectrogramWorkerApi,
|
|
2235
|
+
clipId: spectrogramClipId,
|
|
2236
|
+
onCanvasesReady: spectrogramOnCanvasesReady
|
|
2237
|
+
}
|
|
2238
|
+
);
|
|
2239
|
+
}
|
|
2240
|
+
if (renderMode === "both" && hasSpectrogram) {
|
|
2241
|
+
const halfHeight = Math.floor(waveHeight / 2);
|
|
2242
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
2243
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2244
|
+
SpectrogramChannel,
|
|
2245
|
+
{
|
|
2246
|
+
index: props.index * 2,
|
|
2247
|
+
data: spectrogramData,
|
|
2248
|
+
length: props.length,
|
|
2249
|
+
waveHeight: halfHeight,
|
|
2250
|
+
devicePixelRatio,
|
|
2251
|
+
samplesPerPixel,
|
|
2252
|
+
colorLUT: spectrogramColorLUT,
|
|
2253
|
+
frequencyScaleFn: spectrogramFrequencyScaleFn,
|
|
2254
|
+
minFrequency: spectrogramMinFrequency,
|
|
2255
|
+
maxFrequency: spectrogramMaxFrequency,
|
|
2256
|
+
workerApi: spectrogramWorkerApi,
|
|
2257
|
+
clipId: spectrogramClipId,
|
|
2258
|
+
onCanvasesReady: spectrogramOnCanvasesReady
|
|
2259
|
+
}
|
|
2260
|
+
),
|
|
2261
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { position: "absolute", top: (props.index * 2 + 1) * halfHeight, width: props.length, height: halfHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2262
|
+
Channel,
|
|
2263
|
+
{
|
|
2264
|
+
...props,
|
|
2265
|
+
...theme,
|
|
2266
|
+
index: 0,
|
|
2267
|
+
waveOutlineColor,
|
|
2268
|
+
waveFillColor,
|
|
2269
|
+
waveHeight: halfHeight,
|
|
2270
|
+
devicePixelRatio,
|
|
2271
|
+
barWidth,
|
|
2272
|
+
barGap,
|
|
2273
|
+
transparentBackground,
|
|
2274
|
+
drawMode
|
|
2275
|
+
}
|
|
2276
|
+
) })
|
|
2277
|
+
] });
|
|
2278
|
+
}
|
|
2279
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2004
2280
|
Channel,
|
|
2005
2281
|
{
|
|
2006
2282
|
...props,
|
|
@@ -2017,12 +2293,112 @@ var SmartChannel = ({ isSelected, transparentBackground, ...props }) => {
|
|
|
2017
2293
|
);
|
|
2018
2294
|
};
|
|
2019
2295
|
|
|
2020
|
-
// src/components/
|
|
2296
|
+
// src/components/SpectrogramLabels.tsx
|
|
2021
2297
|
var import_react13 = require("react");
|
|
2298
|
+
var import_styled_components21 = __toESM(require("styled-components"));
|
|
2299
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
2300
|
+
var LABELS_WIDTH = 72;
|
|
2301
|
+
var LabelsStickyWrapper = import_styled_components21.default.div`
|
|
2302
|
+
position: sticky;
|
|
2303
|
+
left: 0;
|
|
2304
|
+
z-index: 101;
|
|
2305
|
+
pointer-events: none;
|
|
2306
|
+
height: 0;
|
|
2307
|
+
width: 0;
|
|
2308
|
+
overflow: visible;
|
|
2309
|
+
`;
|
|
2310
|
+
function getFrequencyLabels(minF, maxF, height) {
|
|
2311
|
+
const allCandidates = [
|
|
2312
|
+
20,
|
|
2313
|
+
50,
|
|
2314
|
+
100,
|
|
2315
|
+
200,
|
|
2316
|
+
500,
|
|
2317
|
+
1e3,
|
|
2318
|
+
2e3,
|
|
2319
|
+
3e3,
|
|
2320
|
+
4e3,
|
|
2321
|
+
5e3,
|
|
2322
|
+
8e3,
|
|
2323
|
+
1e4,
|
|
2324
|
+
12e3,
|
|
2325
|
+
16e3,
|
|
2326
|
+
2e4
|
|
2327
|
+
];
|
|
2328
|
+
const inRange = allCandidates.filter((f) => f >= minF && f <= maxF);
|
|
2329
|
+
const maxLabels = Math.max(2, Math.floor(height / 20));
|
|
2330
|
+
if (inRange.length <= maxLabels) return inRange;
|
|
2331
|
+
const step = (inRange.length - 1) / (maxLabels - 1);
|
|
2332
|
+
const result = [];
|
|
2333
|
+
for (let i = 0; i < maxLabels; i++) {
|
|
2334
|
+
result.push(inRange[Math.round(i * step)]);
|
|
2335
|
+
}
|
|
2336
|
+
return result;
|
|
2337
|
+
}
|
|
2338
|
+
var SpectrogramLabels = ({
|
|
2339
|
+
waveHeight,
|
|
2340
|
+
numChannels,
|
|
2341
|
+
frequencyScaleFn,
|
|
2342
|
+
minFrequency,
|
|
2343
|
+
maxFrequency,
|
|
2344
|
+
labelsColor = "#ccc",
|
|
2345
|
+
labelsBackground = "rgba(0,0,0,0.6)",
|
|
2346
|
+
renderMode = "spectrogram",
|
|
2347
|
+
hasClipHeaders = false
|
|
2348
|
+
}) => {
|
|
2349
|
+
const canvasRef = (0, import_react13.useRef)(null);
|
|
2350
|
+
const devicePixelRatio = useDevicePixelRatio();
|
|
2351
|
+
const spectrogramHeight = renderMode === "both" ? Math.floor(waveHeight / 2) : waveHeight;
|
|
2352
|
+
const totalHeight = numChannels * waveHeight;
|
|
2353
|
+
const clipHeaderOffset = hasClipHeaders ? 22 : 0;
|
|
2354
|
+
(0, import_react13.useLayoutEffect)(() => {
|
|
2355
|
+
const canvas = canvasRef.current;
|
|
2356
|
+
if (!canvas) return;
|
|
2357
|
+
const ctx = canvas.getContext("2d");
|
|
2358
|
+
if (!ctx) return;
|
|
2359
|
+
ctx.resetTransform();
|
|
2360
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2361
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
2362
|
+
const labelFreqs = getFrequencyLabels(minFrequency, maxFrequency, spectrogramHeight);
|
|
2363
|
+
for (let ch = 0; ch < numChannels; ch++) {
|
|
2364
|
+
const channelTop = ch * waveHeight + clipHeaderOffset;
|
|
2365
|
+
ctx.font = "11px monospace";
|
|
2366
|
+
ctx.textBaseline = "middle";
|
|
2367
|
+
for (const freq of labelFreqs) {
|
|
2368
|
+
const normalized = frequencyScaleFn(freq, minFrequency, maxFrequency);
|
|
2369
|
+
if (normalized < 0 || normalized > 1) continue;
|
|
2370
|
+
const y = channelTop + spectrogramHeight * (1 - normalized);
|
|
2371
|
+
const text = freq >= 1e3 ? `${(freq / 1e3).toFixed(1)}k` : `${freq} Hz`;
|
|
2372
|
+
const metrics = ctx.measureText(text);
|
|
2373
|
+
const padding = 3;
|
|
2374
|
+
ctx.fillStyle = labelsBackground;
|
|
2375
|
+
ctx.fillRect(0, y - 7, metrics.width + padding * 2, 14);
|
|
2376
|
+
ctx.fillStyle = labelsColor;
|
|
2377
|
+
ctx.fillText(text, padding, y);
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}, [waveHeight, numChannels, frequencyScaleFn, minFrequency, maxFrequency, labelsColor, labelsBackground, devicePixelRatio, spectrogramHeight, clipHeaderOffset]);
|
|
2381
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2382
|
+
"canvas",
|
|
2383
|
+
{
|
|
2384
|
+
ref: canvasRef,
|
|
2385
|
+
width: LABELS_WIDTH * devicePixelRatio,
|
|
2386
|
+
height: (totalHeight + clipHeaderOffset) * devicePixelRatio,
|
|
2387
|
+
style: {
|
|
2388
|
+
width: LABELS_WIDTH,
|
|
2389
|
+
height: totalHeight + clipHeaderOffset,
|
|
2390
|
+
pointerEvents: "none"
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
) });
|
|
2394
|
+
};
|
|
2395
|
+
|
|
2396
|
+
// src/components/SmartScale.tsx
|
|
2397
|
+
var import_react15 = require("react");
|
|
2022
2398
|
|
|
2023
2399
|
// src/components/TimeScale.tsx
|
|
2024
|
-
var
|
|
2025
|
-
var
|
|
2400
|
+
var import_react14 = __toESM(require("react"));
|
|
2401
|
+
var import_styled_components22 = __toESM(require("styled-components"));
|
|
2026
2402
|
|
|
2027
2403
|
// src/utils/conversions.ts
|
|
2028
2404
|
function samplesToSeconds(samples, sampleRate) {
|
|
@@ -2045,14 +2421,14 @@ function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
|
|
|
2045
2421
|
}
|
|
2046
2422
|
|
|
2047
2423
|
// src/components/TimeScale.tsx
|
|
2048
|
-
var
|
|
2424
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
2049
2425
|
function formatTime2(milliseconds) {
|
|
2050
2426
|
const seconds = Math.floor(milliseconds / 1e3);
|
|
2051
2427
|
const s = seconds % 60;
|
|
2052
2428
|
const m = (seconds - s) / 60;
|
|
2053
2429
|
return `${m}:${String(s).padStart(2, "0")}`;
|
|
2054
2430
|
}
|
|
2055
|
-
var PlaylistTimeScaleScroll =
|
|
2431
|
+
var PlaylistTimeScaleScroll = import_styled_components22.default.div.attrs((props) => ({
|
|
2056
2432
|
style: {
|
|
2057
2433
|
width: `${props.$cssWidth}px`,
|
|
2058
2434
|
marginLeft: `${props.$controlWidth}px`,
|
|
@@ -2064,7 +2440,7 @@ var PlaylistTimeScaleScroll = import_styled_components20.default.div.attrs((prop
|
|
|
2064
2440
|
border-bottom: 1px solid ${(props) => props.theme.timeColor};
|
|
2065
2441
|
box-sizing: border-box;
|
|
2066
2442
|
`;
|
|
2067
|
-
var TimeTicks =
|
|
2443
|
+
var TimeTicks = import_styled_components22.default.canvas.attrs((props) => ({
|
|
2068
2444
|
style: {
|
|
2069
2445
|
width: `${props.$cssWidth}px`,
|
|
2070
2446
|
height: `${props.$timeScaleHeight}px`
|
|
@@ -2075,7 +2451,7 @@ var TimeTicks = import_styled_components20.default.canvas.attrs((props) => ({
|
|
|
2075
2451
|
right: 0;
|
|
2076
2452
|
bottom: 0;
|
|
2077
2453
|
`;
|
|
2078
|
-
var TimeStamp =
|
|
2454
|
+
var TimeStamp = import_styled_components22.default.div.attrs((props) => ({
|
|
2079
2455
|
style: {
|
|
2080
2456
|
left: `${props.$left + 4}px`
|
|
2081
2457
|
// Offset 4px to the right of the tick
|
|
@@ -2097,15 +2473,15 @@ var TimeScale = (props) => {
|
|
|
2097
2473
|
} = props;
|
|
2098
2474
|
const canvasInfo = /* @__PURE__ */ new Map();
|
|
2099
2475
|
const timeMarkers = [];
|
|
2100
|
-
const canvasRef = (0,
|
|
2476
|
+
const canvasRef = (0, import_react14.useRef)(null);
|
|
2101
2477
|
const {
|
|
2102
2478
|
sampleRate,
|
|
2103
2479
|
samplesPerPixel,
|
|
2104
2480
|
timeScaleHeight,
|
|
2105
2481
|
controls: { show: showControls, width: controlWidth }
|
|
2106
|
-
} = (0,
|
|
2482
|
+
} = (0, import_react14.useContext)(PlaylistInfoContext);
|
|
2107
2483
|
const devicePixelRatio = useDevicePixelRatio();
|
|
2108
|
-
(0,
|
|
2484
|
+
(0, import_react14.useEffect)(() => {
|
|
2109
2485
|
if (canvasRef.current !== null) {
|
|
2110
2486
|
const canvas = canvasRef.current;
|
|
2111
2487
|
const ctx = canvas.getContext("2d");
|
|
@@ -2139,7 +2515,7 @@ var TimeScale = (props) => {
|
|
|
2139
2515
|
if (counter % marker === 0) {
|
|
2140
2516
|
const timeMs = counter;
|
|
2141
2517
|
const timestamp = formatTime2(timeMs);
|
|
2142
|
-
const timestampContent = renderTimestamp ? /* @__PURE__ */ (0,
|
|
2518
|
+
const timestampContent = renderTimestamp ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react14.default.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TimeStamp, { $left: pix, children: timestamp }, timestamp);
|
|
2143
2519
|
timeMarkers.push(timestampContent);
|
|
2144
2520
|
canvasInfo.set(pix, timeScaleHeight);
|
|
2145
2521
|
} else if (counter % bigStep === 0) {
|
|
@@ -2149,7 +2525,7 @@ var TimeScale = (props) => {
|
|
|
2149
2525
|
}
|
|
2150
2526
|
counter += secondStep;
|
|
2151
2527
|
}
|
|
2152
|
-
return /* @__PURE__ */ (0,
|
|
2528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
2153
2529
|
PlaylistTimeScaleScroll,
|
|
2154
2530
|
{
|
|
2155
2531
|
$cssWidth: widthX,
|
|
@@ -2157,7 +2533,7 @@ var TimeScale = (props) => {
|
|
|
2157
2533
|
$timeScaleHeight: timeScaleHeight,
|
|
2158
2534
|
children: [
|
|
2159
2535
|
timeMarkers,
|
|
2160
|
-
/* @__PURE__ */ (0,
|
|
2536
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2161
2537
|
TimeTicks,
|
|
2162
2538
|
{
|
|
2163
2539
|
$cssWidth: widthX,
|
|
@@ -2171,10 +2547,10 @@ var TimeScale = (props) => {
|
|
|
2171
2547
|
}
|
|
2172
2548
|
);
|
|
2173
2549
|
};
|
|
2174
|
-
var StyledTimeScale = (0,
|
|
2550
|
+
var StyledTimeScale = (0, import_styled_components22.withTheme)(TimeScale);
|
|
2175
2551
|
|
|
2176
2552
|
// src/components/SmartScale.tsx
|
|
2177
|
-
var
|
|
2553
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
2178
2554
|
var timeinfo = /* @__PURE__ */ new Map([
|
|
2179
2555
|
[
|
|
2180
2556
|
700,
|
|
@@ -2247,24 +2623,25 @@ function getScaleInfo(samplesPerPixel) {
|
|
|
2247
2623
|
}
|
|
2248
2624
|
return config;
|
|
2249
2625
|
}
|
|
2250
|
-
var SmartScale = () => {
|
|
2251
|
-
const { samplesPerPixel, duration } = (0,
|
|
2626
|
+
var SmartScale = ({ renderTimestamp }) => {
|
|
2627
|
+
const { samplesPerPixel, duration } = (0, import_react15.useContext)(PlaylistInfoContext);
|
|
2252
2628
|
let config = getScaleInfo(samplesPerPixel);
|
|
2253
|
-
return /* @__PURE__ */ (0,
|
|
2629
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2254
2630
|
StyledTimeScale,
|
|
2255
2631
|
{
|
|
2256
2632
|
marker: config.marker,
|
|
2257
2633
|
bigStep: config.bigStep,
|
|
2258
2634
|
secondStep: config.smallStep,
|
|
2259
|
-
duration
|
|
2635
|
+
duration,
|
|
2636
|
+
renderTimestamp
|
|
2260
2637
|
}
|
|
2261
2638
|
);
|
|
2262
2639
|
};
|
|
2263
2640
|
|
|
2264
2641
|
// src/components/TimeFormatSelect.tsx
|
|
2265
|
-
var
|
|
2266
|
-
var
|
|
2267
|
-
var SelectWrapper =
|
|
2642
|
+
var import_styled_components23 = __toESM(require("styled-components"));
|
|
2643
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
2644
|
+
var SelectWrapper = import_styled_components23.default.div`
|
|
2268
2645
|
display: inline-flex;
|
|
2269
2646
|
align-items: center;
|
|
2270
2647
|
gap: 0.5rem;
|
|
@@ -2286,7 +2663,7 @@ var TimeFormatSelect = ({
|
|
|
2286
2663
|
const handleChange = (e) => {
|
|
2287
2664
|
onChange(e.target.value);
|
|
2288
2665
|
};
|
|
2289
|
-
return /* @__PURE__ */ (0,
|
|
2666
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SelectWrapper, { className, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2290
2667
|
BaseSelect,
|
|
2291
2668
|
{
|
|
2292
2669
|
className: "time-format",
|
|
@@ -2294,15 +2671,15 @@ var TimeFormatSelect = ({
|
|
|
2294
2671
|
onChange: handleChange,
|
|
2295
2672
|
disabled,
|
|
2296
2673
|
"aria-label": "Time format selection",
|
|
2297
|
-
children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ (0,
|
|
2674
|
+
children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: option.value, children: option.label }, option.value))
|
|
2298
2675
|
}
|
|
2299
2676
|
) });
|
|
2300
2677
|
};
|
|
2301
2678
|
|
|
2302
2679
|
// src/components/Track.tsx
|
|
2303
|
-
var
|
|
2304
|
-
var
|
|
2305
|
-
var Container =
|
|
2680
|
+
var import_styled_components24 = __toESM(require("styled-components"));
|
|
2681
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2682
|
+
var Container = import_styled_components24.default.div.attrs((props) => ({
|
|
2306
2683
|
style: {
|
|
2307
2684
|
height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`
|
|
2308
2685
|
}
|
|
@@ -2311,7 +2688,7 @@ var Container = import_styled_components22.default.div.attrs((props) => ({
|
|
|
2311
2688
|
display: flex;
|
|
2312
2689
|
${(props) => props.$width !== void 0 && `width: ${props.$width}px;`}
|
|
2313
2690
|
`;
|
|
2314
|
-
var ChannelContainer =
|
|
2691
|
+
var ChannelContainer = import_styled_components24.default.div.attrs((props) => ({
|
|
2315
2692
|
style: {
|
|
2316
2693
|
paddingLeft: `${props.$offset || 0}px`
|
|
2317
2694
|
}
|
|
@@ -2320,13 +2697,13 @@ var ChannelContainer = import_styled_components22.default.div.attrs((props) => (
|
|
|
2320
2697
|
background: ${(props) => props.$backgroundColor || "transparent"};
|
|
2321
2698
|
flex: 1;
|
|
2322
2699
|
`;
|
|
2323
|
-
var ControlsWrapper =
|
|
2700
|
+
var ControlsWrapper = import_styled_components24.default.div.attrs((props) => ({
|
|
2324
2701
|
style: {
|
|
2325
2702
|
width: `${props.$controlWidth}px`
|
|
2326
2703
|
}
|
|
2327
2704
|
}))`
|
|
2328
2705
|
position: sticky;
|
|
2329
|
-
z-index:
|
|
2706
|
+
z-index: 102; /* Above waveform content and spectrogram labels (101), below Docusaurus navbar (200) */
|
|
2330
2707
|
left: 0;
|
|
2331
2708
|
height: 100%;
|
|
2332
2709
|
flex-shrink: 0;
|
|
@@ -2356,7 +2733,7 @@ var Track = ({
|
|
|
2356
2733
|
controls: { show, width: controlWidth }
|
|
2357
2734
|
} = usePlaylistInfo();
|
|
2358
2735
|
const controls = useTrackControls();
|
|
2359
|
-
return /* @__PURE__ */ (0,
|
|
2736
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
2360
2737
|
Container,
|
|
2361
2738
|
{
|
|
2362
2739
|
$numChannels: numChannels,
|
|
@@ -2367,7 +2744,7 @@ var Track = ({
|
|
|
2367
2744
|
$hasClipHeaders: hasClipHeaders,
|
|
2368
2745
|
$isSelected: isSelected,
|
|
2369
2746
|
children: [
|
|
2370
|
-
/* @__PURE__ */ (0,
|
|
2747
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2371
2748
|
ControlsWrapper,
|
|
2372
2749
|
{
|
|
2373
2750
|
$controlWidth: show ? controlWidth : 0,
|
|
@@ -2375,7 +2752,7 @@ var Track = ({
|
|
|
2375
2752
|
children: controls
|
|
2376
2753
|
}
|
|
2377
2754
|
),
|
|
2378
|
-
/* @__PURE__ */ (0,
|
|
2755
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2379
2756
|
ChannelContainer,
|
|
2380
2757
|
{
|
|
2381
2758
|
$controlWidth: show ? controlWidth : 0,
|
|
@@ -2392,8 +2769,8 @@ var Track = ({
|
|
|
2392
2769
|
};
|
|
2393
2770
|
|
|
2394
2771
|
// src/components/TrackControls/Button.tsx
|
|
2395
|
-
var
|
|
2396
|
-
var Button =
|
|
2772
|
+
var import_styled_components25 = __toESM(require("styled-components"));
|
|
2773
|
+
var Button = import_styled_components25.default.button.attrs({
|
|
2397
2774
|
type: "button"
|
|
2398
2775
|
})`
|
|
2399
2776
|
display: inline-block;
|
|
@@ -2465,8 +2842,8 @@ var Button = import_styled_components23.default.button.attrs({
|
|
|
2465
2842
|
`;
|
|
2466
2843
|
|
|
2467
2844
|
// src/components/TrackControls/ButtonGroup.tsx
|
|
2468
|
-
var
|
|
2469
|
-
var ButtonGroup =
|
|
2845
|
+
var import_styled_components26 = __toESM(require("styled-components"));
|
|
2846
|
+
var ButtonGroup = import_styled_components26.default.div`
|
|
2470
2847
|
margin-bottom: 0.3rem;
|
|
2471
2848
|
|
|
2472
2849
|
button:not(:first-child) {
|
|
@@ -2480,9 +2857,39 @@ var ButtonGroup = import_styled_components24.default.div`
|
|
|
2480
2857
|
}
|
|
2481
2858
|
`;
|
|
2482
2859
|
|
|
2860
|
+
// src/components/TrackControls/CloseButton.tsx
|
|
2861
|
+
var import_styled_components27 = __toESM(require("styled-components"));
|
|
2862
|
+
var import_react16 = require("@phosphor-icons/react");
|
|
2863
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2864
|
+
var StyledCloseButton = import_styled_components27.default.button`
|
|
2865
|
+
position: absolute;
|
|
2866
|
+
left: 0;
|
|
2867
|
+
top: 0;
|
|
2868
|
+
border: none;
|
|
2869
|
+
background: transparent;
|
|
2870
|
+
color: inherit;
|
|
2871
|
+
cursor: pointer;
|
|
2872
|
+
font-size: 16px;
|
|
2873
|
+
padding: 2px 4px;
|
|
2874
|
+
display: flex;
|
|
2875
|
+
align-items: center;
|
|
2876
|
+
justify-content: center;
|
|
2877
|
+
opacity: 0.7;
|
|
2878
|
+
transition: opacity 0.15s, color 0.15s;
|
|
2879
|
+
|
|
2880
|
+
&:hover {
|
|
2881
|
+
opacity: 1;
|
|
2882
|
+
color: #dc3545;
|
|
2883
|
+
}
|
|
2884
|
+
`;
|
|
2885
|
+
var CloseButton = ({
|
|
2886
|
+
onClick,
|
|
2887
|
+
title = "Remove track"
|
|
2888
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react16.X, { size: 12, weight: "bold" }) });
|
|
2889
|
+
|
|
2483
2890
|
// src/components/TrackControls/Controls.tsx
|
|
2484
|
-
var
|
|
2485
|
-
var Controls =
|
|
2891
|
+
var import_styled_components28 = __toESM(require("styled-components"));
|
|
2892
|
+
var Controls = import_styled_components28.default.div`
|
|
2486
2893
|
background: transparent;
|
|
2487
2894
|
width: 100%;
|
|
2488
2895
|
height: 100%;
|
|
@@ -2498,8 +2905,8 @@ var Controls = import_styled_components25.default.div`
|
|
|
2498
2905
|
`;
|
|
2499
2906
|
|
|
2500
2907
|
// src/components/TrackControls/Header.tsx
|
|
2501
|
-
var
|
|
2502
|
-
var Header =
|
|
2908
|
+
var import_styled_components29 = __toESM(require("styled-components"));
|
|
2909
|
+
var Header = import_styled_components29.default.header`
|
|
2503
2910
|
overflow: hidden;
|
|
2504
2911
|
height: 26px;
|
|
2505
2912
|
width: 100%;
|
|
@@ -2513,23 +2920,28 @@ var Header = import_styled_components26.default.header`
|
|
|
2513
2920
|
`;
|
|
2514
2921
|
|
|
2515
2922
|
// src/components/TrackControls/VolumeDownIcon.tsx
|
|
2516
|
-
var
|
|
2517
|
-
var
|
|
2518
|
-
var VolumeDownIcon = (props) => /* @__PURE__ */ (0,
|
|
2923
|
+
var import_react17 = require("@phosphor-icons/react");
|
|
2924
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
2925
|
+
var VolumeDownIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react17.SpeakerLowIcon, { weight: "light", ...props });
|
|
2519
2926
|
|
|
2520
2927
|
// src/components/TrackControls/VolumeUpIcon.tsx
|
|
2521
|
-
var
|
|
2522
|
-
var
|
|
2523
|
-
var VolumeUpIcon = (props) => /* @__PURE__ */ (0,
|
|
2928
|
+
var import_react18 = require("@phosphor-icons/react");
|
|
2929
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
2930
|
+
var VolumeUpIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react18.SpeakerHighIcon, { weight: "light", ...props });
|
|
2524
2931
|
|
|
2525
2932
|
// src/components/TrackControls/TrashIcon.tsx
|
|
2526
|
-
var
|
|
2527
|
-
var
|
|
2528
|
-
var TrashIcon = (props) => /* @__PURE__ */ (0,
|
|
2933
|
+
var import_react19 = require("@phosphor-icons/react");
|
|
2934
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
2935
|
+
var TrashIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react19.TrashIcon, { weight: "light", ...props });
|
|
2936
|
+
|
|
2937
|
+
// src/components/TrackControls/DotsIcon.tsx
|
|
2938
|
+
var import_react20 = require("@phosphor-icons/react");
|
|
2939
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
2940
|
+
var DotsIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react20.DotsThreeIcon, { weight: "bold", ...props });
|
|
2529
2941
|
|
|
2530
2942
|
// src/components/TrackControls/Slider.tsx
|
|
2531
|
-
var
|
|
2532
|
-
var Slider = (0,
|
|
2943
|
+
var import_styled_components30 = __toESM(require("styled-components"));
|
|
2944
|
+
var Slider = (0, import_styled_components30.default)(BaseSlider)`
|
|
2533
2945
|
width: 75%;
|
|
2534
2946
|
height: 5px;
|
|
2535
2947
|
background: ${(props) => props.theme.sliderTrackColor};
|
|
@@ -2581,8 +2993,8 @@ var Slider = (0, import_styled_components27.default)(BaseSlider)`
|
|
|
2581
2993
|
`;
|
|
2582
2994
|
|
|
2583
2995
|
// src/components/TrackControls/SliderWrapper.tsx
|
|
2584
|
-
var
|
|
2585
|
-
var SliderWrapper =
|
|
2996
|
+
var import_styled_components31 = __toESM(require("styled-components"));
|
|
2997
|
+
var SliderWrapper = import_styled_components31.default.label`
|
|
2586
2998
|
width: 100%;
|
|
2587
2999
|
display: flex;
|
|
2588
3000
|
justify-content: space-between;
|
|
@@ -2592,113 +3004,108 @@ var SliderWrapper = import_styled_components28.default.label`
|
|
|
2592
3004
|
font-size: 14px;
|
|
2593
3005
|
`;
|
|
2594
3006
|
|
|
2595
|
-
// src/components/
|
|
2596
|
-
var
|
|
2597
|
-
var
|
|
2598
|
-
var
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
`;
|
|
2604
|
-
var TrackNameSpan = import_styled_components29.default.span`
|
|
2605
|
-
flex: 1;
|
|
2606
|
-
font-weight: 600;
|
|
2607
|
-
font-size: 0.875rem;
|
|
2608
|
-
overflow: hidden;
|
|
2609
|
-
text-overflow: ellipsis;
|
|
2610
|
-
white-space: nowrap;
|
|
2611
|
-
margin: 0 0.25rem;
|
|
3007
|
+
// src/components/TrackMenu.tsx
|
|
3008
|
+
var import_react21 = __toESM(require("react"));
|
|
3009
|
+
var import_react_dom = require("react-dom");
|
|
3010
|
+
var import_styled_components32 = __toESM(require("styled-components"));
|
|
3011
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
3012
|
+
var MenuContainer = import_styled_components32.default.div`
|
|
3013
|
+
position: relative;
|
|
3014
|
+
display: inline-block;
|
|
2612
3015
|
`;
|
|
2613
|
-
var
|
|
3016
|
+
var MenuButton = import_styled_components32.default.button`
|
|
3017
|
+
background: none;
|
|
3018
|
+
border: none;
|
|
3019
|
+
cursor: pointer;
|
|
3020
|
+
padding: 2px 4px;
|
|
2614
3021
|
display: flex;
|
|
2615
3022
|
align-items: center;
|
|
2616
3023
|
justify-content: center;
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
padding: 0;
|
|
2620
|
-
border: none;
|
|
2621
|
-
background: transparent;
|
|
2622
|
-
color: #999;
|
|
2623
|
-
cursor: pointer;
|
|
2624
|
-
font-size: 16px;
|
|
2625
|
-
line-height: 1;
|
|
2626
|
-
border-radius: 3px;
|
|
2627
|
-
transition: all 0.2s ease-in-out;
|
|
2628
|
-
flex-shrink: 0;
|
|
3024
|
+
color: inherit;
|
|
3025
|
+
opacity: 0.7;
|
|
2629
3026
|
|
|
2630
3027
|
&:hover {
|
|
2631
|
-
|
|
2632
|
-
color: white;
|
|
2633
|
-
}
|
|
2634
|
-
|
|
2635
|
-
&:active {
|
|
2636
|
-
transform: scale(0.9);
|
|
3028
|
+
opacity: 1;
|
|
2637
3029
|
}
|
|
2638
3030
|
`;
|
|
2639
|
-
var
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
3031
|
+
var Dropdown = import_styled_components32.default.div`
|
|
3032
|
+
position: fixed;
|
|
3033
|
+
top: ${(p) => p.$top}px;
|
|
3034
|
+
left: ${(p) => p.$left}px;
|
|
3035
|
+
z-index: 10000;
|
|
3036
|
+
background: ${(p) => p.theme.timescaleBackgroundColor ?? "#222"};
|
|
3037
|
+
color: ${(p) => p.theme.textColor ?? "inherit"};
|
|
3038
|
+
border: 1px solid rgba(128, 128, 128, 0.4);
|
|
3039
|
+
border-radius: 6px;
|
|
3040
|
+
padding: 0.5rem 0;
|
|
3041
|
+
min-width: 180px;
|
|
3042
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
3043
|
+
`;
|
|
3044
|
+
var Divider = import_styled_components32.default.hr`
|
|
3045
|
+
border: none;
|
|
3046
|
+
border-top: 1px solid rgba(128, 128, 128, 0.3);
|
|
3047
|
+
margin: 0.35rem 0;
|
|
3048
|
+
`;
|
|
3049
|
+
var TrackMenu = ({
|
|
3050
|
+
items: itemsProp
|
|
2650
3051
|
}) => {
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
3052
|
+
const [open, setOpen] = (0, import_react21.useState)(false);
|
|
3053
|
+
const close = () => setOpen(false);
|
|
3054
|
+
const items = typeof itemsProp === "function" ? itemsProp(close) : itemsProp;
|
|
3055
|
+
const [dropdownPos, setDropdownPos] = (0, import_react21.useState)({ top: 0, left: 0 });
|
|
3056
|
+
const buttonRef = (0, import_react21.useRef)(null);
|
|
3057
|
+
const dropdownRef = (0, import_react21.useRef)(null);
|
|
3058
|
+
(0, import_react21.useEffect)(() => {
|
|
3059
|
+
if (open && buttonRef.current) {
|
|
3060
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
3061
|
+
setDropdownPos({
|
|
3062
|
+
top: rect.bottom + 2,
|
|
3063
|
+
left: Math.max(0, rect.right - 180)
|
|
3064
|
+
});
|
|
3065
|
+
}
|
|
3066
|
+
}, [open]);
|
|
3067
|
+
(0, import_react21.useEffect)(() => {
|
|
3068
|
+
if (!open) return;
|
|
3069
|
+
const handleClick = (e) => {
|
|
3070
|
+
const target = e.target;
|
|
3071
|
+
if (buttonRef.current && !buttonRef.current.contains(target) && dropdownRef.current && !dropdownRef.current.contains(target)) {
|
|
3072
|
+
setOpen(false);
|
|
3073
|
+
}
|
|
3074
|
+
};
|
|
3075
|
+
document.addEventListener("mousedown", handleClick);
|
|
3076
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
3077
|
+
}, [open]);
|
|
3078
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(MenuContainer, { children: [
|
|
3079
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3080
|
+
MenuButton,
|
|
3081
|
+
{
|
|
3082
|
+
ref: buttonRef,
|
|
3083
|
+
onClick: (e) => {
|
|
3084
|
+
e.stopPropagation();
|
|
3085
|
+
setOpen((prev) => !prev);
|
|
3086
|
+
},
|
|
3087
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
3088
|
+
title: "Track menu",
|
|
3089
|
+
"aria-label": "Track menu",
|
|
3090
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(DotsIcon, { size: 16 })
|
|
3091
|
+
}
|
|
3092
|
+
),
|
|
3093
|
+
open && typeof document !== "undefined" && (0, import_react_dom.createPortal)(
|
|
3094
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3095
|
+
Dropdown,
|
|
2692
3096
|
{
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
3097
|
+
ref: dropdownRef,
|
|
3098
|
+
$top: dropdownPos.top,
|
|
3099
|
+
$left: dropdownPos.left,
|
|
3100
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
3101
|
+
children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react21.default.Fragment, { children: [
|
|
3102
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Divider, {}),
|
|
3103
|
+
item.content
|
|
3104
|
+
] }, item.id))
|
|
2698
3105
|
}
|
|
2699
3106
|
),
|
|
2700
|
-
|
|
2701
|
-
|
|
3107
|
+
document.body
|
|
3108
|
+
)
|
|
2702
3109
|
] });
|
|
2703
3110
|
};
|
|
2704
3111
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -2724,8 +3131,10 @@ var TrackControlsWithDelete = ({
|
|
|
2724
3131
|
ClipBoundary,
|
|
2725
3132
|
ClipHeader,
|
|
2726
3133
|
ClipHeaderPresentational,
|
|
3134
|
+
CloseButton,
|
|
2727
3135
|
Controls,
|
|
2728
3136
|
DevicePixelRatioProvider,
|
|
3137
|
+
DotsIcon,
|
|
2729
3138
|
FadeOverlay,
|
|
2730
3139
|
Header,
|
|
2731
3140
|
InlineLabel,
|
|
@@ -2744,6 +3153,8 @@ var TrackControlsWithDelete = ({
|
|
|
2744
3153
|
SliderWrapper,
|
|
2745
3154
|
SmartChannel,
|
|
2746
3155
|
SmartScale,
|
|
3156
|
+
SpectrogramChannel,
|
|
3157
|
+
SpectrogramLabels,
|
|
2747
3158
|
StyledPlaylist,
|
|
2748
3159
|
StyledTimeScale,
|
|
2749
3160
|
TimeFormatSelect,
|
|
@@ -2752,7 +3163,7 @@ var TrackControlsWithDelete = ({
|
|
|
2752
3163
|
TimescaleLoopRegion,
|
|
2753
3164
|
Track,
|
|
2754
3165
|
TrackControlsContext,
|
|
2755
|
-
|
|
3166
|
+
TrackMenu,
|
|
2756
3167
|
TrashIcon,
|
|
2757
3168
|
VolumeDownIcon,
|
|
2758
3169
|
VolumeUpIcon,
|