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