@k2b/ui 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/browser/chat/ChatComposer.js +261 -274
  2. package/dist/browser/chat/ChatComposer.js.map +1 -1
  3. package/dist/browser/chat/ChatPrimitives.js +9 -6
  4. package/dist/browser/chat/ChatPrimitives.js.map +1 -1
  5. package/dist/browser/content/FileView.js +62 -36
  6. package/dist/browser/content/FileView.js.map +1 -1
  7. package/dist/browser/content/MarkdownView.js +9 -3
  8. package/dist/browser/content/MarkdownView.js.map +1 -1
  9. package/dist/browser/content/VideoPlayer.js +279 -0
  10. package/dist/browser/content/VideoPlayer.js.map +1 -0
  11. package/dist/browser/content/file-view-preview.js +2 -2
  12. package/dist/browser/content/file-view-preview.js.map +1 -1
  13. package/dist/browser/content/index.js +2 -0
  14. package/dist/browser/content/index.js.map +1 -1
  15. package/dist/browser/content/markdown-links.js +178 -0
  16. package/dist/browser/content/markdown-links.js.map +1 -0
  17. package/dist/browser/inputs/FileDropTarget.js +129 -0
  18. package/dist/browser/inputs/FileDropTarget.js.map +1 -0
  19. package/dist/browser/inputs/FileInputs.js +37 -11
  20. package/dist/browser/inputs/FileInputs.js.map +1 -1
  21. package/dist/browser/inputs/file-drop.js +338 -0
  22. package/dist/browser/inputs/file-drop.js.map +1 -0
  23. package/dist/browser/inputs/index.js +1 -0
  24. package/dist/browser/inputs/index.js.map +1 -1
  25. package/dist/browser/inputs/markdown/MarkdownEditor.js +72 -78
  26. package/dist/browser/inputs/markdown/MarkdownEditor.js.map +1 -1
  27. package/dist/browser/intl/messages.js +97 -4
  28. package/dist/browser/intl/messages.js.map +1 -1
  29. package/dist/browser/layout/AppWorkspace.js +210 -44
  30. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  31. package/dist/browser/layout/Navigation.js +278 -170
  32. package/dist/browser/layout/Navigation.js.map +1 -1
  33. package/dist/browser/layout/index.js.map +1 -1
  34. package/dist/browser/layout/navigation-model.js +3 -1
  35. package/dist/browser/layout/navigation-model.js.map +1 -1
  36. package/dist/browser/surfaces/NoticeCard.js +47 -44
  37. package/dist/browser/surfaces/NoticeCard.js.map +1 -1
  38. package/dist/ssr/chat/ChatComposer.js +59 -48
  39. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  40. package/dist/ssr/chat/ChatPrimitives.js +9 -6
  41. package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
  42. package/dist/ssr/content/FileView.js +30 -6
  43. package/dist/ssr/content/FileView.js.map +1 -1
  44. package/dist/ssr/content/MarkdownView.js +9 -3
  45. package/dist/ssr/content/MarkdownView.js.map +1 -1
  46. package/dist/ssr/content/VideoPlayer.js +208 -0
  47. package/dist/ssr/content/VideoPlayer.js.map +1 -0
  48. package/dist/ssr/content/file-view-preview.js +2 -2
  49. package/dist/ssr/content/file-view-preview.js.map +1 -1
  50. package/dist/ssr/content/index.js +2 -0
  51. package/dist/ssr/content/index.js.map +1 -1
  52. package/dist/ssr/content/markdown-links.js +178 -0
  53. package/dist/ssr/content/markdown-links.js.map +1 -0
  54. package/dist/ssr/inputs/FileDropTarget.js +103 -0
  55. package/dist/ssr/inputs/FileDropTarget.js.map +1 -0
  56. package/dist/ssr/inputs/FileInputs.js +32 -10
  57. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  58. package/dist/ssr/inputs/file-drop.js +338 -0
  59. package/dist/ssr/inputs/file-drop.js.map +1 -0
  60. package/dist/ssr/inputs/index.js +1 -0
  61. package/dist/ssr/inputs/index.js.map +1 -1
  62. package/dist/ssr/inputs/markdown/MarkdownEditor.js +14 -2
  63. package/dist/ssr/inputs/markdown/MarkdownEditor.js.map +1 -1
  64. package/dist/ssr/intl/messages.js +97 -4
  65. package/dist/ssr/intl/messages.js.map +1 -1
  66. package/dist/ssr/layout/AppWorkspace.js +173 -27
  67. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  68. package/dist/ssr/layout/Navigation.js +175 -102
  69. package/dist/ssr/layout/Navigation.js.map +1 -1
  70. package/dist/ssr/layout/index.js.map +1 -1
  71. package/dist/ssr/layout/navigation-model.js +3 -1
  72. package/dist/ssr/layout/navigation-model.js.map +1 -1
  73. package/dist/ssr/surfaces/NoticeCard.js +17 -12
  74. package/dist/ssr/surfaces/NoticeCard.js.map +1 -1
  75. package/dist/styles.css +1 -1
  76. package/dist/types/chat/ChatPrimitives.d.ts +1 -1
  77. package/dist/types/content/VideoPlayer.d.ts +41 -0
  78. package/dist/types/content/index.d.ts +2 -0
  79. package/dist/types/content/markdown-links.d.ts +66 -0
  80. package/dist/types/inputs/FileDropTarget.d.ts +23 -0
  81. package/dist/types/inputs/FileInputs.d.ts +2 -0
  82. package/dist/types/inputs/file-drop.d.ts +80 -0
  83. package/dist/types/inputs/index.d.ts +2 -0
  84. package/dist/types/intl/messages.d.ts +47 -0
  85. package/dist/types/layout/AppWorkspace.d.ts +21 -0
  86. package/dist/types/layout/index.d.ts +2 -2
  87. package/dist/types/layout/navigation-model.d.ts +29 -0
  88. package/package.json +1 -1
@@ -0,0 +1,279 @@
1
+ import { template as _$template } from "solid-js/web";
2
+ import { delegateEvents as _$delegateEvents } from "solid-js/web";
3
+ import { setStyleProperty as _$setStyleProperty } from "solid-js/web";
4
+ import { className as _$className } from "solid-js/web";
5
+ import { setAttribute as _$setAttribute } from "solid-js/web";
6
+ import { effect as _$effect } from "solid-js/web";
7
+ import { getNextElement as _$getNextElement } from "solid-js/web";
8
+ import { runHydrationEvents as _$runHydrationEvents } from "solid-js/web";
9
+ import { insert as _$insert } from "solid-js/web";
10
+ import { createComponent as _$createComponent } from "solid-js/web";
11
+ import { use as _$use } from "solid-js/web";
12
+ var _tmpl$ = /*#__PURE__*/_$template(`<video class=k2b-video-player__video controls playsinline preload=metadata tabindex=0>`),
13
+ _tmpl$2 = /*#__PURE__*/_$template(`<div>`),
14
+ _tmpl$3 = /*#__PURE__*/_$template(`<div class=k2b-video-player__fallback>`);
15
+ /**
16
+ * VideoPlayer — one video with the browser's own controls inside a frame whose
17
+ * size never depends on the video. The frame fills the height its host gives
18
+ * it, or keeps a ratio where the host leaves the height open. The picture fits
19
+ * inside uncropped, so a portrait reel and a wide recording both show whole and
20
+ * nothing moves while the video loads.
21
+ */
22
+
23
+ import { createEffect, createSignal, Match, Switch } from "solid-js";
24
+ import { useUiMessages } from "../intl/messages";
25
+ import Placeholder from "../surfaces/Placeholder";
26
+ /** How far the arrow keys move playback, in seconds. */
27
+ const SEEK_STEP = 5;
28
+
29
+ /**
30
+ * How long a renewed address has to hold at the point where its predecessor failed before a failure there counts as
31
+ * new. A video that cannot play at a point fails again within moments, also where the engine reads or decodes ahead of
32
+ * playback; an expired address fails only after its lifetime, which for a signed URL is typically a minute or more.
33
+ */
34
+ const RENEWED_GRACE_MS = 10_000;
35
+
36
+ /** Safari on iOS shows no frame before playback without a poster; a media fragment makes it load and show the first one. */
37
+ const withFirstFrame = src => src.includes("#") ? src : `${src}#t=0.001`;
38
+
39
+ /** Where focus goes when the fallback replaces the focused video. */
40
+ const FALLBACK_FOCUS = ".k2b-video-player__fallback :is(a[href], button:not(:disabled))";
41
+ export function VideoPlayer(props) {
42
+ const messages = useUiMessages();
43
+ const [source, setSource] = createSignal(props.src);
44
+ const [failed, setFailed] = createSignal(false);
45
+ let frame;
46
+ let video;
47
+ /**
48
+ * The last renewal: where to continue once the renewed address has loaded, and since when it shows that point. Until
49
+ * it has held there for a while, a failure means the video itself does not play, not that the address expired.
50
+ */
51
+ let renewal = null;
52
+ /** Playing as the viewer meant it; a failing source may stop without a pause event. */
53
+ let playing = false;
54
+
55
+ // A new address from the host is another video: start over without resuming. It counts as new against the address
56
+ // the player started with, because the host may change it before this effect first runs, for example on mount.
57
+ let hostSrc = props.src;
58
+ createEffect(() => {
59
+ const src = props.src;
60
+ if (src === hostSrc) return;
61
+ hostSrc = src;
62
+ renewal = null;
63
+ playing = false;
64
+ setFailed(false);
65
+ setSource(src);
66
+ });
67
+ const videoSource = () => {
68
+ const src = source();
69
+ return src && !props.poster ? withFirstFrame(src) : src ?? undefined;
70
+ };
71
+ const giveUp = () => {
72
+ // The fallback replaces the video, so focus on the video would drop to the page; it moves into the fallback.
73
+ const focused = video !== undefined && document.activeElement === video;
74
+ setFailed(true);
75
+ if (focused) (frame?.querySelector(FALLBACK_FOCUS) ?? frame)?.focus();
76
+ props.onFallback?.();
77
+ };
78
+ const fail = async () => {
79
+ const element = video;
80
+ if (!element || failed()) return;
81
+ const recent = renewal && (renewal.readyAt === null || performance.now() - renewal.readyAt < RENEWED_GRACE_MS);
82
+ if (!props.renew || recent) return giveUp();
83
+ const requested = props.src;
84
+ renewal = {
85
+ time: element.currentTime,
86
+ rate: element.playbackRate,
87
+ playing,
88
+ loaded: false,
89
+ readyAt: null
90
+ };
91
+ try {
92
+ const renewed = await props.renew();
93
+ if (requested !== props.src) return;
94
+ if (renewed === source()) element.load();else setSource(renewed);
95
+ } catch {
96
+ if (requested === props.src) giveUp();
97
+ }
98
+ };
99
+ const loaded = () => {
100
+ const element = video;
101
+ if (!element) return;
102
+ // Sound in an empty frame: the browser cannot decode the picture, for example HEVC outside Safari. A fresh address
103
+ // does not change that.
104
+ if (element.videoWidth === 0 && element.videoHeight === 0) return giveUp();
105
+ if (!renewal || renewal.loaded) return;
106
+ renewal.loaded = true;
107
+ if (renewal.time > 0) element.currentTime = renewal.time;
108
+ element.playbackRate = renewal.rate;
109
+ if (renewal.playing) void element.play().catch(() => undefined);
110
+ };
111
+
112
+ /** The renewed address holds once the video shows the point where it continues. */
113
+ const ready = event => {
114
+ const element = event.currentTarget;
115
+ if (renewal?.loaded && renewal.readyAt === null && !element.seeking && element.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) renewal.readyAt = performance.now();
116
+ };
117
+ const fullscreen = element => {
118
+ if (document.fullscreenElement) return void document.exitFullscreen().catch(() => undefined);
119
+ if (element.requestFullscreen) return void element.requestFullscreen().catch(() => undefined);
120
+ // Safari on iOS has no element fullscreen for video, only its own player.
121
+ if ("webkitEnterFullscreen" in element && typeof element.webkitEnterFullscreen === "function") element.webkitEnterFullscreen();
122
+ };
123
+ const toggle = element => {
124
+ if (element.paused) void element.play().catch(() => undefined);else element.pause();
125
+ };
126
+
127
+ /** Whether the video was paused when Space went down; null while no Space press is open. */
128
+ let spacePaused = null;
129
+
130
+ // Native controls differ per engine: WebKit has no keyboard playback at all, so the player gives every engine the
131
+ // same keys. The keys reach the player only while the video itself has focus, not one of the browser's buttons.
132
+ const onKeyDown = event => {
133
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
134
+ const element = event.currentTarget;
135
+ switch (event.key) {
136
+ case " ":
137
+ // Chromium toggles on release whatever the page does, so Space waits for it and toggles only if it did not.
138
+ if (!event.repeat) spacePaused = element.paused;
139
+ break;
140
+ case "k":
141
+ toggle(element);
142
+ break;
143
+ case "ArrowLeft":
144
+ element.currentTime = Math.max(0, element.currentTime - SEEK_STEP);
145
+ break;
146
+ case "ArrowRight":
147
+ element.currentTime = Number.isFinite(element.duration) ? Math.min(element.duration, element.currentTime + SEEK_STEP) : element.currentTime + SEEK_STEP;
148
+ break;
149
+ case "m":
150
+ element.muted = !element.muted;
151
+ break;
152
+ case "f":
153
+ fullscreen(element);
154
+ break;
155
+ default:
156
+ return;
157
+ }
158
+ event.preventDefault();
159
+ };
160
+ const onKeyUp = event => {
161
+ if (event.key !== " " || spacePaused === null) return;
162
+ const element = event.currentTarget;
163
+ const paused = spacePaused;
164
+ spacePaused = null;
165
+ setTimeout(() => {
166
+ if (element.paused === paused) toggle(element);
167
+ });
168
+ };
169
+ return (() => {
170
+ var _el$ = _$getNextElement(_tmpl$2);
171
+ var _ref$ = frame;
172
+ typeof _ref$ === "function" ? _$use(_ref$, _el$) : frame = _el$;
173
+ _$insert(_el$, _$createComponent(Switch, {
174
+ get fallback() {
175
+ return _$createComponent(Placeholder, {
176
+ "class": "k2b-video-player__fallback",
177
+ state: "loading"
178
+ });
179
+ },
180
+ get children() {
181
+ return [_$createComponent(Match, {
182
+ get when() {
183
+ return props.error;
184
+ },
185
+ children: error => (() => {
186
+ var _el$3 = _$getNextElement(_tmpl$3);
187
+ _$insert(_el$3, error);
188
+ return _el$3;
189
+ })()
190
+ }), _$createComponent(Match, {
191
+ get when() {
192
+ return failed();
193
+ },
194
+ get children() {
195
+ return _$createComponent(Placeholder, {
196
+ "class": "k2b-video-player__fallback",
197
+ state: "error",
198
+ icon: "ti ti-video-off",
199
+ get title() {
200
+ return messages().videoCannotPlay;
201
+ },
202
+ get description() {
203
+ return messages().videoCannotPlayDescription;
204
+ },
205
+ get action() {
206
+ return props.fallbackAction;
207
+ }
208
+ });
209
+ }
210
+ }), _$createComponent(Match, {
211
+ get when() {
212
+ return source();
213
+ },
214
+ get children() {
215
+ var _el$2 = _$getNextElement(_tmpl$);
216
+ _el$2.addEventListener("error", () => void fail());
217
+ _el$2.addEventListener("canplay", ready);
218
+ _el$2.addEventListener("seeked", ready);
219
+ _el$2.addEventListener("loadeddata", ready);
220
+ _el$2.addEventListener("loadedmetadata", loaded);
221
+ _el$2.addEventListener("ended", () => {
222
+ playing = false;
223
+ });
224
+ _el$2.addEventListener("pause", event => {
225
+ // A video that stops because its source failed has not been paused by the viewer.
226
+ if (!event.currentTarget.error) playing = false;
227
+ });
228
+ _el$2.addEventListener("play", () => {
229
+ playing = true;
230
+ });
231
+ _el$2.$$keyup = onKeyUp;
232
+ _el$2.$$keydown = onKeyDown;
233
+ var _ref$2 = video;
234
+ typeof _ref$2 === "function" ? _$use(_ref$2, _el$2) : video = _el$2;
235
+ _$insert(_el$2, () => messages().videoUnsupported);
236
+ _$effect(_p$ => {
237
+ var _v$ = videoSource(),
238
+ _v$2 = props.poster,
239
+ _v$3 = props.crossOrigin,
240
+ _v$4 = props.label;
241
+ _v$ !== _p$.e && _$setAttribute(_el$2, "src", _p$.e = _v$);
242
+ _v$2 !== _p$.t && _$setAttribute(_el$2, "poster", _p$.t = _v$2);
243
+ _v$3 !== _p$.a && _$setAttribute(_el$2, "crossorigin", _p$.a = _v$3);
244
+ _v$4 !== _p$.o && _$setAttribute(_el$2, "aria-label", _p$.o = _v$4);
245
+ return _p$;
246
+ }, {
247
+ e: undefined,
248
+ t: undefined,
249
+ a: undefined,
250
+ o: undefined
251
+ });
252
+ _$runHydrationEvents();
253
+ return _el$2;
254
+ }
255
+ })];
256
+ }
257
+ }));
258
+ _$effect(_p$ => {
259
+ var _v$5 = `k2b-video-player ${props.class ?? ""}`,
260
+ _v$6 = failed() ? "failed" : undefined,
261
+ _v$7 = failed() ? -1 : undefined,
262
+ _v$8 = String(props.ratio && props.ratio > 0 ? props.ratio : 16 / 9);
263
+ _v$5 !== _p$.e && _$className(_el$, _p$.e = _v$5);
264
+ _v$6 !== _p$.t && _$setAttribute(_el$, "data-state", _p$.t = _v$6);
265
+ _v$7 !== _p$.a && _$setAttribute(_el$, "tabindex", _p$.a = _v$7);
266
+ _v$8 !== _p$.o && _$setStyleProperty(_el$, "aspect-ratio", _p$.o = _v$8);
267
+ return _p$;
268
+ }, {
269
+ e: undefined,
270
+ t: undefined,
271
+ a: undefined,
272
+ o: undefined
273
+ });
274
+ return _el$;
275
+ })();
276
+ }
277
+ export default VideoPlayer;
278
+ _$delegateEvents(["keydown", "keyup"]);
279
+ //# sourceMappingURL=VideoPlayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["content/VideoPlayer.tsx"],"sourcesContent":["/**\n * VideoPlayer — one video with the browser's own controls inside a frame whose\n * size never depends on the video. The frame fills the height its host gives\n * it, or keeps a ratio where the host leaves the height open. The picture fits\n * inside uncropped, so a portrait reel and a wide recording both show whole and\n * nothing moves while the video loads.\n */\n\nimport { createEffect, createSignal, type JSX, Match, Switch } from \"solid-js\";\nimport { useUiMessages } from \"../intl/messages\";\nimport Placeholder from \"../surfaces/Placeholder\";\n\nexport type VideoPlayerProps = {\n /**\n * Inline address of the video. Seeking needs a server that answers `Range` requests; Safari plays nothing without.\n * Null while the host still fetches it, for example a signed URL: the frame already shows at its final size.\n */\n src: string | null;\n /** Names the video for assistive technology, for example its file name. */\n label: string;\n /** Picture shown before playback starts. Without one, the player shows the first frame. */\n poster?: string;\n /** Width divided by height of the frame where the host leaves its height open. Defaults to 16 / 9. */\n ratio?: number;\n /** CORS mode for a video on another origin. Omit to keep the browser default. */\n crossOrigin?: \"anonymous\" | \"use-credentials\";\n /**\n * Resolves a fresh address of the same video, for example a renewed signed URL. When playback fails, the player asks\n * for one and continues at the same time, playing if it was. A renewed address that fails before it shows that point,\n * or within ten seconds after, shows the fallback, so a renewed address must stay valid for longer.\n */\n renew?: () => Promise<string>;\n /** Offered with the fallback when the browser cannot play the video, typically a download. */\n fallbackAction?: JSX.Element;\n /**\n * Shown in the frame instead of the video while set, for example the host's own error with a retry when it could not\n * fetch the address. The frame keeps its size.\n */\n error?: JSX.Element;\n /** Called when the player gives up on the video and shows its fallback. */\n onFallback?: () => void;\n class?: string;\n};\n\n/** How far the arrow keys move playback, in seconds. */\nconst SEEK_STEP = 5;\n\n/**\n * How long a renewed address has to hold at the point where its predecessor failed before a failure there counts as\n * new. A video that cannot play at a point fails again within moments, also where the engine reads or decodes ahead of\n * playback; an expired address fails only after its lifetime, which for a signed URL is typically a minute or more.\n */\nconst RENEWED_GRACE_MS = 10_000;\n\n/** Safari on iOS shows no frame before playback without a poster; a media fragment makes it load and show the first one. */\nconst withFirstFrame = (src: string) => (src.includes(\"#\") ? src : `${src}#t=0.001`);\n\n/** Where focus goes when the fallback replaces the focused video. */\nconst FALLBACK_FOCUS = \".k2b-video-player__fallback :is(a[href], button:not(:disabled))\";\n\nexport function VideoPlayer(props: VideoPlayerProps) {\n const messages = useUiMessages();\n const [source, setSource] = createSignal<string | null>(props.src);\n const [failed, setFailed] = createSignal(false);\n let frame: HTMLDivElement | undefined;\n let video: HTMLVideoElement | undefined;\n /**\n * The last renewal: where to continue once the renewed address has loaded, and since when it shows that point. Until\n * it has held there for a while, a failure means the video itself does not play, not that the address expired.\n */\n let renewal: { time: number; rate: number; playing: boolean; loaded: boolean; readyAt: number | null } | null = null;\n /** Playing as the viewer meant it; a failing source may stop without a pause event. */\n let playing = false;\n\n // A new address from the host is another video: start over without resuming. It counts as new against the address\n // the player started with, because the host may change it before this effect first runs, for example on mount.\n let hostSrc = props.src;\n createEffect(() => {\n const src = props.src;\n if (src === hostSrc) return;\n hostSrc = src;\n renewal = null;\n playing = false;\n setFailed(false);\n setSource(src);\n });\n\n const videoSource = () => {\n const src = source();\n return src && !props.poster ? withFirstFrame(src) : (src ?? undefined);\n };\n\n const giveUp = () => {\n // The fallback replaces the video, so focus on the video would drop to the page; it moves into the fallback.\n const focused = video !== undefined && document.activeElement === video;\n setFailed(true);\n if (focused) (frame?.querySelector<HTMLElement>(FALLBACK_FOCUS) ?? frame)?.focus();\n props.onFallback?.();\n };\n\n const fail = async () => {\n const element = video;\n if (!element || failed()) return;\n const recent = renewal && (renewal.readyAt === null || performance.now() - renewal.readyAt < RENEWED_GRACE_MS);\n if (!props.renew || recent) return giveUp();\n const requested = props.src;\n renewal = { time: element.currentTime, rate: element.playbackRate, playing, loaded: false, readyAt: null };\n try {\n const renewed = await props.renew();\n if (requested !== props.src) return;\n if (renewed === source()) element.load();\n else setSource(renewed);\n } catch {\n if (requested === props.src) giveUp();\n }\n };\n\n const loaded = () => {\n const element = video;\n if (!element) return;\n // Sound in an empty frame: the browser cannot decode the picture, for example HEVC outside Safari. A fresh address\n // does not change that.\n if (element.videoWidth === 0 && element.videoHeight === 0) return giveUp();\n if (!renewal || renewal.loaded) return;\n renewal.loaded = true;\n if (renewal.time > 0) element.currentTime = renewal.time;\n element.playbackRate = renewal.rate;\n if (renewal.playing) void element.play().catch(() => undefined);\n };\n\n /** The renewed address holds once the video shows the point where it continues. */\n const ready = (event: Event & { currentTarget: HTMLVideoElement }) => {\n const element = event.currentTarget;\n if (renewal?.loaded && renewal.readyAt === null && !element.seeking && element.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA)\n renewal.readyAt = performance.now();\n };\n\n const fullscreen = (element: HTMLVideoElement) => {\n if (document.fullscreenElement) return void document.exitFullscreen().catch(() => undefined);\n if (element.requestFullscreen) return void element.requestFullscreen().catch(() => undefined);\n // Safari on iOS has no element fullscreen for video, only its own player.\n if (\"webkitEnterFullscreen\" in element && typeof element.webkitEnterFullscreen === \"function\") element.webkitEnterFullscreen();\n };\n\n const toggle = (element: HTMLVideoElement) => {\n if (element.paused) void element.play().catch(() => undefined);\n else element.pause();\n };\n\n /** Whether the video was paused when Space went down; null while no Space press is open. */\n let spacePaused: boolean | null = null;\n\n // Native controls differ per engine: WebKit has no keyboard playback at all, so the player gives every engine the\n // same keys. The keys reach the player only while the video itself has focus, not one of the browser's buttons.\n const onKeyDown = (event: KeyboardEvent & { currentTarget: HTMLVideoElement }) => {\n if (event.altKey || event.ctrlKey || event.metaKey) return;\n const element = event.currentTarget;\n switch (event.key) {\n case \" \":\n // Chromium toggles on release whatever the page does, so Space waits for it and toggles only if it did not.\n if (!event.repeat) spacePaused = element.paused;\n break;\n case \"k\":\n toggle(element);\n break;\n case \"ArrowLeft\":\n element.currentTime = Math.max(0, element.currentTime - SEEK_STEP);\n break;\n case \"ArrowRight\":\n element.currentTime = Number.isFinite(element.duration)\n ? Math.min(element.duration, element.currentTime + SEEK_STEP)\n : element.currentTime + SEEK_STEP;\n break;\n case \"m\":\n element.muted = !element.muted;\n break;\n case \"f\":\n fullscreen(element);\n break;\n default:\n return;\n }\n event.preventDefault();\n };\n const onKeyUp = (event: KeyboardEvent & { currentTarget: HTMLVideoElement }) => {\n if (event.key !== \" \" || spacePaused === null) return;\n const element = event.currentTarget;\n const paused = spacePaused;\n spacePaused = null;\n setTimeout(() => {\n if (element.paused === paused) toggle(element);\n });\n };\n\n return (\n <div\n ref={frame}\n class={`k2b-video-player ${props.class ?? \"\"}`}\n data-state={failed() ? \"failed\" : undefined}\n tabIndex={failed() ? -1 : undefined}\n style={{ \"aspect-ratio\": String(props.ratio && props.ratio > 0 ? props.ratio : 16 / 9) }}\n >\n <Switch fallback={<Placeholder class=\"k2b-video-player__fallback\" state=\"loading\" />}>\n <Match when={props.error}>{(error) => <div class=\"k2b-video-player__fallback\">{error()}</div>}</Match>\n <Match when={failed()}>\n <Placeholder\n class=\"k2b-video-player__fallback\"\n state=\"error\"\n icon=\"ti ti-video-off\"\n title={messages().videoCannotPlay}\n description={messages().videoCannotPlayDescription}\n action={props.fallbackAction}\n />\n </Match>\n <Match when={source()}>\n <video\n ref={video}\n class=\"k2b-video-player__video\"\n src={videoSource()}\n poster={props.poster}\n crossOrigin={props.crossOrigin}\n controls\n playsinline\n preload=\"metadata\"\n tabIndex={0}\n aria-label={props.label}\n onKeyDown={onKeyDown}\n onKeyUp={onKeyUp}\n onPlay={() => {\n playing = true;\n }}\n onPause={(event) => {\n // A video that stops because its source failed has not been paused by the viewer.\n if (!event.currentTarget.error) playing = false;\n }}\n onEnded={() => {\n playing = false;\n }}\n onLoadedMetadata={loaded}\n onLoadedData={ready}\n onSeeked={ready}\n onCanPlay={ready}\n onError={() => void fail()}\n >\n {messages().videoUnsupported}\n </video>\n </Match>\n </Switch>\n </div>\n );\n}\n\nexport default VideoPlayer;\n"],"mappings":"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","ignoreList":[]}
@@ -13,11 +13,11 @@ const maxPreviewBytes = {
13
13
  // SVG remains inert because FileView renders every image through <img>; never inject its markup into the DOM.
14
14
  const imageMediaTypes = new Set(["image/avif", "image/bmp", "image/gif", "image/jpeg", "image/png", "image/svg+xml", "image/webp", "image/x-icon"]);
15
15
  const audioMediaTypes = new Set(["audio/aac", "audio/flac", "audio/mp4", "audio/mpeg", "audio/ogg", "audio/wav", "audio/webm", "audio/x-wav"]);
16
- const videoMediaTypes = new Set(["video/mp4", "video/ogg", "video/webm"]);
16
+ const videoMediaTypes = new Set(["video/mp4", "video/ogg", "video/quicktime", "video/webm", "video/x-m4v"]);
17
17
  const markdownExtensions = new Set(["md", "markdown"]);
18
18
  const imageExtensions = new Set(["avif", "bmp", "gif", "ico", "jpeg", "jpg", "png", "svg", "webp"]);
19
19
  const audioExtensions = new Set(["aac", "flac", "m4a", "mp3", "oga", "ogg", "wav", "weba"]);
20
- const videoExtensions = new Set(["m4v", "mp4", "ogv", "webm"]);
20
+ const videoExtensions = new Set(["m4v", "mov", "mp4", "ogv", "webm"]);
21
21
  const textExtensions = new Set(["c", "conf", "cpp", "css", "env", "go", "h", "html", "ini", "java", "js", "jsx", "log", "mjs", "php", "py", "rb", "rs", "sh", "sql", "toml", "ts", "tsx", "txt", "xml", "yaml", "yml"]);
22
22
  export const fileViewExtension = path => {
23
23
  const cleanPath = path.split(/[?#]/, 1)[0] ?? path;
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["content/file-view-preview.ts"],"sourcesContent":["export type FileViewFile = {\n path: string;\n mediaType?: string;\n size?: number;\n};\n\nexport type FileViewPreviewKind = \"markdown\" | \"image\" | \"pdf\" | \"json\" | \"delimited-text\" | \"audio\" | \"video\" | \"text\";\n\nconst MEBIBYTE = 1024 * 1024;\n\nconst maxPreviewBytes: Record<FileViewPreviewKind, number> = {\n markdown: 2 * MEBIBYTE,\n image: 25 * MEBIBYTE,\n pdf: 50 * MEBIBYTE,\n json: 2 * MEBIBYTE,\n \"delimited-text\": 2 * MEBIBYTE,\n audio: 50 * MEBIBYTE,\n video: 50 * MEBIBYTE,\n text: 2 * MEBIBYTE,\n};\n\n// SVG remains inert because FileView renders every image through <img>; never inject its markup into the DOM.\nconst imageMediaTypes = new Set([\n \"image/avif\",\n \"image/bmp\",\n \"image/gif\",\n \"image/jpeg\",\n \"image/png\",\n \"image/svg+xml\",\n \"image/webp\",\n \"image/x-icon\",\n]);\nconst audioMediaTypes = new Set([\n \"audio/aac\",\n \"audio/flac\",\n \"audio/mp4\",\n \"audio/mpeg\",\n \"audio/ogg\",\n \"audio/wav\",\n \"audio/webm\",\n \"audio/x-wav\",\n]);\nconst videoMediaTypes = new Set([\"video/mp4\", \"video/ogg\", \"video/webm\"]);\n\nconst markdownExtensions = new Set([\"md\", \"markdown\"]);\nconst imageExtensions = new Set([\"avif\", \"bmp\", \"gif\", \"ico\", \"jpeg\", \"jpg\", \"png\", \"svg\", \"webp\"]);\nconst audioExtensions = new Set([\"aac\", \"flac\", \"m4a\", \"mp3\", \"oga\", \"ogg\", \"wav\", \"weba\"]);\nconst videoExtensions = new Set([\"m4v\", \"mp4\", \"ogv\", \"webm\"]);\nconst textExtensions = new Set([\n \"c\",\n \"conf\",\n \"cpp\",\n \"css\",\n \"env\",\n \"go\",\n \"h\",\n \"html\",\n \"ini\",\n \"java\",\n \"js\",\n \"jsx\",\n \"log\",\n \"mjs\",\n \"php\",\n \"py\",\n \"rb\",\n \"rs\",\n \"sh\",\n \"sql\",\n \"toml\",\n \"ts\",\n \"tsx\",\n \"txt\",\n \"xml\",\n \"yaml\",\n \"yml\",\n]);\n\nexport const fileViewExtension = (path: string): string => {\n const cleanPath = path.split(/[?#]/, 1)[0] ?? path;\n const name = cleanPath.slice(cleanPath.lastIndexOf(\"/\") + 1);\n const dot = name.lastIndexOf(\".\");\n return dot > 0 ? name.slice(dot + 1).toLowerCase() : \"\";\n};\n\nconst normalizedMediaType = (mediaType?: string): string => mediaType?.split(\";\", 1)[0]?.trim().toLowerCase() ?? \"\";\n\nconst previewKindByType = (file: FileViewFile): FileViewPreviewKind | null => {\n const extension = fileViewExtension(file.path);\n const mediaType = normalizedMediaType(file.mediaType);\n\n if (mediaType === \"text/markdown\" || markdownExtensions.has(extension)) return \"markdown\";\n if (mediaType === \"application/json\" || mediaType.endsWith(\"+json\") || extension === \"json\") return \"json\";\n if (mediaType === \"text/csv\" || mediaType === \"text/tab-separated-values\" || extension === \"csv\" || extension === \"tsv\") {\n return \"delimited-text\";\n }\n if (mediaType === \"application/pdf\" || extension === \"pdf\") return \"pdf\";\n if (imageMediaTypes.has(mediaType) || imageExtensions.has(extension)) return \"image\";\n if (audioMediaTypes.has(mediaType) || audioExtensions.has(extension)) return \"audio\";\n if (videoMediaTypes.has(mediaType) || videoExtensions.has(extension)) return \"video\";\n if (\n mediaType.startsWith(\"text/\") ||\n mediaType === \"application/javascript\" ||\n mediaType === \"application/sql\" ||\n mediaType === \"application/xml\" ||\n textExtensions.has(extension)\n ) {\n return \"text\";\n }\n return null;\n};\n\n/**\n * Returns the built-in preview kind when FileView can render the file within\n * its bounded client-side limits. Custom renderers remain registry-driven.\n */\nexport const getFileViewPreviewKind = (file: FileViewFile): FileViewPreviewKind | null => {\n const kind = previewKindByType(file);\n if (!kind) return null;\n if (file.size !== undefined && (!Number.isFinite(file.size) || file.size < 0 || file.size > maxPreviewBytes[kind])) {\n return null;\n }\n return kind;\n};\n\n/** Use this to expose preview actions only for files FileView can render. */\nexport const canPreviewFile = (file: FileViewFile): boolean => getFileViewPreviewKind(file) !== null;\n\nexport type DelimitedTextPreview = {\n rows: string[][];\n truncated: boolean;\n totalRows: number;\n columnsTruncated: boolean;\n};\n\nexport const parseDelimitedText = (\n input: string,\n delimiter: string,\n limits: { rows?: number; columns?: number; offset?: number } = {},\n): DelimitedTextPreview => {\n const maxRows = Math.max(1, limits.rows ?? 201);\n const maxColumns = Math.max(1, limits.columns ?? 50);\n const rows: string[][] = [];\n let row: string[] = [];\n let field = \"\";\n let quoted = false;\n let truncated = false;\n let totalRows = 0;\n let columnsTruncated = false;\n\n const pushField = () => {\n if (row.length < maxColumns) row.push(field);\n else {\n truncated = true;\n columnsTruncated = true;\n }\n field = \"\";\n };\n const pushRow = () => {\n if (rows.length < maxRows && (totalRows === 0 || totalRows > (limits.offset ?? 0))) rows.push(row);\n else truncated = true;\n totalRows++;\n row = [];\n };\n\n for (let index = 0; index < input.length; index += 1) {\n const character = input[index]!;\n if (quoted) {\n if (character === '\"' && input[index + 1] === '\"') {\n field += '\"';\n index += 1;\n } else if (character === '\"') {\n quoted = false;\n } else {\n field += character;\n }\n continue;\n }\n\n if (character === '\"' && field.length === 0) quoted = true;\n else if (character === delimiter) pushField();\n else if (character === \"\\n\" || character === \"\\r\") {\n pushField();\n pushRow();\n if (character === \"\\r\" && input[index + 1] === \"\\n\") index += 1;\n } else {\n field += character;\n }\n }\n\n if (field.length > 0 || row.length > 0) {\n pushField();\n pushRow();\n }\n\n return { rows, truncated, totalRows, columnsTruncated };\n};\n"],"mappings":"AAQA,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI;AAE5B,MAAM,eAAoD,GAAG;EAC3D,QAAQ,EAAE,CAAC,GAAG,QAAQ;EACtB,KAAK,EAAE,EAAE,GAAG,QAAQ;EACpB,GAAG,EAAE,EAAE,GAAG,QAAQ;EAClB,IAAI,EAAE,CAAC,GAAG,QAAQ;EAClB,gBAAgB,EAAE,CAAC,GAAG,QAAQ;EAC9B,KAAK,EAAE,EAAE,GAAG,QAAQ;EACpB,KAAK,EAAE,EAAE,GAAG,QAAQ;EACpB,IAAI,EAAE,CAAC,GAAG;AACZ,CAAC;;AAED;AACA,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAC9B,YAAY,EACZ,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,EACX,eAAe,EACf,YAAY,EACZ,cAAc,CACf,CAAC;AACF,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAC9B,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,YAAY,EACZ,aAAa,CACd,CAAC;AACF,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AAEzE,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AACtD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AACnG,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAC3F,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAC9D,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAC7B,GAAG,EACH,MAAM,EACN,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,GAAG,EACH,MAAM,EACN,KAAK,EACL,MAAM,EACN,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,MAAM,EACN,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,MAAM,EACN,KAAK,CACN,CAAC;AAEF,OAAO,MAAM,iBAAiB,GAAI,IAAY,IAAa;EACzD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI;EAClD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;EAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;EACjC,OAAO,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,GAAG,EAAE;AACzD,CAAC;AAED,MAAM,mBAAmB,GAAI,SAAkB,IAAa,SAAS,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE;AAEnH,MAAM,iBAAiB,GAAI,IAAkB,IAAiC;EAC5E,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;EAC9C,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC;EAErD,IAAI,SAAS,KAAK,eAAe,IAAI,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,UAAU;EACzF,IAAI,SAAS,KAAK,kBAAkB,IAAI,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,SAAS,KAAK,MAAM,EAAE,OAAO,MAAM;EAC1G,IAAI,SAAS,KAAK,UAAU,IAAI,SAAS,KAAK,2BAA2B,IAAI,SAAS,KAAK,KAAK,IAAI,SAAS,KAAK,KAAK,EAAE;IACvH,OAAO,gBAAgB;EACzB;EACA,IAAI,SAAS,KAAK,iBAAiB,IAAI,SAAS,KAAK,KAAK,EAAE,OAAO,KAAK;EACxE,IAAI,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,OAAO;EACpF,IAAI,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,OAAO;EACpF,IAAI,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,OAAO;EACpF,IACE,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,IAC7B,SAAS,KAAK,wBAAwB,IACtC,SAAS,KAAK,iBAAiB,IAC/B,SAAS,KAAK,iBAAiB,IAC/B,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,EAC7B;IACA,OAAO,MAAM;EACf;EACA,OAAO,IAAI;AACb,CAAC;;AAED;AACA;AACA;AACA;AACA,OAAO,MAAM,sBAAsB,GAAI,IAAkB,IAAiC;EACxF,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC;EACpC,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI;EACtB,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE;IAClH,OAAO,IAAI;EACb;EACA,OAAO,IAAI;AACb,CAAC;;AAED;AACA,OAAO,MAAM,cAAc,GAAI,IAAkB,IAAc,sBAAsB,CAAC,IAAI,CAAC,KAAK,IAAI;AASpG,OAAO,MAAM,kBAAkB,GAAG,CAChC,KAAa,EACb,SAAiB,EACjB,MAA4D,GAAG,CAAC,CAAC,KACxC;EACzB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC;EAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;EACpD,MAAM,IAAgB,GAAG,EAAE;EAC3B,IAAI,GAAa,GAAG,EAAE;EACtB,IAAI,KAAK,GAAG,EAAE;EACd,IAAI,MAAM,GAAG,KAAK;EAClB,IAAI,SAAS,GAAG,KAAK;EACrB,IAAI,SAAS,GAAG,CAAC;EACjB,IAAI,gBAAgB,GAAG,KAAK;EAE5B,MAAM,SAAS,GAAG,MAAM;IACtB,IAAI,GAAG,CAAC,MAAM,GAAG,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KACxC;MACH,SAAS,GAAG,IAAI;MAChB,gBAAgB,GAAG,IAAI;IACzB;IACA,KAAK,GAAG,EAAE;EACZ,CAAC;EACD,MAAM,OAAO,GAAG,MAAM;IACpB,IAAI,IAAI,CAAC,MAAM,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC,IAAI,SAAS,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAC9F,SAAS,GAAG,IAAI;IACrB,SAAS,EAAE;IACX,GAAG,GAAG,EAAE;EACV,CAAC;EAED,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE;IACpD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAE;IAC/B,IAAI,MAAM,EAAE;MACV,IAAI,SAAS,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE;QACjD,KAAK,IAAI,GAAG;QACZ,KAAK,IAAI,CAAC;MACZ,CAAC,MAAM,IAAI,SAAS,KAAK,GAAG,EAAE;QAC5B,MAAM,GAAG,KAAK;MAChB,CAAC,MAAM;QACL,KAAK,IAAI,SAAS;MACpB;MACA;IACF;IAEA,IAAI,SAAS,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,KACtD,IAAI,SAAS,KAAK,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,KACzC,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,EAAE;MACjD,SAAS,CAAC,CAAC;MACX,OAAO,CAAC,CAAC;MACT,IAAI,SAAS,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,KAAK,IAAI,CAAC;IACjE,CAAC,MAAM;MACL,KAAK,IAAI,SAAS;IACpB;EACF;EAEA,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE;IACtC,SAAS,CAAC,CAAC;IACX,OAAO,CAAC,CAAC;EACX;EAEA,OAAO;IAAE,IAAI;IAAE,SAAS;IAAE,SAAS;IAAE;EAAiB,CAAC;AACzD,CAAC","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["content/file-view-preview.ts"],"sourcesContent":["export type FileViewFile = {\n path: string;\n mediaType?: string;\n size?: number;\n};\n\nexport type FileViewPreviewKind = \"markdown\" | \"image\" | \"pdf\" | \"json\" | \"delimited-text\" | \"audio\" | \"video\" | \"text\";\n\nconst MEBIBYTE = 1024 * 1024;\n\nconst maxPreviewBytes: Record<FileViewPreviewKind, number> = {\n markdown: 2 * MEBIBYTE,\n image: 25 * MEBIBYTE,\n pdf: 50 * MEBIBYTE,\n json: 2 * MEBIBYTE,\n \"delimited-text\": 2 * MEBIBYTE,\n audio: 50 * MEBIBYTE,\n video: 50 * MEBIBYTE,\n text: 2 * MEBIBYTE,\n};\n\n// SVG remains inert because FileView renders every image through <img>; never inject its markup into the DOM.\nconst imageMediaTypes = new Set([\n \"image/avif\",\n \"image/bmp\",\n \"image/gif\",\n \"image/jpeg\",\n \"image/png\",\n \"image/svg+xml\",\n \"image/webp\",\n \"image/x-icon\",\n]);\nconst audioMediaTypes = new Set([\n \"audio/aac\",\n \"audio/flac\",\n \"audio/mp4\",\n \"audio/mpeg\",\n \"audio/ogg\",\n \"audio/wav\",\n \"audio/webm\",\n \"audio/x-wav\",\n]);\nconst videoMediaTypes = new Set([\"video/mp4\", \"video/ogg\", \"video/quicktime\", \"video/webm\", \"video/x-m4v\"]);\n\nconst markdownExtensions = new Set([\"md\", \"markdown\"]);\nconst imageExtensions = new Set([\"avif\", \"bmp\", \"gif\", \"ico\", \"jpeg\", \"jpg\", \"png\", \"svg\", \"webp\"]);\nconst audioExtensions = new Set([\"aac\", \"flac\", \"m4a\", \"mp3\", \"oga\", \"ogg\", \"wav\", \"weba\"]);\nconst videoExtensions = new Set([\"m4v\", \"mov\", \"mp4\", \"ogv\", \"webm\"]);\nconst textExtensions = new Set([\n \"c\",\n \"conf\",\n \"cpp\",\n \"css\",\n \"env\",\n \"go\",\n \"h\",\n \"html\",\n \"ini\",\n \"java\",\n \"js\",\n \"jsx\",\n \"log\",\n \"mjs\",\n \"php\",\n \"py\",\n \"rb\",\n \"rs\",\n \"sh\",\n \"sql\",\n \"toml\",\n \"ts\",\n \"tsx\",\n \"txt\",\n \"xml\",\n \"yaml\",\n \"yml\",\n]);\n\nexport const fileViewExtension = (path: string): string => {\n const cleanPath = path.split(/[?#]/, 1)[0] ?? path;\n const name = cleanPath.slice(cleanPath.lastIndexOf(\"/\") + 1);\n const dot = name.lastIndexOf(\".\");\n return dot > 0 ? name.slice(dot + 1).toLowerCase() : \"\";\n};\n\nconst normalizedMediaType = (mediaType?: string): string => mediaType?.split(\";\", 1)[0]?.trim().toLowerCase() ?? \"\";\n\nconst previewKindByType = (file: FileViewFile): FileViewPreviewKind | null => {\n const extension = fileViewExtension(file.path);\n const mediaType = normalizedMediaType(file.mediaType);\n\n if (mediaType === \"text/markdown\" || markdownExtensions.has(extension)) return \"markdown\";\n if (mediaType === \"application/json\" || mediaType.endsWith(\"+json\") || extension === \"json\") return \"json\";\n if (mediaType === \"text/csv\" || mediaType === \"text/tab-separated-values\" || extension === \"csv\" || extension === \"tsv\") {\n return \"delimited-text\";\n }\n if (mediaType === \"application/pdf\" || extension === \"pdf\") return \"pdf\";\n if (imageMediaTypes.has(mediaType) || imageExtensions.has(extension)) return \"image\";\n if (audioMediaTypes.has(mediaType) || audioExtensions.has(extension)) return \"audio\";\n if (videoMediaTypes.has(mediaType) || videoExtensions.has(extension)) return \"video\";\n if (\n mediaType.startsWith(\"text/\") ||\n mediaType === \"application/javascript\" ||\n mediaType === \"application/sql\" ||\n mediaType === \"application/xml\" ||\n textExtensions.has(extension)\n ) {\n return \"text\";\n }\n return null;\n};\n\n/**\n * Returns the built-in preview kind when FileView can render the file within\n * its bounded client-side limits. Custom renderers remain registry-driven.\n */\nexport const getFileViewPreviewKind = (file: FileViewFile): FileViewPreviewKind | null => {\n const kind = previewKindByType(file);\n if (!kind) return null;\n if (file.size !== undefined && (!Number.isFinite(file.size) || file.size < 0 || file.size > maxPreviewBytes[kind])) {\n return null;\n }\n return kind;\n};\n\n/** Use this to expose preview actions only for files FileView can render. */\nexport const canPreviewFile = (file: FileViewFile): boolean => getFileViewPreviewKind(file) !== null;\n\nexport type DelimitedTextPreview = {\n rows: string[][];\n truncated: boolean;\n totalRows: number;\n columnsTruncated: boolean;\n};\n\nexport const parseDelimitedText = (\n input: string,\n delimiter: string,\n limits: { rows?: number; columns?: number; offset?: number } = {},\n): DelimitedTextPreview => {\n const maxRows = Math.max(1, limits.rows ?? 201);\n const maxColumns = Math.max(1, limits.columns ?? 50);\n const rows: string[][] = [];\n let row: string[] = [];\n let field = \"\";\n let quoted = false;\n let truncated = false;\n let totalRows = 0;\n let columnsTruncated = false;\n\n const pushField = () => {\n if (row.length < maxColumns) row.push(field);\n else {\n truncated = true;\n columnsTruncated = true;\n }\n field = \"\";\n };\n const pushRow = () => {\n if (rows.length < maxRows && (totalRows === 0 || totalRows > (limits.offset ?? 0))) rows.push(row);\n else truncated = true;\n totalRows++;\n row = [];\n };\n\n for (let index = 0; index < input.length; index += 1) {\n const character = input[index]!;\n if (quoted) {\n if (character === '\"' && input[index + 1] === '\"') {\n field += '\"';\n index += 1;\n } else if (character === '\"') {\n quoted = false;\n } else {\n field += character;\n }\n continue;\n }\n\n if (character === '\"' && field.length === 0) quoted = true;\n else if (character === delimiter) pushField();\n else if (character === \"\\n\" || character === \"\\r\") {\n pushField();\n pushRow();\n if (character === \"\\r\" && input[index + 1] === \"\\n\") index += 1;\n } else {\n field += character;\n }\n }\n\n if (field.length > 0 || row.length > 0) {\n pushField();\n pushRow();\n }\n\n return { rows, truncated, totalRows, columnsTruncated };\n};\n"],"mappings":"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","ignoreList":[]}
@@ -20,10 +20,12 @@ export { default as LogEntriesTable } from "./LogEntriesTable";
20
20
  export { default as MarkdownView, renderSafeMarkdown } from "./MarkdownView";
21
21
  export { MessageRow, MessageSystemRow, startsMessageGroup } from "./MessageRow";
22
22
  export { markdownInfoBlocks, renderMarkdownInfoBlock, scanMarkdownInfoBlock } from "./markdown-info-blocks";
23
+ export { markdownLinkReference, markdownReferenceIcon, markdownReferenceType, markdownReferenceTypeLabel, markStandaloneLinks, renderMarkdownLink } from "./markdown-links";
23
24
  export { Pagination } from "./Pagination";
24
25
  export { default as PdfPreview } from "./PdfPreview";
25
26
  export { default as RangePicker } from "./RangePicker";
26
27
  export { default as StructuredDataPreview, isStructuredDataValue } from "./StructuredDataPreview";
28
+ export { VideoPlayer } from "./VideoPlayer";
27
29
  export { VirtualFeed } from "./VirtualFeed";
28
30
  export { ZoomPanViewport } from "./ZoomPanViewport";
29
31
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["content/index.ts"],"sourcesContent":["export type {\n CalendarAttendee,\n CalendarDayBadge,\n CalendarEvent,\n CalendarEventColor,\n CalendarEventRenderContext,\n CalendarEventTimeChange,\n CalendarLabels,\n CalendarProps,\n CalendarRecurrence,\n CalendarResource,\n CalendarView,\n} from \"./Calendar\";\nexport { default as Calendar } from \"./Calendar\";\n\nexport type { ChartKind, ChartLabels, ChartProps, ChartRenderOptions } from \"./Chart\";\nexport { default as Chart } from \"./Chart\";\nexport type { ChartExplorerColumn, ChartExplorerProps } from \"./ChartExplorer\";\nexport { ChartExplorer } from \"./ChartExplorer\";\nexport type { ChartExplorerControlsProps } from \"./ChartExplorerControls\";\nexport { ChartExplorerControls } from \"./ChartExplorerControls\";\nexport { ChartFilterControls, type ChartFilterControlsProps } from \"./ChartFilterControls\";\nexport { ChartSnapshotView } from \"./ChartSnapshotView\";\nexport type { CodeDisplayLanguage, CodeDisplayProps } from \"./CodeDisplay\";\nexport { default as CodeDisplay } from \"./CodeDisplay\";\nexport { type ChartCursor, createChartCursor } from \"./chart-cursor\";\nexport type {\n ChartExplorerCharts,\n ChartExplorerController,\n ChartExplorerData,\n ChartExplorerOptions,\n ChartExplorerRequest,\n ChartExplorerRow,\n ChartExplorerSnapshot,\n} from \"./chart-explorer\";\nexport { createChartExplorer } from \"./chart-explorer\";\nexport type { ChartDatumRef, ChartSelection, ChartTooltip, ChartTooltipFormatter } from \"./chart-inspection\";\nexport type { ChartSnapshot } from \"./chart-snapshot\";\nexport { prepareChartSnapshot } from \"./chart-snapshot\";\nexport type {\n StateTimelineChartOptions,\n StateTimelineDomain,\n StateTimelineInterval,\n StateTimelineRow,\n StateTimelineState,\n} from \"./chart-state-timeline\";\nexport { type CollectionSelection, createCollectionSelection, type SelectionModifiers } from \"./collection-selection\";\nexport type {\n DataTableColumn,\n DataTableControlsProps,\n DataTableFooter,\n DataTableHeaderProps,\n DataTablePanelFooterProps,\n DataTablePanelProps,\n DataTableProps,\n DataTableRenderCell,\n DataTableRenderHeader,\n DataTableSort,\n} from \"./DataTable\";\nexport { default as DataTable } from \"./DataTable\";\nexport type { DocCodeHighlighter, DocCodeProps, DocConcept, DocNoteVariant, DocRow } from \"./Docs\";\nexport { DocCode, DocConceptGrid, DocInlineCode, DocLead, DocNote, DocPage, DocRows, DocSection } from \"./Docs\";\nexport { FileBrowserPanel, type FileBrowserPanelProps, type FileSource, openFileBrowser } from \"./FileBrowser\";\nexport { FileGrid, type FileGridProps } from \"./FileGrid\";\nexport { default as FileTree, type FileTreeActions, type FileTreeEntry, type FileTreeProps } from \"./FileTree\";\nexport {\n canPreviewFile,\n default as FileView,\n type FileViewContent,\n type FileViewFile,\n type FileViewPreviewKind,\n type FileViewProps,\n type FileViewRenderer,\n type FileViewRendererProps,\n formatFileViewSize,\n getFileViewPreviewKind,\n} from \"./FileView\";\nexport type { LightboxImage } from \"./Lightbox\";\nexport { default as Lightbox } from \"./Lightbox\";\nexport type { LogTableEntry } from \"./LogEntriesTable\";\nexport { default as LogEntriesTable } from \"./LogEntriesTable\";\nexport { default as MarkdownView, type MarkdownRenderOptions, type MarkdownViewProps, renderSafeMarkdown } from \"./MarkdownView\";\nexport type {\n MessageGroupEntry,\n MessageRowAction,\n MessageRowAttachment,\n MessageRowAuthor,\n MessageRowFile,\n MessageRowMedia,\n MessageRowProgress,\n MessageRowProps,\n MessageRowQuote,\n MessageRowReaction,\n MessageRowStatus,\n MessageRowThread,\n MessageSystemRowProps,\n} from \"./MessageRow\";\nexport { MessageRow, MessageSystemRow, startsMessageGroup } from \"./MessageRow\";\nexport {\n type MarkdownInfoBlock,\n type MarkdownInfoBlockRenderInput,\n type MarkdownInfoBlockType,\n markdownInfoBlocks,\n renderMarkdownInfoBlock,\n scanMarkdownInfoBlock,\n} from \"./markdown-info-blocks\";\nexport { Pagination, type PaginationProps } from \"./Pagination\";\nexport type { PdfPreviewProps, PdfPreviewRequest } from \"./PdfPreview\";\nexport { default as PdfPreview } from \"./PdfPreview\";\nexport type { RangeOption, RangePickerProps } from \"./RangePicker\";\nexport { default as RangePicker } from \"./RangePicker\";\nexport type { StructuredDataPreviewMode, StructuredDataPreviewProps, StructuredDataValue } from \"./StructuredDataPreview\";\nexport { default as StructuredDataPreview, isStructuredDataValue } from \"./StructuredDataPreview\";\nexport type { VirtualFeedController, VirtualFeedProps, VirtualFeedScrollOptions } from \"./VirtualFeed\";\nexport { VirtualFeed } from \"./VirtualFeed\";\nexport type { ZoomPanAction, ZoomPanFullscreen, ZoomPanViewportProps } from \"./ZoomPanViewport\";\nexport { ZoomPanViewport } from \"./ZoomPanViewport\";\n"],"mappings":"AAaA,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAGhD,SAAS,OAAO,IAAI,KAAK,QAAQ,SAAS;AAE1C,SAAS,aAAa,QAAQ,iBAAiB;AAE/C,SAAS,qBAAqB,QAAQ,yBAAyB;AAC/D,SAAS,mBAAmB,QAAuC,uBAAuB;AAC1F,SAAS,iBAAiB,QAAQ,qBAAqB;AAEvD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AACtD,SAA2B,iBAAiB,QAAQ,gBAAgB;AAUpE,SAAS,mBAAmB,QAAQ,kBAAkB;AAGtD,SAAS,oBAAoB,QAAQ,kBAAkB;AAQvD,SAAmC,yBAAyB,QAAiC,wBAAwB;AAarH,SAAS,OAAO,IAAI,SAAS,QAAQ,aAAa;AAElD,SAAS,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,QAAQ,QAAQ;AAC/G,SAAS,gBAAgB,EAA+C,eAAe,QAAQ,eAAe;AAC9G,SAAS,QAAQ,QAA4B,YAAY;AACzD,SAAS,OAAO,IAAI,QAAQ,QAAsE,YAAY;AAC9G,SACE,cAAc,EACd,OAAO,IAAI,QAAQ,EAOnB,kBAAkB,EAClB,sBAAsB,QACjB,YAAY;AAEnB,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAEhD,SAAS,OAAO,IAAI,eAAe,QAAQ,mBAAmB;AAC9D,SAAS,OAAO,IAAI,YAAY,EAAsD,kBAAkB,QAAQ,gBAAgB;AAgBhI,SAAS,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,QAAQ,cAAc;AAC/E,SAIE,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,QAChB,wBAAwB;AAC/B,SAAS,UAAU,QAA8B,cAAc;AAE/D,SAAS,OAAO,IAAI,UAAU,QAAQ,cAAc;AAEpD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AAEtD,SAAS,OAAO,IAAI,qBAAqB,EAAE,qBAAqB,QAAQ,yBAAyB;AAEjG,SAAS,WAAW,QAAQ,eAAe;AAE3C,SAAS,eAAe,QAAQ,mBAAmB","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["content/index.ts"],"sourcesContent":["export type {\n CalendarAttendee,\n CalendarDayBadge,\n CalendarEvent,\n CalendarEventColor,\n CalendarEventRenderContext,\n CalendarEventTimeChange,\n CalendarLabels,\n CalendarProps,\n CalendarRecurrence,\n CalendarResource,\n CalendarView,\n} from \"./Calendar\";\nexport { default as Calendar } from \"./Calendar\";\n\nexport type { ChartKind, ChartLabels, ChartProps, ChartRenderOptions } from \"./Chart\";\nexport { default as Chart } from \"./Chart\";\nexport type { ChartExplorerColumn, ChartExplorerProps } from \"./ChartExplorer\";\nexport { ChartExplorer } from \"./ChartExplorer\";\nexport type { ChartExplorerControlsProps } from \"./ChartExplorerControls\";\nexport { ChartExplorerControls } from \"./ChartExplorerControls\";\nexport { ChartFilterControls, type ChartFilterControlsProps } from \"./ChartFilterControls\";\nexport { ChartSnapshotView } from \"./ChartSnapshotView\";\nexport type { CodeDisplayLanguage, CodeDisplayProps } from \"./CodeDisplay\";\nexport { default as CodeDisplay } from \"./CodeDisplay\";\nexport { type ChartCursor, createChartCursor } from \"./chart-cursor\";\nexport type {\n ChartExplorerCharts,\n ChartExplorerController,\n ChartExplorerData,\n ChartExplorerOptions,\n ChartExplorerRequest,\n ChartExplorerRow,\n ChartExplorerSnapshot,\n} from \"./chart-explorer\";\nexport { createChartExplorer } from \"./chart-explorer\";\nexport type { ChartDatumRef, ChartSelection, ChartTooltip, ChartTooltipFormatter } from \"./chart-inspection\";\nexport type { ChartSnapshot } from \"./chart-snapshot\";\nexport { prepareChartSnapshot } from \"./chart-snapshot\";\nexport type {\n StateTimelineChartOptions,\n StateTimelineDomain,\n StateTimelineInterval,\n StateTimelineRow,\n StateTimelineState,\n} from \"./chart-state-timeline\";\nexport { type CollectionSelection, createCollectionSelection, type SelectionModifiers } from \"./collection-selection\";\nexport type {\n DataTableColumn,\n DataTableControlsProps,\n DataTableFooter,\n DataTableHeaderProps,\n DataTablePanelFooterProps,\n DataTablePanelProps,\n DataTableProps,\n DataTableRenderCell,\n DataTableRenderHeader,\n DataTableSort,\n} from \"./DataTable\";\nexport { default as DataTable } from \"./DataTable\";\nexport type { DocCodeHighlighter, DocCodeProps, DocConcept, DocNoteVariant, DocRow } from \"./Docs\";\nexport { DocCode, DocConceptGrid, DocInlineCode, DocLead, DocNote, DocPage, DocRows, DocSection } from \"./Docs\";\nexport { FileBrowserPanel, type FileBrowserPanelProps, type FileSource, openFileBrowser } from \"./FileBrowser\";\nexport { FileGrid, type FileGridProps } from \"./FileGrid\";\nexport { default as FileTree, type FileTreeActions, type FileTreeEntry, type FileTreeProps } from \"./FileTree\";\nexport {\n canPreviewFile,\n default as FileView,\n type FileViewContent,\n type FileViewFile,\n type FileViewPreviewKind,\n type FileViewProps,\n type FileViewRenderer,\n type FileViewRendererProps,\n formatFileViewSize,\n getFileViewPreviewKind,\n} from \"./FileView\";\nexport type { LightboxImage } from \"./Lightbox\";\nexport { default as Lightbox } from \"./Lightbox\";\nexport type { LogTableEntry } from \"./LogEntriesTable\";\nexport { default as LogEntriesTable } from \"./LogEntriesTable\";\nexport { default as MarkdownView, type MarkdownRenderOptions, type MarkdownViewProps, renderSafeMarkdown } from \"./MarkdownView\";\nexport type {\n MessageGroupEntry,\n MessageRowAction,\n MessageRowAttachment,\n MessageRowAuthor,\n MessageRowFile,\n MessageRowMedia,\n MessageRowProgress,\n MessageRowProps,\n MessageRowQuote,\n MessageRowReaction,\n MessageRowStatus,\n MessageRowThread,\n MessageSystemRowProps,\n} from \"./MessageRow\";\nexport { MessageRow, MessageSystemRow, startsMessageGroup } from \"./MessageRow\";\nexport {\n type MarkdownInfoBlock,\n type MarkdownInfoBlockRenderInput,\n type MarkdownInfoBlockType,\n markdownInfoBlocks,\n renderMarkdownInfoBlock,\n scanMarkdownInfoBlock,\n} from \"./markdown-info-blocks\";\nexport {\n type MarkdownLinkInput,\n type MarkdownReference,\n type MarkdownReferenceKind,\n type MarkdownReferenceType,\n markdownLinkReference,\n markdownReferenceIcon,\n markdownReferenceType,\n markdownReferenceTypeLabel,\n markStandaloneLinks,\n renderMarkdownLink,\n} from \"./markdown-links\";\nexport { Pagination, type PaginationProps } from \"./Pagination\";\nexport type { PdfPreviewProps, PdfPreviewRequest } from \"./PdfPreview\";\nexport { default as PdfPreview } from \"./PdfPreview\";\nexport type { RangeOption, RangePickerProps } from \"./RangePicker\";\nexport { default as RangePicker } from \"./RangePicker\";\nexport type { StructuredDataPreviewMode, StructuredDataPreviewProps, StructuredDataValue } from \"./StructuredDataPreview\";\nexport { default as StructuredDataPreview, isStructuredDataValue } from \"./StructuredDataPreview\";\nexport { VideoPlayer, type VideoPlayerProps } from \"./VideoPlayer\";\nexport type { VirtualFeedController, VirtualFeedProps, VirtualFeedScrollOptions } from \"./VirtualFeed\";\nexport { VirtualFeed } from \"./VirtualFeed\";\nexport type { ZoomPanAction, ZoomPanFullscreen, ZoomPanViewportProps } from \"./ZoomPanViewport\";\nexport { ZoomPanViewport } from \"./ZoomPanViewport\";\n"],"mappings":"AAaA,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAGhD,SAAS,OAAO,IAAI,KAAK,QAAQ,SAAS;AAE1C,SAAS,aAAa,QAAQ,iBAAiB;AAE/C,SAAS,qBAAqB,QAAQ,yBAAyB;AAC/D,SAAS,mBAAmB,QAAuC,uBAAuB;AAC1F,SAAS,iBAAiB,QAAQ,qBAAqB;AAEvD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AACtD,SAA2B,iBAAiB,QAAQ,gBAAgB;AAUpE,SAAS,mBAAmB,QAAQ,kBAAkB;AAGtD,SAAS,oBAAoB,QAAQ,kBAAkB;AAQvD,SAAmC,yBAAyB,QAAiC,wBAAwB;AAarH,SAAS,OAAO,IAAI,SAAS,QAAQ,aAAa;AAElD,SAAS,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,QAAQ,QAAQ;AAC/G,SAAS,gBAAgB,EAA+C,eAAe,QAAQ,eAAe;AAC9G,SAAS,QAAQ,QAA4B,YAAY;AACzD,SAAS,OAAO,IAAI,QAAQ,QAAsE,YAAY;AAC9G,SACE,cAAc,EACd,OAAO,IAAI,QAAQ,EAOnB,kBAAkB,EAClB,sBAAsB,QACjB,YAAY;AAEnB,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAEhD,SAAS,OAAO,IAAI,eAAe,QAAQ,mBAAmB;AAC9D,SAAS,OAAO,IAAI,YAAY,EAAsD,kBAAkB,QAAQ,gBAAgB;AAgBhI,SAAS,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,QAAQ,cAAc;AAC/E,SAIE,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,QAChB,wBAAwB;AAC/B,SAKE,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,kBAAkB,QACb,kBAAkB;AACzB,SAAS,UAAU,QAA8B,cAAc;AAE/D,SAAS,OAAO,IAAI,UAAU,QAAQ,cAAc;AAEpD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AAEtD,SAAS,OAAO,IAAI,qBAAqB,EAAE,qBAAqB,QAAQ,yBAAyB;AACjG,SAAS,WAAW,QAA+B,eAAe;AAElE,SAAS,WAAW,QAAQ,eAAe;AAE3C,SAAS,eAAe,QAAQ,mBAAmB","ignoreList":[]}
@@ -0,0 +1,178 @@
1
+ import { fileIcons } from "@k2b/stdlib";
2
+ import { resolveUiMessages } from "../intl/messages";
3
+
4
+ /** What a reference points to inside the host application. */
5
+
6
+ /** The type a reference shows: a file's type from its extension, or the kind of the referenced content. */
7
+
8
+ /**
9
+ * A link to content inside the host application, as opposed to a web or mail link.
10
+ * References render as a calm pill whose icon names the type.
11
+ */
12
+
13
+ const escapeHtml = value => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
14
+ const IMAGE_EXTENSIONS = new Set(["png", "jpg", "jpeg", "gif", "webp", "avif", "bmp", "ico", "svg", "heic", "heif", "tif", "tiff"]);
15
+ const DESIGN_EXTENSIONS = new Set(["afdesign", "afphoto", "afpub", "psd", "psb", "ai", "eps", "indd", "idml", "fig", "sketch", "xd"]);
16
+ const extensionOf = name => {
17
+ const match = /\.([a-z0-9]{1,10})$/i.exec(name.trim());
18
+ return match && /[a-z]/i.test(match[1]) ? match[1].toLowerCase() : null;
19
+ };
20
+
21
+ /** The type of a file reference, from the extension of its name. */
22
+ export const markdownFileType = fileName => {
23
+ const extension = extensionOf(fileName);
24
+ if (extension === "pdf") return "pdf";
25
+ if (extension && IMAGE_EXTENSIONS.has(extension)) return "image";
26
+ if (extension && DESIGN_EXTENSIONS.has(extension)) return "design";
27
+ return "file";
28
+ };
29
+ const REFERENCE_BASE = "https://reference.invalid/";
30
+
31
+ /**
32
+ * The reference a link destination names on its own: `#anchor` is a heading in the same document, and any other
33
+ * relative URL is a page of the host, or a file when its last path segment has a file extension. Absolute URLs,
34
+ * every scheme, and relative URLs that a browser resolves to another host, such as `//host` or `/\host`, are web
35
+ * or mail links; the result is then `null`.
36
+ */
37
+ export const markdownLinkReference = href => {
38
+ const url = href.trim();
39
+ if (!url || /^[a-z][a-z\d+.-]*:/i.test(url)) return null;
40
+ try {
41
+ if (new URL(url, REFERENCE_BASE).origin !== new URL(REFERENCE_BASE).origin) return null;
42
+ } catch {
43
+ return null;
44
+ }
45
+ if (url.startsWith("#")) return {
46
+ kind: "heading"
47
+ };
48
+ const segment = url.split(/[?#]/, 1)[0].split("/").pop() ?? "";
49
+ let fileName = segment;
50
+ try {
51
+ fileName = decodeURIComponent(segment);
52
+ } catch {
53
+ /* A malformed escape stays literal text. */
54
+ }
55
+ return extensionOf(fileName) ? {
56
+ kind: "file",
57
+ fileName
58
+ } : {
59
+ kind: "page"
60
+ };
61
+ };
62
+
63
+ /** The type a reference shows. */
64
+ export const markdownReferenceType = (reference, text) => reference.kind === "file" ? markdownFileType(reference.fileName ?? text) : reference.kind;
65
+ const TYPE_ICONS = {
66
+ pdf: "ti-file-type-pdf",
67
+ image: "ti-photo",
68
+ design: "ti-vector-bezier-2",
69
+ note: "ti-file-text",
70
+ heading: "ti-hash",
71
+ task: "ti-circle-check",
72
+ page: "ti-link"
73
+ };
74
+
75
+ /** The Tabler glyph of a reference type; any other file keeps the glyph of its file family. */
76
+ export const markdownReferenceIcon = (type, fileName = "") => type === "file" ? fileIcons.getFileIcon({
77
+ name: fileName,
78
+ type: "file"
79
+ }).split(/\s+/)[0] ?? "ti-file" : TYPE_ICONS[type];
80
+ const TYPE_LABELS = {
81
+ pdf: "referencePdf",
82
+ image: "referenceImage",
83
+ design: "referenceDesign",
84
+ file: "referenceFile",
85
+ note: "referenceNote",
86
+ heading: "referenceHeading",
87
+ task: "referenceTask",
88
+ page: "referencePage"
89
+ };
90
+
91
+ /** The localized name of a reference type, such as "PDF" or "Überschrift". */
92
+ export const markdownReferenceTypeLabel = (type, locale) => String(resolveUiMessages(locale)[TYPE_LABELS[type]]);
93
+
94
+ /**
95
+ * The text of rendered inline HTML, still as HTML: tags go, character references such as `&copy;` stay. One pass,
96
+ * so a label full of `<` stays linear. The result only ever reaches `textAttribute`, which escapes what is left.
97
+ */
98
+ const htmlText = html => {
99
+ let text = "";
100
+ let index = 0;
101
+ for (let open = html.indexOf("<"); open !== -1; open = html.indexOf("<", index)) {
102
+ const close = html.indexOf(">", open + 1);
103
+ if (close === -1) break;
104
+ text += html.slice(index, open);
105
+ index = close + 1;
106
+ }
107
+ return text + html.slice(index);
108
+ };
109
+
110
+ /** HTML text as an attribute value. Character references stay for the browser to decode; nothing can end the value. */
111
+ const textAttribute = text => text.replace(/&(?!#\d+;|#x[\da-f]+;|[a-z][a-z\d]*;)/gi, "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
112
+ const isMailHref = href => /^(?:mailto|tel):/i.test(href.trim());
113
+
114
+ /**
115
+ * One link of rendered Markdown. A reference to content of the host renders as a calm pill: a neutral fill, the
116
+ * text in the prose colour, and an icon whose colour names the type. Its accessible name starts with the type, as
117
+ * in "PDF: Brand-2026.pdf". A heading in another document shows that document first ("Notes › Heading"). A web
118
+ * link stays prose text with a thin underline in the link accent and a small ↗; a mail link has no arrow. A link
119
+ * around an image is the image: a plain anchor that the image's own text names.
120
+ */
121
+ export const renderMarkdownLink = input => {
122
+ const titleAttribute = input.title ? ` title="${escapeHtml(input.title)}"` : "";
123
+ const targetAttributes = input.target === "_blank" ? ' target="_blank" rel="noopener noreferrer"' : input.rel ? ` rel="${escapeHtml(input.rel)}"` : "";
124
+ const href = escapeHtml(input.href);
125
+ if (/<img\b/i.test(input.html)) return `<a href="${href}"${titleAttribute}${targetAttributes}>${input.html}</a>`;
126
+ const reference = input.reference;
127
+ if (!reference) {
128
+ const mail = isMailHref(input.href);
129
+ const arrow = mail ? "" : '<i class="k2b-text-link__external ti ti-arrow-up-right" aria-hidden="true"></i>';
130
+ return `<a href="${href}" class="k2b-text-link" data-link="${mail ? "mail" : "web"}"${titleAttribute}${targetAttributes}>${input.html}${arrow}</a>`;
131
+ }
132
+ const text = (input.text ?? htmlText(input.html)).trim();
133
+ const type = markdownReferenceType(reference, text);
134
+ const icon = markdownReferenceIcon(type, reference.fileName ?? text);
135
+ const size = input.standalone && reference.kind === "file" && reference.size ? reference.size : "";
136
+ const document = reference.kind === "heading" ? reference.document?.trim() ?? "" : "";
137
+ const name = escapeHtml(`${markdownReferenceTypeLabel(type, input.locale)}: ${document ? `${document} › ` : ""}`) + (input.text === undefined ? textAttribute(text) : escapeHtml(text)) + (size ? escapeHtml(`, ${size}`) : "");
138
+ return `<a href="${href}" class="k2b-reference" data-reference="${type}" aria-label="${name}"${titleAttribute}${targetAttributes}>` + `<i class="k2b-reference__icon ti ${icon}" aria-hidden="true"></i>` + (document ? `<span class="k2b-reference__document">${escapeHtml(document)} ›</span> ` : "") + input.html + (size ? `<span class="k2b-reference__size">${escapeHtml(size)}</span>` : "") + `</a>`;
139
+ };
140
+ const isBlank = token => (token.type === "text" || token.type === "space") && !token.raw.trim();
141
+ const isLink = token => token?.type === "link";
142
+
143
+ /**
144
+ * The links among `tokens` that stand alone on their line: the only content of a paragraph or list item line,
145
+ * between line breaks. Pass the result's check as `standalone` to `renderMarkdownLink`, so a file shows its size
146
+ * in a list of files but not inside a sentence. Call it from `walkTokens` of a renderer with GFM line breaks
147
+ * (`breaks: true`), where every source line is a visual line.
148
+ */
149
+ export const markStandaloneLinks = (token, standalone) => {
150
+ if (token.type !== "paragraph" && token.type !== "text" || !("tokens" in token) || !token.tokens) return;
151
+ let line = [];
152
+ const close = () => {
153
+ const content = line.filter(candidate => !isBlank(candidate));
154
+ if (content.length === 1 && isLink(content[0])) standalone.add(content[0]);
155
+ line = [];
156
+ };
157
+ for (const child of token.tokens) {
158
+ if (child.type === "br") {
159
+ close();
160
+ continue;
161
+ }
162
+ const plain = child.type === "text" && !("tokens" in child && child.tokens?.length) ? child.raw : "";
163
+ if (plain.includes("\n")) {
164
+ for (const [index, part] of plain.split("\n").entries()) {
165
+ if (index > 0) close();
166
+ if (part.trim()) line.push({
167
+ type: "text",
168
+ raw: part,
169
+ text: part
170
+ });
171
+ }
172
+ continue;
173
+ }
174
+ line.push(child);
175
+ }
176
+ close();
177
+ };
178
+ //# sourceMappingURL=markdown-links.js.map