@bendyline/squisq-react 2.11.7 → 2.11.8
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/README.md +19 -18
- package/dist/{chunk-ZY5N3JPM.js → chunk-6PUMLLJH.js} +281 -68
- package/dist/{chunk-ITQ4K5N6.js → chunk-KDTKUE53.js} +1 -1
- package/dist/{chunk-5UGIE2QO.js → chunk-LDXH4FOY.js} +2 -2
- package/dist/{chunk-7QA6EW6Y.js → chunk-SESRQ7XH.js} +1 -1
- package/dist/{chunk-XSOMFBH2.js → chunk-W6MKRWL6.js} +250 -2
- package/dist/{chunk-KGWT454C.js → chunk-Z64OLGMD.js} +136 -265
- package/dist/index.d.ts +36 -4
- package/dist/index.js +10 -6
- package/dist/json-view/index.js +2 -2
- package/dist/layers/index.js +1 -1
- package/dist/markdown/index.js +1 -1
- package/dist/page/index.js +4 -4
- package/dist/player/index.d.ts +1 -1
- package/dist/player/index.js +4 -4
- package/dist/{player-BlD8zREs.d.ts → player-5j10MhBq.d.ts} +24 -2
- package/dist/squisq-player.full.global.js +437 -437
- package/dist/squisq-player.global.js +66 -66
- package/dist/standalone-source.js +1 -1
- package/dist/styles/index.css +30 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -38,24 +38,25 @@ state rather than crashing.
|
|
|
38
38
|
|
|
39
39
|
## Components
|
|
40
40
|
|
|
41
|
-
| Component
|
|
42
|
-
|
|
|
43
|
-
| `DocPlayer`
|
|
44
|
-
| `LinearDocView`
|
|
45
|
-
| `BlockRenderer`
|
|
46
|
-
| `MarkdownRenderer`
|
|
47
|
-
| `DocPlayerWithSidebar`
|
|
48
|
-
| `CaptionOverlay`
|
|
49
|
-
| `DocProgressBar`
|
|
50
|
-
| `DocControlsOverlay`
|
|
51
|
-
| `DocControlsBottom`
|
|
52
|
-
| `DocControlsSidebar`
|
|
53
|
-
| `DocControlsSlideshow`
|
|
54
|
-
| `SocialCaptionOverlay`
|
|
55
|
-
| `InlineVideoPlayer`
|
|
56
|
-
| `InlineAudioPlayer`
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
41
|
+
| Component | Description |
|
|
42
|
+
| ----------------------- | ------------------------------------------------------------------- |
|
|
43
|
+
| `DocPlayer` | Main document player with timed playback, audio sync, and controls |
|
|
44
|
+
| `LinearDocView` | Scroll-based linear rendering of all blocks |
|
|
45
|
+
| `BlockRenderer` | SVG-based renderer for a single block |
|
|
46
|
+
| `MarkdownRenderer` | Renders Squisq markdown as a visual document |
|
|
47
|
+
| `DocPlayerWithSidebar` | DocPlayer with a sidebar navigation panel |
|
|
48
|
+
| `CaptionOverlay` | Timed caption/subtitle overlay |
|
|
49
|
+
| `DocProgressBar` | Playback progress indicator |
|
|
50
|
+
| `DocControlsOverlay` | Floating playback controls |
|
|
51
|
+
| `DocControlsBottom` | Bottom-bar playback controls |
|
|
52
|
+
| `DocControlsSidebar` | Sidebar navigation controls |
|
|
53
|
+
| `DocControlsSlideshow` | Slideshow-style navigation controls |
|
|
54
|
+
| `SocialCaptionOverlay` | Large centered word-by-word (TikTok/Reels-style) captions |
|
|
55
|
+
| `InlineVideoPlayer` | Native `<video>` wrapper resolving `src`/`poster` via MediaContext |
|
|
56
|
+
| `InlineAudioPlayer` | Native `<audio>` wrapper resolving `src` via MediaContext |
|
|
57
|
+
| `AnimatedImageControls` | Play/pause controls over an animated GIF/WebP/APNG image |
|
|
58
|
+
| `MediaClipLayer` | Hidden `<audio>`/`<video>` elements for timed media clips |
|
|
59
|
+
| `JsonView` | Read-only viewer for JSON values bound to a Squisq-annotated schema |
|
|
59
60
|
|
|
60
61
|
### Fenced-code copy control
|
|
61
62
|
|
|
@@ -57,13 +57,198 @@ function InlineAudioPlayer({
|
|
|
57
57
|
return /* @__PURE__ */ jsx2("span", { className: `squisq-inline-audio-player ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx2("audio", { src: resolvedSrc, controls, preload }) });
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// src/animatedImage/AnimatedImageControls.tsx
|
|
61
|
+
import { useLayoutEffect, useRef } from "react";
|
|
62
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
63
|
+
var FORMAT_LABELS = { gif: "GIF", webp: "WEBP", png: "APNG" };
|
|
64
|
+
function AnimatedImageControls({ playback, imageRef }) {
|
|
65
|
+
const canvasRef = useRef(null);
|
|
66
|
+
const { info, status, displaySrc } = playback;
|
|
67
|
+
const paused = status === "paused";
|
|
68
|
+
const animated = info?.animated ?? false;
|
|
69
|
+
useLayoutEffect(() => {
|
|
70
|
+
if (!paused || !animated) return;
|
|
71
|
+
const image = imageRef.current;
|
|
72
|
+
const canvas = canvasRef.current;
|
|
73
|
+
if (!image || !canvas) return;
|
|
74
|
+
const draw = () => {
|
|
75
|
+
if (!image.naturalWidth || !image.naturalHeight) return;
|
|
76
|
+
canvas.width = image.naturalWidth;
|
|
77
|
+
canvas.height = image.naturalHeight;
|
|
78
|
+
canvas.getContext("2d")?.drawImage(image, 0, 0);
|
|
79
|
+
};
|
|
80
|
+
if (image.complete && image.naturalWidth) {
|
|
81
|
+
draw();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
image.addEventListener("load", draw, { once: true });
|
|
85
|
+
return () => image.removeEventListener("load", draw);
|
|
86
|
+
}, [paused, animated, displaySrc, imageRef]);
|
|
87
|
+
if (!info || !animated) return null;
|
|
88
|
+
const label = status === "playing" ? "Pause animation" : status === "ended" ? "Replay animation" : "Play animation";
|
|
89
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
90
|
+
paused && /* @__PURE__ */ jsx3(
|
|
91
|
+
"canvas",
|
|
92
|
+
{
|
|
93
|
+
ref: canvasRef,
|
|
94
|
+
className: "squisq-animated-image-still",
|
|
95
|
+
"aria-hidden": "true",
|
|
96
|
+
style: {
|
|
97
|
+
position: "absolute",
|
|
98
|
+
inset: 0,
|
|
99
|
+
width: "100%",
|
|
100
|
+
height: "100%",
|
|
101
|
+
pointerEvents: "none"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
),
|
|
105
|
+
/* @__PURE__ */ jsxs(
|
|
106
|
+
"button",
|
|
107
|
+
{
|
|
108
|
+
type: "button",
|
|
109
|
+
className: "squisq-animated-image-toggle",
|
|
110
|
+
"data-status": status,
|
|
111
|
+
"aria-label": label,
|
|
112
|
+
title: label,
|
|
113
|
+
style: { position: "absolute", top: 8, left: 8 },
|
|
114
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
115
|
+
onClick: (event) => {
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
event.stopPropagation();
|
|
118
|
+
if (status === "playing") playback.pause();
|
|
119
|
+
else playback.play();
|
|
120
|
+
},
|
|
121
|
+
children: [
|
|
122
|
+
/* @__PURE__ */ jsx3(StatusGlyph, { status }),
|
|
123
|
+
/* @__PURE__ */ jsx3("span", { className: "squisq-animated-image-format", children: FORMAT_LABELS[info.format] })
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
] });
|
|
128
|
+
}
|
|
129
|
+
function StatusGlyph({ status }) {
|
|
130
|
+
const path = status === "playing" ? "M2.5 1.5h2.75v9H2.5zM6.75 1.5H9.5v9H6.75z" : status === "ended" ? "M6 1.5V0L3.5 2.25 6 4.5V3a3 3 0 1 1-3 3H1.5A4.5 4.5 0 1 0 6 1.5z" : "M3 1.5v9l7.5-4.5z";
|
|
131
|
+
return /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 12 12", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx3("path", { d: path }) });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/animatedImage/useAnimatedImage.ts
|
|
135
|
+
import { useCallback, useEffect, useRef as useRef2, useState } from "react";
|
|
136
|
+
import {
|
|
137
|
+
inspectAnimatedImage,
|
|
138
|
+
mayBeAnimatedImage
|
|
139
|
+
} from "@bendyline/squisq/imageEdit";
|
|
140
|
+
var INSPECTION_CACHE_LIMIT = 64;
|
|
141
|
+
var inspections = /* @__PURE__ */ new Map();
|
|
142
|
+
function inspectUrl(url) {
|
|
143
|
+
const cached = inspections.get(url);
|
|
144
|
+
if (cached) return cached;
|
|
145
|
+
const pending = fetch(url).then((response) => response.ok ? response.arrayBuffer() : null).then((buffer) => buffer ? inspectAnimatedImage(buffer) : null).catch(() => null);
|
|
146
|
+
if (!/^data:/i.test(url)) {
|
|
147
|
+
inspections.set(url, pending);
|
|
148
|
+
if (inspections.size > INSPECTION_CACHE_LIMIT) {
|
|
149
|
+
const oldest = inspections.keys().next().value;
|
|
150
|
+
if (oldest !== void 0) inspections.delete(oldest);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return pending;
|
|
154
|
+
}
|
|
155
|
+
function inspectableUrl(url) {
|
|
156
|
+
if (/^(?:blob|data):/i.test(url)) return url;
|
|
157
|
+
if (typeof document === "undefined") return null;
|
|
158
|
+
try {
|
|
159
|
+
const parsed = new URL(url, document.baseURI);
|
|
160
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
161
|
+
return parsed.origin === window.location.origin ? parsed.href : null;
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function prefersReducedMotion() {
|
|
167
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
168
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
169
|
+
}
|
|
170
|
+
function initialStatus() {
|
|
171
|
+
return prefersReducedMotion() ? "paused" : "playing";
|
|
172
|
+
}
|
|
173
|
+
function useAnimatedImage(src, resolvedUrl) {
|
|
174
|
+
const [info, setInfo] = useState(null);
|
|
175
|
+
const [status, setStatus] = useState(initialStatus);
|
|
176
|
+
const [replayUrl, setReplayUrl] = useState(null);
|
|
177
|
+
const [run, setRun] = useState(0);
|
|
178
|
+
const generationRef = useRef2(0);
|
|
179
|
+
const replayUrlRef = useRef2(null);
|
|
180
|
+
const swapReplayUrl = useCallback((next) => {
|
|
181
|
+
if (replayUrlRef.current) URL.revokeObjectURL(replayUrlRef.current);
|
|
182
|
+
replayUrlRef.current = next;
|
|
183
|
+
setReplayUrl(next);
|
|
184
|
+
}, []);
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
setInfo(null);
|
|
187
|
+
const url = resolvedUrl && mayBeAnimatedImage(src) ? inspectableUrl(resolvedUrl) : null;
|
|
188
|
+
if (!url) return;
|
|
189
|
+
let cancelled = false;
|
|
190
|
+
void inspectUrl(url).then((next) => {
|
|
191
|
+
if (!cancelled) setInfo(next);
|
|
192
|
+
});
|
|
193
|
+
return () => {
|
|
194
|
+
cancelled = true;
|
|
195
|
+
};
|
|
196
|
+
}, [src, resolvedUrl]);
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
generationRef.current++;
|
|
199
|
+
swapReplayUrl(null);
|
|
200
|
+
setStatus(initialStatus());
|
|
201
|
+
}, [resolvedUrl, swapReplayUrl]);
|
|
202
|
+
useEffect(
|
|
203
|
+
() => () => {
|
|
204
|
+
if (replayUrlRef.current) URL.revokeObjectURL(replayUrlRef.current);
|
|
205
|
+
},
|
|
206
|
+
[]
|
|
207
|
+
);
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
if (status !== "playing" || !info?.animated || info.playCount === 0) return;
|
|
210
|
+
const timer = setTimeout(() => setStatus("ended"), info.durationMs * info.playCount);
|
|
211
|
+
return () => clearTimeout(timer);
|
|
212
|
+
}, [status, info, run]);
|
|
213
|
+
const play = useCallback(() => {
|
|
214
|
+
const generation = ++generationRef.current;
|
|
215
|
+
const start = (url) => {
|
|
216
|
+
if (generation !== generationRef.current) {
|
|
217
|
+
if (url) URL.revokeObjectURL(url);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (url) swapReplayUrl(url);
|
|
221
|
+
setStatus("playing");
|
|
222
|
+
setRun((n) => n + 1);
|
|
223
|
+
};
|
|
224
|
+
fetch(replayUrl ?? resolvedUrl).then((response) => response.blob()).then(
|
|
225
|
+
(blob) => start(URL.createObjectURL(blob)),
|
|
226
|
+
// Unreadable now: resume the image as it is rather than stay stuck.
|
|
227
|
+
() => start(null)
|
|
228
|
+
);
|
|
229
|
+
}, [replayUrl, resolvedUrl, swapReplayUrl]);
|
|
230
|
+
const pause = useCallback(() => {
|
|
231
|
+
generationRef.current++;
|
|
232
|
+
setStatus("paused");
|
|
233
|
+
}, []);
|
|
234
|
+
return {
|
|
235
|
+
info,
|
|
236
|
+
animated: info?.animated ?? false,
|
|
237
|
+
displaySrc: replayUrl ?? resolvedUrl,
|
|
238
|
+
status,
|
|
239
|
+
play,
|
|
240
|
+
pause
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
60
244
|
// src/MarkdownRenderer.tsx
|
|
61
|
-
import { Component, Fragment, useEffect, useRef, useState } from "react";
|
|
245
|
+
import { Component, Fragment as Fragment2, useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
|
|
246
|
+
import { mayBeAnimatedImage as mayBeAnimatedImage2 } from "@bendyline/squisq/imageEdit";
|
|
62
247
|
import {
|
|
63
248
|
sanitizeHtmlNodes,
|
|
64
249
|
sanitizeUrl
|
|
65
250
|
} from "@bendyline/squisq/markdown";
|
|
66
|
-
import { jsx as
|
|
251
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
67
252
|
var DEFAULT_CTX = { htmlPolicy: "sanitize" };
|
|
68
253
|
var CODE_COPY_LABELS = {
|
|
69
254
|
idle: "Copy",
|
|
@@ -86,15 +271,15 @@ function CodeBlock({
|
|
|
86
271
|
language,
|
|
87
272
|
ctx
|
|
88
273
|
}) {
|
|
89
|
-
const [status, setStatus] =
|
|
90
|
-
const resetTimer =
|
|
91
|
-
|
|
274
|
+
const [status, setStatus] = useState2("idle");
|
|
275
|
+
const resetTimer = useRef3();
|
|
276
|
+
useEffect2(
|
|
92
277
|
() => () => {
|
|
93
278
|
if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
|
|
94
279
|
},
|
|
95
280
|
[]
|
|
96
281
|
);
|
|
97
|
-
const pre = /* @__PURE__ */
|
|
282
|
+
const pre = /* @__PURE__ */ jsx4("pre", { className: "squisq-md-code-block", children: /* @__PURE__ */ jsx4("code", { className: language ? `language-${language}` : void 0, children: value }) });
|
|
98
283
|
if (!ctx.showCodeCopyButton) return pre;
|
|
99
284
|
const handleCopy = async () => {
|
|
100
285
|
if (status === "copying") return;
|
|
@@ -108,9 +293,9 @@ function CodeBlock({
|
|
|
108
293
|
if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
|
|
109
294
|
resetTimer.current = setTimeout(() => setStatus("idle"), 1600);
|
|
110
295
|
};
|
|
111
|
-
return /* @__PURE__ */
|
|
296
|
+
return /* @__PURE__ */ jsxs2("div", { className: "squisq-md-code-frame", children: [
|
|
112
297
|
pre,
|
|
113
|
-
/* @__PURE__ */
|
|
298
|
+
/* @__PURE__ */ jsx4(
|
|
114
299
|
"button",
|
|
115
300
|
{
|
|
116
301
|
type: "button",
|
|
@@ -143,7 +328,7 @@ function HostFence({
|
|
|
143
328
|
}) {
|
|
144
329
|
const renderer = ctx.fenceRenderers?.[lang];
|
|
145
330
|
if (!renderer) return null;
|
|
146
|
-
return /* @__PURE__ */
|
|
331
|
+
return /* @__PURE__ */ jsx4("div", { className: `squisq-md-fence-widget squisq-md-fence-widget-${lang}`, "data-fence-lang": lang, children: renderer({
|
|
147
332
|
lang,
|
|
148
333
|
...node.meta != null ? { meta: node.meta } : {},
|
|
149
334
|
value: node.value,
|
|
@@ -157,37 +342,37 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
157
342
|
switch (node.type) {
|
|
158
343
|
case "text": {
|
|
159
344
|
if (!node.value.includes("\n")) {
|
|
160
|
-
return /* @__PURE__ */
|
|
345
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children: node.value }, key);
|
|
161
346
|
}
|
|
162
347
|
const parts = node.value.split("\n");
|
|
163
|
-
return /* @__PURE__ */
|
|
164
|
-
j > 0 && /* @__PURE__ */
|
|
348
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children: parts.map((part, j) => /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
349
|
+
j > 0 && /* @__PURE__ */ jsx4("br", {}),
|
|
165
350
|
part
|
|
166
351
|
] }, j)) }, key);
|
|
167
352
|
}
|
|
168
353
|
case "emphasis":
|
|
169
|
-
return /* @__PURE__ */
|
|
354
|
+
return /* @__PURE__ */ jsx4("em", { className: "squisq-md-em", children: renderInline(node.children, key, ctx) }, key);
|
|
170
355
|
case "strong":
|
|
171
|
-
return /* @__PURE__ */
|
|
356
|
+
return /* @__PURE__ */ jsx4("strong", { className: "squisq-md-strong", children: renderInline(node.children, key, ctx) }, key);
|
|
172
357
|
case "delete":
|
|
173
|
-
return /* @__PURE__ */
|
|
358
|
+
return /* @__PURE__ */ jsx4("del", { className: "squisq-md-del", children: renderInline(node.children, key, ctx) }, key);
|
|
174
359
|
case "superscript":
|
|
175
|
-
return /* @__PURE__ */
|
|
360
|
+
return /* @__PURE__ */ jsx4("sup", { className: "squisq-md-sup", children: renderInline(node.children, key, ctx) }, key);
|
|
176
361
|
case "subscript":
|
|
177
|
-
return /* @__PURE__ */
|
|
362
|
+
return /* @__PURE__ */ jsx4("sub", { className: "squisq-md-sub", children: renderInline(node.children, key, ctx) }, key);
|
|
178
363
|
case "inlineCode":
|
|
179
|
-
return /* @__PURE__ */
|
|
364
|
+
return /* @__PURE__ */ jsx4("code", { className: "squisq-md-inline-code", children: node.value }, key);
|
|
180
365
|
case "link": {
|
|
181
366
|
if (ctx.accessoryPaths?.has(node.url)) {
|
|
182
|
-
return /* @__PURE__ */
|
|
367
|
+
return /* @__PURE__ */ jsxs2(
|
|
183
368
|
"span",
|
|
184
369
|
{
|
|
185
370
|
className: "squisq-md-attachment",
|
|
186
371
|
"data-attachment-path": node.url,
|
|
187
372
|
title: node.title ?? `Attachment: ${node.url}`,
|
|
188
373
|
children: [
|
|
189
|
-
/* @__PURE__ */
|
|
190
|
-
/* @__PURE__ */
|
|
374
|
+
/* @__PURE__ */ jsx4("i", { className: "fa-solid fa-paperclip", "aria-hidden": "true" }),
|
|
375
|
+
/* @__PURE__ */ jsx4("span", { className: "squisq-md-attachment-label", children: renderInline(node.children, key, ctx) })
|
|
191
376
|
]
|
|
192
377
|
},
|
|
193
378
|
key
|
|
@@ -195,9 +380,9 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
195
380
|
}
|
|
196
381
|
const href = sanitizeUrl(node.url, "link", { extraLinkSchemes: ctx.linkSchemes });
|
|
197
382
|
if (!href) {
|
|
198
|
-
return /* @__PURE__ */
|
|
383
|
+
return /* @__PURE__ */ jsx4("span", { className: "squisq-md-link squisq-md-link--blocked", children: renderInline(node.children, key, ctx) }, key);
|
|
199
384
|
}
|
|
200
|
-
return /* @__PURE__ */
|
|
385
|
+
return /* @__PURE__ */ jsx4(
|
|
201
386
|
"a",
|
|
202
387
|
{
|
|
203
388
|
className: "squisq-md-link",
|
|
@@ -211,13 +396,13 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
211
396
|
);
|
|
212
397
|
}
|
|
213
398
|
case "image":
|
|
214
|
-
return /* @__PURE__ */
|
|
399
|
+
return /* @__PURE__ */ jsx4(MdImage, { src: node.url, alt: node.alt ?? "", title: node.title ?? void 0 }, key);
|
|
215
400
|
case "break":
|
|
216
|
-
return /* @__PURE__ */
|
|
401
|
+
return /* @__PURE__ */ jsx4("br", {}, key);
|
|
217
402
|
case "inlineMath":
|
|
218
|
-
return /* @__PURE__ */
|
|
403
|
+
return /* @__PURE__ */ jsx4("code", { className: "squisq-md-inline-math", children: node.value }, key);
|
|
219
404
|
case "inlineIcon":
|
|
220
|
-
return /* @__PURE__ */
|
|
405
|
+
return /* @__PURE__ */ jsx4(
|
|
221
406
|
"i",
|
|
222
407
|
{
|
|
223
408
|
className: `fa-${node.family} fa-${node.name}`,
|
|
@@ -228,25 +413,25 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
228
413
|
);
|
|
229
414
|
case "htmlInline":
|
|
230
415
|
if (ctx.htmlPolicy === "strip") return null;
|
|
231
|
-
return /* @__PURE__ */
|
|
416
|
+
return /* @__PURE__ */ jsx4("span", { className: "squisq-md-html-inline", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`, ctx) }, key);
|
|
232
417
|
case "footnoteReference":
|
|
233
|
-
return /* @__PURE__ */
|
|
418
|
+
return /* @__PURE__ */ jsx4("sup", { className: "squisq-md-footnote-ref", children: /* @__PURE__ */ jsxs2("a", { href: `#fn-${node.identifier}`, children: [
|
|
234
419
|
"[",
|
|
235
420
|
node.label ?? node.identifier,
|
|
236
421
|
"]"
|
|
237
422
|
] }) }, key);
|
|
238
423
|
case "linkReference":
|
|
239
|
-
return /* @__PURE__ */
|
|
424
|
+
return /* @__PURE__ */ jsx4("span", { className: "squisq-md-link-ref", children: renderInline(node.children, key, ctx) }, key);
|
|
240
425
|
case "imageReference":
|
|
241
|
-
return /* @__PURE__ */
|
|
426
|
+
return /* @__PURE__ */ jsxs2("span", { className: "squisq-md-image-ref", children: [
|
|
242
427
|
"[",
|
|
243
428
|
node.alt ?? node.identifier,
|
|
244
429
|
"]"
|
|
245
430
|
] }, key);
|
|
246
431
|
case "textDirective":
|
|
247
|
-
return /* @__PURE__ */
|
|
432
|
+
return /* @__PURE__ */ jsx4("span", { className: "squisq-md-text-directive", "data-directive": node.name, children: renderInline(node.children, key, ctx) }, key);
|
|
248
433
|
case "mention":
|
|
249
|
-
return /* @__PURE__ */
|
|
434
|
+
return /* @__PURE__ */ jsxs2(
|
|
250
435
|
"span",
|
|
251
436
|
{
|
|
252
437
|
className: "squisq-md-mention mention",
|
|
@@ -269,32 +454,32 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
269
454
|
function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
270
455
|
switch (node.type) {
|
|
271
456
|
case "paragraph":
|
|
272
|
-
return /* @__PURE__ */
|
|
457
|
+
return /* @__PURE__ */ jsx4("p", { className: "squisq-md-p", children: renderInline(node.children, key, ctx) }, key);
|
|
273
458
|
case "heading": {
|
|
274
459
|
const Tag = `h${node.depth}`;
|
|
275
|
-
return /* @__PURE__ */
|
|
460
|
+
return /* @__PURE__ */ jsx4(Tag, { className: `squisq-md-heading squisq-md-h${node.depth}`, children: renderInline(node.children, key, ctx) }, key);
|
|
276
461
|
}
|
|
277
462
|
case "blockquote":
|
|
278
|
-
return /* @__PURE__ */
|
|
463
|
+
return /* @__PURE__ */ jsx4("blockquote", { className: "squisq-md-blockquote", children: renderBlocks(node.children, key, ctx) }, key);
|
|
279
464
|
case "list":
|
|
280
465
|
if (node.ordered) {
|
|
281
|
-
return /* @__PURE__ */
|
|
466
|
+
return /* @__PURE__ */ jsx4("ol", { className: "squisq-md-list squisq-md-ol", start: node.start ?? void 0, children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, ctx)) }, key);
|
|
282
467
|
}
|
|
283
|
-
return /* @__PURE__ */
|
|
468
|
+
return /* @__PURE__ */ jsx4("ul", { className: "squisq-md-list squisq-md-ul", children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, ctx)) }, key);
|
|
284
469
|
case "code": {
|
|
285
470
|
const fenceLang = node.lang?.trim().toLowerCase();
|
|
286
471
|
if (fenceLang && ctx.fenceRenderers?.[fenceLang]) {
|
|
287
|
-
return /* @__PURE__ */
|
|
472
|
+
return /* @__PURE__ */ jsx4(
|
|
288
473
|
FenceWidgetBoundary,
|
|
289
474
|
{
|
|
290
|
-
fallback: /* @__PURE__ */
|
|
291
|
-
children: /* @__PURE__ */
|
|
475
|
+
fallback: /* @__PURE__ */ jsx4(CodeBlock, { value: node.value, language: node.lang, ctx }),
|
|
476
|
+
children: /* @__PURE__ */ jsx4(HostFence, { node, lang: fenceLang, ctx })
|
|
292
477
|
},
|
|
293
478
|
key
|
|
294
479
|
);
|
|
295
480
|
}
|
|
296
481
|
if (fenceLang === "mermaid") {
|
|
297
|
-
return /* @__PURE__ */
|
|
482
|
+
return /* @__PURE__ */ jsx4(
|
|
298
483
|
MermaidDiagram,
|
|
299
484
|
{
|
|
300
485
|
source: node.value,
|
|
@@ -305,39 +490,39 @@ function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
|
305
490
|
key
|
|
306
491
|
);
|
|
307
492
|
}
|
|
308
|
-
return /* @__PURE__ */
|
|
493
|
+
return /* @__PURE__ */ jsx4(CodeBlock, { value: node.value, language: node.lang, ctx }, key);
|
|
309
494
|
}
|
|
310
495
|
case "thematicBreak":
|
|
311
|
-
return /* @__PURE__ */
|
|
496
|
+
return /* @__PURE__ */ jsx4("hr", { className: "squisq-md-hr" }, key);
|
|
312
497
|
case "table":
|
|
313
498
|
return renderTable(node.children, node.align, key, ctx);
|
|
314
499
|
case "htmlBlock":
|
|
315
500
|
if (ctx.htmlPolicy === "strip") return null;
|
|
316
|
-
return /* @__PURE__ */
|
|
501
|
+
return /* @__PURE__ */ jsx4("div", { className: "squisq-md-html-block", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`, ctx) }, key);
|
|
317
502
|
case "math":
|
|
318
|
-
return /* @__PURE__ */
|
|
503
|
+
return /* @__PURE__ */ jsx4("pre", { className: "squisq-md-math-block", children: /* @__PURE__ */ jsx4("code", { children: node.value }) }, key);
|
|
319
504
|
case "definition":
|
|
320
505
|
return null;
|
|
321
506
|
case "footnoteDefinition":
|
|
322
|
-
return /* @__PURE__ */
|
|
323
|
-
/* @__PURE__ */
|
|
507
|
+
return /* @__PURE__ */ jsxs2("div", { className: "squisq-md-footnote-def", id: `fn-${node.identifier}`, children: [
|
|
508
|
+
/* @__PURE__ */ jsx4("sup", { children: node.label ?? node.identifier }),
|
|
324
509
|
renderBlocks(node.children, key, ctx)
|
|
325
510
|
] }, key);
|
|
326
511
|
case "containerDirective":
|
|
327
|
-
return /* @__PURE__ */
|
|
512
|
+
return /* @__PURE__ */ jsxs2(
|
|
328
513
|
"div",
|
|
329
514
|
{
|
|
330
515
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
331
516
|
"data-directive": node.name,
|
|
332
517
|
children: [
|
|
333
|
-
node.label && /* @__PURE__ */
|
|
518
|
+
node.label && /* @__PURE__ */ jsx4("div", { className: "squisq-md-directive-label", children: node.label }),
|
|
334
519
|
renderBlocks(node.children, key, ctx)
|
|
335
520
|
]
|
|
336
521
|
},
|
|
337
522
|
key
|
|
338
523
|
);
|
|
339
524
|
case "leafDirective":
|
|
340
|
-
return /* @__PURE__ */
|
|
525
|
+
return /* @__PURE__ */ jsx4(
|
|
341
526
|
"div",
|
|
342
527
|
{
|
|
343
528
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
@@ -347,11 +532,11 @@ function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
|
347
532
|
key
|
|
348
533
|
);
|
|
349
534
|
case "definitionList":
|
|
350
|
-
return /* @__PURE__ */
|
|
535
|
+
return /* @__PURE__ */ jsx4("dl", { className: "squisq-md-dl", children: node.children.map((child, i) => {
|
|
351
536
|
if (child.type === "definitionTerm") {
|
|
352
|
-
return /* @__PURE__ */
|
|
537
|
+
return /* @__PURE__ */ jsx4("dt", { className: "squisq-md-dt", children: renderInline(child.children, `${key}dt${i}`, ctx) }, `${key}dt${i}`);
|
|
353
538
|
}
|
|
354
|
-
return /* @__PURE__ */
|
|
539
|
+
return /* @__PURE__ */ jsx4("dd", { className: "squisq-md-dd", children: renderBlocks(child.children, `${key}dd${i}`, ctx) }, `${key}dd${i}`);
|
|
355
540
|
}) }, key);
|
|
356
541
|
default:
|
|
357
542
|
return null;
|
|
@@ -359,15 +544,15 @@ function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
|
359
544
|
}
|
|
360
545
|
function renderListItem(item, key, ctx = DEFAULT_CTX) {
|
|
361
546
|
const isTask = item.checked !== null && item.checked !== void 0;
|
|
362
|
-
return /* @__PURE__ */
|
|
363
|
-
isTask && /* @__PURE__ */
|
|
547
|
+
return /* @__PURE__ */ jsxs2("li", { className: `squisq-md-li${isTask ? " squisq-md-task" : ""}`, children: [
|
|
548
|
+
isTask && /* @__PURE__ */ jsx4("input", { type: "checkbox", checked: !!item.checked, readOnly: true, className: "squisq-md-checkbox" }),
|
|
364
549
|
renderBlocks(item.children, key, ctx)
|
|
365
550
|
] }, key);
|
|
366
551
|
}
|
|
367
552
|
function renderTable(rows, align, key, ctx = DEFAULT_CTX) {
|
|
368
553
|
const [headerRow, ...bodyRows] = rows;
|
|
369
|
-
return /* @__PURE__ */
|
|
370
|
-
headerRow && /* @__PURE__ */
|
|
554
|
+
return /* @__PURE__ */ jsxs2("table", { className: "squisq-md-table", children: [
|
|
555
|
+
headerRow && /* @__PURE__ */ jsx4("thead", { children: /* @__PURE__ */ jsx4("tr", { children: headerRow.children.map((cell, ci) => /* @__PURE__ */ jsx4(
|
|
371
556
|
"th",
|
|
372
557
|
{
|
|
373
558
|
className: "squisq-md-th",
|
|
@@ -376,7 +561,7 @@ function renderTable(rows, align, key, ctx = DEFAULT_CTX) {
|
|
|
376
561
|
},
|
|
377
562
|
`${key}th${ci}`
|
|
378
563
|
)) }) }),
|
|
379
|
-
bodyRows.length > 0 && /* @__PURE__ */
|
|
564
|
+
bodyRows.length > 0 && /* @__PURE__ */ jsx4("tbody", { children: bodyRows.map((row, ri) => /* @__PURE__ */ jsx4("tr", { children: row.children.map((cell, ci) => /* @__PURE__ */ jsx4(
|
|
380
565
|
"td",
|
|
381
566
|
{
|
|
382
567
|
className: "squisq-md-td",
|
|
@@ -393,8 +578,34 @@ function renderBlocks(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
393
578
|
function MdImage({ src, alt, title }) {
|
|
394
579
|
const safeSrc = sanitizeUrl(src, "media");
|
|
395
580
|
const resolved = useMediaUrl(safeSrc ?? "", ".");
|
|
581
|
+
const imageRef = useRef3(null);
|
|
582
|
+
const playback = useAnimatedImage(safeSrc ?? "", resolved);
|
|
396
583
|
if (!safeSrc) return null;
|
|
397
|
-
|
|
584
|
+
if (!mayBeAnimatedImage2(safeSrc)) {
|
|
585
|
+
return /* @__PURE__ */ jsx4("img", { className: "squisq-md-image", src: resolved, alt, title });
|
|
586
|
+
}
|
|
587
|
+
return /* @__PURE__ */ jsxs2(
|
|
588
|
+
"span",
|
|
589
|
+
{
|
|
590
|
+
className: "squisq-animated-image",
|
|
591
|
+
"data-animated": playback.animated ? "true" : void 0,
|
|
592
|
+
style: { position: "relative", display: "inline-block", maxWidth: "100%" },
|
|
593
|
+
children: [
|
|
594
|
+
/* @__PURE__ */ jsx4(
|
|
595
|
+
"img",
|
|
596
|
+
{
|
|
597
|
+
ref: imageRef,
|
|
598
|
+
className: "squisq-md-image",
|
|
599
|
+
src: playback.displaySrc,
|
|
600
|
+
alt,
|
|
601
|
+
title,
|
|
602
|
+
style: { display: "block", maxWidth: "100%" }
|
|
603
|
+
}
|
|
604
|
+
),
|
|
605
|
+
/* @__PURE__ */ jsx4(AnimatedImageControls, { playback, imageRef })
|
|
606
|
+
]
|
|
607
|
+
}
|
|
608
|
+
);
|
|
398
609
|
}
|
|
399
610
|
function resolveHtmlNodes(nodes, htmlPolicy) {
|
|
400
611
|
if (htmlPolicy === "strip") return [];
|
|
@@ -466,7 +677,7 @@ function renderHtmlElement(el, key, ctx) {
|
|
|
466
677
|
if (tagName === "video") {
|
|
467
678
|
const src = sanitizeUrl(el.attributes.src ?? "", "media") ?? "";
|
|
468
679
|
const poster = sanitizeUrl(el.attributes.poster ?? "", "media") ?? void 0;
|
|
469
|
-
return /* @__PURE__ */
|
|
680
|
+
return /* @__PURE__ */ jsx4(
|
|
470
681
|
InlineVideoPlayer,
|
|
471
682
|
{
|
|
472
683
|
src,
|
|
@@ -481,7 +692,7 @@ function renderHtmlElement(el, key, ctx) {
|
|
|
481
692
|
}
|
|
482
693
|
if (tagName === "audio") {
|
|
483
694
|
const src = sanitizeUrl(el.attributes.src ?? "", "media") ?? "";
|
|
484
|
-
return /* @__PURE__ */
|
|
695
|
+
return /* @__PURE__ */ jsx4(
|
|
485
696
|
InlineAudioPlayer,
|
|
486
697
|
{
|
|
487
698
|
src,
|
|
@@ -494,9 +705,9 @@ function renderHtmlElement(el, key, ctx) {
|
|
|
494
705
|
const Tag = tagName;
|
|
495
706
|
const props = reactPropsFromAttrs(el.attributes, ctx);
|
|
496
707
|
if (el.selfClosing) {
|
|
497
|
-
return /* @__PURE__ */
|
|
708
|
+
return /* @__PURE__ */ jsx4(Tag, { ...props }, key);
|
|
498
709
|
}
|
|
499
|
-
return /* @__PURE__ */
|
|
710
|
+
return /* @__PURE__ */ jsx4(Tag, { ...props, children: renderHtmlNodes(el.children, `${key}c`, ctx) }, key);
|
|
500
711
|
}
|
|
501
712
|
function renderHtmlNodes(nodes, keyPrefix, ctx = DEFAULT_CTX) {
|
|
502
713
|
return nodes.map((node, i) => {
|
|
@@ -505,7 +716,7 @@ function renderHtmlNodes(nodes, keyPrefix, ctx = DEFAULT_CTX) {
|
|
|
505
716
|
case "htmlElement":
|
|
506
717
|
return renderHtmlElement(node, key, ctx);
|
|
507
718
|
case "htmlText":
|
|
508
|
-
return /* @__PURE__ */
|
|
719
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children: node.value }, key);
|
|
509
720
|
case "htmlComment":
|
|
510
721
|
return null;
|
|
511
722
|
default:
|
|
@@ -527,8 +738,8 @@ function MarkdownRenderer({
|
|
|
527
738
|
}) {
|
|
528
739
|
const ambientFenceRenderers = useFenceRenderers();
|
|
529
740
|
const mediaProvider = useMediaProvider();
|
|
530
|
-
const [accessoryPaths, setAccessoryPaths] =
|
|
531
|
-
|
|
741
|
+
const [accessoryPaths, setAccessoryPaths] = useState2(NO_ACCESSORY_PATHS);
|
|
742
|
+
useEffect2(() => {
|
|
532
743
|
if (!mediaProvider) {
|
|
533
744
|
setAccessoryPaths(clearAccessoryPaths);
|
|
534
745
|
return;
|
|
@@ -547,7 +758,7 @@ function MarkdownRenderer({
|
|
|
547
758
|
};
|
|
548
759
|
}, [mediaProvider]);
|
|
549
760
|
if (!nodes || nodes.length === 0) return null;
|
|
550
|
-
return /* @__PURE__ */
|
|
761
|
+
return /* @__PURE__ */ jsx4("div", { className: `squisq-md ${className || ""}`, children: renderBlocks(nodes, "", {
|
|
551
762
|
htmlPolicy,
|
|
552
763
|
linkSchemes,
|
|
553
764
|
theme,
|
|
@@ -563,5 +774,7 @@ export {
|
|
|
563
774
|
useFenceRenderers,
|
|
564
775
|
InlineVideoPlayer,
|
|
565
776
|
InlineAudioPlayer,
|
|
777
|
+
AnimatedImageControls,
|
|
778
|
+
useAnimatedImage,
|
|
566
779
|
MarkdownRenderer
|
|
567
780
|
};
|