snowkit 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,596 @@
1
+ const PUNCT = /^[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]$/;
2
+ const OPEN = "\ue000";
3
+ const CLOSE = "\ue001";
4
+
5
+ export function escapeHtml(s) {
6
+ return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
7
+ }
8
+
9
+ const ENTITY = /^&(#\d{1,7}|#[xX][0-9a-fA-F]{1,6}|[a-zA-Z][a-zA-Z0-9]{1,31});/;
10
+
11
+ export function safeUrl(raw, { image = false } = {}) {
12
+ const url = String(raw || "").trim().replace(/\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g, "$1");
13
+ const probe = url.replace(/[\u0000- \u007f-\u009f]/g, "").toLowerCase();
14
+ const scheme = probe.match(/^([a-z][a-z0-9+.-]*):/);
15
+ if (scheme) {
16
+ const s = scheme[1];
17
+ const ok = s === "http" || s === "https" || (!image && (s === "mailto" || s === "tel")) || (image && /^data:image\/(png|gif|jpeg|webp);base64,/.test(probe));
18
+ if (!ok) return "#";
19
+ }
20
+ return url.replace(/ /g, "%20");
21
+ }
22
+
23
+ function blank(line) {
24
+ return /^[ \t]*$/.test(line);
25
+ }
26
+
27
+ function indentOf(line) {
28
+ return line.match(/^ */)[0].length;
29
+ }
30
+
31
+ function expandTabs(line) {
32
+ const m = line.match(/^[ \t]+/);
33
+ if (!m || !m[0].includes("\t")) return line;
34
+ let col = 0;
35
+ for (const ch of m[0]) col = ch === "\t" ? col + 4 - (col % 4) : col + 1;
36
+ return " ".repeat(col) + line.slice(m[0].length);
37
+ }
38
+
39
+ function isHr(line) {
40
+ return /^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/.test(line);
41
+ }
42
+
43
+ function fenceStart(line) {
44
+ const m = line.match(/^( {0,3})(`{3,}|~{3,})[ \t]*(.*)$/);
45
+ if (!m) return null;
46
+ if (m[2][0] === "`" && m[3].includes("`")) return null;
47
+ return { indent: m[1].length, fence: m[2], info: m[3].trim().split(/\s+/)[0] || "" };
48
+ }
49
+
50
+ function listStart(line) {
51
+ if (isHr(line)) return null;
52
+ const m = line.match(/^( {0,3})([-+*]|\d{1,9}[.)])(?:([ \t]+)(.*))?$/);
53
+ if (!m) return null;
54
+ const marker = m[2];
55
+ const ordered = /\d/.test(marker[0]);
56
+ let spaces = m[3] ? m[3].length : 0;
57
+ let rest = m[4] || "";
58
+ if (!m[3]) {
59
+ if (line.length > m[1].length + marker.length) return null;
60
+ spaces = 1;
61
+ } else if (spaces > 4 || rest === "") {
62
+ rest = m[3].slice(1) + rest;
63
+ spaces = 1;
64
+ }
65
+ return {
66
+ indent: m[1].length,
67
+ ordered,
68
+ key: ordered ? marker.slice(-1) : marker,
69
+ start: ordered ? parseInt(marker, 10) : 1,
70
+ content: m[1].length + marker.length + spaces,
71
+ rest,
72
+ empty: rest.trim() === "",
73
+ };
74
+ }
75
+
76
+ function splitCells(line) {
77
+ let s = line.trim();
78
+ if (s.startsWith("|")) s = s.slice(1);
79
+ if (s.endsWith("|") && !s.endsWith("\\|")) s = s.slice(0, -1);
80
+ const cells = [];
81
+ let cur = "";
82
+ let tick = 0;
83
+ for (let i = 0; i < s.length; i++) {
84
+ const c = s[i];
85
+ if (c === "\\" && s[i + 1] === "|") {
86
+ cur += tick ? "|" : "\\|";
87
+ i++;
88
+ continue;
89
+ }
90
+ if (c === "`") {
91
+ let n = 1;
92
+ while (s[i + n] === "`") n++;
93
+ if (!tick) tick = n;
94
+ else if (tick === n) tick = 0;
95
+ cur += "`".repeat(n);
96
+ i += n - 1;
97
+ continue;
98
+ }
99
+ if (c === "|" && !tick) {
100
+ cells.push(cur.trim());
101
+ cur = "";
102
+ continue;
103
+ }
104
+ cur += c;
105
+ }
106
+ cells.push(cur.trim());
107
+ return cells;
108
+ }
109
+
110
+ function delimiterRow(line) {
111
+ if (!/-/.test(line) || !/^[ \t|:-]+$/.test(line)) return null;
112
+ const cells = splitCells(line);
113
+ if (!cells.every((c) => /^:?-+:?$/.test(c))) return null;
114
+ if (cells.length === 1 && !line.includes("|")) return null;
115
+ return cells.map((c) => (c.startsWith(":") && c.endsWith(":") ? "center" : c.endsWith(":") ? "right" : c.startsWith(":") ? "left" : ""));
116
+ }
117
+
118
+ function interrupts(line) {
119
+ if (fenceStart(line) || isHr(line) || /^ {0,3}#{1,6}(?:[ \t]|$)/.test(line) || /^ {0,3}>/.test(line)) return true;
120
+ const l = listStart(line);
121
+ return !!(l && !l.empty && (!l.ordered || l.start === 1));
122
+ }
123
+
124
+ const REF_DEF = /^ {0,3}\[((?:[^\]\\]|\\.){1,999})\]:[ \t]*(<[^>\n]*>|\S+)(?:[ \t]+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\((?:[^)\\]|\\.)*\)))?[ \t]*$/;
125
+
126
+ export function normalizeLabel(s) {
127
+ return String(s).trim().replace(/\s+/g, " ").toLowerCase();
128
+ }
129
+
130
+ function unquote(t) {
131
+ return t ? t.slice(1, -1).replace(/\\(.)/g, "$1") : "";
132
+ }
133
+
134
+ function parseBlocks(lines, refs) {
135
+ const out = [];
136
+ let i = 0;
137
+ const n = lines.length;
138
+ while (i < n) {
139
+ const line = lines[i];
140
+ if (blank(line)) {
141
+ i++;
142
+ continue;
143
+ }
144
+ const fence = fenceStart(line);
145
+ if (fence) {
146
+ const body = [];
147
+ i++;
148
+ while (i < n) {
149
+ const close = lines[i].match(/^ {0,3}(`{3,}|~{3,})[ \t]*$/);
150
+ if (close && close[1][0] === fence.fence[0] && close[1].length >= fence.fence.length) {
151
+ i++;
152
+ break;
153
+ }
154
+ body.push(lines[i].replace(new RegExp("^ {0," + fence.indent + "}"), ""));
155
+ i++;
156
+ }
157
+ out.push({ t: "code", lang: fence.info, text: body.join("\n") });
158
+ continue;
159
+ }
160
+ if (/^ {4}/.test(line)) {
161
+ const body = [];
162
+ while (i < n && (blank(lines[i]) || /^ {4}/.test(lines[i]))) body.push(lines[i++].slice(4));
163
+ while (body.length && blank(body[body.length - 1])) body.pop();
164
+ out.push({ t: "code", lang: "", text: body.join("\n") });
165
+ continue;
166
+ }
167
+ const h = line.match(/^ {0,3}(#{1,6})(?:[ \t]+(.*?))?[ \t]*$/);
168
+ if (h) {
169
+ out.push({ t: "heading", level: h[1].length, text: (h[2] || "").replace(/(^|[ \t]+)#+$/, "").trim() });
170
+ i++;
171
+ continue;
172
+ }
173
+ if (isHr(line)) {
174
+ out.push({ t: "hr" });
175
+ i++;
176
+ continue;
177
+ }
178
+ if (/^ {0,3}>/.test(line)) {
179
+ const inner = [];
180
+ while (i < n) {
181
+ const l = lines[i];
182
+ const q = l.match(/^ {0,3}> ?(.*)$/);
183
+ if (q) inner.push(q[1]);
184
+ else if (!blank(l) && inner.length && !blank(inner[inner.length - 1]) && !interrupts(l)) inner.push(l);
185
+ else break;
186
+ i++;
187
+ }
188
+ out.push({ t: "quote", children: parseBlocks(inner, refs) });
189
+ continue;
190
+ }
191
+ const ls = listStart(line);
192
+ if (ls) {
193
+ const list = { t: "list", ordered: ls.ordered, start: ls.start, loose: false, items: [] };
194
+ let prevContent = ls.content;
195
+ while (i < n) {
196
+ const s = listStart(lines[i]);
197
+ if (!s || s.key !== ls.key || s.ordered !== ls.ordered || s.indent >= prevContent) break;
198
+ const body = [s.rest];
199
+ const ci = s.content;
200
+ prevContent = ci;
201
+ i++;
202
+ let inFence = !!fenceStart(s.rest);
203
+ let blankInside = false;
204
+ while (i < n) {
205
+ const l = lines[i];
206
+ if (blank(l)) {
207
+ body.push("");
208
+ i++;
209
+ continue;
210
+ }
211
+ if (indentOf(l) >= ci) {
212
+ const stripped = l.slice(ci);
213
+ if (body.length && blank(body[body.length - 1]) && !inFence) blankInside = true;
214
+ if (fenceStart(stripped)) inFence = !inFence;
215
+ body.push(stripped);
216
+ i++;
217
+ continue;
218
+ }
219
+ if (blank(body[body.length - 1])) break;
220
+ if (!interrupts(l) && !listStart(l) && !inFence) {
221
+ body.push(l);
222
+ i++;
223
+ continue;
224
+ }
225
+ break;
226
+ }
227
+ let trailing = false;
228
+ while (body.length > 1 && blank(body[body.length - 1])) {
229
+ body.pop();
230
+ trailing = true;
231
+ }
232
+ if (blankInside) list.loose = true;
233
+ const next = i < n ? listStart(lines[i]) : null;
234
+ if (trailing && next && next.key === ls.key && next.indent < ci) list.loose = true;
235
+ let task = null;
236
+ const tm = body[0].match(/^\[([ xX])\](?:[ \t]+(.*)|$)/);
237
+ if (tm) {
238
+ task = tm[1] !== " ";
239
+ body[0] = tm[2] || "";
240
+ }
241
+ list.items.push({ task, children: parseBlocks(body, refs) });
242
+ if (trailing && !(next && next.key === ls.key)) break;
243
+ }
244
+ out.push(list);
245
+ continue;
246
+ }
247
+ if (i + 1 < n && line.includes("|")) {
248
+ const align = delimiterRow(lines[i + 1]);
249
+ const head = splitCells(line);
250
+ if (align && align.length === head.length) {
251
+ const rows = [];
252
+ i += 2;
253
+ while (i < n && !blank(lines[i]) && !interrupts(lines[i])) {
254
+ const cells = splitCells(lines[i]);
255
+ rows.push(head.map((_, k) => (cells[k] === undefined ? "" : cells[k])));
256
+ i++;
257
+ }
258
+ out.push({ t: "table", align, head, rows });
259
+ continue;
260
+ }
261
+ }
262
+ const para = [line];
263
+ i++;
264
+ let setext = 0;
265
+ while (i < n && !blank(lines[i])) {
266
+ const l = lines[i];
267
+ if (/^ {0,3}=+[ \t]*$/.test(l)) {
268
+ setext = 1;
269
+ i++;
270
+ break;
271
+ }
272
+ if (/^ {0,3}-+[ \t]*$/.test(l)) {
273
+ setext = 2;
274
+ i++;
275
+ break;
276
+ }
277
+ if (interrupts(l)) break;
278
+ if (l.includes("|") && i + 1 < n && delimiterRow(lines[i + 1])) break;
279
+ para.push(l);
280
+ i++;
281
+ }
282
+ while (para.length) {
283
+ const d = para[0].match(REF_DEF);
284
+ if (!d) break;
285
+ const label = normalizeLabel(d[1]);
286
+ if (label && !refs.has(label)) refs.set(label, { href: d[2].replace(/^<|>$/g, ""), title: unquote(d[3]) });
287
+ para.shift();
288
+ }
289
+ if (!para.length) continue;
290
+ const text = para.map((l) => l.replace(/^ {0,3}/, "")).join("\n").replace(/[ \t]+$/, "");
291
+ out.push(setext ? { t: "heading", level: setext, text: text.trim() } : { t: "para", text });
292
+ }
293
+ return out;
294
+ }
295
+
296
+ function findClose(src, from) {
297
+ let depth = 0;
298
+ for (let i = from; i < src.length; i++) {
299
+ const c = src[i];
300
+ if (c === "\\") {
301
+ i++;
302
+ continue;
303
+ }
304
+ if (c === "`") {
305
+ let k = 1;
306
+ while (src[i + k] === "`") k++;
307
+ const end = src.indexOf("`".repeat(k), i + k);
308
+ if (end > 0) i = end + k - 1;
309
+ else i += k - 1;
310
+ continue;
311
+ }
312
+ if (c === "[") depth++;
313
+ else if (c === "]") {
314
+ if (depth === 0) return i;
315
+ depth--;
316
+ }
317
+ }
318
+ return -1;
319
+ }
320
+
321
+ function parseDestination(src, at) {
322
+ let i = at;
323
+ if (src[i] !== "(") return null;
324
+ i++;
325
+ while (/[ \t\n]/.test(src[i] || "")) i++;
326
+ let href = "";
327
+ if (src[i] === "<") {
328
+ const end = src.indexOf(">", i);
329
+ if (end < 0 || src.slice(i, end).includes("\n")) return null;
330
+ href = src.slice(i + 1, end);
331
+ i = end + 1;
332
+ } else {
333
+ let depth = 0;
334
+ const startHref = i;
335
+ while (i < src.length) {
336
+ const c = src[i];
337
+ if (c === "\\" && i + 1 < src.length) {
338
+ i += 2;
339
+ continue;
340
+ }
341
+ if (/[\s]/.test(c)) break;
342
+ if (c === "(") depth++;
343
+ if (c === ")") {
344
+ if (depth === 0) break;
345
+ depth--;
346
+ }
347
+ i++;
348
+ }
349
+ href = src.slice(startHref, i);
350
+ }
351
+ while (/[ \t\n]/.test(src[i] || "")) i++;
352
+ let title = "";
353
+ const q = src[i];
354
+ if (q === '"' || q === "'" || q === "(") {
355
+ const closer = q === "(" ? ")" : q;
356
+ let j = i + 1;
357
+ while (j < src.length && src[j] !== closer) j += src[j] === "\\" ? 2 : 1;
358
+ if (j >= src.length) return null;
359
+ title = src.slice(i + 1, j).replace(/\\(.)/g, "$1");
360
+ i = j + 1;
361
+ while (/[ \t\n]/.test(src[i] || "")) i++;
362
+ }
363
+ if (src[i] !== ")") return null;
364
+ return { href, title, end: i + 1 };
365
+ }
366
+
367
+ function plainText(html) {
368
+ return html.replace(/<[^>]*>/g, "").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
369
+ }
370
+
371
+ function emphasis(s) {
372
+ return s
373
+ .replace(/\*\*\*(?=[^\s*])([\s\S]*?[^\s*])\*\*\*/g, "<strong><em>$1</em></strong>")
374
+ .replace(/(^|[^\w])___(?=[^\s_])([\s\S]*?[^\s_])___(?![\w])/g, "$1<strong><em>$2</em></strong>")
375
+ .replace(/\*\*(?=[^\s*]|\*[^\s*])([\s\S]*?[^\s])\*\*/g, "<strong>$1</strong>")
376
+ .replace(/(^|[^\w\\])__(?=\S)([\s\S]*?[^\s\\])__(?![\w])/g, "$1<strong>$2</strong>")
377
+ .replace(/(^|[^\\*])\*(?=[^\s*])([\s\S]*?[^\s\\*])\*(?!\*)/g, "$1<em>$2</em>")
378
+ .replace(/(^|[^\w\\])_(?=[^\s_])([\s\S]*?[^\s\\_])_(?![\w])/g, "$1<em>$2</em>")
379
+ .replace(/(^|[^\\~])~~(?=\S)([\s\S]*?\S)~~/g, "$1<del>$2</del>");
380
+ }
381
+
382
+ function linkTag(href, title, inner) {
383
+ return '<a href="' + escapeHtml(safeUrl(href)) + '"' + (title ? ' title="' + escapeHtml(title) + '"' : "") + ">" + inner + "</a>";
384
+ }
385
+
386
+ export function renderInline(src, refs = new Map(), inLink = false) {
387
+ const store = [];
388
+ const put = (html) => OPEN + (store.push(html) - 1) + CLOSE;
389
+ let out = "";
390
+ let i = 0;
391
+ const len = src.length;
392
+ while (i < len) {
393
+ const c = src[i];
394
+ if (c === "\\" && i + 1 < len) {
395
+ const d = src[i + 1];
396
+ if (d === "\n") {
397
+ out += put("<br>\n");
398
+ i += 2;
399
+ continue;
400
+ }
401
+ if (PUNCT.test(d)) {
402
+ out += put(escapeHtml(d));
403
+ i += 2;
404
+ continue;
405
+ }
406
+ }
407
+ if (c === "`") {
408
+ let k = 1;
409
+ while (src[i + k] === "`") k++;
410
+ const fence = "`".repeat(k);
411
+ let end = i + k;
412
+ let found = -1;
413
+ while ((end = src.indexOf(fence, end)) >= 0) {
414
+ if (src[end + k] !== "`" && src[end - 1] !== "`") {
415
+ found = end;
416
+ break;
417
+ }
418
+ while (src[end] === "`") end++;
419
+ }
420
+ if (found >= 0) {
421
+ let code = src.slice(i + k, found).replace(/\n/g, " ");
422
+ if (/^ .*[^ ].* $/.test(code)) code = code.slice(1, -1);
423
+ out += put("<code>" + escapeHtml(code) + "</code>");
424
+ i = found + k;
425
+ } else {
426
+ out += fence;
427
+ i += k;
428
+ }
429
+ continue;
430
+ }
431
+ if ((c === "!" && src[i + 1] === "[") || (c === "[" && !inLink)) {
432
+ const image = c === "!";
433
+ const open = image ? i + 1 : i;
434
+ const close = findClose(src, open + 1);
435
+ if (close > 0) {
436
+ const text = src.slice(open + 1, close);
437
+ let target = parseDestination(src, close + 1);
438
+ let end = target ? target.end : -1;
439
+ if (!target) {
440
+ let label = text;
441
+ end = close + 1;
442
+ const ref = src.slice(close + 1).match(/^\[((?:[^\]\\]|\\.)*)\]/);
443
+ if (ref) {
444
+ if (ref[1].trim()) label = ref[1];
445
+ end = close + 1 + ref[0].length;
446
+ }
447
+ const def = refs.get(normalizeLabel(label));
448
+ if (def) target = { href: def.href, title: def.title };
449
+ }
450
+ if (target) {
451
+ if (image) {
452
+ const alt = plainText(renderInline(text, refs, true));
453
+ out += put('<img src="' + escapeHtml(safeUrl(target.href, { image: true })) + '" alt="' + escapeHtml(alt) + '"' + (target.title ? ' title="' + escapeHtml(target.title) + '"' : "") + ">");
454
+ } else {
455
+ out += put(linkTag(target.href, target.title, renderInline(text, refs, true)));
456
+ }
457
+ i = end;
458
+ continue;
459
+ }
460
+ }
461
+ out += image ? "!" : "[";
462
+ i++;
463
+ continue;
464
+ }
465
+ if (c === "<") {
466
+ const rest = src.slice(i);
467
+ const auto = rest.match(/^<([a-zA-Z][a-zA-Z0-9+.-]{1,31}:[^\s<>]*)>/);
468
+ const mail = rest.match(/^<([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/);
469
+ if (auto && !inLink) {
470
+ out += put(linkTag(auto[1], "", escapeHtml(auto[1])));
471
+ i += auto[0].length;
472
+ continue;
473
+ }
474
+ if (mail && !inLink) {
475
+ out += put(linkTag("mailto:" + mail[1], "", escapeHtml(mail[1])));
476
+ i += mail[0].length;
477
+ continue;
478
+ }
479
+ out += "&lt;";
480
+ i++;
481
+ continue;
482
+ }
483
+ if (!inLink && (c === "h" || c === "H") && (i === 0 || /[\s(*_~]/.test(src[i - 1]))) {
484
+ const m = src.slice(i).match(/^https?:\/\/[^\s<]+/i);
485
+ if (m) {
486
+ let url = m[0];
487
+ while (/[.,:;!?'"*_~]$/.test(url) || (url.endsWith(")") && (url.match(/\(/g) || []).length < (url.match(/\)/g) || []).length)) url = url.slice(0, -1);
488
+ out += put(linkTag(url, "", escapeHtml(url)));
489
+ i += url.length;
490
+ continue;
491
+ }
492
+ }
493
+ if (c === "\n") {
494
+ if (/ {2,}$/.test(out)) {
495
+ out = out.replace(/ +$/, "");
496
+ out += put("<br>\n");
497
+ } else {
498
+ out = out.replace(/ +$/, "") + "\n";
499
+ }
500
+ i++;
501
+ while (src[i] === " ") i++;
502
+ continue;
503
+ }
504
+ if (c === "&") {
505
+ const e = src.slice(i).match(ENTITY);
506
+ if (e) {
507
+ out += put(e[0]);
508
+ i += e[0].length;
509
+ continue;
510
+ }
511
+ out += "&amp;";
512
+ i++;
513
+ continue;
514
+ }
515
+ out += c === ">" ? "&gt;" : c === '"' ? "&quot;" : c === "'" ? "&#39;" : c;
516
+ i++;
517
+ }
518
+ out = emphasis(out);
519
+ const restore = (s) => s.replace(/\ue000(\d+)\ue001/g, (_, k) => restore(store[+k]));
520
+ return restore(out);
521
+ }
522
+
523
+ function slugify(text, ids) {
524
+ let base = plainText(text).toLowerCase().trim().replace(/[^\p{L}\p{N}\s-]/gu, "").replace(/\s+/g, "-") || "section";
525
+ let id = base;
526
+ let k = 1;
527
+ while (ids.has(id)) id = base + "-" + k++;
528
+ ids.add(id);
529
+ return id;
530
+ }
531
+
532
+ function renderBlocks(blocks, ctx, tight) {
533
+ const out = [];
534
+ for (const b of blocks) {
535
+ if (b.t === "para") {
536
+ const inner = renderInline(b.text, ctx.refs);
537
+ out.push(tight ? inner : "<p>" + inner + "</p>");
538
+ } else if (b.t === "heading") {
539
+ const inner = renderInline(b.text, ctx.refs);
540
+ const id = ctx.ids ? ' id="' + escapeHtml(slugify(inner, ctx.ids)) + '"' : "";
541
+ out.push("<h" + b.level + id + ">" + inner + "</h" + b.level + ">");
542
+ } else if (b.t === "hr") {
543
+ out.push("<hr>");
544
+ } else if (b.t === "code") {
545
+ const lang = b.lang.replace(/[^\w+#.-]/g, "");
546
+ out.push("<pre><code" + (lang ? ' class="language-' + escapeHtml(lang) + '"' : "") + ">" + escapeHtml(b.text) + (b.text ? "\n" : "") + "</code></pre>");
547
+ } else if (b.t === "quote") {
548
+ out.push("<blockquote>\n" + renderBlocks(b.children, ctx, false) + "\n</blockquote>");
549
+ } else if (b.t === "list") {
550
+ const tag = b.ordered ? "ol" : "ul";
551
+ const hasTask = b.items.some((it) => it.task !== null);
552
+ const open = "<" + tag + (b.ordered && b.start !== 1 ? ' start="' + b.start + '"' : "") + (hasTask ? ' class="tasks"' : "") + ">";
553
+ const items = b.items.map((it) => {
554
+ const box = it.task === null ? "" : '<input type="checkbox" disabled' + (it.task ? " checked" : "") + "> ";
555
+ const body = renderBlocks(it.children, ctx, !b.loose);
556
+ const cls = it.task === null ? "" : ' class="task"';
557
+ if (!b.loose) return "<li" + cls + ">" + box + body + "</li>";
558
+ return "<li" + cls + ">" + (box ? box.trim() + "\n" : "") + body + "</li>";
559
+ });
560
+ out.push(open + "\n" + items.join("\n") + "\n</" + tag + ">");
561
+ } else if (b.t === "table") {
562
+ const cell = (tag, text, k) => "<" + tag + (b.align[k] ? ' style="text-align:' + b.align[k] + '"' : "") + ">" + renderInline(text, ctx.refs) + "</" + tag + ">";
563
+ const head = "<thead>\n<tr>" + b.head.map((t, k) => cell("th", t, k)).join("") + "</tr>\n</thead>";
564
+ const body = b.rows.length ? "\n<tbody>\n" + b.rows.map((r) => "<tr>" + r.map((t, k) => cell("td", t, k)).join("") + "</tr>").join("\n") + "\n</tbody>" : "";
565
+ out.push("<table>\n" + head + body + "\n</table>");
566
+ }
567
+ }
568
+ return out.join(tight ? "\n" : "\n");
569
+ }
570
+
571
+ export function renderMarkdown(src, { headingIds = true } = {}) {
572
+ const text = String(src == null ? "" : src).replace(/\r\n?/g, "\n").replace(/[\ue000\ue001\u0000]/g, "");
573
+ const lines = text.split("\n").map(expandTabs);
574
+ const refs = new Map();
575
+ const blocks = parseBlocks(lines, refs);
576
+ const html = renderBlocks(blocks, { refs, ids: headingIds ? new Set() : null }, false);
577
+ return html ? html + "\n" : "";
578
+ }
579
+
580
+ export function markdownDocument(bodyHtml, title = "Document") {
581
+ return (
582
+ "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>" +
583
+ escapeHtml(title) +
584
+ "</title>\n<style>\nbody{max-width:760px;margin:40px auto;padding:0 20px;font:17px/1.6 system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif;color:#1a1916;background:#fff}\n" +
585
+ "h1,h2,h3{line-height:1.25}a{color:#b23a08}img{max-width:100%}\npre{padding:14px 16px;overflow:auto;border-radius:8px;background:#f4f1ea}code{font:0.92em ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n" +
586
+ "p code,li code,td code{padding:1px 5px;border-radius:4px;background:#f4f1ea}blockquote{margin:0;padding:2px 18px;border-left:4px solid #ddd5c6;color:#5a554b}\n" +
587
+ "table{border-collapse:collapse}th,td{padding:6px 12px;border:1px solid #ddd5c6}ul.tasks{list-style:none;padding-left:4px}hr{border:0;border-top:1px solid #ddd5c6}\n</style>\n</head>\n<body>\n" +
588
+ bodyHtml +
589
+ "</body>\n</html>\n"
590
+ );
591
+ }
592
+
593
+ export function markdownTitle(src) {
594
+ const m = String(src).match(/^ {0,3}#{1,6}[ \t]+(.+?)[ \t#]*$/m);
595
+ return m ? plainText(renderInline(m[1])).trim() : "";
596
+ }