@ai-matrx/media 0.3.0 → 0.4.1
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 +156 -0
- package/README.md +60 -3
- 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 +1 -1
- 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 +1 -1
- package/dist/react.js.map +1 -1
- package/dist/viewers.cjs +1756 -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 +1738 -0
- package/dist/viewers.js.map +1 -0
- package/package.json +31 -4
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { MediaRefLike } from './index.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* src/viewers/types.ts — the viewer vocabulary.
|
|
6
|
+
*
|
|
7
|
+
* A "viewer" is a full-pane rendering of ONE file: what a file-preview pane,
|
|
8
|
+
* a file tab, or a binary-file fallback in a code editor shows. It is the
|
|
9
|
+
* layer above `/react`'s inline media elements.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The kinds the dispatcher understands. Ported from the matrx-frontend
|
|
14
|
+
* `PreviewKind` vocabulary so a host registry maps 1:1.
|
|
15
|
+
*/
|
|
16
|
+
type MediaViewerKind = "image" | "svg" | "video" | "audio" | "pdf" | "markdown" | "data" | "spreadsheet" | "code" | "office" | "html" | "text" | "generic";
|
|
17
|
+
/**
|
|
18
|
+
* Where the viewer's bytes and display URL come from.
|
|
19
|
+
*
|
|
20
|
+
* - `ref` — the normal lane: identity in, everything through the MediaClient
|
|
21
|
+
* (`resolve()` for the display URL, `getBlob()` for bytes).
|
|
22
|
+
* - `blob` — bytes the host ALREADY has (a code editor that downloaded the
|
|
23
|
+
* file to decide it was binary). No MediaClient round-trip, and the URL is
|
|
24
|
+
* an object URL, so load recovery is off by construction (law 3 is not
|
|
25
|
+
* violated: a `blob:` URL is not a signed URL and never leaves the tab).
|
|
26
|
+
*/
|
|
27
|
+
type MediaViewerSource = {
|
|
28
|
+
kind: "ref";
|
|
29
|
+
ref: MediaRefLike;
|
|
30
|
+
} | {
|
|
31
|
+
kind: "blob";
|
|
32
|
+
blob: Blob;
|
|
33
|
+
url: string;
|
|
34
|
+
};
|
|
35
|
+
/** Escape-hatch actions the generic card offers. */
|
|
36
|
+
interface MediaViewerGenericOptions {
|
|
37
|
+
onDownload?: (() => void) | undefined;
|
|
38
|
+
onViewAsText?: (() => void) | undefined;
|
|
39
|
+
viewAsTextLabel?: string | undefined;
|
|
40
|
+
viewAsTextPrimary?: boolean | undefined;
|
|
41
|
+
viewAsTextBusy?: boolean | undefined;
|
|
42
|
+
message?: string | undefined;
|
|
43
|
+
}
|
|
44
|
+
/** Rendered-vs-source mode for the markup viewers (SVG, HTML). */
|
|
45
|
+
type MediaViewerMarkupMode = "rendered" | "source";
|
|
46
|
+
/** Device-width simulation for the HTML viewer. */
|
|
47
|
+
type MediaViewerViewport = "auto" | "mobile" | "tablet" | "desktop";
|
|
48
|
+
/**
|
|
49
|
+
* Optional external control of the markup viewers. When a host mounts its own
|
|
50
|
+
* control rail (matrx-frontend's `FileViewerControlsProvider`), it drives the
|
|
51
|
+
* viewer through these props and the viewer hides its built-in toolbar. When
|
|
52
|
+
* absent, the viewer owns the state and renders its own toolbar — the C23
|
|
53
|
+
* default: capable standalone, steerable when the host has a rail.
|
|
54
|
+
*/
|
|
55
|
+
interface MediaViewerMarkupControls {
|
|
56
|
+
mode?: MediaViewerMarkupMode | undefined;
|
|
57
|
+
onModeChange?: ((mode: MediaViewerMarkupMode) => void) | undefined;
|
|
58
|
+
viewport?: MediaViewerViewport | undefined;
|
|
59
|
+
/** Bump to force a clean re-mount of the rendered frame. */
|
|
60
|
+
reloadKey?: number | undefined;
|
|
61
|
+
}
|
|
62
|
+
/** The props EVERY viewer body (built-in or host-registered) receives. */
|
|
63
|
+
interface MediaViewerProps {
|
|
64
|
+
source: MediaViewerSource;
|
|
65
|
+
fileName: string;
|
|
66
|
+
fileSize?: number | null | undefined;
|
|
67
|
+
mimeType?: string | null | undefined;
|
|
68
|
+
className?: string | undefined;
|
|
69
|
+
/** Max bytes read for text-shaped viewers. Defaults to 1MB. */
|
|
70
|
+
maxBytes?: number | undefined;
|
|
71
|
+
/** Controlled 1-based page, for paged engines (PDF). */
|
|
72
|
+
pageNumber?: number | undefined;
|
|
73
|
+
onPageChange?: ((pageNumber: number) => void) | undefined;
|
|
74
|
+
/** Passthroughs for the generic (no-viewer) card. */
|
|
75
|
+
generic?: MediaViewerGenericOptions | undefined;
|
|
76
|
+
/** External control of the markup viewers (SVG / HTML). */
|
|
77
|
+
markupControls?: MediaViewerMarkupControls | undefined;
|
|
78
|
+
}
|
|
79
|
+
/** A viewer body. Structural so a host component satisfies it with no import. */
|
|
80
|
+
type MediaViewerComponent = (props: MediaViewerProps) => unknown;
|
|
81
|
+
/**
|
|
82
|
+
* The code-highlighting seam. A syntax highlighter (Prism, Shiki, Monaco) is
|
|
83
|
+
* a multi-hundred-kilobyte browser-only engine — C8 says a headless consumer
|
|
84
|
+
* must never be forced to carry it, so the package ships the code viewer's
|
|
85
|
+
* CHROME and an honest, capable un-highlighted default, and a host registers
|
|
86
|
+
* the engine. `language` is the package's detected Prism-style language id.
|
|
87
|
+
*/
|
|
88
|
+
interface MediaCodeHighlighterProps {
|
|
89
|
+
code: string;
|
|
90
|
+
language: string;
|
|
91
|
+
}
|
|
92
|
+
type MediaCodeHighlighter = (props: MediaCodeHighlighterProps) => unknown;
|
|
93
|
+
|
|
94
|
+
interface MediaFileViewerProps extends MediaViewerProps {
|
|
95
|
+
/**
|
|
96
|
+
* The kind to render. Omit to let the package derive it from the file name
|
|
97
|
+
* and MIME type (`getPreviewKind`).
|
|
98
|
+
*/
|
|
99
|
+
previewKind?: MediaViewerKind | undefined;
|
|
100
|
+
}
|
|
101
|
+
declare function MediaFileViewer({ previewKind, ...props }: MediaFileViewerProps): react.JSX.Element;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* src/viewers/registry.ts — the heavy-engine seam.
|
|
105
|
+
*
|
|
106
|
+
* The package ships working bodies for every viewer kind it can render with
|
|
107
|
+
* DOM primitives alone (text, code chrome, svg, html, generic). The kinds
|
|
108
|
+
* whose rendering IS a multi-megabyte browser-only engine — PDF.js, a
|
|
109
|
+
* Markdown/KaTeX stack, SheetJS, a server-side Office extractor — ride this
|
|
110
|
+
* seam instead (C8 + the diff-package Monaco precedent): a host registers the
|
|
111
|
+
* engine once, and until it does, the dispatcher renders an ANNOUNCING
|
|
112
|
+
* default that names the missing engine and still offers download/open. A
|
|
113
|
+
* dead pane is never an outcome.
|
|
114
|
+
*
|
|
115
|
+
* MODULE-LEVEL MUTABLE STATE IS BANNED (the dual ESM/CJS bundle rule): with
|
|
116
|
+
* `splitting: false` and two formats, each loader graph instantiates its own
|
|
117
|
+
* copy of this module, so a plain module-scope Map would silently split the
|
|
118
|
+
* registry — a host registering through the CJS graph would be invisible to a
|
|
119
|
+
* component rendered from the ESM graph. The maps therefore live on
|
|
120
|
+
* `globalThis` under `Symbol.for` slots, and the tarball canary proves a
|
|
121
|
+
* registration made in one graph is visible from the other.
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Register the viewer body for a kind. Registering a kind the package already
|
|
126
|
+
* renders REPLACES the built-in — that is the sanctioned host override (a
|
|
127
|
+
* feature-specific domain viewer implementing the same contract).
|
|
128
|
+
*
|
|
129
|
+
* Returns an unregister function so a test or a hot-reloading host can undo it.
|
|
130
|
+
*/
|
|
131
|
+
declare function registerMediaViewer(kind: MediaViewerKind, viewer: MediaViewerComponent): () => void;
|
|
132
|
+
/** The registered viewer for a kind, or `null` when nothing is registered. */
|
|
133
|
+
declare function getMediaViewer(kind: MediaViewerKind): MediaViewerComponent | null;
|
|
134
|
+
/** Every kind a host has registered — for diagnostics and tests. */
|
|
135
|
+
declare function registeredMediaViewerKinds(): MediaViewerKind[];
|
|
136
|
+
/**
|
|
137
|
+
* Register the syntax-highlighting engine for the built-in code viewer.
|
|
138
|
+
* Without one the code viewer still renders — monospace, line-numbered,
|
|
139
|
+
* copyable, truncation-honest — it simply is not colored.
|
|
140
|
+
*/
|
|
141
|
+
declare function registerMediaCodeHighlighter(highlighter: MediaCodeHighlighter): () => void;
|
|
142
|
+
declare function getMediaCodeHighlighter(): MediaCodeHighlighter | null;
|
|
143
|
+
|
|
144
|
+
declare function TextViewer({ source, className, maxBytes, }: MediaViewerProps): react.JSX.Element;
|
|
145
|
+
|
|
146
|
+
/** Filename → language id. Exported: hosts registering an engine reuse it. */
|
|
147
|
+
declare function detectCodeLanguage(fileName: string): string;
|
|
148
|
+
declare function CodeViewer({ source, fileName, className, maxBytes, }: MediaViewerProps): react.JSX.Element;
|
|
149
|
+
|
|
150
|
+
declare function SvgViewer(props: MediaViewerProps): react.JSX.Element;
|
|
151
|
+
|
|
152
|
+
declare function HtmlViewer(props: MediaViewerProps): react.JSX.Element;
|
|
153
|
+
|
|
154
|
+
declare function GenericViewer({ fileName, fileSize, generic, className, }: MediaViewerProps): react.JSX.Element;
|
|
155
|
+
|
|
156
|
+
declare function MediaViewerUnavailable({ kind, ...props }: MediaViewerProps & {
|
|
157
|
+
kind: MediaViewerKind;
|
|
158
|
+
}): react.JSX.Element;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* src/viewers/preview-kind.ts — filename/MIME → viewer kind.
|
|
162
|
+
*
|
|
163
|
+
* The DEFAULT resolution, derived from the package's own file-kind registry
|
|
164
|
+
* (`getFileKindDetails`) so a fresh app gets correct dispatch with zero setup.
|
|
165
|
+
* A host with a richer registry of its own — matrx-frontend's `FILE_TYPES`
|
|
166
|
+
* carries preview/thumbnail/size-cap columns this package deliberately does
|
|
167
|
+
* not absorb (C8) — passes `previewKind` explicitly and this is never called.
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
declare function getPreviewKind(fileName: string, mimeType?: string | null): MediaViewerKind;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* src/viewers/chrome.tsx — the shared viewer chrome.
|
|
174
|
+
*
|
|
175
|
+
* The five absorbed matrx-frontend previewers each hand-rolled the same
|
|
176
|
+
* error card, the same skeleton, and the same copy button. One copy here;
|
|
177
|
+
* one place to make them good.
|
|
178
|
+
*/
|
|
179
|
+
/** The uniform loading bar every viewer shows while bytes are in flight. */
|
|
180
|
+
declare function ViewerSkeleton({ className, tone, }: {
|
|
181
|
+
className?: string | undefined;
|
|
182
|
+
tone?: "muted" | "plain" | undefined;
|
|
183
|
+
}): react.JSX.Element;
|
|
184
|
+
/**
|
|
185
|
+
* The honest failure card. It shows the REAL underlying message (HTTP
|
|
186
|
+
* 401/403/404/network) rather than a generic "Preview failed" — the original
|
|
187
|
+
* previewers' deliberate choice, kept: a user who can see the status can act
|
|
188
|
+
* on it, and a support thread starts with a fact instead of a shrug.
|
|
189
|
+
*/
|
|
190
|
+
declare function ViewerError({ title, message, hint, className, }: {
|
|
191
|
+
title?: string | undefined;
|
|
192
|
+
message?: string | null | undefined;
|
|
193
|
+
hint?: string | undefined;
|
|
194
|
+
className?: string | undefined;
|
|
195
|
+
}): react.JSX.Element;
|
|
196
|
+
/**
|
|
197
|
+
* Copy-to-clipboard button with the settled-state flip. Swallows the
|
|
198
|
+
* clipboard rejection that non-secure contexts always throw — a failed copy
|
|
199
|
+
* is not an error worth a card, and the un-flipped button IS the signal.
|
|
200
|
+
*/
|
|
201
|
+
declare function ViewerCopyButton({ text, className, }: {
|
|
202
|
+
text: string | null;
|
|
203
|
+
className?: string | undefined;
|
|
204
|
+
}): react.JSX.Element;
|
|
205
|
+
/** The strip above a text-shaped viewer: status on the left, copy on the right. */
|
|
206
|
+
declare function ViewerToolbar({ children, className, }: {
|
|
207
|
+
children?: React.ReactNode;
|
|
208
|
+
className?: string | undefined;
|
|
209
|
+
}): react.JSX.Element;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* src/viewers/transparency-grid.ts — the alpha-channel checkerboard.
|
|
213
|
+
*
|
|
214
|
+
* Ships as inline style rather than a utility class: the matrx-frontend
|
|
215
|
+
* originals used two different mechanisms for the same thing (an inline
|
|
216
|
+
* gradient stack in ImagePreview, a `bg-checkerboard` utility in SvgPreview),
|
|
217
|
+
* and a utility class is exactly the kind of host-owned dependency C26 bans a
|
|
218
|
+
* package component from assuming. Sized in CSS pixels so the pattern reads
|
|
219
|
+
* the same at any zoom.
|
|
220
|
+
*/
|
|
221
|
+
declare const TRANSPARENCY_GRID_STYLE: {
|
|
222
|
+
backgroundImage: string;
|
|
223
|
+
backgroundSize: string;
|
|
224
|
+
backgroundPosition: string;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* src/viewers/use-viewer-bytes.ts — the ONE byte/text/URL machinery every
|
|
229
|
+
* viewer body shares.
|
|
230
|
+
*
|
|
231
|
+
* Four separate matrx-frontend previewers (Text, Code, the SVG source view,
|
|
232
|
+
* the HTML source view) each carried their own copy of "read the blob, cap it
|
|
233
|
+
* at N bytes, mark it truncated, surface the REAL backend error verbatim,
|
|
234
|
+
* cancel on unmount". Four copies is four places to drift; C22 says that
|
|
235
|
+
* machinery lives in the package exactly once.
|
|
236
|
+
*/
|
|
237
|
+
|
|
238
|
+
interface ViewerBytesState {
|
|
239
|
+
blob: Blob | null;
|
|
240
|
+
loading: boolean;
|
|
241
|
+
/** The real underlying message (HTTP 401/403/404/network) — never generic. */
|
|
242
|
+
error: string | null;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Bytes for a viewer source. A `blob` source is already resolved; a `ref`
|
|
246
|
+
* source goes through `MediaClient.getBlob` (which is also what emits the
|
|
247
|
+
* terminal-failure diagnostics — see `useMediaBlob`).
|
|
248
|
+
*
|
|
249
|
+
* `useMediaBlob` is called UNCONDITIONALLY (with `null` for blob sources) so
|
|
250
|
+
* the hook order never changes with the source shape.
|
|
251
|
+
*/
|
|
252
|
+
declare function useViewerBytes(source: MediaViewerSource): ViewerBytesState;
|
|
253
|
+
interface ViewerTextState {
|
|
254
|
+
text: string | null;
|
|
255
|
+
loading: boolean;
|
|
256
|
+
error: string | null;
|
|
257
|
+
/** True when the file exceeded `maxBytes` and only the head was read. */
|
|
258
|
+
truncated: boolean;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Bytes → text, capped. The cap is not a nicety: a 500MB log file decoded in
|
|
262
|
+
* full takes the tab down, which is how the original previewers were written
|
|
263
|
+
* and why the cap must travel with the package rather than be re-derived.
|
|
264
|
+
*/
|
|
265
|
+
declare function useViewerText(source: MediaViewerSource, maxBytes?: number): ViewerTextState;
|
|
266
|
+
interface ViewerDisplayUrlState {
|
|
267
|
+
/**
|
|
268
|
+
* The URL a media element may bind. Durable by construction: it is either
|
|
269
|
+
* a `MediaClient.resolve()` result or the host's own object URL.
|
|
270
|
+
*/
|
|
271
|
+
url: string | null;
|
|
272
|
+
/**
|
|
273
|
+
* Whether the client's ONE retry policy can rescue a load failure. False
|
|
274
|
+
* for object URLs and for resolutions the client marked unrecoverable
|
|
275
|
+
* (foreign links) — a session refresh cannot resurrect someone else's URL.
|
|
276
|
+
*/
|
|
277
|
+
recoverable: boolean;
|
|
278
|
+
/** `resolve()` refused the ref — render the honest unavailable state. */
|
|
279
|
+
unavailable: boolean;
|
|
280
|
+
loading: boolean;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* The display URL for a viewer source. Law 2: even for a `ref` carrying a
|
|
284
|
+
* caller-supplied URL, resolution goes through the client — the package never
|
|
285
|
+
* trusts a string it was handed.
|
|
286
|
+
*/
|
|
287
|
+
declare function useViewerDisplayUrl(source: MediaViewerSource): ViewerDisplayUrlState;
|
|
288
|
+
|
|
289
|
+
export { CodeViewer, GenericViewer, HtmlViewer, type MediaCodeHighlighter, type MediaCodeHighlighterProps, MediaFileViewer, type MediaFileViewerProps, type MediaViewerComponent, type MediaViewerGenericOptions, type MediaViewerKind, type MediaViewerMarkupControls, type MediaViewerMarkupMode, type MediaViewerProps, type MediaViewerSource, MediaViewerUnavailable, type MediaViewerViewport, SvgViewer, TRANSPARENCY_GRID_STYLE, TextViewer, type ViewerBytesState, ViewerCopyButton, type ViewerDisplayUrlState, ViewerError, ViewerSkeleton, type ViewerTextState, ViewerToolbar, detectCodeLanguage, getMediaCodeHighlighter, getMediaViewer, getPreviewKind, registerMediaCodeHighlighter, registerMediaViewer, registeredMediaViewerKinds, useViewerBytes, useViewerDisplayUrl, useViewerText };
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { MediaRefLike } from './index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* src/viewers/types.ts — the viewer vocabulary.
|
|
6
|
+
*
|
|
7
|
+
* A "viewer" is a full-pane rendering of ONE file: what a file-preview pane,
|
|
8
|
+
* a file tab, or a binary-file fallback in a code editor shows. It is the
|
|
9
|
+
* layer above `/react`'s inline media elements.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The kinds the dispatcher understands. Ported from the matrx-frontend
|
|
14
|
+
* `PreviewKind` vocabulary so a host registry maps 1:1.
|
|
15
|
+
*/
|
|
16
|
+
type MediaViewerKind = "image" | "svg" | "video" | "audio" | "pdf" | "markdown" | "data" | "spreadsheet" | "code" | "office" | "html" | "text" | "generic";
|
|
17
|
+
/**
|
|
18
|
+
* Where the viewer's bytes and display URL come from.
|
|
19
|
+
*
|
|
20
|
+
* - `ref` — the normal lane: identity in, everything through the MediaClient
|
|
21
|
+
* (`resolve()` for the display URL, `getBlob()` for bytes).
|
|
22
|
+
* - `blob` — bytes the host ALREADY has (a code editor that downloaded the
|
|
23
|
+
* file to decide it was binary). No MediaClient round-trip, and the URL is
|
|
24
|
+
* an object URL, so load recovery is off by construction (law 3 is not
|
|
25
|
+
* violated: a `blob:` URL is not a signed URL and never leaves the tab).
|
|
26
|
+
*/
|
|
27
|
+
type MediaViewerSource = {
|
|
28
|
+
kind: "ref";
|
|
29
|
+
ref: MediaRefLike;
|
|
30
|
+
} | {
|
|
31
|
+
kind: "blob";
|
|
32
|
+
blob: Blob;
|
|
33
|
+
url: string;
|
|
34
|
+
};
|
|
35
|
+
/** Escape-hatch actions the generic card offers. */
|
|
36
|
+
interface MediaViewerGenericOptions {
|
|
37
|
+
onDownload?: (() => void) | undefined;
|
|
38
|
+
onViewAsText?: (() => void) | undefined;
|
|
39
|
+
viewAsTextLabel?: string | undefined;
|
|
40
|
+
viewAsTextPrimary?: boolean | undefined;
|
|
41
|
+
viewAsTextBusy?: boolean | undefined;
|
|
42
|
+
message?: string | undefined;
|
|
43
|
+
}
|
|
44
|
+
/** Rendered-vs-source mode for the markup viewers (SVG, HTML). */
|
|
45
|
+
type MediaViewerMarkupMode = "rendered" | "source";
|
|
46
|
+
/** Device-width simulation for the HTML viewer. */
|
|
47
|
+
type MediaViewerViewport = "auto" | "mobile" | "tablet" | "desktop";
|
|
48
|
+
/**
|
|
49
|
+
* Optional external control of the markup viewers. When a host mounts its own
|
|
50
|
+
* control rail (matrx-frontend's `FileViewerControlsProvider`), it drives the
|
|
51
|
+
* viewer through these props and the viewer hides its built-in toolbar. When
|
|
52
|
+
* absent, the viewer owns the state and renders its own toolbar — the C23
|
|
53
|
+
* default: capable standalone, steerable when the host has a rail.
|
|
54
|
+
*/
|
|
55
|
+
interface MediaViewerMarkupControls {
|
|
56
|
+
mode?: MediaViewerMarkupMode | undefined;
|
|
57
|
+
onModeChange?: ((mode: MediaViewerMarkupMode) => void) | undefined;
|
|
58
|
+
viewport?: MediaViewerViewport | undefined;
|
|
59
|
+
/** Bump to force a clean re-mount of the rendered frame. */
|
|
60
|
+
reloadKey?: number | undefined;
|
|
61
|
+
}
|
|
62
|
+
/** The props EVERY viewer body (built-in or host-registered) receives. */
|
|
63
|
+
interface MediaViewerProps {
|
|
64
|
+
source: MediaViewerSource;
|
|
65
|
+
fileName: string;
|
|
66
|
+
fileSize?: number | null | undefined;
|
|
67
|
+
mimeType?: string | null | undefined;
|
|
68
|
+
className?: string | undefined;
|
|
69
|
+
/** Max bytes read for text-shaped viewers. Defaults to 1MB. */
|
|
70
|
+
maxBytes?: number | undefined;
|
|
71
|
+
/** Controlled 1-based page, for paged engines (PDF). */
|
|
72
|
+
pageNumber?: number | undefined;
|
|
73
|
+
onPageChange?: ((pageNumber: number) => void) | undefined;
|
|
74
|
+
/** Passthroughs for the generic (no-viewer) card. */
|
|
75
|
+
generic?: MediaViewerGenericOptions | undefined;
|
|
76
|
+
/** External control of the markup viewers (SVG / HTML). */
|
|
77
|
+
markupControls?: MediaViewerMarkupControls | undefined;
|
|
78
|
+
}
|
|
79
|
+
/** A viewer body. Structural so a host component satisfies it with no import. */
|
|
80
|
+
type MediaViewerComponent = (props: MediaViewerProps) => unknown;
|
|
81
|
+
/**
|
|
82
|
+
* The code-highlighting seam. A syntax highlighter (Prism, Shiki, Monaco) is
|
|
83
|
+
* a multi-hundred-kilobyte browser-only engine — C8 says a headless consumer
|
|
84
|
+
* must never be forced to carry it, so the package ships the code viewer's
|
|
85
|
+
* CHROME and an honest, capable un-highlighted default, and a host registers
|
|
86
|
+
* the engine. `language` is the package's detected Prism-style language id.
|
|
87
|
+
*/
|
|
88
|
+
interface MediaCodeHighlighterProps {
|
|
89
|
+
code: string;
|
|
90
|
+
language: string;
|
|
91
|
+
}
|
|
92
|
+
type MediaCodeHighlighter = (props: MediaCodeHighlighterProps) => unknown;
|
|
93
|
+
|
|
94
|
+
interface MediaFileViewerProps extends MediaViewerProps {
|
|
95
|
+
/**
|
|
96
|
+
* The kind to render. Omit to let the package derive it from the file name
|
|
97
|
+
* and MIME type (`getPreviewKind`).
|
|
98
|
+
*/
|
|
99
|
+
previewKind?: MediaViewerKind | undefined;
|
|
100
|
+
}
|
|
101
|
+
declare function MediaFileViewer({ previewKind, ...props }: MediaFileViewerProps): react.JSX.Element;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* src/viewers/registry.ts — the heavy-engine seam.
|
|
105
|
+
*
|
|
106
|
+
* The package ships working bodies for every viewer kind it can render with
|
|
107
|
+
* DOM primitives alone (text, code chrome, svg, html, generic). The kinds
|
|
108
|
+
* whose rendering IS a multi-megabyte browser-only engine — PDF.js, a
|
|
109
|
+
* Markdown/KaTeX stack, SheetJS, a server-side Office extractor — ride this
|
|
110
|
+
* seam instead (C8 + the diff-package Monaco precedent): a host registers the
|
|
111
|
+
* engine once, and until it does, the dispatcher renders an ANNOUNCING
|
|
112
|
+
* default that names the missing engine and still offers download/open. A
|
|
113
|
+
* dead pane is never an outcome.
|
|
114
|
+
*
|
|
115
|
+
* MODULE-LEVEL MUTABLE STATE IS BANNED (the dual ESM/CJS bundle rule): with
|
|
116
|
+
* `splitting: false` and two formats, each loader graph instantiates its own
|
|
117
|
+
* copy of this module, so a plain module-scope Map would silently split the
|
|
118
|
+
* registry — a host registering through the CJS graph would be invisible to a
|
|
119
|
+
* component rendered from the ESM graph. The maps therefore live on
|
|
120
|
+
* `globalThis` under `Symbol.for` slots, and the tarball canary proves a
|
|
121
|
+
* registration made in one graph is visible from the other.
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Register the viewer body for a kind. Registering a kind the package already
|
|
126
|
+
* renders REPLACES the built-in — that is the sanctioned host override (a
|
|
127
|
+
* feature-specific domain viewer implementing the same contract).
|
|
128
|
+
*
|
|
129
|
+
* Returns an unregister function so a test or a hot-reloading host can undo it.
|
|
130
|
+
*/
|
|
131
|
+
declare function registerMediaViewer(kind: MediaViewerKind, viewer: MediaViewerComponent): () => void;
|
|
132
|
+
/** The registered viewer for a kind, or `null` when nothing is registered. */
|
|
133
|
+
declare function getMediaViewer(kind: MediaViewerKind): MediaViewerComponent | null;
|
|
134
|
+
/** Every kind a host has registered — for diagnostics and tests. */
|
|
135
|
+
declare function registeredMediaViewerKinds(): MediaViewerKind[];
|
|
136
|
+
/**
|
|
137
|
+
* Register the syntax-highlighting engine for the built-in code viewer.
|
|
138
|
+
* Without one the code viewer still renders — monospace, line-numbered,
|
|
139
|
+
* copyable, truncation-honest — it simply is not colored.
|
|
140
|
+
*/
|
|
141
|
+
declare function registerMediaCodeHighlighter(highlighter: MediaCodeHighlighter): () => void;
|
|
142
|
+
declare function getMediaCodeHighlighter(): MediaCodeHighlighter | null;
|
|
143
|
+
|
|
144
|
+
declare function TextViewer({ source, className, maxBytes, }: MediaViewerProps): react.JSX.Element;
|
|
145
|
+
|
|
146
|
+
/** Filename → language id. Exported: hosts registering an engine reuse it. */
|
|
147
|
+
declare function detectCodeLanguage(fileName: string): string;
|
|
148
|
+
declare function CodeViewer({ source, fileName, className, maxBytes, }: MediaViewerProps): react.JSX.Element;
|
|
149
|
+
|
|
150
|
+
declare function SvgViewer(props: MediaViewerProps): react.JSX.Element;
|
|
151
|
+
|
|
152
|
+
declare function HtmlViewer(props: MediaViewerProps): react.JSX.Element;
|
|
153
|
+
|
|
154
|
+
declare function GenericViewer({ fileName, fileSize, generic, className, }: MediaViewerProps): react.JSX.Element;
|
|
155
|
+
|
|
156
|
+
declare function MediaViewerUnavailable({ kind, ...props }: MediaViewerProps & {
|
|
157
|
+
kind: MediaViewerKind;
|
|
158
|
+
}): react.JSX.Element;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* src/viewers/preview-kind.ts — filename/MIME → viewer kind.
|
|
162
|
+
*
|
|
163
|
+
* The DEFAULT resolution, derived from the package's own file-kind registry
|
|
164
|
+
* (`getFileKindDetails`) so a fresh app gets correct dispatch with zero setup.
|
|
165
|
+
* A host with a richer registry of its own — matrx-frontend's `FILE_TYPES`
|
|
166
|
+
* carries preview/thumbnail/size-cap columns this package deliberately does
|
|
167
|
+
* not absorb (C8) — passes `previewKind` explicitly and this is never called.
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
declare function getPreviewKind(fileName: string, mimeType?: string | null): MediaViewerKind;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* src/viewers/chrome.tsx — the shared viewer chrome.
|
|
174
|
+
*
|
|
175
|
+
* The five absorbed matrx-frontend previewers each hand-rolled the same
|
|
176
|
+
* error card, the same skeleton, and the same copy button. One copy here;
|
|
177
|
+
* one place to make them good.
|
|
178
|
+
*/
|
|
179
|
+
/** The uniform loading bar every viewer shows while bytes are in flight. */
|
|
180
|
+
declare function ViewerSkeleton({ className, tone, }: {
|
|
181
|
+
className?: string | undefined;
|
|
182
|
+
tone?: "muted" | "plain" | undefined;
|
|
183
|
+
}): react.JSX.Element;
|
|
184
|
+
/**
|
|
185
|
+
* The honest failure card. It shows the REAL underlying message (HTTP
|
|
186
|
+
* 401/403/404/network) rather than a generic "Preview failed" — the original
|
|
187
|
+
* previewers' deliberate choice, kept: a user who can see the status can act
|
|
188
|
+
* on it, and a support thread starts with a fact instead of a shrug.
|
|
189
|
+
*/
|
|
190
|
+
declare function ViewerError({ title, message, hint, className, }: {
|
|
191
|
+
title?: string | undefined;
|
|
192
|
+
message?: string | null | undefined;
|
|
193
|
+
hint?: string | undefined;
|
|
194
|
+
className?: string | undefined;
|
|
195
|
+
}): react.JSX.Element;
|
|
196
|
+
/**
|
|
197
|
+
* Copy-to-clipboard button with the settled-state flip. Swallows the
|
|
198
|
+
* clipboard rejection that non-secure contexts always throw — a failed copy
|
|
199
|
+
* is not an error worth a card, and the un-flipped button IS the signal.
|
|
200
|
+
*/
|
|
201
|
+
declare function ViewerCopyButton({ text, className, }: {
|
|
202
|
+
text: string | null;
|
|
203
|
+
className?: string | undefined;
|
|
204
|
+
}): react.JSX.Element;
|
|
205
|
+
/** The strip above a text-shaped viewer: status on the left, copy on the right. */
|
|
206
|
+
declare function ViewerToolbar({ children, className, }: {
|
|
207
|
+
children?: React.ReactNode;
|
|
208
|
+
className?: string | undefined;
|
|
209
|
+
}): react.JSX.Element;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* src/viewers/transparency-grid.ts — the alpha-channel checkerboard.
|
|
213
|
+
*
|
|
214
|
+
* Ships as inline style rather than a utility class: the matrx-frontend
|
|
215
|
+
* originals used two different mechanisms for the same thing (an inline
|
|
216
|
+
* gradient stack in ImagePreview, a `bg-checkerboard` utility in SvgPreview),
|
|
217
|
+
* and a utility class is exactly the kind of host-owned dependency C26 bans a
|
|
218
|
+
* package component from assuming. Sized in CSS pixels so the pattern reads
|
|
219
|
+
* the same at any zoom.
|
|
220
|
+
*/
|
|
221
|
+
declare const TRANSPARENCY_GRID_STYLE: {
|
|
222
|
+
backgroundImage: string;
|
|
223
|
+
backgroundSize: string;
|
|
224
|
+
backgroundPosition: string;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* src/viewers/use-viewer-bytes.ts — the ONE byte/text/URL machinery every
|
|
229
|
+
* viewer body shares.
|
|
230
|
+
*
|
|
231
|
+
* Four separate matrx-frontend previewers (Text, Code, the SVG source view,
|
|
232
|
+
* the HTML source view) each carried their own copy of "read the blob, cap it
|
|
233
|
+
* at N bytes, mark it truncated, surface the REAL backend error verbatim,
|
|
234
|
+
* cancel on unmount". Four copies is four places to drift; C22 says that
|
|
235
|
+
* machinery lives in the package exactly once.
|
|
236
|
+
*/
|
|
237
|
+
|
|
238
|
+
interface ViewerBytesState {
|
|
239
|
+
blob: Blob | null;
|
|
240
|
+
loading: boolean;
|
|
241
|
+
/** The real underlying message (HTTP 401/403/404/network) — never generic. */
|
|
242
|
+
error: string | null;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Bytes for a viewer source. A `blob` source is already resolved; a `ref`
|
|
246
|
+
* source goes through `MediaClient.getBlob` (which is also what emits the
|
|
247
|
+
* terminal-failure diagnostics — see `useMediaBlob`).
|
|
248
|
+
*
|
|
249
|
+
* `useMediaBlob` is called UNCONDITIONALLY (with `null` for blob sources) so
|
|
250
|
+
* the hook order never changes with the source shape.
|
|
251
|
+
*/
|
|
252
|
+
declare function useViewerBytes(source: MediaViewerSource): ViewerBytesState;
|
|
253
|
+
interface ViewerTextState {
|
|
254
|
+
text: string | null;
|
|
255
|
+
loading: boolean;
|
|
256
|
+
error: string | null;
|
|
257
|
+
/** True when the file exceeded `maxBytes` and only the head was read. */
|
|
258
|
+
truncated: boolean;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Bytes → text, capped. The cap is not a nicety: a 500MB log file decoded in
|
|
262
|
+
* full takes the tab down, which is how the original previewers were written
|
|
263
|
+
* and why the cap must travel with the package rather than be re-derived.
|
|
264
|
+
*/
|
|
265
|
+
declare function useViewerText(source: MediaViewerSource, maxBytes?: number): ViewerTextState;
|
|
266
|
+
interface ViewerDisplayUrlState {
|
|
267
|
+
/**
|
|
268
|
+
* The URL a media element may bind. Durable by construction: it is either
|
|
269
|
+
* a `MediaClient.resolve()` result or the host's own object URL.
|
|
270
|
+
*/
|
|
271
|
+
url: string | null;
|
|
272
|
+
/**
|
|
273
|
+
* Whether the client's ONE retry policy can rescue a load failure. False
|
|
274
|
+
* for object URLs and for resolutions the client marked unrecoverable
|
|
275
|
+
* (foreign links) — a session refresh cannot resurrect someone else's URL.
|
|
276
|
+
*/
|
|
277
|
+
recoverable: boolean;
|
|
278
|
+
/** `resolve()` refused the ref — render the honest unavailable state. */
|
|
279
|
+
unavailable: boolean;
|
|
280
|
+
loading: boolean;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* The display URL for a viewer source. Law 2: even for a `ref` carrying a
|
|
284
|
+
* caller-supplied URL, resolution goes through the client — the package never
|
|
285
|
+
* trusts a string it was handed.
|
|
286
|
+
*/
|
|
287
|
+
declare function useViewerDisplayUrl(source: MediaViewerSource): ViewerDisplayUrlState;
|
|
288
|
+
|
|
289
|
+
export { CodeViewer, GenericViewer, HtmlViewer, type MediaCodeHighlighter, type MediaCodeHighlighterProps, MediaFileViewer, type MediaFileViewerProps, type MediaViewerComponent, type MediaViewerGenericOptions, type MediaViewerKind, type MediaViewerMarkupControls, type MediaViewerMarkupMode, type MediaViewerProps, type MediaViewerSource, MediaViewerUnavailable, type MediaViewerViewport, SvgViewer, TRANSPARENCY_GRID_STYLE, TextViewer, type ViewerBytesState, ViewerCopyButton, type ViewerDisplayUrlState, ViewerError, ViewerSkeleton, type ViewerTextState, ViewerToolbar, detectCodeLanguage, getMediaCodeHighlighter, getMediaViewer, getPreviewKind, registerMediaCodeHighlighter, registerMediaViewer, registeredMediaViewerKinds, useViewerBytes, useViewerDisplayUrl, useViewerText };
|