brookmd 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +1229 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1265 -0
  4. package/dist/block-props.d.ts +18 -0
  5. package/dist/block-props.js +75 -0
  6. package/dist/client.d.ts +370 -0
  7. package/dist/client.js +754 -0
  8. package/dist/decorate.d.ts +24 -0
  9. package/dist/decorate.js +71 -0
  10. package/dist/dom.d.ts +130 -0
  11. package/dist/dom.js +627 -0
  12. package/dist/element.d.ts +20 -0
  13. package/dist/element.js +288 -0
  14. package/dist/hi.d.ts +12 -0
  15. package/dist/hi.js +215 -0
  16. package/dist/html-to-react.d.ts +61 -0
  17. package/dist/html-to-react.js +338 -0
  18. package/dist/index.d.ts +22 -0
  19. package/dist/index.js +18 -0
  20. package/dist/morph.d.ts +28 -0
  21. package/dist/morph.js +166 -0
  22. package/dist/react.d.ts +236 -0
  23. package/dist/react.js +539 -0
  24. package/dist/renderers/CodeBlock.d.ts +7 -0
  25. package/dist/renderers/CodeBlock.js +75 -0
  26. package/dist/renderers/Math.d.ts +14 -0
  27. package/dist/renderers/Math.js +15 -0
  28. package/dist/renderers/Mermaid.d.ts +13 -0
  29. package/dist/renderers/Mermaid.js +15 -0
  30. package/dist/server-react.d.ts +32 -0
  31. package/dist/server-react.js +48 -0
  32. package/dist/server.d.ts +31 -0
  33. package/dist/server.js +82 -0
  34. package/dist/solid.d.ts +104 -0
  35. package/dist/solid.js +54 -0
  36. package/dist/styles.css +188 -0
  37. package/dist/svelte.d.ts +80 -0
  38. package/dist/svelte.js +59 -0
  39. package/dist/types-core.d.ts +436 -0
  40. package/dist/types-core.js +0 -0
  41. package/dist/types-react.d.ts +13 -0
  42. package/dist/types-react.js +0 -0
  43. package/dist/types.d.ts +2 -0
  44. package/dist/types.js +2 -0
  45. package/dist/url-safety.d.ts +12 -0
  46. package/dist/url-safety.js +45 -0
  47. package/dist/vue.d.ts +94 -0
  48. package/dist/vue.js +79 -0
  49. package/dist/wasm/LICENSE +21 -0
  50. package/dist/wasm/README.md +71 -0
  51. package/dist/wasm/brook_md_core.d.ts +166 -0
  52. package/dist/wasm/brook_md_core.js +512 -0
  53. package/dist/wasm/brook_md_core_bg.wasm +0 -0
  54. package/dist/wasm/brook_md_core_bg.wasm.d.ts +26 -0
  55. package/dist/worker-core.d.ts +65 -0
  56. package/dist/worker-core.js +155 -0
  57. package/dist/worker.d.ts +1 -0
  58. package/dist/worker.js +49 -0
  59. package/package.json +87 -0
package/dist/dom.js ADDED
@@ -0,0 +1,627 @@
1
+ import { highlight } from "./hi.js";
2
+ import { morph } from "./morph.js";
3
+ import { blockProps, extractLang } from "./block-props.js";
4
+ import { decorateSegments } from "./decorate.js";
5
+ import { safeUrl } from "./url-safety.js";
6
+ const INTRINSIC_PX = {
7
+ Paragraph: 80,
8
+ Heading: 44,
9
+ CodeBlock: 300,
10
+ MathBlock: 140,
11
+ Mermaid: 220,
12
+ List: 120,
13
+ Blockquote: 100,
14
+ Alert: 120,
15
+ Table: 200,
16
+ Rule: 24,
17
+ Html: 80,
18
+ Component: 120
19
+ };
20
+ function mountBrookMarkdown(client, container, options = {}) {
21
+ if (typeof document === "undefined") {
22
+ throw new Error("mountBrookMarkdown is browser-only; call it after the DOM exists.");
23
+ }
24
+ const components = options.components && Object.keys(options.components).length > 0 ? options.components : void 0;
25
+ const { sanitize, virtualize, stickToBottom, onRenderMetrics } = options;
26
+ const decorators = options.decorators && options.decorators.length > 0 ? options.decorators : void 0;
27
+ const urlTransform = options.urlTransform;
28
+ const hasInlineTransforms = !!decorators || !!urlTransform;
29
+ const hasPerf = typeof performance !== "undefined";
30
+ const highlightCode = options.highlightCode !== false && !components?.CodeBlock;
31
+ const batch = options.batch !== false && typeof requestAnimationFrame === "function";
32
+ const morphOpenBlocks = options.morphOpenBlocks === true;
33
+ const root = document.createElement("div");
34
+ root.className = options.className ? `brook-md ${options.className}` : "brook-md";
35
+ if (options.id) root.id = options.id;
36
+ if (options.role) root.setAttribute("role", options.role);
37
+ if (options.ariaLive) root.setAttribute("aria-live", options.ariaLive);
38
+ if (options.ariaAtomic !== void 0) root.setAttribute("aria-atomic", String(options.ariaAtomic));
39
+ container.appendChild(root);
40
+ let anchor = null;
41
+ if (stickToBottom) {
42
+ anchor = document.createElement("div");
43
+ anchor.className = "brook-bottom-anchor";
44
+ anchor.setAttribute("aria-hidden", "true");
45
+ anchor.style.scrollSnapAlign = "end";
46
+ root.appendChild(anchor);
47
+ }
48
+ const mounted = /* @__PURE__ */ new Map();
49
+ let order = [];
50
+ let dead = false;
51
+ let frame = 0;
52
+ let lastRenderGeneric = false;
53
+ function sync() {
54
+ if (dead) return;
55
+ const snapshot = client.getSnapshot();
56
+ const nextOrder = new Array(snapshot.length);
57
+ const seen = /* @__PURE__ */ new Set();
58
+ for (let i = 0; i < snapshot.length; i++) {
59
+ const b = snapshot[i];
60
+ nextOrder[i] = b.id;
61
+ seen.add(b.id);
62
+ const existing = mounted.get(b.id);
63
+ if (!existing) {
64
+ const t02 = onRenderMetrics && hasPerf ? performance.now() : 0;
65
+ const mb = {
66
+ id: b.id,
67
+ node: void 0,
68
+ html: b.html,
69
+ open: b.open,
70
+ speculative: b.speculative,
71
+ kind: b.kind.type,
72
+ renderCount: 0,
73
+ toggleCount: 0,
74
+ generic: false
75
+ };
76
+ mb.node = renderBlock(b, mb);
77
+ mb.generic = lastRenderGeneric;
78
+ mounted.set(b.id, mb);
79
+ if (onRenderMetrics) noteRender(mb, b, t02);
80
+ continue;
81
+ }
82
+ if (existing.html === b.html && existing.open === b.open && existing.speculative === b.speculative) {
83
+ continue;
84
+ }
85
+ const t0 = onRenderMetrics && hasPerf ? performance.now() : 0;
86
+ if (existing.table && b.open && b.kind.type === "Table") {
87
+ const data = tableData(b);
88
+ if (data) {
89
+ syncTbody(existing.table, data);
90
+ if (onRenderMetrics) noteRender(existing, b, t0);
91
+ if (existing.speculative !== b.speculative) {
92
+ existing.node.classList.toggle("brook-speculative", b.speculative);
93
+ }
94
+ existing.html = b.html;
95
+ existing.open = b.open;
96
+ existing.speculative = b.speculative;
97
+ continue;
98
+ }
99
+ }
100
+ if (morphOpenBlocks && b.open && existing.open && existing.generic && existing.kind === b.kind.type && usesGenericPath(b)) {
101
+ morph(existing.node, sanitize ? sanitize(b.html) : b.html);
102
+ if (onRenderMetrics) noteRender(existing, b, t0);
103
+ existing.html = b.html;
104
+ existing.speculative = b.speculative;
105
+ existing.node.className = genericClassName(b);
106
+ existing.generic = !sanitize;
107
+ continue;
108
+ }
109
+ if (!sanitize && existing.generic && existing.kind === b.kind.type && existing.open === b.open && existing.speculative === b.speculative && b.html.length > existing.html.length && b.html.startsWith(existing.html) && isDepth0Boundary(existing.html, b.html)) {
110
+ existing.node.insertAdjacentHTML("beforeend", b.html.slice(existing.html.length));
111
+ if (onRenderMetrics) noteRender(existing, b, t0);
112
+ existing.html = b.html;
113
+ continue;
114
+ }
115
+ existing.table = void 0;
116
+ const node = renderBlock(b, existing);
117
+ existing.node.replaceWith(node);
118
+ existing.node = node;
119
+ if (onRenderMetrics) noteRender(existing, b, t0);
120
+ existing.html = b.html;
121
+ existing.open = b.open;
122
+ existing.speculative = b.speculative;
123
+ existing.kind = b.kind.type;
124
+ existing.generic = lastRenderGeneric;
125
+ }
126
+ if (mounted.size > seen.size) {
127
+ for (const [id, mb] of mounted) {
128
+ if (!seen.has(id)) {
129
+ mb.node.remove();
130
+ mounted.delete(id);
131
+ }
132
+ }
133
+ }
134
+ order = nextOrder;
135
+ reconcileChildren();
136
+ }
137
+ function noteRender(mb, b, t0) {
138
+ mb.renderCount++;
139
+ if (mb.speculative !== b.speculative) mb.toggleCount++;
140
+ client.__noteRebuild();
141
+ onRenderMetrics(b.id, {
142
+ renderCount: mb.renderCount,
143
+ speculativeToggleCount: mb.toggleCount,
144
+ lastRenderMs: hasPerf ? performance.now() - t0 : 0,
145
+ kind: b.kind.type
146
+ });
147
+ }
148
+ function reconcileChildren() {
149
+ let cursor = root.firstChild;
150
+ for (let i = 0; i < order.length; i++) {
151
+ const mb = mounted.get(order[i]);
152
+ if (!mb) continue;
153
+ const want = mb.node;
154
+ if (cursor === want) {
155
+ cursor = want.nextSibling;
156
+ continue;
157
+ }
158
+ root.insertBefore(want, cursor);
159
+ }
160
+ }
161
+ function renderBlock(b, mb) {
162
+ const content = renderBlockContent(b, mb);
163
+ if (virtualize && !b.open && !b.speculative) {
164
+ const px = INTRINSIC_PX[b.kind.type] ?? 120;
165
+ content.style.contentVisibility = "auto";
166
+ content.style.containIntrinsicSize = `auto ${px}px`;
167
+ }
168
+ return content;
169
+ }
170
+ function renderBlockContent(b, mb) {
171
+ const kind = b.kind.type;
172
+ lastRenderGeneric = false;
173
+ if (components) {
174
+ if (kind === "Component") {
175
+ const tag = b.kind.data?.tag;
176
+ const override = tag && components[tag] || components.Component;
177
+ if (override) return wrapOverrideResult(override(blockProps(b)));
178
+ }
179
+ const blockOverride = components[kind];
180
+ if (blockOverride) return wrapOverrideResult(blockOverride(blockProps(b)));
181
+ }
182
+ switch (kind) {
183
+ case "CodeBlock":
184
+ if (highlightCode) return renderCodeBlock(b);
185
+ break;
186
+ // fall through to the generic path
187
+ case "MathBlock":
188
+ return renderMathBlock(b);
189
+ case "Mermaid":
190
+ return renderMermaid(b);
191
+ }
192
+ if (kind === "Table" && b.open && !hasInlineTransforms) {
193
+ const data = tableData(b);
194
+ if (data) return buildKeyedTable(b, data, mb);
195
+ }
196
+ if (b.open && kind === "List" && !hasInlineTransforms) {
197
+ const keyed = renderKeyedList(b);
198
+ if (keyed) return keyed;
199
+ }
200
+ const node = document.createElement("div");
201
+ node.className = genericClassName(b);
202
+ if (b.open && !sanitize && !hasInlineTransforms && (kind === "Blockquote" || kind === "Alert")) {
203
+ const wrapper = renderKeyedContainer(b);
204
+ if (wrapper) {
205
+ node.appendChild(wrapper);
206
+ return node;
207
+ }
208
+ }
209
+ node.innerHTML = sanitize ? sanitize(b.html) : b.html;
210
+ if (decorators) decorateDomSubtree(node, decorators);
211
+ if (urlTransform) applyUrlTransformDom(node, urlTransform);
212
+ lastRenderGeneric = !sanitize && !hasInlineTransforms;
213
+ return node;
214
+ }
215
+ function renderKeyedList(b) {
216
+ const ld = b.kind.data;
217
+ const items = ld?.items;
218
+ if (!Array.isArray(items) || items.length === 0) return null;
219
+ const node = document.createElement("div");
220
+ node.className = "brook-block brook-block-list" + (b.open ? " brook-open" : "") + (b.speculative ? " brook-speculative" : "");
221
+ const list = document.createElement(ld?.ordered ? "ol" : "ul");
222
+ if (ld?.ordered && ld.start !== void 0 && ld.start !== 1) {
223
+ list.setAttribute("start", String(ld.start));
224
+ }
225
+ for (const it of items) {
226
+ const li = document.createElement("li");
227
+ li.innerHTML = sanitize ? sanitize(it.html) : it.html;
228
+ list.appendChild(li);
229
+ }
230
+ node.appendChild(list);
231
+ return node;
232
+ }
233
+ function renderKeyedContainer(b) {
234
+ const nested = b.kind.data?.nested;
235
+ if (!Array.isArray(nested)) return null;
236
+ const tagName = b.kind.type === "Alert" ? "div" : "blockquote";
237
+ const wrapper = document.createElement(tagName);
238
+ applyOpenTagAttrs(wrapper, b.html);
239
+ if (b.kind.type === "Alert") {
240
+ const title = alertTitleHtml(b.html);
241
+ if (title) {
242
+ const t = document.createElement("div");
243
+ t.innerHTML = title;
244
+ const titleNode = t.firstElementChild;
245
+ if (titleNode) wrapper.appendChild(titleNode);
246
+ }
247
+ }
248
+ for (let i = 0; i < nested.length; i++) {
249
+ const child = document.createElement("div");
250
+ child.innerHTML = nested[i].html;
251
+ const inner = child.firstElementChild;
252
+ wrapper.appendChild(inner ?? child);
253
+ }
254
+ return wrapper;
255
+ }
256
+ function buildKeyedTable(b, data, mb) {
257
+ const node = document.createElement("div");
258
+ node.className = "brook-block brook-block-table brook-open" + (b.speculative ? " brook-speculative" : "");
259
+ const table = document.createElement("table");
260
+ if (b.html.startsWith('<table dir="auto"')) table.setAttribute("dir", "auto");
261
+ const scope = b.html.includes('<th scope="col"');
262
+ const thead = document.createElement("thead");
263
+ const htr = document.createElement("tr");
264
+ for (let j = 0; j < data.headers.length; j++) {
265
+ htr.appendChild(makeCell("th", data.headers[j].html, data.aligns[j] ?? null, scope));
266
+ }
267
+ thead.appendChild(htr);
268
+ table.appendChild(thead);
269
+ const km = { table, tbody: null, scope, committed: 0, lastRow: null };
270
+ mb.table = km;
271
+ node.appendChild(table);
272
+ syncTbody(km, data);
273
+ return node;
274
+ }
275
+ function syncTbody(km, data) {
276
+ const n = data.rows.length;
277
+ if (n === 0) {
278
+ if (km.tbody) {
279
+ km.tbody.remove();
280
+ km.tbody = null;
281
+ }
282
+ km.committed = 0;
283
+ km.lastRow = null;
284
+ return;
285
+ }
286
+ if (!km.tbody) {
287
+ km.tbody = document.createElement("tbody");
288
+ km.table.appendChild(km.tbody);
289
+ }
290
+ const tbody = km.tbody;
291
+ if (km.lastRow) {
292
+ km.lastRow.remove();
293
+ km.lastRow = null;
294
+ }
295
+ for (let i = km.committed; i < n - 1; i++) {
296
+ tbody.appendChild(makeRow(data.rows[i], data.aligns));
297
+ }
298
+ km.committed = n - 1;
299
+ const last = makeRow(data.rows[n - 1], data.aligns);
300
+ tbody.appendChild(last);
301
+ km.lastRow = last;
302
+ }
303
+ function makeRow(cells, aligns) {
304
+ const tr = document.createElement("tr");
305
+ for (let j = 0; j < cells.length; j++) {
306
+ tr.appendChild(makeCell("td", cells[j].html, aligns[j] ?? null, false));
307
+ }
308
+ return tr;
309
+ }
310
+ function makeCell(tag, html, align, scope) {
311
+ const cell = document.createElement(tag);
312
+ if (tag === "th" && scope) cell.setAttribute("scope", "col");
313
+ if (align) cell.style.textAlign = align;
314
+ cell.innerHTML = sanitize ? sanitize(html) : html;
315
+ return cell;
316
+ }
317
+ function genericClassName(b) {
318
+ return "brook-block brook-block-" + b.kind.type.toLowerCase() + (b.open ? " brook-open" : "") + (b.speculative ? " brook-speculative" : "");
319
+ }
320
+ function usesGenericPath(b) {
321
+ const kind = b.kind.type;
322
+ if (components) {
323
+ if (kind === "Component") {
324
+ const tag = b.kind.data?.tag;
325
+ if (tag && components[tag] || components.Component) return false;
326
+ }
327
+ if (components[kind]) return false;
328
+ }
329
+ if (kind === "CodeBlock") return !highlightCode;
330
+ if (kind === "MathBlock" || kind === "Mermaid") return false;
331
+ return true;
332
+ }
333
+ function wrapOverrideResult(result) {
334
+ if (typeof result === "string") {
335
+ const node = document.createElement("div");
336
+ node.innerHTML = result;
337
+ return node;
338
+ }
339
+ return result;
340
+ }
341
+ function renderCodeBlock(b) {
342
+ const lang = extractLang(b.html) || "text";
343
+ const text = b.open ? "" : decodeCodeText(b.html);
344
+ const highlighted = text ? highlight(text, lang) : null;
345
+ const block = document.createElement("div");
346
+ block.className = "brook-code-block" + (b.open ? " brook-streaming" : "");
347
+ const header = document.createElement("div");
348
+ header.className = "brook-code-header";
349
+ const langSpan = document.createElement("span");
350
+ langSpan.className = "brook-code-lang";
351
+ langSpan.textContent = lang;
352
+ header.appendChild(langSpan);
353
+ if (b.open) {
354
+ const pill = document.createElement("span");
355
+ pill.className = "brook-code-streaming-pill";
356
+ pill.textContent = "streaming";
357
+ header.appendChild(pill);
358
+ } else {
359
+ header.appendChild(makeCopyButton(text));
360
+ }
361
+ block.appendChild(header);
362
+ const body = document.createElement("div");
363
+ body.className = "brook-code-body";
364
+ if (highlighted) {
365
+ const pre = document.createElement("pre");
366
+ pre.tabIndex = 0;
367
+ pre.setAttribute("role", "region");
368
+ pre.setAttribute("aria-label", `${lang} code`);
369
+ const code = document.createElement("code");
370
+ code.innerHTML = highlighted;
371
+ pre.appendChild(code);
372
+ body.appendChild(pre);
373
+ } else {
374
+ const div = document.createElement("div");
375
+ div.tabIndex = 0;
376
+ div.setAttribute("role", "region");
377
+ div.setAttribute("aria-label", `${lang} code`);
378
+ div.innerHTML = b.html;
379
+ body.appendChild(div);
380
+ }
381
+ block.appendChild(body);
382
+ return block;
383
+ }
384
+ function renderMathBlock(b) {
385
+ const block = document.createElement("div");
386
+ block.className = "brook-math-block" + (b.open ? " brook-streaming" : "");
387
+ const header = document.createElement("div");
388
+ header.className = "brook-math-header";
389
+ const lang = document.createElement("span");
390
+ lang.className = "brook-math-lang";
391
+ lang.textContent = "math";
392
+ header.appendChild(lang);
393
+ if (b.open) header.appendChild(streamingPill());
394
+ block.appendChild(header);
395
+ const body = document.createElement("div");
396
+ body.className = "brook-math-body";
397
+ body.innerHTML = b.html;
398
+ block.appendChild(body);
399
+ return block;
400
+ }
401
+ function renderMermaid(b) {
402
+ const block = document.createElement("div");
403
+ block.className = "brook-mermaid-block" + (b.open ? " brook-streaming" : "");
404
+ const header = document.createElement("div");
405
+ header.className = "brook-mermaid-header";
406
+ const lang = document.createElement("span");
407
+ lang.className = "brook-mermaid-lang";
408
+ lang.textContent = "mermaid";
409
+ header.appendChild(lang);
410
+ if (b.open) header.appendChild(streamingPill());
411
+ block.appendChild(header);
412
+ const body = document.createElement("div");
413
+ body.className = "brook-mermaid-body";
414
+ body.innerHTML = b.html;
415
+ block.appendChild(body);
416
+ return block;
417
+ }
418
+ function streamingPill() {
419
+ const pill = document.createElement("span");
420
+ pill.className = "brook-code-streaming-pill";
421
+ pill.textContent = "streaming";
422
+ return pill;
423
+ }
424
+ const COPY_ICON = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"></rect><path d="M5 15V5a2 2 0 0 1 2-2h10"></path></svg><span>Copy</span>';
425
+ const COPIED_ICON = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"></path></svg><span>Copied</span>';
426
+ function makeCopyButton(text) {
427
+ const btn = document.createElement("button");
428
+ btn.type = "button";
429
+ btn.className = "brook-code-copy";
430
+ btn.setAttribute("aria-label", "Copy code");
431
+ btn.setAttribute("aria-live", "polite");
432
+ btn.innerHTML = COPY_ICON;
433
+ let timer = null;
434
+ btn.addEventListener("click", () => {
435
+ const clip = typeof navigator !== "undefined" ? navigator.clipboard : void 0;
436
+ if (!clip || !clip.writeText || !text) return;
437
+ clip.writeText(text).then(
438
+ () => {
439
+ btn.setAttribute("aria-label", "Copied");
440
+ btn.innerHTML = COPIED_ICON;
441
+ if (timer !== null) clearTimeout(timer);
442
+ timer = setTimeout(() => {
443
+ btn.setAttribute("aria-label", "Copy code");
444
+ btn.innerHTML = COPY_ICON;
445
+ }, 1500);
446
+ },
447
+ // Permission denied / blocked: stay silent, leave button usable.
448
+ () => {
449
+ }
450
+ );
451
+ });
452
+ return btn;
453
+ }
454
+ const unsubscribe = client.subscribe(() => {
455
+ if (dead) return;
456
+ if (batch) {
457
+ if (frame === 0) frame = requestAnimationFrame(flush);
458
+ } else {
459
+ sync();
460
+ }
461
+ });
462
+ function flush() {
463
+ frame = 0;
464
+ sync();
465
+ }
466
+ sync();
467
+ return {
468
+ destroy() {
469
+ if (dead) return;
470
+ dead = true;
471
+ if (frame !== 0) {
472
+ cancelAnimationFrame(frame);
473
+ frame = 0;
474
+ }
475
+ unsubscribe();
476
+ root.remove();
477
+ },
478
+ refresh() {
479
+ if (dead) return;
480
+ sync();
481
+ },
482
+ openBlockId() {
483
+ return tailOpenBlockId(client.getSnapshot());
484
+ }
485
+ };
486
+ }
487
+ function tableData(b) {
488
+ if (b.kind.type !== "Table") return void 0;
489
+ const data = b.kind.data;
490
+ if (!data || !Array.isArray(data.rows) || !Array.isArray(data.aligns) || !Array.isArray(data.headers)) {
491
+ return void 0;
492
+ }
493
+ return data;
494
+ }
495
+ const VOID_ELEMENTS = /* @__PURE__ */ new Set([
496
+ "area",
497
+ "base",
498
+ "br",
499
+ "col",
500
+ "embed",
501
+ "hr",
502
+ "img",
503
+ "input",
504
+ "link",
505
+ "meta",
506
+ "param",
507
+ "source",
508
+ "track",
509
+ "wbr"
510
+ ]);
511
+ function isDepth0Boundary(prefix, full) {
512
+ const c0 = full.charCodeAt(prefix.length);
513
+ if (c0 !== 60) return false;
514
+ const c1 = full.charCodeAt(prefix.length + 1);
515
+ const isLetter = c1 >= 65 && c1 <= 90 || c1 >= 97 && c1 <= 122;
516
+ if (!isLetter) return false;
517
+ let depth = 0;
518
+ let i = 0;
519
+ const n = prefix.length;
520
+ while (i < n) {
521
+ const lt = prefix.indexOf("<", i);
522
+ if (lt === -1) break;
523
+ i = lt + 1;
524
+ if (i >= n) return false;
525
+ const ch = prefix.charCodeAt(i);
526
+ if (ch === 33 || ch === 63) return false;
527
+ let closing = false;
528
+ if (ch === 47) {
529
+ closing = true;
530
+ i++;
531
+ }
532
+ const nameStart = i;
533
+ while (i < n) {
534
+ const t = prefix.charCodeAt(i);
535
+ const nameChar = t >= 65 && t <= 90 || t >= 97 && t <= 122 || t >= 48 && t <= 57 || t === 45;
536
+ if (!nameChar) break;
537
+ i++;
538
+ }
539
+ if (i === nameStart) return false;
540
+ const name = prefix.slice(nameStart, i).toLowerCase();
541
+ const gt = prefix.indexOf(">", i);
542
+ if (gt === -1) return false;
543
+ const selfClosing = prefix.charCodeAt(gt - 1) === 47;
544
+ i = gt + 1;
545
+ if (closing) {
546
+ depth--;
547
+ if (depth < 0) return false;
548
+ } else if (!selfClosing && !VOID_ELEMENTS.has(name)) {
549
+ depth++;
550
+ }
551
+ }
552
+ return depth === 0;
553
+ }
554
+ function tailOpenBlockId(snapshot) {
555
+ const tail = snapshot.length > 0 ? snapshot[snapshot.length - 1] : void 0;
556
+ return tail && tail.open ? tail.id : null;
557
+ }
558
+ function decodeCodeText(html) {
559
+ const m = html.match(/<pre><code[^>]*>([\s\S]*?)<\/code><\/pre>/);
560
+ if (!m) return "";
561
+ return m[1].replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&amp;/g, "&");
562
+ }
563
+ const CONTAINER_ATTR_RE = /([a-zA-Z][a-zA-Z0-9-]*)="([^"]*)"/g;
564
+ function applyOpenTagAttrs(el, html) {
565
+ const gt = html.indexOf(">");
566
+ const open = gt < 0 ? html : html.slice(0, gt);
567
+ let m;
568
+ CONTAINER_ATTR_RE.lastIndex = 0;
569
+ while (m = CONTAINER_ATTR_RE.exec(open)) {
570
+ const name = m[1].toLowerCase();
571
+ if (name === "class" || name === "dir" || name === "role" || name.startsWith("data-")) {
572
+ el.setAttribute(name, m[2]);
573
+ }
574
+ }
575
+ }
576
+ function alertTitleHtml(html) {
577
+ const m = html.match(/<p class="markdown-alert-title"[^>]*>[\s\S]*?<\/p>/);
578
+ return m ? m[0] : "";
579
+ }
580
+ function ancestorTags(node, root) {
581
+ const out = [];
582
+ let p = node.parentNode;
583
+ while (p && p !== root) {
584
+ if (p.tagName) out.push(p.tagName.toLowerCase());
585
+ p = p.parentNode;
586
+ }
587
+ return out;
588
+ }
589
+ const SHOW_ALL = 4294967295;
590
+ function decorateDomSubtree(root, decorators) {
591
+ const walker = document.createTreeWalker(root, SHOW_ALL);
592
+ const texts = [];
593
+ for (let n = walker.nextNode(); n; n = walker.nextNode()) {
594
+ if (n.nodeType === 3) texts.push(n);
595
+ }
596
+ for (const t of texts) {
597
+ const segs = decorateSegments(t.data, decorators, ancestorTags(t, root));
598
+ if (segs === null) continue;
599
+ const frag = document.createDocumentFragment();
600
+ for (const s of segs) {
601
+ if (s.type === "text") {
602
+ frag.appendChild(document.createTextNode(s.text));
603
+ continue;
604
+ }
605
+ const replacement = s.decorator.replace(s.matchText, s.groups);
606
+ if (replacement == null) continue;
607
+ frag.appendChild(typeof replacement === "string" ? document.createTextNode(replacement) : replacement);
608
+ }
609
+ t.parentNode?.replaceChild(frag, t);
610
+ }
611
+ }
612
+ function applyUrlTransformDom(root, urlTransform) {
613
+ const els = root.querySelectorAll("[href],[src],[poster]");
614
+ const attrs = ["href", "src", "poster"];
615
+ els.forEach((el) => {
616
+ for (const attr of attrs) {
617
+ if (!el.hasAttribute(attr)) continue;
618
+ const cur = el.getAttribute(attr) ?? "";
619
+ const next = safeUrl(urlTransform(safeUrl(cur), { tag: el.tagName.toLowerCase(), attr }));
620
+ el.setAttribute(attr, next);
621
+ }
622
+ });
623
+ }
624
+ export {
625
+ mountBrookMarkdown,
626
+ tailOpenBlockId
627
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `<brook-markdown>` custom element — thin lifecycle glue over
3
+ * {@link mountBrookMarkdown}. It owns no diffing: connect mounts the DOM
4
+ * renderer into the element itself (LIGHT DOM, so the host app's markdown CSS
5
+ * reaches the content), disconnect tears the mount down. It never reimplements
6
+ * subscribe/patch.
7
+ *
8
+ * Two usage modes:
9
+ * - **Caller-owned client** (`el.client = myClient`): the element subscribes
10
+ * and mounts but NEVER destroys the client — the caller owns the
11
+ * worker/stream lifecycle.
12
+ * - **Self-owned client** (`markdown`/`src`/`textContent` attrs, or
13
+ * `el.append()`): the element lazily creates an internal client from its
14
+ * config attributes and destroys it on disconnect.
15
+ *
16
+ * Not auto-registered (SSR-unsafe): call {@link defineBrookMarkdown} from
17
+ * browser code.
18
+ */
19
+ export declare function parseTriBool(value: string | null): boolean | undefined;
20
+ export declare function defineBrookMarkdown(tag?: string): void;