@ai-matrx/media 0.1.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/react.cjs ADDED
@@ -0,0 +1,2127 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/react.ts
22
+ var react_exports = {};
23
+ __export(react_exports, {
24
+ AlertCircleIcon: () => AlertCircleIcon,
25
+ ArchiveIcon: () => ArchiveIcon,
26
+ BracesIcon: () => BracesIcon,
27
+ CheckCircle2Icon: () => CheckCircle2Icon,
28
+ CheckIcon: () => CheckIcon,
29
+ CodeIcon: () => CodeIcon,
30
+ CopyIcon: () => CopyIcon,
31
+ DatabaseIcon: () => DatabaseIcon,
32
+ DownloadIcon: () => DownloadIcon,
33
+ ExternalLinkIcon: () => ExternalLinkIcon,
34
+ FileAudioIcon: () => FileAudioIcon,
35
+ FileCodeIcon: () => FileCodeIcon,
36
+ FileIcon: () => FileIcon2,
37
+ FileJsonIcon: () => FileJsonIcon,
38
+ FileSpreadsheetIcon: () => FileSpreadsheetIcon,
39
+ FileTextIcon: () => FileTextIcon,
40
+ FileTypeIcon: () => FileTypeIcon,
41
+ FileUploadDropzone: () => FileUploadDropzone,
42
+ FileVideoIcon: () => FileVideoIcon,
43
+ FolderIcon: () => FolderIcon,
44
+ FolderOpenIcon: () => FolderOpenIcon,
45
+ ImageIcon: () => ImageIcon,
46
+ ImageOffIcon: () => ImageOffIcon,
47
+ InlineMediaRef: () => InlineMediaRef,
48
+ MediaActionToolbar: () => MediaActionToolbar,
49
+ MediaLightbox: () => MediaLightbox,
50
+ MediaThumbnail: () => MediaThumbnail,
51
+ MusicIcon: () => MusicIcon,
52
+ PackageBoxIcon: () => PackageBoxIcon,
53
+ PenToolIcon: () => PenToolIcon,
54
+ Share2Icon: () => Share2Icon,
55
+ SubtitlesIcon: () => SubtitlesIcon,
56
+ UploadIcon: () => UploadIcon,
57
+ UploadProgressList: () => UploadProgressList,
58
+ VideoIcon: () => VideoIcon,
59
+ VideoOffIcon: () => VideoOffIcon,
60
+ VolumeXIcon: () => VolumeXIcon,
61
+ XIcon: () => XIcon,
62
+ cn: () => cn,
63
+ formatFileSize: () => formatFileSize,
64
+ useMediaDropzone: () => useMediaDropzone
65
+ });
66
+ module.exports = __toCommonJS(react_exports);
67
+
68
+ // src/react/cn.ts
69
+ var import_tailwind_merge = require("tailwind-merge");
70
+ function cn(...inputs) {
71
+ return (0, import_tailwind_merge.twMerge)(inputs.filter(Boolean).join(" "));
72
+ }
73
+
74
+ // src/react/icons.tsx
75
+ var import_jsx_runtime = require("react/jsx-runtime");
76
+ function svgProps(props) {
77
+ return {
78
+ xmlns: "http://www.w3.org/2000/svg",
79
+ viewBox: "0 0 24 24",
80
+ width: 24,
81
+ height: 24,
82
+ fill: "none",
83
+ stroke: "currentColor",
84
+ strokeWidth: 2,
85
+ strokeLinecap: "round",
86
+ strokeLinejoin: "round",
87
+ "aria-hidden": true,
88
+ ...props
89
+ };
90
+ }
91
+ function ArchiveIcon(props) {
92
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
93
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "20", height: "5", x: "2", y: "3", rx: "1" }),
94
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8" }),
95
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 12h4" })
96
+ ] });
97
+ }
98
+ function BracesIcon(props) {
99
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
100
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1" }),
101
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1" })
102
+ ] });
103
+ }
104
+ function CheckIcon(props) {
105
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { ...svgProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20 6 9 17l-5-5" }) });
106
+ }
107
+ function CodeIcon(props) {
108
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m16 18 6-6-6-6" }),
110
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m8 6-6 6 6 6" })
111
+ ] });
112
+ }
113
+ function CopyIcon(props) {
114
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
117
+ ] });
118
+ }
119
+ function DatabaseIcon(props) {
120
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ellipse", { cx: "12", cy: "5", rx: "9", ry: "3" }),
122
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 5V19A9 3 0 0 0 21 19V5" }),
123
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 12A9 3 0 0 0 21 12" })
124
+ ] });
125
+ }
126
+ function DownloadIcon(props) {
127
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 15V3" }),
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
130
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m7 10 5 5 5-5" })
131
+ ] });
132
+ }
133
+ function ExternalLinkIcon(props) {
134
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
135
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M15 3h6v6" }),
136
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 14 21 3" }),
137
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" })
138
+ ] });
139
+ }
140
+ function FileIcon(props) {
141
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
142
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" }),
143
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" })
144
+ ] });
145
+ }
146
+ function FileAudioIcon(props) {
147
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
148
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 6.835V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-.343" }),
149
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
150
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2 19a2 2 0 0 1 4 0v1a2 2 0 0 1-4 0v-4a6 6 0 0 1 12 0v4a2 2 0 0 1-4 0v-1a2 2 0 0 1 4 0" })
151
+ ] });
152
+ }
153
+ function FileCodeIcon(props) {
154
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" }),
156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 12.5 8 15l2 2.5" }),
158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m14 12.5 2 2.5-2 2.5" })
159
+ ] });
160
+ }
161
+ function FileJsonIcon(props) {
162
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" }),
164
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1" }),
166
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1" })
167
+ ] });
168
+ }
169
+ function FileSpreadsheetIcon(props) {
170
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" }),
172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 13h2" }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 13h2" }),
175
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 17h2" }),
176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 17h2" })
177
+ ] });
178
+ }
179
+ function FileTextIcon(props) {
180
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" }),
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 9H8" }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 13H8" }),
185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 17H8" })
186
+ ] });
187
+ }
188
+ function FileTypeIcon(props) {
189
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" }),
191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 18h2" }),
193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 12v6" }),
194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 13v-.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v.5" })
195
+ ] });
196
+ }
197
+ function FileVideoIcon(props) {
198
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" }),
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M15.033 13.44a.647.647 0 0 1 0 1.12l-4.065 2.352a.645.645 0 0 1-.968-.56v-4.704a.645.645 0 0 1 .967-.56z" })
202
+ ] });
203
+ }
204
+ function FolderIcon(props) {
205
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { ...svgProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" }) });
206
+ }
207
+ function FolderOpenIcon(props) {
208
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { ...svgProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2" }) });
209
+ }
210
+ function ImageIcon(props) {
211
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "9", cy: "9", r: "2" }),
214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
215
+ ] });
216
+ }
217
+ function ImageOffIcon(props) {
218
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2", x2: "22", y1: "2", y2: "22" }),
220
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10.41 10.41a2 2 0 1 1-2.83-2.83" }),
221
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "13.5", x2: "6", y1: "13.5", y2: "21" }),
222
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "18", x2: "21", y1: "12", y2: "15" }),
223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59" }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 15V5a2 2 0 0 0-2-2H9" })
225
+ ] });
226
+ }
227
+ function MusicIcon(props) {
228
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
229
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 18V5l12-2v13" }),
230
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "18", r: "3" }),
231
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "18", cy: "16", r: "3" })
232
+ ] });
233
+ }
234
+ function PackageBoxIcon(props) {
235
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
236
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z" }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 22V12" }),
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3.29 7 12 12 20.71 7" }),
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m7.5 4.27 9 5.15" })
240
+ ] });
241
+ }
242
+ function PenToolIcon(props) {
243
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M15.707 21.293a1 1 0 0 1-1.414 0l-1.586-1.586a1 1 0 0 1 0-1.414l5.586-5.586a1 1 0 0 1 1.414 0l1.586 1.586a1 1 0 0 1 0 1.414z" }),
245
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m18 13-1.375-6.874a1 1 0 0 0-.746-.776L3.235 2.028a1 1 0 0 0-1.207 1.207L5.35 15.879a1 1 0 0 0 .776.746L13 18" }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m2.3 2.3 7.286 7.286" }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "11", r: "2" })
248
+ ] });
249
+ }
250
+ function Share2Icon(props) {
251
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
252
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "18", cy: "5", r: "3" }),
253
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "12", r: "3" }),
254
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "18", cy: "19", r: "3" }),
255
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8.59", x2: "15.42", y1: "13.51", y2: "17.49" }),
256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "15.41", x2: "8.59", y1: "6.51", y2: "10.49" })
257
+ ] });
258
+ }
259
+ function SubtitlesIcon(props) {
260
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "18", height: "14", x: "3", y: "5", rx: "2", ry: "2" }),
262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7 15h4M15 15h2M7 11h2M13 11h4" })
263
+ ] });
264
+ }
265
+ function UploadIcon(props) {
266
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
267
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 3v12" }),
268
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m17 8-5-5-5 5" }),
269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" })
270
+ ] });
271
+ }
272
+ function VideoIcon(props) {
273
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" }),
275
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "6", width: "14", height: "12", rx: "2" })
276
+ ] });
277
+ }
278
+ function VideoOffIcon(props) {
279
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
280
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10.66 6H14a2 2 0 0 1 2 2v2.5l5.248-3.062A.5.5 0 0 1 22 7.87v8.196" }),
281
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2" }),
282
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m2 2 20 20" })
283
+ ] });
284
+ }
285
+ function VolumeXIcon(props) {
286
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z" }),
288
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "22", x2: "16", y1: "9", y2: "15" }),
289
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "16", x2: "22", y1: "9", y2: "15" })
290
+ ] });
291
+ }
292
+ function XIcon(props) {
293
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
294
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18 6 6 18" }),
295
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m6 6 12 12" })
296
+ ] });
297
+ }
298
+ function AlertCircleIcon(props) {
299
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
300
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
301
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", x2: "12", y1: "8", y2: "12" }),
302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" })
303
+ ] });
304
+ }
305
+ function CheckCircle2Icon(props) {
306
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
307
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21.801 10A10 10 0 1 1 17 3.335" }),
308
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m9 11 3 3L22 4" })
309
+ ] });
310
+ }
311
+
312
+ // src/core/file-kind.ts
313
+ var ROWS = [
314
+ [["jpg", "jpeg", "png", "gif", "webp", "avif", "heic", "heif", "bmp", "tif", "tiff", "ico"], "IMAGE", "text-emerald-500", "image", "Image"],
315
+ [["svg"], "IMAGE", "text-amber-500", "pen-tool", "SVG image"],
316
+ [["mp4", "mov", "webm", "mkv", "avi", "m4v"], "VIDEO", "text-purple-500", "video", "Video"],
317
+ [["mp3", "wav", "ogg", "m4a", "aac", "flac", "opus"], "AUDIO", "text-pink-500", "music", "Audio"],
318
+ [["pdf"], "DOCUMENT", "text-red-500", "file-type", "PDF Document"],
319
+ [["md", "markdown", "mdx", "rst", "adoc", "asciidoc", "org"], "DOCUMENT", "text-slate-400", "file-text", "Markdown"],
320
+ [["txt", "text", "asc", "me", "log", "out", "err"], "DOCUMENT", "text-muted-foreground", "file-text", "Text"],
321
+ [["dockerfile", "containerfile", "lua"], "CODE", "text-blue-500", "code", "Code"],
322
+ [["mk", "make", "swift", "ml", "mli", "fs", "fsi", "fsx", "svelte"], "CODE", "text-orange-500", "code", "Code"],
323
+ [["diff", "patch", "pem", "csr", "crt", "cer", "der", "key", "pub", "p7b", "p7c", "pfx", "p12"], "CODE", "text-amber-500", "file-text", "Text"],
324
+ [["ini", "cfg", "conf", "config", "properties", "prefs"], "CODE", "text-amber-400", "braces", "Config"],
325
+ [["tex", "latex", "ltx", "sty", "cls", "bib"], "CODE", "text-emerald-500", "file-text", "LaTeX"],
326
+ [["srt", "vtt"], "SUBTITLES", "text-cyan-500", "subtitles", "Subtitles"],
327
+ [["js", "mjs", "cjs", "jsx"], "CODE", "text-yellow-500", "file-code", "JavaScript"],
328
+ [["ts", "tsx"], "CODE", "text-blue-500", "file-code", "TypeScript"],
329
+ [["py", "clj", "cljs", "cljc", "edn", "vue", "styl", "stylus"], "CODE", "text-emerald-500", "code", "Code"],
330
+ [["rb", "rbw", "rake", "ru", "gemspec", "scala", "sbt"], "CODE", "text-red-500", "code", "Code"],
331
+ [["go", "dart"], "CODE", "text-cyan-500", "code", "Code"],
332
+ [["rs"], "CODE", "text-orange-600", "code", "Rust"],
333
+ [["java"], "CODE", "text-red-600", "code", "Java"],
334
+ [["c", "h"], "CODE", "text-blue-600", "code", "C"],
335
+ [["cpp", "cc", "cxx", "hpp"], "CODE", "text-blue-700", "code", "C++"],
336
+ [["cs"], "CODE", "text-purple-600", "code", "C#"],
337
+ [["html", "htm", "kt", "kts"], "CODE", "text-orange-400", "code", "Code"],
338
+ [["css"], "CODE", "text-sky-400", "code", "CSS"],
339
+ [["scss"], "CODE", "text-pink-400", "code", "SCSS"],
340
+ [["sh", "bash", "zsh", "fish", "ksh", "ash", "bat", "cmd", "twig", "jinja", "j2", "njk"], "CODE", "text-emerald-400", "code", "Script"],
341
+ [["sql"], "CODE", "text-amber-400", "code", "SQL"],
342
+ [["pl", "pm"], "CODE", "text-indigo-400", "code", "Perl"],
343
+ [["r", "rmd", "proto", "ps1", "psm1", "psd1", "less"], "CODE", "text-blue-400", "code", "Code"],
344
+ [["ex", "exs"], "CODE", "text-purple-400", "code", "Elixir"],
345
+ [["erl", "hrl"], "CODE", "text-rose-500", "code", "Erlang"],
346
+ [["hs", "lhs"], "CODE", "text-violet-500", "code", "Haskell"],
347
+ [["zig"], "CODE", "text-yellow-500", "code", "Zig"],
348
+ [["nim", "nims"], "CODE", "text-yellow-400", "code", "Nim"],
349
+ [["jl"], "CODE", "text-purple-500", "code", "Julia"],
350
+ [["astro"], "CODE", "text-fuchsia-500", "code", "Astro"],
351
+ [["graphql", "gql", "graphqls"], "CODE", "text-pink-500", "code", "GraphQL"],
352
+ [["sol"], "CODE", "text-slate-400", "code", "Solidity"],
353
+ [["php", "phtml", "phps"], "CODE", "text-indigo-500", "code", "PHP"],
354
+ [["hbs", "handlebars", "mustache", "ejs", "liquid"], "CODE", "text-amber-500", "code", "Template"],
355
+ [["json", "jsonc", "json5", "har", "geojson", "topojson"], "DATA", "text-amber-500", "file-json", "JSON"],
356
+ [["yaml", "yml", "toml"], "CODE", "text-amber-500", "braces", "Config"],
357
+ [["xml"], "DATA", "text-amber-500", "braces", "XML"],
358
+ [["csv", "tsv"], "DATA", "text-orange-500", "file-spreadsheet", "Spreadsheet data"],
359
+ [["sqlite", "sqlite3", "db"], "DATA", "text-blue-400", "database", "Database"],
360
+ [["ttl", "n3", "nt", "nq", "trig"], "DATA", "text-fuchsia-400", "braces", "RDF data"],
361
+ [["xlsx", "xls"], "DOCUMENT", "text-emerald-600", "file-spreadsheet", "Excel workbook"],
362
+ [["doc", "docx"], "DOCUMENT", "text-blue-500", "file-text", "Word document"],
363
+ [["ppt", "pptx"], "DOCUMENT", "text-orange-600", "file-type", "Presentation"],
364
+ [["ipynb"], "NOTEBOOK", "text-orange-400", "file-json", "Notebook"],
365
+ [["epub"], "EBOOK", "text-indigo-500", "file-text", "eBook"],
366
+ [["eml"], "EMAIL", "text-blue-400", "file-text", "Email"],
367
+ [["glb", "gltf", "stl", "obj", "fbx"], "MODEL_3D", "text-violet-400", "package", "3D model"],
368
+ [["zip", "rar", "7z"], "ARCHIVE", "text-amber-600", "archive", "Archive"],
369
+ [["tar", "gz", "tgz"], "ARCHIVE", "text-amber-600", "package", "Archive"]
370
+ ];
371
+ var BY_EXTENSION = /* @__PURE__ */ new Map();
372
+ for (const [extensions, category, colorClass, icon, displayName] of ROWS) {
373
+ for (const ext of extensions) {
374
+ BY_EXTENSION.set(ext, { category, colorClass, icon, displayName });
375
+ }
376
+ }
377
+ var UNKNOWN_DETAILS = {
378
+ category: "UNKNOWN",
379
+ displayName: "File",
380
+ colorClass: "text-muted-foreground",
381
+ icon: "file"
382
+ };
383
+ var ASSUMED_TEXT_DETAILS = {
384
+ category: "DOCUMENT",
385
+ displayName: "Text",
386
+ colorClass: "text-muted-foreground",
387
+ icon: "file-text"
388
+ };
389
+ var FOLDER_DETAILS = {
390
+ category: "FOLDER",
391
+ displayName: "Folder",
392
+ colorClass: "text-sky-500",
393
+ icon: "folder"
394
+ };
395
+ var IMAGE_DETAILS = BY_EXTENSION.get("png");
396
+ var VIDEO_DETAILS = BY_EXTENSION.get("mp4");
397
+ var AUDIO_DETAILS = BY_EXTENSION.get("mp3");
398
+ var PDF_DETAILS = BY_EXTENSION.get("pdf");
399
+ var SVG_DETAILS = BY_EXTENSION.get("svg");
400
+ function basenameOf(path) {
401
+ const idx = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
402
+ return idx >= 0 ? path.slice(idx + 1) : path;
403
+ }
404
+ function extOf(name) {
405
+ const idx = name.lastIndexOf(".");
406
+ if (idx <= 0 || idx === name.length - 1) return null;
407
+ return name.slice(idx + 1).toLowerCase();
408
+ }
409
+ function dotfileLooksLikeText(filename) {
410
+ const name = basenameOf(filename);
411
+ if (!name.startsWith(".")) return false;
412
+ for (let i = 1; i < name.length; i++) {
413
+ const c = name.charCodeAt(i);
414
+ if (c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c === 45) {
415
+ return true;
416
+ }
417
+ }
418
+ return false;
419
+ }
420
+ function canonicalMimeBase(raw) {
421
+ return raw.split(";")[0]?.trim().toLowerCase() ?? raw;
422
+ }
423
+ function getFileKindDetails(fileName, mimeType) {
424
+ const name = basenameOf(fileName);
425
+ const lower = name.toLowerCase();
426
+ const ext = lower === "dockerfile" || lower === "containerfile" ? lower : extOf(name);
427
+ const fromExt = ext ? BY_EXTENSION.get(ext) : void 0;
428
+ const mime = mimeType ? canonicalMimeBase(mimeType) : null;
429
+ if (mime) {
430
+ if (mime === "image/svg+xml") return fromExt ?? SVG_DETAILS;
431
+ if (mime.startsWith("image/")) return fromExt?.category === "IMAGE" ? fromExt : IMAGE_DETAILS;
432
+ if (mime.startsWith("video/")) return fromExt?.category === "VIDEO" ? fromExt : VIDEO_DETAILS;
433
+ if (mime.startsWith("audio/")) return fromExt?.category === "AUDIO" ? fromExt : AUDIO_DETAILS;
434
+ if (mime === "application/pdf") return PDF_DETAILS;
435
+ }
436
+ if (fromExt) return fromExt;
437
+ if (dotfileLooksLikeText(fileName)) return ASSUMED_TEXT_DETAILS;
438
+ return UNKNOWN_DETAILS;
439
+ }
440
+ function getFolderKindDetails(open = false) {
441
+ return open ? { ...FOLDER_DETAILS, icon: "folder-open" } : FOLDER_DETAILS;
442
+ }
443
+
444
+ // src/react/file-icon.tsx
445
+ var import_jsx_runtime2 = require("react/jsx-runtime");
446
+ var ICONS = {
447
+ image: ImageIcon,
448
+ "pen-tool": PenToolIcon,
449
+ video: VideoIcon,
450
+ music: MusicIcon,
451
+ "file-type": FileTypeIcon,
452
+ "file-text": FileTextIcon,
453
+ code: CodeIcon,
454
+ braces: BracesIcon,
455
+ subtitles: SubtitlesIcon,
456
+ "file-code": FileCodeIcon,
457
+ "file-json": FileJsonIcon,
458
+ "file-spreadsheet": FileSpreadsheetIcon,
459
+ database: DatabaseIcon,
460
+ package: PackageBoxIcon,
461
+ archive: ArchiveIcon,
462
+ folder: FolderIcon,
463
+ "folder-open": FolderOpenIcon,
464
+ file: FileIcon,
465
+ "file-audio": FileAudioIcon,
466
+ "file-video": FileVideoIcon
467
+ };
468
+ function FileIcon2({
469
+ fileName,
470
+ mimeType = null,
471
+ isFolder,
472
+ open,
473
+ colorClass,
474
+ className,
475
+ size = 16
476
+ }) {
477
+ const details = isFolder ? getFolderKindDetails(open) : getFileKindDetails(fileName ?? "", mimeType);
478
+ const Icon = ICONS[details.icon];
479
+ const style = {
480
+ width: `${size}px`,
481
+ height: `${size}px`,
482
+ minWidth: `${size}px`
483
+ };
484
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
485
+ Icon,
486
+ {
487
+ style,
488
+ className: cn("shrink-0", colorClass ?? details.colorClass, className),
489
+ "aria-hidden": "true"
490
+ }
491
+ );
492
+ }
493
+
494
+ // src/react/inline-media-ref.tsx
495
+ var import_react5 = require("react");
496
+
497
+ // src/core/context.ts
498
+ var import_react = require("react");
499
+ var CLIENT_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.client-context");
500
+ var PORTS_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.host-ports-context");
501
+ function clientContext() {
502
+ const host = globalThis;
503
+ host[CLIENT_SLOT] ??= (0, import_react.createContext)(null);
504
+ return host[CLIENT_SLOT];
505
+ }
506
+ var INERT_PORTS = {};
507
+ function portsContext() {
508
+ const host = globalThis;
509
+ host[PORTS_SLOT] ??= (0, import_react.createContext)(INERT_PORTS);
510
+ return host[PORTS_SLOT];
511
+ }
512
+ function useMediaClient() {
513
+ const client = (0, import_react.useContext)(clientContext());
514
+ if (!client) {
515
+ throw new Error(
516
+ "@ai-matrx/media: no MediaClient. Mount <MediaProvider client={...}> above every media surface (canonically wired to @ai-matrx/data/files)."
517
+ );
518
+ }
519
+ return client;
520
+ }
521
+ function useMediaHostPorts() {
522
+ return (0, import_react.useContext)(portsContext());
523
+ }
524
+
525
+ // src/core/use-media-blob.ts
526
+ var import_react2 = require("react");
527
+
528
+ // src/core/ref-key.ts
529
+ function stableRefKey(ref) {
530
+ if (!ref) return "<none>";
531
+ if (typeof ref === "string") return `s:${ref}`;
532
+ if (ref.file_id) return `id:${ref.file_id}:${ref.mime_type ?? ""}`;
533
+ if (ref.url) return `url:${ref.url}:${ref.mime_type ?? ""}`;
534
+ return "<empty-ref>";
535
+ }
536
+
537
+ // src/core/use-media-blob.ts
538
+ function useMediaBlob(ref) {
539
+ const client = useMediaClient();
540
+ const refKey = stableRefKey(ref);
541
+ const [state, setState] = (0, import_react2.useState)({ url: null, blob: null, loading: !!ref, error: null });
542
+ const [retryToken, setRetryToken] = (0, import_react2.useState)(0);
543
+ (0, import_react2.useEffect)(() => {
544
+ if (!ref) {
545
+ setState({ url: null, blob: null, loading: false, error: null });
546
+ return void 0;
547
+ }
548
+ let cancelled = false;
549
+ let release = null;
550
+ setState({ url: null, blob: null, loading: true, error: null });
551
+ client.getBlob(ref).then(
552
+ (handle) => {
553
+ if (cancelled) {
554
+ handle.release();
555
+ return;
556
+ }
557
+ release = handle.release;
558
+ setState({ url: handle.url, blob: handle.blob, loading: false, error: null });
559
+ },
560
+ (err) => {
561
+ if (cancelled) return;
562
+ const message = err instanceof Error ? err.message : String(err ?? "Failed to load file");
563
+ setState({ url: null, blob: null, loading: false, error: message });
564
+ }
565
+ );
566
+ return () => {
567
+ cancelled = true;
568
+ release?.();
569
+ };
570
+ }, [client, refKey, retryToken]);
571
+ return {
572
+ ...state,
573
+ retry: () => setRetryToken((t) => t + 1)
574
+ };
575
+ }
576
+
577
+ // src/core/use-media-load-recovery.ts
578
+ var import_react3 = require("react");
579
+
580
+ // src/types.ts
581
+ function mintDurableSrc(url) {
582
+ return url;
583
+ }
584
+
585
+ // src/core/use-media-load-recovery.ts
586
+ function useMediaLoadRecovery(src, options) {
587
+ const client = useMediaClient();
588
+ const recoverable = options?.recoverable !== false;
589
+ const onTerminal = options?.onTerminal;
590
+ const [result, setResult] = (0, import_react3.useState)({ source: src, retryKey: 0, failed: false });
591
+ const attemptRef = (0, import_react3.useRef)(0);
592
+ (0, import_react3.useEffect)(() => {
593
+ attemptRef.current = 0;
594
+ }, [src]);
595
+ const onLoadError = (event) => {
596
+ if (!src || !recoverable) {
597
+ setResult({ source: src, retryKey: 0, failed: true });
598
+ onTerminal?.(event);
599
+ return;
600
+ }
601
+ attemptRef.current += 1;
602
+ const attempt = attemptRef.current;
603
+ client.recoverLoadError(mintDurableSrc(src), attempt).then(
604
+ (verdict) => {
605
+ if (verdict === "retry") {
606
+ setResult((current) => ({
607
+ source: src,
608
+ retryKey: current.source === src ? current.retryKey + 1 : 1,
609
+ failed: false
610
+ }));
611
+ } else {
612
+ setResult({ source: src, retryKey: 0, failed: true });
613
+ onTerminal?.(event);
614
+ }
615
+ },
616
+ () => {
617
+ setResult({ source: src, retryKey: 0, failed: true });
618
+ onTerminal?.(event);
619
+ }
620
+ );
621
+ };
622
+ return {
623
+ retryKey: result.source === src ? result.retryKey : 0,
624
+ onLoadError,
625
+ failed: result.source === src ? result.failed : false
626
+ };
627
+ }
628
+
629
+ // src/core/use-media-resolution.ts
630
+ var import_react4 = require("react");
631
+ function useMediaResolution(ref) {
632
+ const client = useMediaClient();
633
+ const refKey = stableRefKey(ref);
634
+ return (0, import_react4.useMemo)(() => {
635
+ if (!ref) return { resolution: null, status: "empty", reason: null };
636
+ try {
637
+ const resolution = client.resolve(ref);
638
+ if (!resolution) return { resolution: null, status: "empty", reason: null };
639
+ return { resolution, status: "ready", reason: null };
640
+ } catch (err) {
641
+ return {
642
+ resolution: null,
643
+ status: "unavailable",
644
+ reason: client.classifyError(err)
645
+ };
646
+ }
647
+ }, [client, refKey]);
648
+ }
649
+
650
+ // src/react/inline-media-ref.tsx
651
+ var import_jsx_runtime3 = require("react/jsx-runtime");
652
+ var SIZE_PX = {
653
+ xs: 24,
654
+ sm: 32,
655
+ md: 64,
656
+ lg: 128,
657
+ xl: 256
658
+ };
659
+ var ROUNDED_CLASS = {
660
+ none: "",
661
+ sm: "rounded-sm",
662
+ md: "rounded-md",
663
+ lg: "rounded-lg",
664
+ full: "rounded-full"
665
+ };
666
+ function resolveDimensions(size) {
667
+ if (typeof size === "object") return size;
668
+ if (size === "fill") return "fill";
669
+ const px = SIZE_PX[size];
670
+ return { width: px, height: px };
671
+ }
672
+ function inferElementType(ref, resolutionKind, override) {
673
+ if (override) return override;
674
+ if (ref && typeof ref === "object" && ref.mime_type) {
675
+ if (ref.mime_type.startsWith("video/")) return "video";
676
+ if (ref.mime_type.startsWith("audio/")) return "audio";
677
+ }
678
+ if (resolutionKind === "video") return "video";
679
+ if (resolutionKind === "audio") return "audio";
680
+ return "img";
681
+ }
682
+ function prettifyUrl(url) {
683
+ try {
684
+ const u = new URL(url);
685
+ const tail = `${u.pathname}${u.search}`;
686
+ return { host: u.host, tail: tail === "/" ? "" : tail };
687
+ } catch {
688
+ return { host: null, tail: url };
689
+ }
690
+ }
691
+ var INERT_PLAYBACK = {
692
+ useMediaElementSink(forward) {
693
+ return (0, import_react5.useCallback)(
694
+ (node) => {
695
+ if (!forward) return;
696
+ if (typeof forward === "function") forward(node);
697
+ else forward.current = node;
698
+ },
699
+ [forward]
700
+ );
701
+ },
702
+ usePlaybackSession() {
703
+ }
704
+ };
705
+ function FallbackVisual({
706
+ kind,
707
+ dimensions,
708
+ rounded,
709
+ border,
710
+ className,
711
+ icon
712
+ }) {
713
+ const isFill = dimensions === "fill";
714
+ const wrapperCls = cn(
715
+ "flex items-center justify-center bg-muted/60 text-muted-foreground",
716
+ ROUNDED_CLASS[rounded],
717
+ border === "subtle" && "border border-border",
718
+ kind === "skeleton" && "animate-pulse",
719
+ isFill && "w-full h-full",
720
+ className
721
+ );
722
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
723
+ "div",
724
+ {
725
+ className: wrapperCls,
726
+ style: isFill ? void 0 : { width: dimensions.width, height: dimensions.height },
727
+ "aria-hidden": kind === "skeleton",
728
+ children: kind === "icon" ? icon : null
729
+ }
730
+ );
731
+ }
732
+ function InformativeErrorFallback({
733
+ url,
734
+ alt,
735
+ elementType,
736
+ mediaRef,
737
+ dimensions,
738
+ rounded,
739
+ className
740
+ }) {
741
+ const shortAxis = dimensions === "fill" ? Number.POSITIVE_INFINITY : Math.min(dimensions.width, dimensions.height);
742
+ const isCompact = dimensions !== "fill" && shortAxis < 64;
743
+ const isMidsize = shortAxis < 140;
744
+ const fileId = mediaRef && typeof mediaRef === "object" && "file_id" in mediaRef ? mediaRef.file_id ?? null : null;
745
+ const FailIcon = elementType === "video" ? VideoOffIcon : elementType === "audio" ? VolumeXIcon : ImageOffIcon;
746
+ const tooltip = [
747
+ `Failed to load ${elementType}`,
748
+ alt && `alt: ${alt}`,
749
+ `url: ${url}`,
750
+ fileId && `file_id: ${fileId}`
751
+ ].filter(Boolean).join("\n");
752
+ if (isCompact) {
753
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
754
+ "div",
755
+ {
756
+ className: cn(
757
+ "flex items-center justify-center bg-destructive/5 text-destructive border border-destructive/40 border-dashed",
758
+ ROUNDED_CLASS[rounded],
759
+ className
760
+ ),
761
+ style: { width: dimensions.width, height: dimensions.height },
762
+ role: "img",
763
+ "aria-label": `Failed to load ${elementType}${alt ? `: ${alt}` : ""}`,
764
+ title: tooltip,
765
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FailIcon, { className: "h-1/2 w-1/2 max-h-5 max-w-5" })
766
+ }
767
+ );
768
+ }
769
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
770
+ InformativeErrorPanel,
771
+ {
772
+ url,
773
+ alt,
774
+ elementType,
775
+ fileId,
776
+ dimensions,
777
+ rounded,
778
+ className,
779
+ compact: isMidsize,
780
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FailIcon, { className: "h-3.5 w-3.5 shrink-0" }),
781
+ tooltip
782
+ }
783
+ );
784
+ }
785
+ function InformativeErrorPanel({
786
+ url,
787
+ alt,
788
+ elementType,
789
+ fileId,
790
+ dimensions,
791
+ rounded,
792
+ className,
793
+ compact,
794
+ icon,
795
+ tooltip
796
+ }) {
797
+ const [copied, setCopied] = (0, import_react5.useState)(false);
798
+ const isFill = dimensions === "fill";
799
+ const { host, tail } = prettifyUrl(url);
800
+ const handleCopy = (0, import_react5.useCallback)(
801
+ (event) => {
802
+ event.stopPropagation();
803
+ event.preventDefault();
804
+ navigator.clipboard?.writeText(url).then(() => {
805
+ setCopied(true);
806
+ setTimeout(() => setCopied(false), 1500);
807
+ }).catch(() => {
808
+ });
809
+ },
810
+ [url]
811
+ );
812
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
813
+ "div",
814
+ {
815
+ className: cn(
816
+ "flex flex-col bg-destructive/5 text-foreground border border-destructive/40 border-dashed overflow-hidden",
817
+ ROUNDED_CLASS[rounded],
818
+ compact ? "p-1.5 gap-0.5" : "p-2 gap-1",
819
+ isFill ? "w-full h-full" : "",
820
+ className
821
+ ),
822
+ style: isFill ? void 0 : { width: dimensions.width, height: dimensions.height },
823
+ role: "img",
824
+ "aria-label": `Failed to load ${elementType}${alt ? `: ${alt}` : ""}`,
825
+ title: tooltip,
826
+ children: [
827
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
828
+ "div",
829
+ {
830
+ className: cn(
831
+ "flex items-center gap-1.5 text-destructive font-medium leading-tight",
832
+ compact ? "text-[10px]" : "text-xs"
833
+ ),
834
+ children: [
835
+ icon,
836
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate", children: elementType === "img" ? "Image failed to load" : elementType === "video" ? "Video failed to load" : "Audio failed to load" })
837
+ ]
838
+ }
839
+ ),
840
+ alt && !compact && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
841
+ "div",
842
+ {
843
+ className: "text-[11px] text-muted-foreground italic line-clamp-1",
844
+ title: alt,
845
+ children: alt
846
+ }
847
+ ),
848
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
849
+ "div",
850
+ {
851
+ className: cn(
852
+ "flex items-end gap-1 mt-auto min-w-0",
853
+ compact ? "text-[9px]" : "text-[10px]"
854
+ ),
855
+ children: [
856
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "font-mono text-muted-foreground truncate flex-1 min-w-0", children: host ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
857
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-foreground/80", children: host }),
858
+ tail && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "opacity-70", children: tail })
859
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: tail }) }),
860
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
861
+ "button",
862
+ {
863
+ type: "button",
864
+ onClick: handleCopy,
865
+ className: cn(
866
+ "shrink-0 p-0.5 rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive transition-colors focus:outline-none focus:ring-1 focus:ring-destructive/40",
867
+ copied && "text-emerald-600 dark:text-emerald-500"
868
+ ),
869
+ "aria-label": copied ? "URL copied" : "Copy URL",
870
+ title: copied ? "URL copied" : "Copy URL",
871
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckIcon, { className: "h-3 w-3", width: 12, height: 12 }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CopyIcon, { className: "h-3 w-3", width: 12, height: 12 })
872
+ }
873
+ )
874
+ ]
875
+ }
876
+ ),
877
+ fileId && !compact && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-[9px] font-mono text-muted-foreground/70 truncate", children: fileId })
878
+ ]
879
+ }
880
+ );
881
+ }
882
+ function InlineMediaRef({
883
+ ref: mediaRef,
884
+ size = "md",
885
+ fit = "cover",
886
+ fallback = "icon",
887
+ fallbackIcon,
888
+ errorFallback = "info",
889
+ alt = "",
890
+ as,
891
+ controls = true,
892
+ autoPlay,
893
+ loop,
894
+ muted,
895
+ playsInline,
896
+ preload,
897
+ controlsList,
898
+ rounded = "md",
899
+ onClick,
900
+ onError,
901
+ onLoad,
902
+ mediaElementRef,
903
+ crossOrigin,
904
+ border = "none",
905
+ className
906
+ }) {
907
+ const ports = useMediaHostPorts();
908
+ const { resolution, status, reason } = useMediaResolution(mediaRef);
909
+ const elementType = inferElementType(mediaRef, resolution?.kind, as);
910
+ const needsBlob = Boolean(
911
+ resolution && (resolution.transport === "blob" || crossOrigin && resolution.isCdn !== true)
912
+ );
913
+ const blob = useMediaBlob(needsBlob ? mediaRef : null);
914
+ const url = needsBlob ? blob.url : resolution?.src ?? null;
915
+ const recovery = useMediaLoadRecovery(url, {
916
+ recoverable: !needsBlob && resolution?.recoverable !== false,
917
+ onTerminal: (event) => onError?.(
918
+ event
919
+ )
920
+ });
921
+ const playback = ports.playbackSession ?? INERT_PLAYBACK;
922
+ const sessionElRef = (0, import_react5.useRef)(null);
923
+ const [isMediaPlaying, setIsMediaPlaying] = (0, import_react5.useState)(false);
924
+ const portSinkRef = playback.useMediaElementSink(
925
+ mediaElementRef
926
+ );
927
+ const sinkRef = (node) => {
928
+ sessionElRef.current = node;
929
+ portSinkRef(node);
930
+ };
931
+ const trackKey = (0, import_react5.useMemo)(() => {
932
+ if (mediaRef && typeof mediaRef === "object" && mediaRef.file_id) {
933
+ return mediaRef.file_id;
934
+ }
935
+ return typeof mediaRef === "string" ? mediaRef : void 0;
936
+ }, [mediaRef]);
937
+ playback.usePlaybackSession(
938
+ {
939
+ isPlaying: isMediaPlaying,
940
+ source: "file-media",
941
+ label: alt || "Media file",
942
+ trackKey
943
+ },
944
+ sessionElRef
945
+ );
946
+ const mediaSessionEvents = {
947
+ onPlay: () => setIsMediaPlaying(true),
948
+ onPause: () => setIsMediaPlaying(false),
949
+ onEnded: () => setIsMediaPlaying(false)
950
+ };
951
+ const mediaResolutionError = status === "unavailable" ? reason ?? "unknown" : blob.error ?? null;
952
+ const dimensions = resolveDimensions(size);
953
+ const isFill = dimensions === "fill";
954
+ const hasLoadError = recovery.failed;
955
+ const handleLoadError = recovery.onLoadError;
956
+ const defaultIcon = elementType === "video" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FileVideoIcon, { className: "h-1/2 w-1/2" }) : elementType === "audio" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FileAudioIcon, { className: "h-1/2 w-1/2" }) : mediaRef ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ImageIcon, { className: "h-1/2 w-1/2" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FileIcon, { className: "h-1/2 w-1/2" });
957
+ const unresolvedDisplayUrl = mediaRef && typeof mediaRef === "object" && mediaRef.file_id ? `file:${mediaRef.file_id}` : "unresolved-media";
958
+ if (!url) {
959
+ if (mediaResolutionError) {
960
+ if (errorFallback === null) return null;
961
+ if (errorFallback === "info") {
962
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
963
+ InformativeErrorFallback,
964
+ {
965
+ url: unresolvedDisplayUrl,
966
+ alt,
967
+ elementType,
968
+ mediaRef,
969
+ dimensions,
970
+ rounded,
971
+ className
972
+ }
973
+ );
974
+ }
975
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
976
+ FallbackVisual,
977
+ {
978
+ kind: errorFallback,
979
+ dimensions,
980
+ rounded,
981
+ border,
982
+ className,
983
+ icon: fallbackIcon ?? defaultIcon
984
+ }
985
+ );
986
+ }
987
+ if (needsBlob && blob.loading) {
988
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
989
+ FallbackVisual,
990
+ {
991
+ kind: "skeleton",
992
+ dimensions,
993
+ rounded,
994
+ border,
995
+ className,
996
+ icon: null
997
+ }
998
+ );
999
+ }
1000
+ if (!fallback) return null;
1001
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1002
+ FallbackVisual,
1003
+ {
1004
+ kind: fallback,
1005
+ dimensions,
1006
+ rounded,
1007
+ border,
1008
+ className,
1009
+ icon: fallbackIcon ?? defaultIcon
1010
+ }
1011
+ );
1012
+ }
1013
+ if (hasLoadError) {
1014
+ if (errorFallback === null) return null;
1015
+ if (errorFallback === "info") {
1016
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1017
+ InformativeErrorFallback,
1018
+ {
1019
+ url,
1020
+ alt,
1021
+ elementType,
1022
+ mediaRef,
1023
+ dimensions,
1024
+ rounded,
1025
+ className
1026
+ }
1027
+ );
1028
+ }
1029
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1030
+ FallbackVisual,
1031
+ {
1032
+ kind: errorFallback,
1033
+ dimensions,
1034
+ rounded,
1035
+ border,
1036
+ className,
1037
+ icon: fallbackIcon ?? defaultIcon
1038
+ }
1039
+ );
1040
+ }
1041
+ const interactiveProps = onClick ? {
1042
+ onClick,
1043
+ onKeyDown: (event) => {
1044
+ if (event.key === "Enter" || event.key === " ") {
1045
+ event.preventDefault();
1046
+ onClick(event);
1047
+ }
1048
+ },
1049
+ role: "button",
1050
+ tabIndex: 0,
1051
+ className: cn(
1052
+ "cursor-pointer",
1053
+ "focus:outline-none focus:ring-2 focus:ring-primary/50"
1054
+ )
1055
+ } : {};
1056
+ const baseCls = cn(
1057
+ "block",
1058
+ isFill && "w-full h-full",
1059
+ ROUNDED_CLASS[rounded],
1060
+ border === "subtle" && "border border-border",
1061
+ onClick && "cursor-pointer",
1062
+ interactiveProps.className,
1063
+ className
1064
+ );
1065
+ const objectFitClass = fit === "cover" ? "object-cover" : fit === "contain" ? "object-contain" : "object-fill";
1066
+ const sizeAttrs = isFill ? {} : { width: dimensions.width, height: dimensions.height };
1067
+ if (elementType === "video") {
1068
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1069
+ "video",
1070
+ {
1071
+ ref: sinkRef,
1072
+ src: url,
1073
+ ...sizeAttrs,
1074
+ className: cn(baseCls, objectFitClass),
1075
+ controls,
1076
+ autoPlay,
1077
+ loop,
1078
+ muted,
1079
+ playsInline,
1080
+ preload,
1081
+ controlsList,
1082
+ onLoadedData: onLoad,
1083
+ onError: handleLoadError,
1084
+ crossOrigin,
1085
+ ...mediaSessionEvents,
1086
+ ...interactiveProps
1087
+ },
1088
+ recovery.retryKey
1089
+ );
1090
+ }
1091
+ if (elementType === "audio") {
1092
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1093
+ "audio",
1094
+ {
1095
+ ref: sinkRef,
1096
+ src: url,
1097
+ className: cn(baseCls),
1098
+ controls,
1099
+ autoPlay,
1100
+ loop,
1101
+ muted,
1102
+ preload,
1103
+ ...mediaSessionEvents,
1104
+ onLoadedData: onLoad,
1105
+ onError: handleLoadError,
1106
+ crossOrigin,
1107
+ ...interactiveProps
1108
+ },
1109
+ recovery.retryKey
1110
+ );
1111
+ }
1112
+ const needsPlainImg = !!onLoad || !!mediaElementRef || !!crossOrigin;
1113
+ const isCdnUrl = resolution?.isCdn ?? (url.startsWith("https://cdn.") || url.includes("/cdn/"));
1114
+ const HostImage = ports.ImageComponent;
1115
+ if (HostImage && isCdnUrl && !isFill && !needsPlainImg && !needsBlob) {
1116
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1117
+ HostImage,
1118
+ {
1119
+ src: url,
1120
+ alt,
1121
+ width: dimensions.width,
1122
+ height: dimensions.height,
1123
+ className: cn(baseCls, objectFitClass),
1124
+ onError: handleLoadError,
1125
+ ...interactiveProps
1126
+ },
1127
+ recovery.retryKey
1128
+ );
1129
+ }
1130
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1131
+ "img",
1132
+ {
1133
+ ref: mediaElementRef,
1134
+ src: url,
1135
+ alt,
1136
+ ...sizeAttrs,
1137
+ className: cn(baseCls, objectFitClass),
1138
+ onLoad,
1139
+ onError: handleLoadError,
1140
+ crossOrigin,
1141
+ ...interactiveProps
1142
+ },
1143
+ recovery.retryKey
1144
+ );
1145
+ }
1146
+
1147
+ // src/react/media-thumbnail.tsx
1148
+ var import_react7 = require("react");
1149
+
1150
+ // src/core/use-thumbnail-source.ts
1151
+ var import_react6 = require("react");
1152
+ function useThumbnailSource(ref, options = {}) {
1153
+ const { thumbnailUrl, allowSourceFallback = true } = options;
1154
+ const client = useMediaClient();
1155
+ const { resolution } = useMediaResolution(ref);
1156
+ const explicitThumb = (0, import_react6.useMemo)(() => {
1157
+ if (!thumbnailUrl) return null;
1158
+ try {
1159
+ return client.resolve(thumbnailUrl);
1160
+ } catch {
1161
+ return null;
1162
+ }
1163
+ }, [client, thumbnailUrl]);
1164
+ const needsBlob = allowSourceFallback && !explicitThumb && !resolution?.thumbnailSrc && resolution?.transport === "blob";
1165
+ const blob = useMediaBlob(needsBlob ? ref : null);
1166
+ if (explicitThumb) {
1167
+ return {
1168
+ src: explicitThumb.src,
1169
+ mode: "thumbnail",
1170
+ loading: false,
1171
+ resolution,
1172
+ recoverable: explicitThumb.recoverable !== false
1173
+ };
1174
+ }
1175
+ if (resolution?.thumbnailSrc) {
1176
+ return {
1177
+ src: resolution.thumbnailSrc,
1178
+ mode: "thumbnail",
1179
+ loading: false,
1180
+ resolution,
1181
+ recoverable: resolution.recoverable !== false
1182
+ };
1183
+ }
1184
+ if (allowSourceFallback && resolution) {
1185
+ if (needsBlob) {
1186
+ return {
1187
+ src: blob.url,
1188
+ mode: blob.url ? resolution.kind === "video" ? "live-video-poster" : "live-image" : "icon",
1189
+ loading: blob.loading,
1190
+ resolution,
1191
+ recoverable: false
1192
+ };
1193
+ }
1194
+ if (resolution.kind === "image") {
1195
+ return {
1196
+ src: resolution.src,
1197
+ mode: "live-image",
1198
+ loading: false,
1199
+ resolution,
1200
+ recoverable: resolution.recoverable !== false
1201
+ };
1202
+ }
1203
+ if (resolution.kind === "video") {
1204
+ return {
1205
+ src: resolution.src,
1206
+ mode: "live-video-poster",
1207
+ loading: false,
1208
+ resolution,
1209
+ recoverable: resolution.recoverable !== false
1210
+ };
1211
+ }
1212
+ }
1213
+ return {
1214
+ src: null,
1215
+ mode: "icon",
1216
+ loading: false,
1217
+ resolution,
1218
+ recoverable: false
1219
+ };
1220
+ }
1221
+
1222
+ // src/react/media-thumbnail.tsx
1223
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1224
+ function MediaThumbnail({
1225
+ mediaRef,
1226
+ fileName,
1227
+ mimeType = null,
1228
+ thumbnailUrl,
1229
+ iconSize = 48,
1230
+ className,
1231
+ rounded,
1232
+ allowSourceFallback = true
1233
+ }) {
1234
+ const source = useThumbnailSource(mediaRef, {
1235
+ thumbnailUrl,
1236
+ allowSourceFallback
1237
+ });
1238
+ const fallback = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex h-full w-full items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FileIcon2, { fileName, mimeType, size: iconSize }) });
1239
+ let body = source.loading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "h-full w-full animate-pulse bg-muted" }) : fallback;
1240
+ if (source.src) {
1241
+ if (source.mode === "live-video-poster") {
1242
+ body = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1243
+ VideoPosterThumb,
1244
+ {
1245
+ url: source.src,
1246
+ recoverable: source.recoverable,
1247
+ fallback
1248
+ }
1249
+ );
1250
+ } else {
1251
+ body = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1252
+ ImageThumb,
1253
+ {
1254
+ url: source.src,
1255
+ alt: fileName,
1256
+ recoverable: source.recoverable,
1257
+ fallback
1258
+ }
1259
+ );
1260
+ }
1261
+ }
1262
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1263
+ "div",
1264
+ {
1265
+ className: cn("relative overflow-hidden bg-muted/40", rounded, className),
1266
+ children: body
1267
+ }
1268
+ );
1269
+ }
1270
+ function ImageThumb({
1271
+ url,
1272
+ alt,
1273
+ recoverable,
1274
+ fallback
1275
+ }) {
1276
+ const { retryKey, onLoadError, failed } = useMediaLoadRecovery(url, {
1277
+ recoverable
1278
+ });
1279
+ if (failed) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: fallback });
1280
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1281
+ "img",
1282
+ {
1283
+ src: url,
1284
+ alt,
1285
+ loading: "lazy",
1286
+ className: "h-full w-full object-cover",
1287
+ onError: onLoadError
1288
+ },
1289
+ retryKey
1290
+ );
1291
+ }
1292
+ function VideoPosterThumb({
1293
+ url,
1294
+ recoverable,
1295
+ fallback
1296
+ }) {
1297
+ const { retryKey, onLoadError, failed } = useMediaLoadRecovery(url, {
1298
+ recoverable
1299
+ });
1300
+ const ref = (0, import_react7.useRef)(null);
1301
+ (0, import_react7.useEffect)(() => {
1302
+ const v = ref.current;
1303
+ if (!v) return void 0;
1304
+ const onMeta = () => {
1305
+ try {
1306
+ v.currentTime = 0;
1307
+ } catch {
1308
+ }
1309
+ };
1310
+ v.addEventListener("loadedmetadata", onMeta);
1311
+ return () => v.removeEventListener("loadedmetadata", onMeta);
1312
+ }, []);
1313
+ if (failed) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: fallback });
1314
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1315
+ "video",
1316
+ {
1317
+ ref,
1318
+ src: url,
1319
+ className: "h-full w-full object-cover pointer-events-none",
1320
+ preload: "metadata",
1321
+ muted: true,
1322
+ playsInline: true,
1323
+ onError: onLoadError
1324
+ },
1325
+ retryKey
1326
+ );
1327
+ }
1328
+
1329
+ // src/react/use-media-dropzone.ts
1330
+ var import_react8 = require("react");
1331
+ function matchesAccept(file, accept) {
1332
+ if (!accept || accept.length === 0) return true;
1333
+ const name = file.name.toLowerCase();
1334
+ const mime = file.type.toLowerCase();
1335
+ return accept.some((pattern) => {
1336
+ const p = pattern.trim().toLowerCase();
1337
+ if (!p) return false;
1338
+ if (p.startsWith(".")) return name.endsWith(p);
1339
+ if (p.endsWith("/*")) return mime.startsWith(p.slice(0, -1));
1340
+ return mime === p;
1341
+ });
1342
+ }
1343
+ function useMediaDropzone(options) {
1344
+ const {
1345
+ onFiles,
1346
+ accept,
1347
+ maxSize,
1348
+ onRejected,
1349
+ enablePaste = true,
1350
+ pickerEventName,
1351
+ disabled = false,
1352
+ multiple = true
1353
+ } = options;
1354
+ const [isDragActive, setIsDragActive] = (0, import_react8.useState)(false);
1355
+ const [pasteHighlight, setPasteHighlight] = (0, import_react8.useState)(false);
1356
+ const dragDepth = (0, import_react8.useRef)(0);
1357
+ const inputRef = (0, import_react8.useRef)(null);
1358
+ const onFilesRef = (0, import_react8.useRef)(onFiles);
1359
+ onFilesRef.current = onFiles;
1360
+ const onRejectedRef = (0, import_react8.useRef)(onRejected);
1361
+ onRejectedRef.current = onRejected;
1362
+ const acceptKey = accept?.join(",") ?? "";
1363
+ const filterFiles = (0, import_react8.useCallback)(
1364
+ (files) => {
1365
+ const accepted = [];
1366
+ const rejected = [];
1367
+ for (const file of files) {
1368
+ if (!matchesAccept(file, acceptKey ? acceptKey.split(",") : void 0)) {
1369
+ rejected.push({ file, reason: "type" });
1370
+ } else if (maxSize != null && file.size > maxSize) {
1371
+ rejected.push({ file, reason: "size" });
1372
+ } else {
1373
+ accepted.push(file);
1374
+ }
1375
+ }
1376
+ if (rejected.length) onRejectedRef.current?.(rejected);
1377
+ return accepted;
1378
+ },
1379
+ [acceptKey, maxSize]
1380
+ );
1381
+ const emit = (0, import_react8.useCallback)(
1382
+ (files) => {
1383
+ const accepted = filterFiles(files);
1384
+ if (accepted.length) onFilesRef.current(accepted);
1385
+ },
1386
+ [filterFiles]
1387
+ );
1388
+ const openPicker = (0, import_react8.useCallback)(() => {
1389
+ if (!disabled) inputRef.current?.click();
1390
+ }, [disabled]);
1391
+ (0, import_react8.useEffect)(() => {
1392
+ if (!pickerEventName || typeof window === "undefined") return void 0;
1393
+ const handler = () => openPicker();
1394
+ window.addEventListener(pickerEventName, handler);
1395
+ return () => window.removeEventListener(pickerEventName, handler);
1396
+ }, [openPicker, pickerEventName]);
1397
+ (0, import_react8.useEffect)(() => {
1398
+ if (!enablePaste || disabled || typeof window === "undefined") {
1399
+ return void 0;
1400
+ }
1401
+ const handler = (event) => {
1402
+ if (!event.clipboardData?.items) return;
1403
+ const images = [];
1404
+ for (const item of Array.from(event.clipboardData.items)) {
1405
+ if (item.type.startsWith("image/")) {
1406
+ const blob = item.getAsFile();
1407
+ if (!blob) continue;
1408
+ const ext = blob.type.split("/")[1] ?? "png";
1409
+ const name = `pasted-${Date.now()}.${ext}`;
1410
+ images.push(new File([blob], name, { type: blob.type }));
1411
+ }
1412
+ }
1413
+ if (images.length > 0) {
1414
+ event.preventDefault();
1415
+ setPasteHighlight(true);
1416
+ setTimeout(() => setPasteHighlight(false), 400);
1417
+ emit(images);
1418
+ }
1419
+ };
1420
+ window.addEventListener("paste", handler);
1421
+ return () => window.removeEventListener("paste", handler);
1422
+ }, [enablePaste, disabled, emit]);
1423
+ const getRootProps = (0, import_react8.useCallback)(
1424
+ () => ({
1425
+ onDragEnter: (event) => {
1426
+ if (disabled) return;
1427
+ event.preventDefault();
1428
+ dragDepth.current += 1;
1429
+ setIsDragActive(true);
1430
+ },
1431
+ onDragOver: (event) => {
1432
+ if (disabled) return;
1433
+ event.preventDefault();
1434
+ },
1435
+ onDragLeave: (event) => {
1436
+ if (disabled) return;
1437
+ event.preventDefault();
1438
+ dragDepth.current = Math.max(0, dragDepth.current - 1);
1439
+ if (dragDepth.current === 0) setIsDragActive(false);
1440
+ },
1441
+ onDrop: (event) => {
1442
+ if (disabled) return;
1443
+ event.preventDefault();
1444
+ dragDepth.current = 0;
1445
+ setIsDragActive(false);
1446
+ const files = Array.from(event.dataTransfer?.files ?? []);
1447
+ if (files.length) emit(multiple ? files : files.slice(0, 1));
1448
+ }
1449
+ }),
1450
+ [disabled, emit, multiple]
1451
+ );
1452
+ const inputAccept = (0, import_react8.useMemo)(
1453
+ () => acceptKey.length ? acceptKey : void 0,
1454
+ [acceptKey]
1455
+ );
1456
+ const getInputProps = (0, import_react8.useCallback)(
1457
+ () => ({
1458
+ ref: (node) => {
1459
+ inputRef.current = node;
1460
+ },
1461
+ type: "file",
1462
+ multiple,
1463
+ accept: inputAccept,
1464
+ tabIndex: -1,
1465
+ style: { display: "none" },
1466
+ onChange: (event) => {
1467
+ const files = Array.from(event.target.files ?? []);
1468
+ event.target.value = "";
1469
+ if (files.length) emit(files);
1470
+ }
1471
+ }),
1472
+ [emit, inputAccept, multiple]
1473
+ );
1474
+ return { getRootProps, getInputProps, isDragActive, pasteHighlight, openPicker };
1475
+ }
1476
+
1477
+ // src/react/file-upload-dropzone.tsx
1478
+ var import_react11 = require("react");
1479
+
1480
+ // src/core/use-media-upload.ts
1481
+ var import_react9 = require("react");
1482
+ var entrySeq = 0;
1483
+ function useMediaUpload() {
1484
+ const client = useMediaClient();
1485
+ const [uploading, setUploading] = (0, import_react9.useState)(false);
1486
+ const [progress, setProgress] = (0, import_react9.useState)(null);
1487
+ const [result, setResult] = (0, import_react9.useState)(null);
1488
+ const [error, setError] = (0, import_react9.useState)(null);
1489
+ const [entries, setEntries] = (0, import_react9.useState)([]);
1490
+ const activeCount = (0, import_react9.useRef)(0);
1491
+ const patchEntry = (0, import_react9.useCallback)(
1492
+ (id, patch) => {
1493
+ setEntries(
1494
+ (current) => current.map((e) => e.id === id ? { ...e, ...patch } : e)
1495
+ );
1496
+ },
1497
+ []
1498
+ );
1499
+ const upload = (0, import_react9.useCallback)(
1500
+ async (file, opts = {}) => {
1501
+ const id = `up-${++entrySeq}`;
1502
+ const fileName = opts.fileName ?? (file instanceof File ? file.name : "file");
1503
+ setEntries((current) => [
1504
+ ...current,
1505
+ {
1506
+ id,
1507
+ fileName,
1508
+ fileSize: file.size,
1509
+ bytesUploaded: 0,
1510
+ status: "uploading",
1511
+ error: null,
1512
+ fileId: null,
1513
+ completedAt: null
1514
+ }
1515
+ ]);
1516
+ activeCount.current += 1;
1517
+ setUploading(true);
1518
+ setError(null);
1519
+ setProgress(null);
1520
+ try {
1521
+ const uploaded = await client.upload(file, {
1522
+ ...opts,
1523
+ onProgress: (loaded, total) => {
1524
+ setProgress({ loaded, total, ratio: total > 0 ? loaded / total : 0 });
1525
+ patchEntry(id, { bytesUploaded: loaded });
1526
+ opts.onProgress?.(loaded, total);
1527
+ }
1528
+ });
1529
+ setResult(uploaded);
1530
+ patchEntry(id, {
1531
+ status: "success",
1532
+ fileId: uploaded.fileId,
1533
+ bytesUploaded: file.size,
1534
+ completedAt: Date.now()
1535
+ });
1536
+ return uploaded;
1537
+ } catch (err) {
1538
+ const e = err instanceof Error ? err : new Error(String(err));
1539
+ setError(e);
1540
+ patchEntry(id, {
1541
+ status: "error",
1542
+ error: e.message,
1543
+ completedAt: Date.now()
1544
+ });
1545
+ throw e;
1546
+ } finally {
1547
+ activeCount.current -= 1;
1548
+ if (activeCount.current <= 0) setUploading(false);
1549
+ }
1550
+ },
1551
+ [client, patchEntry]
1552
+ );
1553
+ const uploadMany = (0, import_react9.useCallback)(
1554
+ async (files, opts = {}) => {
1555
+ if (client.uploadMany) {
1556
+ activeCount.current += 1;
1557
+ setUploading(true);
1558
+ try {
1559
+ return await client.uploadMany(files, opts);
1560
+ } finally {
1561
+ activeCount.current -= 1;
1562
+ if (activeCount.current <= 0) setUploading(false);
1563
+ }
1564
+ }
1565
+ const uploaded = [];
1566
+ const failed = [];
1567
+ for (const file of files) {
1568
+ try {
1569
+ const one = await upload(file, opts);
1570
+ uploaded.push(one.fileId);
1571
+ } catch (err) {
1572
+ failed.push({
1573
+ name: file.name,
1574
+ error: err instanceof Error ? err.message : String(err)
1575
+ });
1576
+ }
1577
+ }
1578
+ return { uploaded, failed, cancelled: false };
1579
+ },
1580
+ [client, upload]
1581
+ );
1582
+ const clearEntry = (0, import_react9.useCallback)((id) => {
1583
+ setEntries((current) => current.filter((e) => e.id !== id));
1584
+ }, []);
1585
+ const reset = (0, import_react9.useCallback)(() => {
1586
+ setResult(null);
1587
+ setError(null);
1588
+ setProgress(null);
1589
+ setEntries([]);
1590
+ }, []);
1591
+ return {
1592
+ upload,
1593
+ uploadMany,
1594
+ uploading,
1595
+ progress,
1596
+ result,
1597
+ error,
1598
+ entries,
1599
+ clearEntry,
1600
+ reset
1601
+ };
1602
+ }
1603
+
1604
+ // src/react/upload-progress-list.tsx
1605
+ var import_react10 = require("react");
1606
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1607
+ var AUTO_DISMISS_MS = 1e4;
1608
+ function formatFileSize(bytes) {
1609
+ if (!Number.isFinite(bytes) || bytes < 0) return "0 B";
1610
+ if (bytes < 1024) return `${bytes} B`;
1611
+ const units = ["KB", "MB", "GB", "TB"];
1612
+ let value = bytes;
1613
+ let unit = -1;
1614
+ do {
1615
+ value /= 1024;
1616
+ unit += 1;
1617
+ } while (value >= 1024 && unit < units.length - 1);
1618
+ return `${value >= 10 ? Math.round(value) : value.toFixed(1)} ${units[unit] ?? "TB"}`;
1619
+ }
1620
+ function UploadProgressListImpl({
1621
+ uploads,
1622
+ onDismiss,
1623
+ onOpenFile,
1624
+ className
1625
+ }) {
1626
+ const [hovered, setHovered] = (0, import_react10.useState)(false);
1627
+ const activeCount = uploads.filter((u) => u.status === "uploading").length;
1628
+ const successCount = uploads.filter((u) => u.status === "success").length;
1629
+ const errorCount = uploads.filter((u) => u.status === "error").length;
1630
+ const timersRef = (0, import_react10.useRef)(
1631
+ /* @__PURE__ */ new Map()
1632
+ );
1633
+ const onDismissRef = (0, import_react10.useRef)(onDismiss);
1634
+ onDismissRef.current = onDismiss;
1635
+ (0, import_react10.useEffect)(() => {
1636
+ const timers = timersRef.current;
1637
+ if (hovered) {
1638
+ timers.forEach((t) => clearTimeout(t));
1639
+ timers.clear();
1640
+ return;
1641
+ }
1642
+ for (const u of uploads) {
1643
+ if (u.status !== "success") continue;
1644
+ if (timers.has(u.id)) continue;
1645
+ const elapsed = u.completedAt ? Date.now() - u.completedAt : 0;
1646
+ const remaining = Math.max(0, AUTO_DISMISS_MS - elapsed);
1647
+ const handle = setTimeout(() => {
1648
+ timers.delete(u.id);
1649
+ onDismissRef.current(u.id);
1650
+ }, remaining);
1651
+ timers.set(u.id, handle);
1652
+ }
1653
+ const liveSuccessIds = new Set(
1654
+ uploads.filter((u) => u.status === "success").map((u) => u.id)
1655
+ );
1656
+ for (const [id, handle] of timers) {
1657
+ if (!liveSuccessIds.has(id)) {
1658
+ clearTimeout(handle);
1659
+ timers.delete(id);
1660
+ }
1661
+ }
1662
+ }, [uploads, hovered]);
1663
+ (0, import_react10.useEffect)(() => {
1664
+ const timers = timersRef.current;
1665
+ return () => {
1666
+ timers.forEach((t) => clearTimeout(t));
1667
+ timers.clear();
1668
+ };
1669
+ }, []);
1670
+ if (uploads.length === 0) return null;
1671
+ const headerLabel = activeCount > 0 ? `Uploading ${activeCount} file${activeCount === 1 ? "" : "s"}\u2026` : errorCount > 0 && successCount === 0 ? `${errorCount} upload${errorCount === 1 ? "" : "s"} failed` : `Uploaded ${successCount} file${successCount === 1 ? "" : "s"}`;
1672
+ const completedIds = uploads.filter((u) => u.status === "success" || u.status === "error").map((u) => u.id);
1673
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1674
+ "div",
1675
+ {
1676
+ className: cn(
1677
+ "overflow-hidden rounded-lg border bg-background/95 shadow-lg backdrop-blur",
1678
+ className
1679
+ ),
1680
+ onMouseEnter: () => setHovered(true),
1681
+ onMouseLeave: () => setHovered(false),
1682
+ "aria-label": "Uploads",
1683
+ children: [
1684
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between gap-2 border-b bg-muted/40 px-3 py-1.5", children: [
1685
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate text-xs font-medium text-foreground", children: headerLabel }),
1686
+ completedIds.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1687
+ "button",
1688
+ {
1689
+ type: "button",
1690
+ onClick: () => {
1691
+ for (const id of completedIds) onDismiss(id);
1692
+ },
1693
+ className: "shrink-0 rounded px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground",
1694
+ children: "Clear"
1695
+ }
1696
+ ) : null
1697
+ ] }),
1698
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "max-h-72 space-y-1 overflow-y-auto p-2", children: uploads.map((u) => {
1699
+ const percent = u.fileSize > 0 ? Math.min(100, Math.round(u.bytesUploaded / u.fileSize * 100)) : 0;
1700
+ const done = u.status === "success";
1701
+ const failed = u.status === "error";
1702
+ const canOpen = done && !!u.fileId && !!onOpenFile;
1703
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1704
+ "li",
1705
+ {
1706
+ className: cn(
1707
+ "flex items-center gap-2 rounded-md px-2 py-1.5 text-xs",
1708
+ canOpen && "cursor-pointer hover:bg-accent/60"
1709
+ ),
1710
+ onClick: canOpen ? () => onOpenFile(u.fileId) : void 0,
1711
+ children: [
1712
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FileIcon2, { fileName: u.fileName, size: 18, className: "shrink-0" }),
1713
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "min-w-0 flex-1", children: [
1714
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
1715
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate font-medium text-foreground", children: u.fileName }),
1716
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: failed ? "Failed" : done ? "Done" : `${percent}%` })
1717
+ ] }),
1718
+ done ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mt-1 h-1 w-full rounded bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1719
+ "div",
1720
+ {
1721
+ className: cn(
1722
+ "h-1 rounded transition-all",
1723
+ failed ? "bg-destructive" : "bg-primary"
1724
+ ),
1725
+ style: { width: failed ? "100%" : `${percent}%` }
1726
+ }
1727
+ ) }),
1728
+ failed && u.error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mt-0.5 truncate text-[10px] text-destructive", children: u.error }) : !done ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: [
1729
+ formatFileSize(u.bytesUploaded),
1730
+ " /",
1731
+ " ",
1732
+ formatFileSize(u.fileSize)
1733
+ ] }) : null
1734
+ ] }),
1735
+ canOpen ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex shrink-0 items-center gap-1 rounded px-1.5 py-0.5 text-[11px] font-medium text-primary", children: [
1736
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExternalLinkIcon, { className: "h-3 w-3", width: 12, height: 12 }),
1737
+ "Open"
1738
+ ] }) : done ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1739
+ CheckCircle2Icon,
1740
+ {
1741
+ className: "h-4 w-4 shrink-0 text-emerald-500",
1742
+ width: 16,
1743
+ height: 16
1744
+ }
1745
+ ) : failed ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1746
+ AlertCircleIcon,
1747
+ {
1748
+ className: "h-4 w-4 shrink-0 text-destructive",
1749
+ width: 16,
1750
+ height: 16
1751
+ }
1752
+ ) : null,
1753
+ done || failed ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1754
+ "button",
1755
+ {
1756
+ type: "button",
1757
+ onClick: (e) => {
1758
+ e.stopPropagation();
1759
+ onDismiss(u.id);
1760
+ },
1761
+ "aria-label": "Dismiss",
1762
+ className: "flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground",
1763
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XIcon, { className: "h-3 w-3", width: 12, height: 12 })
1764
+ }
1765
+ ) : null
1766
+ ]
1767
+ },
1768
+ u.id
1769
+ );
1770
+ }) })
1771
+ ]
1772
+ }
1773
+ );
1774
+ }
1775
+ var UploadProgressList = (0, import_react10.memo)(UploadProgressListImpl);
1776
+
1777
+ // src/react/file-upload-dropzone.tsx
1778
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1779
+ function FileUploadDropzone({
1780
+ uploadOptions,
1781
+ accept,
1782
+ maxSize,
1783
+ enablePaste = true,
1784
+ mode = "overlay",
1785
+ children,
1786
+ className,
1787
+ actions,
1788
+ onUploadStart,
1789
+ onUploaded,
1790
+ onError,
1791
+ onOpenFile,
1792
+ pickerEventName
1793
+ }) {
1794
+ const { uploadMany, entries, clearEntry } = useMediaUpload();
1795
+ const handleUpload = (0, import_react11.useCallback)(
1796
+ async (files) => {
1797
+ if (!files.length) return;
1798
+ try {
1799
+ onUploadStart?.(files);
1800
+ const { uploaded, failed } = await uploadMany(files, uploadOptions);
1801
+ if (uploaded.length && onUploaded) onUploaded(uploaded);
1802
+ if (failed.length && onError) {
1803
+ const message = failed.map((f) => `${f.name}: ${f.error}`).join("; ");
1804
+ onError(message);
1805
+ }
1806
+ } catch (err) {
1807
+ onError?.(err instanceof Error ? err.message : String(err));
1808
+ }
1809
+ },
1810
+ [uploadMany, uploadOptions, onUploadStart, onUploaded, onError]
1811
+ );
1812
+ const {
1813
+ getRootProps,
1814
+ getInputProps,
1815
+ isDragActive,
1816
+ pasteHighlight,
1817
+ openPicker
1818
+ } = useMediaDropzone({
1819
+ onFiles: (files) => void handleUpload(files),
1820
+ accept,
1821
+ maxSize,
1822
+ enablePaste,
1823
+ pickerEventName
1824
+ });
1825
+ const highlighted = isDragActive || pasteHighlight;
1826
+ const renderedChildren = typeof children === "function" ? children({ openLocalPicker: openPicker, uploadFiles: handleUpload }) : children;
1827
+ if (mode === "inline") {
1828
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1829
+ "div",
1830
+ {
1831
+ ...getRootProps(),
1832
+ className: cn(
1833
+ "flex flex-col items-center justify-center gap-3 rounded-md border-2 border-dashed p-6",
1834
+ "text-sm text-muted-foreground",
1835
+ highlighted ? "border-primary bg-primary/5" : "border-border",
1836
+ className
1837
+ ),
1838
+ children: [
1839
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { ...getInputProps() }),
1840
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(UploadIcon, { className: "h-8 w-8", width: 32, height: 32, "aria-hidden": "true" }),
1841
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-center", children: [
1842
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "Drag & drop files here." }),
1843
+ enablePaste ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs mt-1 opacity-75", children: "You can also paste an image with \u2318V." }) : null
1844
+ ] }),
1845
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1846
+ "button",
1847
+ {
1848
+ type: "button",
1849
+ onClick: openPicker,
1850
+ className: "rounded-md border px-3 py-1.5 text-xs font-medium text-foreground hover:bg-accent",
1851
+ children: "Browse files"
1852
+ }
1853
+ ),
1854
+ actions,
1855
+ entries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1856
+ UploadProgressList,
1857
+ {
1858
+ uploads: entries,
1859
+ onDismiss: clearEntry,
1860
+ onOpenFile,
1861
+ className: "w-full"
1862
+ }
1863
+ ) : null
1864
+ ]
1865
+ }
1866
+ );
1867
+ }
1868
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1869
+ "div",
1870
+ {
1871
+ ...getRootProps(),
1872
+ className: cn("relative h-full w-full", className),
1873
+ "data-drop-active": highlighted ? "true" : void 0,
1874
+ children: [
1875
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { ...getInputProps() }),
1876
+ renderedChildren,
1877
+ highlighted ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1878
+ "div",
1879
+ {
1880
+ className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-md border-2 border-dashed border-primary bg-primary/10",
1881
+ "aria-live": "polite",
1882
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col items-center gap-2 text-primary", children: [
1883
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(UploadIcon, { className: "h-10 w-10", width: 40, height: 40, "aria-hidden": "true" }),
1884
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-medium", children: "Drop to upload" })
1885
+ ] })
1886
+ }
1887
+ ) : null,
1888
+ entries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "absolute bottom-3 right-3 z-20 w-80 max-w-[90%]", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1889
+ UploadProgressList,
1890
+ {
1891
+ uploads: entries,
1892
+ onDismiss: clearEntry,
1893
+ onOpenFile
1894
+ }
1895
+ ) }) : null
1896
+ ]
1897
+ }
1898
+ );
1899
+ }
1900
+
1901
+ // src/react/media-action-toolbar.tsx
1902
+ var import_react13 = require("react");
1903
+
1904
+ // src/core/use-media-actions.ts
1905
+ var import_react12 = require("react");
1906
+ function useMediaActions(ref, options = {}) {
1907
+ const ports = useMediaHostPorts();
1908
+ const { resolution } = useMediaResolution(ref);
1909
+ const [busy, setBusy] = (0, import_react12.useState)(null);
1910
+ const [lastError, setLastError] = (0, import_react12.useState)(null);
1911
+ const actions = ports.actions;
1912
+ const { fileName, alt } = options;
1913
+ const context = (0, import_react12.useMemo)(() => {
1914
+ if (!ref) return null;
1915
+ return { ref, resolution, fileName, alt };
1916
+ }, [ref, resolution, fileName, alt]);
1917
+ const isAvailable = (0, import_react12.useCallback)(
1918
+ (kind) => {
1919
+ if (!context || !actions) return false;
1920
+ if (kind === "share") return Boolean(actions.share || actions.SharePopover);
1921
+ return Boolean(actions[kind]);
1922
+ },
1923
+ [context, actions]
1924
+ );
1925
+ const run = (0, import_react12.useCallback)(
1926
+ async (kind) => {
1927
+ if (!context || !actions) return;
1928
+ const handler = actions[kind];
1929
+ if (!handler) return;
1930
+ setBusy(kind);
1931
+ setLastError(null);
1932
+ try {
1933
+ await handler(context);
1934
+ } catch (err) {
1935
+ setLastError({
1936
+ kind,
1937
+ message: err instanceof Error ? err.message : String(err)
1938
+ });
1939
+ } finally {
1940
+ setBusy(null);
1941
+ }
1942
+ },
1943
+ [context, actions]
1944
+ );
1945
+ return {
1946
+ context,
1947
+ isAvailable,
1948
+ run,
1949
+ busy,
1950
+ lastError,
1951
+ SharePopover: actions?.SharePopover ?? null
1952
+ };
1953
+ }
1954
+
1955
+ // src/react/media-action-toolbar.tsx
1956
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1957
+ var ACTION_META = {
1958
+ share: { label: "Share", Icon: Share2Icon },
1959
+ download: { label: "Download", Icon: DownloadIcon },
1960
+ copy: { label: "Copy", Icon: CopyIcon },
1961
+ open: { label: "Open", Icon: ExternalLinkIcon }
1962
+ };
1963
+ var DEFAULT_ORDER = ["share", "download", "copy", "open"];
1964
+ function MediaActionToolbar({
1965
+ mediaRef,
1966
+ fileName,
1967
+ alt,
1968
+ actions = DEFAULT_ORDER,
1969
+ hideUnavailable = false,
1970
+ className,
1971
+ buttonClassName
1972
+ }) {
1973
+ const state = useMediaActions(mediaRef, { fileName, alt });
1974
+ const [shareOpen, setShareOpen] = (0, import_react13.useState)(false);
1975
+ const SharePopover = state.SharePopover;
1976
+ const visible = hideUnavailable ? actions.filter((kind) => state.isAvailable(kind)) : actions;
1977
+ if (visible.length === 0) return null;
1978
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: cn("relative inline-flex items-center gap-1", className), children: [
1979
+ visible.map((kind) => {
1980
+ const { label, Icon } = ACTION_META[kind];
1981
+ const available = state.isAvailable(kind);
1982
+ const busy = state.busy === kind;
1983
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1984
+ "button",
1985
+ {
1986
+ type: "button",
1987
+ "aria-label": label,
1988
+ title: label,
1989
+ disabled: !available || busy,
1990
+ onClick: () => {
1991
+ if (kind === "share" && SharePopover) {
1992
+ setShareOpen((open) => !open);
1993
+ return;
1994
+ }
1995
+ void state.run(kind);
1996
+ },
1997
+ className: cn(
1998
+ "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors",
1999
+ available ? "hover:bg-accent hover:text-foreground" : "opacity-40 cursor-not-allowed",
2000
+ busy && "animate-pulse",
2001
+ buttonClassName
2002
+ ),
2003
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, { className: "h-4 w-4", width: 16, height: 16 })
2004
+ },
2005
+ kind
2006
+ );
2007
+ }),
2008
+ state.lastError ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2009
+ "span",
2010
+ {
2011
+ role: "status",
2012
+ className: "ml-1 max-w-48 truncate text-[11px] text-destructive",
2013
+ title: state.lastError.message,
2014
+ children: [
2015
+ ACTION_META[state.lastError.kind].label,
2016
+ " failed"
2017
+ ]
2018
+ }
2019
+ ) : null,
2020
+ shareOpen && SharePopover && state.context ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "absolute right-0 top-full z-30 mt-1", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2021
+ SharePopover,
2022
+ {
2023
+ context: state.context,
2024
+ onClose: () => setShareOpen(false)
2025
+ }
2026
+ ) }) : null
2027
+ ] });
2028
+ }
2029
+
2030
+ // src/react/media-lightbox.tsx
2031
+ var import_react14 = require("react");
2032
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2033
+ function MediaLightbox({
2034
+ open,
2035
+ onClose,
2036
+ mediaRef,
2037
+ fileName,
2038
+ alt,
2039
+ as,
2040
+ actions,
2041
+ hideUnavailableActions = false,
2042
+ headerExtra,
2043
+ className
2044
+ }) {
2045
+ (0, import_react14.useEffect)(() => {
2046
+ if (!open || typeof window === "undefined") return void 0;
2047
+ const handler = (event) => {
2048
+ if (event.key === "Escape") {
2049
+ event.stopPropagation();
2050
+ onClose();
2051
+ }
2052
+ };
2053
+ window.addEventListener("keydown", handler);
2054
+ return () => window.removeEventListener("keydown", handler);
2055
+ }, [open, onClose]);
2056
+ if (!open) return null;
2057
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2058
+ "div",
2059
+ {
2060
+ role: "dialog",
2061
+ "aria-modal": "true",
2062
+ "aria-label": fileName ?? alt ?? "Media viewer",
2063
+ className: cn(
2064
+ "fixed inset-0 z-50 flex flex-col bg-black/80 backdrop-blur-sm",
2065
+ className
2066
+ ),
2067
+ onClick: onClose,
2068
+ children: [
2069
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2070
+ "div",
2071
+ {
2072
+ className: "flex items-center justify-between gap-2 p-3",
2073
+ onClick: (event) => event.stopPropagation(),
2074
+ children: [
2075
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
2076
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate text-sm font-medium text-white/90", children: fileName ?? alt ?? "" }),
2077
+ headerExtra
2078
+ ] }),
2079
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-1", children: [
2080
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2081
+ MediaActionToolbar,
2082
+ {
2083
+ mediaRef,
2084
+ fileName,
2085
+ alt,
2086
+ actions,
2087
+ hideUnavailable: hideUnavailableActions,
2088
+ buttonClassName: "text-white/80 hover:bg-white/10 hover:text-white"
2089
+ }
2090
+ ),
2091
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2092
+ "button",
2093
+ {
2094
+ type: "button",
2095
+ "aria-label": "Close",
2096
+ onClick: onClose,
2097
+ className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-white/80 transition-colors hover:bg-white/10 hover:text-white",
2098
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(XIcon, { className: "h-4 w-4", width: 16, height: 16 })
2099
+ }
2100
+ )
2101
+ ] })
2102
+ ]
2103
+ }
2104
+ ),
2105
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2106
+ "div",
2107
+ {
2108
+ className: "flex min-h-0 flex-1 items-center justify-center p-4",
2109
+ onClick: (event) => event.stopPropagation(),
2110
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "h-full w-full", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2111
+ InlineMediaRef,
2112
+ {
2113
+ ref: mediaRef,
2114
+ size: "fill",
2115
+ fit: "contain",
2116
+ rounded: "none",
2117
+ alt: alt ?? fileName ?? "",
2118
+ as
2119
+ }
2120
+ ) })
2121
+ }
2122
+ )
2123
+ ]
2124
+ }
2125
+ );
2126
+ }
2127
+ //# sourceMappingURL=react.cjs.map