@linloir/limr 0.1.0 → 1.0.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.
Files changed (97) hide show
  1. package/README.en.md +151 -0
  2. package/README.md +80 -127
  3. package/benchmarks/results/layout.json +2593 -0
  4. package/benchmarks/results/markstream-code.json +1823 -0
  5. package/benchmarks/results/markstream-reading-repeat.json +1152 -0
  6. package/benchmarks/results/markstream.json +12486 -0
  7. package/benchmarks/results/streaming-browser.json +77 -0
  8. package/benchmarks/results/streaming.json +357 -0
  9. package/dist/base.css +93 -10
  10. package/dist/{chunk-7JF5HHVF.js → chunk-BG5QUQFR.js} +340 -72
  11. package/dist/chunk-BG5QUQFR.js.map +1 -0
  12. package/dist/chunk-BVHHOVMT.js +179 -0
  13. package/dist/chunk-BVHHOVMT.js.map +1 -0
  14. package/dist/chunk-FJZCQY2S.js +155 -0
  15. package/dist/chunk-FJZCQY2S.js.map +1 -0
  16. package/dist/chunk-FSI5OTBW.js +1 -0
  17. package/dist/{chunk-ETHLPBKQ.js → chunk-GU2RPESF.js} +238 -131
  18. package/dist/chunk-GU2RPESF.js.map +1 -0
  19. package/dist/{chunk-65V5OZ4G.js → chunk-SUAAUGAX.js} +1350 -511
  20. package/dist/chunk-SUAAUGAX.js.map +1 -0
  21. package/dist/core/index.d.ts +6 -3
  22. package/dist/core/index.js +15 -5
  23. package/dist/extensions/index.d.ts +30 -5
  24. package/dist/extensions/index.js +15 -3
  25. package/dist/index.d.ts +4 -4
  26. package/dist/index.js +31 -8
  27. package/dist/{engine-43u8B4yt.d.ts → knuth-plass-C8mgFm7W.d.ts} +67 -1
  28. package/dist/knuth-plass-layout-CHQCWKB5.js +449 -0
  29. package/dist/knuth-plass-layout-CHQCWKB5.js.map +1 -0
  30. package/dist/react/index.d.ts +7 -5
  31. package/dist/react/index.js +7 -3
  32. package/dist/styles.css +93 -10
  33. package/dist/{translations-BliabrPd.d.ts → translations-BXzSwLBJ.d.ts} +49 -4
  34. package/docs/README.md +13 -0
  35. package/docs/api.md +5 -158
  36. package/docs/assets/rendered.svg +9 -0
  37. package/docs/assets/source.svg +9 -0
  38. package/docs/en/README.md +28 -0
  39. package/docs/en/ai-skills.md +57 -0
  40. package/docs/en/annotations.md +86 -0
  41. package/docs/en/api.md +115 -0
  42. package/docs/en/architecture.md +41 -0
  43. package/docs/en/extensions.md +208 -0
  44. package/docs/en/getting-started.md +69 -0
  45. package/docs/en/integration.md +154 -0
  46. package/docs/en/modules.md +84 -0
  47. package/docs/en/performance.md +93 -0
  48. package/docs/en/plugin-packages.md +68 -0
  49. package/docs/en/styles.md +99 -0
  50. package/docs/en/syntax.md +455 -0
  51. package/docs/extensions.md +5 -306
  52. package/docs/implementation.md +5 -76
  53. package/docs/performance.md +5 -81
  54. package/docs/screenshots/blocks.png +0 -0
  55. package/docs/screenshots/dark.png +0 -0
  56. package/docs/screenshots/desktop.png +0 -0
  57. package/docs/screenshots/plugin-mobile.png +0 -0
  58. package/docs/styles.md +5 -100
  59. package/docs/visual-upgrade.md +5 -145
  60. package/docs/zh-CN/README.md +28 -0
  61. package/docs/zh-CN/ai-skills.md +57 -0
  62. package/docs/zh-CN/annotations.md +86 -0
  63. package/docs/zh-CN/api.md +115 -0
  64. package/docs/zh-CN/architecture.md +41 -0
  65. package/docs/zh-CN/extensions.md +210 -0
  66. package/docs/zh-CN/getting-started.md +69 -0
  67. package/docs/zh-CN/integration.md +154 -0
  68. package/docs/zh-CN/modules.md +84 -0
  69. package/docs/zh-CN/performance.md +93 -0
  70. package/docs/zh-CN/plugin-packages.md +68 -0
  71. package/docs/zh-CN/styles.md +99 -0
  72. package/docs/zh-CN/syntax.md +457 -0
  73. package/examples/notes.tsx +18 -11
  74. package/examples/packages/paper-theme/index.css +25 -0
  75. package/examples/packages/paper-theme/package.json +7 -0
  76. package/examples/packages/reading-tools/index.d.ts +2 -0
  77. package/examples/packages/reading-tools/index.js +25 -0
  78. package/examples/packages/reading-tools/package.json +16 -0
  79. package/package.json +17 -5
  80. package/skills/limr-plugin-development/SKILL.md +22 -0
  81. package/skills/limr-plugin-development/agents/openai.yaml +4 -0
  82. package/skills/limr-plugin-development/assets/plugin-template/README.md +30 -0
  83. package/skills/limr-plugin-development/assets/plugin-template/package.json +36 -0
  84. package/skills/limr-plugin-development/assets/plugin-template/src/index.tsx +50 -0
  85. package/skills/limr-plugin-development/assets/plugin-template/src/styles.css +9 -0
  86. package/skills/limr-plugin-development/assets/plugin-template/tsconfig.json +20 -0
  87. package/skills/limr-plugin-development/assets/plugin-template/tsup.config.ts +8 -0
  88. package/skills/limr-plugin-development/references/contract.md +51 -0
  89. package/skills/limr-use/SKILL.md +23 -0
  90. package/skills/limr-use/agents/openai.yaml +4 -0
  91. package/skills/limr-use/references/integration.md +49 -0
  92. package/skills/limr-use/references/syntax.md +20 -0
  93. package/dist/chunk-65V5OZ4G.js.map +0 -1
  94. package/dist/chunk-7JF5HHVF.js.map +0 -1
  95. package/dist/chunk-ETHLPBKQ.js.map +0 -1
  96. package/dist/chunk-V2OG4Y3F.js +0 -1
  97. /package/dist/{chunk-V2OG4Y3F.js.map → chunk-FSI5OTBW.js.map} +0 -0
@@ -1,16 +1,25 @@
1
1
  import {
2
+ annotationReferenceRange,
2
3
  createDocument,
3
4
  defaultHighlight,
4
- intersects,
5
5
  mapText,
6
- rangeOf,
7
6
  resolveAnnotationAnchors,
8
- safeUrl,
9
7
  sameRange,
10
- selectionFromRange,
8
+ selectionFromRange
9
+ } from "./chunk-BG5QUQFR.js";
10
+ import {
11
+ atomicSelectionText,
12
+ leavesIn,
13
+ measurementRange,
14
+ textRange
15
+ } from "./chunk-BVHHOVMT.js";
16
+ import {
17
+ intersects,
18
+ rangeOf,
19
+ safeUrl,
11
20
  textContent,
12
21
  walk
13
- } from "./chunk-7JF5HHVF.js";
22
+ } from "./chunk-FJZCQY2S.js";
14
23
  import {
15
24
  ResourceCache
16
25
  } from "./chunk-6N6TFHEN.js";
@@ -56,6 +65,8 @@ function scrollWithMotion(root, top) {
56
65
 
57
66
  // src/react/runtime.ts
58
67
  var english = {
68
+ language: "en",
69
+ citation: "Citation",
59
70
  contents: "Contents",
60
71
  annotations: "Annotations",
61
72
  expand: "Expand",
@@ -91,10 +102,13 @@ var english = {
91
102
  highlightWavy: "Wavy",
92
103
  hideHighlight: "Hide highlight",
93
104
  showHighlight: "Show highlight",
105
+ inlineHighlightHint: "Markdown inline highlight, editing unavailable",
94
106
  deleteAnnotations: "Delete",
95
107
  deleteAnnotationsHint: "Deletes {count} annotations on this text and any highlight."
96
108
  };
97
109
  var chinese = {
110
+ language: "zh-CN",
111
+ citation: "\u5F15\u7528",
98
112
  contents: "\u76EE\u5F55",
99
113
  annotations: "\u6CE8\u91CA",
100
114
  expand: "\u5C55\u5F00",
@@ -130,6 +144,7 @@ var chinese = {
130
144
  highlightWavy: "\u6CE2\u6D6A\u7EBF",
131
145
  hideHighlight: "\u9690\u85CF\u9AD8\u4EAE",
132
146
  showHighlight: "\u663E\u793A\u9AD8\u4EAE",
147
+ inlineHighlightHint: "Markdown \u5185\u5D4C\u9AD8\u4EAE\uFF0C\u4E0D\u652F\u6301\u7F16\u8F91",
133
148
  deleteAnnotations: "\u5220\u9664",
134
149
  deleteAnnotationsHint: "\u5C06\u5220\u9664\u8FD9\u6BB5\u6587\u5B57\u5173\u8054\u7684 {count} \u4E2A\u6CE8\u91CA\u53CA\u9AD8\u4EAE\u3002"
135
150
  };
@@ -158,7 +173,7 @@ var ReaderRuntime = class {
158
173
  layout = new Store(0);
159
174
  viewport = new Store(0);
160
175
  documentOffset = new Store(0);
161
- decorations = new Store({ annotations: /* @__PURE__ */ new Map(), highlights: /* @__PURE__ */ new Map(), occurrences: [], byBlock: /* @__PURE__ */ new Map() });
176
+ decorations = new Store({ annotations: /* @__PURE__ */ new Map(), highlights: /* @__PURE__ */ new Map(), occurrences: [], referenceRanges: /* @__PURE__ */ new Map(), byBlock: /* @__PURE__ */ new Map() });
162
177
  configuration;
163
178
  resources = new ResourceCache(128);
164
179
  resourceVersions = new Store(/* @__PURE__ */ new Map());
@@ -170,7 +185,9 @@ var ReaderRuntime = class {
170
185
  bindings = /* @__PURE__ */ new WeakMap();
171
186
  textBinders = /* @__PURE__ */ new WeakMap();
172
187
  components = {};
188
+ rendererOverrides = /* @__PURE__ */ new Map();
173
189
  annotationTypes = /* @__PURE__ */ new Map();
190
+ resolvedTypes = /* @__PURE__ */ new WeakMap();
174
191
  locale = english;
175
192
  content = null;
176
193
  selectionRange = null;
@@ -190,7 +207,7 @@ var ReaderRuntime = class {
190
207
  addAnnotation: (annotation, options = {}) => {
191
208
  this.changeAnnotations((items) => {
192
209
  const next = [...items.filter((item) => item.id !== annotation.id), annotation];
193
- if (annotation.type !== "highlight" && options.highlight !== false) {
210
+ if (annotation.type !== "highlight" && options.highlight !== false && (options.highlight !== void 0 || this.annotationTypes.get(annotation.type)?.highlight !== "hover")) {
194
211
  const doc = this.document.getSnapshot();
195
212
  for (const anchor of resolveAnnotationAnchors(annotation, doc)) {
196
213
  if (anchor.range.start === anchor.range.end || next.some((item) => item.type === "highlight" && resolveAnnotationAnchors(item, doc).some((other) => sameRange(other.range, anchor.range)))) continue;
@@ -270,10 +287,24 @@ var ReaderRuntime = class {
270
287
  });
271
288
  }
272
289
  installConfiguration(config) {
290
+ this.locale = { ...english, ...config.locale };
273
291
  this.components = Object.assign({}, ...(config.extensions ?? []).map((ext) => ext.components), config.components);
292
+ this.rendererOverrides = /* @__PURE__ */ new Map();
293
+ const rules = [
294
+ ...(config.extensions ?? []).flatMap((ext) => ext.rendererOverrides ?? []),
295
+ ...Object.entries(config.components ?? {}).map(([type, component]) => ({ type, component })),
296
+ ...config.rendererOverrides ?? []
297
+ ];
298
+ for (const rule of rules) this.rendererOverrides.set(rule.type, [...this.rendererOverrides.get(rule.type) ?? [], rule]);
274
299
  const annotationTypes = [...(config.extensions ?? []).flatMap((ext) => [...ext.annotationTypes ?? []]), ...config.annotationTypes ?? []];
275
- this.annotationTypes = new Map(annotationTypes.map((type) => [type.type, type]));
276
- this.locale = { ...english, ...config.locale };
300
+ this.annotationTypes = new Map(annotationTypes.map((type) => {
301
+ if (typeof type.label === "string") return [type.type, type];
302
+ const label = type.label(this.locale);
303
+ const previous = this.resolvedTypes.get(type);
304
+ const resolved = previous?.label === label ? previous : { ...type, label };
305
+ this.resolvedTypes.set(type, resolved);
306
+ return [type.type, resolved];
307
+ }));
277
308
  }
278
309
  configure(config) {
279
310
  const old = this.configuration.get();
@@ -281,7 +312,7 @@ var ReaderRuntime = class {
281
312
  this.installConfiguration(config);
282
313
  this.configuration.set(config);
283
314
  if (old.extensions !== config.extensions) this.document.setExtensions(config.extensions ?? []);
284
- if (old.annotations !== config.annotations) this.rebuildDecorations();
315
+ else if (old.annotations !== config.annotations || old.annotationTypes !== config.annotationTypes) this.rebuildDecorations();
285
316
  if (old.annotationTypes !== config.annotationTypes || old.extensions !== config.extensions) this.resources.invalidate();
286
317
  }
287
318
  updateInteraction(patch) {
@@ -339,17 +370,29 @@ var ReaderRuntime = class {
339
370
  if ([...versions.keys()].some((id) => !annotations.has(id))) this.resourceVersions.set(new Map([...versions].filter(([id]) => annotations.has(id))));
340
371
  const occurrences = [...doc.occurrences];
341
372
  const allHighlights = [];
342
- for (const annotation of config.annotations ?? this.localAnnotations) {
373
+ for (const annotation of annotations.values()) {
343
374
  resolveAnnotationAnchors(annotation, doc).forEach((anchor, index) => {
344
375
  if (annotation.type === "highlight") {
345
376
  const id = index ? `${annotation.id}:${index}` : annotation.id;
346
377
  const value = { ...annotation.data, id, annotationId: annotation.id, range: anchor.range };
347
378
  const old = previous.highlights.get(id);
348
- allHighlights.push(old && sameRange(old.range, value.range) && old.color === value.color && old.style === value.style && old.hidden === value.hidden ? old : value);
379
+ allHighlights.push(old && !old.occurrenceId && sameRange(old.range, value.range) && old.color === value.color && old.style === value.style && old.hidden === value.hidden && old.readOnly === value.readOnly ? old : value);
349
380
  } else occurrences.push({ ...anchor, id: anchor.id ?? `${doc.id}-${annotation.id}-${index}`, annotationIds: [annotation.id] });
350
381
  });
351
382
  }
352
383
  occurrences.sort((a, b) => a.range.end - b.range.end || a.id.localeCompare(b.id));
384
+ const referenceRanges = new Map(occurrences.map((occurrence) => [occurrence.id, annotationReferenceRange(doc, occurrence)]));
385
+ for (const occurrence of occurrences) {
386
+ const annotation = annotations.get(occurrence.annotationIds[0]);
387
+ const definition2 = annotation && this.annotationTypes.get(annotation.type);
388
+ if (!annotation || definition2?.highlight !== "hover") continue;
389
+ const range = referenceRanges.get(occurrence.id);
390
+ if (range.start === range.end) continue;
391
+ const id = `reference-${occurrence.id}`;
392
+ const value = { id, annotationId: annotation.id, occurrenceId: occurrence.id, range, color: definition2.color ?? defaultHighlight.color, style: "background" };
393
+ const old = previous.highlights.get(id);
394
+ allHighlights.push(old && old.occurrenceId === value.occurrenceId && old.annotationId === value.annotationId && sameRange(old.range, range) && old.color === value.color ? old : value);
395
+ }
353
396
  const byBlock = /* @__PURE__ */ new Map();
354
397
  for (const block of occurrences.length || allHighlights.length ? doc.blocks : []) {
355
398
  const marks = occurrences.filter((item) => !item.embedded && item.range.end > block.range.start && item.range.end <= block.range.end);
@@ -363,7 +406,7 @@ var ReaderRuntime = class {
363
406
  }
364
407
  const sameBlocks = byBlock.size === previous.byBlock.size && [...byBlock].every(([id, value]) => previous.byBlock.get(id) === value);
365
408
  const highlights = new Map(allHighlights.map((item) => [item.id, item]));
366
- this.decorations.set({ annotations, highlights, occurrences, byBlock: sameBlocks ? previous.byBlock : byBlock });
409
+ this.decorations.set({ annotations, highlights, occurrences, referenceRanges, byBlock: sameBlocks ? previous.byBlock : byBlock });
367
410
  const state = this.interaction.get();
368
411
  if (state.editing && !annotations.has(state.editing) && this.pendingOpen !== state.editing) this.updateInteraction({ editing: null });
369
412
  if (state.activeHighlight && !highlights.has(state.activeHighlight)) this.activateHighlight(null);
@@ -514,7 +557,7 @@ var emptyList = EMPTY;
514
557
  import { gfm as syntax } from "micromark-extension-gfm";
515
558
  import { gfmFromMarkdown } from "mdast-util-gfm";
516
559
  function gfm() {
517
- return { name: "gfm", micromark: [syntax()], mdast: gfmFromMarkdown(), scope: "local" };
560
+ return { name: "gfm", directives: ["gfm"], micromark: [syntax()], mdast: gfmFromMarkdown(), scope: "local", components: { gfm: ({ children }) => children } };
518
561
  }
519
562
 
520
563
  // src/extensions/math.tsx
@@ -523,10 +566,10 @@ import { math as syntax2 } from "micromark-extension-math";
523
566
  import { mathFromMarkdown } from "mdast-util-math";
524
567
 
525
568
  // src/components/CopyButton.tsx
526
- import { useEffect as useEffect9, useRef as useRef12, useState as useState9 } from "react";
569
+ import { useEffect as useEffect10, useRef as useRef15, useState as useState10 } from "react";
527
570
 
528
571
  // src/react/context.tsx
529
- import { createContext as createContext2, useCallback as useCallback2, useContext as useContext3, useEffect as useEffect8, useId, useLayoutEffect, useMemo as useMemo4, useRef as useRef11, useState as useState8, useSyncExternalStore } from "react";
572
+ import { createContext as createContext2, useCallback as useCallback3, useContext as useContext3, useEffect as useEffect9, useId, useLayoutEffect, useMemo as useMemo5, useRef as useRef14, useState as useState9, useSyncExternalStore } from "react";
530
573
 
531
574
  // src/extensions/code.tsx
532
575
  import { useEffect, useRef, useState } from "react";
@@ -538,6 +581,16 @@ function literalValue(node) {
538
581
  function mapLiteral(source, node) {
539
582
  const range = rangeOf(node), raw = source.slice(range.start, range.end), value = literalValue(node);
540
583
  const starts = [], ends = [];
584
+ if (node.valueRange) {
585
+ const payload = node.valueRange;
586
+ for (let at = payload.start; at < payload.end && starts.length < value.length; at++) {
587
+ const size = source[at] === "\r" && source[at + 1] === "\n" ? 2 : 1;
588
+ starts.push(at);
589
+ ends.push(at + size);
590
+ at += size - 1;
591
+ }
592
+ return { starts, ends, exact: starts.length === value.length };
593
+ }
541
594
  if (node.type === "inlineCode") {
542
595
  const fence = /^`+/.exec(raw)?.[0].length ?? 0;
543
596
  let from = fence, until = raw.length - fence;
@@ -577,6 +630,24 @@ function mapLiteral(source, node) {
577
630
  return { starts, ends, exact };
578
631
  }
579
632
 
633
+ // src/extensions/literal-directive.ts
634
+ function literalDirective(root, source, name, aliases3 = [name]) {
635
+ walk(root, (node) => {
636
+ if (node.type !== "component" || !aliases3.includes(node.name ?? "") || !node.position) return;
637
+ const start = node.position.start.offset, end = node.position.end.offset;
638
+ const raw = source.slice(start, end);
639
+ const header = /^[^\r\n]*(?:\r\n|\r|\n)/.exec(raw);
640
+ const closing = /(?:\r\n|\r|\n)[ \t]*:{2,}[ \t]*$/.exec(raw);
641
+ const from = start + (header?.[0].length ?? raw.length);
642
+ const until = closing ? start + closing.index : end;
643
+ node.type = name;
644
+ node.value = source.slice(from, until).replace(/\r\n?/g, "\n").replace(node.incomplete ? /\n$/ : /$^/, "");
645
+ node.valueRange = { start: from, end: until };
646
+ if (name === "code") node.lang = String(node.attributes?.lang ?? node.attributes?.language ?? "text");
647
+ node.children = void 0;
648
+ });
649
+ }
650
+
580
651
  // src/extensions/code.tsx
581
652
  import { jsx, jsxs } from "react/jsx-runtime";
582
653
  var cache = new ResourceCache(96);
@@ -606,9 +677,10 @@ function codeHighlight(options = {}) {
606
677
  return () => observer.disconnect();
607
678
  }, [visible, runtime]);
608
679
  useEffect(() => {
609
- if (!visible || code.length > (options.maxLength ?? 1e5)) return;
680
+ if (!visible || pending || code.length > (options.maxLength ?? 1e5)) return;
610
681
  let disposed = false;
611
682
  let release;
683
+ let waiting;
612
684
  const timer = setTimeout(() => {
613
685
  if (normalized === "text" || options.languages && !options.languages.includes(normalized)) return;
614
686
  const request = cache.acquire(JSON.stringify([code, normalized, options.lightTheme, options.darkTheme]), async () => {
@@ -630,14 +702,31 @@ function codeHighlight(options = {}) {
630
702
  });
631
703
  release = request.release;
632
704
  request.promise.then((tokens2) => {
633
- if (!disposed) setResult({ code, tokens: tokens2 });
705
+ const apply = () => {
706
+ if (disposed) return;
707
+ const selection = ref.current?.ownerDocument.getSelection();
708
+ if (selection && !selection.isCollapsed && selection.rangeCount && ref.current && selection.getRangeAt(0).intersectsNode(ref.current)) {
709
+ if (!waiting) {
710
+ waiting = apply;
711
+ document.addEventListener("selectionchange", waiting);
712
+ }
713
+ return;
714
+ }
715
+ if (waiting) {
716
+ document.removeEventListener("selectionchange", waiting);
717
+ waiting = void 0;
718
+ }
719
+ setResult({ code, tokens: tokens2 });
720
+ };
721
+ apply();
634
722
  }).catch(() => {
635
723
  });
636
- }, pending ? 180 : 0);
724
+ }, 0);
637
725
  return () => {
638
726
  disposed = true;
639
727
  clearTimeout(timer);
640
728
  release?.();
729
+ if (waiting) document.removeEventListener("selectionchange", waiting);
641
730
  };
642
731
  }, [code, normalized, pending, visible]);
643
732
  const tokens = result?.code === code ? result.tokens : void 0;
@@ -649,29 +738,29 @@ function codeHighlight(options = {}) {
649
738
  /* @__PURE__ */ jsx("pre", { className: tokens?.length ? "shiki" : void 0, children: /* @__PURE__ */ jsx("code", { children: renderValue(tokens) }) })
650
739
  ] });
651
740
  }
652
- return { name: "code-highlight", scope: "local", components: { code: CodeNode } };
741
+ return { name: "code-highlight", directives: ["code", "code-highlight"], literal: true, scope: "local", components: { code: CodeNode }, transform: (root, context) => literalDirective(root, context.source, "code", ["code", "code-highlight"]) };
653
742
  }
654
743
 
655
744
  // src/extensions/annotations.tsx
656
- import { useMemo as useMemo2, useRef as useRef6 } from "react";
745
+ import { useMemo as useMemo2, useRef as useRef8 } from "react";
657
746
  import { fromMarkdown } from "mdast-util-from-markdown";
658
747
 
659
748
  // src/react/Markdown.tsx
660
- import { useContext, useEffect as useEffect4, useRef as useRef4 } from "react";
749
+ import { useContext, useEffect as useEffect5, useRef as useRef6 } from "react";
661
750
 
662
751
  // src/react/render.tsx
663
- import { cloneElement, Component, createElement, Fragment, isValidElement, memo, useCallback, useMemo, useRef as useRef2 } from "react";
752
+ import { cloneElement, Component as Component2, createElement, Fragment, isValidElement, memo, useCallback as useCallback2, useMemo, useRef as useRef4 } from "react";
664
753
 
665
754
  // src/react/text-binding.ts
666
755
  var atomic = /* @__PURE__ */ new Set(["inlineCode", "code", "math", "inlineMath", "image", "imageReference", "html"]);
667
756
  var hidden = /* @__PURE__ */ new Set(["definition", "footnoteDefinition", "annotationDefinition", "footnoteReference"]);
668
757
  var ordinary = /* @__PURE__ */ new Set(["text", "paragraph", "heading", "strong", "emphasis", "delete", "blockquote", "list", "listItem", "link", "linkReference", "table", "tableRow", "tableCell", "thematicBreak", "break", "annotationReference"]);
669
- function compactTextNodes(node, components) {
758
+ function compactTextNodes(node, components, overrides) {
670
759
  const texts = [];
671
760
  let supported = true;
672
761
  const visit = (node2) => {
673
762
  if (node2.selection === "atomic" || atomic.has(node2.type) || hidden.has(node2.type)) return;
674
- if (!ordinary.has(node2.type) || components[node2.type]) {
763
+ if (!ordinary.has(node2.type) || components[node2.type] || overrides?.has(node2.type)) {
675
764
  supported = false;
676
765
  return;
677
766
  }
@@ -753,8 +842,182 @@ function bindCompactText(element, nodes, runtime) {
753
842
  return clear;
754
843
  }
755
844
 
845
+ // src/react/stream-text.tsx
846
+ import { useCallback, useEffect as useEffect2, useRef as useRef3 } from "react";
847
+
848
+ // src/react/stream-effect.tsx
849
+ import { useRef as useRef2 } from "react";
850
+ import { jsx as jsx2 } from "react/jsx-runtime";
851
+ var defaultStreamEffect = {
852
+ keyframes: [{ opacity: 0 }, { opacity: 1 }],
853
+ timing: { duration: 420, easing: "cubic-bezier(.16,1,.3,1)" }
854
+ };
855
+ function streamEffect(runtime) {
856
+ const config = runtime.configuration.get();
857
+ if (config.streamEffect !== void 0) return config.streamEffect;
858
+ let effect = defaultStreamEffect;
859
+ for (const extension of config.extensions ?? []) if (extension.streaming?.effect !== void 0) effect = extension.streaming.effect;
860
+ return effect;
861
+ }
862
+ function animateStream(element, effect) {
863
+ if (effect === false || !element.animate || matchMedia("(prefers-reduced-motion: reduce)").matches) return;
864
+ return element.animate(effect.keyframes ?? defaultStreamEffect.keyframes, { ...defaultStreamEffect.timing, ...effect.timing });
865
+ }
866
+ function useStreamEntrance(ref, enabled, state = true, contents = false) {
867
+ const runtime = useRuntime();
868
+ useIsomorphicLayoutEffect(() => {
869
+ const host = ref.current;
870
+ const target = contents ? host?.firstElementChild : host;
871
+ if (!enabled || !target) return;
872
+ const effect = streamEffect(runtime);
873
+ const classes = effect && effect.className?.split(/\s+/).filter(Boolean);
874
+ if (classes) target.classList.add(...classes);
875
+ target.dataset.limrEntrance = "";
876
+ const animation = animateStream(target, effect);
877
+ return () => {
878
+ animation?.cancel();
879
+ if (classes) target.classList.remove(...classes);
880
+ };
881
+ }, [runtime, enabled, state]);
882
+ }
883
+ function StreamEntrance({ children, state }) {
884
+ const ref = useRef2(null);
885
+ useStreamEntrance(ref, true, state, true);
886
+ return /* @__PURE__ */ jsx2("div", { ref, className: "limr-stream-block", children });
887
+ }
888
+
889
+ // src/react/stream-text.tsx
890
+ import { jsx as jsx3 } from "react/jsx-runtime";
891
+ function StreamText({ value, animateInitial = true }) {
892
+ const runtime = useRuntime();
893
+ const initial = useRef3(value), previous = useRef3(value);
894
+ const element = useRef3(null);
895
+ const animations = useRef3(/* @__PURE__ */ new Set());
896
+ const waiting = useRef3(false);
897
+ const mounted = useRef3(false);
898
+ const compact = useCallback(() => {
899
+ const host = element.current;
900
+ if (!host) return;
901
+ const selection = host.ownerDocument.getSelection();
902
+ if (selection && !selection.isCollapsed && selection.rangeCount && selection.getRangeAt(0).intersectsNode(host)) {
903
+ if (!waiting.current) {
904
+ host.ownerDocument.addEventListener("selectionchange", compact);
905
+ waiting.current = true;
906
+ }
907
+ return;
908
+ }
909
+ if (waiting.current) {
910
+ host.ownerDocument.removeEventListener("selectionchange", compact);
911
+ waiting.current = false;
912
+ }
913
+ for (const span of host.querySelectorAll("[data-limr-increment-done]")) span.replaceWith(...span.childNodes);
914
+ host.normalize();
915
+ }, []);
916
+ useIsomorphicLayoutEffect(() => {
917
+ const host = element.current;
918
+ if (!host) return;
919
+ const effect = streamEffect(runtime);
920
+ const animate = (element2) => {
921
+ const animation = animateStream(element2, effect);
922
+ if (!animation) return;
923
+ animations.current.add(animation);
924
+ void animation.finished.then(() => {
925
+ animations.current.delete(animation);
926
+ if (element2.hasAttribute("data-limr-increment")) element2.dataset.limrIncrementDone = "";
927
+ compact();
928
+ }, () => animations.current.delete(animation));
929
+ };
930
+ if (!mounted.current) {
931
+ mounted.current = true;
932
+ if (animateInitial && value && runtime.document.getSnapshot().streaming) animate(host);
933
+ }
934
+ if (value === previous.current) return;
935
+ let from = previous.current.length;
936
+ if (!value.startsWith(previous.current)) {
937
+ for (const animation of animations.current) animation.cancel();
938
+ animations.current.clear();
939
+ for (const span of host.querySelectorAll("[data-limr-increment]")) span.dataset.limrIncrementDone = "";
940
+ from = 0;
941
+ while (from < Math.min(previous.current.length, value.length) && previous.current[from] === value[from]) from++;
942
+ const walker = host.ownerDocument.createTreeWalker(host, NodeFilter.SHOW_TEXT);
943
+ const texts = [];
944
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) texts.push(node);
945
+ let at = 0;
946
+ for (const text of texts) {
947
+ const size = text.length;
948
+ if (at >= from) text.remove();
949
+ else if (at + size > from) text.deleteData(from - at, size);
950
+ at += size;
951
+ }
952
+ for (const span of host.querySelectorAll("[data-limr-increment]")) if (!span.textContent) span.remove();
953
+ }
954
+ const addition = value.slice(from);
955
+ if (addition) {
956
+ if (effect === false || !host.animate || matchMedia("(prefers-reduced-motion: reduce)").matches) {
957
+ const tail = host.lastChild;
958
+ if (tail?.nodeType === Node.TEXT_NODE) tail.appendData(addition);
959
+ else host.append(host.ownerDocument.createTextNode(addition));
960
+ } else {
961
+ const span = host.ownerDocument.createElement("span");
962
+ span.dataset.limrIncrement = "";
963
+ span.className = `limr-stream-increment ${effect.className ?? ""}`;
964
+ span.textContent = addition;
965
+ host.append(span);
966
+ animate(span);
967
+ }
968
+ }
969
+ previous.current = value;
970
+ }, [value, runtime]);
971
+ useEffect2(() => {
972
+ const document2 = element.current?.ownerDocument;
973
+ return () => {
974
+ document2?.removeEventListener("selectionchange", compact);
975
+ for (const animation of animations.current) animation.cancel();
976
+ animations.current.clear();
977
+ };
978
+ }, []);
979
+ return /* @__PURE__ */ jsx3("span", { ref: element, "data-limr-stream-text": "", children: initial.current });
980
+ }
981
+
982
+ // src/react/knuth-plass-boundary.tsx
983
+ import { Component } from "react";
984
+ var implementation;
985
+ var KnuthPlassBoundary = class extends Component {
986
+ release;
987
+ version = 0;
988
+ start() {
989
+ const version = ++this.version;
990
+ if (!this.props.enabled || this.props.pending) return;
991
+ implementation ??= import("./knuth-plass-layout-CHQCWKB5.js");
992
+ void implementation.then((module) => {
993
+ if (version !== this.version) return;
994
+ const root = this.props.element();
995
+ if (root) this.release = module.typeset(root, this.props.runtime, this.props.options);
996
+ });
997
+ }
998
+ componentDidMount() {
999
+ this.start();
1000
+ }
1001
+ getSnapshotBeforeUpdate() {
1002
+ this.version++;
1003
+ this.release?.();
1004
+ this.release = void 0;
1005
+ return null;
1006
+ }
1007
+ componentDidUpdate() {
1008
+ this.start();
1009
+ }
1010
+ componentWillUnmount() {
1011
+ this.version++;
1012
+ this.release?.();
1013
+ }
1014
+ render() {
1015
+ return this.props.children;
1016
+ }
1017
+ };
1018
+
756
1019
  // src/react/render.tsx
757
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1020
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
758
1021
  var blockAttributes = (block) => block ? { "data-limr-block": block.id, "data-source-start": block.range.start, "data-source-end": block.range.end, "data-pending": block.pending || void 0 } : {};
759
1022
  function AnnotationMarker({ occurrence }) {
760
1023
  const runtime = useRuntime();
@@ -762,10 +1025,10 @@ function AnnotationMarker({ occurrence }) {
762
1025
  const active = useStoreSelector(runtime.interaction, (state) => state.active === occurrence.id);
763
1026
  const hovered = useStoreSelector(runtime.interaction, (state) => state.hovered === occurrence.id);
764
1027
  const definition2 = useStoreSelector(runtime.configuration, () => runtime.annotationTypes.get(annotation?.type ?? "citation"));
765
- const Icon = definition2?.icon;
1028
+ const Icon2 = definition2?.icon;
766
1029
  const Custom = definition2?.Marker;
767
- const icon = typeof Icon === "function" || Icon && typeof Icon === "object" && !isValidElement(Icon) && "$$typeof" in Icon ? createElement(Icon) : Icon;
768
- return /* @__PURE__ */ jsx2("span", { className: "limr-mark-wrap", "data-limr-ignore": "true", "data-limr-marker": occurrence.id, "data-source-start": occurrence.range.start, "data-source-end": occurrence.range.end, children: /* @__PURE__ */ jsxs2(
1030
+ const icon = typeof Icon2 === "function" || Icon2 && typeof Icon2 === "object" && !isValidElement(Icon2) && "$$typeof" in Icon2 ? createElement(Icon2) : Icon2;
1031
+ return /* @__PURE__ */ jsx4("span", { className: "limr-mark-wrap", "data-limr-ignore": "true", "data-limr-marker": occurrence.id, "data-source-start": occurrence.range.start, "data-source-end": occurrence.range.end, children: /* @__PURE__ */ jsxs2(
769
1032
  "button",
770
1033
  {
771
1034
  type: "button",
@@ -795,8 +1058,8 @@ function AnnotationMarker({ occurrence }) {
795
1058
  if (hovering) runtime.hover(occurrence.id);
796
1059
  },
797
1060
  children: [
798
- /* @__PURE__ */ jsx2("span", { className: "limr-mark-icon", children: Custom && annotation ? /* @__PURE__ */ jsx2(Custom, { annotation, active }) : icon ?? "\u2022" }),
799
- occurrence.annotationIds.length > 1 && /* @__PURE__ */ jsx2("span", { className: "limr-mark-count", children: occurrence.annotationIds.length })
1061
+ /* @__PURE__ */ jsx4("span", { className: "limr-mark-icon", children: Custom && annotation ? /* @__PURE__ */ jsx4(Custom, { annotation, active }) : icon ?? "\u2022" }),
1062
+ occurrence.annotationIds.length > 1 && /* @__PURE__ */ jsx4("span", { className: "limr-mark-count", children: occurrence.annotationIds.length })
800
1063
  ]
801
1064
  }
802
1065
  ) });
@@ -810,7 +1073,7 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
810
1073
  let mapping2;
811
1074
  const raw = source.slice(range.start, range.end);
812
1075
  return () => {
813
- if (literalMapping) return literalMapping;
1076
+ if (literalMapping) return typeof literalMapping === "function" ? literalMapping() : literalMapping;
814
1077
  if (!mapping2) {
815
1078
  const relative = mapText(raw, value, { start: 0, end: raw.length }, { escapes: node.verbatimEscapes !== true });
816
1079
  mapping2 = { starts: relative.starts.map((offset) => offset + range.start), ends: relative.ends.map((offset) => offset + range.start), exact: relative.exact };
@@ -822,7 +1085,7 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
822
1085
  const highlights = context.highlights.filter((highlight) => intersects(highlight.range, range));
823
1086
  if (!marks.length && !highlights.length) {
824
1087
  const direct = !literalMapping && source.slice(range.start, range.end) === value;
825
- return /* @__PURE__ */ jsx2("span", { style, className, "data-limr-text": "", "data-source-start": range.start, "data-source-end": range.end, ref: (element) => {
1088
+ return /* @__PURE__ */ jsx4("span", { style, className, "data-limr-text": "", "data-source-start": range.start, "data-source-end": range.end, ref: (element) => {
826
1089
  if (element) runtime.bindings.set(element, {
827
1090
  range,
828
1091
  atomic: false,
@@ -831,7 +1094,7 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
831
1094
  },
832
1095
  resolve: (offset, edge) => direct ? Math.max(range.start, Math.min(range.end, range.start + offset)) : edge === "start" ? mapped().starts[offset] ?? range.end : mapped().ends[offset - 1] ?? range.start
833
1096
  });
834
- }, children: value });
1097
+ }, children: context.live ? /* @__PURE__ */ jsx4(StreamText, { value, animateInitial: range.end > context.revealFrom }) : value });
835
1098
  }
836
1099
  const mapping = mapped();
837
1100
  const markerAt = /* @__PURE__ */ new Map();
@@ -849,12 +1112,12 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
849
1112
  boundaries.add(end < 0 ? value.length : end);
850
1113
  }
851
1114
  const points = [...boundaries].sort((a, b) => a - b);
852
- return /* @__PURE__ */ jsx2(Fragment2, { children: points.slice(1).map((end, index) => {
1115
+ return /* @__PURE__ */ jsx4(Fragment2, { children: points.slice(1).map((end, index) => {
853
1116
  const start = points[index];
854
1117
  const segmentRange = { start: mapping.starts[start] ?? range.start, end: mapping.ends[end - 1] ?? range.end };
855
1118
  const styles = highlights.filter((highlight) => intersects(highlight.range, segmentRange));
856
- let text = value.slice(start, end);
857
- for (const highlight of styles) text = /* @__PURE__ */ jsx2("mark", { "data-limr-highlight": highlight.id, "data-hidden": highlight.hidden || void 0, className: `limr-highlight limr-highlight-${highlight.style ?? "background"}`, style: { "--limr-highlight": highlight.color ?? "#f3ce7380" }, children: text }, highlight.id);
1119
+ let text = context.live ? /* @__PURE__ */ jsx4(StreamText, { value: value.slice(start, end), animateInitial: segmentRange.end > context.revealFrom }) : value.slice(start, end);
1120
+ for (const highlight of styles) text = /* @__PURE__ */ jsx4("mark", { "data-limr-highlight": highlight.id, "data-hidden": highlight.hidden || void 0, className: `limr-highlight limr-highlight-${highlight.style ?? "background"}`, style: { "--limr-highlight": highlight.color ?? "#f3ce7380" }, children: text }, highlight.id);
858
1121
  return /* @__PURE__ */ jsxs2("span", { style, className, ref: (element) => {
859
1122
  if (element) runtime.bindings.set(element, { range: segmentRange, atomic: false, exact: mapping.exact, resolve: (offset, edge) => {
860
1123
  const at = Math.max(start, Math.min(end, start + offset));
@@ -862,22 +1125,27 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
862
1125
  } });
863
1126
  }, "data-limr-text": "", "data-source-start": segmentRange.start, "data-source-end": segmentRange.end, children: [
864
1127
  text,
865
- markerAt.get(end)?.map((occurrence) => /* @__PURE__ */ jsx2(AnnotationMarker, { occurrence }, occurrence.id))
1128
+ markerAt.get(end)?.map((occurrence) => /* @__PURE__ */ jsx4(AnnotationMarker, { occurrence }, occurrence.id))
866
1129
  ] }, start);
867
1130
  }) });
868
1131
  }
869
1132
  function LiteralValue({ node, context, tokens }) {
870
1133
  const source = context.runtime.document.getSnapshot().source;
871
- const mapping = useMemo(() => mapLiteral(source, node), [node]);
1134
+ const mapped = useMemo(() => {
1135
+ let value2;
1136
+ return () => value2 ??= mapLiteral(source, node);
1137
+ }, [node]);
872
1138
  const value = literalValue(node);
873
1139
  const marks = context.marks.get(node) ?? emptyList;
1140
+ if (!tokens?.length) return /* @__PURE__ */ jsx4(Text, { node: { ...node, type: "text", value }, context, mapping: mapped, marks }, 0);
1141
+ const mapping = mapped();
874
1142
  const segments = tokens?.length ? tokens : [{ content: value, offset: 0 }];
875
- return /* @__PURE__ */ jsx2(Fragment2, { children: segments.map((token, index) => {
1143
+ return /* @__PURE__ */ jsx4(Fragment2, { children: segments.map((token, index) => {
876
1144
  const start = token.offset, end = start + token.content.length;
877
- const mapped = { starts: mapping.starts.slice(start, end), ends: mapping.ends.slice(start, end), exact: mapping.exact };
878
- const range = { start: mapped.starts[0] ?? rangeOf(node).start, end: mapped.ends.at(-1) ?? rangeOf(node).end };
1145
+ const mapped2 = { starts: mapping.starts.slice(start, end), ends: mapping.ends.slice(start, end), exact: mapping.exact };
1146
+ const range = { start: mapped2.starts[0] ?? rangeOf(node).start, end: mapped2.ends.at(-1) ?? rangeOf(node).end };
879
1147
  const leaf = { type: "text", value: token.content, position: { start: { offset: range.start, line: 0, column: 0 }, end: { offset: range.end, line: 0, column: 0 } } };
880
- return /* @__PURE__ */ jsx2(Text, { className: token.style ? "limr-code-token" : void 0, style: token.style, node: leaf, context, mapping: mapped, marks: marks.filter((mark) => mark.range.end > range.start && mark.range.end <= range.end) }, index);
1148
+ return /* @__PURE__ */ jsx4(Text, { className: token.style ? "limr-code-token" : void 0, style: token.style, node: leaf, context, mapping: mapped2, marks: marks.filter((mark) => mark.range.end > range.start && mark.range.end <= range.end) }, index);
881
1149
  }) });
882
1150
  }
883
1151
  function Atomic({ node, context, children, inline, blockElement }) {
@@ -888,7 +1156,7 @@ function Atomic({ node, context, children, inline, blockElement }) {
888
1156
  const image = ["image", "imageReference"].includes(node.type) || node.type === "component" && node.name === "img";
889
1157
  const corner = image || !inline;
890
1158
  const marks = [...context.marks.get(node) ?? [], ...corner ? context.afterMarks.get(node) ?? [] : []];
891
- const markers = marks.map((occurrence) => /* @__PURE__ */ jsx2(AnnotationMarker, { occurrence }, occurrence.id));
1159
+ const markers = marks.map((occurrence) => /* @__PURE__ */ jsx4(AnnotationMarker, { occurrence }, occurrence.id));
892
1160
  return /* @__PURE__ */ jsxs2(Fragment2, { children: [
893
1161
  /* @__PURE__ */ jsxs2(
894
1162
  Tag,
@@ -907,7 +1175,7 @@ function Atomic({ node, context, children, inline, blockElement }) {
907
1175
  },
908
1176
  children: [
909
1177
  children,
910
- corner && !!marks.length && /* @__PURE__ */ jsx2("span", { className: "limr-corner-marks", "data-limr-ignore": "true", children: markers })
1178
+ corner && !!marks.length && /* @__PURE__ */ jsx4("span", { className: "limr-corner-marks", "data-limr-ignore": "true", children: markers })
911
1179
  ]
912
1180
  }
913
1181
  ),
@@ -933,16 +1201,16 @@ function HeadingNode({ node, children, runtime, blockElement }) {
933
1201
  }
934
1202
  function Reference({ node, children, image }) {
935
1203
  const definition2 = useDocumentSelector((doc) => doc.definitions.get(String(node.identifier)));
936
- if (!definition2) return /* @__PURE__ */ jsx2(Fragment2, { children: children || String(node.alt ?? node.label ?? "") });
937
- return image ? /* @__PURE__ */ jsx2("img", { src: safeUrl(definition2.url, true), alt: String(node.alt ?? ""), title: definition2.title ? String(definition2.title) : void 0, loading: "lazy", decoding: "async" }) : /* @__PURE__ */ jsx2("a", { href: safeUrl(definition2.url), title: definition2.title ? String(definition2.title) : void 0, children });
1204
+ if (!definition2) return /* @__PURE__ */ jsx4(Fragment2, { children: children || String(node.alt ?? node.label ?? "") });
1205
+ return image ? /* @__PURE__ */ jsx4("img", { src: safeUrl(definition2.url, true), alt: String(node.alt ?? ""), title: definition2.title ? String(definition2.title) : void 0, loading: "lazy", decoding: "async" }) : /* @__PURE__ */ jsx4("a", { href: safeUrl(definition2.url), title: definition2.title ? String(definition2.title) : void 0, children });
938
1206
  }
939
1207
  function FootnoteReference({ node }) {
940
1208
  const runtime = useRuntime();
941
1209
  const identifier2 = String(node.identifier);
942
1210
  const number = useDocumentSelector((doc) => doc.footnoteOrder.indexOf(identifier2) + 1);
943
- return /* @__PURE__ */ jsx2("sup", { "data-limr-ignore": "true", children: /* @__PURE__ */ jsx2("a", { href: `#${runtime.document.id}-fn-${encodeURIComponent(identifier2)}`, id: `${runtime.document.id}-fnref-${rangeOf(node).start}`, "aria-label": `Footnote ${number}`, children: number }) });
1211
+ return /* @__PURE__ */ jsx4("sup", { "data-limr-ignore": "true", children: /* @__PURE__ */ jsx4("a", { href: `#${runtime.document.id}-fn-${encodeURIComponent(identifier2)}`, id: `${runtime.document.id}-fnref-${rangeOf(node).start}`, "aria-label": `Footnote ${number}`, children: number }) });
944
1212
  }
945
- var ExtensionBoundary = class extends Component {
1213
+ var ExtensionBoundary = class extends Component2 {
946
1214
  state = { failed: false };
947
1215
  static getDerivedStateFromError() {
948
1216
  return { failed: true };
@@ -953,7 +1221,7 @@ var ExtensionBoundary = class extends Component {
953
1221
  if (this.state.failed && previous.fallback !== this.props.fallback) this.setState({ failed: false });
954
1222
  }
955
1223
  render() {
956
- return this.state.failed ? /* @__PURE__ */ jsx2("pre", { className: "limr-extension-error", children: this.props.fallback }) : this.props.children;
1224
+ return this.state.failed ? /* @__PURE__ */ jsx4("pre", { className: "limr-extension-error", children: this.props.fallback }) : this.props.children;
957
1225
  }
958
1226
  };
959
1227
  function containsBlock(node) {
@@ -961,20 +1229,26 @@ function containsBlock(node) {
961
1229
  }
962
1230
  function renderNode(node, context, index = 0, trailing) {
963
1231
  const { runtime } = context;
964
- if (node.type === "text" && context.compact) return node.value ?? "";
1232
+ if (node.type === "text" && context.compact) return context.live ? /* @__PURE__ */ jsx4(StreamText, { value: node.value ?? "", animateInitial: rangeOf(node).end > context.revealFrom }, `text-${node.position?.start.offset ?? index}`) : node.value ?? "";
1233
+ if (node.type === "streamPending") return null;
965
1234
  const blockElement = node === context.block.node ? context.blockElement : void 0;
966
1235
  const key = `${node.type}-${node.position?.start.offset ?? index}-${index}`;
967
- const childContext = node.type === "list" ? { ...context, tightList: !node.spread } : context;
1236
+ let childContext = node.type === "list" ? { ...context, tightList: !node.spread } : context;
1237
+ if (runtime.rendererOverrides.size) childContext = { ...childContext, ancestors: [...context.ancestors ?? [], node] };
968
1238
  const renderChildren = (nodes, options) => {
969
1239
  let last = -1;
970
1240
  nodes.forEach((child, i) => {
971
1241
  if (!["definition", "footnoteDefinition", "annotationDefinition"].includes(child.type)) last = i;
972
1242
  });
973
- if (last < 0 && options?.trailing) return [/* @__PURE__ */ jsx2(Fragment, { children: options.trailing }, "trailing")];
1243
+ if (last < 0 && options?.trailing) return [/* @__PURE__ */ jsx4(Fragment, { children: options.trailing }, "trailing")];
974
1244
  return nodes.map((child, i) => renderNode(child, childContext, i, i === last ? options?.trailing : void 0));
975
1245
  };
976
1246
  const children = node.type === "table" ? [] : node.type === "listItem" && context.tightList ? (node.children ?? []).flatMap((child, index2) => child.type === "paragraph" ? renderChildren(child.children ?? []) : renderNode(child, context, index2)) : renderChildren(node.children ?? []);
977
- const Renderer = runtime.components[node.type === "component" ? node.name ?? "" : node.type];
1247
+ const componentName = node.type === "component" ? node.name ?? "" : node.type;
1248
+ const rules = runtime.rendererOverrides.get(componentName);
1249
+ const override = rules?.slice().reverse().find((rule) => !rule.when || rule.when(node, { pending: context.block.pending, incomplete: !!node.incomplete, ancestors: context.ancestors ?? [] }));
1250
+ const owner = node.incomplete && !override ? runtime.configuration.get().extensions?.slice().reverse().find((extension) => extension.components?.[componentName]) : void 0;
1251
+ const Renderer = override?.component ?? (node.incomplete && owner?.streaming?.Loading || runtime.components[componentName]);
978
1252
  let element;
979
1253
  if (Renderer) {
980
1254
  const range = rangeOf(node);
@@ -982,14 +1256,14 @@ function renderNode(node, context, index = 0, trailing) {
982
1256
  const attribute = node.attributeNodes?.[name];
983
1257
  if (!attribute) return String(node.attributes?.[name] ?? "");
984
1258
  const range2 = rangeOf(attribute);
985
- return /* @__PURE__ */ jsx2(Text, { node: attribute, context, marks: context.occurrences.filter((item) => item.range.end > range2.start && item.range.end <= range2.end) });
1259
+ return /* @__PURE__ */ jsx4(Text, { node: attribute, context, marks: context.occurrences.filter((item) => item.range.end > range2.start && item.range.end <= range2.end) });
986
1260
  };
987
- element = /* @__PURE__ */ jsx2(ExtensionBoundary, { fallback: runtime.document.getSnapshot().source.slice(range.start, range.end), children: /* @__PURE__ */ jsx2(Renderer, { node, children, pending: context.block.pending, source: runtime.document.getSnapshot().source.slice(range.start, range.end), renderChildren, renderValue: (tokens) => /* @__PURE__ */ jsx2(LiteralValue, { node, context, tokens }), renderAttribute }) }, key);
1261
+ element = /* @__PURE__ */ jsx4(ExtensionBoundary, { fallback: runtime.document.getSnapshot().source.slice(range.start, range.end), children: /* @__PURE__ */ jsx4(Renderer, { node, children, pending: context.block.pending, incomplete: !!node.incomplete, source: runtime.document.getSnapshot().source.slice(range.start, range.end), renderChildren, renderValue: (tokens) => /* @__PURE__ */ jsx4(LiteralValue, { node, context, tokens }), renderAttribute }) }, key);
988
1262
  } else {
989
1263
  switch (node.type) {
990
1264
  case "text":
991
1265
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
992
- context.compact ? node.value ?? "" : /* @__PURE__ */ jsx2(Text, { node, context }),
1266
+ context.compact ? node.value ?? "" : /* @__PURE__ */ jsx4(Text, { node, context }),
993
1267
  trailing
994
1268
  ] }, key);
995
1269
  case "paragraph":
@@ -1008,62 +1282,62 @@ function renderNode(node, context, index = 0, trailing) {
1008
1282
  trailing
1009
1283
  ] }, key);
1010
1284
  case "strong":
1011
- element = /* @__PURE__ */ jsx2("strong", { children });
1285
+ element = /* @__PURE__ */ jsx4("strong", { children });
1012
1286
  break;
1013
1287
  case "emphasis":
1014
- element = /* @__PURE__ */ jsx2("em", { children });
1288
+ element = /* @__PURE__ */ jsx4("em", { children });
1015
1289
  break;
1016
1290
  case "delete":
1017
- element = /* @__PURE__ */ jsx2("del", { children });
1291
+ element = /* @__PURE__ */ jsx4("del", { children });
1018
1292
  break;
1019
1293
  case "inlineCode":
1020
- element = /* @__PURE__ */ jsx2("code", { className: "limr-inline-code", "data-limr-inline-syntax": "", "data-source-start": rangeOf(node).start, "data-source-end": rangeOf(node).end, children: /* @__PURE__ */ jsx2(LiteralValue, { node, context }) });
1294
+ element = /* @__PURE__ */ jsx4("code", { className: "limr-inline-code", "data-limr-inline-syntax": "", "data-source-start": rangeOf(node).start, "data-source-end": rangeOf(node).end, children: /* @__PURE__ */ jsx4(LiteralValue, { node, context }) });
1021
1295
  break;
1022
1296
  case "code":
1023
- element = /* @__PURE__ */ jsx2("pre", { children: /* @__PURE__ */ jsx2("code", { children: /* @__PURE__ */ jsx2(LiteralValue, { node, context }) }) });
1297
+ element = /* @__PURE__ */ jsx4("pre", { children: /* @__PURE__ */ jsx4("code", { children: /* @__PURE__ */ jsx4(LiteralValue, { node, context }) }) });
1024
1298
  break;
1025
1299
  case "math":
1026
1300
  case "inlineMath":
1027
- element = /* @__PURE__ */ jsx2("code", { children: node.value });
1301
+ element = /* @__PURE__ */ jsx4("code", { children: node.value });
1028
1302
  break;
1029
1303
  case "blockquote":
1030
1304
  element = /* @__PURE__ */ jsxs2("blockquote", { className: "limr-note", children: [
1031
- /* @__PURE__ */ jsx2("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.note }),
1032
- /* @__PURE__ */ jsx2("div", { className: "limr-block-content", children })
1305
+ /* @__PURE__ */ jsx4("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.note }),
1306
+ /* @__PURE__ */ jsx4("div", { className: "limr-block-content", children })
1033
1307
  ] });
1034
1308
  break;
1035
1309
  case "list":
1036
- element = node.ordered ? /* @__PURE__ */ jsx2("ol", { start: Number(node.start ?? 1), children }) : /* @__PURE__ */ jsx2("ul", { children });
1310
+ element = node.ordered ? /* @__PURE__ */ jsx4("ol", { start: Number(node.start ?? 1), children }) : /* @__PURE__ */ jsx4("ul", { children });
1037
1311
  break;
1038
1312
  case "listItem":
1039
1313
  element = /* @__PURE__ */ jsxs2("li", { className: typeof node.checked === "boolean" ? "limr-task-item" : void 0, children: [
1040
- typeof node.checked === "boolean" && /* @__PURE__ */ jsx2("input", { type: "checkbox", checked: node.checked, disabled: true, "aria-label": "Task" }),
1314
+ typeof node.checked === "boolean" && /* @__PURE__ */ jsx4("input", { type: "checkbox", checked: node.checked, disabled: true, "aria-label": "Task" }),
1041
1315
  children
1042
1316
  ] });
1043
1317
  break;
1044
1318
  case "link":
1045
- element = /* @__PURE__ */ jsx2("a", { href: safeUrl(node.url), title: node.title ? String(node.title) : void 0, children });
1319
+ element = /* @__PURE__ */ jsx4("a", { href: safeUrl(node.url), title: node.title ? String(node.title) : void 0, children });
1046
1320
  break;
1047
1321
  case "image":
1048
- element = /* @__PURE__ */ jsx2("img", { src: safeUrl(node.url, true), alt: String(node.alt ?? ""), title: node.title ? String(node.title) : void 0, loading: "lazy", decoding: "async" });
1322
+ element = /* @__PURE__ */ jsx4("img", { src: safeUrl(node.url, true), alt: String(node.alt ?? ""), title: node.title ? String(node.title) : void 0, loading: "lazy", decoding: "async" });
1049
1323
  break;
1050
1324
  case "linkReference":
1051
- element = /* @__PURE__ */ jsx2(Reference, { node, image: false, children });
1325
+ element = /* @__PURE__ */ jsx4(Reference, { node, image: false, children });
1052
1326
  break;
1053
1327
  case "imageReference":
1054
- element = /* @__PURE__ */ jsx2(Reference, { node, image: true, children });
1328
+ element = /* @__PURE__ */ jsx4(Reference, { node, image: true, children });
1055
1329
  break;
1056
1330
  case "thematicBreak":
1057
- element = /* @__PURE__ */ jsx2("hr", {});
1331
+ element = /* @__PURE__ */ jsx4("hr", {});
1058
1332
  break;
1059
1333
  case "break":
1060
- element = /* @__PURE__ */ jsx2("br", {});
1334
+ element = /* @__PURE__ */ jsx4("br", { "data-limr-break": "", "data-source-start": rangeOf(node).start, "data-source-end": rangeOf(node).end });
1061
1335
  break;
1062
1336
  case "table": {
1063
1337
  const rows = node.children ?? [], alignment = node.align;
1064
- element = /* @__PURE__ */ jsx2("div", { className: "limr-table-scroll", children: /* @__PURE__ */ jsxs2("table", { children: [
1065
- /* @__PURE__ */ jsx2("thead", { children: rows.slice(0, 1).map((row, i) => /* @__PURE__ */ jsx2("tr", { children: row.children?.map((cell, j) => /* @__PURE__ */ jsx2("th", { style: { textAlign: alignment?.[j] }, children: renderChildren(cell.children ?? []) }, j)) }, i)) }),
1066
- /* @__PURE__ */ jsx2("tbody", { children: rows.slice(1).map((row, i) => /* @__PURE__ */ jsx2("tr", { children: row.children?.map((cell, j) => /* @__PURE__ */ jsx2("td", { style: { textAlign: alignment?.[j] }, children: renderChildren(cell.children ?? []) }, j)) }, i)) })
1338
+ element = /* @__PURE__ */ jsx4("div", { className: "limr-table-scroll", children: /* @__PURE__ */ jsxs2("table", { children: [
1339
+ /* @__PURE__ */ jsx4("thead", { children: rows.slice(0, 1).map((row, i) => /* @__PURE__ */ jsx4("tr", { children: row.children?.map((cell, j) => /* @__PURE__ */ jsx4("th", { style: { textAlign: alignment?.[j] }, children: renderChildren(cell.children ?? []) }, j)) }, i)) }),
1340
+ /* @__PURE__ */ jsx4("tbody", { children: rows.slice(1).map((row, i) => /* @__PURE__ */ jsx4("tr", { children: row.children?.map((cell, j) => /* @__PURE__ */ jsx4("td", { style: { textAlign: alignment?.[j] }, children: renderChildren(cell.children ?? []) }, j)) }, i)) })
1067
1341
  ] }) });
1068
1342
  break;
1069
1343
  }
@@ -1073,19 +1347,19 @@ function renderNode(node, context, index = 0, trailing) {
1073
1347
  return null;
1074
1348
  case "footnoteReference":
1075
1349
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
1076
- /* @__PURE__ */ jsx2(FootnoteReference, { node }),
1350
+ /* @__PURE__ */ jsx4(FootnoteReference, { node }),
1077
1351
  trailing
1078
1352
  ] }, key);
1079
1353
  case "annotationReference": {
1080
1354
  const Tag = node.inline ? "span" : "div";
1081
1355
  element = /* @__PURE__ */ jsxs2(Tag, { children: [
1082
1356
  children,
1083
- /* @__PURE__ */ jsx2(AnnotationMarker, { occurrence: { id: `${runtime.document.id}-ref-${rangeOf(node).start}`, range: rangeOf(node), annotationIds: node.annotationIds, embedded: true } })
1357
+ /* @__PURE__ */ jsx4(AnnotationMarker, { occurrence: { id: `${runtime.document.id}-ref-${rangeOf(node).start}`, range: rangeOf(node), annotationIds: node.annotationIds, embedded: true } })
1084
1358
  ] });
1085
1359
  break;
1086
1360
  }
1087
1361
  case "html":
1088
- element = /* @__PURE__ */ jsx2("span", { className: "limr-literal", children: node.value });
1362
+ element = /* @__PURE__ */ jsx4("span", { className: "limr-literal", children: node.value });
1089
1363
  break;
1090
1364
  case "component":
1091
1365
  element = children;
@@ -1097,26 +1371,40 @@ function renderNode(node, context, index = 0, trailing) {
1097
1371
  const atomic2 = node.selection === "atomic" || ["math", "inlineMath", "image", "imageReference", "html"].includes(node.type);
1098
1372
  const corner = atomic2 && (!node.inline && !["inlineCode", "inlineMath", "html"].includes(node.type) || ["image", "imageReference"].includes(node.type) || node.name === "img");
1099
1373
  const after = corner ? void 0 : context.afterMarks.get(node);
1100
- const withMarkers = (element2) => after?.length || trailing ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1101
- element2,
1102
- after?.map((occurrence) => /* @__PURE__ */ jsx2(AnnotationMarker, { occurrence }, occurrence.id)),
1103
- trailing
1104
- ] }, key) : element2;
1105
- if (atomic2) return withMarkers(/* @__PURE__ */ jsx2(Atomic, { node, context, blockElement, inline: node.inline || ["inlineCode", "inlineMath", "image", "imageReference", "html"].includes(node.type), children: element }, key));
1374
+ const withMarkers = (element2) => {
1375
+ const result = after?.length || trailing ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1376
+ element2,
1377
+ after?.map((occurrence) => /* @__PURE__ */ jsx4(AnnotationMarker, { occurrence }, occurrence.id)),
1378
+ trailing
1379
+ ] }, key) : element2;
1380
+ return context.live && !node.inline && (node.type === "component" || ["code", "math", "mermaid", "callout", "blockquote", "table"].includes(node.type)) ? /* @__PURE__ */ jsx4(StreamEntrance, { state: node.type === "component" ? !!node.incomplete : true, children: result }, key) : result;
1381
+ };
1382
+ if (atomic2) return withMarkers(/* @__PURE__ */ jsx4(Atomic, { node, context, blockElement, inline: node.inline || ["inlineCode", "inlineMath", "image", "imageReference", "html"].includes(node.type), children: element }, key));
1106
1383
  if (blockElement) {
1107
1384
  const attributes = { ...blockAttributes(blockElement), ref: blockElement.ref };
1108
- return withMarkers(isValidElement(element) && typeof element.type === "string" ? cloneElement(element, { ...attributes, key }) : /* @__PURE__ */ jsx2("div", { ...attributes, children: element }, key));
1385
+ return withMarkers(isValidElement(element) && typeof element.type === "string" ? cloneElement(element, { ...attributes, key }) : /* @__PURE__ */ jsx4("div", { ...attributes, children: element }, key));
1109
1386
  }
1110
- return withMarkers(isValidElement(element) ? cloneElement(element, { key }) : /* @__PURE__ */ jsx2(Fragment, { children: element }, key));
1387
+ return withMarkers(isValidElement(element) ? cloneElement(element, { key }) : /* @__PURE__ */ jsx4(Fragment, { children: element }, key));
1111
1388
  }
1112
- var BlockView = memo(function BlockView2({ block, runtime, components, extensions, decorations, footnoteId }) {
1113
- const element = useRef2(null);
1114
- const clearBindings = useRef2(void 0);
1115
- const { occurrences, highlights } = decorations;
1389
+ var BlockView = memo(function BlockView2({ block, runtime, components, extensions, rendererOverrides, decorations, footnoteId }) {
1390
+ const streamed = useRef4(runtime.document.getSnapshot().streaming);
1391
+ if (block.pending) streamed.current = true;
1392
+ const revealed = useRef4(block.range.start);
1393
+ const revealFrom = revealed.current;
1394
+ useIsomorphicLayoutEffect(() => {
1395
+ revealed.current = Math.max(revealed.current, block.range.end);
1396
+ }, [block.range.end]);
1397
+ const element = useRef4(null);
1398
+ const getElement = useCallback2(() => element.current, []);
1399
+ const typography = useStoreSelector(runtime.configuration, (config) => config.typography);
1400
+ const knuthPlass = useStoreSelector(runtime.configuration, (config) => config.knuthPlass);
1401
+ const clearBindings = useRef4(void 0);
1402
+ const { occurrences } = decorations;
1403
+ const highlights = useMemo(() => decorations.highlights.filter((highlight) => !highlight.occurrenceId), [decorations.highlights]);
1116
1404
  const root = useMemo(() => footnoteId ? { ...block.node, type: "listItem" } : block.node, [block, footnoteId]);
1117
- const texts = useMemo(() => compactTextNodes(root, runtime.components), [root, runtime, components, extensions]);
1118
- const compact = !!texts && !occurrences.length && !highlights.length;
1119
- const observe = useCallback((next) => {
1405
+ const texts = useMemo(() => compactTextNodes(root, runtime.components, runtime.rendererOverrides), [root, runtime, components, extensions, rendererOverrides]);
1406
+ const compact = typography !== "knuth-plass" && !!texts && !occurrences.length && !highlights.length;
1407
+ const observe = useCallback2((next) => {
1120
1408
  clearBindings.current?.();
1121
1409
  clearBindings.current = void 0;
1122
1410
  if (element.current) runtime.textBinders.delete(element.current);
@@ -1160,148 +1448,32 @@ var BlockView = memo(function BlockView2({ block, runtime, components, extension
1160
1448
  return { marks: map, afterMarks: after };
1161
1449
  }, [block, occurrences]);
1162
1450
  if (!footnoteId && ["definition", "footnoteDefinition", "annotationDefinition"].includes(block.node.type)) return null;
1163
- const context = { runtime, block, marks, afterMarks, highlights, occurrences, compact, blockElement: { id: block.id, range: block.range, pending: block.pending, ref: observe } };
1164
- if (footnoteId) return /* @__PURE__ */ jsx2("li", { id: `${runtime.document.id}-fn-${encodeURIComponent(footnoteId)}`, ...blockAttributes(context.blockElement), ref: observe, children: block.node.children?.map((child, index) => renderNode(child, { ...context, blockElement: void 0 }, index)) });
1165
- return renderNode(block.node, context);
1451
+ const context = { runtime, block, marks, afterMarks, highlights, occurrences, compact, live: streamed.current, revealFrom, blockElement: { id: block.id, range: block.range, pending: block.pending, ref: observe } };
1452
+ const content = footnoteId ? /* @__PURE__ */ jsx4("li", { id: `${runtime.document.id}-fn-${encodeURIComponent(footnoteId)}`, ...blockAttributes(context.blockElement), ref: observe, children: block.node.children?.map((child, index) => renderNode(child, { ...context, blockElement: void 0 }, index)) }) : renderNode(block.node, context);
1453
+ return /* @__PURE__ */ jsx4(KnuthPlassBoundary, { element: getElement, runtime, enabled: typography === "knuth-plass", pending: block.pending, options: knuthPlass, children: content });
1166
1454
  });
1167
1455
  function Footnotes() {
1168
1456
  const runtime = useRuntime();
1169
1457
  const doc = useDocumentSelector((snapshot) => snapshot);
1170
1458
  const components = useStoreSelector(runtime.configuration, (config) => config.components);
1171
1459
  const extensions = useStoreSelector(runtime.configuration, (config) => config.extensions);
1460
+ const rendererOverrides = useStoreSelector(runtime.configuration, (config) => config.rendererOverrides);
1172
1461
  const decorations = useStoreSelector(runtime.decorations, (state) => state.byBlock);
1173
1462
  const blocks = useMemo(() => new Map(doc.blocks.filter((block) => block.node.type === "footnoteDefinition").map((block) => [String(block.node.identifier), block])), [doc.blocks]);
1174
1463
  if (!doc.footnoteOrder.length) return null;
1175
1464
  return /* @__PURE__ */ jsxs2("section", { className: "limr-footnotes", "aria-label": runtime.locale.footnotes, children: [
1176
- /* @__PURE__ */ jsx2("hr", {}),
1177
- /* @__PURE__ */ jsx2("ol", { children: doc.footnoteOrder.map((id) => {
1465
+ /* @__PURE__ */ jsx4("hr", {}),
1466
+ /* @__PURE__ */ jsx4("ol", { children: doc.footnoteOrder.map((id) => {
1178
1467
  const node = doc.footnotes.get(id);
1179
1468
  if (!node) return null;
1180
1469
  const block = blocks.get(id) ?? { id: `${doc.id}-fn-${id}`, range: rangeOf(node), node, pending: false };
1181
- return /* @__PURE__ */ jsx2(BlockView, { block, runtime, components, extensions, footnoteId: id, decorations: decorations.get(block.id) ?? emptyDecorations }, id);
1470
+ return /* @__PURE__ */ jsx4(BlockView, { block, runtime, components, extensions, rendererOverrides, footnoteId: id, decorations: decorations.get(block.id) ?? emptyDecorations }, id);
1182
1471
  }) })
1183
1472
  ] });
1184
1473
  }
1185
1474
 
1186
1475
  // src/react/selection.ts
1187
- import { useEffect as useEffect2 } from "react";
1188
-
1189
- // src/react/source-geometry.ts
1190
- function flowOf(element, container) {
1191
- const known = element.closest("pre,p,figcaption,li,th,td,h1,h2,h3,h4,h5,h6,.limr-choice-text,.limr-definition-title,.limr-question-title,.limr-block-content");
1192
- return known && container.contains(known) ? known : element.closest("[data-limr-block]") ?? container;
1193
- }
1194
- function leavesIn(block, runtime) {
1195
- runtime.prepareTextBindings(block);
1196
- const result = [];
1197
- const visit = (node, inherited) => {
1198
- if (node instanceof HTMLElement) {
1199
- if (node !== block && node.hasAttribute("data-limr-document") || node.hasAttribute("inert")) return;
1200
- const marker = node.dataset.limrMarker;
1201
- const atomic2 = node.dataset.limrAtomic;
1202
- if (marker || atomic2) {
1203
- const image = node.dataset.limrMedia === "image";
1204
- result.push({ node, kind: marker ? "marker" : image ? "image" : atomic2 === "block" ? "block" : "inline", range: { start: Number(node.dataset.sourceStart), end: Number(node.dataset.sourceEnd) }, flow: image ? node : flowOf(node, runtime.content), offset: 0 });
1205
- return;
1206
- }
1207
- if (node.hasAttribute("data-limr-ignore")) return;
1208
- const binding = runtime.bindings.get(node);
1209
- if (binding) inherited = { binding, offset: 0 };
1210
- }
1211
- if (node.nodeType === Node.TEXT_NODE) {
1212
- const binding = runtime.bindings.get(node) ?? inherited?.binding;
1213
- const offset = runtime.bindings.has(node) ? 0 : inherited?.offset ?? 0;
1214
- const length = node.textContent?.length ?? 0;
1215
- if (binding?.resolve && length) result.push({ node, kind: "text", range: { start: binding.resolve(offset, "start"), end: binding.resolve(offset + length, "end") }, flow: flowOf(node.parentElement, runtime.content), binding, offset });
1216
- if (inherited) inherited.offset += length;
1217
- return;
1218
- }
1219
- node.childNodes.forEach((child) => visit(child, inherited));
1220
- };
1221
- visit(block);
1222
- return result;
1223
- }
1224
- function textRange(leaf, source, viewport) {
1225
- const node = leaf.node, length = node.length;
1226
- const resolve = leaf.binding.resolve;
1227
- let lo = 0, hi = length;
1228
- while (lo < hi) {
1229
- const mid = lo + hi >>> 1;
1230
- if (resolve(leaf.offset + mid + 1, "end") <= source.start) lo = mid + 1;
1231
- else hi = mid;
1232
- }
1233
- let start = lo;
1234
- lo = start;
1235
- hi = length;
1236
- while (lo < hi) {
1237
- const mid = lo + hi >>> 1;
1238
- if (resolve(leaf.offset + mid, "start") < source.end) lo = mid + 1;
1239
- else hi = mid;
1240
- }
1241
- if (start === lo) return null;
1242
- let end = lo;
1243
- const range = document.createRange();
1244
- if (viewport && end - start > 1024) {
1245
- range.setStart(node, start);
1246
- range.setEnd(node, end);
1247
- const box = range.getBoundingClientRect();
1248
- if (box.height > viewport.bottom - viewport.top + 80) {
1249
- lo = start;
1250
- hi = end;
1251
- while (lo < hi) {
1252
- const mid = lo + hi >>> 1;
1253
- range.setStart(node, mid);
1254
- range.setEnd(node, mid + 1);
1255
- if (range.getBoundingClientRect().bottom < viewport.top) lo = mid + 1;
1256
- else hi = mid;
1257
- }
1258
- start = lo;
1259
- hi = end;
1260
- while (lo < hi) {
1261
- const mid = lo + hi >>> 1;
1262
- range.setStart(node, mid);
1263
- range.setEnd(node, mid + 1);
1264
- if (range.getBoundingClientRect().top <= viewport.bottom) lo = mid + 1;
1265
- else hi = mid;
1266
- }
1267
- end = lo;
1268
- }
1269
- }
1270
- if (start === end) return null;
1271
- range.setStart(node, start);
1272
- range.setEnd(node, end);
1273
- return range;
1274
- }
1275
- function sourceRangeStartRect(runtime, source) {
1276
- for (const block of runtime.document.getSnapshot().blocks) {
1277
- if (!intersects(block.range, source)) continue;
1278
- const element = runtime.blocks.get(block.id);
1279
- if (!element) continue;
1280
- for (const leaf of leavesIn(element, runtime)) {
1281
- if (leaf.kind === "marker" || !intersects(leaf.range, source)) continue;
1282
- if (leaf.kind === "text") {
1283
- const range = textRange(leaf, source);
1284
- if (!range) continue;
1285
- const node = leaf.node, end = range.endOffset;
1286
- for (let start = range.startOffset; start < end; ) {
1287
- const next = Math.min(end, start + (node.data.codePointAt(start) > 65535 ? 2 : 1));
1288
- range.setStart(node, start);
1289
- range.setEnd(node, next);
1290
- const rect = range.getBoundingClientRect();
1291
- if (rect.width && rect.height) return rect;
1292
- start = next;
1293
- }
1294
- } else {
1295
- const node = leaf.node;
1296
- const rect = (node.querySelector(leaf.kind === "image" ? "img" : "[data-limr-selection-surface]") ?? node).getBoundingClientRect();
1297
- if (rect.width && rect.height) return rect;
1298
- }
1299
- }
1300
- }
1301
- return null;
1302
- }
1303
-
1304
- // src/react/selection.ts
1476
+ import { useEffect as useEffect3 } from "react";
1305
1477
  function visualSelection(runtime, native, options) {
1306
1478
  if (![...runtime.content.querySelectorAll('[data-limr-selection-order="visual"]')].some((element) => native.intersectsNode(element))) return;
1307
1479
  const blocks = [...runtime.blocks.values()].filter((block) => native.intersectsNode(block));
@@ -1348,7 +1520,7 @@ function visualSelection(runtime, native, options) {
1348
1520
  return { ...selections[0], raw: combine("raw"), syntax: combine("syntax"), blocks: combine("blocks") };
1349
1521
  }
1350
1522
  function useSelectionTracking(runtime, enabled) {
1351
- useEffect2(() => {
1523
+ useEffect3(() => {
1352
1524
  const container = runtime.content;
1353
1525
  if (!container || !enabled) return;
1354
1526
  let frame = 0, dragging = false;
@@ -1356,6 +1528,10 @@ function useSelectionTracking(runtime, enabled) {
1356
1528
  function resolve(node, offset, edge) {
1357
1529
  let element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
1358
1530
  if (!element || element.closest("[data-limr-document]") !== container || element.closest("[data-limr-ignore]")) return null;
1531
+ let atom = element.closest("[data-limr-atomic]");
1532
+ for (let outer = atom?.parentElement?.closest("[data-limr-atomic]"); outer && container?.contains(outer); outer = atom.parentElement?.closest("[data-limr-atomic]")) atom = outer;
1533
+ const atomicBinding = atom && runtime.bindings.get(atom);
1534
+ if (atomicBinding?.atomic) return { offset: edge === "start" ? atomicBinding.range.start : atomicBinding.range.end, atomic: true, exact: true };
1359
1535
  runtime.prepareTextBindings(element);
1360
1536
  const native = node.nodeType === Node.TEXT_NODE ? runtime.bindings.get(node) : void 0;
1361
1537
  if (native?.resolve) return { offset: native.resolve(offset, edge), atomic: false, exact: native.exact !== false };
@@ -1363,7 +1539,7 @@ function useSelectionTracking(runtime, enabled) {
1363
1539
  const binding = runtime.bindings.get(element);
1364
1540
  if (binding) {
1365
1541
  if (binding.atomic) return { offset: edge === "start" ? binding.range.start : binding.range.end, atomic: true, exact: true };
1366
- const range = document.createRange();
1542
+ const range = measurementRange(element.ownerDocument);
1367
1543
  range.setStart(element, 0);
1368
1544
  try {
1369
1545
  range.setEnd(node, offset);
@@ -1424,12 +1600,20 @@ function useSelectionTracking(runtime, enabled) {
1424
1600
  previous = { startNode: range.startContainer, start: range.startOffset, endNode: range.endContainer, end: range.endOffset, revision, settled: !dragging };
1425
1601
  runtime.selectionRange = range.cloneRange();
1426
1602
  runtime.highlightReference = null;
1603
+ const text = selection.toString();
1427
1604
  const options = {
1428
- text: selection.toString(),
1605
+ // An image/diagram can prohibit internal text selection while its
1606
+ // complete source block remains selectable and copyable.
1607
+ text: text || (start.atomic && end.atomic ? runtime.document.getSnapshot().source.slice(start.offset, end.offset) : ""),
1429
1608
  settled: !dragging,
1430
1609
  quality: !start.exact || !end.exact ? "unmapped" : start.atomic || end.atomic ? "atomic" : "exact"
1431
1610
  };
1432
- runtime.updateInteraction({ activeHighlight: null, selection: visualSelection(runtime, range, options) ?? selectionFromRange(runtime.document.getSnapshot(), { start: start.offset, end: end.offset }, options) });
1611
+ let captured = visualSelection(runtime, range, options) ?? selectionFromRange(runtime.document.getSnapshot(), { start: start.offset, end: end.offset }, options);
1612
+ if (!dragging) {
1613
+ const atomicText = atomicSelectionText(runtime, captured.raw.ranges);
1614
+ if (atomicText !== void 0) captured = { ...captured, text: atomicText, quality: "atomic" };
1615
+ }
1616
+ runtime.updateInteraction({ activeHighlight: null, selection: captured });
1433
1617
  }
1434
1618
  const schedule = () => {
1435
1619
  if (!frame) frame = requestAnimationFrame(process);
@@ -1441,11 +1625,34 @@ function useSelectionTracking(runtime, enabled) {
1441
1625
  dragging = false;
1442
1626
  schedule();
1443
1627
  };
1628
+ const atomicCopyText = (event) => {
1629
+ const native = window.getSelection(), captured = runtime.interaction.get().selection;
1630
+ if (event.defaultPrevented || !native?.anchorNode || native.isCollapsed || !container.contains(native.anchorNode) || captured?.quality !== "atomic") return null;
1631
+ if (event.target instanceof Element && event.target.closest("input,textarea,[contenteditable],[data-limr-selection-ui]")) return null;
1632
+ return captured.settled ? captured.text : atomicSelectionText(runtime, captured.raw.ranges) ?? captured.text;
1633
+ };
1634
+ const copy = (event) => {
1635
+ const text = atomicCopyText(event);
1636
+ if (!text || !event.clipboardData) return;
1637
+ event.clipboardData.setData("text/plain", text);
1638
+ event.preventDefault();
1639
+ };
1640
+ const copyKey = (event) => {
1641
+ if (event.key.toLowerCase() !== "c" || !(event.ctrlKey || event.metaKey) || event.altKey || !navigator.clipboard?.writeText) return;
1642
+ if (window.getSelection()?.toString()) return;
1643
+ const text = atomicCopyText(event);
1644
+ if (!text) return;
1645
+ event.preventDefault();
1646
+ void navigator.clipboard.writeText(text).catch(() => {
1647
+ });
1648
+ };
1444
1649
  document.addEventListener("selectionchange", schedule);
1445
1650
  container.addEventListener("pointerdown", down);
1446
1651
  document.addEventListener("pointerup", up);
1447
1652
  document.addEventListener("pointercancel", up);
1448
1653
  document.addEventListener("keyup", schedule);
1654
+ document.addEventListener("copy", copy);
1655
+ document.addEventListener("keydown", copyKey);
1449
1656
  if (window.getSelection()?.isCollapsed === false) schedule();
1450
1657
  return () => {
1451
1658
  cancelAnimationFrame(frame);
@@ -1454,13 +1661,15 @@ function useSelectionTracking(runtime, enabled) {
1454
1661
  document.removeEventListener("pointerup", up);
1455
1662
  document.removeEventListener("pointercancel", up);
1456
1663
  document.removeEventListener("keyup", schedule);
1664
+ document.removeEventListener("copy", copy);
1665
+ document.removeEventListener("keydown", copyKey);
1457
1666
  };
1458
1667
  }, [runtime, enabled]);
1459
1668
  }
1460
1669
 
1461
1670
  // src/react/selection-paint.tsx
1462
- import { useEffect as useEffect3, useRef as useRef3 } from "react";
1463
- import { jsx as jsx3 } from "react/jsx-runtime";
1671
+ import { useEffect as useEffect4, useRef as useRef5 } from "react";
1672
+ import { jsx as jsx5 } from "react/jsx-runtime";
1464
1673
  function rectangles(leaves, source, viewport, measure) {
1465
1674
  const raw = [];
1466
1675
  const geometry = (leaf, rect, reference) => {
@@ -1483,7 +1692,7 @@ function rectangles(leaves, source, viewport, measure) {
1483
1692
  };
1484
1693
  const edgeRect = (leaf, edge) => {
1485
1694
  const node = leaf.node;
1486
- const range = document.createRange();
1695
+ const range = measurementRange(node.ownerDocument);
1487
1696
  const offset = edge === "first" ? 0 : Math.max(0, node.length - 1);
1488
1697
  range.setStart(node, offset);
1489
1698
  range.setEnd(node, Math.min(node.length, offset + 1));
@@ -1501,7 +1710,7 @@ function rectangles(leaves, source, viewport, measure) {
1501
1710
  const boxAt = (index) => {
1502
1711
  const leaf = leaves[index];
1503
1712
  if (leaf.kind !== "text") return leaf.node.getBoundingClientRect();
1504
- const range = document.createRange();
1713
+ const range = measurementRange(leaf.node.ownerDocument);
1505
1714
  range.selectNodeContents(leaf.node);
1506
1715
  return range.getBoundingClientRect();
1507
1716
  };
@@ -1531,6 +1740,13 @@ function rectangles(leaves, source, viewport, measure) {
1531
1740
  raw.push({ ...item, marker: leaf.node.dataset.limrMarker });
1532
1741
  } else if (intersects(leaf.range, source)) {
1533
1742
  if (leaf.kind === "text") {
1743
+ const glue = leaf.node.parentElement?.closest(".limr-kp-glue");
1744
+ if (glue) {
1745
+ const rect = glue.getBoundingClientRect();
1746
+ const reference = referenceFor(index, rect);
1747
+ if (rect.width && reference && rect.bottom >= viewport.top && rect.top <= viewport.bottom) raw.push(geometry(leaf, rect, reference));
1748
+ continue;
1749
+ }
1534
1750
  const range = textRange(leaf, source, viewport);
1535
1751
  const shell = leaf.node.parentElement?.closest("[data-limr-inline-syntax]");
1536
1752
  const edges = shell ? Array.from(shell.getClientRects()) : [];
@@ -1579,15 +1795,42 @@ function rectangles(leaves, source, viewport, measure) {
1579
1795
  const gap = right.left - left.left - left.width;
1580
1796
  if (left.flow === right.flow && Math.abs(left.top - right.top) < 0.5 && gap > 0.1 && gap < left.height * 0.6) bridges.push({ left: left.left + left.width, top: left.top, width: gap, height: left.height, flow: left.flow });
1581
1797
  }
1582
- return [...aligned, ...bridges];
1798
+ const result = [], rows = /* @__PURE__ */ new Map();
1799
+ for (const rect of [...aligned, ...bridges]) {
1800
+ if (!rect.flow.hasAttribute("data-limr-kp") || rect.atomic || rect.marker) {
1801
+ result.push(rect);
1802
+ continue;
1803
+ }
1804
+ let flow = rows.get(rect.flow);
1805
+ if (!flow) {
1806
+ flow = /* @__PURE__ */ new Map();
1807
+ rows.set(rect.flow, flow);
1808
+ }
1809
+ const row = flow.get(rect.top) ?? [];
1810
+ row.push(rect);
1811
+ flow.set(rect.top, row);
1812
+ }
1813
+ for (const flow of rows.values()) for (const row of flow.values()) {
1814
+ row.sort((a, b) => a.left - b.left);
1815
+ let previous;
1816
+ for (const rect of row) {
1817
+ if (previous && rect.left <= previous.left + previous.width + 0.1 && Math.abs(rect.height - previous.height) < 0.1) previous.width = Math.max(previous.width, rect.left + rect.width - previous.left);
1818
+ else {
1819
+ previous = { ...rect };
1820
+ result.push(previous);
1821
+ }
1822
+ }
1823
+ }
1824
+ return result;
1583
1825
  }
1584
1826
  function SelectionPaint({ runtime, interactive = true }) {
1585
- const layer = useRef3(null);
1586
- useEffect3(() => {
1827
+ const layer = useRef5(null);
1828
+ useEffect4(() => {
1587
1829
  const container = runtime.content, paint = layer.current;
1588
1830
  if (!container || !paint) return;
1589
- let frame = 0;
1831
+ let frame = 0, translating = false;
1590
1832
  let leavesCache = /* @__PURE__ */ new WeakMap();
1833
+ let geometryCache = /* @__PURE__ */ new WeakMap();
1591
1834
  let metrics = /* @__PURE__ */ new WeakMap();
1592
1835
  const fontMetrics = /* @__PURE__ */ new Map();
1593
1836
  let blocks;
@@ -1603,7 +1846,14 @@ function SelectionPaint({ runtime, interactive = true }) {
1603
1846
  const highlightRects = (id) => [...highlightLayers.values()].flatMap((layer2) => Array.from(layer2.children).filter((item) => item.dataset.paint === id).map((item) => item.getBoundingClientRect()));
1604
1847
  const focusedRange = () => {
1605
1848
  const state = runtime.interaction.get(), decorations = runtime.decorations.get();
1606
- return decorations.occurrences.find((item) => item.id === state.hovered)?.range ?? (state.activeHighlight ? decorations.highlights.get(state.activeHighlight)?.range : void 0) ?? decorations.occurrences.find((item) => item.id === state.active)?.range;
1849
+ return decorations.referenceRanges.get(state.hovered ?? "") ?? (state.activeHighlight ? decorations.highlights.get(state.activeHighlight)?.range : void 0) ?? decorations.referenceRanges.get(state.active ?? "");
1850
+ };
1851
+ const referenceActive = (occurrenceId) => {
1852
+ const state = runtime.interaction.get(), decorations = runtime.decorations.get();
1853
+ if (occurrenceId === state.hovered) return true;
1854
+ if (occurrenceId !== state.active) return false;
1855
+ const hovered = decorations.highlights.get(`reference-${state.hovered}`);
1856
+ return !hovered?.occurrenceId || !sameRange(hovered.range, decorations.referenceRanges.get(occurrenceId));
1607
1857
  };
1608
1858
  const feedback = () => {
1609
1859
  const state = runtime.interaction.get();
@@ -1612,6 +1862,12 @@ function SelectionPaint({ runtime, interactive = true }) {
1612
1862
  for (const layer2 of highlightLayers.values()) for (const item of layer2.children) {
1613
1863
  item.toggleAttribute("data-hovered", item.dataset.paint === state.hoveredHighlight);
1614
1864
  const highlight = decorations.highlights.get(item.dataset.paint ?? "");
1865
+ if (highlight?.occurrenceId) {
1866
+ const active = referenceActive(highlight.occurrenceId);
1867
+ item.toggleAttribute("data-active", active);
1868
+ item.toggleAttribute("data-muted", !active);
1869
+ continue;
1870
+ }
1615
1871
  item.toggleAttribute("data-active", item.dataset.paint === state.activeHighlight || !!focused && !!highlight && sameRange(focused, highlight.range));
1616
1872
  item.toggleAttribute("data-muted", !!highlight?.hidden || !!focused && !!highlight && !sameRange(focused, highlight.range));
1617
1873
  }
@@ -1712,17 +1968,31 @@ function SelectionPaint({ runtime, interactive = true }) {
1712
1968
  };
1713
1969
  for (let index = lo; index < blocks.length; index++) {
1714
1970
  const block = blocks[index];
1715
- if (block.element.getBoundingClientRect().top > viewport.bottom + 32) break;
1971
+ const blockBox = block.element.getBoundingClientRect();
1972
+ if (blockBox.top > viewport.bottom + 32) break;
1716
1973
  const tasks = [...decorations.get(block.id)?.highlights ?? []];
1717
1974
  for (const range of selections) if (intersects(range, block.range)) tasks.push({ id: "selection", range, selection: true });
1718
1975
  if (!tasks.length) continue;
1719
1976
  let leaves = leavesCache.get(block.element);
1720
- if (!leaves) {
1721
- leaves = leavesIn(block.element, runtime);
1722
- leavesCache.set(block.element, leaves);
1977
+ let cached = geometryCache.get(block.element);
1978
+ if (cached?.width !== blockBox.width || cached.height !== blockBox.height) cached = void 0;
1979
+ if (!cached && blockBox.height < (viewport.bottom - viewport.top) * 3) {
1980
+ cached = { width: blockBox.width, height: blockBox.height, tasks: /* @__PURE__ */ new Map() };
1981
+ geometryCache.set(block.element, cached);
1723
1982
  }
1724
1983
  for (const task of tasks) {
1725
- const rects = rectangles(leaves, task.range, { ...viewport, top: viewport.top - 32, bottom: viewport.bottom + 32 }, measure);
1984
+ const saved = cached?.tasks.get(task.id);
1985
+ if (task.occurrenceId && !referenceActive(task.occurrenceId) && !saved) continue;
1986
+ if (!leaves) {
1987
+ leaves = leavesIn(block.element, runtime);
1988
+ leavesCache.set(block.element, leaves);
1989
+ }
1990
+ let local = saved && sameRange(saved.source, task.range) ? saved.rects : void 0;
1991
+ if (!local && cached) {
1992
+ local = rectangles(leaves, task.range, { ...viewport, top: -Infinity, bottom: Infinity }, measure).map((rect) => ({ ...rect, left: rect.left - blockBox.left, top: rect.top - blockBox.top }));
1993
+ cached.tasks.set(task.id, { source: task.range, rects: local });
1994
+ }
1995
+ const rects = local ? local.map((rect) => ({ ...rect, left: rect.left + blockBox.left, top: rect.top + blockBox.top })) : rectangles(leaves, task.range, { ...viewport, top: viewport.top - 32, bottom: viewport.bottom + 32 }, measure);
1726
1996
  for (const rect of rects) {
1727
1997
  const clip = clipFor(rect.flow);
1728
1998
  const left = Math.max(rect.left, clip.left), top = Math.max(rect.top, clip.top);
@@ -1732,13 +2002,14 @@ function SelectionPaint({ runtime, interactive = true }) {
1732
2002
  }
1733
2003
  }
1734
2004
  }
1735
- paint.style.top = `${viewport.top - origin.top + scrollTop}px`;
1736
- paint.style.height = `${Math.max(0, viewport.bottom - viewport.top)}px`;
1737
2005
  const hostGeometry = /* @__PURE__ */ new Map();
1738
2006
  for (const { rect, task } of output) if (!task.selection && !hostGeometry.has(rect.flow)) {
1739
2007
  const host = rect.flow, box = host.getBoundingClientRect();
1740
- hostGeometry.set(host, { box, top: Math.max(viewport.top, box.top) - box.top - host.clientTop + host.scrollTop, positioned: getComputedStyle(host).position !== "static" });
2008
+ const x = host.scrollLeft - host.clientLeft, y = host.scrollTop - host.clientTop;
2009
+ hostGeometry.set(host, { box, x, y, top: Math.max(viewport.top, box.top) - box.top + y, positioned: getComputedStyle(host).position !== "static" });
1741
2010
  }
2011
+ paint.style.top = `${viewport.top - origin.top + scrollTop}px`;
2012
+ paint.style.height = `${Math.max(0, viewport.bottom - viewport.top)}px`;
1742
2013
  const counts = /* @__PURE__ */ new Map();
1743
2014
  const nextHits = [];
1744
2015
  output.forEach(({ rect, task }) => {
@@ -1759,8 +2030,8 @@ function SelectionPaint({ runtime, interactive = true }) {
1759
2030
  layer2.style.top = `${geometry.top}px`;
1760
2031
  layer2.style.height = `${Math.max(0, Math.min(viewport.bottom, geometry.box.bottom) - Math.max(viewport.top, geometry.box.top))}px`;
1761
2032
  target = layer2;
1762
- left += origin.left - geometry.box.left - host.clientLeft + host.scrollLeft;
1763
- top += viewport.top - geometry.box.top - host.clientTop + host.scrollTop - geometry.top;
2033
+ left += origin.left - geometry.box.left + geometry.x;
2034
+ top += viewport.top - geometry.box.top + geometry.y - geometry.top;
1764
2035
  nextHits.push({ id: task.id, rect: { left: rect.left + origin.left - scrollLeft, top: rect.top + viewport.top, width: rect.width, height: rect.height } });
1765
2036
  }
1766
2037
  const index = counts.get(target) ?? 0;
@@ -1769,6 +2040,7 @@ function SelectionPaint({ runtime, interactive = true }) {
1769
2040
  const className = `limr-paint-rect limr-paint-${task.selection ? "selection" : task.style ?? "background"}`;
1770
2041
  if (item.className !== className) item.className = className;
1771
2042
  item.dataset.paint = task.id;
2043
+ item.toggleAttribute("data-reference", !!task.occurrenceId);
1772
2044
  if (rect.marker) item.dataset.marker = rect.marker;
1773
2045
  else delete item.dataset.marker;
1774
2046
  if (rect.atomic) item.dataset.atomic = "true";
@@ -1781,18 +2053,38 @@ function SelectionPaint({ runtime, interactive = true }) {
1781
2053
  item.dataset.geometry = css;
1782
2054
  }
1783
2055
  if (!task.selection) item.style.setProperty("--limr-highlight", task.color ?? "#F2C744");
2056
+ if (task.occurrenceId) {
2057
+ const active2 = referenceActive(task.occurrenceId);
2058
+ item.toggleAttribute("data-active", active2);
2059
+ item.toggleAttribute("data-muted", !active2);
2060
+ }
1784
2061
  if (!item.parentNode) target.append(item);
1785
2062
  });
1786
- for (const layer2 of [paint, ...highlightLayers.values()]) while (layer2.children.length > (counts.get(layer2) ?? 0)) layer2.lastChild.remove();
1787
2063
  for (const [host, layer2] of highlightLayers) if (!counts.has(layer2)) removeLayer(host, layer2);
2064
+ for (const layer2 of [paint, ...highlightLayers.values()]) {
2065
+ const count = counts.get(layer2) ?? 0;
2066
+ if (layer2.children.length > count) layer2.replaceChildren(...Array.from(layer2.children).slice(0, count));
2067
+ }
1788
2068
  hits = nextHits;
1789
2069
  const active = runtime.interaction.get().activeHighlight;
1790
2070
  if (active && !hits.some((hit) => hit.id === active)) runtime.clearSelection();
1791
2071
  feedback();
2072
+ if (translating) {
2073
+ translating = container.getAnimations().some((animation) => animation instanceof CSSTransition && animation.transitionProperty === "translate" && animation.playState === "running");
2074
+ if (translating) schedule();
2075
+ }
1792
2076
  };
1793
2077
  const schedule = () => {
1794
2078
  if (!frame) frame = requestAnimationFrame(render);
1795
2079
  };
2080
+ const translate = (event) => {
2081
+ if (event.target !== container || event.propertyName !== "translate") return;
2082
+ translating = true;
2083
+ schedule();
2084
+ };
2085
+ container.addEventListener("transitionrun", translate);
2086
+ container.addEventListener("transitionend", translate);
2087
+ container.addEventListener("transitioncancel", translate);
1796
2088
  const invalidate = () => {
1797
2089
  blocks = void 0;
1798
2090
  metrics = /* @__PURE__ */ new WeakMap();
@@ -1800,10 +2092,12 @@ function SelectionPaint({ runtime, interactive = true }) {
1800
2092
  };
1801
2093
  const fontsChanged = () => {
1802
2094
  fontMetrics.clear();
2095
+ geometryCache = /* @__PURE__ */ new WeakMap();
1803
2096
  invalidate();
1804
2097
  };
1805
2098
  const unsubscribers = [runtime.interaction.subscribe(() => {
1806
2099
  const state = runtime.interaction.get(), selection = state.selection;
2100
+ const referenceChanged = state.active !== lastAnnotation || state.hovered !== lastHoveredAnnotation;
1807
2101
  if (state.hoveredHighlight !== lastHovered || state.activeHighlight !== lastActive || state.active !== lastAnnotation || state.hovered !== lastHoveredAnnotation) {
1808
2102
  lastHovered = state.hoveredHighlight;
1809
2103
  lastActive = state.activeHighlight;
@@ -1811,6 +2105,7 @@ function SelectionPaint({ runtime, interactive = true }) {
1811
2105
  lastHoveredAnnotation = state.hovered;
1812
2106
  feedback();
1813
2107
  }
2108
+ if (referenceChanged) schedule();
1814
2109
  if (selection === lastSelection) return;
1815
2110
  lastSelection = selection;
1816
2111
  render();
@@ -1819,8 +2114,12 @@ function SelectionPaint({ runtime, interactive = true }) {
1819
2114
  if (decorations === lastDecorations) return;
1820
2115
  lastDecorations = decorations;
1821
2116
  leavesCache = /* @__PURE__ */ new WeakMap();
2117
+ geometryCache = /* @__PURE__ */ new WeakMap();
1822
2118
  schedule();
1823
- }), runtime.layout.subscribe(invalidate), runtime.configuration.subscribe(invalidate)];
2119
+ }), runtime.layout.subscribe(invalidate), runtime.documentOffset.subscribe(schedule), runtime.configuration.subscribe(() => {
2120
+ geometryCache = /* @__PURE__ */ new WeakMap();
2121
+ invalidate();
2122
+ })];
1824
2123
  const observer = new MutationObserver((records) => {
1825
2124
  let changed = false;
1826
2125
  for (const record of records) {
@@ -1828,8 +2127,13 @@ function SelectionPaint({ runtime, interactive = true }) {
1828
2127
  if (!target || target === paint || paint.contains(target) || target.closest(".limr-highlight-layer")) continue;
1829
2128
  if (record.type === "childList" && [...record.addedNodes, ...record.removedNodes].every((node) => node instanceof Element && node.classList.contains("limr-highlight-layer"))) continue;
1830
2129
  const block = target.closest("[data-limr-block]");
1831
- if (block) leavesCache.delete(block);
1832
- else blocks = void 0;
2130
+ if (block) {
2131
+ leavesCache.delete(block);
2132
+ geometryCache.delete(block);
2133
+ } else {
2134
+ blocks = void 0;
2135
+ geometryCache = /* @__PURE__ */ new WeakMap();
2136
+ }
1833
2137
  changed = true;
1834
2138
  }
1835
2139
  if (changed) {
@@ -1838,7 +2142,12 @@ function SelectionPaint({ runtime, interactive = true }) {
1838
2142
  }
1839
2143
  });
1840
2144
  observer.observe(container, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["class", "style", "inert", "open", "data-limr-theme", "data-limr-justify"] });
1841
- const scroll = () => {
2145
+ const scroll = (event) => {
2146
+ const target = event.target;
2147
+ if (target instanceof Element) {
2148
+ const block = target.closest("[data-limr-block]");
2149
+ if (block) geometryCache.delete(block);
2150
+ }
1842
2151
  runtime.hoverHighlight(null);
1843
2152
  schedule();
1844
2153
  };
@@ -1853,7 +2162,7 @@ function SelectionPaint({ runtime, interactive = true }) {
1853
2162
  for (let index = hits.length - 1; index >= 0; index--) {
1854
2163
  const { rect, id } = hits[index];
1855
2164
  const highlight = decorations.highlights.get(id);
1856
- if (highlight?.hidden || focused && highlight && !sameRange(focused, highlight.range)) continue;
2165
+ if (highlight?.occurrenceId || highlight?.hidden || focused && highlight && !sameRange(focused, highlight.range)) continue;
1857
2166
  if (event.clientX >= rect.left && event.clientX <= rect.left + rect.width && event.clientY >= rect.top && event.clientY <= rect.top + rect.height) return id;
1858
2167
  }
1859
2168
  return null;
@@ -1918,6 +2227,9 @@ function SelectionPaint({ runtime, interactive = true }) {
1918
2227
  window.visualViewport?.removeEventListener("resize", schedule);
1919
2228
  window.visualViewport?.removeEventListener("scroll", schedule);
1920
2229
  document.fonts?.removeEventListener("loadingdone", fontsChanged);
2230
+ container.removeEventListener("transitionrun", translate);
2231
+ container.removeEventListener("transitionend", translate);
2232
+ container.removeEventListener("transitioncancel", translate);
1921
2233
  container.removeEventListener("pointermove", move);
1922
2234
  container.removeEventListener("pointerleave", leave);
1923
2235
  container.removeEventListener("pointerdown", pointerDown);
@@ -1926,15 +2238,15 @@ function SelectionPaint({ runtime, interactive = true }) {
1926
2238
  clear();
1927
2239
  };
1928
2240
  }, [runtime, interactive]);
1929
- return /* @__PURE__ */ jsx3("div", { ref: layer, className: "limr-paint-layer", "data-limr-ignore": "true", "aria-hidden": "true" });
2241
+ return /* @__PURE__ */ jsx5("div", { ref: layer, className: "limr-paint-layer", "data-limr-ignore": "true", "aria-hidden": "true" });
1930
2242
  }
1931
2243
 
1932
2244
  // src/react/Markdown.tsx
1933
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2245
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
1934
2246
  function Markdown({ className, style, interactive = true, followOutput = false, justify, ...props }) {
1935
2247
  const parent = useContext(RuntimeContext);
1936
- if (props.source !== void 0 || !parent) return /* @__PURE__ */ jsx4(DocumentProvider, { ...props, justify, children: /* @__PURE__ */ jsx4(MarkdownBody, { className, style, interactive, followOutput }) });
1937
- return /* @__PURE__ */ jsx4(MarkdownBody, { className, style, interactive, followOutput, justify });
2248
+ if (props.source !== void 0 || !parent) return /* @__PURE__ */ jsx6(DocumentProvider, { ...props, justify, children: /* @__PURE__ */ jsx6(MarkdownBody, { className, style, interactive, followOutput }) });
2249
+ return /* @__PURE__ */ jsx6(MarkdownBody, { className, style, interactive, followOutput, justify });
1938
2250
  }
1939
2251
  function MarkdownBody({ className, style, interactive, followOutput, justify }) {
1940
2252
  const runtime = useRuntime();
@@ -1942,17 +2254,20 @@ function MarkdownBody({ className, style, interactive, followOutput, justify })
1942
2254
  const appearance = useAppearance();
1943
2255
  const theme = useStoreSelector(runtime.configuration, (config) => config.theme ?? "light");
1944
2256
  const configuredJustify = useStoreSelector(runtime.configuration, (config) => config.justify ?? false);
2257
+ const typography = useStoreSelector(runtime.configuration, (config) => config.typography ?? "standard");
2258
+ const justified = justify ?? (typography === "justify" || configuredJustify);
1945
2259
  const scrollRoot = useStoreSelector(runtime.configuration, (config) => config.scrollRoot);
1946
2260
  const offset = useStoreSelector(runtime.documentOffset, (value) => value);
1947
2261
  const components = useStoreSelector(runtime.configuration, (config) => config.components);
1948
2262
  const extensions = useStoreSelector(runtime.configuration, (config) => config.extensions);
2263
+ const rendererOverrides = useStoreSelector(runtime.configuration, (config) => config.rendererOverrides);
1949
2264
  const decorations = useStoreSelector(runtime.decorations, (state) => state.byBlock);
1950
- const ref = useRef4(null);
1951
- const pinned = useRef4(true);
2265
+ const ref = useRef6(null);
2266
+ const pinned = useRef6(true);
1952
2267
  useIsomorphicLayoutEffect(() => ref.current ? runtime.mountContent(ref.current) : void 0, [runtime, scrollRoot]);
1953
- useIsomorphicLayoutEffect(() => runtime.requestLayout(), [runtime, justify, configuredJustify]);
2268
+ useIsomorphicLayoutEffect(() => runtime.requestLayout(), [runtime, justify, configuredJustify, typography]);
1954
2269
  useSelectionTracking(runtime, interactive);
1955
- useEffect4(() => {
2270
+ useEffect5(() => {
1956
2271
  if (!followOutput) return;
1957
2272
  const root = scrollRoot ?? window;
1958
2273
  const onScroll = () => {
@@ -1968,23 +2283,23 @@ function MarkdownBody({ className, style, interactive, followOutput, justify })
1968
2283
  const frame = requestAnimationFrame(() => runtime.scrollTo(scrollRoot?.scrollHeight ?? window.document.documentElement.scrollHeight, "instant"));
1969
2284
  return () => cancelAnimationFrame(frame);
1970
2285
  }, [document2, runtime, followOutput, scrollRoot]);
1971
- return /* @__PURE__ */ jsxs3("article", { ref, className: `limr-prose ${className ?? ""}`, style: { ...style, translate: offset ? `0 ${offset}px` : style?.translate }, "data-limr-theme": theme, "data-limr-appearance": appearance, "data-limr-document": document2.id, "data-limr-justify": justify ?? configuredJustify, children: [
1972
- document2.blocks.map((block) => /* @__PURE__ */ jsx4(BlockView, { block, runtime, components, extensions, decorations: decorations.get(block.id) ?? emptyDecorations }, block.id)),
1973
- /* @__PURE__ */ jsx4(Footnotes, {}),
1974
- /* @__PURE__ */ jsx4(SelectionPaint, { runtime, interactive })
2286
+ return /* @__PURE__ */ jsxs3("article", { ref, className: `limr-prose ${className ?? ""}`, style: { ...style, translate: offset ? `0 ${offset}px` : style?.translate }, "data-limr-theme": theme, "data-limr-appearance": appearance, "data-limr-document": document2.id, "data-limr-justify": justified, "data-limr-typography": typography, children: [
2287
+ document2.blocks.map((block) => /* @__PURE__ */ jsx6(BlockView, { block, runtime, components, extensions, rendererOverrides, decorations: decorations.get(block.id) ?? emptyDecorations }, block.id)),
2288
+ /* @__PURE__ */ jsx6(Footnotes, {}),
2289
+ /* @__PURE__ */ jsx6(SelectionPaint, { runtime, interactive })
1975
2290
  ] });
1976
2291
  }
1977
2292
 
1978
2293
  // src/components/OverlayScrollArea.tsx
1979
- import { useRef as useRef5 } from "react";
1980
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2294
+ import { useRef as useRef7 } from "react";
2295
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1981
2296
  function OverlayScrollArea({ children, active, resetKey, label }) {
1982
- const area = useRef5(null);
1983
- const content = useRef5(null);
1984
- const thumb = useRef5(null);
1985
- const track = useRef5(null);
1986
- const drag = useRef5(null);
1987
- const show = useRef5(() => {
2297
+ const area = useRef7(null);
2298
+ const content = useRef7(null);
2299
+ const thumb = useRef7(null);
2300
+ const track = useRef7(null);
2301
+ const drag = useRef7(null);
2302
+ const show = useRef7(() => {
1988
2303
  });
1989
2304
  useIsomorphicLayoutEffect(() => {
1990
2305
  const element = area.current, wrapper = element.parentElement;
@@ -2057,73 +2372,73 @@ function OverlayScrollArea({ children, active, resetKey, label }) {
2057
2372
  show.current();
2058
2373
  };
2059
2374
  return /* @__PURE__ */ jsxs4("div", { className: "limr-overlay-scroll", children: [
2060
- /* @__PURE__ */ jsx5("div", { ref: area, className: "limr-overlay-content", "data-limr-card-body": "", tabIndex: 0, role: "region", "aria-label": label, children: /* @__PURE__ */ jsx5("div", { ref: content, className: "limr-overlay-scroll-inner", children }) }),
2061
- /* @__PURE__ */ jsx5("div", { ref: track, className: "limr-overlay-scrollbar", "aria-hidden": "true", onPointerDown: startDrag, onPointerMove: move, onPointerUp: end, onPointerCancel: end, onLostPointerCapture: end, children: /* @__PURE__ */ jsx5("div", { ref: thumb, className: "limr-overlay-scroll-thumb" }) })
2375
+ /* @__PURE__ */ jsx7("div", { ref: area, className: "limr-overlay-content", "data-limr-card-body": "", tabIndex: 0, role: "region", "aria-label": label, children: /* @__PURE__ */ jsx7("div", { ref: content, className: "limr-overlay-scroll-inner", children }) }),
2376
+ /* @__PURE__ */ jsx7("div", { ref: track, className: "limr-overlay-scrollbar", "aria-hidden": "true", onPointerDown: startDrag, onPointerMove: move, onPointerUp: end, onPointerCancel: end, onLostPointerCapture: end, children: /* @__PURE__ */ jsx7("div", { ref: thumb, className: "limr-overlay-scroll-thumb" }) })
2062
2377
  ] });
2063
2378
  }
2064
2379
 
2065
2380
  // src/components/AnnotationCard.tsx
2066
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2381
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
2067
2382
  function AnnotationCard({ title, bodyLabel, occurrence, expanded, pinned, canExpand, expand, collapse, close, children }) {
2068
2383
  const locale = useLocale();
2069
2384
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
2070
2385
  /* @__PURE__ */ jsxs5("div", { className: "limr-overlay-header", children: [
2071
- /* @__PURE__ */ jsx6("span", { children: title }),
2386
+ /* @__PURE__ */ jsx8("span", { children: title }),
2072
2387
  /* @__PURE__ */ jsxs5("div", { className: "limr-overlay-controls", children: [
2073
- expanded ? /* @__PURE__ */ jsx6("button", { type: "button", onClick: collapse, children: /* @__PURE__ */ jsx6("span", { className: "limr-underline", children: locale.collapse }) }) : canExpand && /* @__PURE__ */ jsx6("button", { type: "button", onClick: expand, children: /* @__PURE__ */ jsx6("span", { className: "limr-underline", children: locale.expand }) }),
2074
- pinned && /* @__PURE__ */ jsx6("button", { type: "button", className: "limr-overlay-close", onClick: close, "aria-label": locale.close, children: "\xD7" })
2388
+ expanded ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: collapse, children: /* @__PURE__ */ jsx8("span", { className: "limr-underline", children: locale.collapse }) }) : canExpand && /* @__PURE__ */ jsx8("button", { type: "button", onClick: expand, children: /* @__PURE__ */ jsx8("span", { className: "limr-underline", children: locale.expand }) }),
2389
+ pinned && /* @__PURE__ */ jsx8("button", { type: "button", className: "limr-overlay-close", onClick: close, "aria-label": locale.close, children: "\xD7" })
2075
2390
  ] })
2076
2391
  ] }),
2077
- /* @__PURE__ */ jsx6(OverlayScrollArea, { active: true, resetKey: occurrence.id, label: bodyLabel ?? (typeof title === "string" ? title : locale.annotations), children })
2392
+ /* @__PURE__ */ jsx8(OverlayScrollArea, { active: true, resetKey: occurrence.id, label: bodyLabel ?? (typeof title === "string" ? title : locale.annotations), children })
2078
2393
  ] });
2079
2394
  }
2080
2395
 
2081
2396
  // src/extensions/annotations.tsx
2082
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2083
- var CitationParagraph = ({ children }) => /* @__PURE__ */ jsx7("p", { children: /* @__PURE__ */ jsx7("span", { className: "limr-underline", children }) });
2397
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2398
+ var CitationParagraph = ({ children }) => /* @__PURE__ */ jsx9("p", { children: /* @__PURE__ */ jsx9("span", { className: "limr-underline", children }) });
2084
2399
  function CitationIcon() {
2085
- return /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", fill: "currentColor", children: /* @__PURE__ */ jsx7("path", { d: "M2.5 4.2h4.3v4.3c0 2.1-1.2 3.3-3.5 3.8l-.5-1.4c1.4-.4 2-1 2-2H2.5V4.2Zm6.7 0h4.3v4.3c0 2.1-1.2 3.3-3.5 3.8l-.5-1.4c1.4-.4 2-1 2-2H9.2V4.2Z" }) });
2400
+ return /* @__PURE__ */ jsx9("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", fill: "currentColor", children: /* @__PURE__ */ jsx9("path", { d: "M2.5 4.2h4.3v4.3c0 2.1-1.2 3.3-3.5 3.8l-.5-1.4c1.4-.4 2-1 2-2H2.5V4.2Zm6.7 0h4.3v4.3c0 2.1-1.2 3.3-3.5 3.8l-.5-1.4c1.4-.4 2-1 2-2H9.2V4.2Z" }) });
2086
2401
  }
2087
2402
  function AnnotationMarkdown({ source, className, ...props }) {
2088
2403
  const runtime = useRuntime();
2089
2404
  const config = useStoreSelector(runtime.configuration, (config2) => config2);
2090
- return /* @__PURE__ */ jsx7(Markdown, { source, extensions: config.extensions, parameters: config.parameters, components: config.components, annotationTypes: config.annotationTypes, theme: config.theme, appearance: config.appearance, locale: config.locale, interactive: false, ...props, className: `limr-annotation-markdown ${className ?? ""}` });
2405
+ return /* @__PURE__ */ jsx9(Markdown, { source, extensions: config.extensions, parameters: config.parameters, components: config.components, annotationTypes: config.annotationTypes, theme: config.theme, appearance: config.appearance, locale: config.locale, interactive: false, ...props, className: `limr-annotation-markdown ${className ?? ""}` });
2091
2406
  }
2092
2407
  function citation(options = {}) {
2093
- function Preview({ annotations }) {
2408
+ function Preview2({ annotations }) {
2094
2409
  const summary = useMemo2(() => annotations.filter((annotation) => annotation.type === (options.type ?? "citation")).map((annotation) => {
2095
2410
  const data = annotation.data;
2096
2411
  const source = data?.summary?.trim() || data?.description?.trim();
2097
2412
  if (source) return fromMarkdown(source).children?.map(textContent).join(" ").replace(/\s+/g, " ").trim();
2098
2413
  return safeUrl(data?.url) || data?.title;
2099
2414
  }).filter(Boolean).join(" \xB7 "), [annotations]);
2100
- return summary ? /* @__PURE__ */ jsx7("span", { title: summary, children: summary }) : null;
2415
+ return summary ? /* @__PURE__ */ jsx9("span", { title: summary, children: summary }) : null;
2101
2416
  }
2102
- function Card(props) {
2417
+ function Card2(props) {
2103
2418
  const locale = useLocale();
2104
- return /* @__PURE__ */ jsx7(AnnotationCard, { ...props, title: locale.annotations });
2419
+ return /* @__PURE__ */ jsx9(AnnotationCard, { ...props, title: locale.citation ?? locale.annotations });
2105
2420
  }
2106
- function Content({ data }) {
2421
+ function Content3({ data }) {
2107
2422
  const runtime = useRuntime();
2108
2423
  const configured = useStoreSelector(runtime.configuration, () => runtime.components);
2109
2424
  const components = useMemo2(() => ({ ...configured, paragraph: configured.paragraph ?? CitationParagraph }), [configured]);
2110
- const link = useRef6(null);
2425
+ const link = useRef8(null);
2111
2426
  const url = safeUrl(data?.url);
2112
2427
  return /* @__PURE__ */ jsxs6("div", { className: "limr-citation-content", "data-linked": !!url || void 0, onClick: (event) => {
2113
2428
  if (url && !event.defaultPrevented && !event.target.closest('a,button,input,textarea,select,summary,[role="button"],[contenteditable="true"],[data-limr-card-interactive]') && window.getSelection()?.isCollapsed !== false) link.current?.click();
2114
2429
  }, children: [
2115
- /* @__PURE__ */ jsx7("a", { ref: link, className: "limr-citation-title", href: url, target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsx7("span", { className: "limr-underline", children: data?.title ?? data?.url ?? "Citation" }) }),
2116
- (data?.description || data?.summary) && /* @__PURE__ */ jsx7(AnnotationMarkdown, { source: data.description || data.summary, components })
2430
+ /* @__PURE__ */ jsx9("a", { ref: link, className: "limr-citation-title", href: url, target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsx9("span", { className: "limr-underline", children: data?.title ?? data?.url ?? "Citation" }) }),
2431
+ (data?.description || data?.summary) && /* @__PURE__ */ jsx9(AnnotationMarkdown, { source: data.description || data.summary, components })
2117
2432
  ] });
2118
2433
  }
2119
2434
  return {
2120
2435
  name: "citation",
2121
2436
  tags: ["citation", "ref"],
2122
2437
  scope: "local",
2123
- annotationTypes: [{ type: "citation", label: "Citation", icon: CitationIcon, color: "#6366f1", Content, ...options, sidebar: { Preview, collapseOnContentClick: false, ...options.sidebar }, overlay: { Card, expand: false, ...options.overlay } }],
2438
+ annotationTypes: [{ type: "citation", label: (locale) => locale.citation ?? "Citation", icon: CitationIcon, color: "#6366f1", highlight: "hover", Content: Content3, ...options, sidebar: { Preview: Preview2, collapseOnContentClick: false, ...options.sidebar }, overlay: { Card: Card2, expand: false, ...options.overlay } }],
2124
2439
  transform(root) {
2125
2440
  walk(root, (node) => {
2126
- if (node.type !== "component") return;
2441
+ if (node.type !== "component" || node.incomplete) return;
2127
2442
  if (node.name === "citation") {
2128
2443
  const attributes = node.attributes ?? {};
2129
2444
  const id = String(attributes["cite-id"] ?? attributes.id ?? "");
@@ -2147,13 +2462,13 @@ function citation(options = {}) {
2147
2462
  }
2148
2463
 
2149
2464
  // src/extensions/translations.tsx
2150
- import { useRef as useRef8, useState as useState3 } from "react";
2465
+ import { useRef as useRef10, useState as useState3 } from "react";
2151
2466
 
2152
2467
  // src/components/InlineMarker.tsx
2153
2468
  import { forwardRef } from "react";
2154
- import { jsx as jsx8 } from "react/jsx-runtime";
2469
+ import { jsx as jsx10 } from "react/jsx-runtime";
2155
2470
  var InlineMarker = forwardRef(function InlineMarker2({ label, color, children, className = "", style, ...props }, ref) {
2156
- return /* @__PURE__ */ jsx8("span", { className: "limr-mark-wrap", "data-limr-ignore": "true", children: /* @__PURE__ */ jsx8(
2471
+ return /* @__PURE__ */ jsx10("span", { className: "limr-mark-wrap", "data-limr-ignore": "true", children: /* @__PURE__ */ jsx10(
2157
2472
  "button",
2158
2473
  {
2159
2474
  ...props,
@@ -2163,18 +2478,18 @@ var InlineMarker = forwardRef(function InlineMarker2({ label, color, children, c
2163
2478
  title: props.title ?? label,
2164
2479
  className: `limr-mark limr-action-mark ${className}`,
2165
2480
  style: { "--limr-mark-color": color, ...style },
2166
- children: /* @__PURE__ */ jsx8("span", { className: "limr-mark-icon", children })
2481
+ children: /* @__PURE__ */ jsx10("span", { className: "limr-mark-icon", children })
2167
2482
  }
2168
2483
  ) });
2169
2484
  });
2170
2485
 
2171
2486
  // src/components/Collapse.tsx
2172
- import { useRef as useRef7, useState as useState2 } from "react";
2173
- import { jsx as jsx9 } from "react/jsx-runtime";
2487
+ import { useRef as useRef9, useState as useState2 } from "react";
2488
+ import { jsx as jsx11 } from "react/jsx-runtime";
2174
2489
  function Collapse({ open, children, className = "", keepMounted = false }) {
2175
2490
  const [mounted, setMounted] = useState2(open);
2176
2491
  const [visible, setVisible] = useState2(open);
2177
- const panel = useRef7(null);
2492
+ const panel = useRef9(null);
2178
2493
  useIsomorphicLayoutEffect(() => {
2179
2494
  if (keepMounted) return;
2180
2495
  if (open) {
@@ -2190,24 +2505,24 @@ function Collapse({ open, children, className = "", keepMounted = false }) {
2190
2505
  const timer = setTimeout(() => setMounted(false), 420);
2191
2506
  return () => clearTimeout(timer);
2192
2507
  }, [open, keepMounted, mounted]);
2193
- return /* @__PURE__ */ jsx9("div", { ref: (element) => {
2508
+ return /* @__PURE__ */ jsx11("div", { ref: (element) => {
2194
2509
  panel.current = element;
2195
2510
  element?.toggleAttribute("inert", !open);
2196
- }, className: `limr-collapse ${className}`, "data-open": (keepMounted ? open : visible) || void 0, "aria-hidden": !open, children: /* @__PURE__ */ jsx9("div", { className: "limr-collapse-inner", children: mounted || keepMounted ? children : null }) });
2511
+ }, className: `limr-collapse ${className}`, "data-open": (keepMounted ? open : visible) || void 0, "aria-hidden": !open, children: /* @__PURE__ */ jsx11("div", { className: "limr-collapse-inner", children: mounted || keepMounted ? children : null }) });
2197
2512
  }
2198
2513
 
2199
2514
  // src/extensions/translations.tsx
2200
- import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2515
+ import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
2201
2516
  var modes = ["origin", "translation", "both"];
2202
2517
  var names = { origin: "\u539F\u6587", translation: "\u8BD1\u6587", both: "\u5BF9\u7167" };
2203
2518
  function TranslationIcon({ mode }) {
2204
- return /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: mode === "both" ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
2205
- /* @__PURE__ */ jsx10("rect", { x: "2.5", y: "4", width: "8", height: "16", rx: "2.5" }),
2206
- /* @__PURE__ */ jsx10("rect", { x: "13.5", y: "4", width: "8", height: "16", rx: "2.5" }),
2207
- /* @__PURE__ */ jsx10("path", { d: "M5 9h3M5 12h3M16 9h3M16 12h3" })
2519
+ return /* @__PURE__ */ jsx12("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: mode === "both" ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
2520
+ /* @__PURE__ */ jsx12("rect", { x: "2.5", y: "4", width: "8", height: "16", rx: "2.5" }),
2521
+ /* @__PURE__ */ jsx12("rect", { x: "13.5", y: "4", width: "8", height: "16", rx: "2.5" }),
2522
+ /* @__PURE__ */ jsx12("path", { d: "M5 9h3M5 12h3M16 9h3M16 12h3" })
2208
2523
  ] }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
2209
- /* @__PURE__ */ jsx10("rect", { x: "4.5", y: "3", width: "15", height: "18", rx: "3" }),
2210
- mode === "origin" ? /* @__PURE__ */ jsx10("path", { d: "M8.5 8h7M8.5 12h7M8.5 16h4" }) : /* @__PURE__ */ jsx10("path", { d: "M8 8.5h8M12 6.5v2M14.5 8.5c-.5 4-3 6.5-6.5 8M9.5 11c1.2 2.5 3.2 4.2 6.5 5.5" })
2524
+ /* @__PURE__ */ jsx12("rect", { x: "4.5", y: "3", width: "15", height: "18", rx: "3" }),
2525
+ mode === "origin" ? /* @__PURE__ */ jsx12("path", { d: "M8.5 8h7M8.5 12h7M8.5 16h4" }) : /* @__PURE__ */ jsx12("path", { d: "M8 8.5h8M12 6.5v2M14.5 8.5c-.5 4-3 6.5-6.5 8M9.5 11c1.2 2.5 3.2 4.2 6.5 5.5" })
2211
2526
  ] }) });
2212
2527
  }
2213
2528
  function modeOf(value, fallback) {
@@ -2231,8 +2546,8 @@ function translations(options = {}) {
2231
2546
  const requested = modeOf(globalMode ?? localMode ?? node.attributes?.default, options.defaultMode ?? "origin");
2232
2547
  const mode = !origin ? "translation" : !translation ? "origin" : requested;
2233
2548
  const next = modes[(modes.indexOf(mode) + 1) % modes.length];
2234
- const marker = useRef8(null);
2235
- const restoreFocus = useRef8(false);
2549
+ const marker = useRef10(null);
2550
+ const restoreFocus = useRef10(false);
2236
2551
  const locked = globalMode !== void 0 && !onGlobalChange;
2237
2552
  useIsomorphicLayoutEffect(() => {
2238
2553
  if (restoreFocus.current) {
@@ -2240,8 +2555,8 @@ function translations(options = {}) {
2240
2555
  restoreFocus.current = false;
2241
2556
  }
2242
2557
  }, [mode]);
2243
- if (!origin && !translation) return /* @__PURE__ */ jsx10(Fragment4, { children });
2244
- const toggle = origin && translation ? /* @__PURE__ */ jsx10(
2558
+ if (!origin && !translation) return /* @__PURE__ */ jsx12(Fragment4, { children });
2559
+ const toggle = origin && translation ? /* @__PURE__ */ jsx12(
2245
2560
  InlineMarker,
2246
2561
  {
2247
2562
  ref: marker,
@@ -2255,12 +2570,12 @@ function translations(options = {}) {
2255
2570
  if (globalMode !== void 0) onGlobalChange?.(next);
2256
2571
  else setLocalMode(next);
2257
2572
  },
2258
- children: /* @__PURE__ */ jsx10(TranslationIcon, { mode })
2573
+ children: /* @__PURE__ */ jsx12(TranslationIcon, { mode })
2259
2574
  }
2260
2575
  ) : void 0;
2261
2576
  return /* @__PURE__ */ jsxs7("div", { className: "limr-translations", "data-mode": mode, children: [
2262
- translation && /* @__PURE__ */ jsx10(Collapse, { open: mode !== "origin", keepMounted: true, className: "limr-translation-panel", children: /* @__PURE__ */ jsx10("div", { className: "limr-translation-text", "data-language": "translation", children: renderChildren(translation.children ?? [], { trailing: mode === "translation" ? toggle : void 0 }) }) }),
2263
- origin && /* @__PURE__ */ jsx10(Collapse, { open: mode !== "translation", keepMounted: true, className: "limr-translation-panel", children: /* @__PURE__ */ jsx10("div", { className: "limr-translation-text", "data-language": "origin", children: renderChildren(origin.children ?? [], { trailing: mode !== "translation" ? toggle : void 0 }) }) })
2577
+ translation && /* @__PURE__ */ jsx12(Collapse, { open: mode !== "origin", keepMounted: true, className: "limr-translation-panel", children: /* @__PURE__ */ jsx12("div", { className: "limr-translation-text", "data-language": "translation", children: renderChildren(translation.children ?? [], { trailing: mode === "translation" ? toggle : void 0 }) }) }),
2578
+ origin && /* @__PURE__ */ jsx12(Collapse, { open: mode !== "translation", keepMounted: true, className: "limr-translation-panel", children: /* @__PURE__ */ jsx12("div", { className: "limr-translation-text", "data-language": "origin", children: renderChildren(origin.children ?? [], { trailing: mode !== "translation" ? toggle : void 0 }) }) })
2264
2579
  ] });
2265
2580
  }
2266
2581
  return {
@@ -2270,16 +2585,338 @@ function translations(options = {}) {
2270
2585
  display: "block",
2271
2586
  scope: "local",
2272
2587
  parameters: { translationMode: void 0, onTranslationModeChange: void 0 },
2273
- components: { translations: Translations, origin: ({ children }) => /* @__PURE__ */ jsx10(Fragment4, { children }), translation: ({ children }) => /* @__PURE__ */ jsx10(Fragment4, { children }) }
2588
+ components: { translations: Translations, origin: ({ children }) => /* @__PURE__ */ jsx12(Fragment4, { children }), translation: ({ children }) => /* @__PURE__ */ jsx12(Fragment4, { children }) }
2274
2589
  };
2275
2590
  }
2276
2591
 
2277
- // src/extensions/blocks.tsx
2278
- import { useState as useState7 } from "react";
2592
+ // src/extensions/mermaid.tsx
2593
+ import { useEffect as useEffect6, useRef as useRef11, useState as useState4 } from "react";
2594
+
2595
+ // src/components/LoadingRing.tsx
2596
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
2597
+ function LoadingRing() {
2598
+ const locale = useLocale();
2599
+ return /* @__PURE__ */ jsx13("span", { className: "limr-loading", role: "status", "aria-label": locale.loading, "data-limr-ignore": "true", children: /* @__PURE__ */ jsxs8("svg", { viewBox: "0 0 32 32", "aria-hidden": "true", children: [
2600
+ /* @__PURE__ */ jsx13("circle", { className: "limr-loading-track", cx: "16", cy: "16", r: "12" }),
2601
+ /* @__PURE__ */ jsx13("g", { className: "limr-loading-orbit", children: /* @__PURE__ */ jsx13("circle", { className: "limr-loading-arc", cx: "16", cy: "16", r: "12", pathLength: "100" }) })
2602
+ ] }) });
2603
+ }
2604
+ function BlockLoading() {
2605
+ return /* @__PURE__ */ jsx13("div", { className: "limr-block-loading", children: /* @__PURE__ */ jsx13(LoadingRing, {}) });
2606
+ }
2607
+
2608
+ // src/components/ToolIcon.tsx
2609
+ import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
2610
+ function ToolIcon({ name, ...props }) {
2611
+ return /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", ...props, children: /* @__PURE__ */ jsxs9("g", { transform: name === "highlight" ? "translate(0 -1.25)" : name === "comment" ? "translate(0 -.5)" : void 0, children: [
2612
+ (name === "eye" || name === "eye-off") && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2613
+ /* @__PURE__ */ jsx14("path", { d: "M3 12s3.3-6 9-6 9 6 9 6-3.3 6-9 6-9-6-9-6Z" }),
2614
+ /* @__PURE__ */ jsx14("circle", { cx: "12", cy: "12", r: "2.7" }),
2615
+ name === "eye-off" && /* @__PURE__ */ jsx14("path", { d: "m4 4 16 16" })
2616
+ ] }),
2617
+ name === "question" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2618
+ /* @__PURE__ */ jsx14("circle", { cx: "12", cy: "12", r: "8.5" }),
2619
+ /* @__PURE__ */ jsx14("path", { d: "M9.4 9a2.7 2.7 0 0 1 5.2 1c0 1.8-2.6 2-2.6 3.7M12 17h.01" })
2620
+ ] }),
2621
+ name === "highlight" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2622
+ /* @__PURE__ */ jsx14("path", { d: "m8 15 7.6-9a2 2 0 0 1 2.8-.2l.8.7a2 2 0 0 1 .2 2.8L12 18l-4-3Z" }),
2623
+ /* @__PURE__ */ jsx14("path", { d: "m8 15-2 3 3 1 3-1M4 21h10M14 8l4 3.5" })
2624
+ ] }),
2625
+ name === "edit" && /* @__PURE__ */ jsx14(Fragment5, { children: /* @__PURE__ */ jsx14("path", { d: "m14.5 5.5 4 4M4.5 19.5l4.5-1 11-11a2.1 2.1 0 0 0 0-3 2.1 2.1 0 0 0-3 0l-11 11-1.5 4Z" }) }),
2626
+ name === "close" && /* @__PURE__ */ jsx14("path", { d: "m6 6 12 12M18 6 6 18" }),
2627
+ name === "copy" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2628
+ /* @__PURE__ */ jsx14("rect", { x: "8.5", y: "8", width: "10.5", height: "12", rx: "3" }),
2629
+ /* @__PURE__ */ jsx14("path", { d: "M15.5 8V6.8A2.8 2.8 0 0 0 12.7 4H7A3 3 0 0 0 4 7v6a3 3 0 0 0 3 3h1.5" })
2630
+ ] }),
2631
+ (name === "zoom-in" || name === "zoom-out") && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2632
+ /* @__PURE__ */ jsx14("circle", { cx: "10.5", cy: "10.5", r: "6.5" }),
2633
+ /* @__PURE__ */ jsx14("path", { d: "m15.4 15.4 4.6 4.6M7.5 10.5h6" }),
2634
+ name === "zoom-in" && /* @__PURE__ */ jsx14("path", { d: "M10.5 7.5v6" })
2635
+ ] }),
2636
+ name === "source" && /* @__PURE__ */ jsx14(Fragment5, { children: /* @__PURE__ */ jsx14("path", { d: "m7 7-4 5 4 5m10-10 4 5-4 5m-4-13-2 16" }) }),
2637
+ name === "image" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2638
+ /* @__PURE__ */ jsx14("rect", { x: "3.5", y: "4.5", width: "17", height: "15", rx: "3.5" }),
2639
+ /* @__PURE__ */ jsx14("circle", { cx: "15.5", cy: "9", r: "1.5" }),
2640
+ /* @__PURE__ */ jsx14("path", { d: "m4 16 4.4-4.4a1.5 1.5 0 0 1 2.2 0L17 19m-3-3 1.4-1.4a1.5 1.5 0 0 1 2.2 0L20 17" })
2641
+ ] }),
2642
+ ["background", "underline", "dotted", "wavy"].includes(name) && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2643
+ name === "background" && /* @__PURE__ */ jsx14("rect", { x: "3", y: "3", width: "18", height: "18", rx: "5", fill: "currentColor", fillOpacity: ".13", stroke: "none" }),
2644
+ /* @__PURE__ */ jsx14("path", { d: "m8 15 3-8a1.05 1.05 0 0 1 2 0l3 8M9.2 12h5.6" }),
2645
+ name === "background" && /* @__PURE__ */ jsx14("path", { d: "M7 19h10", strokeWidth: "2.5" }),
2646
+ name === "underline" && /* @__PURE__ */ jsx14("path", { d: "M5 20h14" }),
2647
+ name === "dotted" && /* @__PURE__ */ jsx14("path", { d: "M5 20h14", strokeDasharray: ".1 3.4", strokeWidth: "2.4" }),
2648
+ name === "wavy" && /* @__PURE__ */ jsx14("path", { d: "M4 20c1.3-2 2.7-2 4 0s2.7 2 4 0 2.7-2 4 0 2.7 2 4 0" })
2649
+ ] }),
2650
+ name === "chevron" && /* @__PURE__ */ jsx14("path", { d: "m8 10 4 4 4-4" }),
2651
+ name === "check" && /* @__PURE__ */ jsx14("path", { d: "m5.5 12 4.2 4.2 8.8-9" }),
2652
+ name === "trash" && /* @__PURE__ */ jsx14(Fragment5, { children: /* @__PURE__ */ jsx14("path", { d: "M4.5 7h15M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M7 7l.7 12a2 2 0 0 0 2 2h4.6a2 2 0 0 0 2-2L17 7M10 11v6M14 11v6" }) }),
2653
+ name === "note" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2654
+ /* @__PURE__ */ jsx14("path", { d: "M14 3H7a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V9l-6-6Z" }),
2655
+ /* @__PURE__ */ jsx14("path", { d: "M14 3v4a2 2 0 0 0 2 2h4M8 13h8M8 17h5" })
2656
+ ] }),
2657
+ name === "comment" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2658
+ /* @__PURE__ */ jsx14("path", { d: "M8 19 4 21V7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v9a3 3 0 0 1-3 3H8Z" }),
2659
+ /* @__PURE__ */ jsx14("path", { d: "M8 9h8M8 13h5" })
2660
+ ] }),
2661
+ name === "footnote" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2662
+ /* @__PURE__ */ jsx14("path", { d: "M5 7h9M5 11h12M5 15h8M5 20h14M18 4h2v5" }),
2663
+ /* @__PURE__ */ jsx14("path", { d: "M17.5 9h4" })
2664
+ ] }),
2665
+ name === "sparkles" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2666
+ /* @__PURE__ */ jsx14("path", { d: "m10.5 4 1.8 4.8 4.7 1.7-4.7 1.8-1.8 4.7-1.7-4.7L4 10.5l4.8-1.7L10.5 4Z" }),
2667
+ /* @__PURE__ */ jsx14("path", { d: "m19 15 .8 2.2L22 18l-2.2.8L19 21l-.8-2.2L16 18l2.2-.8L19 15ZM19 3v4M17 5h4" })
2668
+ ] })
2669
+ ] }) });
2670
+ }
2671
+
2672
+ // src/extensions/mermaid.tsx
2673
+ import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
2674
+ var nextId = 0;
2675
+ var queue = Promise.resolve();
2676
+ var cache2 = new ResourceCache(32);
2677
+ async function diagram(id, code, dark, font, signal) {
2678
+ let result = "";
2679
+ const work = queue.then(async () => {
2680
+ if (signal.aborted) throw signal.reason;
2681
+ const { default: mermaid2 } = await import("mermaid");
2682
+ await document.fonts.ready;
2683
+ if (signal.aborted) throw signal.reason;
2684
+ mermaid2.initialize({
2685
+ startOnLoad: false,
2686
+ securityLevel: "strict",
2687
+ suppressErrorRendering: true,
2688
+ fontFamily: font,
2689
+ theme: "base",
2690
+ themeVariables: dark ? { background: "#242321", primaryColor: "#383531", primaryTextColor: "#e2ded7", primaryBorderColor: "#7b746a", lineColor: "#a29a8e", secondaryColor: "#302e2a", tertiaryColor: "#302e2a" } : { background: "#f9f8f6", primaryColor: "#eeebe6", primaryTextColor: "#49443e", primaryBorderColor: "#b5aca0", lineColor: "#91877a", secondaryColor: "#f5f3ef", tertiaryColor: "#f5f3ef" }
2691
+ });
2692
+ const host = document.createElement("div");
2693
+ host.style.cssText = "position:fixed;left:-100000px;top:0;visibility:hidden;pointer-events:none";
2694
+ host.style.fontFamily = font;
2695
+ document.body.append(host);
2696
+ try {
2697
+ result = (await mermaid2.render(id, code, host)).svg;
2698
+ } finally {
2699
+ host.remove();
2700
+ }
2701
+ });
2702
+ queue = work.catch(() => {
2703
+ });
2704
+ await work;
2705
+ return result;
2706
+ }
2707
+ function MermaidNode({ node, incomplete }) {
2708
+ const locale = useLocale(), theme = useTheme(), runtime = useRuntime();
2709
+ const zh = locale.language?.startsWith("zh");
2710
+ const [id] = useState4(() => `limr-mermaid-${++nextId}`);
2711
+ const [systemDark, setSystemDark] = useState4(false);
2712
+ const [result, setResult] = useState4();
2713
+ const [sourceView, setSourceView] = useState4(false), [scale, setScale] = useState4(1), [attempt, setAttempt] = useState4(0);
2714
+ const ref = useRef11(null), content = useRef11(null);
2715
+ const drag = useRef11(null);
2716
+ const suppressClick = useRef11(false);
2717
+ const code = node.value ?? "";
2718
+ const dark = theme === "dark" || theme === "auto" && systemDark;
2719
+ const key = JSON.stringify([id, code, dark, attempt]);
2720
+ const current = result?.key === key ? result : void 0;
2721
+ useEffect6(() => {
2722
+ if (theme !== "auto") return;
2723
+ const media = matchMedia("(prefers-color-scheme: dark)");
2724
+ const update = () => setSystemDark(media.matches);
2725
+ update();
2726
+ media.addEventListener("change", update);
2727
+ return () => media.removeEventListener("change", update);
2728
+ }, [theme]);
2729
+ useEffect6(() => {
2730
+ if (incomplete) return;
2731
+ let disposed = false;
2732
+ const font = ref.current ? getComputedStyle(ref.current).fontFamily : "sans-serif";
2733
+ const request = cache2.acquire(`${key}:${font}`, (signal) => diagram(id, code, dark, font, signal));
2734
+ request.promise.then((svg) => {
2735
+ if (!disposed) setResult({ key, svg });
2736
+ }, () => {
2737
+ if (!disposed) setResult({ key, error: true });
2738
+ });
2739
+ return () => {
2740
+ disposed = true;
2741
+ request.release();
2742
+ };
2743
+ }, [key, incomplete]);
2744
+ useStreamEntrance(content, !!current?.svg, current?.svg);
2745
+ const loading2 = incomplete || !current;
2746
+ const source = sourceView || current?.error;
2747
+ useEffect6(() => {
2748
+ const viewport = content.current;
2749
+ if (!viewport || source) return;
2750
+ const wheel = (event) => {
2751
+ if (!event.shiftKey || event.ctrlKey || event.metaKey || !event.deltaY || viewport.scrollWidth <= viewport.clientWidth) return;
2752
+ const unit = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? viewport.clientWidth : 1;
2753
+ viewport.scrollLeft += (event.deltaX || event.deltaY) * unit;
2754
+ event.preventDefault();
2755
+ };
2756
+ viewport.addEventListener("wheel", wheel, { passive: false });
2757
+ return () => viewport.removeEventListener("wheel", wheel);
2758
+ }, [loading2, source]);
2759
+ const select = () => {
2760
+ const canvas = content.current?.firstElementChild;
2761
+ if (!canvas) return;
2762
+ const range = document.createRange();
2763
+ range.selectNodeContents(canvas);
2764
+ const selection = canvas.ownerDocument.getSelection();
2765
+ selection?.removeAllRanges();
2766
+ selection?.addRange(range);
2767
+ };
2768
+ const label = sourceView ? zh ? "\u663E\u793A\u56FE\u50CF" : "Show diagram" : zh ? "\u663E\u793A\u6E90\u4EE3\u7801" : "Show source";
2769
+ return /* @__PURE__ */ jsxs10("div", { className: "limr-math-block limr-mermaid-block", ref, "data-mermaid-state": loading2 ? "loading" : current?.error ? "error" : "ready", children: [
2770
+ /* @__PURE__ */ jsxs10("div", { className: "limr-math-display limr-mermaid-display", children: [
2771
+ loading2 && /* @__PURE__ */ jsx15("div", { className: "limr-mermaid-loading", children: /* @__PURE__ */ jsx15(LoadingRing, {}) }),
2772
+ !loading2 && /* @__PURE__ */ jsxs10(Fragment6, { children: [
2773
+ current?.error && /* @__PURE__ */ jsxs10("div", { className: "limr-mermaid-error", "data-limr-ignore": "true", role: "status", children: [
2774
+ zh ? "\u56FE\u8868\u6682\u65F6\u65E0\u6CD5\u7ED8\u5236" : "Unable to render diagram",
2775
+ " ",
2776
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => setAttempt((value) => value + 1), children: locale.retry })
2777
+ ] }),
2778
+ /* @__PURE__ */ jsx15("pre", { className: "limr-mermaid-source", hidden: !source, children: /* @__PURE__ */ jsx15("code", { children: code }) }),
2779
+ /* @__PURE__ */ jsx15(
2780
+ "div",
2781
+ {
2782
+ className: "limr-mermaid-viewport",
2783
+ hidden: !!source,
2784
+ ref: content,
2785
+ tabIndex: 0,
2786
+ role: "region",
2787
+ "aria-label": zh ? "\u56FE\u8868\u89C6\u53E3" : "Diagram viewport",
2788
+ "aria-description": zh ? "\u62D6\u52A8\u5E73\u79FB\uFF1B\u65B9\u5411\u952E\u6216\u6EDA\u8F6E\u79FB\u52A8\uFF0CShift \u52A0\u6EDA\u8F6E\u6A2A\u5411\u79FB\u52A8\uFF1B\u5355\u51FB\u6216\u6309 Enter \u6574\u5757\u9009\u62E9\u3002" : "Drag to pan; use arrow keys or the wheel to scroll, Shift+wheel to scroll horizontally; click or press Enter to select the block.",
2789
+ onDragStart: (event) => event.preventDefault(),
2790
+ onPointerDown: (event) => {
2791
+ suppressClick.current = false;
2792
+ if (event.button !== 0 || event.pointerType === "touch") return;
2793
+ const viewport = event.currentTarget;
2794
+ const bounds = viewport.getBoundingClientRect();
2795
+ if (event.clientX >= bounds.left + viewport.clientLeft + viewport.clientWidth || event.clientY >= bounds.top + viewport.clientTop + viewport.clientHeight) return;
2796
+ event.preventDefault();
2797
+ viewport.focus({ preventScroll: true });
2798
+ drag.current = { id: event.pointerId, x: event.clientX, y: event.clientY, left: viewport.scrollLeft, top: viewport.scrollTop, moved: false };
2799
+ viewport.setPointerCapture(event.pointerId);
2800
+ },
2801
+ onPointerMove: (event) => {
2802
+ const gesture = drag.current;
2803
+ if (!gesture || gesture.id !== event.pointerId) return;
2804
+ const x = event.clientX - gesture.x, y = event.clientY - gesture.y;
2805
+ if (!gesture.moved && Math.hypot(x, y) <= 4) return;
2806
+ if (!gesture.moved) {
2807
+ gesture.moved = true;
2808
+ runtime.clearSelection();
2809
+ event.currentTarget.dataset.dragging = "true";
2810
+ }
2811
+ event.currentTarget.scrollTo(gesture.left - x, gesture.top - y);
2812
+ },
2813
+ onPointerUp: (event) => {
2814
+ if (drag.current?.id !== event.pointerId) return;
2815
+ suppressClick.current = drag.current.moved;
2816
+ drag.current = null;
2817
+ delete event.currentTarget.dataset.dragging;
2818
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
2819
+ },
2820
+ onLostPointerCapture: (event) => {
2821
+ drag.current = null;
2822
+ delete event.currentTarget.dataset.dragging;
2823
+ },
2824
+ onClick: (event) => {
2825
+ if (!event.defaultPrevented && !suppressClick.current && !event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey) select();
2826
+ },
2827
+ onKeyDown: (event) => {
2828
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
2829
+ const viewport = event.currentTarget;
2830
+ const moves = { ArrowLeft: [-48, 0], ArrowRight: [48, 0], ArrowUp: [0, -48], ArrowDown: [0, 48], PageUp: [0, -viewport.clientHeight * 0.9], PageDown: [0, viewport.clientHeight * 0.9] };
2831
+ if (moves[event.key]) viewport.scrollBy(...moves[event.key]);
2832
+ else if (event.key === "Home") viewport.scrollTo(0, 0);
2833
+ else if (event.key === "End") viewport.scrollTo(viewport.scrollWidth, viewport.scrollHeight);
2834
+ else if (event.key === "Enter") select();
2835
+ else return;
2836
+ event.preventDefault();
2837
+ },
2838
+ children: /* @__PURE__ */ jsx15("div", { className: "limr-mermaid-canvas", style: { width: `${scale * 100}%` }, role: "img", "aria-label": zh ? "Mermaid \u56FE\u8868" : "Mermaid diagram", dangerouslySetInnerHTML: { __html: current?.svg ?? "" } })
2839
+ }
2840
+ )
2841
+ ] })
2842
+ ] }),
2843
+ /* @__PURE__ */ jsxs10("div", { className: "limr-block-tools", "data-limr-ignore": "true", role: "group", "aria-label": zh ? "\u56FE\u8868\u5DE5\u5177" : "Diagram tools", children: [
2844
+ /* @__PURE__ */ jsx15(CopyButton, { value: code, label: zh ? "\u590D\u5236 Mermaid \u6E90\u4EE3\u7801" : "Copy Mermaid source" }),
2845
+ /* @__PURE__ */ jsx15("button", { type: "button", disabled: loading2 || !!source || scale >= 3, title: zh ? "\u653E\u5927" : "Zoom in", "aria-label": zh ? "\u653E\u5927" : "Zoom in", onClick: () => setScale((value) => Math.min(3, +(value + 0.25).toFixed(2))), children: /* @__PURE__ */ jsx15(ToolIcon, { name: "zoom-in" }) }),
2846
+ /* @__PURE__ */ jsx15("button", { type: "button", disabled: loading2 || !!source || scale <= 0.5, title: zh ? "\u7F29\u5C0F" : "Zoom out", "aria-label": zh ? "\u7F29\u5C0F" : "Zoom out", onClick: () => setScale((value) => Math.max(0.5, +(value - 0.25).toFixed(2))), children: /* @__PURE__ */ jsx15(ToolIcon, { name: "zoom-out" }) }),
2847
+ /* @__PURE__ */ jsx15("button", { type: "button", disabled: loading2 || current?.error, title: label, "aria-label": label, "aria-pressed": sourceView, onClick: () => setSourceView((value) => !value), children: /* @__PURE__ */ jsx15(ToolIcon, { name: sourceView ? "image" : "source" }) })
2848
+ ] })
2849
+ ] });
2850
+ }
2851
+ function mermaid() {
2852
+ return {
2853
+ name: "mermaid",
2854
+ directives: ["mermaid"],
2855
+ literal: true,
2856
+ scope: "local",
2857
+ components: { mermaid: MermaidNode },
2858
+ transform(root, context) {
2859
+ literalDirective(root, context.source, "mermaid");
2860
+ walk(root, (node) => {
2861
+ if (node.type === "code" && String(node.lang).toLowerCase() === "mermaid") node.type = "mermaid";
2862
+ if (node.type === "mermaid") node.selection = "atomic";
2863
+ });
2864
+ }
2865
+ };
2866
+ }
2867
+
2868
+ // src/extensions/callout.tsx
2869
+ import { useState as useState5 } from "react";
2870
+
2871
+ // src/extensions/inline-source.ts
2872
+ function point(source, offset, base) {
2873
+ const between = source.slice(base.offset, offset), breaks = [...between.matchAll(/\r\n|\r|\n/g)], last = breaks.at(-1);
2874
+ return { offset, line: base.line + breaks.length, column: last ? between.length - last.index - last[0].length + 1 : base.column + between.length };
2875
+ }
2876
+ function sliceInline(node, from, until, source) {
2877
+ if (!node.position) return;
2878
+ const range = rangeOf(node);
2879
+ if (range.end <= from || range.start >= until) return;
2880
+ if (range.start >= from && range.end <= until) return node;
2881
+ let start = Math.max(range.start, from), end = Math.min(range.end, until);
2882
+ const result = { ...node };
2883
+ if (node.type === "text") {
2884
+ const value = (node.value ?? "").replace(/\r\n?/g, "\n"), mapped = mapText(source, value, range);
2885
+ const first = mapped.ends.findIndex((offset) => offset > start);
2886
+ let last = mapped.starts.findIndex((offset) => offset >= end);
2887
+ if (first < 0) return;
2888
+ if (last < 0) last = mapped.starts.length;
2889
+ if (last <= first) return;
2890
+ result.value = value.slice(first, last);
2891
+ start = mapped.starts[first];
2892
+ end = mapped.ends[last - 1];
2893
+ }
2894
+ if (node.children) result.children = sliceChildren(node.children, start, end, source);
2895
+ result.position = { start: point(source, start, node.position.start), end: point(source, end, node.position.start) };
2896
+ return result;
2897
+ }
2898
+ function sliceChildren(children, from, until, source) {
2899
+ return children.flatMap((child) => {
2900
+ const value = sliceInline(child, from, until, source);
2901
+ return value ? [value] : [];
2902
+ });
2903
+ }
2904
+ function textTokens(children, expression, source) {
2905
+ return children.flatMap((node) => {
2906
+ if (node.type !== "text" || !node.value || !node.position) return [];
2907
+ const matches = [...node.value.matchAll(expression)];
2908
+ if (!matches.length) return [];
2909
+ const map = mapText(source, node.value, rangeOf(node));
2910
+ return matches.flatMap((match) => {
2911
+ const start = map.starts[match.index], end = map.ends[match.index + match[0].length - 1];
2912
+ return source.slice(start, end) === match[0] ? [{ start, end }] : [];
2913
+ });
2914
+ });
2915
+ }
2279
2916
 
2280
2917
  // src/extensions/define.ts
2281
- function defineBlock({ name, component, tags = [name], directives = [name], selection = "content" }) {
2282
- return { name, tags, directives, selection, display: "block", scope: "local", components: Object.fromEntries([.../* @__PURE__ */ new Set([...tags, ...directives])].map((tag) => [tag, component])) };
2918
+ function defineBlock({ name, component, tags = [name], directives = [name], selection = "content", streaming }) {
2919
+ return { name, tags, directives, selection, streaming, display: "block", scope: "local", components: Object.fromEntries([.../* @__PURE__ */ new Set([...tags, ...directives])].map((tag) => [tag, component])) };
2283
2920
  }
2284
2921
  function defineAnnotation(type) {
2285
2922
  return { name: `annotation:${type.type}`, annotationTypes: [type], scope: "local" };
@@ -2288,43 +2925,293 @@ function defineExtension(extension) {
2288
2925
  return extension;
2289
2926
  }
2290
2927
 
2928
+ // src/extensions/callout.tsx
2929
+ import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
2930
+ var types = {
2931
+ note: { color: "#6488b5", icon: "edit" },
2932
+ abstract: { color: "#4b9ba0", icon: "note" },
2933
+ info: { color: "#6488b5", icon: "note" },
2934
+ todo: { color: "#6488b5", icon: "check" },
2935
+ tip: { color: "#4b9b8d", icon: "sparkles" },
2936
+ success: { color: "#659664", icon: "check" },
2937
+ question: { color: "#b38b4e", icon: "question" },
2938
+ warning: { color: "#bc8850", icon: "question" },
2939
+ failure: { color: "#be6967", icon: "close" },
2940
+ danger: { color: "#be6967", icon: "close" },
2941
+ bug: { color: "#be6967", icon: "note" },
2942
+ example: { color: "#9580b4", icon: "note" },
2943
+ quote: { color: "#928b80", icon: "comment" }
2944
+ };
2945
+ var aliases2 = { summary: "abstract", tldr: "abstract", hint: "tip", important: "tip", check: "success", done: "success", help: "question", faq: "question", caution: "warning", attention: "warning", fail: "failure", missing: "failure", error: "danger", cite: "quote" };
2946
+ function calloutType(value) {
2947
+ const name = value.toLowerCase();
2948
+ return aliases2[name] ?? (Object.hasOwn(types, name) ? name : "note");
2949
+ }
2950
+ function Callout({ node, renderChildren, renderAttribute }) {
2951
+ const zh = useLocale().language?.startsWith("zh");
2952
+ const name = String(node.attributes?.type ?? "note").toLowerCase(), type = calloutType(name), style = types[type];
2953
+ const title = node.children?.find((child) => child.type === "calloutTitle");
2954
+ const body = node.children?.filter((child) => child.type !== "calloutTitle") ?? [];
2955
+ const fold = node.attributes?.fold;
2956
+ const [expanded, setExpanded] = useState5(fold !== "-" && fold !== "closed");
2957
+ const content = /* @__PURE__ */ jsx16("div", { className: "limr-block-content", children: renderChildren(body) });
2958
+ const label = /* @__PURE__ */ jsxs11(Fragment7, { children: [
2959
+ /* @__PURE__ */ jsx16(ToolIcon, { name: style.icon, "data-limr-ignore": "true" }),
2960
+ /* @__PURE__ */ jsx16("span", { children: title ? renderChildren(title.children ?? []) : node.attributes?.title ? renderAttribute("title") : /* @__PURE__ */ jsx16("span", { "data-limr-ignore": "true", children: name.charAt(0).toUpperCase() + name.slice(1) }) })
2961
+ ] });
2962
+ return /* @__PURE__ */ jsxs11("aside", { className: "limr-note limr-callout", "data-callout": name, "data-callout-type": type, style: { "--limr-callout-color": style.color }, children: [
2963
+ /* @__PURE__ */ jsxs11("div", { className: "limr-card-label limr-callout-title", children: [
2964
+ label,
2965
+ fold && /* @__PURE__ */ jsx16("button", { type: "button", className: "limr-callout-toggle", "aria-label": expanded ? zh ? "\u6536\u8D77\u63D0\u793A\u5757" : "Collapse callout" : zh ? "\u5C55\u5F00\u63D0\u793A\u5757" : "Expand callout", "aria-expanded": expanded, onClick: () => setExpanded((value) => !value), "data-limr-ignore": "true", children: /* @__PURE__ */ jsx16(ToolIcon, { name: "chevron" }) })
2966
+ ] }),
2967
+ fold ? /* @__PURE__ */ jsx16(Collapse, { open: expanded, keepMounted: true, children: content }) : content
2968
+ ] });
2969
+ }
2970
+ function callout(options = {}) {
2971
+ return {
2972
+ ...defineBlock({ name: "callout", component: options.component ?? Callout, streaming: options.component ? void 0 : { Loading: BlockLoading } }),
2973
+ transform(root, context) {
2974
+ walk(root, (node) => {
2975
+ if (node.type !== "blockquote") return;
2976
+ const first = node.children?.[0];
2977
+ if (first?.type !== "paragraph" || !first.position) return;
2978
+ const from = first.position.start.offset, end = first.position.end.offset;
2979
+ const raw = context.source.slice(from, end);
2980
+ const header = /^\[!([\w-]+)\]([+-])?[ \t]*/.exec(raw);
2981
+ if (!header) {
2982
+ if (context.streaming && /^\[![\w-]*$/.test(raw)) {
2983
+ node.type = "callout";
2984
+ node.incomplete = true;
2985
+ node.children = [];
2986
+ }
2987
+ return;
2988
+ }
2989
+ node.type = "callout";
2990
+ node.attributes = { type: header[1].toLowerCase(), ...header[2] ? { fold: header[2] } : {} };
2991
+ const newline = /\r\n|\r|\n/.exec(raw), titleEnd = newline ? from + newline.index : end;
2992
+ const bodyStart = newline ? titleEnd + newline[0].length : end;
2993
+ const title = sliceInline(first, from + header[0].length, titleEnd, context.source);
2994
+ const body = sliceInline(first, bodyStart, end, context.source);
2995
+ node.children = [...title?.children?.length ? [{ ...title, type: "calloutTitle" }] : [], ...body?.children?.length ? [body] : [], ...node.children.slice(1)];
2996
+ });
2997
+ }
2998
+ };
2999
+ }
3000
+
3001
+ // src/extensions/inline-highlights.tsx
3002
+ import { Fragment as Fragment8, jsx as jsx17 } from "react/jsx-runtime";
3003
+ var Content = ({ children }) => /* @__PURE__ */ jsx17(Fragment8, { children });
3004
+ function inlineHighlights(options = {}) {
3005
+ const decorate = (node, range) => {
3006
+ node.type = "embeddedHighlight";
3007
+ node.highlightRange = range;
3008
+ node.annotation = {
3009
+ id: `inline-highlight:${rangeOf(node).start}`,
3010
+ type: "highlight",
3011
+ range,
3012
+ data: { color: options.color ?? "var(--limr-inline-highlight, #6B9BD2)", style: "background", readOnly: true }
3013
+ };
3014
+ };
3015
+ return {
3016
+ name: "inline-highlights",
3017
+ tags: ["highlight"],
3018
+ scope: "local",
3019
+ components: { embeddedHighlight: options.component ?? Content },
3020
+ transform(root, { source, streaming }) {
3021
+ const visit = (node) => {
3022
+ if (!node.children || ["code", "inlineCode", "math", "inlineMath", "html", "footnoteDefinition", "annotationDefinition"].includes(node.type)) return;
3023
+ node.children.forEach(visit);
3024
+ if (node.type === "component" && node.name === "highlight") {
3025
+ const first = node.children[0], last = node.children.at(-1);
3026
+ if (first && last) decorate(node, { start: rangeOf(first).start, end: rangeOf(last).end });
3027
+ return;
3028
+ }
3029
+ const tokens = textTokens(node.children, /(?<!=)==(?!=)/g, source);
3030
+ if (!node.position) return;
3031
+ if (!tokens.length) {
3032
+ const last = node.children.at(-1), end = rangeOf(node).end;
3033
+ if (streaming && end === source.length && last?.type === "text" && source.endsWith("=") && source[end - 2] !== "\\") node.children = sliceChildren(node.children, rangeOf(node).start, end - 1, source);
3034
+ return;
3035
+ }
3036
+ const children = [];
3037
+ let from = rangeOf(node).start;
3038
+ for (let index = 0; index < tokens.length; index += 2) {
3039
+ const open = tokens[index], close = tokens[index + 1];
3040
+ if (!close && (!streaming || rangeOf(node).end < source.trimEnd().length)) break;
3041
+ let end = close?.start ?? rangeOf(node).end;
3042
+ if (!close && source[end - 1] === "=" && source[end - 2] !== "=" && source[end - 2] !== "\\") end--;
3043
+ const body = sliceChildren(node.children, open.end, end, source);
3044
+ if (!body.length && close) continue;
3045
+ children.push(...sliceChildren(node.children, from, open.start, source));
3046
+ const highlight = {
3047
+ type: "embeddedHighlight",
3048
+ inline: true,
3049
+ children: body,
3050
+ incomplete: close ? void 0 : true,
3051
+ position: { start: point(source, open.start, node.position.start), end: point(source, close?.end ?? rangeOf(node).end, node.position.start) }
3052
+ };
3053
+ if (body.length) decorate(highlight, { start: open.end, end });
3054
+ children.push(highlight);
3055
+ from = close?.end ?? rangeOf(node).end;
3056
+ }
3057
+ children.push(...sliceChildren(node.children, from, rangeOf(node).end, source));
3058
+ node.children = children;
3059
+ };
3060
+ visit(root);
3061
+ }
3062
+ };
3063
+ }
3064
+
3065
+ // src/extensions/footnotes.tsx
3066
+ import { useMemo as useMemo3 } from "react";
3067
+ import { fromMarkdown as fromMarkdown2 } from "mdast-util-from-markdown";
3068
+ import { jsx as jsx18 } from "react/jsx-runtime";
3069
+ var idFor = (identifier2) => `footnote:${String(identifier2).trim().replace(/\s+/g, " ").toLowerCase()}`;
3070
+ function Icon() {
3071
+ return /* @__PURE__ */ jsx18(ToolIcon, { name: "comment" });
3072
+ }
3073
+ function Content2({ data }) {
3074
+ return /* @__PURE__ */ jsx18(AnnotationMarkdown, { source: data?.body ?? "" });
3075
+ }
3076
+ function Card(props) {
3077
+ return /* @__PURE__ */ jsx18(AnnotationCard, { ...props, title: useLocale().footnotes });
3078
+ }
3079
+ function Preview({ data }) {
3080
+ const summary = useMemo3(() => textContent(fromMarkdown2(data?.body ?? "")).replace(/\s+/g, " ").trim(), [data?.body]);
3081
+ return summary ? /* @__PURE__ */ jsx18("span", { title: summary, children: summary }) : null;
3082
+ }
3083
+ function footnotes(options = {}) {
3084
+ const type = options.type ?? "footnote";
3085
+ const definition2 = (id, body) => ({ id, type, data: { body } });
3086
+ return {
3087
+ name: "footnotes",
3088
+ tags: ["footnote", "footnote-ref"],
3089
+ scope: "local",
3090
+ annotationTypes: [{
3091
+ type,
3092
+ label: (locale) => locale.footnotes,
3093
+ icon: Icon,
3094
+ color: "#6B9BD2",
3095
+ highlight: "hover",
3096
+ Content: Content2,
3097
+ ...options,
3098
+ sidebar: { Preview, collapseOnContentClick: false, ...options.sidebar },
3099
+ overlay: { Card, expand: false, ...options.overlay }
3100
+ }],
3101
+ transform(root, { source, streaming }) {
3102
+ const visit = (node) => {
3103
+ if (node.type === "footnoteDefinition") {
3104
+ const range = rangeOf(node), raw = source.slice(range.start, range.end);
3105
+ const body = raw.replace(/^\[\^[^\]]+\]:[ \t]*/, "").replace(/(\r\n|\r|\n)(?: {4}|\t)/g, "$1");
3106
+ node.annotation = definition2(idFor(node.identifier), body);
3107
+ return;
3108
+ }
3109
+ if (node.type === "footnoteReference") {
3110
+ node.type = "annotationReference";
3111
+ node.inline = true;
3112
+ node.annotationIds = [idFor(node.identifier)];
3113
+ return;
3114
+ }
3115
+ if (node.type === "component" && ["footnote", "footnote-ref"].includes(node.name ?? "")) {
3116
+ if (node.incomplete) {
3117
+ node.children = [];
3118
+ return;
3119
+ }
3120
+ const id = idFor(node.attributes?.id ?? "");
3121
+ if (node.name === "footnote") {
3122
+ const first = node.children?.[0], last = node.children?.at(-1);
3123
+ node.type = "annotationDefinition";
3124
+ node.annotation = definition2(id, first && last ? source.slice(rangeOf(first).start, rangeOf(last).end) : String(node.attributes?.body ?? ""));
3125
+ return;
3126
+ }
3127
+ node.type = "annotationReference";
3128
+ node.annotationIds = [id];
3129
+ return;
3130
+ }
3131
+ if (!node.children || ["code", "inlineCode", "math", "inlineMath", "html", "annotationDefinition"].includes(node.type)) return;
3132
+ node.children.forEach(visit);
3133
+ const opens = textTokens(node.children, /\^\[/g, source);
3134
+ if (opens.length && node.position) {
3135
+ const children = [], range = rangeOf(node);
3136
+ let from = range.start;
3137
+ for (const open of opens) {
3138
+ if (open.start < from) continue;
3139
+ let depth = 1, at = open.end;
3140
+ const protectedNodes = node.children.filter((child) => child.type !== "text").map(rangeOf);
3141
+ for (; at < range.end && depth; at++) {
3142
+ const protectedNode = protectedNodes.find((child) => child.start <= at && child.end > at);
3143
+ if (protectedNode) {
3144
+ at = protectedNode.end - 1;
3145
+ continue;
3146
+ }
3147
+ if (source[at] === "\\") {
3148
+ at++;
3149
+ continue;
3150
+ }
3151
+ if (source[at] === "[") depth++;
3152
+ else if (source[at] === "]") depth--;
3153
+ }
3154
+ if (depth) {
3155
+ if (streaming && range.end >= source.trimEnd().length) {
3156
+ children.push(...sliceChildren(node.children, from, open.start, source));
3157
+ from = range.end;
3158
+ break;
3159
+ }
3160
+ continue;
3161
+ }
3162
+ const id = `inline-footnote:${open.start}`;
3163
+ children.push(...sliceChildren(node.children, from, open.start, source), {
3164
+ type: "annotationReference",
3165
+ inline: true,
3166
+ annotationIds: [id],
3167
+ annotation: definition2(id, source.slice(open.end, at - 1)),
3168
+ position: { start: point(source, open.start, node.position.start), end: point(source, at, node.position.start) }
3169
+ });
3170
+ from = at;
3171
+ }
3172
+ children.push(...sliceChildren(node.children, from, range.end, source));
3173
+ node.children = children;
3174
+ }
3175
+ node.children.forEach((child, index) => {
3176
+ const previous = node.children[index - 1];
3177
+ if (child.type === "annotationReference" && previous?.type === "embeddedHighlight" && rangeOf(previous).end === rangeOf(child).start) child.referenceRange = previous.highlightRange;
3178
+ });
3179
+ };
3180
+ visit(root);
3181
+ }
3182
+ };
3183
+ }
3184
+
3185
+ // src/extensions/blocks.tsx
3186
+ import { useState as useState8 } from "react";
3187
+
2291
3188
  // src/components/Disclosure.tsx
2292
- import { useEffect as useEffect5, useState as useState4 } from "react";
2293
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
3189
+ import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
2294
3190
  function Disclosure({ title, open, onChange, children }) {
2295
- const [present, setPresent] = useState4(open);
2296
- useEffect5(() => {
2297
- if (open) {
2298
- setPresent(true);
2299
- return;
2300
- }
2301
- const timer = setTimeout(() => setPresent(false), 420);
2302
- return () => clearTimeout(timer);
2303
- }, [open]);
2304
- return /* @__PURE__ */ jsxs8("details", { className: "limr-disclosure", open: present || open, "data-expanded": open || void 0, children: [
2305
- /* @__PURE__ */ jsxs8("summary", { "aria-expanded": open, onClick: (event) => {
3191
+ return /* @__PURE__ */ jsxs12("details", { className: "limr-disclosure", open: true, "data-expanded": open || void 0, children: [
3192
+ /* @__PURE__ */ jsxs12("summary", { "aria-expanded": open, onClick: (event) => {
2306
3193
  event.preventDefault();
2307
3194
  onChange(!open);
2308
3195
  }, "data-limr-ignore": "true", children: [
2309
- /* @__PURE__ */ jsx11("span", { className: "limr-underline", children: title }),
2310
- /* @__PURE__ */ jsx11("svg", { viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx11("path", { d: "m6 8 4 4 4-4" }) })
3196
+ /* @__PURE__ */ jsx19("span", { className: "limr-underline", children: title }),
3197
+ /* @__PURE__ */ jsx19("svg", { viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx19("path", { d: "m6 8 4 4 4-4" }) })
2311
3198
  ] }),
2312
- /* @__PURE__ */ jsx11(Collapse, { open, keepMounted: true, children: /* @__PURE__ */ jsx11("div", { className: "limr-question-detail", children }) })
3199
+ /* @__PURE__ */ jsx19(Collapse, { open, keepMounted: true, children: /* @__PURE__ */ jsx19("div", { className: "limr-question-detail", children }) })
2313
3200
  ] });
2314
3201
  }
2315
3202
 
2316
3203
  // src/components/DiamondCheck.tsx
2317
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
3204
+ import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
2318
3205
  function DiamondCheck({ checked }) {
2319
- return /* @__PURE__ */ jsxs9("svg", { className: "limr-choice-diamond", width: "14", height: "14", viewBox: "0 0 18 18", fill: "none", "aria-hidden": "true", "data-limr-ignore": "true", "data-checked": checked || void 0, children: [
2320
- /* @__PURE__ */ jsx12("path", { className: "limr-diamond-outline", d: "M9 1.5L16.5 9L9 16.5L1.5 9Z", fill: "none" }),
2321
- /* @__PURE__ */ jsx12("path", { className: "limr-diamond-fill", d: "M9 5L13 9L9 13L5 9Z", fill: "currentColor" })
3206
+ return /* @__PURE__ */ jsxs13("svg", { className: "limr-choice-diamond", width: "14", height: "14", viewBox: "0 0 18 18", fill: "none", "aria-hidden": "true", "data-limr-ignore": "true", "data-checked": checked || void 0, children: [
3207
+ /* @__PURE__ */ jsx20("path", { className: "limr-diamond-outline", d: "M9 1.5L16.5 9L9 16.5L1.5 9Z", fill: "none" }),
3208
+ /* @__PURE__ */ jsx20("path", { className: "limr-diamond-fill", d: "M9 5L13 9L9 13L5 9Z", fill: "currentColor" })
2322
3209
  ] });
2323
3210
  }
2324
3211
 
2325
3212
  // src/extensions/video.tsx
2326
- import { useEffect as useEffect6, useRef as useRef9, useState as useState5 } from "react";
2327
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
3213
+ import { useEffect as useEffect7, useRef as useRef12, useState as useState6 } from "react";
3214
+ import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
2328
3215
  function resolveVideo(source, provider = "auto") {
2329
3216
  const safe = safeUrl(source);
2330
3217
  if (!safe) return;
@@ -2372,21 +3259,21 @@ function video(options = {}) {
2372
3259
  function Video({ node, source: markdown, renderAttribute }) {
2373
3260
  const runtime = useRuntime();
2374
3261
  const scrollRoot = useStoreSelector(runtime.configuration, (config) => config.scrollRoot);
2375
- const element = useRef9(null);
2376
- const [visible, setVisible] = useState5(false);
2377
- const [playing, setPlaying] = useState5(false);
2378
- const [failed, setFailed] = useState5(false);
3262
+ const element = useRef12(null);
3263
+ const [visible, setVisible] = useState6(false);
3264
+ const [playing, setPlaying] = useState6(false);
3265
+ const [failed, setFailed] = useState6(false);
2379
3266
  const provider = String(node.attributes?.provider ?? "auto");
2380
3267
  const source = String(node.attributes?.url ?? node.attributes?.src ?? "");
2381
3268
  const requested = node.attributes?.loading ?? options.loading ?? "visible";
2382
3269
  const loading2 = requested === "click" || requested === "eager" ? requested : "visible";
2383
3270
  const resolved = options.resolveUrl?.(provider, source);
2384
3271
  const media = resolved && safeUrl(resolved) ? { url: safeUrl(resolved), native: provider === "native" } : resolveVideo(source, provider);
2385
- useEffect6(() => {
3272
+ useEffect7(() => {
2386
3273
  setFailed(false);
2387
3274
  setPlaying(false);
2388
3275
  }, [source, provider]);
2389
- useEffect6(() => {
3276
+ useEffect7(() => {
2390
3277
  if (visible || loading2 !== "visible" || !element.current) return;
2391
3278
  if (typeof IntersectionObserver === "undefined") {
2392
3279
  setVisible(true);
@@ -2404,20 +3291,20 @@ function video(options = {}) {
2404
3291
  const enabled = loading2 === "eager" || loading2 === "visible" && visible || playing;
2405
3292
  const title = String(node.attributes?.title ?? `${provider === "auto" ? "Video" : provider} video`);
2406
3293
  const original = safeUrl(source);
2407
- return /* @__PURE__ */ jsxs10("div", { ref: element, className: "limr-video", "data-limr-selection-surface": "", "data-loading": loading2, children: [
2408
- /* @__PURE__ */ jsx13("div", { className: "limr-video-frame", children: !media || failed ? /* @__PURE__ */ jsxs10("div", { className: "limr-video-error", children: [
2409
- /* @__PURE__ */ jsx13("span", { children: runtime.locale.unavailable }),
2410
- original && /* @__PURE__ */ jsx13("a", { href: original, target: "_blank", rel: "noopener noreferrer", children: source })
2411
- ] }) : enabled ? media.native ? /* @__PURE__ */ jsx13("video", { controls: true, playsInline: true, preload: "metadata", src: media.url, "aria-label": title, onError: () => setFailed(true) }) : /* @__PURE__ */ jsx13("iframe", { src: media.url, title, allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", allowFullScreen: true, referrerPolicy: "strict-origin-when-cross-origin", onError: () => setFailed(true) }) : /* @__PURE__ */ jsx13("button", { type: "button", className: "limr-video-play", onClick: () => setPlaying(true), "aria-label": runtime.locale.play, children: /* @__PURE__ */ jsx13("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx13("path", { d: "m8 5 11 7-11 7Z", fill: "currentColor" }) }) }) }),
2412
- node.attributes?.title && /* @__PURE__ */ jsx13("div", { className: "limr-video-title", children: renderAttribute("title") }),
2413
- /* @__PURE__ */ jsx13(CopyButton, { className: "limr-video-copy", variant: "glass", value: markdown })
3294
+ return /* @__PURE__ */ jsxs14("div", { ref: element, className: "limr-video", "data-limr-selection-surface": "", "data-loading": loading2, children: [
3295
+ /* @__PURE__ */ jsx21("div", { className: "limr-video-frame", children: !media || failed ? /* @__PURE__ */ jsxs14("div", { className: "limr-video-error", children: [
3296
+ /* @__PURE__ */ jsx21("span", { children: runtime.locale.unavailable }),
3297
+ original && /* @__PURE__ */ jsx21("a", { href: original, target: "_blank", rel: "noopener noreferrer", children: source })
3298
+ ] }) : enabled ? media.native ? /* @__PURE__ */ jsx21("video", { controls: true, playsInline: true, preload: "metadata", src: media.url, "aria-label": title, onError: () => setFailed(true) }) : /* @__PURE__ */ jsx21("iframe", { src: media.url, title, allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", allowFullScreen: true, referrerPolicy: "strict-origin-when-cross-origin", onError: () => setFailed(true) }) : /* @__PURE__ */ jsx21("button", { type: "button", className: "limr-video-play", onClick: () => setPlaying(true), "aria-label": runtime.locale.play, children: /* @__PURE__ */ jsx21("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx21("path", { d: "m8 5 11 7-11 7Z", fill: "currentColor" }) }) }) }),
3299
+ node.attributes?.title && /* @__PURE__ */ jsx21("div", { className: "limr-video-title", children: renderAttribute("title") }),
3300
+ /* @__PURE__ */ jsx21(CopyButton, { className: "limr-video-copy", variant: "glass", value: markdown })
2414
3301
  ] });
2415
3302
  }
2416
- return defineBlock({ name: "video", tags: ["video", "md-video"], component: options.component ?? Video, selection: "atomic" });
3303
+ return defineBlock({ name: "video", streaming: options.component ? void 0 : { Loading: BlockLoading }, tags: ["video", "md-video"], component: options.component ?? Video, selection: "atomic" });
2417
3304
  }
2418
3305
 
2419
3306
  // src/extensions/figures.tsx
2420
- import { createContext, useContext as useContext2, useEffect as useEffect7, useMemo as useMemo3, useRef as useRef10, useState as useState6 } from "react";
3307
+ import { createContext, useContext as useContext2, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef13, useState as useState7 } from "react";
2421
3308
 
2422
3309
  // src/extensions/figure-layout.ts
2423
3310
  var defaultFigureLimits = { minWidth: 220, maxWidth: 560, minHeight: 120, maxHeight: 480 };
@@ -2459,7 +3346,7 @@ function figureLimits(attributes = {}, defaults2 = defaultFigureLimits) {
2459
3346
  }
2460
3347
 
2461
3348
  // src/extensions/figures.tsx
2462
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
3349
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
2463
3350
  var FigureContext = createContext(defaultFigureLimits);
2464
3351
  var captionNode = (node) => node.name === "figcaption" || node.name === "caption";
2465
3352
  var flatten = (nodes) => nodes.flatMap((node) => node.type === "paragraph" && node.children?.some((child) => child.name === "figure" || captionNode(child)) ? node.children : [node]);
@@ -2476,14 +3363,14 @@ function figures(options = {}) {
2476
3363
  const panels = nodes.filter((child) => child.name === "figure");
2477
3364
  const captions = nodes.filter(captionNode);
2478
3365
  const body = nodes.filter((child) => !captionNode(child));
2479
- const limits = useMemo3(() => figureLimits(node.attributes, defaults2), [node.attributes]);
2480
- const grid = useRef10(null);
2481
- const [columns, setColumns] = useState6(1);
3366
+ const limits = useMemo4(() => figureLimits(node.attributes, defaults2), [node.attributes]);
3367
+ const grid = useRef13(null);
3368
+ const [columns, setColumns] = useState7(1);
2482
3369
  const gap = Math.min(100, dimension(node.attributes?.gap, options.gap ?? 16));
2483
3370
  const rows = dimension(node.attributes?.rows, options.rows ?? 0);
2484
3371
  const requested = dimension(node.attributes?.columns ?? node.attributes?.cols, options.columns ?? 0) || (rows > 0 ? Math.ceil(panels.length / rows) : void 0);
2485
3372
  const responsive = String(node.attributes?.responsive ?? options.responsive ?? true) !== "false";
2486
- useEffect7(() => {
3373
+ useEffect8(() => {
2487
3374
  const element = grid.current;
2488
3375
  if (!element) return;
2489
3376
  const measure = () => {
@@ -2506,22 +3393,22 @@ function figures(options = {}) {
2506
3393
  element.removeEventListener("error", measure, true);
2507
3394
  };
2508
3395
  }, [node, limits, gap, requested, responsive]);
2509
- return /* @__PURE__ */ jsxs11("figure", { id: identifier(node), className: "limr-figures", "data-limr-selection-order": "visual", "data-columns": columns, "data-responsive": responsive, children: [
2510
- /* @__PURE__ */ jsx14(CopyButton, { value: source, label: locale.copyFigures, variant: "glass", className: "limr-figures-copy" }),
2511
- /* @__PURE__ */ jsx14(FigureContext.Provider, { value: limits, children: /* @__PURE__ */ jsx14("div", { ref: grid, className: "limr-figures-grid", style: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, gridTemplateRows: rows ? `repeat(${Math.max(1, Math.min(24, Math.floor(rows)))}, auto auto)` : void 0, gap }, children: renderChildren(body) }) }),
2512
- captions.length ? renderChildren(captions) : node.attributes?.caption ? /* @__PURE__ */ jsx14("figcaption", { children: renderAttribute("caption") }) : null
3396
+ return /* @__PURE__ */ jsxs15("figure", { id: identifier(node), className: "limr-figures", "data-limr-selection-order": "visual", "data-columns": columns, "data-responsive": responsive, children: [
3397
+ /* @__PURE__ */ jsx22(CopyButton, { value: source, label: locale.copyFigures, variant: "glass", className: "limr-figures-copy" }),
3398
+ /* @__PURE__ */ jsx22(FigureContext.Provider, { value: limits, children: /* @__PURE__ */ jsx22("div", { ref: grid, className: "limr-figures-grid", style: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, gridTemplateRows: rows ? `repeat(${Math.max(1, Math.min(24, Math.floor(rows)))}, auto auto)` : void 0, gap }, children: renderChildren(body) }) }),
3399
+ captions.length ? renderChildren(captions) : node.attributes?.caption ? /* @__PURE__ */ jsx22("figcaption", { children: renderAttribute("caption") }) : null
2513
3400
  ] });
2514
3401
  }
2515
3402
  function Figure({ node, renderChildren, renderAttribute }) {
2516
3403
  const locale = useLocale();
2517
3404
  const inherited = useContext2(FigureContext);
2518
3405
  const limits = figureLimits(node.attributes, inherited === defaultFigureLimits ? defaults2 : inherited);
2519
- const media = useRef10(null);
2520
- const [ratio, setRatio] = useState6(ratioOf(node.attributes?.width, node.attributes?.height));
3406
+ const media = useRef13(null);
3407
+ const [ratio, setRatio] = useState7(ratioOf(node.attributes?.width, node.attributes?.height));
2521
3408
  const nodes = flatten(node.children ?? []);
2522
3409
  const captions = nodes.filter(captionNode);
2523
3410
  const body = nodes.filter((child) => !captionNode(child));
2524
- useEffect7(() => {
3411
+ useEffect8(() => {
2525
3412
  const element = media.current;
2526
3413
  if (!element) return;
2527
3414
  const measure = () => {
@@ -2533,23 +3420,24 @@ function figures(options = {}) {
2533
3420
  return () => element.removeEventListener("load", measure, true);
2534
3421
  }, [node]);
2535
3422
  const maximum = figureWidths({ ratio, limits }).maximum;
2536
- return /* @__PURE__ */ jsxs11("figure", { id: identifier(node), className: "limr-figure", "data-limr-selection-order": "visual", style: {
3423
+ return /* @__PURE__ */ jsxs15("figure", { id: identifier(node), className: "limr-figure", "data-limr-selection-order": "visual", style: {
2537
3424
  "--limr-figure-width": Number.isFinite(maximum) ? `${maximum}px` : "100%",
2538
3425
  "--limr-figure-min-height": `${Math.min(limits.minHeight, limits.maxHeight)}px`,
2539
3426
  "--limr-figure-max-height": Number.isFinite(limits.maxHeight) ? `${limits.maxHeight}px` : "none"
2540
3427
  }, children: [
2541
- /* @__PURE__ */ jsx14("div", { ref: media, className: "limr-figure-media", children: renderChildren(body) }),
2542
- /* @__PURE__ */ jsx14(CopyButton, { onCopy: async () => {
3428
+ /* @__PURE__ */ jsx22("div", { ref: media, className: "limr-figure-media", children: renderChildren(body) }),
3429
+ /* @__PURE__ */ jsx22(CopyButton, { onCopy: async () => {
2543
3430
  const url = media.current?.querySelector("img")?.getAttribute("src");
2544
3431
  if (!url) throw new Error("Image URL unavailable");
2545
3432
  await navigator.clipboard.writeText(url);
2546
3433
  }, label: locale.copyImage, variant: "glass", className: "limr-figure-copy" }),
2547
- captions.length ? renderChildren(captions) : node.attributes?.caption ? /* @__PURE__ */ jsx14("figcaption", { children: renderAttribute("caption") }) : null
3434
+ captions.length ? renderChildren(captions) : node.attributes?.caption ? /* @__PURE__ */ jsx22("figcaption", { children: renderAttribute("caption") }) : null
2548
3435
  ] });
2549
3436
  }
2550
- const Caption = ({ children }) => /* @__PURE__ */ jsx14("figcaption", { children });
3437
+ const Caption = ({ children }) => /* @__PURE__ */ jsx22("figcaption", { children });
2551
3438
  return {
2552
3439
  name: "figures",
3440
+ streaming: { Loading: BlockLoading },
2553
3441
  tags: ["figures", "figure", "figcaption", "caption", "img"],
2554
3442
  directives: ["figures", "figure", "figcaption"],
2555
3443
  scope: "local",
@@ -2559,7 +3447,7 @@ function figures(options = {}) {
2559
3447
  figure: Figure,
2560
3448
  figcaption: Caption,
2561
3449
  caption: Caption,
2562
- img: ({ node }) => /* @__PURE__ */ jsx14("img", { src: safeUrl(node.attributes?.src, true), alt: String(node.attributes?.alt ?? ""), loading: "lazy", decoding: "async" })
3450
+ img: ({ node }) => /* @__PURE__ */ jsx22("img", { src: safeUrl(node.attributes?.src, true), alt: String(node.attributes?.alt ?? ""), loading: "lazy", decoding: "async" })
2563
3451
  },
2564
3452
  transform(root) {
2565
3453
  const visit = (node) => {
@@ -2579,7 +3467,7 @@ function figures(options = {}) {
2579
3467
  }
2580
3468
 
2581
3469
  // src/extensions/blocks.tsx
2582
- import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
3470
+ import { Fragment as Fragment9, jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
2583
3471
  function parts(node, name) {
2584
3472
  const result = [];
2585
3473
  walk(node, (child) => {
@@ -2587,25 +3475,25 @@ function parts(node, name) {
2587
3475
  });
2588
3476
  return result;
2589
3477
  }
2590
- var PassThrough = ({ children }) => /* @__PURE__ */ jsx15(Fragment5, { children });
3478
+ var PassThrough = ({ children }) => /* @__PURE__ */ jsx23(Fragment9, { children });
2591
3479
  function definition(options = {}) {
2592
3480
  function Definition({ node, children, renderChildren, renderAttribute }) {
2593
3481
  const runtime = useRuntime();
2594
3482
  const title = parts(node, "title")[0];
2595
3483
  const content = parts(node, "content")[0];
2596
- return /* @__PURE__ */ jsxs12("aside", { className: "limr-definition", children: [
2597
- /* @__PURE__ */ jsx15("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.definition }),
2598
- (title || node.attributes?.title) && /* @__PURE__ */ jsx15("div", { className: "limr-definition-title", children: title ? renderChildren(title.children ?? []) : renderAttribute("title") }),
2599
- /* @__PURE__ */ jsx15("div", { className: "limr-block-content", children: content ? renderChildren(content.children ?? []) : children })
3484
+ return /* @__PURE__ */ jsxs16("aside", { className: "limr-definition", children: [
3485
+ /* @__PURE__ */ jsx23("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.definition }),
3486
+ (title || node.attributes?.title) && /* @__PURE__ */ jsx23("div", { className: "limr-definition-title", children: title ? renderChildren(title.children ?? []) : renderAttribute("title") }),
3487
+ /* @__PURE__ */ jsx23("div", { className: "limr-block-content", children: content ? renderChildren(content.children ?? []) : children })
2600
3488
  ] });
2601
3489
  }
2602
- return { ...defineBlock({ name: "definition", component: options.component ?? Definition }), tags: ["definition", "title", "content"], components: { definition: options.component ?? Definition, title: PassThrough, content: PassThrough } };
3490
+ return { ...defineBlock({ name: "definition", streaming: options.component ? void 0 : { Loading: BlockLoading }, component: options.component ?? Definition }), tags: ["definition", "title", "content"], components: { definition: options.component ?? Definition, title: PassThrough, content: PassThrough } };
2603
3491
  }
2604
3492
  function question(options = {}) {
2605
3493
  function Question({ node, children, renderChildren, renderAttribute }) {
2606
3494
  const runtime = useRuntime();
2607
- const [showHint, setHint] = useState7(false), [showAnswer, setAnswer] = useState7(false);
2608
- const [selected, setSelected] = useState7([]), [checked, setChecked] = useState7(false);
3495
+ const [showHint, setHint] = useState8(false), [showAnswer, setAnswer] = useState8(false);
3496
+ const [selected, setSelected] = useState8([]), [checked, setChecked] = useState8(false);
2609
3497
  const content = parts(node, "question-content")[0];
2610
3498
  const hint = parts(node, "question-hint")[0];
2611
3499
  const answer = parts(node, "question-answer")[0];
@@ -2619,22 +3507,22 @@ function question(options = {}) {
2619
3507
  } catch {
2620
3508
  }
2621
3509
  const success = correct.length === selected.length && correct.every((index) => selected.includes(index));
2622
- return /* @__PURE__ */ jsxs12("section", { className: "limr-question", children: [
2623
- /* @__PURE__ */ jsx15("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.question }),
2624
- node.attributes?.title && /* @__PURE__ */ jsx15("div", { className: "limr-question-title", children: renderAttribute("title") }),
2625
- /* @__PURE__ */ jsx15("div", { className: "limr-block-content", children: content ? renderChildren(content.children ?? []) : !selections.length && !hint && !answer ? children : null }),
2626
- selections.length > 0 && /* @__PURE__ */ jsxs12("fieldset", { className: "limr-question-options", children: [
2627
- /* @__PURE__ */ jsx15("legend", { className: "limr-sr-only", children: String(node.attributes?.title ?? runtime.locale.question) }),
2628
- selections.map((selection, index) => /* @__PURE__ */ jsxs12("label", { "data-selected": selected.includes(index) || void 0, "data-result": checked ? correct.includes(index) ? "correct" : selected.includes(index) ? "incorrect" : void 0 : void 0, children: [
2629
- /* @__PURE__ */ jsx15("input", { type: multi ? "checkbox" : "radio", name: `${runtime.document.id}-question-${rangeOf(node).start}`, checked: selected.includes(index), disabled: checked, onChange: () => {
3510
+ return /* @__PURE__ */ jsxs16("section", { className: "limr-question", children: [
3511
+ /* @__PURE__ */ jsx23("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.question }),
3512
+ node.attributes?.title && /* @__PURE__ */ jsx23("div", { className: "limr-question-title", children: renderAttribute("title") }),
3513
+ /* @__PURE__ */ jsx23("div", { className: "limr-block-content", children: content ? renderChildren(content.children ?? []) : !selections.length && !hint && !answer ? children : null }),
3514
+ selections.length > 0 && /* @__PURE__ */ jsxs16("fieldset", { className: "limr-question-options", children: [
3515
+ /* @__PURE__ */ jsx23("legend", { className: "limr-sr-only", children: String(node.attributes?.title ?? runtime.locale.question) }),
3516
+ selections.map((selection, index) => /* @__PURE__ */ jsxs16("label", { "data-selected": selected.includes(index) || void 0, "data-result": checked ? correct.includes(index) ? "correct" : selected.includes(index) ? "incorrect" : void 0 : void 0, children: [
3517
+ /* @__PURE__ */ jsx23("input", { type: multi ? "checkbox" : "radio", name: `${runtime.document.id}-question-${rangeOf(node).start}`, checked: selected.includes(index), disabled: checked, onChange: () => {
2630
3518
  setSelected(multi ? selected.includes(index) ? selected.filter((item) => item !== index) : [...selected, index] : [index]);
2631
3519
  } }),
2632
- /* @__PURE__ */ jsx15(DiamondCheck, { checked: selected.includes(index) || checked && correct.includes(index) }),
2633
- /* @__PURE__ */ jsx15("span", { className: "limr-choice-text", children: renderChildren(selection.children ?? []) })
3520
+ /* @__PURE__ */ jsx23(DiamondCheck, { checked: selected.includes(index) || checked && correct.includes(index) }),
3521
+ /* @__PURE__ */ jsx23("span", { className: "limr-choice-text", children: renderChildren(selection.children ?? []) })
2634
3522
  ] }, index))
2635
3523
  ] }),
2636
- selections.length > 0 && correct.length > 0 && /* @__PURE__ */ jsxs12("div", { className: "limr-question-actions", "data-limr-ignore": "true", children: [
2637
- /* @__PURE__ */ jsx15("button", { type: "button", "data-checked": checked || void 0, disabled: !checked && !selected.length, onClick: () => {
3524
+ selections.length > 0 && correct.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "limr-question-actions", "data-limr-ignore": "true", children: [
3525
+ /* @__PURE__ */ jsx23("button", { type: "button", "data-checked": checked || void 0, disabled: !checked && !selected.length, onClick: () => {
2638
3526
  if (checked) {
2639
3527
  setChecked(false);
2640
3528
  setSelected([]);
@@ -2643,53 +3531,53 @@ function question(options = {}) {
2643
3531
  setChecked(true);
2644
3532
  setAnswer(true);
2645
3533
  }
2646
- }, children: /* @__PURE__ */ jsx15("span", { className: "limr-button-label", children: checked ? runtime.locale.retry : runtime.locale.submit }, checked ? "retry" : "submit") }),
2647
- /* @__PURE__ */ jsx15(Collapse, { open: checked, children: /* @__PURE__ */ jsx15("span", { role: "status", className: `limr-question-result ${success ? "limr-success" : "limr-error"}`, children: success ? runtime.locale.correct : runtime.locale.incorrect }) })
3534
+ }, children: /* @__PURE__ */ jsx23("span", { className: "limr-button-label", children: checked ? runtime.locale.retry : runtime.locale.submit }, checked ? "retry" : "submit") }),
3535
+ /* @__PURE__ */ jsx23(Collapse, { open: checked, children: /* @__PURE__ */ jsx23("span", { role: "status", className: `limr-question-result ${success ? "limr-success" : "limr-error"}`, children: success ? runtime.locale.correct : runtime.locale.incorrect }) })
2648
3536
  ] }),
2649
- hint && /* @__PURE__ */ jsx15(Disclosure, { title: runtime.locale.hint, open: showHint, onChange: setHint, children: renderChildren(hint.children ?? []) }),
2650
- answer && /* @__PURE__ */ jsx15(Disclosure, { title: runtime.locale.answer, open: showAnswer, onChange: setAnswer, children: renderChildren(answer.children ?? []) })
3537
+ hint && /* @__PURE__ */ jsx23(Disclosure, { title: runtime.locale.hint, open: showHint, onChange: setHint, children: renderChildren(hint.children ?? []) }),
3538
+ answer && /* @__PURE__ */ jsx23(Disclosure, { title: runtime.locale.answer, open: showAnswer, onChange: setAnswer, children: renderChildren(answer.children ?? []) })
2651
3539
  ] });
2652
3540
  }
2653
3541
  const names2 = ["question-content", "question-hint", "question-answer", "question-sel-grp", "question-sel"];
2654
- return { ...defineBlock({ name: "question", component: options.component ?? Question }), tags: ["question", ...names2], components: { question: options.component ?? Question, ...Object.fromEntries(names2.map((name) => [name, PassThrough])) } };
3542
+ return { ...defineBlock({ name: "question", streaming: options.component ? void 0 : { Loading: BlockLoading }, component: options.component ?? Question }), tags: ["question", ...names2], components: { question: options.component ?? Question, ...Object.fromEntries(names2.map((name) => [name, PassThrough])) } };
2655
3543
  }
2656
3544
  function extras(options = {}) {
2657
- const Extra = ({ node, children, renderAttribute }) => /* @__PURE__ */ jsxs12("div", { className: "limr-extra", children: [
2658
- /* @__PURE__ */ jsxs12("a", { className: "limr-extra-link", href: safeUrl(node.attributes?.url), target: "_blank", rel: "noreferrer", children: [
2659
- /* @__PURE__ */ jsx15("span", { className: "limr-underline", children: renderAttribute(node.attributes?.title ? "title" : "url") }),
2660
- node.attributes?.desc && /* @__PURE__ */ jsx15("p", { children: /* @__PURE__ */ jsx15("span", { className: "limr-underline", children: renderAttribute("desc") }) })
3545
+ const Extra = ({ node, children, renderAttribute }) => /* @__PURE__ */ jsxs16("div", { className: "limr-extra", children: [
3546
+ /* @__PURE__ */ jsxs16("a", { className: "limr-extra-link", href: safeUrl(node.attributes?.url), target: "_blank", rel: "noreferrer", children: [
3547
+ /* @__PURE__ */ jsx23("span", { className: "limr-underline", children: renderAttribute(node.attributes?.title ? "title" : "url") }),
3548
+ node.attributes?.desc && /* @__PURE__ */ jsx23("p", { children: /* @__PURE__ */ jsx23("span", { className: "limr-underline", children: renderAttribute("desc") }) })
2661
3549
  ] }),
2662
3550
  children
2663
3551
  ] });
2664
3552
  const Extras = ({ children }) => {
2665
3553
  const runtime = useRuntime();
2666
- return /* @__PURE__ */ jsxs12("aside", { className: "limr-extras", children: [
2667
- /* @__PURE__ */ jsx15("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.extra }),
3554
+ return /* @__PURE__ */ jsxs16("aside", { className: "limr-extras", children: [
3555
+ /* @__PURE__ */ jsx23("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.extra }),
2668
3556
  children
2669
3557
  ] });
2670
3558
  };
2671
- return { ...defineBlock({ name: "extras", component: options.component ?? Extras }), tags: ["extras", "extra"], components: { extras: options.component ?? Extras, extra: Extra } };
3559
+ return { ...defineBlock({ name: "extras", streaming: options.component ? void 0 : { Loading: BlockLoading }, component: options.component ?? Extras }), tags: ["extras", "extra"], components: { extras: options.component ?? Extras, extra: Extra } };
2672
3560
  }
2673
3561
 
2674
3562
  // src/extensions/standard.ts
2675
3563
  function standard() {
2676
- return [gfm(), math(), codeHighlight(), definition(), question(), extras(), video(), figures(), citation(), translations()];
3564
+ return [gfm(), math(), codeHighlight(), mermaid(), callout(), definition(), question(), extras(), video(), figures(), citation(), translations(), inlineHighlights(), footnotes()];
2677
3565
  }
2678
3566
 
2679
3567
  // src/react/context.tsx
2680
- import { jsx as jsx16 } from "react/jsx-runtime";
3568
+ import { jsx as jsx24 } from "react/jsx-runtime";
2681
3569
  var defaults;
2682
3570
  var RuntimeContext = createContext2(null);
2683
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect8;
3571
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect9;
2684
3572
  function DocumentProvider({ children, source = "", streaming = false, documentId, ...options }) {
2685
3573
  const generatedId = useId();
2686
- const extensions = useMemo4(() => {
3574
+ const extensions = useMemo5(() => {
2687
3575
  const base = options.extensions ?? (defaults ??= standard());
2688
3576
  return options.plugins?.length ? [...base, ...options.plugins] : base;
2689
3577
  }, [options.extensions, options.plugins]);
2690
3578
  const config = { ...options, extensions };
2691
- const [runtime] = useState8(() => new ReaderRuntime(documentId ?? `limr-${generatedId.replace(/[^a-z\d_-]/gi, "")}`, source, streaming, config));
2692
- const previous = useRef11({ source, streaming, config });
3579
+ const [runtime] = useState9(() => new ReaderRuntime(documentId ?? `limr-${generatedId.replace(/[^a-z\d_-]/gi, "")}`, source, streaming, config));
3580
+ const previous = useRef14({ source, streaming, config });
2693
3581
  useIsomorphicLayoutEffect(() => {
2694
3582
  const old = previous.current;
2695
3583
  const changed = Object.keys({ ...old.config, ...config }).some((key) => old.config[key] !== config[key]);
@@ -2697,11 +3585,11 @@ function DocumentProvider({ children, source = "", streaming = false, documentId
2697
3585
  if (old.source !== source || old.streaming !== streaming) runtime.document.update(source, { streaming });
2698
3586
  previous.current = { source, streaming, config };
2699
3587
  });
2700
- useEffect8(() => {
3588
+ useEffect9(() => {
2701
3589
  runtime.start();
2702
3590
  return () => runtime.dispose();
2703
3591
  }, [runtime]);
2704
- return /* @__PURE__ */ jsx16(RuntimeContext.Provider, { value: runtime, children });
3592
+ return /* @__PURE__ */ jsx24(RuntimeContext.Provider, { value: runtime, children });
2705
3593
  }
2706
3594
  function useRuntime() {
2707
3595
  const runtime = useContext3(RuntimeContext);
@@ -2720,14 +3608,14 @@ function useRendererParameter(name) {
2720
3608
  });
2721
3609
  }
2722
3610
  function useStoreSelector(store, select) {
2723
- const selectRef = useRef11(select);
3611
+ const selectRef = useRef14(select);
2724
3612
  selectRef.current = select;
2725
- const cache2 = useRef11(void 0);
2726
- const get = useCallback2(() => {
3613
+ const cache3 = useRef14(void 0);
3614
+ const get = useCallback3(() => {
2727
3615
  const state = store.get();
2728
- if (cache2.current && cache2.current.state === state && cache2.current.select === selectRef.current) return cache2.current.value;
3616
+ if (cache3.current && cache3.current.state === state && cache3.current.select === selectRef.current) return cache3.current.value;
2729
3617
  const value = selectRef.current(state);
2730
- cache2.current = { state, select: selectRef.current, value };
3618
+ cache3.current = { state, select: selectRef.current, value };
2731
3619
  return value;
2732
3620
  }, [store]);
2733
3621
  return useSyncExternalStore(store.subscribe, get, get);
@@ -2738,7 +3626,7 @@ function useDocument() {
2738
3626
  }
2739
3627
  function useDocumentSelector(select) {
2740
3628
  const runtime = useRuntime();
2741
- const store = useMemo4(() => ({ subscribe: runtime.document.subscribe, get: runtime.document.getSnapshot }), [runtime]);
3629
+ const store = useMemo5(() => ({ subscribe: runtime.document.subscribe, get: runtime.document.getSnapshot }), [runtime]);
2742
3630
  return useStoreSelector(store, select);
2743
3631
  }
2744
3632
  function useSelection() {
@@ -2760,7 +3648,7 @@ function useAnnotation(id) {
2760
3648
  }
2761
3649
  function useAnnotations() {
2762
3650
  const state = useStoreSelector(useRuntime().decorations, (state2) => state2);
2763
- return useMemo4(() => ({ annotations: state.annotations, occurrences: state.occurrences }), [state.annotations, state.occurrences]);
3651
+ return useMemo5(() => ({ annotations: state.annotations, occurrences: state.occurrences }), [state.annotations, state.occurrences]);
2764
3652
  }
2765
3653
  function useAnnotationState() {
2766
3654
  const runtime = useRuntime();
@@ -2777,7 +3665,7 @@ function useAnchor(id) {
2777
3665
  }
2778
3666
  function useLiMR() {
2779
3667
  const runtime = useRuntime();
2780
- return useMemo4(() => ({
3668
+ return useMemo5(() => ({
2781
3669
  ...runtime.commands,
2782
3670
  document: runtime.document,
2783
3671
  get signal() {
@@ -2791,14 +3679,14 @@ function useLiMR() {
2791
3679
  }
2792
3680
 
2793
3681
  // src/components/CopyButton.tsx
2794
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
3682
+ import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
2795
3683
  function CopyButton({ value = "", onCopy, label, variant = "plain", className = "" }) {
2796
3684
  const locale = useLocale();
2797
- const [status, setStatus] = useState9("idle");
2798
- const timer = useRef12(void 0);
2799
- const mounted = useRef12(true);
3685
+ const [status, setStatus] = useState10("idle");
3686
+ const timer = useRef15(void 0);
3687
+ const mounted = useRef15(true);
2800
3688
  const title = status === "copied" ? locale.copied : status === "error" ? locale.copyFailed : label ?? locale.copy;
2801
- useEffect9(() => {
3689
+ useEffect10(() => {
2802
3690
  mounted.current = true;
2803
3691
  return () => {
2804
3692
  mounted.current = false;
@@ -2818,7 +3706,7 @@ function CopyButton({ value = "", onCopy, label, variant = "plain", className =
2818
3706
  clearTimeout(timer.current);
2819
3707
  timer.current = setTimeout(() => setStatus("idle"), 2200);
2820
3708
  };
2821
- return /* @__PURE__ */ jsxs13(
3709
+ return /* @__PURE__ */ jsxs17(
2822
3710
  "button",
2823
3711
  {
2824
3712
  type: "button",
@@ -2830,21 +3718,21 @@ function CopyButton({ value = "", onCopy, label, variant = "plain", className =
2830
3718
  title,
2831
3719
  onClick: copy,
2832
3720
  children: [
2833
- /* @__PURE__ */ jsxs13("svg", { className: "limr-copy-glyph", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
2834
- /* @__PURE__ */ jsxs13("g", { className: "limr-copy-sheets", children: [
2835
- /* @__PURE__ */ jsx17("rect", { x: "8.5", y: "8", width: "10.5", height: "12", rx: "3" }),
2836
- /* @__PURE__ */ jsx17("path", { d: "M15.5 8V6.8A2.8 2.8 0 0 0 12.7 4H7A3 3 0 0 0 4 7v6a3 3 0 0 0 3 3h1.5" })
3721
+ /* @__PURE__ */ jsxs17("svg", { className: "limr-copy-glyph", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
3722
+ /* @__PURE__ */ jsxs17("g", { className: "limr-copy-sheets", children: [
3723
+ /* @__PURE__ */ jsx25("rect", { x: "8.5", y: "8", width: "10.5", height: "12", rx: "3" }),
3724
+ /* @__PURE__ */ jsx25("path", { d: "M15.5 8V6.8A2.8 2.8 0 0 0 12.7 4H7A3 3 0 0 0 4 7v6a3 3 0 0 0 3 3h1.5" })
2837
3725
  ] }),
2838
- /* @__PURE__ */ jsx17("path", { className: "limr-copy-check", d: "m5.5 12 4.2 4.2 8.8-9", pathLength: "1" })
3726
+ /* @__PURE__ */ jsx25("path", { className: "limr-copy-check", d: "m5.5 12 4.2 4.2 8.8-9", pathLength: "1" })
2839
3727
  ] }),
2840
- /* @__PURE__ */ jsx17("span", { className: status === "error" ? "limr-copy-error" : "limr-sr-only", role: "status", children: status === "copied" ? locale.copied : status === "error" ? locale.copyFailed : "" })
3728
+ /* @__PURE__ */ jsx25("span", { className: status === "error" ? "limr-copy-error" : "limr-sr-only", role: "status", children: status === "copied" ? locale.copied : status === "error" ? locale.copyFailed : "" })
2841
3729
  ]
2842
3730
  }
2843
3731
  );
2844
3732
  }
2845
3733
 
2846
3734
  // src/extensions/math.tsx
2847
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
3735
+ import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
2848
3736
  var loaded;
2849
3737
  var loading;
2850
3738
  var load = () => loading ??= import("./math-renderer-L6DOH4RX.js").then((module) => {
@@ -2858,74 +3746,21 @@ async function preloadMath() {
2858
3746
  function math(options = {}) {
2859
3747
  function MathNode({ node }) {
2860
3748
  const display = node.type === "math";
2861
- const fallback = /* @__PURE__ */ jsx18("code", { className: "limr-math-source", children: node.value });
3749
+ const fallback = /* @__PURE__ */ jsx26("code", { className: "limr-math-source", children: node.value });
2862
3750
  const Renderer = loaded ?? MathContent;
2863
- const content = /* @__PURE__ */ jsx18(Suspense, { fallback, children: /* @__PURE__ */ jsx18(Renderer, { value: node.value ?? "", display, options }) });
2864
- return display ? /* @__PURE__ */ jsxs14("div", { className: "limr-math-block", children: [
3751
+ const content = /* @__PURE__ */ jsx26(Suspense, { fallback, children: /* @__PURE__ */ jsx26(Renderer, { value: node.value ?? "", display, options }) });
3752
+ return display ? /* @__PURE__ */ jsxs18("div", { className: "limr-math-block", children: [
2865
3753
  content,
2866
- /* @__PURE__ */ jsx18(CopyButton, { className: "limr-math-copy", variant: "glass", label: "Copy LaTeX", value: `$$
3754
+ /* @__PURE__ */ jsx26(CopyButton, { className: "limr-math-copy", variant: "glass", label: "Copy LaTeX", value: `$$
2867
3755
  ${node.value ?? ""}
2868
3756
  $$` })
2869
3757
  ] }) : content;
2870
3758
  }
2871
- return { name: "math", micromark: [syntax2()], mdast: [mathFromMarkdown()], scope: "local", components: { math: MathNode, inlineMath: MathNode } };
2872
- }
2873
-
2874
- // src/components/ToolIcon.tsx
2875
- import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
2876
- function ToolIcon({ name, ...props }) {
2877
- return /* @__PURE__ */ jsx19("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", ...props, children: /* @__PURE__ */ jsxs15("g", { transform: name === "highlight" ? "translate(0 -1.25)" : name === "comment" ? "translate(0 -.5)" : void 0, children: [
2878
- (name === "eye" || name === "eye-off") && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2879
- /* @__PURE__ */ jsx19("path", { d: "M3 12s3.3-6 9-6 9 6 9 6-3.3 6-9 6-9-6-9-6Z" }),
2880
- /* @__PURE__ */ jsx19("circle", { cx: "12", cy: "12", r: "2.7" }),
2881
- name === "eye-off" && /* @__PURE__ */ jsx19("path", { d: "m4 4 16 16" })
2882
- ] }),
2883
- name === "question" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2884
- /* @__PURE__ */ jsx19("circle", { cx: "12", cy: "12", r: "8.5" }),
2885
- /* @__PURE__ */ jsx19("path", { d: "M9.4 9a2.7 2.7 0 0 1 5.2 1c0 1.8-2.6 2-2.6 3.7M12 17h.01" })
2886
- ] }),
2887
- name === "highlight" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2888
- /* @__PURE__ */ jsx19("path", { d: "m8 15 7.6-9a2 2 0 0 1 2.8-.2l.8.7a2 2 0 0 1 .2 2.8L12 18l-4-3Z" }),
2889
- /* @__PURE__ */ jsx19("path", { d: "m8 15-2 3 3 1 3-1M4 21h10M14 8l4 3.5" })
2890
- ] }),
2891
- name === "edit" && /* @__PURE__ */ jsx19(Fragment6, { children: /* @__PURE__ */ jsx19("path", { d: "m14.5 5.5 4 4M4.5 19.5l4.5-1 11-11a2.1 2.1 0 0 0 0-3 2.1 2.1 0 0 0-3 0l-11 11-1.5 4Z" }) }),
2892
- name === "close" && /* @__PURE__ */ jsx19("path", { d: "m6 6 12 12M18 6 6 18" }),
2893
- name === "copy" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2894
- /* @__PURE__ */ jsx19("rect", { x: "8.5", y: "8", width: "10.5", height: "12", rx: "3" }),
2895
- /* @__PURE__ */ jsx19("path", { d: "M15.5 8V6.8A2.8 2.8 0 0 0 12.7 4H7A3 3 0 0 0 4 7v6a3 3 0 0 0 3 3h1.5" })
2896
- ] }),
2897
- ["background", "underline", "dotted", "wavy"].includes(name) && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2898
- name === "background" && /* @__PURE__ */ jsx19("rect", { x: "3", y: "3", width: "18", height: "18", rx: "5", fill: "currentColor", fillOpacity: ".13", stroke: "none" }),
2899
- /* @__PURE__ */ jsx19("path", { d: "m8 15 3-8a1.05 1.05 0 0 1 2 0l3 8M9.2 12h5.6" }),
2900
- name === "background" && /* @__PURE__ */ jsx19("path", { d: "M7 19h10", strokeWidth: "2.5" }),
2901
- name === "underline" && /* @__PURE__ */ jsx19("path", { d: "M5 20h14" }),
2902
- name === "dotted" && /* @__PURE__ */ jsx19("path", { d: "M5 20h14", strokeDasharray: ".1 3.4", strokeWidth: "2.4" }),
2903
- name === "wavy" && /* @__PURE__ */ jsx19("path", { d: "M4 20c1.3-2 2.7-2 4 0s2.7 2 4 0 2.7-2 4 0 2.7 2 4 0" })
2904
- ] }),
2905
- name === "chevron" && /* @__PURE__ */ jsx19("path", { d: "m8 10 4 4 4-4" }),
2906
- name === "check" && /* @__PURE__ */ jsx19("path", { d: "m5.5 12 4.2 4.2 8.8-9" }),
2907
- name === "trash" && /* @__PURE__ */ jsx19(Fragment6, { children: /* @__PURE__ */ jsx19("path", { d: "M4.5 7h15M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M7 7l.7 12a2 2 0 0 0 2 2h4.6a2 2 0 0 0 2-2L17 7M10 11v6M14 11v6" }) }),
2908
- name === "note" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2909
- /* @__PURE__ */ jsx19("path", { d: "M14 3H7a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V9l-6-6Z" }),
2910
- /* @__PURE__ */ jsx19("path", { d: "M14 3v4a2 2 0 0 0 2 2h4M8 13h8M8 17h5" })
2911
- ] }),
2912
- name === "comment" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2913
- /* @__PURE__ */ jsx19("path", { d: "M8 19 4 21V7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v9a3 3 0 0 1-3 3H8Z" }),
2914
- /* @__PURE__ */ jsx19("path", { d: "M8 9h8M8 13h5" })
2915
- ] }),
2916
- name === "footnote" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2917
- /* @__PURE__ */ jsx19("path", { d: "M5 7h9M5 11h12M5 15h8M5 20h14M18 4h2v5" }),
2918
- /* @__PURE__ */ jsx19("path", { d: "M17.5 9h4" })
2919
- ] }),
2920
- name === "sparkles" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2921
- /* @__PURE__ */ jsx19("path", { d: "m10.5 4 1.8 4.8 4.7 1.7-4.7 1.8-1.8 4.7-1.7-4.7L4 10.5l4.8-1.7L10.5 4Z" }),
2922
- /* @__PURE__ */ jsx19("path", { d: "m19 15 .8 2.2L22 18l-2.2.8L19 21l-.8-2.2L16 18l2.2-.8L19 15ZM19 3v4M17 5h4" })
2923
- ] })
2924
- ] }) });
3759
+ return { name: "math", directives: ["math"], literal: true, micromark: [syntax2()], mdast: [mathFromMarkdown()], scope: "local", components: { math: MathNode, inlineMath: MathNode }, transform: (root, context) => literalDirective(root, context.source, "math") };
2925
3760
  }
2926
3761
 
2927
3762
  // src/extensions/selection-actions.tsx
2928
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
3763
+ import { jsx as jsx27, jsxs as jsxs19 } from "react/jsx-runtime";
2929
3764
  var highlightColors = [
2930
3765
  { color: "#E57373", label: "Rose", labelZh: "\u73AB\u7470" },
2931
3766
  { color: "#F2C744", label: "Gold", labelZh: "\u6696\u9EC4" },
@@ -2939,9 +3774,9 @@ function copyText(options = {}) {
2939
3774
  return {
2940
3775
  id: `copy-${options.format ?? "text"}`,
2941
3776
  label: options.label ?? ((context) => context.locale.copy),
2942
- icon: /* @__PURE__ */ jsx20(ToolIcon, { name: "copy" }),
3777
+ icon: /* @__PURE__ */ jsx27(ToolIcon, { name: "copy" }),
2943
3778
  successLabel: (context) => context.locale.copied,
2944
- successIcon: /* @__PURE__ */ jsx20(ToolIcon, { name: "check" }),
3779
+ successIcon: /* @__PURE__ */ jsx27(ToolIcon, { name: "check" }),
2945
3780
  run: async ({ selection }) => {
2946
3781
  const format = options.format ?? "text";
2947
3782
  await navigator.clipboard.writeText(format === "text" ? selection.text : selection[format].sourceText);
@@ -2963,17 +3798,17 @@ function HighlightSettings({ context, options }) {
2963
3798
  context.setHighlightAppearance(appearance);
2964
3799
  if (highlight) void context.run((current) => current.updateAnnotation(highlight.id, { data: appearance }));
2965
3800
  };
2966
- return /* @__PURE__ */ jsxs16("div", { className: "limr-highlight-settings", children: [
2967
- /* @__PURE__ */ jsxs16("fieldset", { disabled: context.busy, children: [
2968
- /* @__PURE__ */ jsx20("legend", { children: locale.highlightStyle }),
2969
- /* @__PURE__ */ jsx20("div", { className: "limr-highlight-styles", children: styles.map(([value, label]) => /* @__PURE__ */ jsxs16("button", { type: "button", "aria-pressed": style === value, onClick: () => change(color, value), children: [
2970
- /* @__PURE__ */ jsx20(ToolIcon, { name: value }),
2971
- /* @__PURE__ */ jsx20("span", { className: "limr-underline", children: label })
3801
+ return /* @__PURE__ */ jsxs19("div", { className: "limr-highlight-settings", children: [
3802
+ /* @__PURE__ */ jsxs19("fieldset", { disabled: context.busy, children: [
3803
+ /* @__PURE__ */ jsx27("legend", { children: locale.highlightStyle }),
3804
+ /* @__PURE__ */ jsx27("div", { className: "limr-highlight-styles", children: styles.map(([value, label]) => /* @__PURE__ */ jsxs19("button", { type: "button", "aria-pressed": style === value, onClick: () => change(color, value), children: [
3805
+ /* @__PURE__ */ jsx27(ToolIcon, { name: value }),
3806
+ /* @__PURE__ */ jsx27("span", { className: "limr-highlight-style-label", children: /* @__PURE__ */ jsx27("span", { className: "limr-underline", children: label }) })
2972
3807
  ] }, value)) })
2973
3808
  ] }),
2974
- /* @__PURE__ */ jsxs16("fieldset", { disabled: context.busy, children: [
2975
- /* @__PURE__ */ jsx20("legend", { children: locale.highlightColor }),
2976
- /* @__PURE__ */ jsx20("div", { className: "limr-highlight-colors", children: (options.colors ?? highlightColors).map((item) => /* @__PURE__ */ jsx20("button", { type: "button", className: "limr-color-swatch", "aria-label": locale.highlight === "\u9AD8\u4EAE" ? item.labelZh ?? item.label : item.label, title: locale.highlight === "\u9AD8\u4EAE" ? item.labelZh ?? item.label : item.label, "aria-pressed": color.toLowerCase() === item.color.toLowerCase(), style: { "--swatch": item.color }, onClick: () => change(item.color, style), children: /* @__PURE__ */ jsx20(ToolIcon, { name: "check" }) }, item.color)) })
3809
+ /* @__PURE__ */ jsxs19("fieldset", { disabled: context.busy, children: [
3810
+ /* @__PURE__ */ jsx27("legend", { children: locale.highlightColor }),
3811
+ /* @__PURE__ */ jsx27("div", { className: "limr-highlight-colors", children: (options.colors ?? highlightColors).map((item) => /* @__PURE__ */ jsx27("button", { type: "button", className: "limr-color-swatch", "aria-label": locale.highlight === "\u9AD8\u4EAE" ? item.labelZh ?? item.label : item.label, title: locale.highlight === "\u9AD8\u4EAE" ? item.labelZh ?? item.label : item.label, "aria-pressed": color.toLowerCase() === item.color.toLowerCase(), style: { "--swatch": item.color }, onClick: () => change(item.color, style), children: /* @__PURE__ */ jsx27(ToolIcon, { name: "check" }) }, item.color)) })
2977
3812
  ] })
2978
3813
  ] });
2979
3814
  }
@@ -2981,17 +3816,17 @@ function highlightPalette(options = {}) {
2981
3816
  return {
2982
3817
  id: "highlight-palette",
2983
3818
  label: (context) => context.locale.editHighlight,
2984
- icon: /* @__PURE__ */ jsx20(ToolIcon, { name: "background" }),
3819
+ icon: /* @__PURE__ */ jsx27(ToolIcon, { name: "background" }),
2985
3820
  placement: "start",
2986
- render: (context) => /* @__PURE__ */ jsx20("button", { type: "button", className: "limr-toolbar-swatch", "aria-label": context.locale.editHighlight, "aria-expanded": context.expanded, disabled: context.busy, onClick: () => context.expanded ? context.collapse() : context.expand(), style: { "--swatch": context.highlightAppearance.color ?? defaultHighlight.color } }),
2987
- renderExpanded: (context) => /* @__PURE__ */ jsx20(HighlightSettings, { context, options })
3821
+ render: (context) => /* @__PURE__ */ jsx27("button", { type: "button", className: "limr-toolbar-swatch", "aria-label": context.locale.editHighlight, "aria-expanded": context.expanded, disabled: context.busy, onClick: () => context.expanded ? context.collapse() : context.expand(), style: { "--swatch": context.highlightAppearance.color ?? defaultHighlight.color } }),
3822
+ renderExpanded: (context) => /* @__PURE__ */ jsx27(HighlightSettings, { context, options })
2988
3823
  };
2989
3824
  }
2990
3825
  function highlightSelection() {
2991
3826
  return {
2992
3827
  id: "highlight",
2993
3828
  label: (context) => !context.highlight ? context.locale.addHighlight : context.highlight.data?.hidden ? context.locale.showHighlight : context.locale.hideHighlight,
2994
- icon: (context) => /* @__PURE__ */ jsx20(ToolIcon, { name: !context.highlight ? "highlight" : context.highlight.data?.hidden ? "eye" : "eye-off" }),
3829
+ icon: (context) => /* @__PURE__ */ jsx27(ToolIcon, { name: !context.highlight ? "highlight" : context.highlight.data?.hidden ? "eye" : "eye-off" }),
2995
3830
  visible: (context) => !context.highlight || context.annotations.some((annotation) => annotation.type !== "highlight"),
2996
3831
  run: (context) => {
2997
3832
  if (context.highlight) context.updateAnnotation(context.highlight.id, { data: { ...context.highlight.data, hidden: !context.highlight.data?.hidden } });
@@ -3006,7 +3841,7 @@ function deleteAnnotations() {
3006
3841
  return {
3007
3842
  id: "delete-annotations",
3008
3843
  label: (context) => context.locale.deleteAnnotations,
3009
- icon: /* @__PURE__ */ jsx20(ToolIcon, { name: "trash" }),
3844
+ icon: /* @__PURE__ */ jsx27(ToolIcon, { name: "trash" }),
3010
3845
  placement: "end",
3011
3846
  tone: "danger",
3012
3847
  visible: (context) => context.annotations.length > 0,
@@ -3026,7 +3861,6 @@ export {
3026
3861
  preloadMath,
3027
3862
  math,
3028
3863
  codeHighlight,
3029
- sourceRangeStartRect,
3030
3864
  Markdown,
3031
3865
  OverlayScrollArea,
3032
3866
  AnnotationCard,
@@ -3035,9 +3869,15 @@ export {
3035
3869
  InlineMarker,
3036
3870
  Collapse,
3037
3871
  translations,
3872
+ ToolIcon,
3873
+ mermaid,
3038
3874
  defineBlock,
3039
3875
  defineAnnotation,
3040
3876
  defineExtension,
3877
+ calloutType,
3878
+ callout,
3879
+ inlineHighlights,
3880
+ footnotes,
3041
3881
  video,
3042
3882
  figures,
3043
3883
  definition,
@@ -3060,11 +3900,10 @@ export {
3060
3900
  useAnnotationState,
3061
3901
  useAnchor,
3062
3902
  useLiMR,
3063
- ToolIcon,
3064
3903
  highlightColors,
3065
3904
  copyText,
3066
3905
  highlightPalette,
3067
3906
  highlightSelection,
3068
3907
  deleteAnnotations
3069
3908
  };
3070
- //# sourceMappingURL=chunk-65V5OZ4G.js.map
3909
+ //# sourceMappingURL=chunk-SUAAUGAX.js.map