@bendyline/squisq 2.12.1 → 2.12.3

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 (41) hide show
  1. package/dist/{Doc-CHD4fxfm.d.ts → Doc-BkSiCJnM.d.ts} +28 -2
  2. package/dist/{ImageEditDoc-DSUM5MiA.d.ts → ImageEditDoc-UjQv8q3j.d.ts} +1 -1
  3. package/dist/{chunk-WAX4QYHA.js → chunk-E2NFHYCB.js} +14 -3
  4. package/dist/{chunk-H54A35NZ.js → chunk-EX75I5HX.js} +3 -3
  5. package/dist/{chunk-3H7SYCZS.js → chunk-FTN6SGW5.js} +1 -1
  6. package/dist/{chunk-QHNO67JS.js → chunk-GX3VSKYS.js} +1 -1
  7. package/dist/{chunk-I4L4GYDN.js → chunk-HYJ63N6V.js} +1 -1
  8. package/dist/{chunk-ISH4HDH7.js → chunk-J6AGNXFO.js} +1 -1
  9. package/dist/{chunk-B3AFOXMA.js → chunk-MPEGJKFJ.js} +1031 -3
  10. package/dist/chunk-MWSGNEH6.js +2376 -0
  11. package/dist/{chunk-M6YXFNGO.js → chunk-P53Z4RYC.js} +10 -0
  12. package/dist/{chunk-B2BQFF5B.js → chunk-SK5TQSYN.js} +20 -20
  13. package/dist/{chunk-RKCARBWM.js → chunk-VTVBSZXB.js} +78 -1048
  14. package/dist/chunk-Y25EJEZN.js +178 -0
  15. package/dist/{chunk-LN72K54P.js → chunk-ZBPWNH4L.js} +10 -9
  16. package/dist/{chunk-VTG7PNOL.js → chunk-ZKDJHO6Y.js} +3 -8
  17. package/dist/doc/index.d.ts +17 -9
  18. package/dist/doc/index.js +41 -39
  19. package/dist/fence/index.d.ts +1 -1
  20. package/dist/generate/index.d.ts +1 -1
  21. package/dist/imageEdit/index.d.ts +3 -3
  22. package/dist/index.d.ts +6 -6
  23. package/dist/index.js +68 -48
  24. package/dist/jsonForm/index.d.ts +1 -1
  25. package/dist/jsonForm/index.js +4 -4
  26. package/dist/markdown/index.d.ts +143 -1
  27. package/dist/markdown/index.js +29 -10
  28. package/dist/{materializePageSection-Dx1Sz0vK.d.ts → materializePageSection-DEXohgVt.d.ts} +1 -1
  29. package/dist/mediaEdit/index.d.ts +2 -2
  30. package/dist/narration/index.d.ts +1 -1
  31. package/dist/narration/index.js +8 -8
  32. package/dist/recommend/index.js +3 -2
  33. package/dist/schemas/index.d.ts +4 -4
  34. package/dist/schemas/index.js +3 -3
  35. package/dist/{themeLibrary-DLmZ1Ct8.d.ts → themeLibrary-BrBc0mrP.d.ts} +1 -1
  36. package/dist/timing/index.d.ts +1 -1
  37. package/dist/transform/index.d.ts +2 -2
  38. package/dist/transform/index.js +8 -8
  39. package/package.json +1 -1
  40. package/dist/chunk-NNJA7ICB.js +0 -1088
  41. package/dist/chunk-ZYWYSM6W.js +0 -657
@@ -0,0 +1,2376 @@
1
+ import {
2
+ BUILD_EFFECTS,
3
+ BUILD_MODES,
4
+ BUILD_PREVIOUS,
5
+ SVG_ISLAND_LIMITS,
6
+ TRANSITION_DIRECTIONS,
7
+ TRANSITION_TYPES,
8
+ normalizeTransitionDirection,
9
+ normalizeTransitionType
10
+ } from "./chunk-P53Z4RYC.js";
11
+
12
+ // src/markdown/mediaReference.ts
13
+ var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set(["mp4", "m4v", "mov", "webm", "ogv"]);
14
+ var AUDIO_EXTENSIONS = /* @__PURE__ */ new Set(["mp3", "m4a", "aac", "wav", "ogg", "oga", "opus", "flac"]);
15
+ var CONTAINER_EXTENSIONS = /* @__PURE__ */ new Set(["webm", "mp4"]);
16
+ function mediaKindForUrl(url) {
17
+ const path = url.split(/[?#]/, 1)[0] ?? "";
18
+ const name = path.slice(path.lastIndexOf("/") + 1);
19
+ const dot = name.lastIndexOf(".");
20
+ if (dot <= 0) return null;
21
+ const ext = name.slice(dot + 1).toLowerCase();
22
+ if (AUDIO_EXTENSIONS.has(ext)) return "audio";
23
+ if (CONTAINER_EXTENSIONS.has(ext) && /^(?:\.\/)?audio\//i.test(path)) return "audio";
24
+ if (VIDEO_EXTENSIONS.has(ext)) return "video";
25
+ return null;
26
+ }
27
+
28
+ // src/markdown/htmlParse.ts
29
+ import { fromHtml } from "hast-util-from-html";
30
+ var HAST_PROP_TO_ATTR = {
31
+ className: "class",
32
+ htmlFor: "for",
33
+ httpEquiv: "http-equiv",
34
+ acceptCharset: "accept-charset"
35
+ };
36
+ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
37
+ "area",
38
+ "base",
39
+ "br",
40
+ "col",
41
+ "embed",
42
+ "hr",
43
+ "img",
44
+ "input",
45
+ "link",
46
+ "meta",
47
+ "param",
48
+ "source",
49
+ "track",
50
+ "wbr"
51
+ ]);
52
+ function propertyToAttribute(prop) {
53
+ if (HAST_PROP_TO_ATTR[prop]) return HAST_PROP_TO_ATTR[prop];
54
+ if (prop.length > 4 && prop.startsWith("data") && prop[4] >= "A" && prop[4] <= "Z") {
55
+ return "data-" + prop.slice(4).replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()).replace(/^-/, "");
56
+ }
57
+ if (prop.length > 4 && prop.startsWith("aria") && prop[4] >= "A" && prop[4] <= "Z") {
58
+ return "aria-" + prop.slice(4).replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()).replace(/^-/, "");
59
+ }
60
+ return prop.toLowerCase();
61
+ }
62
+ function convertHastProperties(properties) {
63
+ const attrs = {};
64
+ for (const [key, value] of Object.entries(properties)) {
65
+ if (value === false || value === void 0 || value === null) continue;
66
+ const attrName = propertyToAttribute(key);
67
+ if (value === true) {
68
+ attrs[attrName] = "";
69
+ } else if (Array.isArray(value)) {
70
+ attrs[attrName] = value.join(" ");
71
+ } else {
72
+ attrs[attrName] = String(value);
73
+ }
74
+ }
75
+ return attrs;
76
+ }
77
+ function hastChildrenToNodes(children) {
78
+ const result = [];
79
+ for (const child of children) {
80
+ const node = hastNodeToHtmlNode(child);
81
+ if (node) result.push(node);
82
+ }
83
+ return result;
84
+ }
85
+ function hastNodeToHtmlNode(node) {
86
+ switch (node.type) {
87
+ case "element": {
88
+ const tagName = node.tagName;
89
+ const properties = node.properties ?? {};
90
+ const children = node.children ?? [];
91
+ return {
92
+ type: "htmlElement",
93
+ tagName,
94
+ attributes: convertHastProperties(properties),
95
+ children: hastChildrenToNodes(children),
96
+ selfClosing: VOID_ELEMENTS.has(tagName) && children.length === 0
97
+ };
98
+ }
99
+ case "text":
100
+ return {
101
+ type: "htmlText",
102
+ value: node.value
103
+ };
104
+ case "comment":
105
+ return {
106
+ type: "htmlComment",
107
+ value: node.value
108
+ };
109
+ case "doctype":
110
+ return null;
111
+ default:
112
+ return null;
113
+ }
114
+ }
115
+ function parseHtmlToNodes(html) {
116
+ if (!html || !html.trim()) return [];
117
+ try {
118
+ const tree = fromHtml(html, { fragment: true });
119
+ return hastChildrenToNodes(tree.children);
120
+ } catch {
121
+ return [{ type: "htmlText", value: html }];
122
+ }
123
+ }
124
+ function escapeHtmlAttr(value) {
125
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
126
+ }
127
+ function escapeHtmlText(value) {
128
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
129
+ }
130
+ var RAW_TEXT_ELEMENTS = /* @__PURE__ */ new Set(["script", "style"]);
131
+ function stringifyNode(node, rawText = false) {
132
+ switch (node.type) {
133
+ case "htmlElement": {
134
+ const attrs = Object.entries(node.attributes).map(([k, v]) => v === "" ? k : `${k}="${escapeHtmlAttr(v)}"`).join(" ");
135
+ const open = attrs ? `<${node.tagName} ${attrs}>` : `<${node.tagName}>`;
136
+ if (node.selfClosing) return open;
137
+ const raw = RAW_TEXT_ELEMENTS.has(node.tagName.toLowerCase());
138
+ const inner = node.children.map((child) => stringifyNode(child, raw)).join("");
139
+ return `${open}${inner}</${node.tagName}>`;
140
+ }
141
+ case "htmlText":
142
+ return rawText ? node.value : escapeHtmlText(node.value);
143
+ case "htmlComment":
144
+ return `<!--${node.value}-->`;
145
+ }
146
+ }
147
+ function stringifyHtmlNodes(nodes) {
148
+ return nodes.map((node) => stringifyNode(node)).join("");
149
+ }
150
+
151
+ // src/markdown/utils.ts
152
+ function getChildren(node) {
153
+ if ("children" in node && Array.isArray(node.children)) {
154
+ return node.children;
155
+ }
156
+ return [];
157
+ }
158
+ function walkMarkdownTree(node, visitor, parent) {
159
+ const skip = visitor(node, parent);
160
+ if (skip === true) return;
161
+ const children = getChildren(node);
162
+ for (const child of children) {
163
+ walkMarkdownTree(child, visitor, node);
164
+ }
165
+ }
166
+ function findNodesByType(root, type) {
167
+ const results = [];
168
+ walkMarkdownTree(root, (node) => {
169
+ if (node.type === type) {
170
+ results.push(node);
171
+ }
172
+ });
173
+ return results;
174
+ }
175
+ function extractPlainText(node) {
176
+ if ("value" in node && typeof node.value === "string") {
177
+ return node.value;
178
+ }
179
+ const children = getChildren(node);
180
+ const separator = node.type === "list" || node.type === "listItem" || node.type === "table" ? "\n" : node.type === "tableRow" ? " " : "";
181
+ return children.map(extractPlainText).join(separator);
182
+ }
183
+ function countNodes(node) {
184
+ let count = 1;
185
+ const children = getChildren(node);
186
+ for (const child of children) {
187
+ count += countNodes(child);
188
+ }
189
+ return count;
190
+ }
191
+ var YAML_TRUE = /* @__PURE__ */ new Set(["true", "True", "TRUE"]);
192
+ var YAML_FALSE = /* @__PURE__ */ new Set(["false", "False", "FALSE"]);
193
+ var YAML_NULL = /* @__PURE__ */ new Set(["null", "Null", "NULL", "~"]);
194
+ var YAML_CANONICAL_NUMBER_RE = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:e[+-][0-9]+)?$/;
195
+ function isCanonicalNumber(text) {
196
+ return YAML_CANONICAL_NUMBER_RE.test(text) && String(Number(text)) === text;
197
+ }
198
+ function assignKey(map, key, value) {
199
+ Object.defineProperty(map, key, {
200
+ value,
201
+ enumerable: true,
202
+ configurable: true,
203
+ writable: true
204
+ });
205
+ }
206
+ function indentOf(line) {
207
+ return line.length - line.trimStart().length;
208
+ }
209
+ function isSkippableLine(line) {
210
+ const trimmed = line.trim();
211
+ return trimmed === "" || trimmed.startsWith("#");
212
+ }
213
+ function nextSignificant(cur) {
214
+ let j = cur.i;
215
+ while (j < cur.lines.length && isSkippableLine(cur.lines[j])) j++;
216
+ return j;
217
+ }
218
+ function isSequenceItem(content) {
219
+ return content === "-" || content.startsWith("- ");
220
+ }
221
+ function readQuotedScalar(text) {
222
+ const quote = text[0];
223
+ if (quote === '"') {
224
+ for (let i = 1; i < text.length; i++) {
225
+ if (text[i] === "\\") {
226
+ i++;
227
+ continue;
228
+ }
229
+ if (text[i] !== '"') continue;
230
+ const raw = text.slice(0, i + 1);
231
+ try {
232
+ return { value: JSON.parse(raw), rest: text.slice(i + 1) };
233
+ } catch {
234
+ return { value: raw.slice(1, -1), rest: text.slice(i + 1) };
235
+ }
236
+ }
237
+ return null;
238
+ }
239
+ if (quote === "'") {
240
+ let value = "";
241
+ for (let i = 1; i < text.length; i++) {
242
+ if (text[i] !== "'") {
243
+ value += text[i];
244
+ continue;
245
+ }
246
+ if (text[i + 1] === "'") {
247
+ value += "'";
248
+ i++;
249
+ continue;
250
+ }
251
+ return { value, rest: text.slice(i + 1) };
252
+ }
253
+ return null;
254
+ }
255
+ return null;
256
+ }
257
+ function splitMappingKey(content) {
258
+ if (content.startsWith("{") || content.startsWith("[")) return null;
259
+ if (content.startsWith('"') || content.startsWith("'")) {
260
+ const quoted = readQuotedScalar(content);
261
+ if (!quoted) return null;
262
+ const after = quoted.rest;
263
+ if (after === ":" || after.startsWith(": ") || after.startsWith(": ")) {
264
+ return { key: quoted.value, rest: after.slice(1).trim() };
265
+ }
266
+ return null;
267
+ }
268
+ for (let i = 0; i < content.length; i++) {
269
+ if (content[i] !== ":") continue;
270
+ const next = content[i + 1];
271
+ if (next !== void 0 && next !== " " && next !== " ") continue;
272
+ const key = content.slice(0, i).trim();
273
+ if (!key) return null;
274
+ return { key, rest: content.slice(i + 1).trim() };
275
+ }
276
+ const colon = content.indexOf(":");
277
+ if (colon > 0 && content[colon + 1] !== "/") {
278
+ const key = content.slice(0, colon).trim();
279
+ if (key && !key.includes(" ")) return { key, rest: content.slice(colon + 1).trim() };
280
+ }
281
+ return null;
282
+ }
283
+ function foldBlockLines(lines) {
284
+ const parts = [];
285
+ let i = 0;
286
+ while (i < lines.length) {
287
+ const line = lines[i];
288
+ if (line === "") {
289
+ parts.push("\n");
290
+ i++;
291
+ continue;
292
+ }
293
+ if (/^\s/.test(line)) {
294
+ parts.push((parts.length > 0 ? "\n" : "") + line);
295
+ i++;
296
+ continue;
297
+ }
298
+ parts.push(line);
299
+ i++;
300
+ while (i < lines.length && lines[i] !== "" && !/^\s/.test(lines[i])) {
301
+ parts.push(` ${lines[i]}`);
302
+ i++;
303
+ }
304
+ }
305
+ return parts.join("");
306
+ }
307
+ function parseBlockScalar(cur, keyIndent, folded, chomp) {
308
+ const blockLines = [];
309
+ let j = cur.i;
310
+ for (; j < cur.lines.length; j++) {
311
+ const line = cur.lines[j];
312
+ if (line.trim() === "") {
313
+ blockLines.push("");
314
+ continue;
315
+ }
316
+ if (indentOf(line) <= keyIndent) break;
317
+ blockLines.push(line);
318
+ }
319
+ cur.i = j;
320
+ if (chomp !== "+") {
321
+ while (blockLines.length > 0 && blockLines[blockLines.length - 1] === "") blockLines.pop();
322
+ }
323
+ let commonIndent = Infinity;
324
+ for (const line of blockLines) {
325
+ if (line === "") continue;
326
+ commonIndent = Math.min(commonIndent, indentOf(line));
327
+ }
328
+ if (!isFinite(commonIndent)) commonIndent = 0;
329
+ const dedented = blockLines.map((line) => line === "" ? "" : line.slice(commonIndent));
330
+ return folded ? foldBlockLines(dedented) : dedented.join("\n");
331
+ }
332
+ function scalarFromText(text) {
333
+ if (text === "" || YAML_NULL.has(text)) return null;
334
+ if (YAML_TRUE.has(text)) return true;
335
+ if (YAML_FALSE.has(text)) return false;
336
+ if (isCanonicalNumber(text)) return Number(text);
337
+ return text;
338
+ }
339
+ function collectPlainContinuation(cur, keyIndent, first) {
340
+ const parts = [first];
341
+ while (cur.i < cur.lines.length) {
342
+ const line = cur.lines[cur.i];
343
+ if (isSkippableLine(line)) break;
344
+ if (indentOf(line) <= keyIndent) break;
345
+ parts.push(line.trim());
346
+ cur.i++;
347
+ }
348
+ return parts.join(" ");
349
+ }
350
+ function parseValue(cur, keyIndent, inline) {
351
+ const blockScalar = /^([|>])([0-9]*)([+-]?)$/.exec(inline);
352
+ if (blockScalar) {
353
+ return parseBlockScalar(cur, keyIndent, blockScalar[1] === ">", blockScalar[3]);
354
+ }
355
+ if (inline === "") return parseNodeBelowKey(cur, keyIndent);
356
+ if (inline.startsWith('"') || inline.startsWith("'")) {
357
+ const quoted = readQuotedScalar(inline);
358
+ if (quoted && quoted.rest.trim() === "") return quoted.value;
359
+ const quote = inline[0];
360
+ if (inline.length >= 2 && inline.endsWith(quote)) {
361
+ const body = inline.slice(1, -1);
362
+ return quote === "'" ? body.replace(/''/g, "'") : body;
363
+ }
364
+ return inline;
365
+ }
366
+ if (inline === "[]") return [];
367
+ if (inline === "{}") return {};
368
+ if (inline.startsWith("{") || inline.startsWith("[")) return inline;
369
+ return scalarFromText(collectPlainContinuation(cur, keyIndent, inline));
370
+ }
371
+ function parseNodeBelowKey(cur, keyIndent) {
372
+ const j = nextSignificant(cur);
373
+ if (j >= cur.lines.length) return null;
374
+ const line = cur.lines[j];
375
+ const ind = indentOf(line);
376
+ const content = line.slice(ind);
377
+ if (isSequenceItem(content) && ind >= keyIndent) {
378
+ cur.i = j;
379
+ return parseSequence(cur, ind);
380
+ }
381
+ if (ind > keyIndent) {
382
+ cur.i = j;
383
+ if (splitMappingKey(content)) return parseMapping(cur, ind);
384
+ cur.i = j + 1;
385
+ return scalarFromText(collectPlainContinuation(cur, keyIndent, content.trim()));
386
+ }
387
+ return null;
388
+ }
389
+ function parseNodeBelowDash(cur, dashIndent) {
390
+ const j = nextSignificant(cur);
391
+ if (j >= cur.lines.length) return null;
392
+ const line = cur.lines[j];
393
+ const ind = indentOf(line);
394
+ if (ind <= dashIndent) return null;
395
+ const content = line.slice(ind);
396
+ cur.i = j;
397
+ if (isSequenceItem(content)) return parseSequence(cur, ind);
398
+ if (splitMappingKey(content)) return parseMapping(cur, ind);
399
+ cur.i = j + 1;
400
+ return scalarFromText(collectPlainContinuation(cur, dashIndent, content.trim()));
401
+ }
402
+ function parseSequence(cur, indent) {
403
+ const items = [];
404
+ for (; ; ) {
405
+ const j = nextSignificant(cur);
406
+ if (j >= cur.lines.length) {
407
+ cur.i = j;
408
+ break;
409
+ }
410
+ const line = cur.lines[j];
411
+ const ind = indentOf(line);
412
+ if (ind !== indent) break;
413
+ const content = line.slice(ind);
414
+ if (!isSequenceItem(content)) break;
415
+ const afterDash = content.slice(1);
416
+ const itemText = afterDash.trimStart();
417
+ if (itemText === "") {
418
+ cur.i = j + 1;
419
+ items.push(parseNodeBelowDash(cur, ind));
420
+ continue;
421
+ }
422
+ const itemCol = ind + 1 + (afterDash.length - itemText.length);
423
+ const entry = splitMappingKey(itemText);
424
+ if (entry) {
425
+ cur.lines[j] = " ".repeat(itemCol) + itemText;
426
+ cur.i = j;
427
+ items.push(parseMapping(cur, itemCol));
428
+ continue;
429
+ }
430
+ cur.i = j + 1;
431
+ items.push(parseValue(cur, ind, itemText));
432
+ }
433
+ return items;
434
+ }
435
+ function parseMapping(cur, indent) {
436
+ const map = {};
437
+ for (; ; ) {
438
+ const j = nextSignificant(cur);
439
+ if (j >= cur.lines.length) {
440
+ cur.i = j;
441
+ break;
442
+ }
443
+ const line = cur.lines[j];
444
+ const ind = indentOf(line);
445
+ if (ind !== indent) break;
446
+ const content = line.slice(ind);
447
+ if (isSequenceItem(content)) break;
448
+ const entry = splitMappingKey(content);
449
+ if (!entry) break;
450
+ cur.i = j + 1;
451
+ assignKey(map, entry.key, parseValue(cur, indent, entry.rest));
452
+ }
453
+ return map;
454
+ }
455
+ function parseFrontmatter(yaml) {
456
+ if (!yaml || !yaml.trim()) return null;
457
+ const cur = { lines: yaml.replace(/\r\n?/g, "\n").split("\n"), i: 0 };
458
+ const result = {};
459
+ for (; ; ) {
460
+ const j = nextSignificant(cur);
461
+ if (j >= cur.lines.length) break;
462
+ cur.i = j;
463
+ const map = parseMapping(cur, indentOf(cur.lines[j]));
464
+ for (const [key, value] of Object.entries(map)) assignKey(result, key, value);
465
+ if (cur.i <= j) cur.i = j + 1;
466
+ }
467
+ return Object.keys(result).length > 0 ? result : null;
468
+ }
469
+ function formatBlockScalar(value) {
470
+ const body = value.split("\n").map((line) => line === "" ? "" : ` ${line}`).join("\n");
471
+ return `|-
472
+ ${body}`;
473
+ }
474
+ var FRONTMATTER_BLOCK_RE = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n)?/;
475
+ var EMPTY_FRONTMATTER_BLOCK_RE = /^---\r?\n---(\r?\n)?/;
476
+ function splitFrontmatterBlock(source) {
477
+ const match = FRONTMATTER_BLOCK_RE.exec(source) ?? EMPTY_FRONTMATTER_BLOCK_RE.exec(source);
478
+ if (!match) return { frontmatter: null, body: source };
479
+ return { frontmatter: match[0], body: source.slice(match[0].length) };
480
+ }
481
+ function formatFrontmatterValue(value) {
482
+ if (typeof value === "boolean" || typeof value === "number") return String(value);
483
+ if (/[\r\n]/.test(value)) return formatBlockScalar(value.replace(/\r\n?/g, "\n"));
484
+ if (value !== "[]" && value !== "{}" && /^[{[]/.test(value)) return value;
485
+ const needsQuote = /^\s|\s$/.test(value) || /^[!&*?|>%@`~]/.test(value) || /[:#]/.test(value) || value === "" || value === "[]" || value === "{}" || YAML_TRUE.has(value) || YAML_FALSE.has(value) || YAML_NULL.has(value) || value !== "" && !Number.isNaN(Number(value)) || /^["']/.test(value) && /["']$/.test(value);
486
+ if (!needsQuote) return value;
487
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
488
+ return `"${escaped}"`;
489
+ }
490
+ function formatFrontmatterKey(key) {
491
+ return /^[A-Za-z0-9_](?:[A-Za-z0-9 _.-]*[A-Za-z0-9_.-])?$/.test(key) ? key : formatFrontmatterValue(key);
492
+ }
493
+ function isPlainRecord(value) {
494
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
495
+ const proto = Object.getPrototypeOf(value);
496
+ return proto === Object.prototype || proto === null;
497
+ }
498
+ function isOmittedValue(value) {
499
+ return value === void 0 || typeof value === "function" || typeof value === "symbol";
500
+ }
501
+ function toScalar(value) {
502
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
503
+ return value;
504
+ }
505
+ if (value instanceof Date) return value.toISOString();
506
+ return String(value);
507
+ }
508
+ function pushScalar(out, pad, prefix, value) {
509
+ const formatted = formatFrontmatterValue(toScalar(value));
510
+ if (!formatted.includes("\n")) {
511
+ out.push(`${pad}${prefix}${formatted}`);
512
+ return;
513
+ }
514
+ const [head, ...body] = formatted.split("\n");
515
+ out.push(`${pad}${prefix}${head}`);
516
+ for (const line of body) out.push(line === "" ? "" : `${pad}${line}`);
517
+ }
518
+ function emitSequence(items, indent, out) {
519
+ const pad = " ".repeat(indent);
520
+ for (const item of items) {
521
+ if (item === null || isOmittedValue(item)) {
522
+ out.push(`${pad}- null`);
523
+ continue;
524
+ }
525
+ if (Array.isArray(item)) {
526
+ if (item.length === 0) {
527
+ out.push(`${pad}- []`);
528
+ continue;
529
+ }
530
+ out.push(`${pad}-`);
531
+ emitSequence(item, indent + 2, out);
532
+ continue;
533
+ }
534
+ if (isPlainRecord(item)) {
535
+ const sub = [];
536
+ emitMapping(item, indent + 2, sub);
537
+ if (sub.length === 0) {
538
+ out.push(`${pad}- {}`);
539
+ continue;
540
+ }
541
+ sub[0] = `${pad}- ${sub[0].slice(indent + 2)}`;
542
+ out.push(...sub);
543
+ continue;
544
+ }
545
+ pushScalar(out, pad, "- ", item);
546
+ }
547
+ }
548
+ function emitMapping(map, indent, out) {
549
+ const pad = " ".repeat(indent);
550
+ for (const [rawKey, value] of Object.entries(map)) {
551
+ if (isOmittedValue(value)) continue;
552
+ const key = formatFrontmatterKey(rawKey);
553
+ if (value === null) {
554
+ out.push(`${pad}${key}: null`);
555
+ continue;
556
+ }
557
+ if (Array.isArray(value)) {
558
+ if (value.length === 0) {
559
+ out.push(`${pad}${key}: []`);
560
+ continue;
561
+ }
562
+ out.push(`${pad}${key}:`);
563
+ emitSequence(value, indent + 2, out);
564
+ continue;
565
+ }
566
+ if (isPlainRecord(value)) {
567
+ const sub = [];
568
+ emitMapping(value, indent + 2, sub);
569
+ if (sub.length === 0) {
570
+ out.push(`${pad}${key}: {}`);
571
+ continue;
572
+ }
573
+ out.push(`${pad}${key}:`);
574
+ out.push(...sub);
575
+ continue;
576
+ }
577
+ pushScalar(out, pad, `${key}: `, value);
578
+ }
579
+ }
580
+ function formatFrontmatterYaml(frontmatter) {
581
+ const out = [];
582
+ emitMapping(frontmatter, 0, out);
583
+ return out.join("\n");
584
+ }
585
+ function setFrontmatterValues(source, updates) {
586
+ const updateKeys = Object.keys(updates);
587
+ if (updateKeys.length === 0) return source;
588
+ const match = source.match(FRONTMATTER_BLOCK_RE);
589
+ const handled = /* @__PURE__ */ new Set();
590
+ if (match) {
591
+ const inner = match[1];
592
+ const lines = inner.split(/\r?\n/);
593
+ const newLines = [];
594
+ const firstSignificant = lines.find((line) => !isSkippableLine(line));
595
+ const rootIndent = firstSignificant === void 0 ? 0 : indentOf(firstSignificant);
596
+ let i = 0;
597
+ while (i < lines.length) {
598
+ const line = lines[i];
599
+ const ind = indentOf(line);
600
+ const entry = isSkippableLine(line) || ind !== rootIndent ? null : splitMappingKey(line.slice(ind));
601
+ if (!entry) {
602
+ newLines.push(line);
603
+ i++;
604
+ continue;
605
+ }
606
+ let end = i + 1;
607
+ while (end < lines.length) {
608
+ if (lines[end].trim() === "") {
609
+ let probe = end;
610
+ while (probe < lines.length && lines[probe].trim() === "") probe++;
611
+ if (probe < lines.length && indentOf(lines[probe]) > rootIndent) {
612
+ end = probe;
613
+ continue;
614
+ }
615
+ break;
616
+ }
617
+ if (indentOf(lines[end]) <= rootIndent) break;
618
+ end++;
619
+ }
620
+ if (!Object.prototype.hasOwnProperty.call(updates, entry.key)) {
621
+ for (let k = i; k < end; k++) newLines.push(lines[k]);
622
+ i = end;
623
+ continue;
624
+ }
625
+ handled.add(entry.key);
626
+ const next = updates[entry.key];
627
+ if (next !== null && next !== void 0) {
628
+ pushScalar(newLines, " ".repeat(rootIndent), `${formatFrontmatterKey(entry.key)}: `, next);
629
+ }
630
+ i = end;
631
+ }
632
+ for (const key of updateKeys) {
633
+ if (handled.has(key)) continue;
634
+ const next = updates[key];
635
+ if (next === null || next === void 0) continue;
636
+ pushScalar(newLines, "", `${formatFrontmatterKey(key)}: `, next);
637
+ }
638
+ const nonEmpty = newLines.filter((l) => l.trim().length > 0);
639
+ const rest = source.slice(match[0].length);
640
+ if (nonEmpty.length === 0) return rest;
641
+ return `---
642
+ ${newLines.join("\n")}
643
+ ---
644
+ ${rest}`;
645
+ }
646
+ const fresh = [];
647
+ for (const key of updateKeys) {
648
+ const next = updates[key];
649
+ if (next === null || next === void 0) continue;
650
+ pushScalar(fresh, "", `${formatFrontmatterKey(key)}: `, next);
651
+ }
652
+ if (fresh.length === 0) return source;
653
+ return `---
654
+ ${fresh.join("\n")}
655
+ ---
656
+ ${source}`;
657
+ }
658
+ function createDocument(...children) {
659
+ return {
660
+ type: "document",
661
+ children
662
+ };
663
+ }
664
+ function inferDocumentTitle(doc) {
665
+ const fmTitle = doc.frontmatter?.title;
666
+ if (typeof fmTitle === "string") {
667
+ const trimmed = fmTitle.trim();
668
+ if (trimmed) return trimmed;
669
+ }
670
+ let best = null;
671
+ for (const node of doc.children) {
672
+ if (node.type !== "heading") continue;
673
+ const text = extractPlainText(node).trim();
674
+ if (!text) continue;
675
+ if (best === null || node.depth < best.depth) {
676
+ best = { depth: node.depth, text };
677
+ if (best.depth === 1) break;
678
+ }
679
+ }
680
+ return best?.text;
681
+ }
682
+ function readFrontmatterThemeId(frontmatter) {
683
+ if (!frontmatter) return void 0;
684
+ const keys = ["squisq-theme", "themeId", "theme"];
685
+ for (const key of keys) {
686
+ const raw = frontmatter[key];
687
+ if (typeof raw === "string") {
688
+ const trimmed = raw.trim();
689
+ if (trimmed) return trimmed;
690
+ }
691
+ }
692
+ return void 0;
693
+ }
694
+ function plainTextFromInlineHtml(html) {
695
+ const BLOCK = /* @__PURE__ */ new Set([
696
+ "p",
697
+ "div",
698
+ "h1",
699
+ "h2",
700
+ "h3",
701
+ "h4",
702
+ "h5",
703
+ "h6",
704
+ "li",
705
+ "blockquote",
706
+ "pre"
707
+ ]);
708
+ const walk = (nodes) => nodes.map((n) => {
709
+ if (n.type === "htmlText") return n.value;
710
+ if (n.type !== "htmlElement") return "";
711
+ const tag = n.tagName.toLowerCase();
712
+ if (tag === "br") return "\n";
713
+ const inner = walk(n.children);
714
+ return BLOCK.has(tag) ? `${inner}
715
+ ` : inner;
716
+ }).join("");
717
+ return walk(parseHtmlToNodes(html)).replace(/\u00A0/g, " ").replace(/\n{3,}/g, "\n\n").trim();
718
+ }
719
+
720
+ // src/markdown/annotationCoercion.ts
721
+ var KNOWN_BLOCK_META_KEYS = {
722
+ build: "build",
723
+ buildEffect: "buildEffect",
724
+ buildDuration: "time",
725
+ buildPrevious: "buildPrevious",
726
+ x: "number",
727
+ y: "number",
728
+ startTime: "time",
729
+ duration: "time",
730
+ connectsTo: "connectionList",
731
+ transition: "transition",
732
+ transitionDuration: "time",
733
+ transitionDirection: "transitionDirection"
734
+ };
735
+ var BLOCK_META_KEY_DESCRIPTORS = [
736
+ { key: "build", description: "Progressive reveal grouping", values: BUILD_MODES },
737
+ { key: "buildEffect", description: "Build entrance effect", values: BUILD_EFFECTS },
738
+ {
739
+ key: "buildDuration",
740
+ description: "Build entrance duration",
741
+ valueHint: "seconds \u2014 e.g. 0.4, 400ms"
742
+ },
743
+ {
744
+ key: "buildPrevious",
745
+ description: "Appearance of previous build steps",
746
+ values: BUILD_PREVIOUS
747
+ },
748
+ {
749
+ key: "transition",
750
+ description: "Block-to-block transition effect",
751
+ values: TRANSITION_TYPES
752
+ },
753
+ {
754
+ key: "transitionDuration",
755
+ description: "How long the transition lasts",
756
+ valueHint: "seconds \u2014 e.g. 0.7, 700ms"
757
+ },
758
+ {
759
+ key: "transitionDirection",
760
+ description: "Directional variant for the transition",
761
+ values: TRANSITION_DIRECTIONS
762
+ },
763
+ {
764
+ key: "startTime",
765
+ description: "Timeline start time of this block",
766
+ valueHint: "mm:ss or seconds \u2014 e.g. 01:30, 5, 1500ms"
767
+ },
768
+ {
769
+ key: "duration",
770
+ description: "How long this block lasts",
771
+ valueHint: "mm:ss or seconds \u2014 e.g. 45, 1500ms"
772
+ },
773
+ {
774
+ key: "x",
775
+ description: "Horizontal position on the diagram canvas",
776
+ valueHint: "number \u2014 e.g. 600"
777
+ },
778
+ {
779
+ key: "y",
780
+ description: "Vertical position on the diagram canvas",
781
+ valueHint: "number \u2014 e.g. 300"
782
+ },
783
+ {
784
+ key: "connectsTo",
785
+ description: "Diagram connections to other blocks",
786
+ valueHint: "comma-separated target or target:type \u2014 e.g. foo,bar:flow"
787
+ }
788
+ ];
789
+ function coerceAnnotationValues(params) {
790
+ const blockMeta = {};
791
+ const metadata = {};
792
+ const warnings = [];
793
+ const build2 = {};
794
+ let transitionType = null;
795
+ let transitionDuration;
796
+ let transitionDirection;
797
+ for (const [key, raw] of Object.entries(params)) {
798
+ const kind = KNOWN_BLOCK_META_KEYS[key];
799
+ if (!kind) {
800
+ metadata[key] = raw;
801
+ continue;
802
+ }
803
+ if (kind === "number") {
804
+ const n = parseNumber(raw);
805
+ if (n == null) {
806
+ warnings.push(`Invalid number for "${key}": ${JSON.stringify(raw)}`);
807
+ } else {
808
+ blockMeta[key] = n;
809
+ }
810
+ } else if (kind === "time") {
811
+ const s = parseTimeSeconds(raw);
812
+ if (s == null) {
813
+ warnings.push(`Invalid time for "${key}": ${JSON.stringify(raw)}`);
814
+ } else if (key === "buildDuration") {
815
+ if (s < 0) warnings.push("Build duration must be non-negative.");
816
+ else build2.duration = s;
817
+ } else if (key === "transitionDuration") {
818
+ transitionDuration = s;
819
+ } else {
820
+ blockMeta[key] = s;
821
+ }
822
+ } else if (kind === "build" || kind === "buildEffect" || kind === "buildPrevious") {
823
+ const values = kind === "build" ? BUILD_MODES : kind === "buildEffect" ? BUILD_EFFECTS : BUILD_PREVIOUS;
824
+ if (!values.includes(raw))
825
+ warnings.push(`Invalid value for "${key}": ${JSON.stringify(raw)}`);
826
+ else if (kind === "build") build2.mode = raw;
827
+ else if (kind === "buildEffect") build2.effect = raw;
828
+ else build2.previous = raw;
829
+ } else if (kind === "connectionList") {
830
+ const { list, warning } = parseConnectionList(raw);
831
+ if (warning) warnings.push(`"${key}": ${warning}`);
832
+ blockMeta.connectsTo = list;
833
+ } else if (kind === "transition") {
834
+ const transition = normalizeTransitionType(raw);
835
+ if (transition == null) {
836
+ warnings.push(`Invalid transition for "${key}": ${JSON.stringify(raw)}`);
837
+ } else {
838
+ transitionType = transition;
839
+ }
840
+ } else if (kind === "transitionDirection") {
841
+ const direction = normalizeTransitionDirection(raw);
842
+ if (direction == null) {
843
+ warnings.push(`Invalid transition direction for "${key}": ${JSON.stringify(raw)}`);
844
+ } else {
845
+ transitionDirection = direction;
846
+ }
847
+ }
848
+ }
849
+ if (build2.mode) blockMeta.build = { ...build2, mode: build2.mode };
850
+ if (transitionType) {
851
+ blockMeta.transition = {
852
+ type: transitionType,
853
+ ...transitionDuration !== void 0 ? { duration: transitionDuration } : {},
854
+ ...transitionDirection !== void 0 ? { direction: transitionDirection } : {}
855
+ };
856
+ }
857
+ return { blockMeta, metadata, warnings };
858
+ }
859
+ function parseNumber(raw) {
860
+ if (raw.trim() === "") return null;
861
+ const n = Number(raw);
862
+ return Number.isFinite(n) ? n : null;
863
+ }
864
+ var TIME_BARE_RE = /^\d+(?:\.\d+)?$/;
865
+ var TIME_MS_RE = /^(\d+(?:\.\d+)?)ms$/;
866
+ var TIME_MMSS_RE = /^(\d+):(\d{1,2})(?:\.(\d+))?$/;
867
+ function parseTimeSeconds(raw) {
868
+ const trimmed = raw.trim();
869
+ if (!trimmed) return null;
870
+ if (TIME_BARE_RE.test(trimmed)) {
871
+ const n = Number(trimmed);
872
+ return Number.isFinite(n) ? n : null;
873
+ }
874
+ const msMatch = trimmed.match(TIME_MS_RE);
875
+ if (msMatch) {
876
+ const n = Number(msMatch[1]);
877
+ return Number.isFinite(n) ? n / 1e3 : null;
878
+ }
879
+ const mmssMatch = trimmed.match(TIME_MMSS_RE);
880
+ if (mmssMatch) {
881
+ const mins = Number(mmssMatch[1]);
882
+ const secs = Number(mmssMatch[2]);
883
+ const frac = mmssMatch[3] ? Number(`0.${mmssMatch[3]}`) : 0;
884
+ if (!Number.isFinite(mins) || !Number.isFinite(secs) || !Number.isFinite(frac)) {
885
+ return null;
886
+ }
887
+ if (secs >= 60) return null;
888
+ return mins * 60 + secs + frac;
889
+ }
890
+ return null;
891
+ }
892
+ function parseConnectionList(raw) {
893
+ const trimmed = raw.trim();
894
+ if (!trimmed) return { list: [], warning: null };
895
+ const list = [];
896
+ let droppedEmpty = false;
897
+ for (const part of trimmed.split(",")) {
898
+ const entry = part.trim();
899
+ if (!entry) {
900
+ droppedEmpty = true;
901
+ continue;
902
+ }
903
+ const colonIdx = entry.indexOf(":");
904
+ if (colonIdx < 0) {
905
+ list.push({ target: entry });
906
+ } else {
907
+ const target = entry.slice(0, colonIdx).trim();
908
+ const type = entry.slice(colonIdx + 1).trim();
909
+ if (!target) {
910
+ droppedEmpty = true;
911
+ continue;
912
+ }
913
+ list.push(type ? { target, type } : { target });
914
+ }
915
+ }
916
+ return {
917
+ list,
918
+ warning: droppedEmpty ? "dropped empty connection entries" : null
919
+ };
920
+ }
921
+
922
+ // src/markdown/videoEmbed.ts
923
+ var VIDEO_EMBED_PROVIDERS = [
924
+ { id: "youtube", name: "YouTube", example: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" },
925
+ { id: "vimeo", name: "Vimeo", example: "https://vimeo.com/76979871" },
926
+ {
927
+ id: "loom",
928
+ name: "Loom",
929
+ example: "https://www.loom.com/share/0281766fa2d04bb788eaf19e65135184"
930
+ },
931
+ { id: "dailymotion", name: "Dailymotion", example: "https://www.dailymotion.com/video/x7tgad0" },
932
+ { id: "wistia", name: "Wistia", example: "https://home.wistia.com/medias/e4a27b971d" }
933
+ ];
934
+ var PROVIDER_NAMES = Object.fromEntries(
935
+ VIDEO_EMBED_PROVIDERS.map((p) => [p.id, p.name])
936
+ );
937
+ var LANDSCAPE = 16 / 9;
938
+ var PORTRAIT = 9 / 16;
939
+ function unescapeMarkdown(value) {
940
+ return value.replace(/\\([!-/:-@[-`{-~])/g, "$1");
941
+ }
942
+ function toUrl(input) {
943
+ let text = unescapeMarkdown(input.trim());
944
+ if (text.startsWith("<") && text.endsWith(">")) text = text.slice(1, -1).trim();
945
+ if (!text || /\s/.test(text)) return null;
946
+ if (text.startsWith("//")) text = `https:${text}`;
947
+ else if (!/^[a-z][a-z0-9+.-]*:/i.test(text)) text = `https://${text}`;
948
+ let url;
949
+ try {
950
+ url = new URL(text);
951
+ } catch {
952
+ return null;
953
+ }
954
+ if (url.protocol !== "https:" && url.protocol !== "http:") return null;
955
+ if (url.username || url.password || url.port) return null;
956
+ return url;
957
+ }
958
+ function baseHost(url) {
959
+ return url.hostname.toLowerCase().replace(/^(?:www|m)\./, "");
960
+ }
961
+ function pathSegments(url) {
962
+ return url.pathname.split("/").filter(Boolean).map((segment) => {
963
+ try {
964
+ return decodeURIComponent(segment);
965
+ } catch {
966
+ return segment;
967
+ }
968
+ });
969
+ }
970
+ function fragmentParam(url, key) {
971
+ const hash = url.hash.replace(/^#/, "");
972
+ if (!hash) return null;
973
+ return new URLSearchParams(hash).get(key);
974
+ }
975
+ var HMS_RE = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s?)?$/;
976
+ var CLOCK_RE = /^(?:(\d+):)?(\d{1,2}):(\d{2})$/;
977
+ function parseVideoStartTime(raw) {
978
+ if (!raw) return void 0;
979
+ const value = raw.trim().toLowerCase();
980
+ if (!value) return void 0;
981
+ let total = null;
982
+ const hms = HMS_RE.exec(value);
983
+ if (hms && (hms[1] || hms[2] || hms[3])) {
984
+ total = Number(hms[1] ?? 0) * 3600 + Number(hms[2] ?? 0) * 60 + Number(hms[3] ?? 0);
985
+ } else {
986
+ const clock = CLOCK_RE.exec(value);
987
+ const minutes = Number(clock?.[2]);
988
+ const seconds = Number(clock?.[3]);
989
+ if (clock && seconds < 60 && (clock[1] === void 0 || minutes < 60)) {
990
+ total = Number(clock[1] ?? 0) * 3600 + minutes * 60 + seconds;
991
+ }
992
+ }
993
+ if (total === null || !Number.isFinite(total) || total <= 0) return void 0;
994
+ return Math.floor(total);
995
+ }
996
+ function withQuery(base, params) {
997
+ const query = params.filter((entry) => entry[1] !== void 0).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&");
998
+ return query ? `${base}?${query}` : base;
999
+ }
1000
+ function build(provider, fields) {
1001
+ const embed = {
1002
+ provider,
1003
+ providerName: PROVIDER_NAMES[provider],
1004
+ id: fields.id,
1005
+ embedUrl: fields.embedUrl,
1006
+ watchUrl: fields.watchUrl,
1007
+ aspectRatio: fields.aspectRatio ?? LANDSCAPE
1008
+ };
1009
+ if (fields.playlistId) embed.playlistId = fields.playlistId;
1010
+ if (fields.startSeconds) embed.startSeconds = fields.startSeconds;
1011
+ if (fields.thumbnailUrl) embed.thumbnailUrl = fields.thumbnailUrl;
1012
+ return embed;
1013
+ }
1014
+ var YOUTUBE_HOSTS = /* @__PURE__ */ new Set(["youtube.com", "music.youtube.com", "youtube-nocookie.com"]);
1015
+ var YOUTUBE_ID_RE = /^[A-Za-z0-9_-]{11}$/;
1016
+ var YOUTUBE_LIST_RE = /^[A-Za-z0-9_-]{2,80}$/;
1017
+ var YOUTUBE_ID_PATHS = /* @__PURE__ */ new Set(["embed", "v", "e", "live", "shorts"]);
1018
+ var YOUTUBE_EMBED_ORIGIN = "https://www.youtube-nocookie.com";
1019
+ function parseYouTube(url) {
1020
+ const host = baseHost(url);
1021
+ const segments = pathSegments(url);
1022
+ const params = url.searchParams;
1023
+ let id = null;
1024
+ let shorts = false;
1025
+ if (host === "youtu.be") {
1026
+ id = segments[0] ?? null;
1027
+ } else if (YOUTUBE_HOSTS.has(host)) {
1028
+ const [head, second] = segments;
1029
+ if (head === "watch") id = params.get("v");
1030
+ else if (head && YOUTUBE_ID_PATHS.has(head) && second !== "videoseries") {
1031
+ id = second ?? null;
1032
+ shorts = head === "shorts";
1033
+ } else if (head !== "playlist" && !(head === "embed" && second === "videoseries")) {
1034
+ return null;
1035
+ }
1036
+ } else {
1037
+ return null;
1038
+ }
1039
+ if (id !== null && !YOUTUBE_ID_RE.test(id)) return null;
1040
+ const list = params.get("list");
1041
+ const playlistId = list && YOUTUBE_LIST_RE.test(list) ? list : void 0;
1042
+ if (!id && !playlistId) return null;
1043
+ const startSeconds = parseVideoStartTime(
1044
+ params.get("t") ?? params.get("start") ?? fragmentParam(url, "t")
1045
+ );
1046
+ const start = startSeconds ? String(startSeconds) : void 0;
1047
+ if (!id) {
1048
+ return build("youtube", {
1049
+ id: playlistId,
1050
+ playlistId,
1051
+ embedUrl: withQuery(`${YOUTUBE_EMBED_ORIGIN}/embed/videoseries`, [["list", playlistId]]),
1052
+ watchUrl: withQuery("https://www.youtube.com/playlist", [["list", playlistId]])
1053
+ });
1054
+ }
1055
+ return build("youtube", {
1056
+ id,
1057
+ playlistId,
1058
+ startSeconds,
1059
+ embedUrl: withQuery(`${YOUTUBE_EMBED_ORIGIN}/embed/${id}`, [
1060
+ ["list", playlistId],
1061
+ ["start", start]
1062
+ ]),
1063
+ watchUrl: shorts ? `https://www.youtube.com/shorts/${id}` : withQuery("https://www.youtube.com/watch", [
1064
+ ["v", id],
1065
+ ["list", playlistId],
1066
+ ["t", start && `${start}s`]
1067
+ ]),
1068
+ thumbnailUrl: `https://i.ytimg.com/vi/${id}/hqdefault.jpg`,
1069
+ aspectRatio: shorts ? PORTRAIT : LANDSCAPE
1070
+ });
1071
+ }
1072
+ var VIMEO_ID_RE = /^\d{1,12}$/;
1073
+ var VIMEO_HASH_RE = /^[0-9a-f]{6,24}$/i;
1074
+ function vimeoIdFromSegments(segments) {
1075
+ const [a, b, c, d] = segments;
1076
+ if (a && VIMEO_ID_RE.test(a)) {
1077
+ return { id: a, ...b && VIMEO_HASH_RE.test(b) ? { hash: b } : {} };
1078
+ }
1079
+ if (a === "video" && b && VIMEO_ID_RE.test(b)) return { id: b };
1080
+ if (a === "channels" && c && VIMEO_ID_RE.test(c)) return { id: c };
1081
+ if (a === "groups" && c === "videos" && d && VIMEO_ID_RE.test(d)) return { id: d };
1082
+ if ((a === "album" || a === "showcase") && c === "video" && d && VIMEO_ID_RE.test(d)) {
1083
+ return { id: d };
1084
+ }
1085
+ return null;
1086
+ }
1087
+ function parseVimeo(url) {
1088
+ const host = baseHost(url);
1089
+ if (host !== "vimeo.com" && host !== "player.vimeo.com") return null;
1090
+ const found = vimeoIdFromSegments(pathSegments(url));
1091
+ if (!found) return null;
1092
+ const queryHash = url.searchParams.get("h");
1093
+ const hash = found.hash ?? (queryHash && VIMEO_HASH_RE.test(queryHash) ? queryHash : void 0);
1094
+ const startSeconds = parseVideoStartTime(
1095
+ fragmentParam(url, "t") ?? url.searchParams.get("t") ?? url.searchParams.get("start")
1096
+ );
1097
+ const timeFragment = startSeconds ? `#t=${startSeconds}s` : "";
1098
+ return build("vimeo", {
1099
+ id: found.id,
1100
+ startSeconds,
1101
+ embedUrl: withQuery(`https://player.vimeo.com/video/${found.id}`, [["h", hash]]) + timeFragment,
1102
+ watchUrl: `https://vimeo.com/${found.id}${hash ? `/${hash}` : ""}${timeFragment}`
1103
+ });
1104
+ }
1105
+ var LOOM_ID_RE = /(?:^|-)([0-9a-f]{32})$/i;
1106
+ function parseLoom(url) {
1107
+ if (baseHost(url) !== "loom.com") return null;
1108
+ const [head, second] = pathSegments(url);
1109
+ if (head !== "share" && head !== "embed" || !second) return null;
1110
+ const match = LOOM_ID_RE.exec(second);
1111
+ if (!match) return null;
1112
+ const id = match[1].toLowerCase();
1113
+ return build("loom", {
1114
+ id,
1115
+ embedUrl: `https://www.loom.com/embed/${id}`,
1116
+ watchUrl: `https://www.loom.com/share/${id}`
1117
+ });
1118
+ }
1119
+ var DAILYMOTION_ID_RE = /^[A-Za-z0-9]{5,20}$/;
1120
+ function parseDailymotion(url) {
1121
+ const host = baseHost(url);
1122
+ const segments = pathSegments(url);
1123
+ let raw = null;
1124
+ if (host === "dai.ly") raw = segments[0] ?? null;
1125
+ else if (host === "dailymotion.com") {
1126
+ if (segments[0] === "video") raw = segments[1] ?? null;
1127
+ else if (segments[0] === "embed" && segments[1] === "video") raw = segments[2] ?? null;
1128
+ else return null;
1129
+ } else if (host === "geo.dailymotion.com") {
1130
+ raw = url.searchParams.get("video");
1131
+ } else {
1132
+ return null;
1133
+ }
1134
+ const id = raw?.split("_")[0];
1135
+ if (!id || !DAILYMOTION_ID_RE.test(id)) return null;
1136
+ const startSeconds = parseVideoStartTime(url.searchParams.get("start"));
1137
+ const start = startSeconds ? String(startSeconds) : void 0;
1138
+ return build("dailymotion", {
1139
+ id,
1140
+ startSeconds,
1141
+ embedUrl: withQuery(`https://www.dailymotion.com/embed/video/${id}`, [["start", start]]),
1142
+ watchUrl: withQuery(`https://www.dailymotion.com/video/${id}`, [["start", start]]),
1143
+ thumbnailUrl: `https://www.dailymotion.com/thumbnail/video/${id}`
1144
+ });
1145
+ }
1146
+ var WISTIA_ID_RE = /^[a-z0-9]{10}$/;
1147
+ var WISTIA_ACCOUNT_HOST_RE = /^[a-z0-9-]{1,63}\.wistia\.com$/;
1148
+ function parseWistia(url) {
1149
+ const host = url.hostname.toLowerCase();
1150
+ const fastHost = host === "fast.wistia.net" || host === "fast.wistia.com";
1151
+ if (!fastHost && !WISTIA_ACCOUNT_HOST_RE.test(host)) return null;
1152
+ const segments = pathSegments(url);
1153
+ let raw;
1154
+ if (segments[0] === "medias") raw = segments[1];
1155
+ else if (segments[0] === "embed" && (segments[1] === "iframe" || segments[1] === "medias")) {
1156
+ raw = segments[2];
1157
+ }
1158
+ const id = raw?.replace(/\.jsonp$/, "");
1159
+ if (!id || !WISTIA_ID_RE.test(id)) return null;
1160
+ const embedUrl = `https://fast.wistia.net/embed/iframe/${id}`;
1161
+ return build("wistia", {
1162
+ id,
1163
+ embedUrl,
1164
+ // A media page lives on the account's own subdomain; the fast host only
1165
+ // serves the player, which doubles as a viewable page.
1166
+ watchUrl: fastHost ? embedUrl : `https://${host}/medias/${id}`
1167
+ });
1168
+ }
1169
+ var PARSERS = [
1170
+ parseYouTube,
1171
+ parseVimeo,
1172
+ parseLoom,
1173
+ parseDailymotion,
1174
+ parseWistia
1175
+ ];
1176
+ function parseVideoEmbedUrl(input) {
1177
+ if (typeof input !== "string") return null;
1178
+ const url = toUrl(input);
1179
+ if (!url) return null;
1180
+ for (const parse of PARSERS) {
1181
+ const embed = parse(url);
1182
+ if (embed) return embed;
1183
+ }
1184
+ return null;
1185
+ }
1186
+ function isVideoEmbedUrl(input) {
1187
+ return parseVideoEmbedUrl(input) !== null;
1188
+ }
1189
+ var VIDEO_EMBED_IFRAME_ALLOW = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen";
1190
+ function videoEmbedIframeAttributes(embed, title) {
1191
+ return {
1192
+ src: embed.embedUrl,
1193
+ title: title?.trim() || `${embed.providerName} video`,
1194
+ allow: VIDEO_EMBED_IFRAME_ALLOW,
1195
+ allowfullscreen: "",
1196
+ referrerpolicy: "strict-origin-when-cross-origin",
1197
+ loading: "lazy"
1198
+ };
1199
+ }
1200
+ var GENERIC_PLAYER_TITLE_RE = /^(?:[\w .-]+[ -])?(?:video[ -])?player$/i;
1201
+ function attributeOf(element, name) {
1202
+ for (const [key, value] of Object.entries(element.attributes)) {
1203
+ if (key.toLowerCase() === name) return value;
1204
+ }
1205
+ return void 0;
1206
+ }
1207
+ function soleIframe(nodes) {
1208
+ let found = null;
1209
+ let disqualified = false;
1210
+ const visit = (list) => {
1211
+ for (const node of list) {
1212
+ if (disqualified) return;
1213
+ if (node.type === "htmlText") {
1214
+ if (node.value.trim()) disqualified = true;
1215
+ continue;
1216
+ }
1217
+ if (node.type !== "htmlElement") continue;
1218
+ const tag = node.tagName.toLowerCase();
1219
+ if (tag === "iframe") {
1220
+ if (found) disqualified = true;
1221
+ found = node;
1222
+ continue;
1223
+ }
1224
+ if (tag === "script" || tag === "style" || tag === "noscript") continue;
1225
+ visit(node.children);
1226
+ }
1227
+ };
1228
+ visit(nodes);
1229
+ return disqualified ? null : found;
1230
+ }
1231
+ function iframeEmbed(element) {
1232
+ const embed = parseVideoEmbedUrl(attributeOf(element, "src"));
1233
+ if (!embed) return null;
1234
+ const title = attributeOf(element, "title")?.trim() ?? "";
1235
+ const generic = GENERIC_PLAYER_TITLE_RE.test(title) || title.toLowerCase() === `${embed.providerName} video`.toLowerCase();
1236
+ return { embed, title: title && !generic ? title : null };
1237
+ }
1238
+ function videoEmbedFromIframe(element) {
1239
+ return element.tagName.toLowerCase() === "iframe" ? iframeEmbed(element) : null;
1240
+ }
1241
+ function parseVideoEmbedInput(input) {
1242
+ if (typeof input !== "string") return null;
1243
+ const text = input.trim();
1244
+ if (!text) return null;
1245
+ if (/^<(?!https?:)/i.test(text)) {
1246
+ if (!/<iframe\b/i.test(text)) return null;
1247
+ const iframe = soleIframe(parseHtmlToNodes(text));
1248
+ return iframe ? iframeEmbed(iframe) : null;
1249
+ }
1250
+ const embed = parseVideoEmbedUrl(text);
1251
+ return embed ? { embed, title: null } : null;
1252
+ }
1253
+ function isBlankInline(node) {
1254
+ return node.type === "text" && !node.value.trim() || node.type === "htmlInline" && node.htmlChildren.length === 0;
1255
+ }
1256
+ function findBlockVideoEmbed(node) {
1257
+ if (node.type === "htmlBlock") {
1258
+ const iframe = soleIframe(node.htmlChildren);
1259
+ const found = iframe ? iframeEmbed(iframe) : null;
1260
+ return found ? { ...found, form: "iframe" } : null;
1261
+ }
1262
+ if (node.type !== "paragraph") return null;
1263
+ const text = extractPlainText(node).trim();
1264
+ if (text && !/\s/.test(text)) {
1265
+ const bare = parseVideoEmbedUrl(text);
1266
+ if (bare) return { embed: bare, title: null, form: "url" };
1267
+ }
1268
+ const significant = node.children.filter((child) => !isBlankInline(child));
1269
+ if (significant.length !== 1 || significant[0].type !== "link") return null;
1270
+ const link = significant[0];
1271
+ const embed = parseVideoEmbedUrl(link.url);
1272
+ if (!embed) return null;
1273
+ const label = extractPlainText(link).trim();
1274
+ const title = label && !isVideoEmbedUrl(label) && label !== link.url ? label : null;
1275
+ return { embed, title, form: title ? "link" : "url" };
1276
+ }
1277
+
1278
+ // src/markdown/sanitize.ts
1279
+ var SAFE_LINK_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
1280
+ var SAFE_MEDIA_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "blob"]);
1281
+ var NEVER_ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["javascript", "vbscript", "data"]);
1282
+ var SAFE_DATA_MEDIA_RE = /^data:(?:image\/(?!svg\+xml)[a-z0-9.+-]+|audio\/[a-z0-9.+-]+|video\/[a-z0-9.+-]+);/i;
1283
+ var SAFE_TAGS = /* @__PURE__ */ new Set([
1284
+ "a",
1285
+ "abbr",
1286
+ "b",
1287
+ "blockquote",
1288
+ "br",
1289
+ "caption",
1290
+ "cite",
1291
+ "code",
1292
+ "col",
1293
+ "colgroup",
1294
+ "data",
1295
+ "dd",
1296
+ "del",
1297
+ "details",
1298
+ "dfn",
1299
+ "div",
1300
+ "dl",
1301
+ "dt",
1302
+ "em",
1303
+ "figcaption",
1304
+ "figure",
1305
+ "h1",
1306
+ "h2",
1307
+ "h3",
1308
+ "h4",
1309
+ "h5",
1310
+ "h6",
1311
+ "hr",
1312
+ "i",
1313
+ "img",
1314
+ "kbd",
1315
+ "li",
1316
+ "mark",
1317
+ "ol",
1318
+ "p",
1319
+ "pre",
1320
+ "q",
1321
+ "s",
1322
+ "samp",
1323
+ "small",
1324
+ "source",
1325
+ "span",
1326
+ "strong",
1327
+ "sub",
1328
+ "summary",
1329
+ "sup",
1330
+ "table",
1331
+ "tbody",
1332
+ "td",
1333
+ "tfoot",
1334
+ "th",
1335
+ "thead",
1336
+ "time",
1337
+ "tr",
1338
+ "track",
1339
+ "u",
1340
+ "ul",
1341
+ "var",
1342
+ "video",
1343
+ "audio"
1344
+ ]);
1345
+ var DROP_WITH_CONTENT_TAGS = /* @__PURE__ */ new Set([
1346
+ "base",
1347
+ "button",
1348
+ "embed",
1349
+ "form",
1350
+ "iframe",
1351
+ "input",
1352
+ "link",
1353
+ "meta",
1354
+ "object",
1355
+ "option",
1356
+ "script",
1357
+ "select",
1358
+ "style",
1359
+ "svg",
1360
+ "template",
1361
+ "textarea"
1362
+ ]);
1363
+ var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "id", "title", "role"]);
1364
+ var TAG_ATTRS = {
1365
+ a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
1366
+ audio: /* @__PURE__ */ new Set(["src", "controls", "preload", "muted", "loop"]),
1367
+ col: /* @__PURE__ */ new Set(["span", "width"]),
1368
+ colgroup: /* @__PURE__ */ new Set(["span"]),
1369
+ data: /* @__PURE__ */ new Set(["value"]),
1370
+ img: /* @__PURE__ */ new Set(["src", "alt", "width", "height", "loading", "decoding"]),
1371
+ li: /* @__PURE__ */ new Set(["value"]),
1372
+ ol: /* @__PURE__ */ new Set(["start", "type"]),
1373
+ q: /* @__PURE__ */ new Set(["cite"]),
1374
+ source: /* @__PURE__ */ new Set(["src", "type", "media"]),
1375
+ td: /* @__PURE__ */ new Set(["colspan", "rowspan", "align"]),
1376
+ th: /* @__PURE__ */ new Set(["colspan", "rowspan", "align", "scope"]),
1377
+ time: /* @__PURE__ */ new Set(["datetime"]),
1378
+ track: /* @__PURE__ */ new Set(["src", "kind", "label", "srclang", "default"]),
1379
+ video: /* @__PURE__ */ new Set([
1380
+ "src",
1381
+ "poster",
1382
+ "width",
1383
+ "height",
1384
+ "controls",
1385
+ "preload",
1386
+ "muted",
1387
+ "loop",
1388
+ "playsinline"
1389
+ ])
1390
+ };
1391
+ var SAFE_ATTR_NAME_RE = /^[a-z][a-z0-9:_.-]*$/;
1392
+ var SAFE_OL_TYPES = /* @__PURE__ */ new Set(["1", "a", "A", "i", "I"]);
1393
+ var SAFE_PRELOAD_VALUES = /* @__PURE__ */ new Set(["none", "metadata", "auto"]);
1394
+ var SAFE_TRACK_KINDS = /* @__PURE__ */ new Set(["subtitles", "captions", "descriptions", "chapters", "metadata"]);
1395
+ function sanitizeUrl(url, kind = "link", options) {
1396
+ if (typeof url !== "string") return null;
1397
+ const trimmed = url.trim();
1398
+ if (!trimmed) return null;
1399
+ const compact = stripUrlSchemeNoise(trimmed);
1400
+ const colon = compact.indexOf(":");
1401
+ const firstPathChar = firstIndexOfAny(compact, ["/", "?", "#"]);
1402
+ const hasScheme = colon >= 0 && (firstPathChar < 0 || colon < firstPathChar);
1403
+ if (!hasScheme) return trimmed;
1404
+ const scheme = compact.slice(0, colon).toLowerCase();
1405
+ if (kind === "media") {
1406
+ if (scheme === "data") return SAFE_DATA_MEDIA_RE.test(compact) ? trimmed : null;
1407
+ return SAFE_MEDIA_SCHEMES.has(scheme) ? trimmed : null;
1408
+ }
1409
+ if (SAFE_LINK_SCHEMES.has(scheme)) return trimmed;
1410
+ if (options?.extraLinkSchemes?.includes(scheme) && !NEVER_ALLOWED_SCHEMES.has(scheme)) {
1411
+ return trimmed;
1412
+ }
1413
+ return null;
1414
+ }
1415
+ function sanitizeHtmlNodes(nodes) {
1416
+ const out = [];
1417
+ for (const node of nodes) {
1418
+ out.push(...sanitizeHtmlNode(node));
1419
+ }
1420
+ return out;
1421
+ }
1422
+ function sanitizeHtmlNode(node) {
1423
+ switch (node.type) {
1424
+ case "htmlText":
1425
+ return [node];
1426
+ case "htmlComment":
1427
+ return [];
1428
+ case "htmlElement":
1429
+ return sanitizeHtmlElement(node);
1430
+ }
1431
+ }
1432
+ function sanitizeHtmlElement(node) {
1433
+ const tag = node.tagName.toLowerCase();
1434
+ if (tag === "iframe") return sanitizeVideoEmbedIframe(node);
1435
+ if (DROP_WITH_CONTENT_TAGS.has(tag)) return [];
1436
+ const children = sanitizeHtmlNodes(node.children);
1437
+ if (!SAFE_TAGS.has(tag)) return children;
1438
+ return [
1439
+ {
1440
+ type: "htmlElement",
1441
+ tagName: tag,
1442
+ attributes: sanitizeAttrs(tag, node.attributes),
1443
+ children,
1444
+ selfClosing: node.selfClosing
1445
+ }
1446
+ ];
1447
+ }
1448
+ function sanitizeVideoEmbedIframe(node) {
1449
+ const found = videoEmbedFromIframe(node);
1450
+ if (!found) return [];
1451
+ const attributes = videoEmbedIframeAttributes(found.embed, found.title);
1452
+ for (const [rawName, value] of Object.entries(node.attributes)) {
1453
+ const name = rawName.toLowerCase();
1454
+ if ((name === "width" || name === "height") && isNonNegativeInteger(value)) {
1455
+ attributes[name] = value.trim();
1456
+ }
1457
+ }
1458
+ return [{ type: "htmlElement", tagName: "iframe", attributes, children: [], selfClosing: false }];
1459
+ }
1460
+ function sanitizeAttrs(tag, attrs) {
1461
+ const out = {};
1462
+ for (const [rawName, rawValue] of Object.entries(attrs)) {
1463
+ const name = rawName.toLowerCase();
1464
+ if (!isAllowedAttr(tag, name)) continue;
1465
+ const value = String(rawValue);
1466
+ const safeValue = sanitizeAttrValue(tag, name, value);
1467
+ if (safeValue === null) continue;
1468
+ out[name] = safeValue;
1469
+ }
1470
+ if (tag === "a" && out.target === "_blank") {
1471
+ out.rel = ensureRelTokens(out.rel, ["noopener", "noreferrer"]);
1472
+ }
1473
+ return out;
1474
+ }
1475
+ function isAllowedAttr(tag, name) {
1476
+ if (!SAFE_ATTR_NAME_RE.test(name)) return false;
1477
+ if (name.startsWith("on")) return false;
1478
+ if (name.startsWith("aria-") || name.startsWith("data-")) return true;
1479
+ if (GLOBAL_ATTRS.has(name)) return true;
1480
+ return TAG_ATTRS[tag]?.has(name) ?? false;
1481
+ }
1482
+ function sanitizeAttrValue(tag, name, value) {
1483
+ if (name === "href") return sanitizeUrl(value, "link");
1484
+ if (name === "src" || name === "poster") return sanitizeUrl(value, "media");
1485
+ if ((name === "width" || name === "height" || name === "span" || name === "colspan" || name === "rowspan" || name === "value") && !isNonNegativeInteger(value)) {
1486
+ return null;
1487
+ }
1488
+ if (name === "start" && !isInteger(value)) return null;
1489
+ if (name === "type" && tag === "ol" && !SAFE_OL_TYPES.has(value)) return null;
1490
+ if (name === "preload" && !SAFE_PRELOAD_VALUES.has(value)) return null;
1491
+ if (name === "kind" && tag === "track" && !SAFE_TRACK_KINDS.has(value)) return null;
1492
+ if (name === "align" && value !== "left" && value !== "right" && value !== "center") return null;
1493
+ if (name === "target")
1494
+ return value === "_blank" || value === "_self" || value === "_parent" || value === "_top" ? value : null;
1495
+ if (name === "rel") return sanitizeRel(value);
1496
+ return value;
1497
+ }
1498
+ function sanitizeRel(value) {
1499
+ return relTokens(value).join(" ");
1500
+ }
1501
+ function ensureRelTokens(value, required) {
1502
+ const tokens = new Set(value ? relTokens(value) : []);
1503
+ for (const token of required) tokens.add(token);
1504
+ return Array.from(tokens).join(" ");
1505
+ }
1506
+ function relTokens(value) {
1507
+ const tokens = value.split(/\s+/).map((token) => token.trim().toLowerCase()).filter(Boolean).filter((token) => /^[a-z0-9_-]+$/.test(token));
1508
+ return Array.from(new Set(tokens));
1509
+ }
1510
+ function isInteger(value) {
1511
+ return /^-?\d+$/.test(value.trim());
1512
+ }
1513
+ function isNonNegativeInteger(value) {
1514
+ return /^\d+$/.test(value.trim());
1515
+ }
1516
+ function firstIndexOfAny(value, needles) {
1517
+ let result = -1;
1518
+ for (const needle of needles) {
1519
+ const index = value.indexOf(needle);
1520
+ if (index >= 0 && (result < 0 || index < result)) result = index;
1521
+ }
1522
+ return result;
1523
+ }
1524
+ function stripUrlSchemeNoise(value) {
1525
+ let out = "";
1526
+ for (const char of value) {
1527
+ const code = char.charCodeAt(0);
1528
+ if (code <= 32 || code === 127 || char.trim() === "") continue;
1529
+ out += char;
1530
+ }
1531
+ return out;
1532
+ }
1533
+
1534
+ // src/markdown/svgIsland.ts
1535
+ import { fromHtml as fromHtml2 } from "hast-util-from-html";
1536
+ function isSvgIslandLang(lang) {
1537
+ return lang?.trim().toLowerCase() === "svg";
1538
+ }
1539
+ function isSvgIslandFence(node) {
1540
+ return node.type === "code" && isSvgIslandLang(node.lang);
1541
+ }
1542
+ function isSvgImageSource(src) {
1543
+ const trimmed = src?.trim();
1544
+ if (!trimmed) return false;
1545
+ if (/^data:image\/svg\+xml[;,]/i.test(trimmed)) return true;
1546
+ return /\.svg$/i.test(trimmed.split(/[?#]/, 1)[0]);
1547
+ }
1548
+ function svgSourceFromBytes(bytes) {
1549
+ return new TextDecoder("utf-8").decode(bytes).replace(/^\uFEFF/, "");
1550
+ }
1551
+ function svgSourceFromDataUrl(url) {
1552
+ const match = /^data:image\/svg\+xml((?:;[^,]*)?),([\s\S]*)$/i.exec(url.trim());
1553
+ if (!match) return null;
1554
+ const [, params, payload] = match;
1555
+ try {
1556
+ if (/;base64/i.test(params ?? "")) {
1557
+ const binary = atob(payload.replace(/\s+/g, ""));
1558
+ const bytes = new Uint8Array(binary.length);
1559
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
1560
+ return svgSourceFromBytes(bytes);
1561
+ }
1562
+ return decodeURIComponent(payload);
1563
+ } catch {
1564
+ return null;
1565
+ }
1566
+ }
1567
+ var SVG_NS = "http://www.w3.org/2000/svg";
1568
+ var XLINK_NS = "http://www.w3.org/1999/xlink";
1569
+ var ELEMENTS = [
1570
+ "svg",
1571
+ "g",
1572
+ "defs",
1573
+ "symbol",
1574
+ "use",
1575
+ "switch",
1576
+ "title",
1577
+ "desc",
1578
+ "style",
1579
+ "path",
1580
+ "rect",
1581
+ "circle",
1582
+ "ellipse",
1583
+ "line",
1584
+ "polyline",
1585
+ "polygon",
1586
+ "text",
1587
+ "tspan",
1588
+ "textPath",
1589
+ "image",
1590
+ "linearGradient",
1591
+ "radialGradient",
1592
+ "stop",
1593
+ "pattern",
1594
+ "clipPath",
1595
+ "mask",
1596
+ "marker",
1597
+ "filter",
1598
+ "feBlend",
1599
+ "feColorMatrix",
1600
+ "feComponentTransfer",
1601
+ "feComposite",
1602
+ "feConvolveMatrix",
1603
+ "feDiffuseLighting",
1604
+ "feDisplacementMap",
1605
+ "feDistantLight",
1606
+ "feDropShadow",
1607
+ "feFlood",
1608
+ "feFuncA",
1609
+ "feFuncB",
1610
+ "feFuncG",
1611
+ "feFuncR",
1612
+ "feGaussianBlur",
1613
+ "feImage",
1614
+ "feMerge",
1615
+ "feMergeNode",
1616
+ "feMorphology",
1617
+ "feOffset",
1618
+ "fePointLight",
1619
+ "feSpecularLighting",
1620
+ "feSpotLight",
1621
+ "feTile",
1622
+ "feTurbulence",
1623
+ "animate",
1624
+ "animateTransform",
1625
+ "animateMotion",
1626
+ "mpath",
1627
+ "set"
1628
+ ];
1629
+ var ELEMENT_BY_LOWER = new Map(ELEMENTS.map((tag) => [tag.toLowerCase(), tag]));
1630
+ var ANIMATION_ELEMENTS = /* @__PURE__ */ new Set(["animate", "animateTransform", "animateMotion", "set"]);
1631
+ var TEXT_ELEMENTS = /* @__PURE__ */ new Set(["text", "tspan", "textPath", "title", "desc", "style"]);
1632
+ var TEXT_ONLY_ELEMENTS = /* @__PURE__ */ new Set(["title", "desc", "style"]);
1633
+ var LOCAL_HREF_ELEMENTS = /* @__PURE__ */ new Set([
1634
+ "use",
1635
+ "textPath",
1636
+ "mpath",
1637
+ "feImage",
1638
+ "linearGradient",
1639
+ "radialGradient",
1640
+ "pattern",
1641
+ "filter",
1642
+ "animate",
1643
+ "animateTransform",
1644
+ "animateMotion",
1645
+ "set"
1646
+ ]);
1647
+ var ATTRIBUTES = [
1648
+ // Core
1649
+ "id",
1650
+ "class",
1651
+ "style",
1652
+ "lang",
1653
+ "xml:lang",
1654
+ "xml:space",
1655
+ "role",
1656
+ "aria-label",
1657
+ "aria-hidden",
1658
+ "href",
1659
+ "xlink:href",
1660
+ "version",
1661
+ // Geometry & coordinate systems
1662
+ "x",
1663
+ "y",
1664
+ "x1",
1665
+ "y1",
1666
+ "x2",
1667
+ "y2",
1668
+ "cx",
1669
+ "cy",
1670
+ "r",
1671
+ "rx",
1672
+ "ry",
1673
+ "fx",
1674
+ "fy",
1675
+ "fr",
1676
+ "dx",
1677
+ "dy",
1678
+ "width",
1679
+ "height",
1680
+ "d",
1681
+ "points",
1682
+ "pathLength",
1683
+ "transform",
1684
+ "transform-origin",
1685
+ "viewBox",
1686
+ "preserveAspectRatio",
1687
+ "overflow",
1688
+ "rotate",
1689
+ "textLength",
1690
+ "lengthAdjust",
1691
+ "startOffset",
1692
+ "method",
1693
+ "spacing",
1694
+ "side",
1695
+ "refX",
1696
+ "refY",
1697
+ "markerWidth",
1698
+ "markerHeight",
1699
+ "markerUnits",
1700
+ "orient",
1701
+ "patternUnits",
1702
+ "patternContentUnits",
1703
+ "patternTransform",
1704
+ "gradientUnits",
1705
+ "gradientTransform",
1706
+ "spreadMethod",
1707
+ "offset",
1708
+ "clipPathUnits",
1709
+ "maskUnits",
1710
+ "maskContentUnits",
1711
+ "filterUnits",
1712
+ "primitiveUnits",
1713
+ "requiredExtensions",
1714
+ "systemLanguage",
1715
+ // Presentation
1716
+ "fill",
1717
+ "fill-opacity",
1718
+ "fill-rule",
1719
+ "stroke",
1720
+ "stroke-width",
1721
+ "stroke-opacity",
1722
+ "stroke-linecap",
1723
+ "stroke-linejoin",
1724
+ "stroke-miterlimit",
1725
+ "stroke-dasharray",
1726
+ "stroke-dashoffset",
1727
+ "opacity",
1728
+ "color",
1729
+ "display",
1730
+ "visibility",
1731
+ "clip-path",
1732
+ "clip-rule",
1733
+ "mask",
1734
+ "filter",
1735
+ "marker-start",
1736
+ "marker-mid",
1737
+ "marker-end",
1738
+ "stop-color",
1739
+ "stop-opacity",
1740
+ "font-family",
1741
+ "font-size",
1742
+ "font-size-adjust",
1743
+ "font-stretch",
1744
+ "font-style",
1745
+ "font-variant",
1746
+ "font-weight",
1747
+ "letter-spacing",
1748
+ "word-spacing",
1749
+ "text-anchor",
1750
+ "text-decoration",
1751
+ "dominant-baseline",
1752
+ "alignment-baseline",
1753
+ "baseline-shift",
1754
+ "writing-mode",
1755
+ "direction",
1756
+ "unicode-bidi",
1757
+ "shape-rendering",
1758
+ "text-rendering",
1759
+ "image-rendering",
1760
+ "color-interpolation",
1761
+ "color-interpolation-filters",
1762
+ "flood-color",
1763
+ "flood-opacity",
1764
+ "lighting-color",
1765
+ "paint-order",
1766
+ "vector-effect",
1767
+ // Filter primitives
1768
+ "in",
1769
+ "in2",
1770
+ "result",
1771
+ "stdDeviation",
1772
+ "operator",
1773
+ "k1",
1774
+ "k2",
1775
+ "k3",
1776
+ "k4",
1777
+ "mode",
1778
+ "type",
1779
+ "values",
1780
+ "tableValues",
1781
+ "slope",
1782
+ "intercept",
1783
+ "amplitude",
1784
+ "exponent",
1785
+ "kernelMatrix",
1786
+ "order",
1787
+ "divisor",
1788
+ "bias",
1789
+ "targetX",
1790
+ "targetY",
1791
+ "edgeMode",
1792
+ "preserveAlpha",
1793
+ "kernelUnitLength",
1794
+ "surfaceScale",
1795
+ "specularConstant",
1796
+ "specularExponent",
1797
+ "diffuseConstant",
1798
+ "azimuth",
1799
+ "elevation",
1800
+ "pointsAtX",
1801
+ "pointsAtY",
1802
+ "pointsAtZ",
1803
+ "limitingConeAngle",
1804
+ "scale",
1805
+ "xChannelSelector",
1806
+ "yChannelSelector",
1807
+ "baseFrequency",
1808
+ "numOctaves",
1809
+ "seed",
1810
+ "stitchTiles",
1811
+ "radius",
1812
+ "z",
1813
+ // Animation
1814
+ "attributeName",
1815
+ "attributeType",
1816
+ "from",
1817
+ "to",
1818
+ "by",
1819
+ "begin",
1820
+ "dur",
1821
+ "end",
1822
+ "min",
1823
+ "max",
1824
+ "restart",
1825
+ "repeatCount",
1826
+ "repeatDur",
1827
+ "calcMode",
1828
+ "keyTimes",
1829
+ "keySplines",
1830
+ "keyPoints",
1831
+ "additive",
1832
+ "accumulate",
1833
+ "path"
1834
+ ];
1835
+ function attributeKey(name) {
1836
+ return name.toLowerCase().replace(/[-:]/g, "");
1837
+ }
1838
+ var ATTRIBUTE_BY_KEY = new Map([
1839
+ ...ATTRIBUTES.map((name) => [attributeKey(name), name]),
1840
+ ["classname", "class"]
1841
+ ]);
1842
+ var FREE_TEXT_ATTRIBUTES = /* @__PURE__ */ new Set(["aria-label"]);
1843
+ var UNANIMATABLE = /* @__PURE__ */ new Set(["href", "xlink:href", "style"]);
1844
+ var RASTER_DATA_URI_RE = /^data:image\/(?:png|jpeg|gif|webp);base64,[A-Za-z0-9+/=\s]*$/i;
1845
+ var LOCAL_REF_RE = /^#[^\s"'<>()\\]+$/;
1846
+ var URL_FUNCTION_RE = /url\s*\(/gi;
1847
+ var LOCAL_URL_RE = /^url\(\s*(['"]?)#[^\s"'<>()\\]+\1\s*\)/i;
1848
+ var FETCHING_FUNCTION_RE = /(?:^|[^a-z0-9_-])(?:-webkit-image-set|image-set|image|cross-fade|src|element|paint)\s*\(/i;
1849
+ var FORBIDDEN_CSS_RE = /@import|@font-face|@namespace|@document|-moz-binding|behavior\s*:|expression\s*\(|javascript:/i;
1850
+ var REMOTE_REF_RE = /[a-z][a-z0-9+.-]*:\/\//i;
1851
+ var XML_INVALID_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F￾￿]/g;
1852
+ var LONE_SURROGATE_RE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
1853
+ var SAFE_COLOR_RE = /^(?:#[0-9a-f]{3,8}|(?:rgb|rgba|hsl|hsla)\([\d\s.,%a-z/+-]*\)|[a-z]{3,20})$/i;
1854
+ var ISLAND_TREES = /* @__PURE__ */ new WeakMap();
1855
+ var IslandLimitError = class extends Error {
1856
+ constructor(reason, message) {
1857
+ super(message);
1858
+ this.reason = reason;
1859
+ }
1860
+ };
1861
+ var DiagnosticLog = class {
1862
+ constructor() {
1863
+ this.counts = /* @__PURE__ */ new Map();
1864
+ }
1865
+ add(kind, name) {
1866
+ const key = `${kind}:${name}`;
1867
+ const existing = this.counts.get(key);
1868
+ if (existing) existing.count += 1;
1869
+ else this.counts.set(key, { kind, name, count: 1 });
1870
+ }
1871
+ list() {
1872
+ return [...this.counts.values()];
1873
+ }
1874
+ };
1875
+ function isSafeCssValue(value) {
1876
+ if (value.includes("\\")) return false;
1877
+ if (FORBIDDEN_CSS_RE.test(value) || REMOTE_REF_RE.test(value)) return false;
1878
+ if (FETCHING_FUNCTION_RE.test(value)) return false;
1879
+ URL_FUNCTION_RE.lastIndex = 0;
1880
+ for (let match = URL_FUNCTION_RE.exec(value); match; match = URL_FUNCTION_RE.exec(value)) {
1881
+ if (!LOCAL_URL_RE.test(value.slice(match.index))) return false;
1882
+ }
1883
+ return true;
1884
+ }
1885
+ function cleanText(value) {
1886
+ return value.replace(LONE_SURROGATE_RE, "\uFFFD").replace(XML_INVALID_RE, "");
1887
+ }
1888
+ function splitCssStatements(css) {
1889
+ const statements = [];
1890
+ let depth = 0;
1891
+ let quote = null;
1892
+ let start = 0;
1893
+ for (let i = 0; i < css.length; i++) {
1894
+ const ch = css[i];
1895
+ if (quote) {
1896
+ if (ch === quote) quote = null;
1897
+ continue;
1898
+ }
1899
+ if (ch === '"' || ch === "'") quote = ch;
1900
+ else if (ch === "{") depth += 1;
1901
+ else if (ch === "}") {
1902
+ depth -= 1;
1903
+ if (depth <= 0) {
1904
+ statements.push(css.slice(start, i + 1));
1905
+ start = i + 1;
1906
+ depth = 0;
1907
+ }
1908
+ } else if (ch === ";" && depth === 0) {
1909
+ statements.push(css.slice(start, i + 1));
1910
+ start = i + 1;
1911
+ }
1912
+ }
1913
+ if (depth === 0 && quote === null && css.slice(start).trim()) statements.push(css.slice(start));
1914
+ return statements.map((statement) => statement.trim()).filter(Boolean);
1915
+ }
1916
+ var ANIMATION_DECLARATION_RE = /(?:^|[;{\s])(?:animation|transition)[a-z-]*\s*:[^;{}]*;?/gi;
1917
+ function stripCssMotion(css) {
1918
+ return css.replace(ANIMATION_DECLARATION_RE, (match) => /^[;{\s]/.test(match) ? match[0] : "");
1919
+ }
1920
+ function sanitizeCss(css, isStatic, log) {
1921
+ const uncommented = cleanText(css).replace(/\/\*[\s\S]*?(?:\*\/|$)/g, " ");
1922
+ const kept = [];
1923
+ for (const statement of splitCssStatements(uncommented)) {
1924
+ if (!isSafeCssValue(statement)) {
1925
+ log.add("style", "style rule");
1926
+ continue;
1927
+ }
1928
+ if (isStatic && /^@(?:-[a-z]+-)?keyframes\b/i.test(statement)) continue;
1929
+ kept.push(isStatic ? stripCssMotion(statement) : statement);
1930
+ }
1931
+ return kept.join("\n");
1932
+ }
1933
+ function propertyText(name, value) {
1934
+ if (value === null || value === void 0 || value === false) return null;
1935
+ if (value === true) return "";
1936
+ if (Array.isArray(value)) return value.join(name === "systemLanguage" ? ", " : " ");
1937
+ if (typeof value === "number" || typeof value === "string") return String(value);
1938
+ return null;
1939
+ }
1940
+ function sanitizeAttribute(tag, name, value, isStatic, log) {
1941
+ const text = cleanText(value);
1942
+ if (name === "href" || name === "xlink:href") {
1943
+ const ref = text.trim();
1944
+ if (tag === "image" && RASTER_DATA_URI_RE.test(ref)) return ref;
1945
+ if (LOCAL_HREF_ELEMENTS.has(tag) && LOCAL_REF_RE.test(ref)) return ref;
1946
+ log.add("attribute", name);
1947
+ return null;
1948
+ }
1949
+ if (name === "style") {
1950
+ const css = sanitizeCss(text, isStatic, log);
1951
+ return css.trim() ? css : null;
1952
+ }
1953
+ if (name === "attributeName") {
1954
+ const target = ATTRIBUTE_BY_KEY.get(attributeKey(text.trim()));
1955
+ if (!target || UNANIMATABLE.has(target)) {
1956
+ log.add("attribute", "attributeName");
1957
+ return null;
1958
+ }
1959
+ return target;
1960
+ }
1961
+ if (FREE_TEXT_ATTRIBUTES.has(name)) return text;
1962
+ if (!isSafeCssValue(text)) {
1963
+ log.add("attribute", name);
1964
+ return null;
1965
+ }
1966
+ return text;
1967
+ }
1968
+ function stripProlog(source) {
1969
+ let rest = source.replace(/^\uFEFF/, "");
1970
+ for (let i = 0; i < 16; i++) {
1971
+ rest = rest.trimStart();
1972
+ let end = -1;
1973
+ if (rest.startsWith("<?")) {
1974
+ end = rest.indexOf("?>");
1975
+ if (end >= 0) end += 2;
1976
+ } else if (rest.startsWith("<!--")) {
1977
+ end = rest.indexOf("-->", 4);
1978
+ if (end >= 0) end += 3;
1979
+ } else if (/^<!doctype/i.test(rest)) {
1980
+ const gt = rest.indexOf(">");
1981
+ const bracket = rest.indexOf("[");
1982
+ if (bracket >= 0 && (gt < 0 || bracket < gt)) {
1983
+ const close = rest.indexOf("]", bracket);
1984
+ const afterClose = close >= 0 ? rest.indexOf(">", close) : -1;
1985
+ end = afterClose >= 0 ? afterClose + 1 : -1;
1986
+ } else {
1987
+ end = gt >= 0 ? gt + 1 : -1;
1988
+ }
1989
+ } else {
1990
+ break;
1991
+ }
1992
+ if (end < 0) break;
1993
+ rest = rest.slice(end);
1994
+ }
1995
+ return rest;
1996
+ }
1997
+ function textContent(node) {
1998
+ if (node.type === "text") return node.value ?? "";
1999
+ return (node.children ?? []).map(textContent).join("");
2000
+ }
2001
+ function getAttr(element, name) {
2002
+ return element.attrs.find(([attr]) => attr === name)?.[1];
2003
+ }
2004
+ function setAttr(element, name, value) {
2005
+ const at = element.attrs.findIndex(([attr]) => attr === name);
2006
+ if (at >= 0) element.attrs[at] = [name, value];
2007
+ else element.attrs.push([name, value]);
2008
+ }
2009
+ function sanitizeElement(node, canonical, depth, state) {
2010
+ if (depth > SVG_ISLAND_LIMITS.maxDepth) {
2011
+ throw new IslandLimitError("too-deep", "The SVG is nested too deeply to render.");
2012
+ }
2013
+ state.elements += 1;
2014
+ if (state.elements > SVG_ISLAND_LIMITS.maxElements) {
2015
+ throw new IslandLimitError("too-large", "The SVG has too many elements to render.");
2016
+ }
2017
+ const element = { tag: canonical, attrs: [], children: [] };
2018
+ for (const [prop, raw] of Object.entries(node.properties ?? {})) {
2019
+ if (/^on/i.test(prop)) {
2020
+ state.log.add("attribute", prop.toLowerCase());
2021
+ continue;
2022
+ }
2023
+ const name = ATTRIBUTE_BY_KEY.get(attributeKey(prop));
2024
+ if (!name || getAttr(element, name) !== void 0) continue;
2025
+ const text = propertyText(prop, raw);
2026
+ if (text === null) continue;
2027
+ const value = sanitizeAttribute(canonical, name, text, state.isStatic, state.log);
2028
+ if (value !== null) element.attrs.push([name, value]);
2029
+ }
2030
+ if (TEXT_ONLY_ELEMENTS.has(canonical)) {
2031
+ const text = textContent(node);
2032
+ const content = canonical === "style" ? sanitizeCss(text, state.isStatic, state.log) : text;
2033
+ if (content.trim()) element.children.push(cleanText(content));
2034
+ return element;
2035
+ }
2036
+ const freezes = [];
2037
+ element.children = sanitizeChildren(node.children ?? [], canonical, depth, state, freezes);
2038
+ for (const freeze of freezes) {
2039
+ const value = sanitizeAttribute(canonical, freeze.name, freeze.value, true, state.log);
2040
+ if (value !== null) setAttr(element, freeze.name, value);
2041
+ }
2042
+ return element;
2043
+ }
2044
+ function sanitizeChildren(children, parentTag, depth, state, freezes) {
2045
+ const out = [];
2046
+ for (const child of children) {
2047
+ if (child.type === "text") {
2048
+ if (TEXT_ELEMENTS.has(parentTag) && child.value) out.push(cleanText(child.value));
2049
+ continue;
2050
+ }
2051
+ if (child.type !== "element" || !child.tagName) continue;
2052
+ if (child.tagName.toLowerCase() === "a") {
2053
+ out.push(...sanitizeChildren(child.children ?? [], parentTag, depth + 1, state, []));
2054
+ continue;
2055
+ }
2056
+ const canonical = ELEMENT_BY_LOWER.get(child.tagName.toLowerCase());
2057
+ if (!canonical) {
2058
+ state.log.add("element", child.tagName);
2059
+ continue;
2060
+ }
2061
+ if (state.isStatic && ANIMATION_ELEMENTS.has(canonical)) {
2062
+ const freeze = frozenEndValue(child, canonical);
2063
+ if (freeze) freezes.push(freeze);
2064
+ continue;
2065
+ }
2066
+ out.push(sanitizeElement(child, canonical, depth + 1, state));
2067
+ }
2068
+ return out;
2069
+ }
2070
+ function frozenEndValue(node, tag) {
2071
+ const props = node.properties ?? {};
2072
+ if (tag !== "animate" && tag !== "set") return null;
2073
+ if (propertyText("fill", props.fill)?.trim() !== "freeze") return null;
2074
+ if (props.href !== void 0 || props.xLinkHref !== void 0) return null;
2075
+ const targetName = propertyText("attributeName", props.attributeName);
2076
+ const name = targetName ? ATTRIBUTE_BY_KEY.get(attributeKey(targetName.trim())) : void 0;
2077
+ if (!name || UNANIMATABLE.has(name)) return null;
2078
+ let value = propertyText("to", props.to);
2079
+ if (value === null && tag === "animate") {
2080
+ const values = propertyText("values", props.values)?.split(";").map((part) => part.trim()).filter(Boolean);
2081
+ value = values && values.length > 0 ? values[values.length - 1] : null;
2082
+ }
2083
+ return value === null ? null : { name, value };
2084
+ }
2085
+ function checkUseExpansion(root) {
2086
+ const byId = /* @__PURE__ */ new Map();
2087
+ const index = (element) => {
2088
+ const id = getAttr(element, "id");
2089
+ if (id && !byId.has(id)) byId.set(id, element);
2090
+ for (const child of element.children) if (typeof child !== "string") index(child);
2091
+ };
2092
+ index(root);
2093
+ const limit = SVG_ISLAND_LIMITS.maxExpandedElements;
2094
+ const sizes = /* @__PURE__ */ new Map();
2095
+ const visiting = /* @__PURE__ */ new Set();
2096
+ const expandedSize = (element) => {
2097
+ const known = sizes.get(element);
2098
+ if (known !== void 0) return known;
2099
+ if (visiting.has(element)) {
2100
+ throw new IslandLimitError("too-large", "The SVG has a circular <use> reference.");
2101
+ }
2102
+ visiting.add(element);
2103
+ let total = 1;
2104
+ for (const child of element.children) {
2105
+ if (typeof child !== "string") total += expandedSize(child);
2106
+ }
2107
+ if (element.tag === "use") {
2108
+ const ref = getAttr(element, "href") ?? getAttr(element, "xlink:href");
2109
+ const target = ref ? byId.get(ref.slice(1)) : void 0;
2110
+ if (target) total += expandedSize(target);
2111
+ }
2112
+ visiting.delete(element);
2113
+ if (total > limit) {
2114
+ throw new IslandLimitError("too-large", "The SVG expands to too many shapes to render.");
2115
+ }
2116
+ sizes.set(element, total);
2117
+ return total;
2118
+ };
2119
+ expandedSize(root);
2120
+ }
2121
+ var PX_PER_UNIT = {
2122
+ "": 1,
2123
+ px: 1,
2124
+ pt: 96 / 72,
2125
+ pc: 16,
2126
+ in: 96,
2127
+ cm: 96 / 2.54,
2128
+ mm: 96 / 25.4
2129
+ };
2130
+ var LENGTH_RE = /^\s*\+?(\d*\.?\d+(?:e[+-]?\d+)?)\s*(px|pt|pc|in|cm|mm)?\s*$/i;
2131
+ function lengthPx(value) {
2132
+ const match = value ? LENGTH_RE.exec(value) : null;
2133
+ if (!match) return void 0;
2134
+ const px = Number(match[1]) * PX_PER_UNIT[(match[2] ?? "").toLowerCase()];
2135
+ return Number.isFinite(px) && px > 0 ? px : void 0;
2136
+ }
2137
+ function parseViewBox(value) {
2138
+ const parts = value?.trim().split(/[\s,]+/).map(Number);
2139
+ if (!parts || parts.length !== 4 || !parts.every(Number.isFinite)) return void 0;
2140
+ const [, , width, height] = parts;
2141
+ return width > 0 && height > 0 ? { width, height } : void 0;
2142
+ }
2143
+ function roundSize(value) {
2144
+ return Math.round(value * 1e3) / 1e3;
2145
+ }
2146
+ function sizeRoot(root) {
2147
+ const viewBox = parseViewBox(getAttr(root, "viewBox"));
2148
+ let width = lengthPx(getAttr(root, "width"));
2149
+ let height = lengthPx(getAttr(root, "height"));
2150
+ if (!width || !height) {
2151
+ if (viewBox) {
2152
+ if (width) height = width * viewBox.height / viewBox.width;
2153
+ else if (height) width = height * viewBox.width / viewBox.height;
2154
+ else ({ width, height } = viewBox);
2155
+ } else {
2156
+ width ?? (width = 300);
2157
+ height ?? (height = 150);
2158
+ }
2159
+ }
2160
+ width = roundSize(width);
2161
+ height = roundSize(height);
2162
+ setAttr(root, "width", String(width));
2163
+ setAttr(root, "height", String(height));
2164
+ if (!viewBox) setAttr(root, "viewBox", `0 0 ${width} ${height}`);
2165
+ return { width, height };
2166
+ }
2167
+ function islandTitle(root) {
2168
+ const label = getAttr(root, "aria-label");
2169
+ const titleElement = root.children.find(
2170
+ (child) => typeof child !== "string" && child.tag === "title"
2171
+ );
2172
+ const raw = label ?? (titleElement?.children.join("") || void 0);
2173
+ const title = raw?.replace(/\s+/g, " ").trim().slice(0, 200);
2174
+ return title || void 0;
2175
+ }
2176
+ function escapeText(value) {
2177
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2178
+ }
2179
+ function escapeAttr(value) {
2180
+ return escapeText(value).replace(/"/g, "&quot;").replace(/\t/g, "&#9;").replace(/\n/g, "&#10;").replace(/\r/g, "&#13;");
2181
+ }
2182
+ function usesXlink(element) {
2183
+ if (element.attrs.some(([name]) => name.startsWith("xlink:"))) return true;
2184
+ return element.children.some((child) => typeof child !== "string" && usesXlink(child));
2185
+ }
2186
+ function serializeElement(element, options, root) {
2187
+ const recolor = (value) => options.replaceCurrentColor && options.color ? value.replace(/currentcolor/gi, options.color) : value;
2188
+ let attrs = "";
2189
+ if (root) {
2190
+ attrs += ` xmlns="${SVG_NS}"`;
2191
+ if (usesXlink(element)) attrs += ` xmlns:xlink="${XLINK_NS}"`;
2192
+ if (options.color && getAttr(element, "color") === void 0) {
2193
+ attrs += ` color="${escapeAttr(options.color)}"`;
2194
+ }
2195
+ }
2196
+ for (const [name, value] of element.attrs) attrs += ` ${name}="${escapeAttr(recolor(value))}"`;
2197
+ if (element.children.length === 0) return `<${element.tag}${attrs}/>`;
2198
+ const body = element.children.map(
2199
+ (child) => typeof child === "string" ? escapeText(element.tag === "style" ? recolor(child) : child) : serializeElement(child, options, false)
2200
+ ).join("");
2201
+ return `<${element.tag}${attrs}>${body}</${element.tag}>`;
2202
+ }
2203
+ function utf8Length(value) {
2204
+ let bytes = 0;
2205
+ for (let i = 0; i < value.length; i++) {
2206
+ const code = value.charCodeAt(i);
2207
+ if (code < 128) bytes += 1;
2208
+ else if (code < 2048) bytes += 2;
2209
+ else if (code >= 55296 && code < 56320 && i + 1 < value.length) {
2210
+ bytes += 4;
2211
+ i += 1;
2212
+ } else bytes += 3;
2213
+ }
2214
+ return bytes;
2215
+ }
2216
+ function failure(reason, message) {
2217
+ return Object.freeze({ ok: false, reason, message });
2218
+ }
2219
+ function parseUncached(source, isStatic) {
2220
+ if (!source.trim()) return failure("empty", "The SVG fence is empty.");
2221
+ const max = SVG_ISLAND_LIMITS.maxSourceBytes;
2222
+ if (source.length > max || utf8Length(source) > max) {
2223
+ return failure("too-large", "The SVG is too large to render.");
2224
+ }
2225
+ let tree;
2226
+ try {
2227
+ tree = fromHtml2(stripProlog(source), { fragment: true });
2228
+ } catch (err) {
2229
+ return err instanceof RangeError ? failure("too-deep", "The SVG is nested too deeply to render.") : failure("parse", "The SVG could not be read.");
2230
+ }
2231
+ let rootNode;
2232
+ for (const child of tree.children ?? []) {
2233
+ if (child.type === "comment" || child.type === "doctype") continue;
2234
+ if (child.type === "text" && !(child.value ?? "").trim()) continue;
2235
+ if (child.type === "element" && child.tagName === "svg" && !rootNode) {
2236
+ rootNode = child;
2237
+ continue;
2238
+ }
2239
+ return failure("not-svg", "An SVG fence must hold one <svg> element and nothing else.");
2240
+ }
2241
+ if (!rootNode) {
2242
+ return failure("not-svg", "An SVG fence must hold one <svg> element and nothing else.");
2243
+ }
2244
+ const state = { isStatic, log: new DiagnosticLog(), elements: 0 };
2245
+ let root;
2246
+ try {
2247
+ root = sanitizeElement(rootNode, "svg", 0, state);
2248
+ checkUseExpansion(root);
2249
+ } catch (err) {
2250
+ if (err instanceof IslandLimitError) return failure(err.reason, err.message);
2251
+ if (err instanceof RangeError) {
2252
+ return failure("too-deep", "The SVG is nested too deeply to render.");
2253
+ }
2254
+ throw err;
2255
+ }
2256
+ const { width, height } = sizeRoot(root);
2257
+ const title = islandTitle(root);
2258
+ const island = Object.freeze({
2259
+ markup: serializeElement(root, {}, true),
2260
+ width,
2261
+ height,
2262
+ aspectRatio: width / height,
2263
+ ...title ? { title } : {}
2264
+ });
2265
+ ISLAND_TREES.set(island, root);
2266
+ return Object.freeze({
2267
+ ok: true,
2268
+ island,
2269
+ diagnostics: Object.freeze(state.log.list())
2270
+ });
2271
+ }
2272
+ var RESULT_CACHE = /* @__PURE__ */ new Map();
2273
+ var RESULT_CACHE_LIMIT = 48;
2274
+ var CACHEABLE_SOURCE_LENGTH = 128 * 1024;
2275
+ function parseSvgIsland(source, options = {}) {
2276
+ if (typeof source !== "string") return failure("empty", "The SVG fence is empty.");
2277
+ const isStatic = options.static === true;
2278
+ if (source.length > CACHEABLE_SOURCE_LENGTH) return parseUncached(source, isStatic);
2279
+ const key = (isStatic ? "s:" : "a:") + source;
2280
+ const cached = RESULT_CACHE.get(key);
2281
+ if (cached) {
2282
+ RESULT_CACHE.delete(key);
2283
+ RESULT_CACHE.set(key, cached);
2284
+ return cached;
2285
+ }
2286
+ const result = parseUncached(source, isStatic);
2287
+ RESULT_CACHE.set(key, result);
2288
+ if (RESULT_CACHE.size > RESULT_CACHE_LIMIT) {
2289
+ const oldest = RESULT_CACHE.keys().next().value;
2290
+ if (oldest !== void 0) RESULT_CACHE.delete(oldest);
2291
+ }
2292
+ return result;
2293
+ }
2294
+ function svgIslandMarkup(island, options = {}) {
2295
+ const color = options.color?.trim();
2296
+ const tree = ISLAND_TREES.get(island);
2297
+ if (!tree || !color || !SAFE_COLOR_RE.test(color)) return island.markup;
2298
+ return serializeElement(
2299
+ tree,
2300
+ { color, replaceCurrentColor: options.replaceCurrentColor === true },
2301
+ true
2302
+ );
2303
+ }
2304
+ var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2305
+ function base64(bytes) {
2306
+ const parts = [];
2307
+ for (let i = 0; i < bytes.length; i += 3) {
2308
+ const a = bytes[i];
2309
+ const b = bytes[i + 1];
2310
+ const c = bytes[i + 2];
2311
+ const n = a << 16 | (b ?? 0) << 8 | (c ?? 0);
2312
+ parts.push(
2313
+ BASE64[n >> 18 & 63] + BASE64[n >> 12 & 63] + (b === void 0 ? "=" : BASE64[n >> 6 & 63]) + (c === void 0 ? "=" : BASE64[n & 63])
2314
+ );
2315
+ }
2316
+ return parts.join("");
2317
+ }
2318
+ var DATA_URL_CACHE = /* @__PURE__ */ new WeakMap();
2319
+ function svgIslandDataUrl(island, options = {}) {
2320
+ const key = `${options.color ?? ""}|${options.replaceCurrentColor ? 1 : 0}`;
2321
+ let perIsland = DATA_URL_CACHE.get(island);
2322
+ const cached = perIsland?.get(key);
2323
+ if (cached) return cached;
2324
+ const url = `data:image/svg+xml;base64,${base64(new TextEncoder().encode(svgIslandMarkup(island, options)))}`;
2325
+ if (!perIsland) {
2326
+ perIsland = /* @__PURE__ */ new Map();
2327
+ DATA_URL_CACHE.set(island, perIsland);
2328
+ }
2329
+ if (perIsland.size < 8) perIsland.set(key, url);
2330
+ return url;
2331
+ }
2332
+
2333
+ export {
2334
+ mediaKindForUrl,
2335
+ parseHtmlToNodes,
2336
+ stringifyHtmlNodes,
2337
+ getChildren,
2338
+ walkMarkdownTree,
2339
+ findNodesByType,
2340
+ extractPlainText,
2341
+ countNodes,
2342
+ parseFrontmatter,
2343
+ formatBlockScalar,
2344
+ splitFrontmatterBlock,
2345
+ formatFrontmatterValue,
2346
+ formatFrontmatterYaml,
2347
+ setFrontmatterValues,
2348
+ createDocument,
2349
+ inferDocumentTitle,
2350
+ readFrontmatterThemeId,
2351
+ plainTextFromInlineHtml,
2352
+ KNOWN_BLOCK_META_KEYS,
2353
+ BLOCK_META_KEY_DESCRIPTORS,
2354
+ coerceAnnotationValues,
2355
+ parseNumber,
2356
+ parseTimeSeconds,
2357
+ VIDEO_EMBED_PROVIDERS,
2358
+ parseVideoStartTime,
2359
+ parseVideoEmbedUrl,
2360
+ isVideoEmbedUrl,
2361
+ VIDEO_EMBED_IFRAME_ALLOW,
2362
+ videoEmbedIframeAttributes,
2363
+ videoEmbedFromIframe,
2364
+ parseVideoEmbedInput,
2365
+ findBlockVideoEmbed,
2366
+ sanitizeUrl,
2367
+ sanitizeHtmlNodes,
2368
+ isSvgIslandLang,
2369
+ isSvgIslandFence,
2370
+ isSvgImageSource,
2371
+ svgSourceFromBytes,
2372
+ svgSourceFromDataUrl,
2373
+ parseSvgIsland,
2374
+ svgIslandMarkup,
2375
+ svgIslandDataUrl
2376
+ };