@ai-matrx/media 0.2.3 → 0.4.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 +185 -0
- package/README.md +82 -3
- package/dist/core.cjs +177 -39
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +84 -4
- package/dist/core.d.ts +84 -4
- package/dist/core.js +164 -26
- package/dist/core.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -2
- package/dist/index.d.ts +77 -2
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +92 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +74 -0
- package/dist/next.d.ts +74 -0
- package/dist/next.js +60 -0
- package/dist/next.js.map +1 -0
- package/dist/react.cjs +231 -80
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +10 -1
- package/dist/react.d.ts +10 -1
- package/dist/react.js +209 -57
- package/dist/react.js.map +1 -1
- package/dist/viewers.cjs +1747 -0
- package/dist/viewers.cjs.map +1 -0
- package/dist/viewers.d.cts +289 -0
- package/dist/viewers.d.ts +289 -0
- package/dist/viewers.js +1729 -0
- package/dist/viewers.js.map +1 -0
- package/package.json +32 -5
package/dist/viewers.cjs
ADDED
|
@@ -0,0 +1,1747 @@
|
|
|
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/viewers.ts
|
|
22
|
+
var viewers_exports = {};
|
|
23
|
+
__export(viewers_exports, {
|
|
24
|
+
CodeViewer: () => CodeViewer,
|
|
25
|
+
GenericViewer: () => GenericViewer,
|
|
26
|
+
HtmlViewer: () => HtmlViewer,
|
|
27
|
+
MediaFileViewer: () => MediaFileViewer,
|
|
28
|
+
MediaViewerUnavailable: () => MediaViewerUnavailable,
|
|
29
|
+
SvgViewer: () => SvgViewer,
|
|
30
|
+
TRANSPARENCY_GRID_STYLE: () => TRANSPARENCY_GRID_STYLE,
|
|
31
|
+
TextViewer: () => TextViewer,
|
|
32
|
+
ViewerCopyButton: () => ViewerCopyButton,
|
|
33
|
+
ViewerError: () => ViewerError,
|
|
34
|
+
ViewerSkeleton: () => ViewerSkeleton,
|
|
35
|
+
ViewerToolbar: () => ViewerToolbar,
|
|
36
|
+
detectCodeLanguage: () => detectCodeLanguage,
|
|
37
|
+
getMediaCodeHighlighter: () => getMediaCodeHighlighter,
|
|
38
|
+
getMediaViewer: () => getMediaViewer,
|
|
39
|
+
getPreviewKind: () => getPreviewKind,
|
|
40
|
+
registerMediaCodeHighlighter: () => registerMediaCodeHighlighter,
|
|
41
|
+
registerMediaViewer: () => registerMediaViewer,
|
|
42
|
+
registeredMediaViewerKinds: () => registeredMediaViewerKinds,
|
|
43
|
+
useViewerBytes: () => useViewerBytes,
|
|
44
|
+
useViewerDisplayUrl: () => useViewerDisplayUrl,
|
|
45
|
+
useViewerText: () => useViewerText
|
|
46
|
+
});
|
|
47
|
+
module.exports = __toCommonJS(viewers_exports);
|
|
48
|
+
|
|
49
|
+
// src/viewers/registry.ts
|
|
50
|
+
var VIEWER_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.viewer-registry");
|
|
51
|
+
var HIGHLIGHTER_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.code-highlighter");
|
|
52
|
+
function slots() {
|
|
53
|
+
return globalThis;
|
|
54
|
+
}
|
|
55
|
+
function viewerMap() {
|
|
56
|
+
const host = slots();
|
|
57
|
+
const existing = host[VIEWER_SLOT];
|
|
58
|
+
if (existing) return existing;
|
|
59
|
+
const created = /* @__PURE__ */ new Map();
|
|
60
|
+
host[VIEWER_SLOT] = created;
|
|
61
|
+
return created;
|
|
62
|
+
}
|
|
63
|
+
function registerMediaViewer(kind, viewer) {
|
|
64
|
+
const map = viewerMap();
|
|
65
|
+
const previous = map.get(kind);
|
|
66
|
+
map.set(kind, viewer);
|
|
67
|
+
return () => {
|
|
68
|
+
if (previous) map.set(kind, previous);
|
|
69
|
+
else map.delete(kind);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function getMediaViewer(kind) {
|
|
73
|
+
return viewerMap().get(kind) ?? null;
|
|
74
|
+
}
|
|
75
|
+
function registeredMediaViewerKinds() {
|
|
76
|
+
return [...viewerMap().keys()];
|
|
77
|
+
}
|
|
78
|
+
function registerMediaCodeHighlighter(highlighter) {
|
|
79
|
+
const host = slots();
|
|
80
|
+
const previous = host[HIGHLIGHTER_SLOT] ?? null;
|
|
81
|
+
host[HIGHLIGHTER_SLOT] = highlighter;
|
|
82
|
+
return () => {
|
|
83
|
+
host[HIGHLIGHTER_SLOT] = previous;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function getMediaCodeHighlighter() {
|
|
87
|
+
return slots()[HIGHLIGHTER_SLOT] ?? null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/viewers/code-viewer.tsx
|
|
91
|
+
var import_react7 = require("react");
|
|
92
|
+
|
|
93
|
+
// src/react/cn.ts
|
|
94
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
95
|
+
function cn(...inputs) {
|
|
96
|
+
return (0, import_tailwind_merge.twMerge)(inputs.filter(Boolean).join(" "));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/viewers/chrome.tsx
|
|
100
|
+
var import_react = require("react");
|
|
101
|
+
|
|
102
|
+
// src/react/icons.tsx
|
|
103
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
104
|
+
function svgProps(props) {
|
|
105
|
+
return {
|
|
106
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
107
|
+
viewBox: "0 0 24 24",
|
|
108
|
+
width: 24,
|
|
109
|
+
height: 24,
|
|
110
|
+
fill: "none",
|
|
111
|
+
stroke: "currentColor",
|
|
112
|
+
strokeWidth: 2,
|
|
113
|
+
strokeLinecap: "round",
|
|
114
|
+
strokeLinejoin: "round",
|
|
115
|
+
"aria-hidden": true,
|
|
116
|
+
...props
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function ArchiveIcon(props) {
|
|
120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
121
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "20", height: "5", x: "2", y: "3", rx: "1" }),
|
|
122
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8" }),
|
|
123
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 12h4" })
|
|
124
|
+
] });
|
|
125
|
+
}
|
|
126
|
+
function BracesIcon(props) {
|
|
127
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
128
|
+
/* @__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" }),
|
|
129
|
+
/* @__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" })
|
|
130
|
+
] });
|
|
131
|
+
}
|
|
132
|
+
function CheckIcon(props) {
|
|
133
|
+
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" }) });
|
|
134
|
+
}
|
|
135
|
+
function CodeIcon(props) {
|
|
136
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
137
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m16 18 6-6-6-6" }),
|
|
138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m8 6-6 6 6 6" })
|
|
139
|
+
] });
|
|
140
|
+
}
|
|
141
|
+
function CopyIcon(props) {
|
|
142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
143
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
|
|
144
|
+
/* @__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" })
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
function DatabaseIcon(props) {
|
|
148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ellipse", { cx: "12", cy: "5", rx: "9", ry: "3" }),
|
|
150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 5V19A9 3 0 0 0 21 19V5" }),
|
|
151
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 12A9 3 0 0 0 21 12" })
|
|
152
|
+
] });
|
|
153
|
+
}
|
|
154
|
+
function DownloadIcon(props) {
|
|
155
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
156
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 15V3" }),
|
|
157
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
158
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m7 10 5 5 5-5" })
|
|
159
|
+
] });
|
|
160
|
+
}
|
|
161
|
+
function ExternalLinkIcon(props) {
|
|
162
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
163
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M15 3h6v6" }),
|
|
164
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 14 21 3" }),
|
|
165
|
+
/* @__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" })
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
168
|
+
function FileIcon(props) {
|
|
169
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
170
|
+
/* @__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" }),
|
|
171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" })
|
|
172
|
+
] });
|
|
173
|
+
}
|
|
174
|
+
function FileAudioIcon(props) {
|
|
175
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
176
|
+
/* @__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" }),
|
|
177
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
|
|
178
|
+
/* @__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" })
|
|
179
|
+
] });
|
|
180
|
+
}
|
|
181
|
+
function FileCodeIcon(props) {
|
|
182
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
183
|
+
/* @__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" }),
|
|
184
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
|
|
185
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 12.5 8 15l2 2.5" }),
|
|
186
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m14 12.5 2 2.5-2 2.5" })
|
|
187
|
+
] });
|
|
188
|
+
}
|
|
189
|
+
function FileJsonIcon(props) {
|
|
190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
191
|
+
/* @__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" }),
|
|
192
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
|
|
193
|
+
/* @__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" }),
|
|
194
|
+
/* @__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" })
|
|
195
|
+
] });
|
|
196
|
+
}
|
|
197
|
+
function FileSpreadsheetIcon(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: "M8 13h2" }),
|
|
202
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 13h2" }),
|
|
203
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 17h2" }),
|
|
204
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 17h2" })
|
|
205
|
+
] });
|
|
206
|
+
}
|
|
207
|
+
function FileTextIcon(props) {
|
|
208
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
209
|
+
/* @__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" }),
|
|
210
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
|
|
211
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 9H8" }),
|
|
212
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 13H8" }),
|
|
213
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 17H8" })
|
|
214
|
+
] });
|
|
215
|
+
}
|
|
216
|
+
function FileTypeIcon(props) {
|
|
217
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
218
|
+
/* @__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" }),
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 18h2" }),
|
|
221
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 12v6" }),
|
|
222
|
+
/* @__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" })
|
|
223
|
+
] });
|
|
224
|
+
}
|
|
225
|
+
function FileVideoIcon(props) {
|
|
226
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
227
|
+
/* @__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" }),
|
|
228
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
|
|
229
|
+
/* @__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" })
|
|
230
|
+
] });
|
|
231
|
+
}
|
|
232
|
+
function FolderIcon(props) {
|
|
233
|
+
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" }) });
|
|
234
|
+
}
|
|
235
|
+
function FolderOpenIcon(props) {
|
|
236
|
+
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" }) });
|
|
237
|
+
}
|
|
238
|
+
function ImageIcon(props) {
|
|
239
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
240
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
|
|
241
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "9", cy: "9", r: "2" }),
|
|
242
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
|
|
243
|
+
] });
|
|
244
|
+
}
|
|
245
|
+
function MusicIcon(props) {
|
|
246
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
247
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 18V5l12-2v13" }),
|
|
248
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "18", r: "3" }),
|
|
249
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "18", cy: "16", r: "3" })
|
|
250
|
+
] });
|
|
251
|
+
}
|
|
252
|
+
function PackageBoxIcon(props) {
|
|
253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
254
|
+
/* @__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" }),
|
|
255
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 22V12" }),
|
|
256
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3.29 7 12 12 20.71 7" }),
|
|
257
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m7.5 4.27 9 5.15" })
|
|
258
|
+
] });
|
|
259
|
+
}
|
|
260
|
+
function PenToolIcon(props) {
|
|
261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
262
|
+
/* @__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" }),
|
|
263
|
+
/* @__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" }),
|
|
264
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m2.3 2.3 7.286 7.286" }),
|
|
265
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "11", r: "2" })
|
|
266
|
+
] });
|
|
267
|
+
}
|
|
268
|
+
function SubtitlesIcon(props) {
|
|
269
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
270
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: "18", height: "14", x: "3", y: "5", rx: "2", ry: "2" }),
|
|
271
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7 15h4M15 15h2M7 11h2M13 11h4" })
|
|
272
|
+
] });
|
|
273
|
+
}
|
|
274
|
+
function VideoIcon(props) {
|
|
275
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
276
|
+
/* @__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" }),
|
|
277
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "6", width: "14", height: "12", rx: "2" })
|
|
278
|
+
] });
|
|
279
|
+
}
|
|
280
|
+
function XIcon(props) {
|
|
281
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18 6 6 18" }),
|
|
283
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m6 6 12 12" })
|
|
284
|
+
] });
|
|
285
|
+
}
|
|
286
|
+
function AlertCircleIcon(props) {
|
|
287
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
288
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
289
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", x2: "12", y1: "8", y2: "12" }),
|
|
290
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" })
|
|
291
|
+
] });
|
|
292
|
+
}
|
|
293
|
+
function CheckCircle2Icon(props) {
|
|
294
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
295
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21.801 10A10 10 0 1 1 17 3.335" }),
|
|
296
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m9 11 3 3L22 4" })
|
|
297
|
+
] });
|
|
298
|
+
}
|
|
299
|
+
function GlobeIcon(props) {
|
|
300
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgProps(props), children: [
|
|
301
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
302
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" }),
|
|
303
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2 12h20" })
|
|
304
|
+
] });
|
|
305
|
+
}
|
|
306
|
+
function SpinnerIcon(props) {
|
|
307
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { ...svgProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/viewers/chrome.tsx
|
|
311
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
312
|
+
function ViewerSkeleton({
|
|
313
|
+
className,
|
|
314
|
+
tone = "muted"
|
|
315
|
+
}) {
|
|
316
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
317
|
+
"div",
|
|
318
|
+
{
|
|
319
|
+
className: cn(
|
|
320
|
+
"flex h-full w-full items-center justify-center",
|
|
321
|
+
tone === "muted" && "bg-muted/20",
|
|
322
|
+
className
|
|
323
|
+
),
|
|
324
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-6 w-40 animate-pulse rounded bg-muted" })
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
function ViewerError({
|
|
329
|
+
title = "Couldn't load this file",
|
|
330
|
+
message,
|
|
331
|
+
hint,
|
|
332
|
+
className
|
|
333
|
+
}) {
|
|
334
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
335
|
+
"div",
|
|
336
|
+
{
|
|
337
|
+
className: cn(
|
|
338
|
+
"flex h-full w-full flex-col items-center justify-center gap-3 p-6 text-center",
|
|
339
|
+
className
|
|
340
|
+
),
|
|
341
|
+
role: "alert",
|
|
342
|
+
children: [
|
|
343
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-destructive/10", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AlertCircleIcon, { className: "h-6 w-6 text-destructive" }) }),
|
|
344
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-1", children: [
|
|
345
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "text-sm font-semibold", children: title }),
|
|
346
|
+
message ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "max-w-md text-xs break-words text-muted-foreground", children: message }) : null,
|
|
347
|
+
hint ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "max-w-md text-[11px] text-muted-foreground/80", children: hint }) : null
|
|
348
|
+
] })
|
|
349
|
+
]
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
function ViewerCopyButton({
|
|
354
|
+
text,
|
|
355
|
+
className
|
|
356
|
+
}) {
|
|
357
|
+
const [copied, setCopied] = (0, import_react.useState)(false);
|
|
358
|
+
const onCopy = (0, import_react.useCallback)(() => {
|
|
359
|
+
if (text == null) return;
|
|
360
|
+
void navigator.clipboard.writeText(text).then(() => {
|
|
361
|
+
setCopied(true);
|
|
362
|
+
setTimeout(() => setCopied(false), 1600);
|
|
363
|
+
}).catch(() => {
|
|
364
|
+
});
|
|
365
|
+
}, [text]);
|
|
366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
367
|
+
"button",
|
|
368
|
+
{
|
|
369
|
+
type: "button",
|
|
370
|
+
onClick: onCopy,
|
|
371
|
+
"aria-label": "Copy text to clipboard",
|
|
372
|
+
title: "Copy text to clipboard",
|
|
373
|
+
className: cn(
|
|
374
|
+
"inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
375
|
+
copied && "text-emerald-600 dark:text-emerald-400",
|
|
376
|
+
className
|
|
377
|
+
),
|
|
378
|
+
children: [
|
|
379
|
+
copied ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CheckIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CopyIcon, { className: "h-3.5 w-3.5" }),
|
|
380
|
+
copied ? "Copied" : "Copy"
|
|
381
|
+
]
|
|
382
|
+
}
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
function ViewerToolbar({
|
|
386
|
+
children,
|
|
387
|
+
className
|
|
388
|
+
}) {
|
|
389
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
390
|
+
"div",
|
|
391
|
+
{
|
|
392
|
+
className: cn(
|
|
393
|
+
"flex shrink-0 items-center justify-between gap-2 border-b border-border/60 bg-muted/20 px-3 py-1 text-xs",
|
|
394
|
+
className
|
|
395
|
+
),
|
|
396
|
+
children
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
function truncationNote(maxBytes) {
|
|
401
|
+
const mb = maxBytes / 1024 / 1024;
|
|
402
|
+
return `truncated to the first ${mb >= 1 ? `${mb.toFixed(0)}MB` : `${(maxBytes / 1024).toFixed(0)}KB`}`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/viewers/use-viewer-bytes.ts
|
|
406
|
+
var import_react6 = require("react");
|
|
407
|
+
|
|
408
|
+
// src/core/use-media-blob.ts
|
|
409
|
+
var import_react4 = require("react");
|
|
410
|
+
|
|
411
|
+
// src/core/context.ts
|
|
412
|
+
var import_react2 = require("react");
|
|
413
|
+
var CLIENT_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.client-context");
|
|
414
|
+
var PORTS_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.host-ports-context");
|
|
415
|
+
function clientContext() {
|
|
416
|
+
const host = globalThis;
|
|
417
|
+
host[CLIENT_SLOT] ??= (0, import_react2.createContext)(null);
|
|
418
|
+
return host[CLIENT_SLOT];
|
|
419
|
+
}
|
|
420
|
+
var INERT_PORTS = {};
|
|
421
|
+
function portsContext() {
|
|
422
|
+
const host = globalThis;
|
|
423
|
+
host[PORTS_SLOT] ??= (0, import_react2.createContext)(INERT_PORTS);
|
|
424
|
+
return host[PORTS_SLOT];
|
|
425
|
+
}
|
|
426
|
+
function useMediaClient() {
|
|
427
|
+
const client = (0, import_react2.useContext)(clientContext());
|
|
428
|
+
if (!client) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
"@ai-matrx/media: no MediaClient. Mount <MediaProvider client={...}> above every media surface (canonically wired to @ai-matrx/data/files)."
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
return client;
|
|
434
|
+
}
|
|
435
|
+
function useMediaHostPorts() {
|
|
436
|
+
return (0, import_react2.useContext)(portsContext());
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// src/core/diagnostics.ts
|
|
440
|
+
var import_react3 = require("react");
|
|
441
|
+
var LATCH_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.failure-latch");
|
|
442
|
+
function latch() {
|
|
443
|
+
const host = globalThis;
|
|
444
|
+
host[LATCH_SLOT] ??= /* @__PURE__ */ new Set();
|
|
445
|
+
return host[LATCH_SLOT];
|
|
446
|
+
}
|
|
447
|
+
function failureMediaIdentity(ref, src) {
|
|
448
|
+
if (ref && typeof ref === "object" && ref.file_id) return `file:${ref.file_id}`;
|
|
449
|
+
const raw = typeof ref === "string" ? ref : (ref && typeof ref === "object" ? ref.url ?? null : null) ?? src ?? null;
|
|
450
|
+
if (!raw) return "<unknown-media>";
|
|
451
|
+
if (raw.startsWith("blob:")) return "blob:object-url";
|
|
452
|
+
try {
|
|
453
|
+
const u = new URL(raw);
|
|
454
|
+
return `url:${u.origin}${u.pathname}`;
|
|
455
|
+
} catch {
|
|
456
|
+
const clean = raw.split(/[?#]/, 1)[0] ?? raw;
|
|
457
|
+
return `ref:${clean}`;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
function emitMediaFailure(capture, info) {
|
|
461
|
+
const key = `${info.terminal ? "terminal" : "warning"}|${info.mediaRef}`;
|
|
462
|
+
const seen = latch();
|
|
463
|
+
if (seen.has(key)) return;
|
|
464
|
+
seen.add(key);
|
|
465
|
+
if (capture) {
|
|
466
|
+
try {
|
|
467
|
+
capture(info);
|
|
468
|
+
return;
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
console.error(
|
|
473
|
+
"[@ai-matrx/media] media failure (no diagnostics sink captured it \u2014 bind MediaHostPorts.diagnostics.capture to your error system)",
|
|
474
|
+
info
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
function useMediaFailureCapture() {
|
|
478
|
+
const ports = useMediaHostPorts();
|
|
479
|
+
const capture = ports.diagnostics?.capture;
|
|
480
|
+
const captureRef = (0, import_react3.useRef)(capture);
|
|
481
|
+
captureRef.current = capture;
|
|
482
|
+
return (0, import_react3.useCallback)((info) => {
|
|
483
|
+
emitMediaFailure(captureRef.current, { source: "media", ...info });
|
|
484
|
+
}, []);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/core/ref-key.ts
|
|
488
|
+
function stableRefKey(ref) {
|
|
489
|
+
if (!ref) return "<none>";
|
|
490
|
+
if (typeof ref === "string") return `s:${ref}`;
|
|
491
|
+
if (ref.file_id) return `id:${ref.file_id}:${ref.mime_type ?? ""}`;
|
|
492
|
+
if (ref.url) return `url:${ref.url}:${ref.mime_type ?? ""}`;
|
|
493
|
+
return "<empty-ref>";
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/core/use-media-blob.ts
|
|
497
|
+
function statusOf(err) {
|
|
498
|
+
if (err && typeof err === "object") {
|
|
499
|
+
const candidate = err.status ?? err.statusCode;
|
|
500
|
+
if (typeof candidate === "number") return candidate;
|
|
501
|
+
}
|
|
502
|
+
return void 0;
|
|
503
|
+
}
|
|
504
|
+
function useMediaBlob(ref) {
|
|
505
|
+
const client = useMediaClient();
|
|
506
|
+
const capture = useMediaFailureCapture();
|
|
507
|
+
const refKey = stableRefKey(ref);
|
|
508
|
+
const [state, setState] = (0, import_react4.useState)({ url: null, blob: null, loading: !!ref, error: null });
|
|
509
|
+
const [retryToken, setRetryToken] = (0, import_react4.useState)(0);
|
|
510
|
+
(0, import_react4.useEffect)(() => {
|
|
511
|
+
if (!ref) {
|
|
512
|
+
setState({ url: null, blob: null, loading: false, error: null });
|
|
513
|
+
return void 0;
|
|
514
|
+
}
|
|
515
|
+
let cancelled = false;
|
|
516
|
+
let release = null;
|
|
517
|
+
setState({ url: null, blob: null, loading: true, error: null });
|
|
518
|
+
client.getBlob(ref).then(
|
|
519
|
+
(handle) => {
|
|
520
|
+
if (cancelled) {
|
|
521
|
+
handle.release();
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
release = handle.release;
|
|
525
|
+
setState({ url: handle.url, blob: handle.blob, loading: false, error: null });
|
|
526
|
+
},
|
|
527
|
+
(err) => {
|
|
528
|
+
if (cancelled) return;
|
|
529
|
+
const message = err instanceof Error ? err.message : String(err ?? "Failed to load file");
|
|
530
|
+
setState({ url: null, blob: null, loading: false, error: message });
|
|
531
|
+
const status = statusOf(err);
|
|
532
|
+
capture({
|
|
533
|
+
phase: "blob",
|
|
534
|
+
mediaRef: failureMediaIdentity(ref),
|
|
535
|
+
terminal: true,
|
|
536
|
+
message: `getBlob() failed: ${message}`,
|
|
537
|
+
...status !== void 0 ? { status } : {}
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
);
|
|
541
|
+
return () => {
|
|
542
|
+
cancelled = true;
|
|
543
|
+
release?.();
|
|
544
|
+
};
|
|
545
|
+
}, [client, refKey, retryToken]);
|
|
546
|
+
return {
|
|
547
|
+
...state,
|
|
548
|
+
retry: () => setRetryToken((t) => t + 1)
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/core/use-media-resolution.ts
|
|
553
|
+
var import_react5 = require("react");
|
|
554
|
+
function useMediaResolution(ref) {
|
|
555
|
+
const client = useMediaClient();
|
|
556
|
+
const capture = useMediaFailureCapture();
|
|
557
|
+
const refKey = stableRefKey(ref);
|
|
558
|
+
const { state, failureMessage } = (0, import_react5.useMemo)(() => {
|
|
559
|
+
if (!ref) {
|
|
560
|
+
return {
|
|
561
|
+
state: { resolution: null, status: "empty", reason: null },
|
|
562
|
+
failureMessage: null
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
const resolution = client.resolve(ref);
|
|
567
|
+
if (!resolution) {
|
|
568
|
+
return {
|
|
569
|
+
state: { resolution: null, status: "empty", reason: null },
|
|
570
|
+
failureMessage: null
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
return {
|
|
574
|
+
state: { resolution, status: "ready", reason: null },
|
|
575
|
+
failureMessage: null
|
|
576
|
+
};
|
|
577
|
+
} catch (err) {
|
|
578
|
+
return {
|
|
579
|
+
state: {
|
|
580
|
+
resolution: null,
|
|
581
|
+
status: "unavailable",
|
|
582
|
+
reason: client.classifyError(err)
|
|
583
|
+
},
|
|
584
|
+
failureMessage: err instanceof Error ? err.message : String(err ?? "resolve() failed")
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}, [client, refKey]);
|
|
588
|
+
(0, import_react5.useEffect)(() => {
|
|
589
|
+
if (state.status !== "unavailable") return;
|
|
590
|
+
capture({
|
|
591
|
+
phase: "resolve",
|
|
592
|
+
mediaRef: failureMediaIdentity(ref ?? null),
|
|
593
|
+
terminal: true,
|
|
594
|
+
message: `resolve() refused (${state.reason ?? "unknown"}): ${failureMessage ?? "no error message"}`
|
|
595
|
+
});
|
|
596
|
+
}, [refKey, state.status]);
|
|
597
|
+
return state;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// src/viewers/use-viewer-bytes.ts
|
|
601
|
+
function useViewerBytes(source) {
|
|
602
|
+
const fromClient = useMediaBlob(source.kind === "ref" ? source.ref : null);
|
|
603
|
+
if (source.kind === "blob") {
|
|
604
|
+
return { blob: source.blob, loading: false, error: null };
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
blob: fromClient.blob,
|
|
608
|
+
loading: fromClient.loading,
|
|
609
|
+
error: fromClient.error
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
async function readBlobText(blob) {
|
|
613
|
+
const maybeText = blob.text;
|
|
614
|
+
if (typeof maybeText === "function") return await maybeText.call(blob);
|
|
615
|
+
const maybeArrayBuffer = blob.arrayBuffer;
|
|
616
|
+
if (typeof maybeArrayBuffer === "function") {
|
|
617
|
+
return new TextDecoder().decode(await maybeArrayBuffer.call(blob));
|
|
618
|
+
}
|
|
619
|
+
return await new Promise((resolve, reject) => {
|
|
620
|
+
const reader = new FileReader();
|
|
621
|
+
reader.onload = () => resolve(String(reader.result ?? ""));
|
|
622
|
+
reader.onerror = () => reject(reader.error ?? new Error("could not read the file's bytes"));
|
|
623
|
+
reader.readAsText(blob);
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
function useViewerText(source, maxBytes = 1024 * 1024) {
|
|
627
|
+
const { blob, loading, error: bytesError } = useViewerBytes(source);
|
|
628
|
+
const [text, setText] = (0, import_react6.useState)(null);
|
|
629
|
+
const [readError, setReadError] = (0, import_react6.useState)(null);
|
|
630
|
+
const [truncated, setTruncated] = (0, import_react6.useState)(false);
|
|
631
|
+
(0, import_react6.useEffect)(() => {
|
|
632
|
+
if (!blob) {
|
|
633
|
+
setText(null);
|
|
634
|
+
return void 0;
|
|
635
|
+
}
|
|
636
|
+
let cancelled = false;
|
|
637
|
+
setReadError(null);
|
|
638
|
+
setText(null);
|
|
639
|
+
setTruncated(false);
|
|
640
|
+
void (async () => {
|
|
641
|
+
try {
|
|
642
|
+
const oversized = blob.size > maxBytes;
|
|
643
|
+
const value = await readBlobText(
|
|
644
|
+
oversized ? blob.slice(0, maxBytes) : blob
|
|
645
|
+
);
|
|
646
|
+
if (cancelled) return;
|
|
647
|
+
setTruncated(oversized);
|
|
648
|
+
setText(value);
|
|
649
|
+
} catch (err) {
|
|
650
|
+
if (cancelled) return;
|
|
651
|
+
setReadError(err instanceof Error ? err.message : String(err));
|
|
652
|
+
}
|
|
653
|
+
})();
|
|
654
|
+
return () => {
|
|
655
|
+
cancelled = true;
|
|
656
|
+
};
|
|
657
|
+
}, [blob, maxBytes]);
|
|
658
|
+
const error = readError ?? bytesError;
|
|
659
|
+
return {
|
|
660
|
+
text,
|
|
661
|
+
error,
|
|
662
|
+
truncated,
|
|
663
|
+
loading: (loading || text === null) && error === null
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function useViewerDisplayUrl(source) {
|
|
667
|
+
const resolved = useMediaResolution(
|
|
668
|
+
source.kind === "ref" ? source.ref : null
|
|
669
|
+
);
|
|
670
|
+
if (source.kind === "blob") {
|
|
671
|
+
return {
|
|
672
|
+
url: source.url,
|
|
673
|
+
recoverable: false,
|
|
674
|
+
unavailable: false,
|
|
675
|
+
loading: false
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
url: resolved.resolution?.src ?? null,
|
|
680
|
+
recoverable: resolved.resolution?.recoverable !== false,
|
|
681
|
+
unavailable: resolved.status === "unavailable",
|
|
682
|
+
loading: resolved.status === "empty"
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// src/viewers/code-viewer.tsx
|
|
687
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
688
|
+
var EXT_TO_LANGUAGE = {
|
|
689
|
+
js: "javascript",
|
|
690
|
+
jsx: "jsx",
|
|
691
|
+
mjs: "javascript",
|
|
692
|
+
cjs: "javascript",
|
|
693
|
+
ts: "typescript",
|
|
694
|
+
tsx: "tsx",
|
|
695
|
+
py: "python",
|
|
696
|
+
rb: "ruby",
|
|
697
|
+
go: "go",
|
|
698
|
+
rs: "rust",
|
|
699
|
+
java: "java",
|
|
700
|
+
kt: "kotlin",
|
|
701
|
+
swift: "swift",
|
|
702
|
+
c: "c",
|
|
703
|
+
h: "c",
|
|
704
|
+
cpp: "cpp",
|
|
705
|
+
cc: "cpp",
|
|
706
|
+
cxx: "cpp",
|
|
707
|
+
hpp: "cpp",
|
|
708
|
+
cs: "csharp",
|
|
709
|
+
php: "php",
|
|
710
|
+
scala: "scala",
|
|
711
|
+
html: "markup",
|
|
712
|
+
htm: "markup",
|
|
713
|
+
xml: "markup",
|
|
714
|
+
svg: "markup",
|
|
715
|
+
css: "css",
|
|
716
|
+
scss: "scss",
|
|
717
|
+
less: "less",
|
|
718
|
+
json: "json",
|
|
719
|
+
yaml: "yaml",
|
|
720
|
+
yml: "yaml",
|
|
721
|
+
toml: "toml",
|
|
722
|
+
ini: "ini",
|
|
723
|
+
sh: "bash",
|
|
724
|
+
bash: "bash",
|
|
725
|
+
zsh: "bash",
|
|
726
|
+
fish: "bash",
|
|
727
|
+
ps1: "powershell",
|
|
728
|
+
sql: "sql",
|
|
729
|
+
graphql: "graphql",
|
|
730
|
+
gql: "graphql",
|
|
731
|
+
proto: "protobuf",
|
|
732
|
+
dockerfile: "docker",
|
|
733
|
+
makefile: "makefile",
|
|
734
|
+
lua: "lua",
|
|
735
|
+
r: "r"
|
|
736
|
+
};
|
|
737
|
+
function extensionOf(fileName) {
|
|
738
|
+
const base = fileName.split(/[\\/]/).pop() ?? fileName;
|
|
739
|
+
const dot = base.lastIndexOf(".");
|
|
740
|
+
if (dot <= 0) return "";
|
|
741
|
+
return base.slice(dot + 1).toLowerCase();
|
|
742
|
+
}
|
|
743
|
+
function detectCodeLanguage(fileName) {
|
|
744
|
+
const ext = extensionOf(fileName);
|
|
745
|
+
if (ext) return EXT_TO_LANGUAGE[ext] ?? "text";
|
|
746
|
+
const lower = fileName.toLowerCase();
|
|
747
|
+
if (lower === "dockerfile") return "docker";
|
|
748
|
+
if (lower === "makefile") return "makefile";
|
|
749
|
+
return "text";
|
|
750
|
+
}
|
|
751
|
+
function CodeViewer({
|
|
752
|
+
source,
|
|
753
|
+
fileName,
|
|
754
|
+
className,
|
|
755
|
+
maxBytes = 1024 * 1024
|
|
756
|
+
}) {
|
|
757
|
+
const { text, error, loading, truncated } = useViewerText(source, maxBytes);
|
|
758
|
+
const [wrap, setWrap] = (0, import_react7.useState)(false);
|
|
759
|
+
const language = (0, import_react7.useMemo)(() => detectCodeLanguage(fileName), [fileName]);
|
|
760
|
+
const highlighter = getMediaCodeHighlighter();
|
|
761
|
+
if (error) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ViewerError, { message: error, className });
|
|
762
|
+
if (loading || text === null) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ViewerSkeleton, { className });
|
|
763
|
+
const lines = text.split("\n");
|
|
764
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
765
|
+
"div",
|
|
766
|
+
{
|
|
767
|
+
className: cn(
|
|
768
|
+
"flex h-full w-full flex-col overflow-hidden bg-card",
|
|
769
|
+
className
|
|
770
|
+
),
|
|
771
|
+
children: [
|
|
772
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-border bg-muted/30 px-3 py-1.5", children: [
|
|
773
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs text-muted-foreground", children: [
|
|
774
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "font-mono tracking-wide uppercase", children: language }),
|
|
775
|
+
truncated ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "text-warning", children: [
|
|
776
|
+
"\xB7 ",
|
|
777
|
+
truncationNote(maxBytes)
|
|
778
|
+
] }) : null
|
|
779
|
+
] }),
|
|
780
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
781
|
+
highlighter ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
782
|
+
"button",
|
|
783
|
+
{
|
|
784
|
+
type: "button",
|
|
785
|
+
onClick: () => setWrap((value) => !value),
|
|
786
|
+
"aria-pressed": wrap,
|
|
787
|
+
className: cn(
|
|
788
|
+
"rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
789
|
+
wrap && "bg-accent text-accent-foreground"
|
|
790
|
+
),
|
|
791
|
+
children: "Wrap"
|
|
792
|
+
}
|
|
793
|
+
),
|
|
794
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ViewerCopyButton, { text })
|
|
795
|
+
] })
|
|
796
|
+
] }),
|
|
797
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "min-h-0 flex-1 overflow-auto", children: highlighter ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: highlighter({ code: text, language }) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("pre", { className: "m-0 flex p-3 font-mono text-xs leading-5", children: [
|
|
798
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
799
|
+
"code",
|
|
800
|
+
{
|
|
801
|
+
"aria-hidden": true,
|
|
802
|
+
className: "mr-3 shrink-0 border-r border-border/60 pr-3 text-right text-muted-foreground/60 select-none",
|
|
803
|
+
children: lines.map((_, index) => `${index + 1}
|
|
804
|
+
`).join("")
|
|
805
|
+
}
|
|
806
|
+
),
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
808
|
+
"code",
|
|
809
|
+
{
|
|
810
|
+
className: cn(
|
|
811
|
+
"min-w-0 flex-1 text-foreground",
|
|
812
|
+
wrap ? "break-words whitespace-pre-wrap" : "whitespace-pre"
|
|
813
|
+
),
|
|
814
|
+
children: text
|
|
815
|
+
}
|
|
816
|
+
)
|
|
817
|
+
] }) })
|
|
818
|
+
]
|
|
819
|
+
}
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// src/core/file-kind.ts
|
|
824
|
+
var ROWS = [
|
|
825
|
+
[["jpg", "jpeg", "png", "gif", "webp", "avif", "heic", "heif", "bmp", "tif", "tiff", "ico"], "IMAGE", "text-emerald-500", "image", "Image"],
|
|
826
|
+
[["svg"], "IMAGE", "text-amber-500", "pen-tool", "SVG image"],
|
|
827
|
+
[["mp4", "mov", "webm", "mkv", "avi", "m4v"], "VIDEO", "text-purple-500", "video", "Video"],
|
|
828
|
+
[["mp3", "wav", "ogg", "m4a", "aac", "flac", "opus"], "AUDIO", "text-pink-500", "music", "Audio"],
|
|
829
|
+
[["pdf"], "DOCUMENT", "text-red-500", "file-type", "PDF Document"],
|
|
830
|
+
[["md", "markdown", "mdx", "rst", "adoc", "asciidoc", "org"], "DOCUMENT", "text-slate-400", "file-text", "Markdown"],
|
|
831
|
+
[["txt", "text", "asc", "me", "log", "out", "err"], "DOCUMENT", "text-muted-foreground", "file-text", "Text"],
|
|
832
|
+
[["dockerfile", "containerfile", "lua"], "CODE", "text-blue-500", "code", "Code"],
|
|
833
|
+
[["mk", "make", "swift", "ml", "mli", "fs", "fsi", "fsx", "svelte"], "CODE", "text-orange-500", "code", "Code"],
|
|
834
|
+
[["diff", "patch", "pem", "csr", "crt", "cer", "der", "key", "pub", "p7b", "p7c", "pfx", "p12"], "CODE", "text-amber-500", "file-text", "Text"],
|
|
835
|
+
[["ini", "cfg", "conf", "config", "properties", "prefs"], "CODE", "text-amber-400", "braces", "Config"],
|
|
836
|
+
[["tex", "latex", "ltx", "sty", "cls", "bib"], "CODE", "text-emerald-500", "file-text", "LaTeX"],
|
|
837
|
+
[["srt", "vtt"], "SUBTITLES", "text-cyan-500", "subtitles", "Subtitles"],
|
|
838
|
+
[["js", "mjs", "cjs", "jsx"], "CODE", "text-yellow-500", "file-code", "JavaScript"],
|
|
839
|
+
[["ts", "tsx"], "CODE", "text-blue-500", "file-code", "TypeScript"],
|
|
840
|
+
[["py", "clj", "cljs", "cljc", "edn", "vue", "styl", "stylus"], "CODE", "text-emerald-500", "code", "Code"],
|
|
841
|
+
[["rb", "rbw", "rake", "ru", "gemspec", "scala", "sbt"], "CODE", "text-red-500", "code", "Code"],
|
|
842
|
+
[["go", "dart"], "CODE", "text-cyan-500", "code", "Code"],
|
|
843
|
+
[["rs"], "CODE", "text-orange-600", "code", "Rust"],
|
|
844
|
+
[["java"], "CODE", "text-red-600", "code", "Java"],
|
|
845
|
+
[["c", "h"], "CODE", "text-blue-600", "code", "C"],
|
|
846
|
+
[["cpp", "cc", "cxx", "hpp"], "CODE", "text-blue-700", "code", "C++"],
|
|
847
|
+
[["cs"], "CODE", "text-purple-600", "code", "C#"],
|
|
848
|
+
[["html", "htm", "kt", "kts"], "CODE", "text-orange-400", "code", "Code"],
|
|
849
|
+
[["css"], "CODE", "text-sky-400", "code", "CSS"],
|
|
850
|
+
[["scss"], "CODE", "text-pink-400", "code", "SCSS"],
|
|
851
|
+
[["sh", "bash", "zsh", "fish", "ksh", "ash", "bat", "cmd", "twig", "jinja", "j2", "njk"], "CODE", "text-emerald-400", "code", "Script"],
|
|
852
|
+
[["sql"], "CODE", "text-amber-400", "code", "SQL"],
|
|
853
|
+
[["pl", "pm"], "CODE", "text-indigo-400", "code", "Perl"],
|
|
854
|
+
[["r", "rmd", "proto", "ps1", "psm1", "psd1", "less"], "CODE", "text-blue-400", "code", "Code"],
|
|
855
|
+
[["ex", "exs"], "CODE", "text-purple-400", "code", "Elixir"],
|
|
856
|
+
[["erl", "hrl"], "CODE", "text-rose-500", "code", "Erlang"],
|
|
857
|
+
[["hs", "lhs"], "CODE", "text-violet-500", "code", "Haskell"],
|
|
858
|
+
[["zig"], "CODE", "text-yellow-500", "code", "Zig"],
|
|
859
|
+
[["nim", "nims"], "CODE", "text-yellow-400", "code", "Nim"],
|
|
860
|
+
[["jl"], "CODE", "text-purple-500", "code", "Julia"],
|
|
861
|
+
[["astro"], "CODE", "text-fuchsia-500", "code", "Astro"],
|
|
862
|
+
[["graphql", "gql", "graphqls"], "CODE", "text-pink-500", "code", "GraphQL"],
|
|
863
|
+
[["sol"], "CODE", "text-slate-400", "code", "Solidity"],
|
|
864
|
+
[["php", "phtml", "phps"], "CODE", "text-indigo-500", "code", "PHP"],
|
|
865
|
+
[["hbs", "handlebars", "mustache", "ejs", "liquid"], "CODE", "text-amber-500", "code", "Template"],
|
|
866
|
+
[["json", "jsonc", "json5", "har", "geojson", "topojson"], "DATA", "text-amber-500", "file-json", "JSON"],
|
|
867
|
+
[["yaml", "yml", "toml"], "CODE", "text-amber-500", "braces", "Config"],
|
|
868
|
+
[["xml"], "DATA", "text-amber-500", "braces", "XML"],
|
|
869
|
+
[["csv", "tsv"], "DATA", "text-orange-500", "file-spreadsheet", "Spreadsheet data"],
|
|
870
|
+
[["sqlite", "sqlite3", "db"], "DATA", "text-blue-400", "database", "Database"],
|
|
871
|
+
[["ttl", "n3", "nt", "nq", "trig"], "DATA", "text-fuchsia-400", "braces", "RDF data"],
|
|
872
|
+
[["xlsx", "xls"], "DOCUMENT", "text-emerald-600", "file-spreadsheet", "Excel workbook"],
|
|
873
|
+
[["doc", "docx"], "DOCUMENT", "text-blue-500", "file-text", "Word document"],
|
|
874
|
+
[["ppt", "pptx"], "DOCUMENT", "text-orange-600", "file-type", "Presentation"],
|
|
875
|
+
[["ipynb"], "NOTEBOOK", "text-orange-400", "file-json", "Notebook"],
|
|
876
|
+
[["epub"], "EBOOK", "text-indigo-500", "file-text", "eBook"],
|
|
877
|
+
[["eml"], "EMAIL", "text-blue-400", "file-text", "Email"],
|
|
878
|
+
[["glb", "gltf", "stl", "obj", "fbx"], "MODEL_3D", "text-violet-400", "package", "3D model"],
|
|
879
|
+
[["zip", "rar", "7z"], "ARCHIVE", "text-amber-600", "archive", "Archive"],
|
|
880
|
+
[["tar", "gz", "tgz"], "ARCHIVE", "text-amber-600", "package", "Archive"]
|
|
881
|
+
];
|
|
882
|
+
var BY_EXTENSION = /* @__PURE__ */ new Map();
|
|
883
|
+
for (const [extensions, category, colorClass, icon, displayName] of ROWS) {
|
|
884
|
+
for (const ext of extensions) {
|
|
885
|
+
BY_EXTENSION.set(ext, { category, colorClass, icon, displayName });
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
var UNKNOWN_DETAILS = {
|
|
889
|
+
category: "UNKNOWN",
|
|
890
|
+
displayName: "File",
|
|
891
|
+
colorClass: "text-muted-foreground",
|
|
892
|
+
icon: "file"
|
|
893
|
+
};
|
|
894
|
+
var ASSUMED_TEXT_DETAILS = {
|
|
895
|
+
category: "DOCUMENT",
|
|
896
|
+
displayName: "Text",
|
|
897
|
+
colorClass: "text-muted-foreground",
|
|
898
|
+
icon: "file-text"
|
|
899
|
+
};
|
|
900
|
+
var FOLDER_DETAILS = {
|
|
901
|
+
category: "FOLDER",
|
|
902
|
+
displayName: "Folder",
|
|
903
|
+
colorClass: "text-sky-500",
|
|
904
|
+
icon: "folder"
|
|
905
|
+
};
|
|
906
|
+
var IMAGE_DETAILS = BY_EXTENSION.get("png");
|
|
907
|
+
var VIDEO_DETAILS = BY_EXTENSION.get("mp4");
|
|
908
|
+
var AUDIO_DETAILS = BY_EXTENSION.get("mp3");
|
|
909
|
+
var PDF_DETAILS = BY_EXTENSION.get("pdf");
|
|
910
|
+
var SVG_DETAILS = BY_EXTENSION.get("svg");
|
|
911
|
+
function basenameOf(path) {
|
|
912
|
+
const idx = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
913
|
+
return idx >= 0 ? path.slice(idx + 1) : path;
|
|
914
|
+
}
|
|
915
|
+
function extOf(name) {
|
|
916
|
+
const idx = name.lastIndexOf(".");
|
|
917
|
+
if (idx <= 0 || idx === name.length - 1) return null;
|
|
918
|
+
return name.slice(idx + 1).toLowerCase();
|
|
919
|
+
}
|
|
920
|
+
function dotfileLooksLikeText(filename) {
|
|
921
|
+
const name = basenameOf(filename);
|
|
922
|
+
if (!name.startsWith(".")) return false;
|
|
923
|
+
for (let i = 1; i < name.length; i++) {
|
|
924
|
+
const c = name.charCodeAt(i);
|
|
925
|
+
if (c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c === 45) {
|
|
926
|
+
return true;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
function canonicalMimeBase(raw) {
|
|
932
|
+
return raw.split(";")[0]?.trim().toLowerCase() ?? raw;
|
|
933
|
+
}
|
|
934
|
+
function getFileKindDetails(fileName, mimeType) {
|
|
935
|
+
const name = basenameOf(fileName);
|
|
936
|
+
const lower = name.toLowerCase();
|
|
937
|
+
const ext = lower === "dockerfile" || lower === "containerfile" ? lower : extOf(name);
|
|
938
|
+
const fromExt = ext ? BY_EXTENSION.get(ext) : void 0;
|
|
939
|
+
const mime = mimeType ? canonicalMimeBase(mimeType) : null;
|
|
940
|
+
if (mime) {
|
|
941
|
+
if (mime === "image/svg+xml") return fromExt ?? SVG_DETAILS;
|
|
942
|
+
if (mime.startsWith("image/")) return fromExt?.category === "IMAGE" ? fromExt : IMAGE_DETAILS;
|
|
943
|
+
if (mime.startsWith("video/")) return fromExt?.category === "VIDEO" ? fromExt : VIDEO_DETAILS;
|
|
944
|
+
if (mime.startsWith("audio/")) return fromExt?.category === "AUDIO" ? fromExt : AUDIO_DETAILS;
|
|
945
|
+
if (mime === "application/pdf") return PDF_DETAILS;
|
|
946
|
+
}
|
|
947
|
+
if (fromExt) return fromExt;
|
|
948
|
+
if (dotfileLooksLikeText(fileName)) return ASSUMED_TEXT_DETAILS;
|
|
949
|
+
return UNKNOWN_DETAILS;
|
|
950
|
+
}
|
|
951
|
+
function getFolderKindDetails(open = false) {
|
|
952
|
+
return open ? { ...FOLDER_DETAILS, icon: "folder-open" } : FOLDER_DETAILS;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// src/react/file-icon.tsx
|
|
956
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
957
|
+
var ICONS = {
|
|
958
|
+
image: ImageIcon,
|
|
959
|
+
"pen-tool": PenToolIcon,
|
|
960
|
+
video: VideoIcon,
|
|
961
|
+
music: MusicIcon,
|
|
962
|
+
"file-type": FileTypeIcon,
|
|
963
|
+
"file-text": FileTextIcon,
|
|
964
|
+
code: CodeIcon,
|
|
965
|
+
braces: BracesIcon,
|
|
966
|
+
subtitles: SubtitlesIcon,
|
|
967
|
+
"file-code": FileCodeIcon,
|
|
968
|
+
"file-json": FileJsonIcon,
|
|
969
|
+
"file-spreadsheet": FileSpreadsheetIcon,
|
|
970
|
+
database: DatabaseIcon,
|
|
971
|
+
package: PackageBoxIcon,
|
|
972
|
+
archive: ArchiveIcon,
|
|
973
|
+
folder: FolderIcon,
|
|
974
|
+
"folder-open": FolderOpenIcon,
|
|
975
|
+
file: FileIcon,
|
|
976
|
+
"file-audio": FileAudioIcon,
|
|
977
|
+
"file-video": FileVideoIcon
|
|
978
|
+
};
|
|
979
|
+
function FileIcon2({
|
|
980
|
+
fileName,
|
|
981
|
+
mimeType = null,
|
|
982
|
+
isFolder,
|
|
983
|
+
open,
|
|
984
|
+
colorClass,
|
|
985
|
+
className,
|
|
986
|
+
size = 16
|
|
987
|
+
}) {
|
|
988
|
+
const details = isFolder ? getFolderKindDetails(open) : getFileKindDetails(fileName ?? "", mimeType);
|
|
989
|
+
const Icon = ICONS[details.icon];
|
|
990
|
+
const style = {
|
|
991
|
+
width: `${size}px`,
|
|
992
|
+
height: `${size}px`,
|
|
993
|
+
minWidth: `${size}px`
|
|
994
|
+
};
|
|
995
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
996
|
+
Icon,
|
|
997
|
+
{
|
|
998
|
+
style,
|
|
999
|
+
className: cn("shrink-0", colorClass ?? details.colorClass, className),
|
|
1000
|
+
"aria-hidden": "true"
|
|
1001
|
+
}
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/react/upload-progress-list.tsx
|
|
1006
|
+
var import_react8 = require("react");
|
|
1007
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1008
|
+
var AUTO_DISMISS_MS = 1e4;
|
|
1009
|
+
function formatFileSize(bytes) {
|
|
1010
|
+
if (bytes == null || !Number.isFinite(bytes) || bytes < 0) return "\u2014";
|
|
1011
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1012
|
+
const units = ["KB", "MB", "GB", "TB"];
|
|
1013
|
+
let value = bytes;
|
|
1014
|
+
let unit = -1;
|
|
1015
|
+
do {
|
|
1016
|
+
value /= 1024;
|
|
1017
|
+
unit += 1;
|
|
1018
|
+
} while (value >= 1024 && unit < units.length - 1);
|
|
1019
|
+
return `${value >= 10 ? Math.round(value) : value.toFixed(1)} ${units[unit] ?? "TB"}`;
|
|
1020
|
+
}
|
|
1021
|
+
function UploadProgressListImpl({
|
|
1022
|
+
uploads,
|
|
1023
|
+
onDismiss,
|
|
1024
|
+
onOpenFile,
|
|
1025
|
+
className
|
|
1026
|
+
}) {
|
|
1027
|
+
const [hovered, setHovered] = (0, import_react8.useState)(false);
|
|
1028
|
+
const activeCount = uploads.filter((u) => u.status === "uploading").length;
|
|
1029
|
+
const successCount = uploads.filter((u) => u.status === "success").length;
|
|
1030
|
+
const errorCount = uploads.filter((u) => u.status === "error").length;
|
|
1031
|
+
const timersRef = (0, import_react8.useRef)(
|
|
1032
|
+
/* @__PURE__ */ new Map()
|
|
1033
|
+
);
|
|
1034
|
+
const onDismissRef = (0, import_react8.useRef)(onDismiss);
|
|
1035
|
+
onDismissRef.current = onDismiss;
|
|
1036
|
+
(0, import_react8.useEffect)(() => {
|
|
1037
|
+
const timers = timersRef.current;
|
|
1038
|
+
if (hovered) {
|
|
1039
|
+
timers.forEach((t) => clearTimeout(t));
|
|
1040
|
+
timers.clear();
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
for (const u of uploads) {
|
|
1044
|
+
if (u.status !== "success") continue;
|
|
1045
|
+
if (timers.has(u.id)) continue;
|
|
1046
|
+
const elapsed = u.completedAt ? Date.now() - u.completedAt : 0;
|
|
1047
|
+
const remaining = Math.max(0, AUTO_DISMISS_MS - elapsed);
|
|
1048
|
+
const handle = setTimeout(() => {
|
|
1049
|
+
timers.delete(u.id);
|
|
1050
|
+
onDismissRef.current(u.id);
|
|
1051
|
+
}, remaining);
|
|
1052
|
+
timers.set(u.id, handle);
|
|
1053
|
+
}
|
|
1054
|
+
const liveSuccessIds = new Set(
|
|
1055
|
+
uploads.filter((u) => u.status === "success").map((u) => u.id)
|
|
1056
|
+
);
|
|
1057
|
+
for (const [id, handle] of timers) {
|
|
1058
|
+
if (!liveSuccessIds.has(id)) {
|
|
1059
|
+
clearTimeout(handle);
|
|
1060
|
+
timers.delete(id);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}, [uploads, hovered]);
|
|
1064
|
+
(0, import_react8.useEffect)(() => {
|
|
1065
|
+
const timers = timersRef.current;
|
|
1066
|
+
return () => {
|
|
1067
|
+
timers.forEach((t) => clearTimeout(t));
|
|
1068
|
+
timers.clear();
|
|
1069
|
+
};
|
|
1070
|
+
}, []);
|
|
1071
|
+
if (uploads.length === 0) return null;
|
|
1072
|
+
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"}`;
|
|
1073
|
+
const completedIds = uploads.filter((u) => u.status === "success" || u.status === "error").map((u) => u.id);
|
|
1074
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1075
|
+
"div",
|
|
1076
|
+
{
|
|
1077
|
+
className: cn(
|
|
1078
|
+
"overflow-hidden rounded-lg border bg-background/95 shadow-lg backdrop-blur",
|
|
1079
|
+
className
|
|
1080
|
+
),
|
|
1081
|
+
onMouseEnter: () => setHovered(true),
|
|
1082
|
+
onMouseLeave: () => setHovered(false),
|
|
1083
|
+
"aria-label": "Uploads",
|
|
1084
|
+
children: [
|
|
1085
|
+
/* @__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: [
|
|
1086
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate text-xs font-medium text-foreground", children: headerLabel }),
|
|
1087
|
+
completedIds.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1088
|
+
"button",
|
|
1089
|
+
{
|
|
1090
|
+
type: "button",
|
|
1091
|
+
onClick: () => {
|
|
1092
|
+
for (const id of completedIds) onDismiss(id);
|
|
1093
|
+
},
|
|
1094
|
+
className: "shrink-0 rounded px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
1095
|
+
children: "Clear"
|
|
1096
|
+
}
|
|
1097
|
+
) : null
|
|
1098
|
+
] }),
|
|
1099
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "max-h-72 space-y-1 overflow-y-auto p-2", children: uploads.map((u) => {
|
|
1100
|
+
const percent = u.fileSize > 0 ? Math.min(100, Math.round(u.bytesUploaded / u.fileSize * 100)) : 0;
|
|
1101
|
+
const done = u.status === "success";
|
|
1102
|
+
const failed = u.status === "error";
|
|
1103
|
+
const canOpen = done && !!u.fileId && !!onOpenFile;
|
|
1104
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1105
|
+
"li",
|
|
1106
|
+
{
|
|
1107
|
+
className: cn(
|
|
1108
|
+
"flex items-center gap-2 rounded-md px-2 py-1.5 text-xs",
|
|
1109
|
+
canOpen && "cursor-pointer hover:bg-accent/60"
|
|
1110
|
+
),
|
|
1111
|
+
onClick: canOpen ? () => onOpenFile(u.fileId) : void 0,
|
|
1112
|
+
children: [
|
|
1113
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FileIcon2, { fileName: u.fileName, size: 18, className: "shrink-0" }),
|
|
1114
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1115
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1116
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate font-medium text-foreground", children: u.fileName }),
|
|
1117
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: failed ? "Failed" : done ? "Done" : `${percent}%` })
|
|
1118
|
+
] }),
|
|
1119
|
+
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)(
|
|
1120
|
+
"div",
|
|
1121
|
+
{
|
|
1122
|
+
className: cn(
|
|
1123
|
+
"h-1 rounded transition-all",
|
|
1124
|
+
failed ? "bg-destructive" : "bg-primary"
|
|
1125
|
+
),
|
|
1126
|
+
style: { width: failed ? "100%" : `${percent}%` }
|
|
1127
|
+
}
|
|
1128
|
+
) }),
|
|
1129
|
+
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: [
|
|
1130
|
+
formatFileSize(u.bytesUploaded),
|
|
1131
|
+
" /",
|
|
1132
|
+
" ",
|
|
1133
|
+
formatFileSize(u.fileSize)
|
|
1134
|
+
] }) : null
|
|
1135
|
+
] }),
|
|
1136
|
+
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: [
|
|
1137
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExternalLinkIcon, { className: "h-3 w-3", width: 12, height: 12 }),
|
|
1138
|
+
"Open"
|
|
1139
|
+
] }) : done ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1140
|
+
CheckCircle2Icon,
|
|
1141
|
+
{
|
|
1142
|
+
className: "h-4 w-4 shrink-0 text-emerald-500",
|
|
1143
|
+
width: 16,
|
|
1144
|
+
height: 16
|
|
1145
|
+
}
|
|
1146
|
+
) : failed ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1147
|
+
AlertCircleIcon,
|
|
1148
|
+
{
|
|
1149
|
+
className: "h-4 w-4 shrink-0 text-destructive",
|
|
1150
|
+
width: 16,
|
|
1151
|
+
height: 16
|
|
1152
|
+
}
|
|
1153
|
+
) : null,
|
|
1154
|
+
done || failed ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1155
|
+
"button",
|
|
1156
|
+
{
|
|
1157
|
+
type: "button",
|
|
1158
|
+
onClick: (e) => {
|
|
1159
|
+
e.stopPropagation();
|
|
1160
|
+
onDismiss(u.id);
|
|
1161
|
+
},
|
|
1162
|
+
"aria-label": "Dismiss",
|
|
1163
|
+
className: "flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
1164
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XIcon, { className: "h-3 w-3", width: 12, height: 12 })
|
|
1165
|
+
}
|
|
1166
|
+
) : null
|
|
1167
|
+
]
|
|
1168
|
+
},
|
|
1169
|
+
u.id
|
|
1170
|
+
);
|
|
1171
|
+
}) })
|
|
1172
|
+
]
|
|
1173
|
+
}
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
var UploadProgressList = (0, import_react8.memo)(UploadProgressListImpl);
|
|
1177
|
+
|
|
1178
|
+
// src/viewers/generic-viewer.tsx
|
|
1179
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1180
|
+
function GenericViewer({
|
|
1181
|
+
fileName,
|
|
1182
|
+
fileSize,
|
|
1183
|
+
generic,
|
|
1184
|
+
className
|
|
1185
|
+
}) {
|
|
1186
|
+
const onDownload = generic?.onDownload;
|
|
1187
|
+
const onViewAsText = generic?.onViewAsText;
|
|
1188
|
+
const viewAsTextLabel = generic?.viewAsTextLabel ?? "View as text";
|
|
1189
|
+
const viewAsTextPrimary = generic?.viewAsTextPrimary ?? true;
|
|
1190
|
+
const viewAsTextBusy = generic?.viewAsTextBusy ?? false;
|
|
1191
|
+
const hasViewAsText = typeof onViewAsText === "function";
|
|
1192
|
+
const hasDownload = typeof onDownload === "function";
|
|
1193
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1194
|
+
"div",
|
|
1195
|
+
{
|
|
1196
|
+
className: cn(
|
|
1197
|
+
"relative flex h-full w-full flex-col items-center justify-center gap-4 p-6 text-center",
|
|
1198
|
+
className
|
|
1199
|
+
),
|
|
1200
|
+
children: [
|
|
1201
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FileIcon2, { fileName, size: 48 }),
|
|
1202
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1203
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "max-w-md truncate text-sm font-medium", children: fileName }),
|
|
1204
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs text-muted-foreground", children: formatFileSize(fileSize ?? null) })
|
|
1205
|
+
] }),
|
|
1206
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "max-w-sm text-sm text-muted-foreground", children: generic?.message ?? (hasViewAsText ? "No built-in previewer for this file type." : "This file type can't be previewed here.") }),
|
|
1207
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
|
|
1208
|
+
onViewAsText && viewAsTextPrimary ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1209
|
+
ViewAsTextButton,
|
|
1210
|
+
{
|
|
1211
|
+
onClick: onViewAsText,
|
|
1212
|
+
label: viewAsTextLabel,
|
|
1213
|
+
busy: viewAsTextBusy,
|
|
1214
|
+
variant: "primary"
|
|
1215
|
+
}
|
|
1216
|
+
) : null,
|
|
1217
|
+
onDownload ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1218
|
+
"button",
|
|
1219
|
+
{
|
|
1220
|
+
type: "button",
|
|
1221
|
+
onClick: onDownload,
|
|
1222
|
+
className: cn(
|
|
1223
|
+
"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm",
|
|
1224
|
+
hasViewAsText && viewAsTextPrimary ? "border border-input bg-background text-foreground hover:bg-accent hover:text-accent-foreground" : "bg-primary text-primary-foreground hover:bg-primary/90"
|
|
1225
|
+
),
|
|
1226
|
+
children: [
|
|
1227
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DownloadIcon, { className: "h-4 w-4" }),
|
|
1228
|
+
"Download"
|
|
1229
|
+
]
|
|
1230
|
+
}
|
|
1231
|
+
) : null,
|
|
1232
|
+
onViewAsText && !viewAsTextPrimary ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1233
|
+
ViewAsTextButton,
|
|
1234
|
+
{
|
|
1235
|
+
onClick: onViewAsText,
|
|
1236
|
+
label: viewAsTextLabel,
|
|
1237
|
+
busy: viewAsTextBusy,
|
|
1238
|
+
variant: "secondary"
|
|
1239
|
+
}
|
|
1240
|
+
) : null
|
|
1241
|
+
] }),
|
|
1242
|
+
!hasDownload && !hasViewAsText ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "max-w-sm text-[11px] text-muted-foreground/70", children: "Download it from the file's actions menu to open it in another application." }) : null
|
|
1243
|
+
]
|
|
1244
|
+
}
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
function ViewAsTextButton({
|
|
1248
|
+
onClick,
|
|
1249
|
+
label,
|
|
1250
|
+
busy,
|
|
1251
|
+
variant
|
|
1252
|
+
}) {
|
|
1253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1254
|
+
"button",
|
|
1255
|
+
{
|
|
1256
|
+
type: "button",
|
|
1257
|
+
onClick,
|
|
1258
|
+
disabled: busy,
|
|
1259
|
+
className: cn(
|
|
1260
|
+
"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm transition-colors",
|
|
1261
|
+
"disabled:cursor-not-allowed disabled:opacity-60",
|
|
1262
|
+
variant === "primary" ? "bg-primary text-primary-foreground hover:bg-primary/90" : "border border-input bg-background text-foreground hover:bg-accent hover:text-accent-foreground"
|
|
1263
|
+
),
|
|
1264
|
+
children: [
|
|
1265
|
+
busy ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpinnerIcon, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FileTextIcon, { className: "h-4 w-4" }),
|
|
1266
|
+
label
|
|
1267
|
+
]
|
|
1268
|
+
}
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// src/viewers/html-viewer.tsx
|
|
1273
|
+
var import_react10 = require("react");
|
|
1274
|
+
|
|
1275
|
+
// src/viewers/markup-chrome.tsx
|
|
1276
|
+
var import_react9 = require("react");
|
|
1277
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1278
|
+
function useMarkupMode(controls) {
|
|
1279
|
+
const [localMode, setLocalMode] = (0, import_react9.useState)("rendered");
|
|
1280
|
+
const controlled = controls?.mode !== void 0;
|
|
1281
|
+
return {
|
|
1282
|
+
mode: controls?.mode ?? localMode,
|
|
1283
|
+
setMode: controls?.onModeChange ?? setLocalMode,
|
|
1284
|
+
controlled
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function MarkupModeToggle({
|
|
1288
|
+
mode,
|
|
1289
|
+
onChange,
|
|
1290
|
+
renderedLabel = "Rendered",
|
|
1291
|
+
renderedIcon = "image",
|
|
1292
|
+
className
|
|
1293
|
+
}) {
|
|
1294
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: cn("flex items-center gap-0.5", className), children: [
|
|
1295
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1296
|
+
ToggleButton,
|
|
1297
|
+
{
|
|
1298
|
+
active: mode === "rendered",
|
|
1299
|
+
onClick: () => onChange("rendered"),
|
|
1300
|
+
label: renderedLabel,
|
|
1301
|
+
icon: renderedIcon === "globe" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(GlobeIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ImageIcon, { className: "h-3.5 w-3.5" })
|
|
1302
|
+
}
|
|
1303
|
+
),
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1305
|
+
ToggleButton,
|
|
1306
|
+
{
|
|
1307
|
+
active: mode === "source",
|
|
1308
|
+
onClick: () => onChange("source"),
|
|
1309
|
+
label: "Source",
|
|
1310
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CodeIcon, { className: "h-3.5 w-3.5" })
|
|
1311
|
+
}
|
|
1312
|
+
)
|
|
1313
|
+
] });
|
|
1314
|
+
}
|
|
1315
|
+
function ToggleButton({
|
|
1316
|
+
active,
|
|
1317
|
+
onClick,
|
|
1318
|
+
icon,
|
|
1319
|
+
label
|
|
1320
|
+
}) {
|
|
1321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1322
|
+
"button",
|
|
1323
|
+
{
|
|
1324
|
+
type: "button",
|
|
1325
|
+
onClick,
|
|
1326
|
+
"aria-pressed": active,
|
|
1327
|
+
className: cn(
|
|
1328
|
+
"inline-flex items-center gap-1.5 rounded px-2 py-1 text-[11px] font-medium transition-colors",
|
|
1329
|
+
active ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
1330
|
+
),
|
|
1331
|
+
children: [
|
|
1332
|
+
icon,
|
|
1333
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: label })
|
|
1334
|
+
]
|
|
1335
|
+
}
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// src/viewers/html-viewer.tsx
|
|
1340
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1341
|
+
var VIEWPORT_WIDTH = {
|
|
1342
|
+
mobile: 390,
|
|
1343
|
+
tablet: 768,
|
|
1344
|
+
desktop: 1280
|
|
1345
|
+
};
|
|
1346
|
+
function HtmlViewer(props) {
|
|
1347
|
+
const { className, fileName, markupControls } = props;
|
|
1348
|
+
const { mode, setMode, controlled } = useMarkupMode(markupControls);
|
|
1349
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: cn("flex h-full w-full flex-col", className), children: [
|
|
1350
|
+
controlled ? null : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-border/60 bg-background px-3 py-1.5 text-xs", children: [
|
|
1351
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate font-medium text-foreground", children: fileName }),
|
|
1352
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1353
|
+
MarkupModeToggle,
|
|
1354
|
+
{
|
|
1355
|
+
mode,
|
|
1356
|
+
onChange: setMode,
|
|
1357
|
+
renderedLabel: "Rendered",
|
|
1358
|
+
renderedIcon: "globe"
|
|
1359
|
+
}
|
|
1360
|
+
)
|
|
1361
|
+
] }),
|
|
1362
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "relative min-h-0 flex-1", children: mode === "rendered" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(HtmlRenderedView, { ...props }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(HtmlSourceView, { ...props }) })
|
|
1363
|
+
] });
|
|
1364
|
+
}
|
|
1365
|
+
function HtmlRenderedView({ source, fileName, markupControls }) {
|
|
1366
|
+
const { url, unavailable } = useViewerDisplayUrl(source);
|
|
1367
|
+
const viewport = markupControls?.viewport ?? "auto";
|
|
1368
|
+
const reloadKey = markupControls?.reloadKey ?? 0;
|
|
1369
|
+
const [errored, setErrored] = (0, import_react10.useState)(false);
|
|
1370
|
+
(0, import_react10.useEffect)(() => {
|
|
1371
|
+
setErrored(false);
|
|
1372
|
+
}, [reloadKey, url]);
|
|
1373
|
+
if (unavailable) {
|
|
1374
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1375
|
+
ViewerError,
|
|
1376
|
+
{
|
|
1377
|
+
title: "This page isn't available",
|
|
1378
|
+
hint: "You may not have access to it, or it may have been deleted."
|
|
1379
|
+
}
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ViewerSkeleton, {});
|
|
1383
|
+
if (errored) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ViewerError, { title: "Preview unavailable" });
|
|
1384
|
+
const constrained = viewport !== "auto";
|
|
1385
|
+
const width = constrained ? VIEWPORT_WIDTH[viewport] : void 0;
|
|
1386
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1387
|
+
"div",
|
|
1388
|
+
{
|
|
1389
|
+
className: cn(
|
|
1390
|
+
"h-full w-full overflow-auto bg-muted/10",
|
|
1391
|
+
constrained && "flex items-start justify-center"
|
|
1392
|
+
),
|
|
1393
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1394
|
+
"div",
|
|
1395
|
+
{
|
|
1396
|
+
className: cn(
|
|
1397
|
+
"h-full bg-background",
|
|
1398
|
+
constrained ? "my-4 overflow-hidden rounded-md border border-border/60 shadow-lg" : "w-full"
|
|
1399
|
+
),
|
|
1400
|
+
style: constrained ? { width } : void 0,
|
|
1401
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1402
|
+
"iframe",
|
|
1403
|
+
{
|
|
1404
|
+
src: url,
|
|
1405
|
+
title: fileName,
|
|
1406
|
+
sandbox: "allow-scripts allow-popups allow-forms",
|
|
1407
|
+
referrerPolicy: "no-referrer",
|
|
1408
|
+
className: "h-full w-full border-0 bg-white",
|
|
1409
|
+
onError: () => setErrored(true)
|
|
1410
|
+
},
|
|
1411
|
+
`${reloadKey}-${url}`
|
|
1412
|
+
)
|
|
1413
|
+
}
|
|
1414
|
+
)
|
|
1415
|
+
}
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
function HtmlSourceView({ source, maxBytes = 1024 * 1024 }) {
|
|
1419
|
+
const { text, error, loading } = useViewerText(source, maxBytes);
|
|
1420
|
+
if (error) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ViewerError, { title: "Couldn't load source", message: error });
|
|
1421
|
+
if (loading || text === null) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ViewerSkeleton, {});
|
|
1422
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("pre", { className: "h-full w-full overflow-auto bg-muted/10 p-4 font-mono text-xs leading-relaxed break-all whitespace-pre-wrap", children: text });
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// src/core/use-media-load-recovery.ts
|
|
1426
|
+
var import_react11 = require("react");
|
|
1427
|
+
|
|
1428
|
+
// src/types.ts
|
|
1429
|
+
function mintDurableSrc(url) {
|
|
1430
|
+
return url;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// src/core/use-media-load-recovery.ts
|
|
1434
|
+
function normalizeOutcome(outcome) {
|
|
1435
|
+
return typeof outcome === "string" ? { verdict: outcome } : outcome;
|
|
1436
|
+
}
|
|
1437
|
+
var EPHEMERAL_SECRET_IDENTITY = "server:files-session-secret";
|
|
1438
|
+
function useMediaLoadRecovery(src, options) {
|
|
1439
|
+
const client = useMediaClient();
|
|
1440
|
+
const capture = useMediaFailureCapture();
|
|
1441
|
+
const recoverable = options?.recoverable !== false;
|
|
1442
|
+
const onTerminal = options?.onTerminal;
|
|
1443
|
+
const failureRef = options?.failureRef;
|
|
1444
|
+
const [result, setResult] = (0, import_react11.useState)({ source: src, retryKey: 0, failed: false });
|
|
1445
|
+
const attemptRef = (0, import_react11.useRef)(0);
|
|
1446
|
+
(0, import_react11.useEffect)(() => {
|
|
1447
|
+
attemptRef.current = 0;
|
|
1448
|
+
}, [src]);
|
|
1449
|
+
const identity = () => failureMediaIdentity(failureRef ?? null, src);
|
|
1450
|
+
const onLoadError = (event) => {
|
|
1451
|
+
if (!src || !recoverable) {
|
|
1452
|
+
setResult({ source: src, retryKey: 0, failed: true });
|
|
1453
|
+
if (src) {
|
|
1454
|
+
capture({
|
|
1455
|
+
phase: "element-load",
|
|
1456
|
+
mediaRef: identity(),
|
|
1457
|
+
terminal: true,
|
|
1458
|
+
retryOutcome: "not-attempted",
|
|
1459
|
+
message: "media element failed to load and the source is unrecoverable (foreign/object URL \u2014 a session refresh cannot fix it)"
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
onTerminal?.(event);
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
attemptRef.current += 1;
|
|
1466
|
+
const attempt = attemptRef.current;
|
|
1467
|
+
client.recoverLoadError(mintDurableSrc(src), attempt).then(
|
|
1468
|
+
(outcome) => {
|
|
1469
|
+
const report = normalizeOutcome(outcome);
|
|
1470
|
+
if (report.secretSource === "ephemeral") {
|
|
1471
|
+
capture({
|
|
1472
|
+
phase: "session",
|
|
1473
|
+
mediaRef: EPHEMERAL_SECRET_IDENTITY,
|
|
1474
|
+
terminal: false,
|
|
1475
|
+
message: "server cookie secret is ephemeral \u2014 private renders will break across restarts/instances (set a persistent files-session secret on the server)"
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
if (report.verdict === "retry") {
|
|
1479
|
+
setResult((current) => ({
|
|
1480
|
+
source: src,
|
|
1481
|
+
retryKey: current.source === src ? current.retryKey + 1 : 1,
|
|
1482
|
+
failed: false
|
|
1483
|
+
}));
|
|
1484
|
+
} else {
|
|
1485
|
+
setResult({ source: src, retryKey: 0, failed: true });
|
|
1486
|
+
capture({
|
|
1487
|
+
phase: attempt > 1 ? "element-load" : "recovery",
|
|
1488
|
+
mediaRef: identity(),
|
|
1489
|
+
terminal: true,
|
|
1490
|
+
retryOutcome: attempt > 1 ? "retried" : "not-attempted",
|
|
1491
|
+
message: attempt > 1 ? "media element failed to load after the session-refresh retry" : "media load recovery declined on the first attempt (session refresh did not produce a usable session)",
|
|
1492
|
+
...report.status !== void 0 ? { status: report.status } : {}
|
|
1493
|
+
});
|
|
1494
|
+
onTerminal?.(event);
|
|
1495
|
+
}
|
|
1496
|
+
},
|
|
1497
|
+
(err) => {
|
|
1498
|
+
setResult({ source: src, retryKey: 0, failed: true });
|
|
1499
|
+
capture({
|
|
1500
|
+
phase: "session",
|
|
1501
|
+
mediaRef: identity(),
|
|
1502
|
+
terminal: true,
|
|
1503
|
+
retryOutcome: "recovery-failed",
|
|
1504
|
+
message: `session recovery failed: ${err instanceof Error ? err.message : String(err ?? "unknown error")}`
|
|
1505
|
+
});
|
|
1506
|
+
onTerminal?.(event);
|
|
1507
|
+
}
|
|
1508
|
+
);
|
|
1509
|
+
};
|
|
1510
|
+
return {
|
|
1511
|
+
retryKey: result.source === src ? result.retryKey : 0,
|
|
1512
|
+
onLoadError,
|
|
1513
|
+
failed: result.source === src ? result.failed : false
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
// src/viewers/transparency-grid.ts
|
|
1518
|
+
var TRANSPARENCY_GRID_STYLE = {
|
|
1519
|
+
backgroundImage: "linear-gradient(45deg, rgb(0 0 0 / 0.08) 25%, transparent 25%), linear-gradient(-45deg, rgb(0 0 0 / 0.08) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgb(0 0 0 / 0.08) 75%), linear-gradient(-45deg, transparent 75%, rgb(0 0 0 / 0.08) 75%)",
|
|
1520
|
+
backgroundSize: "16px 16px",
|
|
1521
|
+
backgroundPosition: "0 0, 0 8px, 8px -8px, -8px 0"
|
|
1522
|
+
};
|
|
1523
|
+
|
|
1524
|
+
// src/viewers/svg-viewer.tsx
|
|
1525
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1526
|
+
function SvgViewer(props) {
|
|
1527
|
+
const { className, markupControls } = props;
|
|
1528
|
+
const { mode, setMode, controlled } = useMarkupMode(markupControls);
|
|
1529
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1530
|
+
"div",
|
|
1531
|
+
{
|
|
1532
|
+
className: cn(
|
|
1533
|
+
"relative flex h-full min-h-0 w-full flex-col bg-background",
|
|
1534
|
+
className
|
|
1535
|
+
),
|
|
1536
|
+
children: [
|
|
1537
|
+
controlled ? null : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1538
|
+
MarkupModeToggle,
|
|
1539
|
+
{
|
|
1540
|
+
mode,
|
|
1541
|
+
onChange: setMode,
|
|
1542
|
+
className: "absolute top-3 right-3 z-10 rounded-md border border-border bg-background/95 p-0.5 shadow-sm backdrop-blur-sm"
|
|
1543
|
+
}
|
|
1544
|
+
),
|
|
1545
|
+
mode === "rendered" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SvgRenderedView, { ...props }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SvgSourceView, { ...props })
|
|
1546
|
+
]
|
|
1547
|
+
}
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
function SvgRenderedView({ source, fileName }) {
|
|
1551
|
+
const { url, recoverable, unavailable } = useViewerDisplayUrl(source);
|
|
1552
|
+
const { retryKey, onLoadError, failed } = useMediaLoadRecovery(url, {
|
|
1553
|
+
recoverable,
|
|
1554
|
+
...source.kind === "ref" ? { failureRef: source.ref } : {}
|
|
1555
|
+
});
|
|
1556
|
+
if (unavailable) {
|
|
1557
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1558
|
+
ViewerError,
|
|
1559
|
+
{
|
|
1560
|
+
title: "This SVG isn't available",
|
|
1561
|
+
hint: "You may not have access to it, or it may have been deleted."
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ViewerSkeleton, { className: "flex-1" });
|
|
1566
|
+
if (failed) {
|
|
1567
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1568
|
+
ViewerError,
|
|
1569
|
+
{
|
|
1570
|
+
title: "SVG could not be rendered",
|
|
1571
|
+
hint: "Switch to the Source view to inspect the markup."
|
|
1572
|
+
}
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1576
|
+
"div",
|
|
1577
|
+
{
|
|
1578
|
+
className: "flex flex-1 items-center justify-center overflow-auto p-4",
|
|
1579
|
+
style: TRANSPARENCY_GRID_STYLE,
|
|
1580
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1581
|
+
"img",
|
|
1582
|
+
{
|
|
1583
|
+
src: url,
|
|
1584
|
+
alt: fileName,
|
|
1585
|
+
className: "max-h-full max-w-full object-contain drop-shadow-sm",
|
|
1586
|
+
onError: onLoadError
|
|
1587
|
+
},
|
|
1588
|
+
retryKey
|
|
1589
|
+
)
|
|
1590
|
+
}
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
function SvgSourceView({ source, maxBytes = 1024 * 1024 }) {
|
|
1594
|
+
const { text, error, loading } = useViewerText(source, maxBytes);
|
|
1595
|
+
if (error) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ViewerError, { title: "Could not load source", message: error });
|
|
1596
|
+
if (loading || text === null) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ViewerSkeleton, { className: "flex-1" });
|
|
1597
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("pre", { className: "m-0 flex-1 overflow-auto bg-muted/20 px-4 py-3 font-mono text-xs leading-relaxed break-words whitespace-pre-wrap text-foreground", children: text });
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// src/viewers/text-viewer.tsx
|
|
1601
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1602
|
+
function TextViewer({
|
|
1603
|
+
source,
|
|
1604
|
+
className,
|
|
1605
|
+
maxBytes = 1024 * 1024
|
|
1606
|
+
}) {
|
|
1607
|
+
const { text, error, loading, truncated } = useViewerText(source, maxBytes);
|
|
1608
|
+
if (error) return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ViewerError, { message: error, className });
|
|
1609
|
+
if (loading || text === null) {
|
|
1610
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ViewerSkeleton, { tone: "plain", className });
|
|
1611
|
+
}
|
|
1612
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1613
|
+
"div",
|
|
1614
|
+
{
|
|
1615
|
+
className: cn("flex h-full w-full flex-col overflow-hidden", className),
|
|
1616
|
+
children: [
|
|
1617
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ViewerToolbar, { children: [
|
|
1618
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-muted-foreground", children: truncated ? `Preview ${truncationNote(maxBytes)}.` : `${text.length.toLocaleString()} characters` }),
|
|
1619
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ViewerCopyButton, { text })
|
|
1620
|
+
] }),
|
|
1621
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("pre", { className: "flex-1 overflow-auto bg-background p-3 font-mono text-xs leading-5 break-words whitespace-pre-wrap", children: text })
|
|
1622
|
+
]
|
|
1623
|
+
}
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
// src/viewers/unavailable-viewer.tsx
|
|
1628
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1629
|
+
var ENGINE_FOR_KIND = {
|
|
1630
|
+
pdf: "a PDF engine (e.g. PDF.js / react-pdf)",
|
|
1631
|
+
markdown: "a Markdown renderer",
|
|
1632
|
+
data: "a spreadsheet/CSV parser",
|
|
1633
|
+
spreadsheet: "a spreadsheet/CSV parser",
|
|
1634
|
+
office: "an Office document extractor",
|
|
1635
|
+
image: "an image viewer",
|
|
1636
|
+
video: "a video player",
|
|
1637
|
+
audio: "an audio player"
|
|
1638
|
+
};
|
|
1639
|
+
function MediaViewerUnavailable({
|
|
1640
|
+
kind,
|
|
1641
|
+
...props
|
|
1642
|
+
}) {
|
|
1643
|
+
const engine = ENGINE_FOR_KIND[kind] ?? `a "${kind}" viewer`;
|
|
1644
|
+
const generic = props.generic;
|
|
1645
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: cn("flex h-full w-full flex-col", props.className), children: [
|
|
1646
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "shrink-0 border-b border-amber-500/30 bg-amber-500/10 px-3 py-2 text-center text-xs text-amber-700 dark:text-amber-300", children: [
|
|
1647
|
+
"No ",
|
|
1648
|
+
kind,
|
|
1649
|
+
" viewer is registered in this app, so this file can't be displayed here. An app developer registers ",
|
|
1650
|
+
engine,
|
|
1651
|
+
" with",
|
|
1652
|
+
" ",
|
|
1653
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("code", { className: "font-mono", children: [
|
|
1654
|
+
'registerMediaViewer("',
|
|
1655
|
+
kind,
|
|
1656
|
+
'", \u2026)'
|
|
1657
|
+
] }),
|
|
1658
|
+
"."
|
|
1659
|
+
] }),
|
|
1660
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1661
|
+
GenericViewer,
|
|
1662
|
+
{
|
|
1663
|
+
...props,
|
|
1664
|
+
className: void 0,
|
|
1665
|
+
generic: {
|
|
1666
|
+
...generic,
|
|
1667
|
+
message: generic?.message ?? "You can still download this file and open it in another application."
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
) })
|
|
1671
|
+
] });
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
// src/viewers/preview-kind.ts
|
|
1675
|
+
var EXTENSION_KIND = {
|
|
1676
|
+
svg: "svg",
|
|
1677
|
+
pdf: "pdf",
|
|
1678
|
+
md: "markdown",
|
|
1679
|
+
markdown: "markdown",
|
|
1680
|
+
mdx: "markdown",
|
|
1681
|
+
html: "html",
|
|
1682
|
+
htm: "html",
|
|
1683
|
+
csv: "data",
|
|
1684
|
+
tsv: "data",
|
|
1685
|
+
xlsx: "spreadsheet",
|
|
1686
|
+
xls: "spreadsheet",
|
|
1687
|
+
doc: "office",
|
|
1688
|
+
docx: "office",
|
|
1689
|
+
ppt: "office",
|
|
1690
|
+
pptx: "office",
|
|
1691
|
+
txt: "text",
|
|
1692
|
+
log: "text",
|
|
1693
|
+
text: "text"
|
|
1694
|
+
};
|
|
1695
|
+
function extensionOf2(fileName) {
|
|
1696
|
+
const base = fileName.split(/[\\/]/).pop() ?? fileName;
|
|
1697
|
+
const dot = base.lastIndexOf(".");
|
|
1698
|
+
if (dot <= 0) return "";
|
|
1699
|
+
return base.slice(dot + 1).toLowerCase();
|
|
1700
|
+
}
|
|
1701
|
+
function getPreviewKind(fileName, mimeType) {
|
|
1702
|
+
const explicit = EXTENSION_KIND[extensionOf2(fileName)];
|
|
1703
|
+
if (explicit) return explicit;
|
|
1704
|
+
const details = getFileKindDetails(fileName, mimeType ?? void 0);
|
|
1705
|
+
switch (details.category) {
|
|
1706
|
+
case "IMAGE":
|
|
1707
|
+
return details.icon === "pen-tool" ? "svg" : "image";
|
|
1708
|
+
case "VIDEO":
|
|
1709
|
+
return "video";
|
|
1710
|
+
case "AUDIO":
|
|
1711
|
+
return "audio";
|
|
1712
|
+
case "CODE":
|
|
1713
|
+
return "code";
|
|
1714
|
+
case "DATA":
|
|
1715
|
+
return "code";
|
|
1716
|
+
case "NOTEBOOK":
|
|
1717
|
+
return "code";
|
|
1718
|
+
case "SUBTITLES":
|
|
1719
|
+
return "text";
|
|
1720
|
+
case "DOCUMENT":
|
|
1721
|
+
return "text";
|
|
1722
|
+
default:
|
|
1723
|
+
return "generic";
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// src/viewers/media-file-viewer.tsx
|
|
1728
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1729
|
+
var BUILT_IN = {
|
|
1730
|
+
text: TextViewer,
|
|
1731
|
+
code: CodeViewer,
|
|
1732
|
+
svg: SvgViewer,
|
|
1733
|
+
html: HtmlViewer,
|
|
1734
|
+
generic: GenericViewer
|
|
1735
|
+
};
|
|
1736
|
+
function MediaFileViewer({
|
|
1737
|
+
previewKind,
|
|
1738
|
+
...props
|
|
1739
|
+
}) {
|
|
1740
|
+
const kind = previewKind ?? getPreviewKind(props.fileName, props.mimeType ?? null);
|
|
1741
|
+
const registered = getMediaViewer(kind);
|
|
1742
|
+
if (registered) return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: registered(props) });
|
|
1743
|
+
const builtIn = BUILT_IN[kind];
|
|
1744
|
+
if (builtIn) return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: builtIn(props) });
|
|
1745
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MediaViewerUnavailable, { ...props, kind });
|
|
1746
|
+
}
|
|
1747
|
+
//# sourceMappingURL=viewers.cjs.map
|