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