@proteos/ui 0.76.0 → 0.78.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.cjs +991 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +592 -2
- package/dist/index.d.ts +592 -2
- package/dist/index.js +969 -9
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/components/data-table/data-table.scroll-restore.test.tsx +97 -0
- package/src/components/data-table/data-table.test.tsx +22 -2
- package/src/components/data-table/data-table.tsx +31 -1
- package/src/components/frame-strip/frame-strip.stories.tsx +61 -0
- package/src/components/frame-strip/frame-strip.test.tsx +112 -0
- package/src/components/frame-strip/frame-strip.tsx +131 -0
- package/src/components/frame-strip/index.ts +2 -0
- package/src/components/media-player/index.ts +22 -0
- package/src/components/media-player/media-controls.tsx +145 -0
- package/src/components/media-player/media-markers.tsx +108 -0
- package/src/components/media-player/media-peaks.test.ts +68 -0
- package/src/components/media-player/media-peaks.ts +78 -0
- package/src/components/media-player/media-player.stories.tsx +243 -0
- package/src/components/media-player/media-player.test.tsx +318 -0
- package/src/components/media-player/media-player.tsx +334 -0
- package/src/components/media-player/media-time.ts +27 -0
- package/src/components/media-player/media-timeline.test.tsx +133 -0
- package/src/components/media-player/media-timeline.tsx +78 -0
- package/src/components/media-player/media-video-stage.tsx +113 -0
- package/src/components/media-player/media-waveform.tsx +330 -0
- package/src/components/slider/index.ts +2 -0
- package/src/components/slider/slider.stories.tsx +32 -0
- package/src/components/slider/slider.test.tsx +35 -0
- package/src/components/slider/slider.tsx +89 -0
- package/src/index.ts +3 -0
package/dist/index.cjs
CHANGED
|
@@ -126,12 +126,12 @@ function formatFileSize(bytes) {
|
|
|
126
126
|
if (bytes < 1024) return `${bytes} B`;
|
|
127
127
|
const units = ["KB", "MB", "GB", "TB"];
|
|
128
128
|
let size = bytes / 1024;
|
|
129
|
-
let
|
|
130
|
-
while (size >= 1024 &&
|
|
129
|
+
let unit2 = 0;
|
|
130
|
+
while (size >= 1024 && unit2 < units.length - 1) {
|
|
131
131
|
size /= 1024;
|
|
132
|
-
|
|
132
|
+
unit2 += 1;
|
|
133
133
|
}
|
|
134
|
-
return `${size < 10 ? size.toFixed(1) : Math.round(size)} ${units[
|
|
134
|
+
return `${size < 10 ? size.toFixed(1) : Math.round(size)} ${units[unit2]}`;
|
|
135
135
|
}
|
|
136
136
|
function FileField({
|
|
137
137
|
value,
|
|
@@ -3998,13 +3998,35 @@ function DataTable({
|
|
|
3998
3998
|
setListOffset((prev) => prev === top ? prev : top);
|
|
3999
3999
|
});
|
|
4000
4000
|
const headingHeight = groupHeaderHeight ?? rowHeight;
|
|
4001
|
+
const [isScrollerAttached, setIsScrollerAttached] = react.useState(false);
|
|
4002
|
+
react.useLayoutEffect(() => setIsScrollerAttached(true), []);
|
|
4001
4003
|
const virtualizer = reactVirtual.useVirtualizer({
|
|
4004
|
+
enabled: initialScrollOffset === void 0 || isScrollerAttached,
|
|
4002
4005
|
count: listItems.length,
|
|
4003
4006
|
getScrollElement: () => scrollerRef.current,
|
|
4004
4007
|
estimateSize: (index) => listItems[index]?.kind === "group" ? headingHeight : rowHeight,
|
|
4005
4008
|
overscan,
|
|
4006
4009
|
scrollMargin: listOffset,
|
|
4007
|
-
|
|
4010
|
+
// Read once, with the scroller attached (see `enabled`). Clamp the restored
|
|
4011
|
+
// offset to what the scroller will be able to reach: while the skeleton
|
|
4012
|
+
// shows, or when the list shrank since the visit, the browser clamps
|
|
4013
|
+
// scrollTop WITHOUT firing a scroll event, and the virtualizer — which only
|
|
4014
|
+
// re-reads its offset on scroll events — would sit past the rows and paint
|
|
4015
|
+
// empty. The reach comes from the row data, not `scrollHeight`: the disabled
|
|
4016
|
+
// first render committed an empty spacer, so the DOM can't report it yet.
|
|
4017
|
+
...initialScrollOffset !== void 0 ? {
|
|
4018
|
+
initialOffset: () => {
|
|
4019
|
+
const el = scrollerRef.current;
|
|
4020
|
+
if (!el) return 0;
|
|
4021
|
+
const estimatedSize = listItems.reduce(
|
|
4022
|
+
(sum, item) => sum + (item.kind === "group" ? headingHeight : rowHeight),
|
|
4023
|
+
0
|
|
4024
|
+
);
|
|
4025
|
+
const listTop = listRef.current?.offsetTop ?? 0;
|
|
4026
|
+
const reachable = listTop + estimatedSize - el.clientHeight;
|
|
4027
|
+
return Math.max(0, Math.min(initialScrollOffset, reachable));
|
|
4028
|
+
}
|
|
4029
|
+
} : {}
|
|
4008
4030
|
});
|
|
4009
4031
|
const sentinelRef = useInfiniteScrollSentinel({
|
|
4010
4032
|
onIntersect: () => {
|
|
@@ -6029,6 +6051,103 @@ function FileViewer({
|
|
|
6029
6051
|
const skeleton = /* @__PURE__ */ jsxRuntime.jsx(chunk4ACELYEP_cjs.Skeleton, { "aria-label": `Loading preview of ${name}`, className: "h-full w-full rounded-none" });
|
|
6030
6052
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunk4ACELYEP_cjs.cn(fileViewerStyles(), className), style: { height: heightPx }, children: !src ? skeleton : kind === "image" ? /* @__PURE__ */ jsxRuntime.jsx(ImageView, { name, src, ...onDownload ? { onDownload } : {} }) : kind === "video" ? /* @__PURE__ */ jsxRuntime.jsx(VideoView, { name, src }) : /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: skeleton, children: /* @__PURE__ */ jsxRuntime.jsx(PdfView, { name, src, ...onDownload ? { onDownload } : {} }) }) });
|
|
6031
6053
|
}
|
|
6054
|
+
|
|
6055
|
+
// src/components/media-player/media-time.ts
|
|
6056
|
+
var pad = (value) => String(value).padStart(2, "0");
|
|
6057
|
+
function formatMediaTime(seconds) {
|
|
6058
|
+
const total = Math.max(0, Math.floor(Number.isFinite(seconds) ? seconds : 0));
|
|
6059
|
+
const hours = Math.floor(total / 3600);
|
|
6060
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
6061
|
+
const rest = total % 60;
|
|
6062
|
+
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(rest)}` : `${pad(minutes)}:${pad(rest)}`;
|
|
6063
|
+
}
|
|
6064
|
+
var unit = (value, name) => `${value} ${name}${value === 1 ? "" : "s"}`;
|
|
6065
|
+
function spokenMediaTime(seconds) {
|
|
6066
|
+
const total = Math.max(0, Math.floor(Number.isFinite(seconds) ? seconds : 0));
|
|
6067
|
+
const hours = Math.floor(total / 3600);
|
|
6068
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
6069
|
+
const rest = total % 60;
|
|
6070
|
+
return [
|
|
6071
|
+
hours > 0 ? unit(hours, "hour") : "",
|
|
6072
|
+
minutes > 0 ? unit(minutes, "minute") : "",
|
|
6073
|
+
rest > 0 || total === 0 ? unit(rest, "second") : ""
|
|
6074
|
+
].filter(Boolean).join(" ");
|
|
6075
|
+
}
|
|
6076
|
+
var frameStripStyles = tailwindVariants.tv({
|
|
6077
|
+
slots: {
|
|
6078
|
+
root: "flex shrink-0 gap-2 overflow-x-auto overscroll-x-contain scroll-px-[18px] px-[18px] py-3",
|
|
6079
|
+
item: [
|
|
6080
|
+
"group flex w-[120px] shrink-0 cursor-pointer flex-col gap-1.5 rounded-md p-1 outline-none",
|
|
6081
|
+
"transition-[background-color,box-shadow] duration-150",
|
|
6082
|
+
"hovered:bg-surface-2",
|
|
6083
|
+
"pressed:bg-surface-3",
|
|
6084
|
+
"data-[focus-visible]:shadow-[var(--shadow-focus)]"
|
|
6085
|
+
],
|
|
6086
|
+
thumbnail: [
|
|
6087
|
+
"relative aspect-video w-full overflow-hidden rounded-sm border border-rule bg-surface-2",
|
|
6088
|
+
"*:size-full *:object-cover",
|
|
6089
|
+
"transition-[border-color,box-shadow] duration-150",
|
|
6090
|
+
"group-data-[hovered]:border-rule-strong",
|
|
6091
|
+
"group-data-[selected]:border-ink group-data-[selected]:shadow-[inset_0_0_0_1px_var(--color-ink)]"
|
|
6092
|
+
],
|
|
6093
|
+
caption: "flex min-w-0 items-baseline gap-1.5 px-0.5",
|
|
6094
|
+
label: [
|
|
6095
|
+
"min-w-0 flex-1 truncate text-[12px] leading-4 text-ink-2",
|
|
6096
|
+
"group-data-[selected]:font-medium group-data-[selected]:text-ink"
|
|
6097
|
+
],
|
|
6098
|
+
time: "shrink-0 font-mono text-[11px] leading-4 tabular-nums text-ink-4"
|
|
6099
|
+
}
|
|
6100
|
+
});
|
|
6101
|
+
function FrameStrip({ items, selectedKey, onSelect, className, ...props }) {
|
|
6102
|
+
const styles4 = frameStripStyles();
|
|
6103
|
+
const rootRef = react.useRef(null);
|
|
6104
|
+
react.useEffect(() => {
|
|
6105
|
+
const root = rootRef.current;
|
|
6106
|
+
if (selectedKey === void 0 || !root) return;
|
|
6107
|
+
for (const element of root.querySelectorAll("[data-frame-key]")) {
|
|
6108
|
+
if (element.dataset.frameKey !== selectedKey) continue;
|
|
6109
|
+
if (typeof element.scrollIntoView === "function")
|
|
6110
|
+
element.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
6111
|
+
break;
|
|
6112
|
+
}
|
|
6113
|
+
}, [selectedKey]);
|
|
6114
|
+
if (items.length === 0) return null;
|
|
6115
|
+
const isLabelled = props["aria-label"] !== void 0 || props["aria-labelledby"] !== void 0;
|
|
6116
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6117
|
+
reactAriaComponents.ToggleButtonGroup,
|
|
6118
|
+
{
|
|
6119
|
+
...isLabelled ? {} : { "aria-label": "Frames" },
|
|
6120
|
+
...props,
|
|
6121
|
+
ref: rootRef,
|
|
6122
|
+
orientation: "horizontal",
|
|
6123
|
+
selectionMode: "single",
|
|
6124
|
+
disallowEmptySelection: true,
|
|
6125
|
+
selectedKeys: selectedKey === void 0 ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([selectedKey]),
|
|
6126
|
+
onSelectionChange: (keys) => {
|
|
6127
|
+
const [next] = Array.from(keys);
|
|
6128
|
+
if (next !== void 0) onSelect(String(next));
|
|
6129
|
+
},
|
|
6130
|
+
className: chunk4ACELYEP_cjs.cn(styles4.root(), className),
|
|
6131
|
+
children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6132
|
+
reactAriaComponents.ToggleButton,
|
|
6133
|
+
{
|
|
6134
|
+
id: item.key,
|
|
6135
|
+
"data-frame-key": item.key,
|
|
6136
|
+
"aria-label": `${item.label}, at ${formatMediaTime(item.at)}`,
|
|
6137
|
+
className: styles4.item(),
|
|
6138
|
+
children: [
|
|
6139
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles4.thumbnail(), children: item.thumbnail }),
|
|
6140
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: styles4.caption(), children: [
|
|
6141
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles4.label(), children: item.label }),
|
|
6142
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles4.time(), children: formatMediaTime(item.at) })
|
|
6143
|
+
] })
|
|
6144
|
+
]
|
|
6145
|
+
},
|
|
6146
|
+
item.key
|
|
6147
|
+
))
|
|
6148
|
+
}
|
|
6149
|
+
);
|
|
6150
|
+
}
|
|
6032
6151
|
var glowFrameStyles = tailwindVariants.tv({
|
|
6033
6152
|
slots: {
|
|
6034
6153
|
room: "glow-room flex min-h-0 flex-col",
|
|
@@ -6233,6 +6352,847 @@ function Input({
|
|
|
6233
6352
|
}
|
|
6234
6353
|
);
|
|
6235
6354
|
}
|
|
6355
|
+
var sliderStyles = tailwindVariants.tv({
|
|
6356
|
+
slots: {
|
|
6357
|
+
root: "flex w-full items-center",
|
|
6358
|
+
track: "group relative h-[18px] w-full cursor-pointer data-[disabled]:cursor-default",
|
|
6359
|
+
rail: "absolute inset-x-0 top-1/2 h-1 -translate-y-1/2 overflow-hidden rounded-pill bg-surface-3",
|
|
6360
|
+
fill: "h-full bg-ink",
|
|
6361
|
+
thumb: [
|
|
6362
|
+
"top-1/2 size-3 rounded-pill bg-ink",
|
|
6363
|
+
"shadow-[var(--shadow-rest)]",
|
|
6364
|
+
"data-[focus-visible]:shadow-[var(--shadow-focus)]",
|
|
6365
|
+
"data-[disabled]:hidden"
|
|
6366
|
+
]
|
|
6367
|
+
}
|
|
6368
|
+
});
|
|
6369
|
+
function Slider({
|
|
6370
|
+
value,
|
|
6371
|
+
onChange,
|
|
6372
|
+
onChangeEnd,
|
|
6373
|
+
valueText,
|
|
6374
|
+
className,
|
|
6375
|
+
...props
|
|
6376
|
+
}) {
|
|
6377
|
+
const styles4 = sliderStyles();
|
|
6378
|
+
const inputRef = react.useRef(null);
|
|
6379
|
+
react.useLayoutEffect(() => {
|
|
6380
|
+
if (valueText !== void 0) inputRef.current?.setAttribute("aria-valuetext", valueText);
|
|
6381
|
+
});
|
|
6382
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6383
|
+
reactAriaComponents.Slider,
|
|
6384
|
+
{
|
|
6385
|
+
...props,
|
|
6386
|
+
value,
|
|
6387
|
+
...onChange ? { onChange } : {},
|
|
6388
|
+
...onChangeEnd ? { onChangeEnd } : {},
|
|
6389
|
+
className: (renderProps) => chunk4ACELYEP_cjs.cn(styles4.root(), typeof className === "function" ? className(renderProps) : className),
|
|
6390
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactAriaComponents.SliderTrack, { className: styles4.track(), children: ({ state }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6391
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles4.rail(), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6392
|
+
"div",
|
|
6393
|
+
{
|
|
6394
|
+
className: styles4.fill(),
|
|
6395
|
+
style: { width: `${state.getThumbPercent(0) * 100}%` }
|
|
6396
|
+
}
|
|
6397
|
+
) }),
|
|
6398
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactAriaComponents.SliderThumb, { inputRef, className: styles4.thumb() })
|
|
6399
|
+
] }) })
|
|
6400
|
+
}
|
|
6401
|
+
);
|
|
6402
|
+
}
|
|
6403
|
+
|
|
6404
|
+
// src/components/media-player/media-peaks.ts
|
|
6405
|
+
var MAX_DECODE_BYTES = 50 * 1024 * 1024;
|
|
6406
|
+
var DECODE_SAMPLE_RATE = 8e3;
|
|
6407
|
+
var PEAK_BUCKETS = 4e3;
|
|
6408
|
+
var SAMPLES_PER_BUCKET = 256;
|
|
6409
|
+
function peaksFromChannels(channels, buckets = PEAK_BUCKETS) {
|
|
6410
|
+
const length = channels[0]?.length ?? 0;
|
|
6411
|
+
const peaks = new Float32Array(buckets);
|
|
6412
|
+
if (length === 0) return peaks;
|
|
6413
|
+
const stride = Math.max(1, Math.floor(length / buckets / SAMPLES_PER_BUCKET));
|
|
6414
|
+
let loudest = 0;
|
|
6415
|
+
for (let bucket = 0; bucket < buckets; bucket++) {
|
|
6416
|
+
const start = Math.floor(bucket / buckets * length);
|
|
6417
|
+
const end = Math.floor((bucket + 1) / buckets * length);
|
|
6418
|
+
let sum = 0;
|
|
6419
|
+
let count = 0;
|
|
6420
|
+
for (let index = start; index < end; index += stride) {
|
|
6421
|
+
let sample = 0;
|
|
6422
|
+
for (const channel of channels) sample = Math.max(sample, Math.abs(channel[index] ?? 0));
|
|
6423
|
+
sum += sample * sample;
|
|
6424
|
+
count++;
|
|
6425
|
+
}
|
|
6426
|
+
const level = count > 0 ? Math.sqrt(sum / count) : 0;
|
|
6427
|
+
peaks[bucket] = level;
|
|
6428
|
+
if (level > loudest) loudest = level;
|
|
6429
|
+
}
|
|
6430
|
+
if (loudest > 0)
|
|
6431
|
+
for (let bucket = 0; bucket < buckets; bucket++) peaks[bucket] = (peaks[bucket] ?? 0) / loudest;
|
|
6432
|
+
return peaks;
|
|
6433
|
+
}
|
|
6434
|
+
function maskPeaks(peaks, duration, segments) {
|
|
6435
|
+
const masked = new Float32Array(peaks.length);
|
|
6436
|
+
if (duration <= 0) return masked;
|
|
6437
|
+
for (const [start, end] of segments) {
|
|
6438
|
+
const from = Math.max(0, Math.floor(start / duration * peaks.length));
|
|
6439
|
+
const to = Math.min(peaks.length, Math.ceil(end / duration * peaks.length));
|
|
6440
|
+
for (let bucket = from; bucket < to; bucket++) masked[bucket] = peaks[bucket] ?? 0;
|
|
6441
|
+
}
|
|
6442
|
+
return masked;
|
|
6443
|
+
}
|
|
6444
|
+
async function decodePeaks(blob) {
|
|
6445
|
+
const context = new OfflineAudioContext(1, 1, DECODE_SAMPLE_RATE);
|
|
6446
|
+
const buffer = await context.decodeAudioData(await blob.arrayBuffer());
|
|
6447
|
+
const channels = Array.from(
|
|
6448
|
+
{ length: buffer.numberOfChannels },
|
|
6449
|
+
(_, index) => buffer.getChannelData(index)
|
|
6450
|
+
);
|
|
6451
|
+
return { peaks: peaksFromChannels(channels), duration: buffer.duration };
|
|
6452
|
+
}
|
|
6453
|
+
var mediaPlayerStyles = tailwindVariants.tv({
|
|
6454
|
+
slots: {
|
|
6455
|
+
root: "flex min-h-0 flex-1 flex-col",
|
|
6456
|
+
stage: "relative flex min-h-0 flex-1 flex-col"
|
|
6457
|
+
}
|
|
6458
|
+
});
|
|
6459
|
+
var MediaPlayerContext = react.createContext(null);
|
|
6460
|
+
function useMediaPlayer() {
|
|
6461
|
+
const context = react.useContext(MediaPlayerContext);
|
|
6462
|
+
if (!context) throw new Error("useMediaPlayer must be used inside a MediaPlayer");
|
|
6463
|
+
return context;
|
|
6464
|
+
}
|
|
6465
|
+
var HAVE_METADATA = 1;
|
|
6466
|
+
function MediaPlayer({
|
|
6467
|
+
source,
|
|
6468
|
+
label,
|
|
6469
|
+
kind = "audio",
|
|
6470
|
+
onError,
|
|
6471
|
+
className,
|
|
6472
|
+
children
|
|
6473
|
+
}) {
|
|
6474
|
+
const [media, setMedia] = react.useState(null);
|
|
6475
|
+
const [src, setSrc] = react.useState(null);
|
|
6476
|
+
const [loaded, setLoaded] = react.useState(null);
|
|
6477
|
+
const [isPlaying, setIsPlaying] = react.useState(false);
|
|
6478
|
+
const [currentTime, setCurrentTime] = react.useState(0);
|
|
6479
|
+
const [mediaDuration, setMediaDuration] = react.useState(0);
|
|
6480
|
+
const [playbackRate, setPlaybackRateState] = react.useState(1);
|
|
6481
|
+
const [waveformStatus, setWaveformStatus] = react.useState("decoding");
|
|
6482
|
+
const [decoded, setDecoded] = react.useState(null);
|
|
6483
|
+
const blob = "blob" in source ? source.blob : null;
|
|
6484
|
+
const url = "url" in source ? source.url : null;
|
|
6485
|
+
const givenPeaks = "url" in source ? source.peaks ?? null : null;
|
|
6486
|
+
const givenDuration = source.duration ?? null;
|
|
6487
|
+
react.useEffect(() => {
|
|
6488
|
+
if (!blob) {
|
|
6489
|
+
setSrc(url);
|
|
6490
|
+
return;
|
|
6491
|
+
}
|
|
6492
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
6493
|
+
setSrc(objectUrl);
|
|
6494
|
+
return () => URL.revokeObjectURL(objectUrl);
|
|
6495
|
+
}, [blob, url]);
|
|
6496
|
+
react.useEffect(() => {
|
|
6497
|
+
if (!blob) {
|
|
6498
|
+
if (givenPeaks && givenDuration !== null) {
|
|
6499
|
+
setDecoded({ peaks: givenPeaks, duration: givenDuration });
|
|
6500
|
+
setWaveformStatus("ready");
|
|
6501
|
+
} else {
|
|
6502
|
+
setDecoded(null);
|
|
6503
|
+
setWaveformStatus("unavailable");
|
|
6504
|
+
}
|
|
6505
|
+
return;
|
|
6506
|
+
}
|
|
6507
|
+
setDecoded(null);
|
|
6508
|
+
if (blob.size > MAX_DECODE_BYTES) {
|
|
6509
|
+
setWaveformStatus("unavailable");
|
|
6510
|
+
return;
|
|
6511
|
+
}
|
|
6512
|
+
setWaveformStatus("decoding");
|
|
6513
|
+
let isCancelled = false;
|
|
6514
|
+
decodePeaks(blob).then((result) => {
|
|
6515
|
+
if (isCancelled) return;
|
|
6516
|
+
setDecoded(result);
|
|
6517
|
+
setWaveformStatus("ready");
|
|
6518
|
+
}).catch(() => {
|
|
6519
|
+
if (!isCancelled) setWaveformStatus("unavailable");
|
|
6520
|
+
});
|
|
6521
|
+
return () => {
|
|
6522
|
+
isCancelled = true;
|
|
6523
|
+
};
|
|
6524
|
+
}, [blob, givenPeaks, givenDuration]);
|
|
6525
|
+
react.useEffect(() => {
|
|
6526
|
+
if (!media) return;
|
|
6527
|
+
const syncTime = () => setCurrentTime(media.currentTime);
|
|
6528
|
+
const syncDuration = () => setMediaDuration(Number.isFinite(media.duration) ? media.duration : 0);
|
|
6529
|
+
const syncLoaded = () => {
|
|
6530
|
+
setLoaded(media);
|
|
6531
|
+
syncDuration();
|
|
6532
|
+
};
|
|
6533
|
+
const syncEmptied = () => {
|
|
6534
|
+
setLoaded(null);
|
|
6535
|
+
setMediaDuration(0);
|
|
6536
|
+
setCurrentTime(0);
|
|
6537
|
+
};
|
|
6538
|
+
const syncPlaying = () => setIsPlaying(!media.paused && !media.ended);
|
|
6539
|
+
const handleError = () => onError?.();
|
|
6540
|
+
if (media.readyState >= HAVE_METADATA) syncLoaded();
|
|
6541
|
+
const events = [
|
|
6542
|
+
["timeupdate", syncTime],
|
|
6543
|
+
["seeked", syncTime],
|
|
6544
|
+
["loadedmetadata", syncLoaded],
|
|
6545
|
+
["durationchange", syncDuration],
|
|
6546
|
+
["emptied", syncEmptied],
|
|
6547
|
+
["play", syncPlaying],
|
|
6548
|
+
["pause", syncPlaying],
|
|
6549
|
+
["ended", syncPlaying],
|
|
6550
|
+
["error", handleError]
|
|
6551
|
+
];
|
|
6552
|
+
for (const [name, handler] of events) media.addEventListener(name, handler);
|
|
6553
|
+
return () => {
|
|
6554
|
+
for (const [name, handler] of events) media.removeEventListener(name, handler);
|
|
6555
|
+
media.pause();
|
|
6556
|
+
};
|
|
6557
|
+
}, [media, onError]);
|
|
6558
|
+
const duration = mediaDuration || givenDuration || decoded?.duration || 0;
|
|
6559
|
+
const isReady = media !== null && loaded === media;
|
|
6560
|
+
const toggle = react.useCallback(() => {
|
|
6561
|
+
if (!media) return;
|
|
6562
|
+
if (media.paused || media.ended) {
|
|
6563
|
+
void media.play().catch(() => void 0);
|
|
6564
|
+
} else {
|
|
6565
|
+
media.pause();
|
|
6566
|
+
}
|
|
6567
|
+
}, [media]);
|
|
6568
|
+
const seek = react.useCallback(
|
|
6569
|
+
(seconds) => {
|
|
6570
|
+
if (!media) return;
|
|
6571
|
+
const limit = Number.isFinite(media.duration) ? media.duration : duration > 0 ? duration : seconds;
|
|
6572
|
+
const next = Math.max(0, Math.min(limit, seconds));
|
|
6573
|
+
media.currentTime = next;
|
|
6574
|
+
setCurrentTime(next);
|
|
6575
|
+
},
|
|
6576
|
+
[media, duration]
|
|
6577
|
+
);
|
|
6578
|
+
const skip = react.useCallback(
|
|
6579
|
+
(seconds) => {
|
|
6580
|
+
if (media) seek(media.currentTime + seconds);
|
|
6581
|
+
},
|
|
6582
|
+
[media, seek]
|
|
6583
|
+
);
|
|
6584
|
+
const stop = react.useCallback(() => {
|
|
6585
|
+
media?.pause();
|
|
6586
|
+
seek(0);
|
|
6587
|
+
}, [media, seek]);
|
|
6588
|
+
const setPlaybackRate = react.useCallback(
|
|
6589
|
+
(rate) => {
|
|
6590
|
+
if (media) media.playbackRate = rate;
|
|
6591
|
+
setPlaybackRateState(rate);
|
|
6592
|
+
},
|
|
6593
|
+
[media]
|
|
6594
|
+
);
|
|
6595
|
+
const value = react.useMemo(
|
|
6596
|
+
() => ({
|
|
6597
|
+
media,
|
|
6598
|
+
mediaRef: setMedia,
|
|
6599
|
+
src,
|
|
6600
|
+
isReady,
|
|
6601
|
+
isPlaying,
|
|
6602
|
+
currentTime,
|
|
6603
|
+
duration,
|
|
6604
|
+
playbackRate,
|
|
6605
|
+
waveformStatus,
|
|
6606
|
+
decoded,
|
|
6607
|
+
toggle,
|
|
6608
|
+
stop,
|
|
6609
|
+
seek,
|
|
6610
|
+
skip,
|
|
6611
|
+
setPlaybackRate
|
|
6612
|
+
}),
|
|
6613
|
+
[
|
|
6614
|
+
media,
|
|
6615
|
+
src,
|
|
6616
|
+
isReady,
|
|
6617
|
+
isPlaying,
|
|
6618
|
+
currentTime,
|
|
6619
|
+
duration,
|
|
6620
|
+
playbackRate,
|
|
6621
|
+
waveformStatus,
|
|
6622
|
+
decoded,
|
|
6623
|
+
toggle,
|
|
6624
|
+
stop,
|
|
6625
|
+
seek,
|
|
6626
|
+
skip,
|
|
6627
|
+
setPlaybackRate
|
|
6628
|
+
]
|
|
6629
|
+
);
|
|
6630
|
+
const handleKeyDown = (event) => {
|
|
6631
|
+
if (event.code !== "Space") return;
|
|
6632
|
+
if (event.target.closest("button, input")) return;
|
|
6633
|
+
event.preventDefault();
|
|
6634
|
+
toggle();
|
|
6635
|
+
};
|
|
6636
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MediaPlayerContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6637
|
+
"section",
|
|
6638
|
+
{
|
|
6639
|
+
"aria-label": label,
|
|
6640
|
+
onKeyDown: handleKeyDown,
|
|
6641
|
+
className: chunk4ACELYEP_cjs.cn(mediaPlayerStyles().root(), className),
|
|
6642
|
+
children: [
|
|
6643
|
+
kind === "audio" ? /* @__PURE__ */ jsxRuntime.jsx("audio", { ref: setMedia, preload: "metadata", ...src ? { src } : {} }) : null,
|
|
6644
|
+
children
|
|
6645
|
+
]
|
|
6646
|
+
}
|
|
6647
|
+
) });
|
|
6648
|
+
}
|
|
6649
|
+
function MediaStage({
|
|
6650
|
+
ref,
|
|
6651
|
+
className,
|
|
6652
|
+
children
|
|
6653
|
+
}) {
|
|
6654
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: chunk4ACELYEP_cjs.cn(mediaPlayerStyles().stage(), className), children });
|
|
6655
|
+
}
|
|
6656
|
+
var mediaControlsStyles = tailwindVariants.tv({
|
|
6657
|
+
slots: {
|
|
6658
|
+
root: "shrink-0 border-t border-rule-2 px-[18px] pt-3 pb-3.5",
|
|
6659
|
+
seek: "grid grid-cols-[auto_1fr_auto] items-center gap-2.5",
|
|
6660
|
+
elapsed: "font-mono text-[12.5px] leading-none tabular-nums text-ink-2",
|
|
6661
|
+
total: "font-mono text-[12.5px] leading-none tabular-nums text-ink-4",
|
|
6662
|
+
transport: "mt-2 grid grid-cols-[1fr_auto_1fr] items-center",
|
|
6663
|
+
center: "flex items-center gap-1.5",
|
|
6664
|
+
end: "justify-self-end",
|
|
6665
|
+
text: "font-mono"
|
|
6666
|
+
}
|
|
6667
|
+
});
|
|
6668
|
+
var SKIP_SECONDS = 10;
|
|
6669
|
+
var SPEEDS = [1, 1.25, 1.5, 2];
|
|
6670
|
+
function MediaControls({ describeTime, hasSeek = true, className }) {
|
|
6671
|
+
const {
|
|
6672
|
+
isReady,
|
|
6673
|
+
isPlaying,
|
|
6674
|
+
currentTime,
|
|
6675
|
+
duration,
|
|
6676
|
+
playbackRate,
|
|
6677
|
+
toggle,
|
|
6678
|
+
stop,
|
|
6679
|
+
seek,
|
|
6680
|
+
skip,
|
|
6681
|
+
setPlaybackRate
|
|
6682
|
+
} = useMediaPlayer();
|
|
6683
|
+
const styles4 = mediaControlsStyles();
|
|
6684
|
+
const description = describeTime?.(currentTime);
|
|
6685
|
+
const valueText = `${spokenMediaTime(currentTime)} of ${spokenMediaTime(duration)}${description ? `, ${description}` : ""}`;
|
|
6686
|
+
const nextSpeed = SPEEDS[(SPEEDS.indexOf(playbackRate) + 1) % SPEEDS.length] ?? 1;
|
|
6687
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunk4ACELYEP_cjs.cn(styles4.root(), className), children: [
|
|
6688
|
+
hasSeek ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles4.seek(), children: [
|
|
6689
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles4.elapsed(), children: formatMediaTime(currentTime) }),
|
|
6690
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6691
|
+
Slider,
|
|
6692
|
+
{
|
|
6693
|
+
"aria-label": "Seek",
|
|
6694
|
+
value: currentTime,
|
|
6695
|
+
minValue: 0,
|
|
6696
|
+
maxValue: duration || 1,
|
|
6697
|
+
step: 1,
|
|
6698
|
+
isDisabled: !isReady,
|
|
6699
|
+
valueText,
|
|
6700
|
+
onChange: seek
|
|
6701
|
+
}
|
|
6702
|
+
),
|
|
6703
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles4.total(), children: duration > 0 ? formatMediaTime(duration) : "--:--" })
|
|
6704
|
+
] }) : null,
|
|
6705
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunk4ACELYEP_cjs.cn(styles4.transport(), !hasSeek && "mt-0"), children: [
|
|
6706
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6707
|
+
chunk4ACELYEP_cjs.Button,
|
|
6708
|
+
{
|
|
6709
|
+
variant: "ghost",
|
|
6710
|
+
size: "icon-md",
|
|
6711
|
+
"aria-label": "Stop",
|
|
6712
|
+
isDisabled: !isReady,
|
|
6713
|
+
onPress: stop,
|
|
6714
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, {})
|
|
6715
|
+
}
|
|
6716
|
+
) }),
|
|
6717
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles4.center(), children: [
|
|
6718
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6719
|
+
chunk4ACELYEP_cjs.Button,
|
|
6720
|
+
{
|
|
6721
|
+
variant: "ghost",
|
|
6722
|
+
size: "sm",
|
|
6723
|
+
"aria-label": `Back ${SKIP_SECONDS} seconds`,
|
|
6724
|
+
isDisabled: !isReady,
|
|
6725
|
+
onPress: () => skip(-SKIP_SECONDS),
|
|
6726
|
+
className: styles4.text(),
|
|
6727
|
+
children: [
|
|
6728
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, {}),
|
|
6729
|
+
SKIP_SECONDS
|
|
6730
|
+
]
|
|
6731
|
+
}
|
|
6732
|
+
),
|
|
6733
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6734
|
+
chunk4ACELYEP_cjs.Button,
|
|
6735
|
+
{
|
|
6736
|
+
variant: "cta",
|
|
6737
|
+
size: "icon-lg",
|
|
6738
|
+
"aria-label": isPlaying ? "Pause" : "Play",
|
|
6739
|
+
isDisabled: !isReady,
|
|
6740
|
+
onPress: toggle,
|
|
6741
|
+
children: isPlaying ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pause, { fill: "currentColor" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { fill: "currentColor" })
|
|
6742
|
+
}
|
|
6743
|
+
),
|
|
6744
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6745
|
+
chunk4ACELYEP_cjs.Button,
|
|
6746
|
+
{
|
|
6747
|
+
variant: "ghost",
|
|
6748
|
+
size: "sm",
|
|
6749
|
+
"aria-label": `Forward ${SKIP_SECONDS} seconds`,
|
|
6750
|
+
isDisabled: !isReady,
|
|
6751
|
+
onPress: () => skip(SKIP_SECONDS),
|
|
6752
|
+
className: styles4.text(),
|
|
6753
|
+
children: [
|
|
6754
|
+
SKIP_SECONDS,
|
|
6755
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCw, {})
|
|
6756
|
+
]
|
|
6757
|
+
}
|
|
6758
|
+
)
|
|
6759
|
+
] }),
|
|
6760
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles4.end(), children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6761
|
+
chunk4ACELYEP_cjs.Button,
|
|
6762
|
+
{
|
|
6763
|
+
variant: "ghost",
|
|
6764
|
+
size: "sm",
|
|
6765
|
+
"aria-label": `Playback speed ${playbackRate} times`,
|
|
6766
|
+
isDisabled: !isReady,
|
|
6767
|
+
onPress: () => setPlaybackRate(nextSpeed),
|
|
6768
|
+
className: styles4.text(),
|
|
6769
|
+
children: [
|
|
6770
|
+
playbackRate,
|
|
6771
|
+
"\xD7"
|
|
6772
|
+
]
|
|
6773
|
+
}
|
|
6774
|
+
) })
|
|
6775
|
+
] })
|
|
6776
|
+
] });
|
|
6777
|
+
}
|
|
6778
|
+
var mediaMarkersStyles = tailwindVariants.tv({
|
|
6779
|
+
slots: {
|
|
6780
|
+
root: "flex shrink-0 flex-col gap-[3px]",
|
|
6781
|
+
heading: "flex min-w-0 shrink-0 items-center gap-1.5 text-[12px] leading-4 text-ink-3",
|
|
6782
|
+
dot: "size-[5px] shrink-0 rounded-pill",
|
|
6783
|
+
current: "min-w-0 truncate font-medium text-ink",
|
|
6784
|
+
count: "ml-auto shrink-0 font-mono text-[11px] text-ink-4",
|
|
6785
|
+
track: "relative h-3",
|
|
6786
|
+
marker: [
|
|
6787
|
+
"absolute top-0.5 h-2 min-w-1.5 cursor-pointer rounded-sm border p-0 outline-none",
|
|
6788
|
+
"border-[color-mix(in_oklab,var(--color-accent)_45%,var(--color-rule))]",
|
|
6789
|
+
"bg-[color-mix(in_oklab,var(--color-accent)_14%,var(--color-surface))]",
|
|
6790
|
+
"transition-colors duration-150",
|
|
6791
|
+
"hovered:border-accent hovered:bg-accent",
|
|
6792
|
+
"data-[current]:border-accent data-[current]:bg-accent",
|
|
6793
|
+
"data-[focus-visible]:shadow-[var(--shadow-focus)]",
|
|
6794
|
+
// The bar is thin; the press target isn't.
|
|
6795
|
+
"after:absolute after:inset-x-0 after:-inset-y-2 after:content-['']"
|
|
6796
|
+
]
|
|
6797
|
+
}
|
|
6798
|
+
});
|
|
6799
|
+
function currentMediaMarkers(markers, time) {
|
|
6800
|
+
return markers.filter((marker) => time >= marker.start && time < marker.end);
|
|
6801
|
+
}
|
|
6802
|
+
function MediaMarkerTrack({
|
|
6803
|
+
markers,
|
|
6804
|
+
label = "Markers",
|
|
6805
|
+
currentTime,
|
|
6806
|
+
duration,
|
|
6807
|
+
onSeek,
|
|
6808
|
+
className
|
|
6809
|
+
}) {
|
|
6810
|
+
const styles4 = mediaMarkersStyles();
|
|
6811
|
+
const current = currentMediaMarkers(markers, currentTime);
|
|
6812
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunk4ACELYEP_cjs.cn(styles4.root(), className), children: [
|
|
6813
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles4.heading(), children: [
|
|
6814
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: styles4.dot() }),
|
|
6815
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: label }),
|
|
6816
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles4.current(), children: current.map((marker) => marker.label).join(" \xB7 ") }),
|
|
6817
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles4.count(), children: markers.length })
|
|
6818
|
+
] }),
|
|
6819
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles4.track(), children: markers.map((marker) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6820
|
+
reactAriaComponents.Button,
|
|
6821
|
+
{
|
|
6822
|
+
"data-media-marker": marker.key,
|
|
6823
|
+
"data-current": current.includes(marker) || void 0,
|
|
6824
|
+
"aria-label": `${marker.label}, at ${formatMediaTime(marker.start)}`,
|
|
6825
|
+
onPress: () => onSeek(marker.start),
|
|
6826
|
+
className: styles4.marker(),
|
|
6827
|
+
style: duration > 0 ? {
|
|
6828
|
+
left: `${marker.start / duration * 100}%`,
|
|
6829
|
+
width: `${(marker.end - marker.start) / duration * 100}%`
|
|
6830
|
+
} : { display: "none" }
|
|
6831
|
+
},
|
|
6832
|
+
marker.key
|
|
6833
|
+
)) })
|
|
6834
|
+
] });
|
|
6835
|
+
}
|
|
6836
|
+
var mediaTimelineStyles = tailwindVariants.tv({
|
|
6837
|
+
slots: {
|
|
6838
|
+
root: "flex shrink-0 flex-col gap-1 px-[18px] pt-2 pb-3",
|
|
6839
|
+
markers: "pt-1"
|
|
6840
|
+
}
|
|
6841
|
+
});
|
|
6842
|
+
function MediaTimeline({
|
|
6843
|
+
markers = [],
|
|
6844
|
+
markersLabel = "Markers",
|
|
6845
|
+
label = "Timeline",
|
|
6846
|
+
durationOverride,
|
|
6847
|
+
className
|
|
6848
|
+
}) {
|
|
6849
|
+
const { currentTime, duration: playerDuration, isReady, seek } = useMediaPlayer();
|
|
6850
|
+
const duration = durationOverride ?? playerDuration;
|
|
6851
|
+
const max = duration > 0 ? duration : 1;
|
|
6852
|
+
const styles4 = mediaTimelineStyles();
|
|
6853
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunk4ACELYEP_cjs.cn(styles4.root(), className), children: [
|
|
6854
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6855
|
+
Slider,
|
|
6856
|
+
{
|
|
6857
|
+
"aria-label": label,
|
|
6858
|
+
value: Math.min(currentTime, max),
|
|
6859
|
+
minValue: 0,
|
|
6860
|
+
maxValue: max,
|
|
6861
|
+
step: 1,
|
|
6862
|
+
isDisabled: !isReady,
|
|
6863
|
+
valueText: `${spokenMediaTime(currentTime)} of ${spokenMediaTime(duration)}`,
|
|
6864
|
+
onChange: seek
|
|
6865
|
+
}
|
|
6866
|
+
),
|
|
6867
|
+
markers.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6868
|
+
MediaMarkerTrack,
|
|
6869
|
+
{
|
|
6870
|
+
markers,
|
|
6871
|
+
label: markersLabel,
|
|
6872
|
+
currentTime,
|
|
6873
|
+
duration,
|
|
6874
|
+
onSeek: seek,
|
|
6875
|
+
className: styles4.markers()
|
|
6876
|
+
}
|
|
6877
|
+
) : null
|
|
6878
|
+
] });
|
|
6879
|
+
}
|
|
6880
|
+
var mediaVideoStageStyles = tailwindVariants.tv({
|
|
6881
|
+
slots: {
|
|
6882
|
+
root: "items-center justify-center overflow-hidden",
|
|
6883
|
+
box: "relative shrink-0 bg-ink",
|
|
6884
|
+
video: "block size-full",
|
|
6885
|
+
overlay: "pointer-events-none absolute inset-0"
|
|
6886
|
+
}
|
|
6887
|
+
});
|
|
6888
|
+
var DEFAULT_ASPECT = 16 / 9;
|
|
6889
|
+
function fitMediaBox(width, height, aspect) {
|
|
6890
|
+
if (width <= 0 || aspect <= 0) return { width: 0, height: 0 };
|
|
6891
|
+
if (height <= 0 || width / height <= aspect) return { width, height: width / aspect };
|
|
6892
|
+
return { width: height * aspect, height };
|
|
6893
|
+
}
|
|
6894
|
+
function MediaVideoStage({ children, className }) {
|
|
6895
|
+
const { src, mediaRef } = useMediaPlayer();
|
|
6896
|
+
const styles4 = mediaVideoStageStyles();
|
|
6897
|
+
const stageRef = react.useRef(null);
|
|
6898
|
+
const [video, setVideo] = react.useState(null);
|
|
6899
|
+
const [aspect, setAspect] = react.useState(DEFAULT_ASPECT);
|
|
6900
|
+
const [box, setBox] = react.useState({ width: 0, height: 0 });
|
|
6901
|
+
const registerVideo = react.useCallback(
|
|
6902
|
+
(element) => {
|
|
6903
|
+
setVideo(element);
|
|
6904
|
+
mediaRef(element);
|
|
6905
|
+
},
|
|
6906
|
+
[mediaRef]
|
|
6907
|
+
);
|
|
6908
|
+
react.useEffect(() => {
|
|
6909
|
+
if (!video) return;
|
|
6910
|
+
const sync2 = () => {
|
|
6911
|
+
if (video.videoWidth > 0 && video.videoHeight > 0)
|
|
6912
|
+
setAspect(video.videoWidth / video.videoHeight);
|
|
6913
|
+
};
|
|
6914
|
+
sync2();
|
|
6915
|
+
video.addEventListener("loadedmetadata", sync2);
|
|
6916
|
+
video.addEventListener("resize", sync2);
|
|
6917
|
+
return () => {
|
|
6918
|
+
video.removeEventListener("loadedmetadata", sync2);
|
|
6919
|
+
video.removeEventListener("resize", sync2);
|
|
6920
|
+
};
|
|
6921
|
+
}, [video]);
|
|
6922
|
+
react.useEffect(() => {
|
|
6923
|
+
const stage = stageRef.current;
|
|
6924
|
+
if (!stage) return;
|
|
6925
|
+
const observer = new ResizeObserver((entries) => {
|
|
6926
|
+
const rect = entries[0]?.contentRect;
|
|
6927
|
+
if (!rect) return;
|
|
6928
|
+
const next = fitMediaBox(rect.width, rect.height, aspect);
|
|
6929
|
+
setBox(
|
|
6930
|
+
(previous) => previous.width === next.width && previous.height === next.height ? previous : next
|
|
6931
|
+
);
|
|
6932
|
+
});
|
|
6933
|
+
observer.observe(stage);
|
|
6934
|
+
return () => observer.disconnect();
|
|
6935
|
+
}, [aspect]);
|
|
6936
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MediaStage, { ref: stageRef, className: chunk4ACELYEP_cjs.cn(styles4.root(), className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles4.box(), style: { width: box.width, height: box.height }, children: [
|
|
6937
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6938
|
+
"video",
|
|
6939
|
+
{
|
|
6940
|
+
ref: registerVideo,
|
|
6941
|
+
playsInline: true,
|
|
6942
|
+
preload: "metadata",
|
|
6943
|
+
className: styles4.video(),
|
|
6944
|
+
...src ? { src } : {}
|
|
6945
|
+
}
|
|
6946
|
+
),
|
|
6947
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles4.overlay(), children })
|
|
6948
|
+
] }) });
|
|
6949
|
+
}
|
|
6950
|
+
var mediaWaveformStyles = tailwindVariants.tv({
|
|
6951
|
+
slots: {
|
|
6952
|
+
root: "relative flex min-h-0 flex-1 cursor-pointer touch-none flex-col justify-center gap-2.5 px-[18px] py-3.5",
|
|
6953
|
+
lane: "flex min-h-[34px] max-h-24 flex-1 basis-0 flex-col gap-0.5",
|
|
6954
|
+
laneName: "flex min-w-0 shrink-0 items-center gap-1.5 text-[12px] leading-4 text-ink-3",
|
|
6955
|
+
laneDot: "size-[5px] shrink-0 rounded-pill",
|
|
6956
|
+
laneMeta: "ml-auto shrink-0 font-mono text-[11px] text-ink-4",
|
|
6957
|
+
bars: [
|
|
6958
|
+
"relative min-h-0 flex-1",
|
|
6959
|
+
"before:absolute before:inset-x-0 before:top-1/2 before:h-px before:bg-rule-2 before:content-['']"
|
|
6960
|
+
],
|
|
6961
|
+
canvas: "absolute inset-0",
|
|
6962
|
+
playhead: "pointer-events-none absolute inset-y-0 z-10 w-px bg-ink",
|
|
6963
|
+
hover: "pointer-events-none absolute inset-y-0 z-10 w-px bg-ink-4",
|
|
6964
|
+
chip: [
|
|
6965
|
+
"pointer-events-none absolute top-2 z-20 -translate-x-1/2 whitespace-nowrap rounded-sm bg-ink px-1.5 py-1",
|
|
6966
|
+
"font-mono text-[11px] leading-none text-surface"
|
|
6967
|
+
],
|
|
6968
|
+
status: "pointer-events-none px-[18px] pb-3 text-[12px] text-ink-3",
|
|
6969
|
+
markers: "border-t border-rule-2 pt-2"
|
|
6970
|
+
},
|
|
6971
|
+
variants: {
|
|
6972
|
+
isActive: {
|
|
6973
|
+
true: { laneName: "font-medium text-ink", laneDot: "bg-ink" }
|
|
6974
|
+
},
|
|
6975
|
+
isSeekable: {
|
|
6976
|
+
false: { root: "cursor-default" }
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6979
|
+
});
|
|
6980
|
+
var WHOLE_RECORDING = [{ key: "all", label: null, segments: null }];
|
|
6981
|
+
var isWithin = (segments, time) => segments?.some(([start, end]) => time >= start && time < end) ?? false;
|
|
6982
|
+
function useWaveColors() {
|
|
6983
|
+
const [colors, setColors] = react.useState(() => readColors());
|
|
6984
|
+
react.useEffect(() => {
|
|
6985
|
+
const observer = new MutationObserver(() => setColors(readColors()));
|
|
6986
|
+
observer.observe(document.documentElement, {
|
|
6987
|
+
attributes: true,
|
|
6988
|
+
attributeFilter: ["data-theme"]
|
|
6989
|
+
});
|
|
6990
|
+
return () => observer.disconnect();
|
|
6991
|
+
}, []);
|
|
6992
|
+
return colors;
|
|
6993
|
+
}
|
|
6994
|
+
function readColors() {
|
|
6995
|
+
const style = getComputedStyle(document.documentElement);
|
|
6996
|
+
return {
|
|
6997
|
+
wave: style.getPropertyValue("--color-rule-strong").trim() || "gray",
|
|
6998
|
+
progress: style.getPropertyValue("--color-ink").trim() || "black"
|
|
6999
|
+
};
|
|
7000
|
+
}
|
|
7001
|
+
var LaneBars = react.memo(function LaneBars2({
|
|
7002
|
+
media,
|
|
7003
|
+
peaks,
|
|
7004
|
+
duration,
|
|
7005
|
+
wave,
|
|
7006
|
+
progress,
|
|
7007
|
+
onDrawFailed
|
|
7008
|
+
}) {
|
|
7009
|
+
const containerRef = react.useRef(null);
|
|
7010
|
+
const instanceRef = react.useRef(null);
|
|
7011
|
+
const colorsRef = react.useRef({ wave, progress });
|
|
7012
|
+
colorsRef.current = { wave, progress };
|
|
7013
|
+
react.useEffect(() => {
|
|
7014
|
+
const container = containerRef.current;
|
|
7015
|
+
if (!container) return;
|
|
7016
|
+
let isCancelled = false;
|
|
7017
|
+
let instance = null;
|
|
7018
|
+
let observer = null;
|
|
7019
|
+
import('wavesurfer.js').then(({ default: WaveSurferClass }) => {
|
|
7020
|
+
if (isCancelled) return;
|
|
7021
|
+
instance = WaveSurferClass.create({
|
|
7022
|
+
container,
|
|
7023
|
+
media,
|
|
7024
|
+
peaks: [peaks],
|
|
7025
|
+
duration,
|
|
7026
|
+
height: container.clientHeight || 1,
|
|
7027
|
+
barWidth: 2,
|
|
7028
|
+
barGap: 1,
|
|
7029
|
+
barRadius: 1,
|
|
7030
|
+
cursorWidth: 0,
|
|
7031
|
+
interact: false,
|
|
7032
|
+
normalize: false,
|
|
7033
|
+
autoScroll: false,
|
|
7034
|
+
hideScrollbar: true,
|
|
7035
|
+
// A new instance otherwise resets the shared element to its own default.
|
|
7036
|
+
audioRate: media.playbackRate,
|
|
7037
|
+
waveColor: colorsRef.current.wave,
|
|
7038
|
+
progressColor: colorsRef.current.progress
|
|
7039
|
+
});
|
|
7040
|
+
instanceRef.current = instance;
|
|
7041
|
+
let lastHeight = container.clientHeight;
|
|
7042
|
+
observer = new ResizeObserver(() => {
|
|
7043
|
+
const height = container.clientHeight;
|
|
7044
|
+
if (height > 0 && height !== lastHeight) {
|
|
7045
|
+
lastHeight = height;
|
|
7046
|
+
instance?.setOptions({ height });
|
|
7047
|
+
}
|
|
7048
|
+
});
|
|
7049
|
+
observer.observe(container);
|
|
7050
|
+
}).catch(() => {
|
|
7051
|
+
if (!isCancelled) onDrawFailed();
|
|
7052
|
+
});
|
|
7053
|
+
return () => {
|
|
7054
|
+
isCancelled = true;
|
|
7055
|
+
observer?.disconnect();
|
|
7056
|
+
instance?.destroy();
|
|
7057
|
+
instanceRef.current = null;
|
|
7058
|
+
};
|
|
7059
|
+
}, [media, peaks, duration, onDrawFailed]);
|
|
7060
|
+
react.useEffect(() => {
|
|
7061
|
+
instanceRef.current?.setOptions({ waveColor: wave, progressColor: progress });
|
|
7062
|
+
}, [wave, progress]);
|
|
7063
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: mediaWaveformStyles().canvas(), "aria-hidden": true });
|
|
7064
|
+
});
|
|
7065
|
+
function MediaWaveform({
|
|
7066
|
+
lanes: lanesProp,
|
|
7067
|
+
markers = [],
|
|
7068
|
+
markersLabel = "Markers",
|
|
7069
|
+
className
|
|
7070
|
+
}) {
|
|
7071
|
+
const { media, decoded, duration, currentTime, waveformStatus, isReady, isPlaying, seek } = useMediaPlayer();
|
|
7072
|
+
const lanes = lanesProp && lanesProp.length > 0 ? lanesProp : WHOLE_RECORDING;
|
|
7073
|
+
const colors = useWaveColors();
|
|
7074
|
+
const styles4 = mediaWaveformStyles({ isSeekable: isReady });
|
|
7075
|
+
const rootRef = react.useRef(null);
|
|
7076
|
+
const playheadRef = react.useRef(null);
|
|
7077
|
+
const [hover, setHover] = react.useState(null);
|
|
7078
|
+
const [isDrawFailed, setIsDrawFailed] = react.useState(false);
|
|
7079
|
+
const handleDrawFailed = react.useCallback(() => setIsDrawFailed(true), []);
|
|
7080
|
+
const lanePeaks = react.useMemo(
|
|
7081
|
+
() => lanes.map(
|
|
7082
|
+
(lane) => decoded && lane.segments ? maskPeaks(decoded.peaks, decoded.duration, lane.segments) : decoded?.peaks ?? null
|
|
7083
|
+
),
|
|
7084
|
+
[lanes, decoded]
|
|
7085
|
+
);
|
|
7086
|
+
react.useEffect(() => {
|
|
7087
|
+
const playhead = playheadRef.current;
|
|
7088
|
+
if (!media || !playhead) return;
|
|
7089
|
+
let frame = 0;
|
|
7090
|
+
const place = () => {
|
|
7091
|
+
const position = duration > 0 ? Math.min(1, media.currentTime / duration) : 0;
|
|
7092
|
+
playhead.style.left = `calc(18px + (100% - 36px) * ${position})`;
|
|
7093
|
+
playhead.style.visibility = position > 0 ? "visible" : "hidden";
|
|
7094
|
+
};
|
|
7095
|
+
const loop = () => {
|
|
7096
|
+
place();
|
|
7097
|
+
frame = requestAnimationFrame(loop);
|
|
7098
|
+
};
|
|
7099
|
+
place();
|
|
7100
|
+
if (isPlaying) frame = requestAnimationFrame(loop);
|
|
7101
|
+
media.addEventListener("seeked", place);
|
|
7102
|
+
return () => {
|
|
7103
|
+
cancelAnimationFrame(frame);
|
|
7104
|
+
media.removeEventListener("seeked", place);
|
|
7105
|
+
};
|
|
7106
|
+
}, [media, duration, isPlaying]);
|
|
7107
|
+
const timeAt = (clientX) => {
|
|
7108
|
+
const rect = rootRef.current?.getBoundingClientRect();
|
|
7109
|
+
if (!rect || duration <= 0) return 0;
|
|
7110
|
+
return (Math.max(0, Math.min(1, (clientX - rect.left - 18) / (rect.width - 36))) || 0) * duration;
|
|
7111
|
+
};
|
|
7112
|
+
const handlePointerDown = (event) => {
|
|
7113
|
+
if (!isReady || event.target.closest("[data-media-marker]")) return;
|
|
7114
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
7115
|
+
seek(timeAt(event.clientX));
|
|
7116
|
+
};
|
|
7117
|
+
const handlePointerMove = (event) => {
|
|
7118
|
+
if (!isReady) return;
|
|
7119
|
+
const time = timeAt(event.clientX);
|
|
7120
|
+
if (event.buttons > 0 && event.currentTarget.hasPointerCapture(event.pointerId)) seek(time);
|
|
7121
|
+
const target = event.target;
|
|
7122
|
+
const markerKey = target.closest("[data-media-marker]")?.dataset.mediaMarker;
|
|
7123
|
+
const laneKey = target.closest("[data-media-lane]")?.dataset.mediaLane;
|
|
7124
|
+
const marker = markers.find((entry) => entry.key === markerKey);
|
|
7125
|
+
const lane = lanes.find((entry) => entry.key === laneKey);
|
|
7126
|
+
const name = marker?.label ?? (lane?.label && isWithin(lane.segments, time) ? lane.label : null);
|
|
7127
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
7128
|
+
setHover({
|
|
7129
|
+
x: event.clientX - rect.left,
|
|
7130
|
+
text: name ? `${name} \xB7 ${formatMediaTime(time)}` : formatMediaTime(time)
|
|
7131
|
+
});
|
|
7132
|
+
};
|
|
7133
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7134
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7135
|
+
"div",
|
|
7136
|
+
{
|
|
7137
|
+
ref: rootRef,
|
|
7138
|
+
onPointerDown: handlePointerDown,
|
|
7139
|
+
onPointerMove: handlePointerMove,
|
|
7140
|
+
onPointerLeave: () => setHover(null),
|
|
7141
|
+
className: chunk4ACELYEP_cjs.cn(styles4.root(), className),
|
|
7142
|
+
children: [
|
|
7143
|
+
lanes.map((lane, index) => {
|
|
7144
|
+
const isActive = lane.segments !== null && isWithin(lane.segments, currentTime);
|
|
7145
|
+
const peaks = lanePeaks[index];
|
|
7146
|
+
const laneStyles = mediaWaveformStyles({ isActive });
|
|
7147
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-media-lane": lane.key, className: styles4.lane(), children: [
|
|
7148
|
+
lane.label !== null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: laneStyles.laneName(), "data-active": isActive || void 0, children: [
|
|
7149
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: laneStyles.laneDot() }),
|
|
7150
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: lane.label }),
|
|
7151
|
+
lane.meta ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles4.laneMeta(), children: lane.meta }) : null
|
|
7152
|
+
] }) : null,
|
|
7153
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles4.bars(), children: media && peaks && decoded ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7154
|
+
LaneBars,
|
|
7155
|
+
{
|
|
7156
|
+
media,
|
|
7157
|
+
peaks,
|
|
7158
|
+
duration: decoded.duration,
|
|
7159
|
+
wave: colors.wave,
|
|
7160
|
+
progress: colors.progress,
|
|
7161
|
+
onDrawFailed: handleDrawFailed
|
|
7162
|
+
}
|
|
7163
|
+
) : null })
|
|
7164
|
+
] }, lane.key);
|
|
7165
|
+
}),
|
|
7166
|
+
markers.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7167
|
+
MediaMarkerTrack,
|
|
7168
|
+
{
|
|
7169
|
+
markers,
|
|
7170
|
+
label: markersLabel,
|
|
7171
|
+
currentTime,
|
|
7172
|
+
duration,
|
|
7173
|
+
onSeek: seek,
|
|
7174
|
+
className: styles4.markers()
|
|
7175
|
+
}
|
|
7176
|
+
) : null,
|
|
7177
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: playheadRef, className: styles4.playhead() }),
|
|
7178
|
+
hover ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7179
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles4.hover(), style: { left: hover.x } }),
|
|
7180
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7181
|
+
"span",
|
|
7182
|
+
{
|
|
7183
|
+
className: styles4.chip(),
|
|
7184
|
+
style: { left: `clamp(70px, ${hover.x}px, calc(100% - 70px))` },
|
|
7185
|
+
children: hover.text
|
|
7186
|
+
}
|
|
7187
|
+
)
|
|
7188
|
+
] }) : null
|
|
7189
|
+
]
|
|
7190
|
+
}
|
|
7191
|
+
),
|
|
7192
|
+
waveformStatus === "decoding" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: styles4.status(), children: "Drawing waveform" }) : null,
|
|
7193
|
+
waveformStatus === "unavailable" || isDrawFailed ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: styles4.status(), children: "Waveform isn\u2019t available. Use the slider to seek." }) : null
|
|
7194
|
+
] });
|
|
7195
|
+
}
|
|
6236
7196
|
var MenuTrigger = reactAriaComponents.MenuTrigger;
|
|
6237
7197
|
var SubmenuTrigger = reactAriaComponents.SubmenuTrigger;
|
|
6238
7198
|
function MenuPopover({ className, offset = 4, placement = "bottom start", ...props }) {
|
|
@@ -9466,6 +10426,7 @@ exports.FileViewer = FileViewer;
|
|
|
9466
10426
|
exports.FileViewerFallbackCard = FileViewerFallbackCard;
|
|
9467
10427
|
exports.FilterRow = FilterRow;
|
|
9468
10428
|
exports.FilterValueInput = FilterValueInput;
|
|
10429
|
+
exports.FrameStrip = FrameStrip;
|
|
9469
10430
|
exports.GlowFrame = GlowFrame;
|
|
9470
10431
|
exports.IconPicker = IconPicker;
|
|
9471
10432
|
exports.ImageAttachment = ImageAttachment;
|
|
@@ -9474,6 +10435,14 @@ exports.Input = Input;
|
|
|
9474
10435
|
exports.Kbd = Kbd;
|
|
9475
10436
|
exports.LinkCell = LinkCell;
|
|
9476
10437
|
exports.MAX_COMPOSER_ATTACHMENT_BYTES = MAX_COMPOSER_ATTACHMENT_BYTES;
|
|
10438
|
+
exports.MAX_DECODE_BYTES = MAX_DECODE_BYTES;
|
|
10439
|
+
exports.MediaControls = MediaControls;
|
|
10440
|
+
exports.MediaMarkerTrack = MediaMarkerTrack;
|
|
10441
|
+
exports.MediaPlayer = MediaPlayer;
|
|
10442
|
+
exports.MediaStage = MediaStage;
|
|
10443
|
+
exports.MediaTimeline = MediaTimeline;
|
|
10444
|
+
exports.MediaVideoStage = MediaVideoStage;
|
|
10445
|
+
exports.MediaWaveform = MediaWaveform;
|
|
9477
10446
|
exports.Menu = Menu;
|
|
9478
10447
|
exports.MenuItem = MenuItem;
|
|
9479
10448
|
exports.MenuPopover = MenuPopover;
|
|
@@ -9521,6 +10490,7 @@ exports.SELECTION_COLUMN_ID = SELECTION_COLUMN_ID;
|
|
|
9521
10490
|
exports.SegmentedControl = SegmentedControl;
|
|
9522
10491
|
exports.Select = Select;
|
|
9523
10492
|
exports.Separator = Separator;
|
|
10493
|
+
exports.Slider = Slider;
|
|
9524
10494
|
exports.Stepper = Stepper;
|
|
9525
10495
|
exports.StepperStep = StepperStep;
|
|
9526
10496
|
exports.SubmenuTrigger = SubmenuTrigger;
|
|
@@ -9566,6 +10536,8 @@ exports.countLeaves = countLeaves;
|
|
|
9566
10536
|
exports.createConversationBriefClient = createConversationBriefClient;
|
|
9567
10537
|
exports.createDraftMessageClient = createDraftMessageClient;
|
|
9568
10538
|
exports.createToastQueue = createToastQueue;
|
|
10539
|
+
exports.currentMediaMarkers = currentMediaMarkers;
|
|
10540
|
+
exports.decodePeaks = decodePeaks;
|
|
9569
10541
|
exports.defaultValueFor = defaultValueFor;
|
|
9570
10542
|
exports.draftMessageCardStyles = draftMessageCardStyles;
|
|
9571
10543
|
exports.draftMessageComposerStyles = draftMessageComposerStyles;
|
|
@@ -9578,8 +10550,11 @@ exports.fieldLabelStyles = fieldLabelStyles;
|
|
|
9578
10550
|
exports.fileViewerFallbackCardStyles = fileViewerFallbackCardStyles;
|
|
9579
10551
|
exports.fileViewerStyles = fileViewerStyles;
|
|
9580
10552
|
exports.fitBadgeCount = fitBadgeCount;
|
|
10553
|
+
exports.fitMediaBox = fitMediaBox;
|
|
9581
10554
|
exports.formatFileSize = formatFileSize;
|
|
10555
|
+
exports.formatMediaTime = formatMediaTime;
|
|
9582
10556
|
exports.formatValue = formatValue;
|
|
10557
|
+
exports.frameStripStyles = frameStripStyles;
|
|
9583
10558
|
exports.getPlatform = getPlatform;
|
|
9584
10559
|
exports.glowFrameStyles = glowFrameStyles;
|
|
9585
10560
|
exports.iconForContentType = iconForContentType;
|
|
@@ -9592,6 +10567,13 @@ exports.isEditableTarget = isEditableTarget;
|
|
|
9592
10567
|
exports.isPreviewableImageName = isPreviewableImageName;
|
|
9593
10568
|
exports.joinFieldLabel = joinFieldLabel;
|
|
9594
10569
|
exports.makeActionsColumn = makeActionsColumn;
|
|
10570
|
+
exports.maskPeaks = maskPeaks;
|
|
10571
|
+
exports.mediaControlsStyles = mediaControlsStyles;
|
|
10572
|
+
exports.mediaMarkersStyles = mediaMarkersStyles;
|
|
10573
|
+
exports.mediaPlayerStyles = mediaPlayerStyles;
|
|
10574
|
+
exports.mediaTimelineStyles = mediaTimelineStyles;
|
|
10575
|
+
exports.mediaVideoStageStyles = mediaVideoStageStyles;
|
|
10576
|
+
exports.mediaWaveformStyles = mediaWaveformStyles;
|
|
9595
10577
|
exports.messageComposerStyles = messageComposerStyles;
|
|
9596
10578
|
exports.namesOf = namesOf;
|
|
9597
10579
|
exports.navItemStyles = navItemStyles;
|
|
@@ -9599,6 +10581,7 @@ exports.normalizeHexColor = normalizeHexColor;
|
|
|
9599
10581
|
exports.operatorById = operatorById;
|
|
9600
10582
|
exports.operatorsForType = operatorsForType;
|
|
9601
10583
|
exports.parseCombo = parseCombo;
|
|
10584
|
+
exports.peaksFromChannels = peaksFromChannels;
|
|
9602
10585
|
exports.progressBarStyles = progressBarStyles;
|
|
9603
10586
|
exports.progressRingStyles = progressRingStyles;
|
|
9604
10587
|
exports.publishDraftMessageOutcome = publishDraftMessageOutcome;
|
|
@@ -9614,6 +10597,8 @@ exports.resolveLucideIcon = resolveLucideIcon;
|
|
|
9614
10597
|
exports.resolveLucideIconName = resolveLucideIconName;
|
|
9615
10598
|
exports.segmentedControlStyles = segmentedControlStyles;
|
|
9616
10599
|
exports.setJoiner = setJoiner;
|
|
10600
|
+
exports.sliderStyles = sliderStyles;
|
|
10601
|
+
exports.spokenMediaTime = spokenMediaTime;
|
|
9617
10602
|
exports.stepperStyles = stepperStyles;
|
|
9618
10603
|
exports.subscribeDraftMessageOutcome = subscribeDraftMessageOutcome;
|
|
9619
10604
|
exports.summaryFor = summaryFor;
|
|
@@ -9634,6 +10619,7 @@ exports.useDraftMessageActions = useDraftMessageActions;
|
|
|
9634
10619
|
exports.useDraftMessageOutcome = useDraftMessageOutcome;
|
|
9635
10620
|
exports.useEscapeKey = useEscapeKey;
|
|
9636
10621
|
exports.useInfiniteScrollSentinel = useInfiniteScrollSentinel;
|
|
10622
|
+
exports.useMediaPlayer = useMediaPlayer;
|
|
9637
10623
|
exports.useRenderChipItem = useRenderChipItem;
|
|
9638
10624
|
exports.useRenderValueInput = useRenderValueInput;
|
|
9639
10625
|
exports.useShowDetails = useShowDetails;
|