@fanvue/ui 3.12.1 → 3.14.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.
Files changed (66) hide show
  1. package/dist/cjs/components/Accordion/AccordionTrigger.cjs +11 -2
  2. package/dist/cjs/components/Accordion/AccordionTrigger.cjs.map +1 -1
  3. package/dist/cjs/components/AudioPlayer/AudioPlayer.cjs +357 -0
  4. package/dist/cjs/components/AudioPlayer/AudioPlayer.cjs.map +1 -0
  5. package/dist/cjs/components/Avatar/Avatar.cjs +2 -2
  6. package/dist/cjs/components/Avatar/Avatar.cjs.map +1 -1
  7. package/dist/cjs/components/Dialog/Dialog.cjs +2 -1
  8. package/dist/cjs/components/Dialog/Dialog.cjs.map +1 -1
  9. package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs +25 -2
  10. package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  11. package/dist/cjs/components/FloatingActionButton/FloatingActionButton.cjs +59 -0
  12. package/dist/cjs/components/FloatingActionButton/FloatingActionButton.cjs.map +1 -0
  13. package/dist/cjs/components/Icons/GifIcon.cjs +12 -36
  14. package/dist/cjs/components/Icons/GifIcon.cjs.map +1 -1
  15. package/dist/cjs/components/Icons/GridViewIcon.cjs +50 -0
  16. package/dist/cjs/components/Icons/GridViewIcon.cjs.map +1 -0
  17. package/dist/cjs/components/Icons/ListViewIcon.cjs +50 -0
  18. package/dist/cjs/components/Icons/ListViewIcon.cjs.map +1 -0
  19. package/dist/cjs/components/ProgressBar/ProgressBar.cjs +30 -5
  20. package/dist/cjs/components/ProgressBar/ProgressBar.cjs.map +1 -1
  21. package/dist/cjs/components/ProgressBar/ProgressBarItem.cjs +49 -0
  22. package/dist/cjs/components/ProgressBar/ProgressBarItem.cjs.map +1 -0
  23. package/dist/cjs/components/ProgressBar/ProgressBarSteps.cjs +66 -0
  24. package/dist/cjs/components/ProgressBar/ProgressBarSteps.cjs.map +1 -0
  25. package/dist/cjs/components/SegmentedControl/SegmentedControl.cjs +42 -11
  26. package/dist/cjs/components/SegmentedControl/SegmentedControl.cjs.map +1 -1
  27. package/dist/cjs/components/TextArea/TextArea.cjs +4 -4
  28. package/dist/cjs/components/TextArea/TextArea.cjs.map +1 -1
  29. package/dist/cjs/components/TextField/TextField.cjs +105 -62
  30. package/dist/cjs/components/TextField/TextField.cjs.map +1 -1
  31. package/dist/cjs/index.cjs +12 -0
  32. package/dist/cjs/index.cjs.map +1 -1
  33. package/dist/components/Accordion/AccordionTrigger.mjs +11 -2
  34. package/dist/components/Accordion/AccordionTrigger.mjs.map +1 -1
  35. package/dist/components/AudioPlayer/AudioPlayer.mjs +340 -0
  36. package/dist/components/AudioPlayer/AudioPlayer.mjs.map +1 -0
  37. package/dist/components/Avatar/Avatar.mjs +2 -2
  38. package/dist/components/Avatar/Avatar.mjs.map +1 -1
  39. package/dist/components/Dialog/Dialog.mjs +2 -1
  40. package/dist/components/Dialog/Dialog.mjs.map +1 -1
  41. package/dist/components/DropdownMenu/DropdownMenu.mjs +25 -2
  42. package/dist/components/DropdownMenu/DropdownMenu.mjs.map +1 -1
  43. package/dist/components/FloatingActionButton/FloatingActionButton.mjs +42 -0
  44. package/dist/components/FloatingActionButton/FloatingActionButton.mjs.map +1 -0
  45. package/dist/components/Icons/GifIcon.mjs +12 -36
  46. package/dist/components/Icons/GifIcon.mjs.map +1 -1
  47. package/dist/components/Icons/GridViewIcon.mjs +33 -0
  48. package/dist/components/Icons/GridViewIcon.mjs.map +1 -0
  49. package/dist/components/Icons/ListViewIcon.mjs +33 -0
  50. package/dist/components/Icons/ListViewIcon.mjs.map +1 -0
  51. package/dist/components/ProgressBar/ProgressBar.mjs +30 -5
  52. package/dist/components/ProgressBar/ProgressBar.mjs.map +1 -1
  53. package/dist/components/ProgressBar/ProgressBarItem.mjs +32 -0
  54. package/dist/components/ProgressBar/ProgressBarItem.mjs.map +1 -0
  55. package/dist/components/ProgressBar/ProgressBarSteps.mjs +49 -0
  56. package/dist/components/ProgressBar/ProgressBarSteps.mjs.map +1 -0
  57. package/dist/components/SegmentedControl/SegmentedControl.mjs +42 -11
  58. package/dist/components/SegmentedControl/SegmentedControl.mjs.map +1 -1
  59. package/dist/components/TextArea/TextArea.mjs +4 -4
  60. package/dist/components/TextArea/TextArea.mjs.map +1 -1
  61. package/dist/components/TextField/TextField.mjs +105 -62
  62. package/dist/components/TextField/TextField.mjs.map +1 -1
  63. package/dist/index.d.ts +237 -10
  64. package/dist/index.mjs +12 -0
  65. package/dist/index.mjs.map +1 -1
  66. package/package.json +1 -1
@@ -24,7 +24,7 @@ function _interopNamespaceDefault(e) {
24
24
  }
25
25
  const AccordionPrimitive__namespace = /* @__PURE__ */ _interopNamespaceDefault(AccordionPrimitive);
26
26
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
27
- const AccordionTrigger = React__namespace.forwardRef(({ className, children, icon, ...props }, ref) => {
27
+ const AccordionTrigger = React__namespace.forwardRef(({ className, children, icon, description, leadingIcon, avatar, ...props }, ref) => {
28
28
  const showIcon = icon !== null;
29
29
  const iconElement = icon === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon.ChevronDownIcon, { className: "size-4 shrink-0 text-content-secondary" }) : icon;
30
30
  return /* @__PURE__ */ jsxRuntime.jsx(AccordionPrimitive__namespace.Header, { className: "flex", children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -44,7 +44,16 @@ const AccordionTrigger = React__namespace.forwardRef(({ className, children, ico
44
44
  ),
45
45
  ...props,
46
46
  children: [
47
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left", children }),
47
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
48
+ avatar && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex shrink-0 items-center", "aria-hidden": "true", children: avatar }),
49
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 items-start gap-2 text-left", children: [
50
+ leadingIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex shrink-0 items-center pt-px [&>svg]:size-4", "aria-hidden": "true", children: leadingIcon }),
51
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
52
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate typography-body-small-14px-semibold text-content-primary", children }),
53
+ description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "typography-description-12px-regular text-content-secondary", children: description })
54
+ ] })
55
+ ] })
56
+ ] }),
48
57
  showIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 motion-safe:transition-transform motion-safe:duration-200 [[data-state=open]>&]:rotate-180", children: iconElement })
49
58
  ]
50
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AccordionTrigger.cjs","sources":["../../../../src/components/Accordion/AccordionTrigger.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\n\n/** Props for the {@link AccordionTrigger} button component. */\nexport type AccordionTriggerProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Trigger\n> & {\n /** Custom icon element. Defaults to `ChevronDownIcon`. Pass `null` to suppress the icon entirely. */\n icon?: React.ReactNode | null;\n};\n\n/** An interactive button that toggles the visibility of its associated {@link AccordionContent} panel. */\nexport const AccordionTrigger = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Trigger>,\n AccordionTriggerProps\n>(({ className, children, icon, ...props }, ref) => {\n const showIcon = icon !== null;\n const iconElement =\n icon === undefined ? (\n <ChevronDownIcon className=\"size-4 shrink-0 text-content-secondary\" />\n ) : (\n icon\n );\n\n return (\n <AccordionPrimitive.Header className=\"flex\">\n <AccordionPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex flex-1 items-center justify-between gap-3\",\n \"rounded-sm px-3 py-3\",\n \"typography-body-small-14px-semibold text-content-primary\",\n \"cursor-pointer\",\n \"motion-safe:transition-colors motion-safe:duration-200 motion-safe:ease-in-out\",\n \"hover:bg-neutral-alphas-100\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n \"data-disabled:pointer-events-none data-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n <span className=\"min-w-0 flex-1 truncate text-left\">{children}</span>\n {showIcon && (\n <span className=\"shrink-0 motion-safe:transition-transform motion-safe:duration-200 [[data-state=open]>&]:rotate-180\">\n {iconElement}\n </span>\n )}\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n );\n});\n\nAccordionTrigger.displayName = \"AccordionTrigger\";\n"],"names":["React","ChevronDownIcon","jsx","AccordionPrimitive","jsxs","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAcO,MAAM,mBAAmBA,iBAAM,WAGpC,CAAC,EAAE,WAAW,UAAU,MAAM,GAAG,MAAA,GAAS,QAAQ;AAClD,QAAM,WAAW,SAAS;AAC1B,QAAM,cACJ,SAAS,wCACNC,gBAAAA,iBAAA,EAAgB,WAAU,0CAAyC,IAEpE;AAGJ,SACEC,2BAAAA,IAACC,8BAAmB,QAAnB,EAA0B,WAAU,QACnC,UAAAC,2BAAAA;AAAAA,IAACD,8BAAmB;AAAA,IAAnB;AAAA,MACC;AAAA,MACA,WAAWE,GAAAA;AAAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAAH,2BAAAA,IAAC,QAAA,EAAK,WAAU,qCAAqC,SAAA,CAAS;AAAA,QAC7D,YACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,uGACb,UAAA,YAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAGN;AAEJ,CAAC;AAED,iBAAiB,cAAc;;"}
1
+ {"version":3,"file":"AccordionTrigger.cjs","sources":["../../../../src/components/Accordion/AccordionTrigger.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\n\n/** Props for the {@link AccordionTrigger} button component. */\nexport type AccordionTriggerProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Trigger\n> & {\n /** Trailing indicator icon. Defaults to `ChevronDownIcon` (rotates when open). Pass `null` to suppress it. */\n icon?: React.ReactNode | null;\n /** Secondary line rendered under the title, for extra context. */\n description?: React.ReactNode;\n /** Leading icon shown before the title (sized to 16px). */\n leadingIcon?: React.ReactNode;\n /** Leading avatar shown before the title, for headers representing a person or account. Pass an `Avatar` sized to `24`. */\n avatar?: React.ReactNode;\n};\n\n/** An interactive button that toggles the visibility of its associated {@link AccordionContent} panel. */\nexport const AccordionTrigger = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Trigger>,\n AccordionTriggerProps\n>(({ className, children, icon, description, leadingIcon, avatar, ...props }, ref) => {\n const showIcon = icon !== null;\n const iconElement =\n icon === undefined ? (\n <ChevronDownIcon className=\"size-4 shrink-0 text-content-secondary\" />\n ) : (\n icon\n );\n\n return (\n <AccordionPrimitive.Header className=\"flex\">\n <AccordionPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex flex-1 items-center justify-between gap-3\",\n \"rounded-sm px-3 py-3\",\n \"typography-body-small-14px-semibold text-content-primary\",\n \"cursor-pointer\",\n \"motion-safe:transition-colors motion-safe:duration-200 motion-safe:ease-in-out\",\n \"hover:bg-neutral-alphas-100\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n \"data-disabled:pointer-events-none data-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n <span className=\"flex min-w-0 flex-1 items-center gap-3\">\n {avatar && (\n <span className=\"flex shrink-0 items-center\" aria-hidden=\"true\">\n {avatar}\n </span>\n )}\n <span className=\"flex min-w-0 flex-1 items-start gap-2 text-left\">\n {leadingIcon && (\n <span className=\"flex shrink-0 items-center pt-px [&>svg]:size-4\" aria-hidden=\"true\">\n {leadingIcon}\n </span>\n )}\n <span className=\"flex min-w-0 flex-1 flex-col gap-1\">\n <span className=\"truncate typography-body-small-14px-semibold text-content-primary\">\n {children}\n </span>\n {description && (\n <span className=\"typography-description-12px-regular text-content-secondary\">\n {description}\n </span>\n )}\n </span>\n </span>\n </span>\n {showIcon && (\n <span className=\"shrink-0 motion-safe:transition-transform motion-safe:duration-200 [[data-state=open]>&]:rotate-180\">\n {iconElement}\n </span>\n )}\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n );\n});\n\nAccordionTrigger.displayName = \"AccordionTrigger\";\n"],"names":["React","ChevronDownIcon","jsx","AccordionPrimitive","jsxs","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAoBO,MAAM,mBAAmBA,iBAAM,WAGpC,CAAC,EAAE,WAAW,UAAU,MAAM,aAAa,aAAa,QAAQ,GAAG,MAAA,GAAS,QAAQ;AACpF,QAAM,WAAW,SAAS;AAC1B,QAAM,cACJ,SAAS,wCACNC,gBAAAA,iBAAA,EAAgB,WAAU,0CAAyC,IAEpE;AAGJ,SACEC,2BAAAA,IAACC,8BAAmB,QAAnB,EAA0B,WAAU,QACnC,UAAAC,2BAAAA;AAAAA,IAACD,8BAAmB;AAAA,IAAnB;AAAA,MACC;AAAA,MACA,WAAWE,GAAAA;AAAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAAD,2BAAAA,KAAC,QAAA,EAAK,WAAU,0CACb,UAAA;AAAA,UAAA,yCACE,QAAA,EAAK,WAAU,8BAA6B,eAAY,QACtD,UAAA,QACH;AAAA,UAEFA,2BAAAA,KAAC,QAAA,EAAK,WAAU,mDACb,UAAA;AAAA,YAAA,8CACE,QAAA,EAAK,WAAU,mDAAkD,eAAY,QAC3E,UAAA,aACH;AAAA,YAEFA,2BAAAA,KAAC,QAAA,EAAK,WAAU,sCACd,UAAA;AAAA,cAAAF,2BAAAA,IAAC,QAAA,EAAK,WAAU,qEACb,SAAA,CACH;AAAA,cACC,eACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,8DACb,UAAA,YAAA,CACH;AAAA,YAAA,EAAA,CAEJ;AAAA,UAAA,EAAA,CACF;AAAA,QAAA,GACF;AAAA,QACC,YACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,uGACb,UAAA,YAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAGN;AAEJ,CAAC;AAED,iBAAiB,cAAc;;"}
@@ -0,0 +1,357 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const jsxRuntime = require("react/jsx-runtime");
5
+ const React = require("react");
6
+ const cn = require("../../utils/cn.cjs");
7
+ const PauseIcon = require("../Icons/PauseIcon.cjs");
8
+ const PlayIcon = require("../Icons/PlayIcon.cjs");
9
+ function _interopNamespaceDefault(e) {
10
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
11
+ if (e) {
12
+ for (const k in e) {
13
+ if (k !== "default") {
14
+ const d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: () => e[k]
18
+ });
19
+ }
20
+ }
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+ const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
26
+ const BAR_WIDTH_PX = 4;
27
+ const BAR_GAP_PX = 4;
28
+ const MIN_BAR_HEIGHT_PX = 4;
29
+ const MAX_BAR_HEIGHT_PX = 26;
30
+ const DEFAULT_BAR_COUNT = 24;
31
+ const RAW_PEAK_COUNT = 128;
32
+ const SEEK_STEP_SECONDS = 1;
33
+ const SEEK_STEP_LARGE_SECONDS = 5;
34
+ function formatTime(seconds) {
35
+ if (seconds === void 0 || !Number.isFinite(seconds)) return "--:--";
36
+ const totalSeconds = Math.max(0, Math.floor(seconds));
37
+ const minutes = Math.floor(totalSeconds / 60);
38
+ const remainingSeconds = totalSeconds % 60;
39
+ return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
40
+ }
41
+ function hashString(value) {
42
+ let hash = 2166136261;
43
+ for (let i = 0; i < value.length; i++) {
44
+ hash ^= value.charCodeAt(i);
45
+ hash = Math.imul(hash, 16777619);
46
+ }
47
+ return hash >>> 0;
48
+ }
49
+ function createSeededRandom(seed) {
50
+ let state = seed;
51
+ return () => {
52
+ state = state + 1831565813 | 0;
53
+ let t = Math.imul(state ^ state >>> 15, 1 | state);
54
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
55
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
56
+ };
57
+ }
58
+ function generateFallbackPeaks(src, count) {
59
+ const random = createSeededRandom(hashString(src));
60
+ return Array.from({ length: count }, () => 0.2 + random() * 0.8);
61
+ }
62
+ function computePeaksFromChannelData(channelData, count) {
63
+ const blockSize = Math.max(1, Math.floor(channelData.length / count));
64
+ const peaks = [];
65
+ for (let i = 0; i < count; i++) {
66
+ const start = i * blockSize;
67
+ let max = 0;
68
+ for (let j = 0; j < blockSize && start + j < channelData.length; j++) {
69
+ max = Math.max(max, Math.abs(channelData[start + j] ?? 0));
70
+ }
71
+ peaks.push(max);
72
+ }
73
+ return peaks;
74
+ }
75
+ function resamplePeaks(peaks, barCount) {
76
+ if (peaks.length === 0 || barCount <= 0) return [];
77
+ const step = peaks.length / barCount;
78
+ return Array.from(
79
+ { length: barCount },
80
+ (_, i) => peaks[Math.min(peaks.length - 1, Math.floor(i * step))] ?? 0
81
+ );
82
+ }
83
+ async function decodeAudioPeaks(src, signal) {
84
+ const AudioContextCtor = window.AudioContext ?? window.webkitAudioContext;
85
+ if (!AudioContextCtor) throw new Error("WebAudio unsupported");
86
+ const response = await fetch(src, { signal });
87
+ const arrayBuffer = await response.arrayBuffer();
88
+ const audioContext = new AudioContextCtor();
89
+ try {
90
+ const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
91
+ return computePeaksFromChannelData(audioBuffer.getChannelData(0), RAW_PEAK_COUNT);
92
+ } finally {
93
+ if (audioContext.state !== "closed") audioContext.close().catch(() => {
94
+ });
95
+ }
96
+ }
97
+ const AudioPlayer = React__namespace.forwardRef(
98
+ ({
99
+ className,
100
+ src,
101
+ duration,
102
+ playing: controlledPlaying,
103
+ defaultPlaying = false,
104
+ onPlay,
105
+ onPause,
106
+ onEnded,
107
+ size = "40",
108
+ ...props
109
+ }, ref) => {
110
+ const audioRef = React__namespace.useRef(null);
111
+ const trackRef = React__namespace.useRef(null);
112
+ const [internalPlaying, setInternalPlaying] = React__namespace.useState(defaultPlaying);
113
+ const isControlled = controlledPlaying !== void 0;
114
+ const playing = isControlled ? controlledPlaying : internalPlaying;
115
+ const [currentTime, setCurrentTime] = React__namespace.useState(0);
116
+ const [mediaDuration, setMediaDuration] = React__namespace.useState(void 0);
117
+ const [barCount, setBarCount] = React__namespace.useState(DEFAULT_BAR_COUNT);
118
+ const [peaks, setPeaks] = React__namespace.useState(
119
+ () => generateFallbackPeaks(src, RAW_PEAK_COUNT)
120
+ );
121
+ const [isDragging, setIsDragging] = React__namespace.useState(false);
122
+ const displayDuration = mediaDuration ?? duration;
123
+ const hasStarted = playing || currentTime > 0;
124
+ const playingRef = React__namespace.useRef(playing);
125
+ playingRef.current = playing;
126
+ const isControlledRef = React__namespace.useRef(isControlled);
127
+ isControlledRef.current = isControlled;
128
+ const onPauseRef = React__namespace.useRef(onPause);
129
+ onPauseRef.current = onPause;
130
+ const isFirstSrcRenderRef = React__namespace.useRef(true);
131
+ React__namespace.useEffect(() => {
132
+ if (isFirstSrcRenderRef.current) {
133
+ isFirstSrcRenderRef.current = false;
134
+ return;
135
+ }
136
+ setCurrentTime(0);
137
+ setMediaDuration(void 0);
138
+ setPeaks(generateFallbackPeaks(src, RAW_PEAK_COUNT));
139
+ if (playingRef.current) {
140
+ if (!isControlledRef.current) setInternalPlaying(false);
141
+ onPauseRef.current?.();
142
+ }
143
+ }, [src]);
144
+ React__namespace.useEffect(() => {
145
+ let cancelled = false;
146
+ const abortController = new AbortController();
147
+ decodeAudioPeaks(src, abortController.signal).then((decodedPeaks) => {
148
+ if (!cancelled) setPeaks(decodedPeaks);
149
+ }).catch(() => {
150
+ if (!cancelled) setPeaks(generateFallbackPeaks(src, RAW_PEAK_COUNT));
151
+ });
152
+ return () => {
153
+ cancelled = true;
154
+ abortController.abort();
155
+ };
156
+ }, [src]);
157
+ React__namespace.useEffect(() => {
158
+ const track = trackRef.current;
159
+ if (!track) return;
160
+ const updateFromWidth = (width) => {
161
+ if (width <= 0) return;
162
+ setBarCount(Math.max(1, Math.floor((width + BAR_GAP_PX) / (BAR_WIDTH_PX + BAR_GAP_PX))));
163
+ };
164
+ updateFromWidth(track.getBoundingClientRect().width);
165
+ const observer = new ResizeObserver((entries) => {
166
+ const entry = entries[0];
167
+ if (entry) updateFromWidth(entry.contentRect.width);
168
+ });
169
+ observer.observe(track);
170
+ return () => observer.disconnect();
171
+ }, []);
172
+ React__namespace.useEffect(() => {
173
+ const audio = audioRef.current;
174
+ if (!audio) return;
175
+ if (playing) {
176
+ audio.play().catch(() => {
177
+ if (!isControlled) setInternalPlaying(false);
178
+ onPauseRef.current?.();
179
+ });
180
+ } else {
181
+ audio.pause();
182
+ }
183
+ }, [playing, isControlled]);
184
+ const handleToggle = () => {
185
+ const next = !playing;
186
+ if (!isControlled) setInternalPlaying(next);
187
+ if (next) onPlay?.();
188
+ else onPause?.();
189
+ };
190
+ const seekTo = React__namespace.useCallback(
191
+ (time) => {
192
+ if (displayDuration === void 0) return;
193
+ const clamped = Math.min(Math.max(time, 0), displayDuration);
194
+ setCurrentTime(clamped);
195
+ const audio = audioRef.current;
196
+ if (audio) audio.currentTime = clamped;
197
+ },
198
+ [displayDuration]
199
+ );
200
+ const seekFromClientX = React__namespace.useCallback(
201
+ (clientX) => {
202
+ const track = trackRef.current;
203
+ if (!track || displayDuration === void 0) return;
204
+ const rect = track.getBoundingClientRect();
205
+ if (rect.width <= 0) return;
206
+ const fraction = Math.min(Math.max((clientX - rect.left) / rect.width, 0), 1);
207
+ seekTo(fraction * displayDuration);
208
+ },
209
+ [displayDuration, seekTo]
210
+ );
211
+ React__namespace.useEffect(() => {
212
+ if (!isDragging) return;
213
+ const handleWindowPointerMove = (event) => seekFromClientX(event.clientX);
214
+ const handleWindowPointerUp = () => setIsDragging(false);
215
+ window.addEventListener("pointermove", handleWindowPointerMove);
216
+ window.addEventListener("pointerup", handleWindowPointerUp);
217
+ return () => {
218
+ window.removeEventListener("pointermove", handleWindowPointerMove);
219
+ window.removeEventListener("pointerup", handleWindowPointerUp);
220
+ };
221
+ }, [isDragging, seekFromClientX]);
222
+ const handlePointerDown = (event) => {
223
+ seekFromClientX(event.clientX);
224
+ setIsDragging(true);
225
+ };
226
+ const handleKeyDown = (event) => {
227
+ if (displayDuration === void 0) return;
228
+ switch (event.key) {
229
+ case "ArrowRight":
230
+ case "ArrowUp":
231
+ event.preventDefault();
232
+ seekTo(currentTime + SEEK_STEP_SECONDS);
233
+ break;
234
+ case "ArrowLeft":
235
+ case "ArrowDown":
236
+ event.preventDefault();
237
+ seekTo(currentTime - SEEK_STEP_SECONDS);
238
+ break;
239
+ case "PageUp":
240
+ event.preventDefault();
241
+ seekTo(currentTime + SEEK_STEP_LARGE_SECONDS);
242
+ break;
243
+ case "PageDown":
244
+ event.preventDefault();
245
+ seekTo(currentTime - SEEK_STEP_LARGE_SECONDS);
246
+ break;
247
+ case "Home":
248
+ event.preventDefault();
249
+ seekTo(0);
250
+ break;
251
+ case "End":
252
+ event.preventDefault();
253
+ seekTo(displayDuration);
254
+ break;
255
+ }
256
+ };
257
+ const bars = resamplePeaks(peaks, barCount);
258
+ const progressFraction = displayDuration && displayDuration > 0 ? Math.min(Math.max(currentTime / displayDuration, 0), 1) : 0;
259
+ const playedBarCount = hasStarted ? Math.round(progressFraction * bars.length) : bars.length;
260
+ const barData = bars.map((amplitude, index) => {
261
+ const isPlayed = index < playedBarCount;
262
+ return {
263
+ heightPx: MIN_BAR_HEIGHT_PX + amplitude * (MAX_BAR_HEIGHT_PX - MIN_BAR_HEIGHT_PX),
264
+ className: cn.cn(
265
+ "w-1 shrink-0 rounded-3xs bg-messages-waveform-default",
266
+ "motion-safe:transition-opacity motion-safe:duration-150 motion-safe:ease-in-out",
267
+ isPlayed ? "opacity-100" : "opacity-50"
268
+ )
269
+ };
270
+ });
271
+ return /* @__PURE__ */ jsxRuntime.jsxs(
272
+ "div",
273
+ {
274
+ ref,
275
+ "data-size": size,
276
+ className: cn.cn("flex w-full items-center gap-3", className),
277
+ ...props,
278
+ children: [
279
+ /* @__PURE__ */ jsxRuntime.jsx(
280
+ "button",
281
+ {
282
+ type: "button",
283
+ onClick: handleToggle,
284
+ "aria-label": playing ? "Pause" : "Play",
285
+ className: cn.cn(
286
+ "flex size-8 shrink-0 items-center justify-center rounded-full",
287
+ "bg-buttons-secondary-default text-icons-primary backdrop-blur-[40px]",
288
+ "motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out",
289
+ "hover:bg-buttons-secondary-hover",
290
+ "focus-visible:shadow-focus-ring focus-visible:outline-none"
291
+ ),
292
+ children: playing ? /* @__PURE__ */ jsxRuntime.jsx(PauseIcon.PauseIcon, { size: 16, filled: true }) : /* @__PURE__ */ jsxRuntime.jsx(PlayIcon.PlayIcon, { size: 16, filled: true })
293
+ }
294
+ ),
295
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
296
+ /* @__PURE__ */ jsxRuntime.jsx(
297
+ "div",
298
+ {
299
+ ref: trackRef,
300
+ role: "slider",
301
+ tabIndex: 0,
302
+ "aria-label": "Seek",
303
+ "aria-orientation": "horizontal",
304
+ "aria-valuemin": 0,
305
+ "aria-valuemax": displayDuration,
306
+ "aria-valuenow": Math.round(currentTime),
307
+ "aria-valuetext": displayDuration !== void 0 ? `${formatTime(currentTime)} of ${formatTime(displayDuration)}` : formatTime(currentTime),
308
+ onPointerDown: handlePointerDown,
309
+ onKeyDown: handleKeyDown,
310
+ className: cn.cn(
311
+ "flex h-10 min-w-0 flex-1 cursor-pointer touch-none select-none items-center gap-1 overflow-hidden",
312
+ "focus-visible:shadow-focus-ring focus-visible:outline-none"
313
+ ),
314
+ children: barData.map((bar, index) => (
315
+ // biome-ignore lint/suspicious/noArrayIndexKey: bars are a fixed-count, non-reorderable list
316
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: bar.className, style: { height: bar.heightPx } }, index)
317
+ ))
318
+ }
319
+ ),
320
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "typography-body-small-14px-semibold flex shrink-0 items-center gap-0.5 whitespace-nowrap", children: hasStarted ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
321
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-content-primary", children: formatTime(currentTime) }),
322
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-content-secondary", children: [
323
+ "/",
324
+ formatTime(displayDuration)
325
+ ] })
326
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-content-primary", children: formatTime(displayDuration) }) })
327
+ ] }),
328
+ /* @__PURE__ */ jsxRuntime.jsx(
329
+ "audio",
330
+ {
331
+ ref: audioRef,
332
+ src,
333
+ preload: "metadata",
334
+ className: "hidden",
335
+ onLoadedMetadata: (event) => {
336
+ const { duration: nativeDuration } = event.currentTarget;
337
+ setMediaDuration(Number.isFinite(nativeDuration) ? nativeDuration : void 0);
338
+ },
339
+ onTimeUpdate: (event) => {
340
+ if (isDragging) return;
341
+ setCurrentTime(event.currentTarget.currentTime);
342
+ },
343
+ onEnded: () => {
344
+ setCurrentTime(0);
345
+ if (!isControlled) setInternalPlaying(false);
346
+ onEnded?.();
347
+ }
348
+ }
349
+ )
350
+ ]
351
+ }
352
+ );
353
+ }
354
+ );
355
+ AudioPlayer.displayName = "AudioPlayer";
356
+ exports.AudioPlayer = AudioPlayer;
357
+ //# sourceMappingURL=AudioPlayer.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioPlayer.cjs","sources":["../../../../src/components/AudioPlayer/AudioPlayer.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { PauseIcon } from \"../Icons/PauseIcon\";\nimport { PlayIcon } from \"../Icons/PlayIcon\";\n\n/** Width of a single waveform bar, in pixels. */\nconst BAR_WIDTH_PX = 4;\n/** Gap between waveform bars, in pixels. */\nconst BAR_GAP_PX = 4;\n/** Shortest a waveform bar can render, in pixels. */\nconst MIN_BAR_HEIGHT_PX = 4;\n/** Tallest a waveform bar can render, in pixels — matches the 40px row height. */\nconst MAX_BAR_HEIGHT_PX = 26;\n/** Bar count used before the container has been measured (e.g. during SSR or in jsdom, where layout is unavailable). */\nconst DEFAULT_BAR_COUNT = 24;\n/** Number of amplitude samples kept internally, resampled to the rendered bar count. */\nconst RAW_PEAK_COUNT = 128;\n/** Seconds moved per Arrow key press while seeking. */\nconst SEEK_STEP_SECONDS = 1;\n/** Seconds moved per Page Up/Down press while seeking. */\nconst SEEK_STEP_LARGE_SECONDS = 5;\n\n/** Height of the audio player row, in pixels. Matches both the Vault card overlay and the \"Generated Audio\" modal row in Figma — both use an identical 32px play button and 40px row. */\nexport type AudioPlayerSize = \"40\";\n\nexport interface AudioPlayerProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onPlay\" | \"onPause\" | \"onEnded\"> {\n /** URL of the audio file to play. */\n src: string;\n /**\n * Total duration in seconds, used to render the timestamp before the\n * media's own metadata has loaded (and as a fallback if it never does).\n */\n duration?: number;\n /**\n * Whether playback is active (controlled). Note: once `onEnded` fires, the\n * browser has already stopped native playback. To loop or replay under\n * controlled usage, toggle this prop `false` then `true` (rather than\n * leaving it `true`) — the sync effect only calls `play()` again when this\n * value actually changes.\n */\n playing?: boolean;\n /** Initial playback state (uncontrolled). @default false */\n defaultPlaying?: boolean;\n /** Called when playback starts. */\n onPlay?: () => void;\n /** Called when playback is paused. */\n onPause?: () => void;\n /** Called when playback reaches the end of the media. */\n onEnded?: () => void;\n /** Height of the player row, in pixels. @default \"40\" */\n size?: AudioPlayerSize;\n}\n\nfunction formatTime(seconds: number | undefined): string {\n if (seconds === undefined || !Number.isFinite(seconds)) return \"--:--\";\n const totalSeconds = Math.max(0, Math.floor(seconds));\n const minutes = Math.floor(totalSeconds / 60);\n const remainingSeconds = totalSeconds % 60;\n return `${minutes}:${String(remainingSeconds).padStart(2, \"0\")}`;\n}\n\n/** Deterministic 32-bit string hash (FNV-1a), used to seed the fallback waveform. */\nfunction hashString(value: string): number {\n let hash = 2166136261;\n for (let i = 0; i < value.length; i++) {\n hash ^= value.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return hash >>> 0;\n}\n\n/** Seeded PRNG (mulberry32) — deterministic across runs, unlike `Math.random`. */\nfunction createSeededRandom(seed: number): () => number {\n let state = seed;\n return () => {\n state = (state + 0x6d2b79f5) | 0;\n let t = Math.imul(state ^ (state >>> 15), 1 | state);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/** Deterministic placeholder amplitudes used when audio can't be decoded (network/CORS/format failure). */\nfunction generateFallbackPeaks(src: string, count: number): number[] {\n const random = createSeededRandom(hashString(src));\n return Array.from({ length: count }, () => 0.2 + random() * 0.8);\n}\n\n/** Downsamples decoded PCM data to `count` peak (max-abs) amplitudes. */\nfunction computePeaksFromChannelData(channelData: Float32Array, count: number): number[] {\n const blockSize = Math.max(1, Math.floor(channelData.length / count));\n const peaks: number[] = [];\n for (let i = 0; i < count; i++) {\n const start = i * blockSize;\n let max = 0;\n for (let j = 0; j < blockSize && start + j < channelData.length; j++) {\n max = Math.max(max, Math.abs(channelData[start + j] ?? 0));\n }\n peaks.push(max);\n }\n return peaks;\n}\n\n/** Resamples a peaks array to the number of bars that currently fit the container. */\nfunction resamplePeaks(peaks: number[], barCount: number): number[] {\n if (peaks.length === 0 || barCount <= 0) return [];\n const step = peaks.length / barCount;\n return Array.from(\n { length: barCount },\n (_, i) => peaks[Math.min(peaks.length - 1, Math.floor(i * step))] ?? 0,\n );\n}\n\n/** Decodes `src` via WebAudio and returns downsampled peak amplitudes. Throws on any failure. */\nasync function decodeAudioPeaks(src: string, signal: AbortSignal): Promise<number[]> {\n const AudioContextCtor =\n window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"WebAudio unsupported\");\n\n const response = await fetch(src, { signal });\n const arrayBuffer = await response.arrayBuffer();\n const audioContext = new AudioContextCtor();\n try {\n const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n return computePeaksFromChannelData(audioBuffer.getChannelData(0), RAW_PEAK_COUNT);\n } finally {\n if (audioContext.state !== \"closed\") audioContext.close().catch(() => {});\n }\n}\n\n/**\n * A compact playback control for a single audio clip: a play/pause toggle, a\n * static amplitude waveform that doubles as a seek scrubber, and elapsed/total\n * timestamps. Designed to sit as an overlay on media thumbnails (Vault cards)\n * as well as inline rows (the AI Voice Message \"Generated Audio\" row).\n *\n * The waveform is seeded from the decoded audio when possible, falling back\n * to a deterministic (not random) placeholder derived from `src` so the\n * result is stable across renders, SSR, and Chromatic snapshots.\n *\n * @example\n * ```tsx\n * <AudioPlayer src=\"https://example.com/clip.mp3\" duration={5} />\n * ```\n */\nexport const AudioPlayer = React.forwardRef<HTMLDivElement, AudioPlayerProps>(\n (\n {\n className,\n src,\n duration,\n playing: controlledPlaying,\n defaultPlaying = false,\n onPlay,\n onPause,\n onEnded,\n size = \"40\",\n ...props\n },\n ref,\n ) => {\n const audioRef = React.useRef<HTMLAudioElement>(null);\n const trackRef = React.useRef<HTMLDivElement>(null);\n\n const [internalPlaying, setInternalPlaying] = React.useState(defaultPlaying);\n const isControlled = controlledPlaying !== undefined;\n const playing = isControlled ? controlledPlaying : internalPlaying;\n\n const [currentTime, setCurrentTime] = React.useState(0);\n const [mediaDuration, setMediaDuration] = React.useState<number | undefined>(undefined);\n const [barCount, setBarCount] = React.useState(DEFAULT_BAR_COUNT);\n const [peaks, setPeaks] = React.useState<number[]>(() =>\n generateFallbackPeaks(src, RAW_PEAK_COUNT),\n );\n const [isDragging, setIsDragging] = React.useState(false);\n\n const displayDuration = mediaDuration ?? duration;\n const hasStarted = playing || currentTime > 0;\n\n // Latest-value refs so the src-change effect below can react to a src swap\n // without re-running whenever `playing`/`isControlled`/`onPause` change.\n const playingRef = React.useRef(playing);\n playingRef.current = playing;\n const isControlledRef = React.useRef(isControlled);\n isControlledRef.current = isControlled;\n const onPauseRef = React.useRef(onPause);\n onPauseRef.current = onPause;\n\n // Reset transient playback state when the source changes. Skips the\n // initial mount (there's nothing to reset yet) and, if playback was\n // active, stops it — the browser silently drops playback on a source\n // swap, so the UI (and any controlled parent) must be told via onPause.\n const isFirstSrcRenderRef = React.useRef(true);\n React.useEffect(() => {\n if (isFirstSrcRenderRef.current) {\n isFirstSrcRenderRef.current = false;\n return;\n }\n setCurrentTime(0);\n setMediaDuration(undefined);\n setPeaks(generateFallbackPeaks(src, RAW_PEAK_COUNT));\n if (playingRef.current) {\n if (!isControlledRef.current) setInternalPlaying(false);\n onPauseRef.current?.();\n }\n }, [src]);\n\n // Decode the audio to derive real waveform amplitudes, falling back to a\n // deterministic placeholder on any failure (network, CORS, unsupported format).\n React.useEffect(() => {\n let cancelled = false;\n const abortController = new AbortController();\n\n decodeAudioPeaks(src, abortController.signal)\n .then((decodedPeaks) => {\n if (!cancelled) setPeaks(decodedPeaks);\n })\n .catch(() => {\n if (!cancelled) setPeaks(generateFallbackPeaks(src, RAW_PEAK_COUNT));\n });\n\n return () => {\n cancelled = true;\n abortController.abort();\n };\n }, [src]);\n\n // Measure the track to decide how many bars fit; falls back to DEFAULT_BAR_COUNT\n // when layout is unavailable (SSR, jsdom).\n React.useEffect(() => {\n const track = trackRef.current;\n if (!track) return;\n\n const updateFromWidth = (width: number) => {\n if (width <= 0) return;\n setBarCount(Math.max(1, Math.floor((width + BAR_GAP_PX) / (BAR_WIDTH_PX + BAR_GAP_PX))));\n };\n\n updateFromWidth(track.getBoundingClientRect().width);\n\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (entry) updateFromWidth(entry.contentRect.width);\n });\n observer.observe(track);\n return () => observer.disconnect();\n }, []);\n\n // Sync the controlled/uncontrolled `playing` state to the media element.\n React.useEffect(() => {\n const audio = audioRef.current;\n if (!audio) return;\n if (playing) {\n audio.play().catch(() => {\n if (!isControlled) setInternalPlaying(false);\n onPauseRef.current?.();\n });\n } else {\n audio.pause();\n }\n }, [playing, isControlled]);\n\n const handleToggle = () => {\n const next = !playing;\n if (!isControlled) setInternalPlaying(next);\n if (next) onPlay?.();\n else onPause?.();\n };\n\n const seekTo = React.useCallback(\n (time: number) => {\n if (displayDuration === undefined) return;\n const clamped = Math.min(Math.max(time, 0), displayDuration);\n setCurrentTime(clamped);\n const audio = audioRef.current;\n if (audio) audio.currentTime = clamped;\n },\n [displayDuration],\n );\n\n const seekFromClientX = React.useCallback(\n (clientX: number) => {\n const track = trackRef.current;\n if (!track || displayDuration === undefined) return;\n const rect = track.getBoundingClientRect();\n if (rect.width <= 0) return;\n const fraction = Math.min(Math.max((clientX - rect.left) / rect.width, 0), 1);\n seekTo(fraction * displayDuration);\n },\n [displayDuration, seekTo],\n );\n\n // Track drag outside the element via window listeners rather than pointer\n // capture, which some environments (and jsdom) don't fully implement.\n React.useEffect(() => {\n if (!isDragging) return;\n const handleWindowPointerMove = (event: PointerEvent) => seekFromClientX(event.clientX);\n const handleWindowPointerUp = () => setIsDragging(false);\n window.addEventListener(\"pointermove\", handleWindowPointerMove);\n window.addEventListener(\"pointerup\", handleWindowPointerUp);\n return () => {\n window.removeEventListener(\"pointermove\", handleWindowPointerMove);\n window.removeEventListener(\"pointerup\", handleWindowPointerUp);\n };\n }, [isDragging, seekFromClientX]);\n\n const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n seekFromClientX(event.clientX);\n setIsDragging(true);\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (displayDuration === undefined) return;\n switch (event.key) {\n case \"ArrowRight\":\n case \"ArrowUp\":\n event.preventDefault();\n seekTo(currentTime + SEEK_STEP_SECONDS);\n break;\n case \"ArrowLeft\":\n case \"ArrowDown\":\n event.preventDefault();\n seekTo(currentTime - SEEK_STEP_SECONDS);\n break;\n case \"PageUp\":\n event.preventDefault();\n seekTo(currentTime + SEEK_STEP_LARGE_SECONDS);\n break;\n case \"PageDown\":\n event.preventDefault();\n seekTo(currentTime - SEEK_STEP_LARGE_SECONDS);\n break;\n case \"Home\":\n event.preventDefault();\n seekTo(0);\n break;\n case \"End\":\n event.preventDefault();\n seekTo(displayDuration);\n break;\n default:\n break;\n }\n };\n\n const bars = resamplePeaks(peaks, barCount);\n const progressFraction =\n displayDuration && displayDuration > 0\n ? Math.min(Math.max(currentTime / displayDuration, 0), 1)\n : 0;\n const playedBarCount = hasStarted ? Math.round(progressFraction * bars.length) : bars.length;\n const barData = bars.map((amplitude, index) => {\n const isPlayed = index < playedBarCount;\n return {\n heightPx: MIN_BAR_HEIGHT_PX + amplitude * (MAX_BAR_HEIGHT_PX - MIN_BAR_HEIGHT_PX),\n className: cn(\n \"w-1 shrink-0 rounded-3xs bg-messages-waveform-default\",\n \"motion-safe:transition-opacity motion-safe:duration-150 motion-safe:ease-in-out\",\n isPlayed ? \"opacity-100\" : \"opacity-50\",\n ),\n };\n });\n\n return (\n <div\n ref={ref}\n data-size={size}\n className={cn(\"flex w-full items-center gap-3\", className)}\n {...props}\n >\n <button\n type=\"button\"\n onClick={handleToggle}\n aria-label={playing ? \"Pause\" : \"Play\"}\n className={cn(\n \"flex size-8 shrink-0 items-center justify-center rounded-full\",\n \"bg-buttons-secondary-default text-icons-primary backdrop-blur-[40px]\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"hover:bg-buttons-secondary-hover\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n )}\n >\n {playing ? <PauseIcon size={16} filled /> : <PlayIcon size={16} filled />}\n </button>\n\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <div\n ref={trackRef}\n role=\"slider\"\n tabIndex={0}\n aria-label=\"Seek\"\n aria-orientation=\"horizontal\"\n aria-valuemin={0}\n aria-valuemax={displayDuration}\n aria-valuenow={Math.round(currentTime)}\n aria-valuetext={\n displayDuration !== undefined\n ? `${formatTime(currentTime)} of ${formatTime(displayDuration)}`\n : formatTime(currentTime)\n }\n onPointerDown={handlePointerDown}\n onKeyDown={handleKeyDown}\n className={cn(\n \"flex h-10 min-w-0 flex-1 cursor-pointer touch-none select-none items-center gap-1 overflow-hidden\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n )}\n >\n {barData.map((bar, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: bars are a fixed-count, non-reorderable list\n <span key={index} className={bar.className} style={{ height: bar.heightPx }} />\n ))}\n </div>\n\n <span className=\"typography-body-small-14px-semibold flex shrink-0 items-center gap-0.5 whitespace-nowrap\">\n {hasStarted ? (\n <>\n <span className=\"text-content-primary\">{formatTime(currentTime)}</span>\n <span className=\"text-content-secondary\">/{formatTime(displayDuration)}</span>\n </>\n ) : (\n <span className=\"text-content-primary\">{formatTime(displayDuration)}</span>\n )}\n </span>\n </div>\n\n {/* biome-ignore lint/a11y/useMediaCaption: this is a UI sound clip (voice note / short recording), not spoken/narrated content requiring captions. */}\n <audio\n ref={audioRef}\n src={src}\n preload=\"metadata\"\n className=\"hidden\"\n onLoadedMetadata={(event) => {\n const { duration: nativeDuration } = event.currentTarget;\n setMediaDuration(Number.isFinite(nativeDuration) ? nativeDuration : undefined);\n }}\n onTimeUpdate={(event) => {\n if (isDragging) return;\n setCurrentTime(event.currentTarget.currentTime);\n }}\n onEnded={() => {\n setCurrentTime(0);\n if (!isControlled) setInternalPlaying(false);\n onEnded?.();\n }}\n />\n </div>\n );\n },\n);\n\nAudioPlayer.displayName = \"AudioPlayer\";\n"],"names":["React","cn","jsxs","jsx","PauseIcon","PlayIcon","Fragment"],"mappings":"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"}
@@ -48,7 +48,7 @@ const AvatarRoot = React__namespace.forwardRef(
48
48
  ...props
49
49
  }, ref) => {
50
50
  const statusPosition = STATUS_POSITIONS[size];
51
- return /* @__PURE__ */ jsxRuntime.jsx(AvatarContext.Provider, { value: { size, NSFWShow }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative inline-flex", children: [
51
+ return /* @__PURE__ */ jsxRuntime.jsx(AvatarContext.Provider, { value: { size, NSFWShow }, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative inline-flex", children: [
52
52
  /* @__PURE__ */ jsxRuntime.jsx(
53
53
  AvatarPrimitive__namespace.Root,
54
54
  {
@@ -71,7 +71,7 @@ const AvatarRoot = React__namespace.forwardRef(
71
71
  }
72
72
  ),
73
73
  platinumShow && /* @__PURE__ */ jsxRuntime.jsx(
74
- "div",
74
+ "span",
75
75
  {
76
76
  className: "pointer-events-none absolute inset-0 rounded-full",
77
77
  style: {
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.cjs","sources":["../../../../src/components/Avatar/Avatar.tsx"],"sourcesContent":["import * as AvatarPrimitive from \"@radix-ui/react-avatar\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Allowed pixel sizes for the avatar. */\nexport type AvatarSize = 16 | 24 | 32 | 40 | 48 | 64 | 88 | 148;\n\nconst AvatarContext = React.createContext<{ size: AvatarSize; NSFWShow: boolean }>({\n size: 40,\n NSFWShow: false,\n});\n\nconst STATUS_POSITIONS: Record<\n AvatarSize,\n { top: number; right: number; indicatorSize: string; borderSize: string }\n> = {\n 16: { top: -2, right: -2, indicatorSize: \"size-2\", borderSize: \"border\" },\n 24: { top: 0, right: 0, indicatorSize: \"size-2\", borderSize: \"border\" },\n 32: { top: 0, right: 0, indicatorSize: \"size-2\", borderSize: \"border\" },\n 40: { top: 2, right: 2, indicatorSize: \"size-2\", borderSize: \"border\" },\n 48: { top: 5, right: 2, indicatorSize: \"size-2\", borderSize: \"border\" },\n 64: { top: 5, right: 1, indicatorSize: \"size-3\", borderSize: \"border\" },\n 88: { top: 8, right: 6, indicatorSize: \"size-3\", borderSize: \"border\" },\n 148: { top: 15, right: 15, indicatorSize: \"size-3\", borderSize: \"border\" },\n};\n\n/** Shared avatar styling props. */\ninterface AvatarStyleProps {\n /** Pixel size of the avatar. @default 40 */\n size?: AvatarSize;\n /** Whether to show the online-status indicator dot. @default false */\n onlineIndicator?: boolean;\n /** Whether to show the platinum gradient border ring. @default false */\n platinumShow?: boolean;\n /** Whether to apply the NSFW blur filter over the image. @default false */\n NSFWShow?: boolean;\n}\n\n/** Props for the low-level {@link AvatarRoot} compound component. */\nexport interface AvatarRootProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,\n AvatarStyleProps {}\n\n/**\n * Low-level avatar root for custom compositions. Provides size context to\n * child `AvatarImage` and `AvatarFallback` components.\n *\n * Prefer the higher-level {@link Avatar} component for most use cases.\n */\nconst AvatarRoot = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Root>,\n AvatarRootProps\n>(\n (\n {\n className,\n size = 40,\n onlineIndicator = false,\n platinumShow = false,\n NSFWShow = false,\n children,\n ...props\n },\n ref,\n ) => {\n const statusPosition = STATUS_POSITIONS[size];\n\n return (\n <AvatarContext.Provider value={{ size, NSFWShow }}>\n <div className=\"relative inline-flex\">\n <AvatarPrimitive.Root\n ref={ref}\n data-testid=\"avatar\"\n className={cn(\n \"relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-background-avatar\",\n size === 16 && \"size-4 text-2xs\",\n size === 24 && \"size-6 text-xs\",\n size === 32 && \"size-8 text-xs\",\n size === 40 && \"size-10 text-sm\",\n size === 48 && \"size-12 text-base\",\n size === 64 && \"size-16 text-xl\",\n size === 88 && \"size-22 text-2xl\",\n size === 148 && \"size-37 text-4xl\",\n className,\n )}\n {...props}\n >\n {children}\n </AvatarPrimitive.Root>\n {platinumShow && (\n <div\n className=\"pointer-events-none absolute inset-0 rounded-full\"\n style={{\n background: `linear-gradient(143deg, #504F54 0%, #B1B1B1 20.3154%, #13181C 37.3727%, #C6C6C8 58.8154%, #FFFFFF 69.3154%, #0C0F14 81.3154%, #696A6E 100%)`,\n WebkitMask: \"radial-gradient(circle closest-side, transparent 96%, black 96%)\",\n mask: \"radial-gradient(circle closest-side, transparent 96%, black 96%)\",\n }}\n aria-hidden=\"true\"\n />\n )}\n {onlineIndicator && (\n <span\n className={cn(\n \"absolute rounded-full border-surface-primary bg-brand-primary-default\",\n statusPosition.borderSize,\n statusPosition.indicatorSize,\n )}\n style={{\n top: `${statusPosition.top}px`,\n right: `${statusPosition.right}px`,\n }}\n aria-hidden=\"true\"\n />\n )}\n </div>\n </AvatarContext.Provider>\n );\n },\n);\n\nAvatarRoot.displayName = \"AvatarRoot\";\n\n/** Props for the {@link AvatarImage} compound component. */\nexport interface AvatarImageProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> {}\n\n/** Renders the avatar image. Automatically applies the NSFW blur when enabled on the parent `AvatarRoot`. */\nconst AvatarImage = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Image>,\n AvatarImageProps\n>(({ className, ...props }, ref) => {\n const { NSFWShow } = React.useContext(AvatarContext);\n return (\n <AvatarPrimitive.Image\n ref={ref}\n className={cn(\"size-full object-cover\", NSFWShow && \"blur-md\", className)}\n {...props}\n />\n );\n});\n\nAvatarImage.displayName = \"AvatarImage\";\n\n/** Props for the {@link AvatarFallback} compound component. */\nexport interface AvatarFallbackProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> {}\n\n/** Renders fallback content (e.g. initials or an icon) when the avatar image has not loaded. */\nconst AvatarFallback = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Fallback>,\n AvatarFallbackProps\n>(({ className, children, ...props }, ref) => (\n <AvatarPrimitive.Fallback\n ref={ref}\n className={cn(\n \"flex size-full items-center justify-center font-semibold text-content-primary uppercase leading-none\",\n className,\n )}\n delayMs={0}\n {...props}\n >\n {children}\n </AvatarPrimitive.Fallback>\n));\n\nAvatarFallback.displayName = \"AvatarFallback\";\n\nexport interface AvatarProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,\n AvatarStyleProps {\n /** URL of the avatar image. */\n src?: string;\n /** Alt text for the avatar image. @default \"Avatar\" */\n alt?: string;\n /** Fallback content rendered when the image has not loaded (e.g. initials or an icon). */\n fallback?: React.ReactNode;\n}\n\n/**\n * Displays a user avatar with optional online indicator, platinum border, and\n * NSFW blur. Pass `src` and `fallback` for the simple API, or compose your own\n * layout with `AvatarRoot`, `AvatarImage`, and `AvatarFallback` as children.\n *\n * @example\n * ```tsx\n * <Avatar src=\"/photo.jpg\" alt=\"Jane Doe\" fallback=\"JD\" size={48} />\n * ```\n */\nexport const Avatar = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Root>,\n AvatarProps\n>(\n (\n {\n className,\n size = 40,\n src,\n alt,\n fallback,\n onlineIndicator = false,\n platinumShow = false,\n NSFWShow = false,\n children,\n ...props\n },\n ref,\n ) => {\n const rootProps = {\n ref,\n size,\n onlineIndicator,\n platinumShow,\n NSFWShow,\n className,\n ...props,\n };\n\n if (children) {\n return <AvatarRoot {...rootProps}>{children}</AvatarRoot>;\n }\n\n return (\n <AvatarRoot {...rootProps}>\n {src && <AvatarImage src={src} alt={alt ?? \"Avatar\"} />}\n <AvatarFallback>{fallback}</AvatarFallback>\n </AvatarRoot>\n );\n },\n);\n\nAvatar.displayName = \"Avatar\";\n\nexport { AvatarRoot, AvatarImage, AvatarFallback };\n"],"names":["React","jsx","jsxs","AvatarPrimitive","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAOA,MAAM,gBAAgBA,iBAAM,cAAuD;AAAA,EACjF,MAAM;AAAA,EACN,UAAU;AACZ,CAAC;AAED,MAAM,mBAGF;AAAA,EACF,IAAI,EAAE,KAAK,IAAI,OAAO,IAAI,eAAe,UAAU,YAAY,SAAA;AAAA,EAC/D,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,eAAe,UAAU,YAAY,SAAA;AAAA,EAC7D,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,eAAe,UAAU,YAAY,SAAA;AAAA,EAC7D,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,eAAe,UAAU,YAAY,SAAA;AAAA,EAC7D,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,eAAe,UAAU,YAAY,SAAA;AAAA,EAC7D,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,eAAe,UAAU,YAAY,SAAA;AAAA,EAC7D,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,eAAe,UAAU,YAAY,SAAA;AAAA,EAC7D,KAAK,EAAE,KAAK,IAAI,OAAO,IAAI,eAAe,UAAU,YAAY,SAAA;AAClE;AAyBA,MAAM,aAAaA,iBAAM;AAAA,EAIvB,CACE;AAAA,IACE;AAAA,IACA,OAAO;AAAA,IACP,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,WAAW;AAAA,IACX;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,iBAAiB,iBAAiB,IAAI;AAE5C,WACEC,2BAAAA,IAAC,cAAc,UAAd,EAAuB,OAAO,EAAE,MAAM,SAAA,GACrC,UAAAC,2BAAAA,KAAC,OAAA,EAAI,WAAU,wBACb,UAAA;AAAA,MAAAD,2BAAAA;AAAAA,QAACE,2BAAgB;AAAA,QAAhB;AAAA,UACC;AAAA,UACA,eAAY;AAAA,UACZ,WAAWC,GAAAA;AAAAA,YACT;AAAA,YACA,SAAS,MAAM;AAAA,YACf,SAAS,MAAM;AAAA,YACf,SAAS,MAAM;AAAA,YACf,SAAS,MAAM;AAAA,YACf,SAAS,MAAM;AAAA,YACf,SAAS,MAAM;AAAA,YACf,SAAS,MAAM;AAAA,YACf,SAAS,OAAO;AAAA,YAChB;AAAA,UAAA;AAAA,UAED,GAAG;AAAA,UAEH;AAAA,QAAA;AAAA,MAAA;AAAA,MAEF,gBACCH,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO;AAAA,YACL,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,MAAM;AAAA,UAAA;AAAA,UAER,eAAY;AAAA,QAAA;AAAA,MAAA;AAAA,MAGf,mBACCA,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAWG,GAAAA;AAAAA,YACT;AAAA,YACA,eAAe;AAAA,YACf,eAAe;AAAA,UAAA;AAAA,UAEjB,OAAO;AAAA,YACL,KAAK,GAAG,eAAe,GAAG;AAAA,YAC1B,OAAO,GAAG,eAAe,KAAK;AAAA,UAAA;AAAA,UAEhC,eAAY;AAAA,QAAA;AAAA,MAAA;AAAA,IACd,EAAA,CAEJ,EAAA,CACF;AAAA,EAEJ;AACF;AAEA,WAAW,cAAc;AAOzB,MAAM,cAAcJ,iBAAM,WAGxB,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAAQ;AAClC,QAAM,EAAE,SAAA,IAAaA,iBAAM,WAAW,aAAa;AACnD,SACEC,2BAAAA;AAAAA,IAACE,2BAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,WAAWC,GAAAA,GAAG,0BAA0B,YAAY,WAAW,SAAS;AAAA,MACvE,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV,CAAC;AAED,YAAY,cAAc;AAO1B,MAAM,iBAAiBJ,iBAAM,WAG3B,CAAC,EAAE,WAAW,UAAU,GAAG,SAAS,QACpCC,2BAAAA;AAAAA,EAACE,2BAAgB;AAAA,EAAhB;AAAA,IACC;AAAA,IACA,WAAWC,GAAAA;AAAAA,MACT;AAAA,MACA;AAAA,IAAA;AAAA,IAEF,SAAS;AAAA,IACR,GAAG;AAAA,IAEH;AAAA,EAAA;AACH,CACD;AAED,eAAe,cAAc;AAuBtB,MAAM,SAASJ,iBAAM;AAAA,EAI1B,CACE;AAAA,IACE;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,WAAW;AAAA,IACX;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,YAAY;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IAAA;AAGL,QAAI,UAAU;AACZ,aAAOC,2BAAAA,IAAC,YAAA,EAAY,GAAG,WAAY,SAAA,CAAS;AAAA,IAC9C;AAEA,WACEC,2BAAAA,KAAC,YAAA,EAAY,GAAG,WACb,UAAA;AAAA,MAAA,OAAOD,2BAAAA,IAAC,aAAA,EAAY,KAAU,KAAK,OAAO,UAAU;AAAA,MACrDA,2BAAAA,IAAC,kBAAgB,UAAA,SAAA,CAAS;AAAA,IAAA,GAC5B;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;;;;;"}
1
+ {"version":3,"file":"Avatar.cjs","sources":["../../../../src/components/Avatar/Avatar.tsx"],"sourcesContent":["import * as AvatarPrimitive from \"@radix-ui/react-avatar\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Allowed pixel sizes for the avatar. */\nexport type AvatarSize = 16 | 24 | 32 | 40 | 48 | 64 | 88 | 148;\n\nconst AvatarContext = React.createContext<{ size: AvatarSize; NSFWShow: boolean }>({\n size: 40,\n NSFWShow: false,\n});\n\nconst STATUS_POSITIONS: Record<\n AvatarSize,\n { top: number; right: number; indicatorSize: string; borderSize: string }\n> = {\n 16: { top: -2, right: -2, indicatorSize: \"size-2\", borderSize: \"border\" },\n 24: { top: 0, right: 0, indicatorSize: \"size-2\", borderSize: \"border\" },\n 32: { top: 0, right: 0, indicatorSize: \"size-2\", borderSize: \"border\" },\n 40: { top: 2, right: 2, indicatorSize: \"size-2\", borderSize: \"border\" },\n 48: { top: 5, right: 2, indicatorSize: \"size-2\", borderSize: \"border\" },\n 64: { top: 5, right: 1, indicatorSize: \"size-3\", borderSize: \"border\" },\n 88: { top: 8, right: 6, indicatorSize: \"size-3\", borderSize: \"border\" },\n 148: { top: 15, right: 15, indicatorSize: \"size-3\", borderSize: \"border\" },\n};\n\n/** Shared avatar styling props. */\ninterface AvatarStyleProps {\n /** Pixel size of the avatar. @default 40 */\n size?: AvatarSize;\n /** Whether to show the online-status indicator dot. @default false */\n onlineIndicator?: boolean;\n /** Whether to show the platinum gradient border ring. @default false */\n platinumShow?: boolean;\n /** Whether to apply the NSFW blur filter over the image. @default false */\n NSFWShow?: boolean;\n}\n\n/** Props for the low-level {@link AvatarRoot} compound component. */\nexport interface AvatarRootProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,\n AvatarStyleProps {}\n\n/**\n * Low-level avatar root for custom compositions. Provides size context to\n * child `AvatarImage` and `AvatarFallback` components.\n *\n * Prefer the higher-level {@link Avatar} component for most use cases.\n */\nconst AvatarRoot = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Root>,\n AvatarRootProps\n>(\n (\n {\n className,\n size = 40,\n onlineIndicator = false,\n platinumShow = false,\n NSFWShow = false,\n children,\n ...props\n },\n ref,\n ) => {\n const statusPosition = STATUS_POSITIONS[size];\n\n return (\n <AvatarContext.Provider value={{ size, NSFWShow }}>\n <span className=\"relative inline-flex\">\n <AvatarPrimitive.Root\n ref={ref}\n data-testid=\"avatar\"\n className={cn(\n \"relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-background-avatar\",\n size === 16 && \"size-4 text-2xs\",\n size === 24 && \"size-6 text-xs\",\n size === 32 && \"size-8 text-xs\",\n size === 40 && \"size-10 text-sm\",\n size === 48 && \"size-12 text-base\",\n size === 64 && \"size-16 text-xl\",\n size === 88 && \"size-22 text-2xl\",\n size === 148 && \"size-37 text-4xl\",\n className,\n )}\n {...props}\n >\n {children}\n </AvatarPrimitive.Root>\n {platinumShow && (\n <span\n className=\"pointer-events-none absolute inset-0 rounded-full\"\n style={{\n background: `linear-gradient(143deg, #504F54 0%, #B1B1B1 20.3154%, #13181C 37.3727%, #C6C6C8 58.8154%, #FFFFFF 69.3154%, #0C0F14 81.3154%, #696A6E 100%)`,\n WebkitMask: \"radial-gradient(circle closest-side, transparent 96%, black 96%)\",\n mask: \"radial-gradient(circle closest-side, transparent 96%, black 96%)\",\n }}\n aria-hidden=\"true\"\n />\n )}\n {onlineIndicator && (\n <span\n className={cn(\n \"absolute rounded-full border-surface-primary bg-brand-primary-default\",\n statusPosition.borderSize,\n statusPosition.indicatorSize,\n )}\n style={{\n top: `${statusPosition.top}px`,\n right: `${statusPosition.right}px`,\n }}\n aria-hidden=\"true\"\n />\n )}\n </span>\n </AvatarContext.Provider>\n );\n },\n);\n\nAvatarRoot.displayName = \"AvatarRoot\";\n\n/** Props for the {@link AvatarImage} compound component. */\nexport interface AvatarImageProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> {}\n\n/** Renders the avatar image. Automatically applies the NSFW blur when enabled on the parent `AvatarRoot`. */\nconst AvatarImage = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Image>,\n AvatarImageProps\n>(({ className, ...props }, ref) => {\n const { NSFWShow } = React.useContext(AvatarContext);\n return (\n <AvatarPrimitive.Image\n ref={ref}\n className={cn(\"size-full object-cover\", NSFWShow && \"blur-md\", className)}\n {...props}\n />\n );\n});\n\nAvatarImage.displayName = \"AvatarImage\";\n\n/** Props for the {@link AvatarFallback} compound component. */\nexport interface AvatarFallbackProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> {}\n\n/** Renders fallback content (e.g. initials or an icon) when the avatar image has not loaded. */\nconst AvatarFallback = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Fallback>,\n AvatarFallbackProps\n>(({ className, children, ...props }, ref) => (\n <AvatarPrimitive.Fallback\n ref={ref}\n className={cn(\n \"flex size-full items-center justify-center font-semibold text-content-primary uppercase leading-none\",\n className,\n )}\n delayMs={0}\n {...props}\n >\n {children}\n </AvatarPrimitive.Fallback>\n));\n\nAvatarFallback.displayName = \"AvatarFallback\";\n\nexport interface AvatarProps\n extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,\n AvatarStyleProps {\n /** URL of the avatar image. */\n src?: string;\n /** Alt text for the avatar image. @default \"Avatar\" */\n alt?: string;\n /** Fallback content rendered when the image has not loaded (e.g. initials or an icon). */\n fallback?: React.ReactNode;\n}\n\n/**\n * Displays a user avatar with optional online indicator, platinum border, and\n * NSFW blur. Pass `src` and `fallback` for the simple API, or compose your own\n * layout with `AvatarRoot`, `AvatarImage`, and `AvatarFallback` as children.\n *\n * @example\n * ```tsx\n * <Avatar src=\"/photo.jpg\" alt=\"Jane Doe\" fallback=\"JD\" size={48} />\n * ```\n */\nexport const Avatar = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Root>,\n AvatarProps\n>(\n (\n {\n className,\n size = 40,\n src,\n alt,\n fallback,\n onlineIndicator = false,\n platinumShow = false,\n NSFWShow = false,\n children,\n ...props\n },\n ref,\n ) => {\n const rootProps = {\n ref,\n size,\n onlineIndicator,\n platinumShow,\n NSFWShow,\n className,\n ...props,\n };\n\n if (children) {\n return <AvatarRoot {...rootProps}>{children}</AvatarRoot>;\n }\n\n return (\n <AvatarRoot {...rootProps}>\n {src && <AvatarImage src={src} alt={alt ?? \"Avatar\"} />}\n <AvatarFallback>{fallback}</AvatarFallback>\n </AvatarRoot>\n );\n },\n);\n\nAvatar.displayName = \"Avatar\";\n\nexport { AvatarRoot, AvatarImage, AvatarFallback };\n"],"names":["React","jsx","jsxs","AvatarPrimitive","cn"],"mappings":"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"}
@@ -58,12 +58,13 @@ const DialogContent = React__namespace.forwardRef(
58
58
  portal = true,
59
59
  showMobileHandle = true,
60
60
  mobilePresentation = "sheet",
61
+ overlayProps,
61
62
  style,
62
63
  onOpenAutoFocus,
63
64
  ...props
64
65
  }, ref) => {
65
66
  const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
66
- overlay && /* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
67
+ overlay && /* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, { ...overlayProps }),
67
68
  /* @__PURE__ */ jsxRuntime.jsxs(
68
69
  DialogPrimitive__namespace.Content,
69
70
  {