@linloir/limr 1.0.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 (64) hide show
  1. package/README.en.md +36 -8
  2. package/README.md +36 -8
  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 +83 -3
  10. package/dist/{chunk-TTISGBST.js → chunk-BG5QUQFR.js} +327 -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-N77VD2YR.js → chunk-GU2RPESF.js} +145 -83
  18. package/dist/chunk-GU2RPESF.js.map +1 -0
  19. package/dist/{chunk-3ILUZKDB.js → chunk-SUAAUGAX.js} +1280 -501
  20. package/dist/chunk-SUAAUGAX.js.map +1 -0
  21. package/dist/core/index.d.ts +2 -2
  22. package/dist/core/index.js +13 -5
  23. package/dist/extensions/index.d.ts +30 -5
  24. package/dist/extensions/index.js +15 -3
  25. package/dist/index.d.ts +3 -3
  26. package/dist/index.js +27 -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 +5 -4
  31. package/dist/react/index.js +5 -3
  32. package/dist/styles.css +83 -3
  33. package/dist/{translations-DHnYMg4h.d.ts → translations-BXzSwLBJ.d.ts} +39 -2
  34. package/docs/en/README.md +8 -6
  35. package/docs/en/annotations.md +3 -1
  36. package/docs/en/api.md +9 -1
  37. package/docs/en/architecture.md +11 -3
  38. package/docs/en/extensions.md +53 -4
  39. package/docs/en/getting-started.md +2 -0
  40. package/docs/en/integration.md +26 -2
  41. package/docs/en/modules.md +9 -5
  42. package/docs/en/performance.md +42 -2
  43. package/docs/en/styles.md +21 -2
  44. package/docs/en/syntax.md +118 -15
  45. package/docs/zh-CN/README.md +8 -6
  46. package/docs/zh-CN/annotations.md +3 -1
  47. package/docs/zh-CN/api.md +9 -1
  48. package/docs/zh-CN/architecture.md +11 -3
  49. package/docs/zh-CN/extensions.md +55 -4
  50. package/docs/zh-CN/getting-started.md +2 -0
  51. package/docs/zh-CN/integration.md +26 -2
  52. package/docs/zh-CN/modules.md +9 -5
  53. package/docs/zh-CN/performance.md +42 -2
  54. package/docs/zh-CN/styles.md +21 -2
  55. package/docs/zh-CN/syntax.md +120 -15
  56. package/package.json +9 -1
  57. package/skills/limr-plugin-development/references/contract.md +9 -3
  58. package/skills/limr-use/references/integration.md +11 -3
  59. package/skills/limr-use/references/syntax.md +8 -5
  60. package/dist/chunk-3ILUZKDB.js.map +0 -1
  61. package/dist/chunk-N77VD2YR.js.map +0 -1
  62. package/dist/chunk-TTISGBST.js.map +0 -1
  63. package/dist/chunk-V2OG4Y3F.js +0 -1
  64. /package/dist/{chunk-V2OG4Y3F.js.map → chunk-FSI5OTBW.js.map} +0 -0
@@ -2,16 +2,24 @@ import {
2
2
  annotationReferenceRange,
3
3
  createDocument,
4
4
  defaultHighlight,
5
- intersects,
6
5
  mapText,
7
- rangeOf,
8
6
  resolveAnnotationAnchors,
9
- safeUrl,
10
7
  sameRange,
11
- 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,
12
20
  textContent,
13
21
  walk
14
- } from "./chunk-TTISGBST.js";
22
+ } from "./chunk-FJZCQY2S.js";
15
23
  import {
16
24
  ResourceCache
17
25
  } from "./chunk-6N6TFHEN.js";
@@ -94,6 +102,7 @@ var english = {
94
102
  highlightWavy: "Wavy",
95
103
  hideHighlight: "Hide highlight",
96
104
  showHighlight: "Show highlight",
105
+ inlineHighlightHint: "Markdown inline highlight, editing unavailable",
97
106
  deleteAnnotations: "Delete",
98
107
  deleteAnnotationsHint: "Deletes {count} annotations on this text and any highlight."
99
108
  };
@@ -135,6 +144,7 @@ var chinese = {
135
144
  highlightWavy: "\u6CE2\u6D6A\u7EBF",
136
145
  hideHighlight: "\u9690\u85CF\u9AD8\u4EAE",
137
146
  showHighlight: "\u663E\u793A\u9AD8\u4EAE",
147
+ inlineHighlightHint: "Markdown \u5185\u5D4C\u9AD8\u4EAE\uFF0C\u4E0D\u652F\u6301\u7F16\u8F91",
138
148
  deleteAnnotations: "\u5220\u9664",
139
149
  deleteAnnotationsHint: "\u5C06\u5220\u9664\u8FD9\u6BB5\u6587\u5B57\u5173\u8054\u7684 {count} \u4E2A\u6CE8\u91CA\u53CA\u9AD8\u4EAE\u3002"
140
150
  };
@@ -175,6 +185,7 @@ var ReaderRuntime = class {
175
185
  bindings = /* @__PURE__ */ new WeakMap();
176
186
  textBinders = /* @__PURE__ */ new WeakMap();
177
187
  components = {};
188
+ rendererOverrides = /* @__PURE__ */ new Map();
178
189
  annotationTypes = /* @__PURE__ */ new Map();
179
190
  resolvedTypes = /* @__PURE__ */ new WeakMap();
180
191
  locale = english;
@@ -278,6 +289,13 @@ var ReaderRuntime = class {
278
289
  installConfiguration(config) {
279
290
  this.locale = { ...english, ...config.locale };
280
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]);
281
299
  const annotationTypes = [...(config.extensions ?? []).flatMap((ext) => [...ext.annotationTypes ?? []]), ...config.annotationTypes ?? []];
282
300
  this.annotationTypes = new Map(annotationTypes.map((type) => {
283
301
  if (typeof type.label === "string") return [type.type, type];
@@ -352,13 +370,13 @@ var ReaderRuntime = class {
352
370
  if ([...versions.keys()].some((id) => !annotations.has(id))) this.resourceVersions.set(new Map([...versions].filter(([id]) => annotations.has(id))));
353
371
  const occurrences = [...doc.occurrences];
354
372
  const allHighlights = [];
355
- for (const annotation of config.annotations ?? this.localAnnotations) {
373
+ for (const annotation of annotations.values()) {
356
374
  resolveAnnotationAnchors(annotation, doc).forEach((anchor, index) => {
357
375
  if (annotation.type === "highlight") {
358
376
  const id = index ? `${annotation.id}:${index}` : annotation.id;
359
377
  const value = { ...annotation.data, id, annotationId: annotation.id, range: anchor.range };
360
378
  const old = previous.highlights.get(id);
361
- allHighlights.push(old && !old.occurrenceId && 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);
362
380
  } else occurrences.push({ ...anchor, id: anchor.id ?? `${doc.id}-${annotation.id}-${index}`, annotationIds: [annotation.id] });
363
381
  });
364
382
  }
@@ -539,7 +557,7 @@ var emptyList = EMPTY;
539
557
  import { gfm as syntax } from "micromark-extension-gfm";
540
558
  import { gfmFromMarkdown } from "mdast-util-gfm";
541
559
  function gfm() {
542
- 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 } };
543
561
  }
544
562
 
545
563
  // src/extensions/math.tsx
@@ -548,10 +566,10 @@ import { math as syntax2 } from "micromark-extension-math";
548
566
  import { mathFromMarkdown } from "mdast-util-math";
549
567
 
550
568
  // src/components/CopyButton.tsx
551
- 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";
552
570
 
553
571
  // src/react/context.tsx
554
- 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";
555
573
 
556
574
  // src/extensions/code.tsx
557
575
  import { useEffect, useRef, useState } from "react";
@@ -563,6 +581,16 @@ function literalValue(node) {
563
581
  function mapLiteral(source, node) {
564
582
  const range = rangeOf(node), raw = source.slice(range.start, range.end), value = literalValue(node);
565
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
+ }
566
594
  if (node.type === "inlineCode") {
567
595
  const fence = /^`+/.exec(raw)?.[0].length ?? 0;
568
596
  let from = fence, until = raw.length - fence;
@@ -602,6 +630,24 @@ function mapLiteral(source, node) {
602
630
  return { starts, ends, exact };
603
631
  }
604
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
+
605
651
  // src/extensions/code.tsx
606
652
  import { jsx, jsxs } from "react/jsx-runtime";
607
653
  var cache = new ResourceCache(96);
@@ -631,9 +677,10 @@ function codeHighlight(options = {}) {
631
677
  return () => observer.disconnect();
632
678
  }, [visible, runtime]);
633
679
  useEffect(() => {
634
- if (!visible || code.length > (options.maxLength ?? 1e5)) return;
680
+ if (!visible || pending || code.length > (options.maxLength ?? 1e5)) return;
635
681
  let disposed = false;
636
682
  let release;
683
+ let waiting;
637
684
  const timer = setTimeout(() => {
638
685
  if (normalized === "text" || options.languages && !options.languages.includes(normalized)) return;
639
686
  const request = cache.acquire(JSON.stringify([code, normalized, options.lightTheme, options.darkTheme]), async () => {
@@ -655,14 +702,31 @@ function codeHighlight(options = {}) {
655
702
  });
656
703
  release = request.release;
657
704
  request.promise.then((tokens2) => {
658
- 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();
659
722
  }).catch(() => {
660
723
  });
661
- }, pending ? 180 : 0);
724
+ }, 0);
662
725
  return () => {
663
726
  disposed = true;
664
727
  clearTimeout(timer);
665
728
  release?.();
729
+ if (waiting) document.removeEventListener("selectionchange", waiting);
666
730
  };
667
731
  }, [code, normalized, pending, visible]);
668
732
  const tokens = result?.code === code ? result.tokens : void 0;
@@ -674,29 +738,29 @@ function codeHighlight(options = {}) {
674
738
  /* @__PURE__ */ jsx("pre", { className: tokens?.length ? "shiki" : void 0, children: /* @__PURE__ */ jsx("code", { children: renderValue(tokens) }) })
675
739
  ] });
676
740
  }
677
- 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"]) };
678
742
  }
679
743
 
680
744
  // src/extensions/annotations.tsx
681
- import { useMemo as useMemo2, useRef as useRef6 } from "react";
745
+ import { useMemo as useMemo2, useRef as useRef8 } from "react";
682
746
  import { fromMarkdown } from "mdast-util-from-markdown";
683
747
 
684
748
  // src/react/Markdown.tsx
685
- import { useContext, useEffect as useEffect4, useRef as useRef4 } from "react";
749
+ import { useContext, useEffect as useEffect5, useRef as useRef6 } from "react";
686
750
 
687
751
  // src/react/render.tsx
688
- 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";
689
753
 
690
754
  // src/react/text-binding.ts
691
755
  var atomic = /* @__PURE__ */ new Set(["inlineCode", "code", "math", "inlineMath", "image", "imageReference", "html"]);
692
756
  var hidden = /* @__PURE__ */ new Set(["definition", "footnoteDefinition", "annotationDefinition", "footnoteReference"]);
693
757
  var ordinary = /* @__PURE__ */ new Set(["text", "paragraph", "heading", "strong", "emphasis", "delete", "blockquote", "list", "listItem", "link", "linkReference", "table", "tableRow", "tableCell", "thematicBreak", "break", "annotationReference"]);
694
- function compactTextNodes(node, components) {
758
+ function compactTextNodes(node, components, overrides) {
695
759
  const texts = [];
696
760
  let supported = true;
697
761
  const visit = (node2) => {
698
762
  if (node2.selection === "atomic" || atomic.has(node2.type) || hidden.has(node2.type)) return;
699
- if (!ordinary.has(node2.type) || components[node2.type]) {
763
+ if (!ordinary.has(node2.type) || components[node2.type] || overrides?.has(node2.type)) {
700
764
  supported = false;
701
765
  return;
702
766
  }
@@ -778,8 +842,182 @@ function bindCompactText(element, nodes, runtime) {
778
842
  return clear;
779
843
  }
780
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
+
781
1019
  // src/react/render.tsx
782
- 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";
783
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 } : {};
784
1022
  function AnnotationMarker({ occurrence }) {
785
1023
  const runtime = useRuntime();
@@ -787,10 +1025,10 @@ function AnnotationMarker({ occurrence }) {
787
1025
  const active = useStoreSelector(runtime.interaction, (state) => state.active === occurrence.id);
788
1026
  const hovered = useStoreSelector(runtime.interaction, (state) => state.hovered === occurrence.id);
789
1027
  const definition2 = useStoreSelector(runtime.configuration, () => runtime.annotationTypes.get(annotation?.type ?? "citation"));
790
- const Icon = definition2?.icon;
1028
+ const Icon2 = definition2?.icon;
791
1029
  const Custom = definition2?.Marker;
792
- const icon = typeof Icon === "function" || Icon && typeof Icon === "object" && !isValidElement(Icon) && "$$typeof" in Icon ? createElement(Icon) : Icon;
793
- 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(
794
1032
  "button",
795
1033
  {
796
1034
  type: "button",
@@ -820,8 +1058,8 @@ function AnnotationMarker({ occurrence }) {
820
1058
  if (hovering) runtime.hover(occurrence.id);
821
1059
  },
822
1060
  children: [
823
- /* @__PURE__ */ jsx2("span", { className: "limr-mark-icon", children: Custom && annotation ? /* @__PURE__ */ jsx2(Custom, { annotation, active }) : icon ?? "\u2022" }),
824
- 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 })
825
1063
  ]
826
1064
  }
827
1065
  ) });
@@ -835,7 +1073,7 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
835
1073
  let mapping2;
836
1074
  const raw = source.slice(range.start, range.end);
837
1075
  return () => {
838
- if (literalMapping) return literalMapping;
1076
+ if (literalMapping) return typeof literalMapping === "function" ? literalMapping() : literalMapping;
839
1077
  if (!mapping2) {
840
1078
  const relative = mapText(raw, value, { start: 0, end: raw.length }, { escapes: node.verbatimEscapes !== true });
841
1079
  mapping2 = { starts: relative.starts.map((offset) => offset + range.start), ends: relative.ends.map((offset) => offset + range.start), exact: relative.exact };
@@ -847,7 +1085,7 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
847
1085
  const highlights = context.highlights.filter((highlight) => intersects(highlight.range, range));
848
1086
  if (!marks.length && !highlights.length) {
849
1087
  const direct = !literalMapping && source.slice(range.start, range.end) === value;
850
- 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) => {
851
1089
  if (element) runtime.bindings.set(element, {
852
1090
  range,
853
1091
  atomic: false,
@@ -856,7 +1094,7 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
856
1094
  },
857
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
858
1096
  });
859
- }, children: value });
1097
+ }, children: context.live ? /* @__PURE__ */ jsx4(StreamText, { value, animateInitial: range.end > context.revealFrom }) : value });
860
1098
  }
861
1099
  const mapping = mapped();
862
1100
  const markerAt = /* @__PURE__ */ new Map();
@@ -874,12 +1112,12 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
874
1112
  boundaries.add(end < 0 ? value.length : end);
875
1113
  }
876
1114
  const points = [...boundaries].sort((a, b) => a - b);
877
- return /* @__PURE__ */ jsx2(Fragment2, { children: points.slice(1).map((end, index) => {
1115
+ return /* @__PURE__ */ jsx4(Fragment2, { children: points.slice(1).map((end, index) => {
878
1116
  const start = points[index];
879
1117
  const segmentRange = { start: mapping.starts[start] ?? range.start, end: mapping.ends[end - 1] ?? range.end };
880
1118
  const styles = highlights.filter((highlight) => intersects(highlight.range, segmentRange));
881
- let text = value.slice(start, end);
882
- 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);
883
1121
  return /* @__PURE__ */ jsxs2("span", { style, className, ref: (element) => {
884
1122
  if (element) runtime.bindings.set(element, { range: segmentRange, atomic: false, exact: mapping.exact, resolve: (offset, edge) => {
885
1123
  const at = Math.max(start, Math.min(end, start + offset));
@@ -887,22 +1125,27 @@ function Text({ node, context, mapping: literalMapping, marks: literalMarks, sty
887
1125
  } });
888
1126
  }, "data-limr-text": "", "data-source-start": segmentRange.start, "data-source-end": segmentRange.end, children: [
889
1127
  text,
890
- markerAt.get(end)?.map((occurrence) => /* @__PURE__ */ jsx2(AnnotationMarker, { occurrence }, occurrence.id))
1128
+ markerAt.get(end)?.map((occurrence) => /* @__PURE__ */ jsx4(AnnotationMarker, { occurrence }, occurrence.id))
891
1129
  ] }, start);
892
1130
  }) });
893
1131
  }
894
1132
  function LiteralValue({ node, context, tokens }) {
895
1133
  const source = context.runtime.document.getSnapshot().source;
896
- const mapping = useMemo(() => mapLiteral(source, node), [node]);
1134
+ const mapped = useMemo(() => {
1135
+ let value2;
1136
+ return () => value2 ??= mapLiteral(source, node);
1137
+ }, [node]);
897
1138
  const value = literalValue(node);
898
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();
899
1142
  const segments = tokens?.length ? tokens : [{ content: value, offset: 0 }];
900
- return /* @__PURE__ */ jsx2(Fragment2, { children: segments.map((token, index) => {
1143
+ return /* @__PURE__ */ jsx4(Fragment2, { children: segments.map((token, index) => {
901
1144
  const start = token.offset, end = start + token.content.length;
902
- const mapped = { starts: mapping.starts.slice(start, end), ends: mapping.ends.slice(start, end), exact: mapping.exact };
903
- 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 };
904
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 } } };
905
- 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);
906
1149
  }) });
907
1150
  }
908
1151
  function Atomic({ node, context, children, inline, blockElement }) {
@@ -913,7 +1156,7 @@ function Atomic({ node, context, children, inline, blockElement }) {
913
1156
  const image = ["image", "imageReference"].includes(node.type) || node.type === "component" && node.name === "img";
914
1157
  const corner = image || !inline;
915
1158
  const marks = [...context.marks.get(node) ?? [], ...corner ? context.afterMarks.get(node) ?? [] : []];
916
- const markers = marks.map((occurrence) => /* @__PURE__ */ jsx2(AnnotationMarker, { occurrence }, occurrence.id));
1159
+ const markers = marks.map((occurrence) => /* @__PURE__ */ jsx4(AnnotationMarker, { occurrence }, occurrence.id));
917
1160
  return /* @__PURE__ */ jsxs2(Fragment2, { children: [
918
1161
  /* @__PURE__ */ jsxs2(
919
1162
  Tag,
@@ -932,7 +1175,7 @@ function Atomic({ node, context, children, inline, blockElement }) {
932
1175
  },
933
1176
  children: [
934
1177
  children,
935
- 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 })
936
1179
  ]
937
1180
  }
938
1181
  ),
@@ -958,16 +1201,16 @@ function HeadingNode({ node, children, runtime, blockElement }) {
958
1201
  }
959
1202
  function Reference({ node, children, image }) {
960
1203
  const definition2 = useDocumentSelector((doc) => doc.definitions.get(String(node.identifier)));
961
- if (!definition2) return /* @__PURE__ */ jsx2(Fragment2, { children: children || String(node.alt ?? node.label ?? "") });
962
- 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 });
963
1206
  }
964
1207
  function FootnoteReference({ node }) {
965
1208
  const runtime = useRuntime();
966
1209
  const identifier2 = String(node.identifier);
967
1210
  const number = useDocumentSelector((doc) => doc.footnoteOrder.indexOf(identifier2) + 1);
968
- 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 }) });
969
1212
  }
970
- var ExtensionBoundary = class extends Component {
1213
+ var ExtensionBoundary = class extends Component2 {
971
1214
  state = { failed: false };
972
1215
  static getDerivedStateFromError() {
973
1216
  return { failed: true };
@@ -978,7 +1221,7 @@ var ExtensionBoundary = class extends Component {
978
1221
  if (this.state.failed && previous.fallback !== this.props.fallback) this.setState({ failed: false });
979
1222
  }
980
1223
  render() {
981
- 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;
982
1225
  }
983
1226
  };
984
1227
  function containsBlock(node) {
@@ -986,20 +1229,26 @@ function containsBlock(node) {
986
1229
  }
987
1230
  function renderNode(node, context, index = 0, trailing) {
988
1231
  const { runtime } = context;
989
- 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;
990
1234
  const blockElement = node === context.block.node ? context.blockElement : void 0;
991
1235
  const key = `${node.type}-${node.position?.start.offset ?? index}-${index}`;
992
- 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] };
993
1238
  const renderChildren = (nodes, options) => {
994
1239
  let last = -1;
995
1240
  nodes.forEach((child, i) => {
996
1241
  if (!["definition", "footnoteDefinition", "annotationDefinition"].includes(child.type)) last = i;
997
1242
  });
998
- 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")];
999
1244
  return nodes.map((child, i) => renderNode(child, childContext, i, i === last ? options?.trailing : void 0));
1000
1245
  };
1001
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 ?? []);
1002
- 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]);
1003
1252
  let element;
1004
1253
  if (Renderer) {
1005
1254
  const range = rangeOf(node);
@@ -1007,14 +1256,14 @@ function renderNode(node, context, index = 0, trailing) {
1007
1256
  const attribute = node.attributeNodes?.[name];
1008
1257
  if (!attribute) return String(node.attributes?.[name] ?? "");
1009
1258
  const range2 = rangeOf(attribute);
1010
- 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) });
1011
1260
  };
1012
- 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);
1013
1262
  } else {
1014
1263
  switch (node.type) {
1015
1264
  case "text":
1016
1265
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
1017
- context.compact ? node.value ?? "" : /* @__PURE__ */ jsx2(Text, { node, context }),
1266
+ context.compact ? node.value ?? "" : /* @__PURE__ */ jsx4(Text, { node, context }),
1018
1267
  trailing
1019
1268
  ] }, key);
1020
1269
  case "paragraph":
@@ -1033,62 +1282,62 @@ function renderNode(node, context, index = 0, trailing) {
1033
1282
  trailing
1034
1283
  ] }, key);
1035
1284
  case "strong":
1036
- element = /* @__PURE__ */ jsx2("strong", { children });
1285
+ element = /* @__PURE__ */ jsx4("strong", { children });
1037
1286
  break;
1038
1287
  case "emphasis":
1039
- element = /* @__PURE__ */ jsx2("em", { children });
1288
+ element = /* @__PURE__ */ jsx4("em", { children });
1040
1289
  break;
1041
1290
  case "delete":
1042
- element = /* @__PURE__ */ jsx2("del", { children });
1291
+ element = /* @__PURE__ */ jsx4("del", { children });
1043
1292
  break;
1044
1293
  case "inlineCode":
1045
- 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 }) });
1046
1295
  break;
1047
1296
  case "code":
1048
- 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 }) }) });
1049
1298
  break;
1050
1299
  case "math":
1051
1300
  case "inlineMath":
1052
- element = /* @__PURE__ */ jsx2("code", { children: node.value });
1301
+ element = /* @__PURE__ */ jsx4("code", { children: node.value });
1053
1302
  break;
1054
1303
  case "blockquote":
1055
1304
  element = /* @__PURE__ */ jsxs2("blockquote", { className: "limr-note", children: [
1056
- /* @__PURE__ */ jsx2("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.note }),
1057
- /* @__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 })
1058
1307
  ] });
1059
1308
  break;
1060
1309
  case "list":
1061
- 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 });
1062
1311
  break;
1063
1312
  case "listItem":
1064
1313
  element = /* @__PURE__ */ jsxs2("li", { className: typeof node.checked === "boolean" ? "limr-task-item" : void 0, children: [
1065
- 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" }),
1066
1315
  children
1067
1316
  ] });
1068
1317
  break;
1069
1318
  case "link":
1070
- 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 });
1071
1320
  break;
1072
1321
  case "image":
1073
- 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" });
1074
1323
  break;
1075
1324
  case "linkReference":
1076
- element = /* @__PURE__ */ jsx2(Reference, { node, image: false, children });
1325
+ element = /* @__PURE__ */ jsx4(Reference, { node, image: false, children });
1077
1326
  break;
1078
1327
  case "imageReference":
1079
- element = /* @__PURE__ */ jsx2(Reference, { node, image: true, children });
1328
+ element = /* @__PURE__ */ jsx4(Reference, { node, image: true, children });
1080
1329
  break;
1081
1330
  case "thematicBreak":
1082
- element = /* @__PURE__ */ jsx2("hr", {});
1331
+ element = /* @__PURE__ */ jsx4("hr", {});
1083
1332
  break;
1084
1333
  case "break":
1085
- element = /* @__PURE__ */ jsx2("br", {});
1334
+ element = /* @__PURE__ */ jsx4("br", { "data-limr-break": "", "data-source-start": rangeOf(node).start, "data-source-end": rangeOf(node).end });
1086
1335
  break;
1087
1336
  case "table": {
1088
1337
  const rows = node.children ?? [], alignment = node.align;
1089
- element = /* @__PURE__ */ jsx2("div", { className: "limr-table-scroll", children: /* @__PURE__ */ jsxs2("table", { children: [
1090
- /* @__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)) }),
1091
- /* @__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)) })
1092
1341
  ] }) });
1093
1342
  break;
1094
1343
  }
@@ -1098,19 +1347,19 @@ function renderNode(node, context, index = 0, trailing) {
1098
1347
  return null;
1099
1348
  case "footnoteReference":
1100
1349
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
1101
- /* @__PURE__ */ jsx2(FootnoteReference, { node }),
1350
+ /* @__PURE__ */ jsx4(FootnoteReference, { node }),
1102
1351
  trailing
1103
1352
  ] }, key);
1104
1353
  case "annotationReference": {
1105
1354
  const Tag = node.inline ? "span" : "div";
1106
1355
  element = /* @__PURE__ */ jsxs2(Tag, { children: [
1107
1356
  children,
1108
- /* @__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 } })
1109
1358
  ] });
1110
1359
  break;
1111
1360
  }
1112
1361
  case "html":
1113
- element = /* @__PURE__ */ jsx2("span", { className: "limr-literal", children: node.value });
1362
+ element = /* @__PURE__ */ jsx4("span", { className: "limr-literal", children: node.value });
1114
1363
  break;
1115
1364
  case "component":
1116
1365
  element = children;
@@ -1122,27 +1371,40 @@ function renderNode(node, context, index = 0, trailing) {
1122
1371
  const atomic2 = node.selection === "atomic" || ["math", "inlineMath", "image", "imageReference", "html"].includes(node.type);
1123
1372
  const corner = atomic2 && (!node.inline && !["inlineCode", "inlineMath", "html"].includes(node.type) || ["image", "imageReference"].includes(node.type) || node.name === "img");
1124
1373
  const after = corner ? void 0 : context.afterMarks.get(node);
1125
- const withMarkers = (element2) => after?.length || trailing ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1126
- element2,
1127
- after?.map((occurrence) => /* @__PURE__ */ jsx2(AnnotationMarker, { occurrence }, occurrence.id)),
1128
- trailing
1129
- ] }, key) : element2;
1130
- 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));
1131
1383
  if (blockElement) {
1132
1384
  const attributes = { ...blockAttributes(blockElement), ref: blockElement.ref };
1133
- 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));
1134
1386
  }
1135
- 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));
1136
1388
  }
1137
- var BlockView = memo(function BlockView2({ block, runtime, components, extensions, decorations, footnoteId }) {
1138
- const element = useRef2(null);
1139
- const clearBindings = useRef2(void 0);
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);
1140
1402
  const { occurrences } = decorations;
1141
1403
  const highlights = useMemo(() => decorations.highlights.filter((highlight) => !highlight.occurrenceId), [decorations.highlights]);
1142
1404
  const root = useMemo(() => footnoteId ? { ...block.node, type: "listItem" } : block.node, [block, footnoteId]);
1143
- const texts = useMemo(() => compactTextNodes(root, runtime.components), [root, runtime, components, extensions]);
1144
- const compact = !!texts && !occurrences.length && !highlights.length;
1145
- 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) => {
1146
1408
  clearBindings.current?.();
1147
1409
  clearBindings.current = void 0;
1148
1410
  if (element.current) runtime.textBinders.delete(element.current);
@@ -1186,148 +1448,32 @@ var BlockView = memo(function BlockView2({ block, runtime, components, extension
1186
1448
  return { marks: map, afterMarks: after };
1187
1449
  }, [block, occurrences]);
1188
1450
  if (!footnoteId && ["definition", "footnoteDefinition", "annotationDefinition"].includes(block.node.type)) return null;
1189
- const context = { runtime, block, marks, afterMarks, highlights, occurrences, compact, blockElement: { id: block.id, range: block.range, pending: block.pending, ref: observe } };
1190
- 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)) });
1191
- 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 });
1192
1454
  });
1193
1455
  function Footnotes() {
1194
1456
  const runtime = useRuntime();
1195
1457
  const doc = useDocumentSelector((snapshot) => snapshot);
1196
1458
  const components = useStoreSelector(runtime.configuration, (config) => config.components);
1197
1459
  const extensions = useStoreSelector(runtime.configuration, (config) => config.extensions);
1460
+ const rendererOverrides = useStoreSelector(runtime.configuration, (config) => config.rendererOverrides);
1198
1461
  const decorations = useStoreSelector(runtime.decorations, (state) => state.byBlock);
1199
1462
  const blocks = useMemo(() => new Map(doc.blocks.filter((block) => block.node.type === "footnoteDefinition").map((block) => [String(block.node.identifier), block])), [doc.blocks]);
1200
1463
  if (!doc.footnoteOrder.length) return null;
1201
1464
  return /* @__PURE__ */ jsxs2("section", { className: "limr-footnotes", "aria-label": runtime.locale.footnotes, children: [
1202
- /* @__PURE__ */ jsx2("hr", {}),
1203
- /* @__PURE__ */ jsx2("ol", { children: doc.footnoteOrder.map((id) => {
1465
+ /* @__PURE__ */ jsx4("hr", {}),
1466
+ /* @__PURE__ */ jsx4("ol", { children: doc.footnoteOrder.map((id) => {
1204
1467
  const node = doc.footnotes.get(id);
1205
1468
  if (!node) return null;
1206
1469
  const block = blocks.get(id) ?? { id: `${doc.id}-fn-${id}`, range: rangeOf(node), node, pending: false };
1207
- 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);
1208
1471
  }) })
1209
1472
  ] });
1210
1473
  }
1211
1474
 
1212
1475
  // src/react/selection.ts
1213
- import { useEffect as useEffect2 } from "react";
1214
-
1215
- // src/react/source-geometry.ts
1216
- function flowOf(element, container) {
1217
- 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");
1218
- return known && container.contains(known) ? known : element.closest("[data-limr-block]") ?? container;
1219
- }
1220
- function leavesIn(block, runtime) {
1221
- runtime.prepareTextBindings(block);
1222
- const result = [];
1223
- const visit = (node, inherited) => {
1224
- if (node instanceof HTMLElement) {
1225
- if (node !== block && node.hasAttribute("data-limr-document") || node.hasAttribute("inert")) return;
1226
- const marker = node.dataset.limrMarker;
1227
- const atomic2 = node.dataset.limrAtomic;
1228
- if (marker || atomic2) {
1229
- const image = node.dataset.limrMedia === "image";
1230
- 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 });
1231
- return;
1232
- }
1233
- if (node.hasAttribute("data-limr-ignore")) return;
1234
- const binding = runtime.bindings.get(node);
1235
- if (binding) inherited = { binding, offset: 0 };
1236
- }
1237
- if (node.nodeType === Node.TEXT_NODE) {
1238
- const binding = runtime.bindings.get(node) ?? inherited?.binding;
1239
- const offset = runtime.bindings.has(node) ? 0 : inherited?.offset ?? 0;
1240
- const length = node.textContent?.length ?? 0;
1241
- 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 });
1242
- if (inherited) inherited.offset += length;
1243
- return;
1244
- }
1245
- node.childNodes.forEach((child) => visit(child, inherited));
1246
- };
1247
- visit(block);
1248
- return result;
1249
- }
1250
- function textRange(leaf, source, viewport) {
1251
- const node = leaf.node, length = node.length;
1252
- const resolve = leaf.binding.resolve;
1253
- let lo = 0, hi = length;
1254
- while (lo < hi) {
1255
- const mid = lo + hi >>> 1;
1256
- if (resolve(leaf.offset + mid + 1, "end") <= source.start) lo = mid + 1;
1257
- else hi = mid;
1258
- }
1259
- let start = lo;
1260
- lo = start;
1261
- hi = length;
1262
- while (lo < hi) {
1263
- const mid = lo + hi >>> 1;
1264
- if (resolve(leaf.offset + mid, "start") < source.end) lo = mid + 1;
1265
- else hi = mid;
1266
- }
1267
- if (start === lo) return null;
1268
- let end = lo;
1269
- const range = document.createRange();
1270
- if (viewport && end - start > 1024) {
1271
- range.setStart(node, start);
1272
- range.setEnd(node, end);
1273
- const box = range.getBoundingClientRect();
1274
- if (box.height > viewport.bottom - viewport.top + 80) {
1275
- lo = start;
1276
- hi = end;
1277
- while (lo < hi) {
1278
- const mid = lo + hi >>> 1;
1279
- range.setStart(node, mid);
1280
- range.setEnd(node, mid + 1);
1281
- if (range.getBoundingClientRect().bottom < viewport.top) lo = mid + 1;
1282
- else hi = mid;
1283
- }
1284
- start = lo;
1285
- hi = end;
1286
- while (lo < hi) {
1287
- const mid = lo + hi >>> 1;
1288
- range.setStart(node, mid);
1289
- range.setEnd(node, mid + 1);
1290
- if (range.getBoundingClientRect().top <= viewport.bottom) lo = mid + 1;
1291
- else hi = mid;
1292
- }
1293
- end = lo;
1294
- }
1295
- }
1296
- if (start === end) return null;
1297
- range.setStart(node, start);
1298
- range.setEnd(node, end);
1299
- return range;
1300
- }
1301
- function sourceRangeStartRect(runtime, source) {
1302
- for (const block of runtime.document.getSnapshot().blocks) {
1303
- if (!intersects(block.range, source)) continue;
1304
- const element = runtime.blocks.get(block.id);
1305
- if (!element) continue;
1306
- for (const leaf of leavesIn(element, runtime)) {
1307
- if (leaf.kind === "marker" || !intersects(leaf.range, source)) continue;
1308
- if (leaf.kind === "text") {
1309
- const range = textRange(leaf, source);
1310
- if (!range) continue;
1311
- const node = leaf.node, end = range.endOffset;
1312
- for (let start = range.startOffset; start < end; ) {
1313
- const next = Math.min(end, start + (node.data.codePointAt(start) > 65535 ? 2 : 1));
1314
- range.setStart(node, start);
1315
- range.setEnd(node, next);
1316
- const rect = range.getBoundingClientRect();
1317
- if (rect.width && rect.height) return rect;
1318
- start = next;
1319
- }
1320
- } else {
1321
- const node = leaf.node;
1322
- const rect = (node.querySelector(leaf.kind === "image" ? "img" : "[data-limr-selection-surface]") ?? node).getBoundingClientRect();
1323
- if (rect.width && rect.height) return rect;
1324
- }
1325
- }
1326
- }
1327
- return null;
1328
- }
1329
-
1330
- // src/react/selection.ts
1476
+ import { useEffect as useEffect3 } from "react";
1331
1477
  function visualSelection(runtime, native, options) {
1332
1478
  if (![...runtime.content.querySelectorAll('[data-limr-selection-order="visual"]')].some((element) => native.intersectsNode(element))) return;
1333
1479
  const blocks = [...runtime.blocks.values()].filter((block) => native.intersectsNode(block));
@@ -1374,7 +1520,7 @@ function visualSelection(runtime, native, options) {
1374
1520
  return { ...selections[0], raw: combine("raw"), syntax: combine("syntax"), blocks: combine("blocks") };
1375
1521
  }
1376
1522
  function useSelectionTracking(runtime, enabled) {
1377
- useEffect2(() => {
1523
+ useEffect3(() => {
1378
1524
  const container = runtime.content;
1379
1525
  if (!container || !enabled) return;
1380
1526
  let frame = 0, dragging = false;
@@ -1382,6 +1528,10 @@ function useSelectionTracking(runtime, enabled) {
1382
1528
  function resolve(node, offset, edge) {
1383
1529
  let element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
1384
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 };
1385
1535
  runtime.prepareTextBindings(element);
1386
1536
  const native = node.nodeType === Node.TEXT_NODE ? runtime.bindings.get(node) : void 0;
1387
1537
  if (native?.resolve) return { offset: native.resolve(offset, edge), atomic: false, exact: native.exact !== false };
@@ -1389,7 +1539,7 @@ function useSelectionTracking(runtime, enabled) {
1389
1539
  const binding = runtime.bindings.get(element);
1390
1540
  if (binding) {
1391
1541
  if (binding.atomic) return { offset: edge === "start" ? binding.range.start : binding.range.end, atomic: true, exact: true };
1392
- const range = document.createRange();
1542
+ const range = measurementRange(element.ownerDocument);
1393
1543
  range.setStart(element, 0);
1394
1544
  try {
1395
1545
  range.setEnd(node, offset);
@@ -1450,12 +1600,20 @@ function useSelectionTracking(runtime, enabled) {
1450
1600
  previous = { startNode: range.startContainer, start: range.startOffset, endNode: range.endContainer, end: range.endOffset, revision, settled: !dragging };
1451
1601
  runtime.selectionRange = range.cloneRange();
1452
1602
  runtime.highlightReference = null;
1603
+ const text = selection.toString();
1453
1604
  const options = {
1454
- 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) : ""),
1455
1608
  settled: !dragging,
1456
1609
  quality: !start.exact || !end.exact ? "unmapped" : start.atomic || end.atomic ? "atomic" : "exact"
1457
1610
  };
1458
- 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 });
1459
1617
  }
1460
1618
  const schedule = () => {
1461
1619
  if (!frame) frame = requestAnimationFrame(process);
@@ -1467,11 +1625,34 @@ function useSelectionTracking(runtime, enabled) {
1467
1625
  dragging = false;
1468
1626
  schedule();
1469
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
+ };
1470
1649
  document.addEventListener("selectionchange", schedule);
1471
1650
  container.addEventListener("pointerdown", down);
1472
1651
  document.addEventListener("pointerup", up);
1473
1652
  document.addEventListener("pointercancel", up);
1474
1653
  document.addEventListener("keyup", schedule);
1654
+ document.addEventListener("copy", copy);
1655
+ document.addEventListener("keydown", copyKey);
1475
1656
  if (window.getSelection()?.isCollapsed === false) schedule();
1476
1657
  return () => {
1477
1658
  cancelAnimationFrame(frame);
@@ -1480,13 +1661,15 @@ function useSelectionTracking(runtime, enabled) {
1480
1661
  document.removeEventListener("pointerup", up);
1481
1662
  document.removeEventListener("pointercancel", up);
1482
1663
  document.removeEventListener("keyup", schedule);
1664
+ document.removeEventListener("copy", copy);
1665
+ document.removeEventListener("keydown", copyKey);
1483
1666
  };
1484
1667
  }, [runtime, enabled]);
1485
1668
  }
1486
1669
 
1487
1670
  // src/react/selection-paint.tsx
1488
- import { useEffect as useEffect3, useRef as useRef3 } from "react";
1489
- 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";
1490
1673
  function rectangles(leaves, source, viewport, measure) {
1491
1674
  const raw = [];
1492
1675
  const geometry = (leaf, rect, reference) => {
@@ -1509,7 +1692,7 @@ function rectangles(leaves, source, viewport, measure) {
1509
1692
  };
1510
1693
  const edgeRect = (leaf, edge) => {
1511
1694
  const node = leaf.node;
1512
- const range = document.createRange();
1695
+ const range = measurementRange(node.ownerDocument);
1513
1696
  const offset = edge === "first" ? 0 : Math.max(0, node.length - 1);
1514
1697
  range.setStart(node, offset);
1515
1698
  range.setEnd(node, Math.min(node.length, offset + 1));
@@ -1527,7 +1710,7 @@ function rectangles(leaves, source, viewport, measure) {
1527
1710
  const boxAt = (index) => {
1528
1711
  const leaf = leaves[index];
1529
1712
  if (leaf.kind !== "text") return leaf.node.getBoundingClientRect();
1530
- const range = document.createRange();
1713
+ const range = measurementRange(leaf.node.ownerDocument);
1531
1714
  range.selectNodeContents(leaf.node);
1532
1715
  return range.getBoundingClientRect();
1533
1716
  };
@@ -1557,6 +1740,13 @@ function rectangles(leaves, source, viewport, measure) {
1557
1740
  raw.push({ ...item, marker: leaf.node.dataset.limrMarker });
1558
1741
  } else if (intersects(leaf.range, source)) {
1559
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
+ }
1560
1750
  const range = textRange(leaf, source, viewport);
1561
1751
  const shell = leaf.node.parentElement?.closest("[data-limr-inline-syntax]");
1562
1752
  const edges = shell ? Array.from(shell.getClientRects()) : [];
@@ -1605,15 +1795,42 @@ function rectangles(leaves, source, viewport, measure) {
1605
1795
  const gap = right.left - left.left - left.width;
1606
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 });
1607
1797
  }
1608
- 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;
1609
1825
  }
1610
1826
  function SelectionPaint({ runtime, interactive = true }) {
1611
- const layer = useRef3(null);
1612
- useEffect3(() => {
1827
+ const layer = useRef5(null);
1828
+ useEffect4(() => {
1613
1829
  const container = runtime.content, paint = layer.current;
1614
1830
  if (!container || !paint) return;
1615
1831
  let frame = 0, translating = false;
1616
1832
  let leavesCache = /* @__PURE__ */ new WeakMap();
1833
+ let geometryCache = /* @__PURE__ */ new WeakMap();
1617
1834
  let metrics = /* @__PURE__ */ new WeakMap();
1618
1835
  const fontMetrics = /* @__PURE__ */ new Map();
1619
1836
  let blocks;
@@ -1751,17 +1968,31 @@ function SelectionPaint({ runtime, interactive = true }) {
1751
1968
  };
1752
1969
  for (let index = lo; index < blocks.length; index++) {
1753
1970
  const block = blocks[index];
1754
- if (block.element.getBoundingClientRect().top > viewport.bottom + 32) break;
1971
+ const blockBox = block.element.getBoundingClientRect();
1972
+ if (blockBox.top > viewport.bottom + 32) break;
1755
1973
  const tasks = [...decorations.get(block.id)?.highlights ?? []];
1756
1974
  for (const range of selections) if (intersects(range, block.range)) tasks.push({ id: "selection", range, selection: true });
1757
1975
  if (!tasks.length) continue;
1758
1976
  let leaves = leavesCache.get(block.element);
1759
- if (!leaves) {
1760
- leaves = leavesIn(block.element, runtime);
1761
- 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);
1762
1982
  }
1763
1983
  for (const task of tasks) {
1764
- 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);
1765
1996
  for (const rect of rects) {
1766
1997
  const clip = clipFor(rect.flow);
1767
1998
  const left = Math.max(rect.left, clip.left), top = Math.max(rect.top, clip.top);
@@ -1771,13 +2002,14 @@ function SelectionPaint({ runtime, interactive = true }) {
1771
2002
  }
1772
2003
  }
1773
2004
  }
1774
- paint.style.top = `${viewport.top - origin.top + scrollTop}px`;
1775
- paint.style.height = `${Math.max(0, viewport.bottom - viewport.top)}px`;
1776
2005
  const hostGeometry = /* @__PURE__ */ new Map();
1777
2006
  for (const { rect, task } of output) if (!task.selection && !hostGeometry.has(rect.flow)) {
1778
2007
  const host = rect.flow, box = host.getBoundingClientRect();
1779
- 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" });
1780
2010
  }
2011
+ paint.style.top = `${viewport.top - origin.top + scrollTop}px`;
2012
+ paint.style.height = `${Math.max(0, viewport.bottom - viewport.top)}px`;
1781
2013
  const counts = /* @__PURE__ */ new Map();
1782
2014
  const nextHits = [];
1783
2015
  output.forEach(({ rect, task }) => {
@@ -1798,8 +2030,8 @@ function SelectionPaint({ runtime, interactive = true }) {
1798
2030
  layer2.style.top = `${geometry.top}px`;
1799
2031
  layer2.style.height = `${Math.max(0, Math.min(viewport.bottom, geometry.box.bottom) - Math.max(viewport.top, geometry.box.top))}px`;
1800
2032
  target = layer2;
1801
- left += origin.left - geometry.box.left - host.clientLeft + host.scrollLeft;
1802
- 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;
1803
2035
  nextHits.push({ id: task.id, rect: { left: rect.left + origin.left - scrollLeft, top: rect.top + viewport.top, width: rect.width, height: rect.height } });
1804
2036
  }
1805
2037
  const index = counts.get(target) ?? 0;
@@ -1828,8 +2060,11 @@ function SelectionPaint({ runtime, interactive = true }) {
1828
2060
  }
1829
2061
  if (!item.parentNode) target.append(item);
1830
2062
  });
1831
- for (const layer2 of [paint, ...highlightLayers.values()]) while (layer2.children.length > (counts.get(layer2) ?? 0)) layer2.lastChild.remove();
1832
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
+ }
1833
2068
  hits = nextHits;
1834
2069
  const active = runtime.interaction.get().activeHighlight;
1835
2070
  if (active && !hits.some((hit) => hit.id === active)) runtime.clearSelection();
@@ -1857,10 +2092,12 @@ function SelectionPaint({ runtime, interactive = true }) {
1857
2092
  };
1858
2093
  const fontsChanged = () => {
1859
2094
  fontMetrics.clear();
2095
+ geometryCache = /* @__PURE__ */ new WeakMap();
1860
2096
  invalidate();
1861
2097
  };
1862
2098
  const unsubscribers = [runtime.interaction.subscribe(() => {
1863
2099
  const state = runtime.interaction.get(), selection = state.selection;
2100
+ const referenceChanged = state.active !== lastAnnotation || state.hovered !== lastHoveredAnnotation;
1864
2101
  if (state.hoveredHighlight !== lastHovered || state.activeHighlight !== lastActive || state.active !== lastAnnotation || state.hovered !== lastHoveredAnnotation) {
1865
2102
  lastHovered = state.hoveredHighlight;
1866
2103
  lastActive = state.activeHighlight;
@@ -1868,6 +2105,7 @@ function SelectionPaint({ runtime, interactive = true }) {
1868
2105
  lastHoveredAnnotation = state.hovered;
1869
2106
  feedback();
1870
2107
  }
2108
+ if (referenceChanged) schedule();
1871
2109
  if (selection === lastSelection) return;
1872
2110
  lastSelection = selection;
1873
2111
  render();
@@ -1876,8 +2114,12 @@ function SelectionPaint({ runtime, interactive = true }) {
1876
2114
  if (decorations === lastDecorations) return;
1877
2115
  lastDecorations = decorations;
1878
2116
  leavesCache = /* @__PURE__ */ new WeakMap();
2117
+ geometryCache = /* @__PURE__ */ new WeakMap();
1879
2118
  schedule();
1880
- }), runtime.layout.subscribe(invalidate), runtime.documentOffset.subscribe(schedule), runtime.configuration.subscribe(invalidate)];
2119
+ }), runtime.layout.subscribe(invalidate), runtime.documentOffset.subscribe(schedule), runtime.configuration.subscribe(() => {
2120
+ geometryCache = /* @__PURE__ */ new WeakMap();
2121
+ invalidate();
2122
+ })];
1881
2123
  const observer = new MutationObserver((records) => {
1882
2124
  let changed = false;
1883
2125
  for (const record of records) {
@@ -1885,8 +2127,13 @@ function SelectionPaint({ runtime, interactive = true }) {
1885
2127
  if (!target || target === paint || paint.contains(target) || target.closest(".limr-highlight-layer")) continue;
1886
2128
  if (record.type === "childList" && [...record.addedNodes, ...record.removedNodes].every((node) => node instanceof Element && node.classList.contains("limr-highlight-layer"))) continue;
1887
2129
  const block = target.closest("[data-limr-block]");
1888
- if (block) leavesCache.delete(block);
1889
- 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
+ }
1890
2137
  changed = true;
1891
2138
  }
1892
2139
  if (changed) {
@@ -1895,7 +2142,12 @@ function SelectionPaint({ runtime, interactive = true }) {
1895
2142
  }
1896
2143
  });
1897
2144
  observer.observe(container, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["class", "style", "inert", "open", "data-limr-theme", "data-limr-justify"] });
1898
- 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
+ }
1899
2151
  runtime.hoverHighlight(null);
1900
2152
  schedule();
1901
2153
  };
@@ -1986,15 +2238,15 @@ function SelectionPaint({ runtime, interactive = true }) {
1986
2238
  clear();
1987
2239
  };
1988
2240
  }, [runtime, interactive]);
1989
- 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" });
1990
2242
  }
1991
2243
 
1992
2244
  // src/react/Markdown.tsx
1993
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2245
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
1994
2246
  function Markdown({ className, style, interactive = true, followOutput = false, justify, ...props }) {
1995
2247
  const parent = useContext(RuntimeContext);
1996
- if (props.source !== void 0 || !parent) return /* @__PURE__ */ jsx4(DocumentProvider, { ...props, justify, children: /* @__PURE__ */ jsx4(MarkdownBody, { className, style, interactive, followOutput }) });
1997
- 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 });
1998
2250
  }
1999
2251
  function MarkdownBody({ className, style, interactive, followOutput, justify }) {
2000
2252
  const runtime = useRuntime();
@@ -2002,17 +2254,20 @@ function MarkdownBody({ className, style, interactive, followOutput, justify })
2002
2254
  const appearance = useAppearance();
2003
2255
  const theme = useStoreSelector(runtime.configuration, (config) => config.theme ?? "light");
2004
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);
2005
2259
  const scrollRoot = useStoreSelector(runtime.configuration, (config) => config.scrollRoot);
2006
2260
  const offset = useStoreSelector(runtime.documentOffset, (value) => value);
2007
2261
  const components = useStoreSelector(runtime.configuration, (config) => config.components);
2008
2262
  const extensions = useStoreSelector(runtime.configuration, (config) => config.extensions);
2263
+ const rendererOverrides = useStoreSelector(runtime.configuration, (config) => config.rendererOverrides);
2009
2264
  const decorations = useStoreSelector(runtime.decorations, (state) => state.byBlock);
2010
- const ref = useRef4(null);
2011
- const pinned = useRef4(true);
2265
+ const ref = useRef6(null);
2266
+ const pinned = useRef6(true);
2012
2267
  useIsomorphicLayoutEffect(() => ref.current ? runtime.mountContent(ref.current) : void 0, [runtime, scrollRoot]);
2013
- useIsomorphicLayoutEffect(() => runtime.requestLayout(), [runtime, justify, configuredJustify]);
2268
+ useIsomorphicLayoutEffect(() => runtime.requestLayout(), [runtime, justify, configuredJustify, typography]);
2014
2269
  useSelectionTracking(runtime, interactive);
2015
- useEffect4(() => {
2270
+ useEffect5(() => {
2016
2271
  if (!followOutput) return;
2017
2272
  const root = scrollRoot ?? window;
2018
2273
  const onScroll = () => {
@@ -2028,23 +2283,23 @@ function MarkdownBody({ className, style, interactive, followOutput, justify })
2028
2283
  const frame = requestAnimationFrame(() => runtime.scrollTo(scrollRoot?.scrollHeight ?? window.document.documentElement.scrollHeight, "instant"));
2029
2284
  return () => cancelAnimationFrame(frame);
2030
2285
  }, [document2, runtime, followOutput, scrollRoot]);
2031
- 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: [
2032
- document2.blocks.map((block) => /* @__PURE__ */ jsx4(BlockView, { block, runtime, components, extensions, decorations: decorations.get(block.id) ?? emptyDecorations }, block.id)),
2033
- /* @__PURE__ */ jsx4(Footnotes, {}),
2034
- /* @__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 })
2035
2290
  ] });
2036
2291
  }
2037
2292
 
2038
2293
  // src/components/OverlayScrollArea.tsx
2039
- import { useRef as useRef5 } from "react";
2040
- 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";
2041
2296
  function OverlayScrollArea({ children, active, resetKey, label }) {
2042
- const area = useRef5(null);
2043
- const content = useRef5(null);
2044
- const thumb = useRef5(null);
2045
- const track = useRef5(null);
2046
- const drag = useRef5(null);
2047
- 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(() => {
2048
2303
  });
2049
2304
  useIsomorphicLayoutEffect(() => {
2050
2305
  const element = area.current, wrapper = element.parentElement;
@@ -2117,73 +2372,73 @@ function OverlayScrollArea({ children, active, resetKey, label }) {
2117
2372
  show.current();
2118
2373
  };
2119
2374
  return /* @__PURE__ */ jsxs4("div", { className: "limr-overlay-scroll", children: [
2120
- /* @__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 }) }),
2121
- /* @__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" }) })
2122
2377
  ] });
2123
2378
  }
2124
2379
 
2125
2380
  // src/components/AnnotationCard.tsx
2126
- 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";
2127
2382
  function AnnotationCard({ title, bodyLabel, occurrence, expanded, pinned, canExpand, expand, collapse, close, children }) {
2128
2383
  const locale = useLocale();
2129
2384
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
2130
2385
  /* @__PURE__ */ jsxs5("div", { className: "limr-overlay-header", children: [
2131
- /* @__PURE__ */ jsx6("span", { children: title }),
2386
+ /* @__PURE__ */ jsx8("span", { children: title }),
2132
2387
  /* @__PURE__ */ jsxs5("div", { className: "limr-overlay-controls", children: [
2133
- 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 }) }),
2134
- 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" })
2135
2390
  ] })
2136
2391
  ] }),
2137
- /* @__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 })
2138
2393
  ] });
2139
2394
  }
2140
2395
 
2141
2396
  // src/extensions/annotations.tsx
2142
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2143
- 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 }) });
2144
2399
  function CitationIcon() {
2145
- 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" }) });
2146
2401
  }
2147
2402
  function AnnotationMarkdown({ source, className, ...props }) {
2148
2403
  const runtime = useRuntime();
2149
2404
  const config = useStoreSelector(runtime.configuration, (config2) => config2);
2150
- 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 ?? ""}` });
2151
2406
  }
2152
2407
  function citation(options = {}) {
2153
- function Preview({ annotations }) {
2408
+ function Preview2({ annotations }) {
2154
2409
  const summary = useMemo2(() => annotations.filter((annotation) => annotation.type === (options.type ?? "citation")).map((annotation) => {
2155
2410
  const data = annotation.data;
2156
2411
  const source = data?.summary?.trim() || data?.description?.trim();
2157
2412
  if (source) return fromMarkdown(source).children?.map(textContent).join(" ").replace(/\s+/g, " ").trim();
2158
2413
  return safeUrl(data?.url) || data?.title;
2159
2414
  }).filter(Boolean).join(" \xB7 "), [annotations]);
2160
- return summary ? /* @__PURE__ */ jsx7("span", { title: summary, children: summary }) : null;
2415
+ return summary ? /* @__PURE__ */ jsx9("span", { title: summary, children: summary }) : null;
2161
2416
  }
2162
- function Card(props) {
2417
+ function Card2(props) {
2163
2418
  const locale = useLocale();
2164
- return /* @__PURE__ */ jsx7(AnnotationCard, { ...props, title: locale.citation ?? locale.annotations });
2419
+ return /* @__PURE__ */ jsx9(AnnotationCard, { ...props, title: locale.citation ?? locale.annotations });
2165
2420
  }
2166
- function Content({ data }) {
2421
+ function Content3({ data }) {
2167
2422
  const runtime = useRuntime();
2168
2423
  const configured = useStoreSelector(runtime.configuration, () => runtime.components);
2169
2424
  const components = useMemo2(() => ({ ...configured, paragraph: configured.paragraph ?? CitationParagraph }), [configured]);
2170
- const link = useRef6(null);
2425
+ const link = useRef8(null);
2171
2426
  const url = safeUrl(data?.url);
2172
2427
  return /* @__PURE__ */ jsxs6("div", { className: "limr-citation-content", "data-linked": !!url || void 0, onClick: (event) => {
2173
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();
2174
2429
  }, children: [
2175
- /* @__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" }) }),
2176
- (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 })
2177
2432
  ] });
2178
2433
  }
2179
2434
  return {
2180
2435
  name: "citation",
2181
2436
  tags: ["citation", "ref"],
2182
2437
  scope: "local",
2183
- annotationTypes: [{ type: "citation", label: (locale) => locale.citation ?? "Citation", icon: CitationIcon, color: "#6366f1", highlight: "hover", 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 } }],
2184
2439
  transform(root) {
2185
2440
  walk(root, (node) => {
2186
- if (node.type !== "component") return;
2441
+ if (node.type !== "component" || node.incomplete) return;
2187
2442
  if (node.name === "citation") {
2188
2443
  const attributes = node.attributes ?? {};
2189
2444
  const id = String(attributes["cite-id"] ?? attributes.id ?? "");
@@ -2207,13 +2462,13 @@ function citation(options = {}) {
2207
2462
  }
2208
2463
 
2209
2464
  // src/extensions/translations.tsx
2210
- import { useRef as useRef8, useState as useState3 } from "react";
2465
+ import { useRef as useRef10, useState as useState3 } from "react";
2211
2466
 
2212
2467
  // src/components/InlineMarker.tsx
2213
2468
  import { forwardRef } from "react";
2214
- import { jsx as jsx8 } from "react/jsx-runtime";
2469
+ import { jsx as jsx10 } from "react/jsx-runtime";
2215
2470
  var InlineMarker = forwardRef(function InlineMarker2({ label, color, children, className = "", style, ...props }, ref) {
2216
- 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(
2217
2472
  "button",
2218
2473
  {
2219
2474
  ...props,
@@ -2223,18 +2478,18 @@ var InlineMarker = forwardRef(function InlineMarker2({ label, color, children, c
2223
2478
  title: props.title ?? label,
2224
2479
  className: `limr-mark limr-action-mark ${className}`,
2225
2480
  style: { "--limr-mark-color": color, ...style },
2226
- children: /* @__PURE__ */ jsx8("span", { className: "limr-mark-icon", children })
2481
+ children: /* @__PURE__ */ jsx10("span", { className: "limr-mark-icon", children })
2227
2482
  }
2228
2483
  ) });
2229
2484
  });
2230
2485
 
2231
2486
  // src/components/Collapse.tsx
2232
- import { useRef as useRef7, useState as useState2 } from "react";
2233
- 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";
2234
2489
  function Collapse({ open, children, className = "", keepMounted = false }) {
2235
2490
  const [mounted, setMounted] = useState2(open);
2236
2491
  const [visible, setVisible] = useState2(open);
2237
- const panel = useRef7(null);
2492
+ const panel = useRef9(null);
2238
2493
  useIsomorphicLayoutEffect(() => {
2239
2494
  if (keepMounted) return;
2240
2495
  if (open) {
@@ -2250,24 +2505,24 @@ function Collapse({ open, children, className = "", keepMounted = false }) {
2250
2505
  const timer = setTimeout(() => setMounted(false), 420);
2251
2506
  return () => clearTimeout(timer);
2252
2507
  }, [open, keepMounted, mounted]);
2253
- return /* @__PURE__ */ jsx9("div", { ref: (element) => {
2508
+ return /* @__PURE__ */ jsx11("div", { ref: (element) => {
2254
2509
  panel.current = element;
2255
2510
  element?.toggleAttribute("inert", !open);
2256
- }, 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 }) });
2257
2512
  }
2258
2513
 
2259
2514
  // src/extensions/translations.tsx
2260
- 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";
2261
2516
  var modes = ["origin", "translation", "both"];
2262
2517
  var names = { origin: "\u539F\u6587", translation: "\u8BD1\u6587", both: "\u5BF9\u7167" };
2263
2518
  function TranslationIcon({ mode }) {
2264
- 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: [
2265
- /* @__PURE__ */ jsx10("rect", { x: "2.5", y: "4", width: "8", height: "16", rx: "2.5" }),
2266
- /* @__PURE__ */ jsx10("rect", { x: "13.5", y: "4", width: "8", height: "16", rx: "2.5" }),
2267
- /* @__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" })
2268
2523
  ] }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
2269
- /* @__PURE__ */ jsx10("rect", { x: "4.5", y: "3", width: "15", height: "18", rx: "3" }),
2270
- 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" })
2271
2526
  ] }) });
2272
2527
  }
2273
2528
  function modeOf(value, fallback) {
@@ -2291,8 +2546,8 @@ function translations(options = {}) {
2291
2546
  const requested = modeOf(globalMode ?? localMode ?? node.attributes?.default, options.defaultMode ?? "origin");
2292
2547
  const mode = !origin ? "translation" : !translation ? "origin" : requested;
2293
2548
  const next = modes[(modes.indexOf(mode) + 1) % modes.length];
2294
- const marker = useRef8(null);
2295
- const restoreFocus = useRef8(false);
2549
+ const marker = useRef10(null);
2550
+ const restoreFocus = useRef10(false);
2296
2551
  const locked = globalMode !== void 0 && !onGlobalChange;
2297
2552
  useIsomorphicLayoutEffect(() => {
2298
2553
  if (restoreFocus.current) {
@@ -2300,8 +2555,8 @@ function translations(options = {}) {
2300
2555
  restoreFocus.current = false;
2301
2556
  }
2302
2557
  }, [mode]);
2303
- if (!origin && !translation) return /* @__PURE__ */ jsx10(Fragment4, { children });
2304
- const toggle = origin && translation ? /* @__PURE__ */ jsx10(
2558
+ if (!origin && !translation) return /* @__PURE__ */ jsx12(Fragment4, { children });
2559
+ const toggle = origin && translation ? /* @__PURE__ */ jsx12(
2305
2560
  InlineMarker,
2306
2561
  {
2307
2562
  ref: marker,
@@ -2315,12 +2570,12 @@ function translations(options = {}) {
2315
2570
  if (globalMode !== void 0) onGlobalChange?.(next);
2316
2571
  else setLocalMode(next);
2317
2572
  },
2318
- children: /* @__PURE__ */ jsx10(TranslationIcon, { mode })
2573
+ children: /* @__PURE__ */ jsx12(TranslationIcon, { mode })
2319
2574
  }
2320
2575
  ) : void 0;
2321
2576
  return /* @__PURE__ */ jsxs7("div", { className: "limr-translations", "data-mode": mode, children: [
2322
- 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 }) }) }),
2323
- 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 }) }) })
2324
2579
  ] });
2325
2580
  }
2326
2581
  return {
@@ -2330,16 +2585,338 @@ function translations(options = {}) {
2330
2585
  display: "block",
2331
2586
  scope: "local",
2332
2587
  parameters: { translationMode: void 0, onTranslationModeChange: void 0 },
2333
- 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 }) }
2334
2589
  };
2335
2590
  }
2336
2591
 
2337
- // src/extensions/blocks.tsx
2338
- 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
+ }
2339
2916
 
2340
2917
  // src/extensions/define.ts
2341
- function defineBlock({ name, component, tags = [name], directives = [name], selection = "content" }) {
2342
- 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])) };
2343
2920
  }
2344
2921
  function defineAnnotation(type) {
2345
2922
  return { name: `annotation:${type.type}`, annotationTypes: [type], scope: "local" };
@@ -2348,43 +2925,293 @@ function defineExtension(extension) {
2348
2925
  return extension;
2349
2926
  }
2350
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
+
2351
3188
  // src/components/Disclosure.tsx
2352
- import { useEffect as useEffect5, useState as useState4 } from "react";
2353
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
3189
+ import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
2354
3190
  function Disclosure({ title, open, onChange, children }) {
2355
- const [present, setPresent] = useState4(open);
2356
- useEffect5(() => {
2357
- if (open) {
2358
- setPresent(true);
2359
- return;
2360
- }
2361
- const timer = setTimeout(() => setPresent(false), 420);
2362
- return () => clearTimeout(timer);
2363
- }, [open]);
2364
- return /* @__PURE__ */ jsxs8("details", { className: "limr-disclosure", open: present || open, "data-expanded": open || void 0, children: [
2365
- /* @__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) => {
2366
3193
  event.preventDefault();
2367
3194
  onChange(!open);
2368
3195
  }, "data-limr-ignore": "true", children: [
2369
- /* @__PURE__ */ jsx11("span", { className: "limr-underline", children: title }),
2370
- /* @__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" }) })
2371
3198
  ] }),
2372
- /* @__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 }) })
2373
3200
  ] });
2374
3201
  }
2375
3202
 
2376
3203
  // src/components/DiamondCheck.tsx
2377
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
3204
+ import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
2378
3205
  function DiamondCheck({ checked }) {
2379
- 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: [
2380
- /* @__PURE__ */ jsx12("path", { className: "limr-diamond-outline", d: "M9 1.5L16.5 9L9 16.5L1.5 9Z", fill: "none" }),
2381
- /* @__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" })
2382
3209
  ] });
2383
3210
  }
2384
3211
 
2385
3212
  // src/extensions/video.tsx
2386
- import { useEffect as useEffect6, useRef as useRef9, useState as useState5 } from "react";
2387
- 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";
2388
3215
  function resolveVideo(source, provider = "auto") {
2389
3216
  const safe = safeUrl(source);
2390
3217
  if (!safe) return;
@@ -2432,21 +3259,21 @@ function video(options = {}) {
2432
3259
  function Video({ node, source: markdown, renderAttribute }) {
2433
3260
  const runtime = useRuntime();
2434
3261
  const scrollRoot = useStoreSelector(runtime.configuration, (config) => config.scrollRoot);
2435
- const element = useRef9(null);
2436
- const [visible, setVisible] = useState5(false);
2437
- const [playing, setPlaying] = useState5(false);
2438
- 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);
2439
3266
  const provider = String(node.attributes?.provider ?? "auto");
2440
3267
  const source = String(node.attributes?.url ?? node.attributes?.src ?? "");
2441
3268
  const requested = node.attributes?.loading ?? options.loading ?? "visible";
2442
3269
  const loading2 = requested === "click" || requested === "eager" ? requested : "visible";
2443
3270
  const resolved = options.resolveUrl?.(provider, source);
2444
3271
  const media = resolved && safeUrl(resolved) ? { url: safeUrl(resolved), native: provider === "native" } : resolveVideo(source, provider);
2445
- useEffect6(() => {
3272
+ useEffect7(() => {
2446
3273
  setFailed(false);
2447
3274
  setPlaying(false);
2448
3275
  }, [source, provider]);
2449
- useEffect6(() => {
3276
+ useEffect7(() => {
2450
3277
  if (visible || loading2 !== "visible" || !element.current) return;
2451
3278
  if (typeof IntersectionObserver === "undefined") {
2452
3279
  setVisible(true);
@@ -2464,20 +3291,20 @@ function video(options = {}) {
2464
3291
  const enabled = loading2 === "eager" || loading2 === "visible" && visible || playing;
2465
3292
  const title = String(node.attributes?.title ?? `${provider === "auto" ? "Video" : provider} video`);
2466
3293
  const original = safeUrl(source);
2467
- return /* @__PURE__ */ jsxs10("div", { ref: element, className: "limr-video", "data-limr-selection-surface": "", "data-loading": loading2, children: [
2468
- /* @__PURE__ */ jsx13("div", { className: "limr-video-frame", children: !media || failed ? /* @__PURE__ */ jsxs10("div", { className: "limr-video-error", children: [
2469
- /* @__PURE__ */ jsx13("span", { children: runtime.locale.unavailable }),
2470
- original && /* @__PURE__ */ jsx13("a", { href: original, target: "_blank", rel: "noopener noreferrer", children: source })
2471
- ] }) : 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" }) }) }) }),
2472
- node.attributes?.title && /* @__PURE__ */ jsx13("div", { className: "limr-video-title", children: renderAttribute("title") }),
2473
- /* @__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 })
2474
3301
  ] });
2475
3302
  }
2476
- 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" });
2477
3304
  }
2478
3305
 
2479
3306
  // src/extensions/figures.tsx
2480
- 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";
2481
3308
 
2482
3309
  // src/extensions/figure-layout.ts
2483
3310
  var defaultFigureLimits = { minWidth: 220, maxWidth: 560, minHeight: 120, maxHeight: 480 };
@@ -2519,7 +3346,7 @@ function figureLimits(attributes = {}, defaults2 = defaultFigureLimits) {
2519
3346
  }
2520
3347
 
2521
3348
  // src/extensions/figures.tsx
2522
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
3349
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
2523
3350
  var FigureContext = createContext(defaultFigureLimits);
2524
3351
  var captionNode = (node) => node.name === "figcaption" || node.name === "caption";
2525
3352
  var flatten = (nodes) => nodes.flatMap((node) => node.type === "paragraph" && node.children?.some((child) => child.name === "figure" || captionNode(child)) ? node.children : [node]);
@@ -2536,14 +3363,14 @@ function figures(options = {}) {
2536
3363
  const panels = nodes.filter((child) => child.name === "figure");
2537
3364
  const captions = nodes.filter(captionNode);
2538
3365
  const body = nodes.filter((child) => !captionNode(child));
2539
- const limits = useMemo3(() => figureLimits(node.attributes, defaults2), [node.attributes]);
2540
- const grid = useRef10(null);
2541
- 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);
2542
3369
  const gap = Math.min(100, dimension(node.attributes?.gap, options.gap ?? 16));
2543
3370
  const rows = dimension(node.attributes?.rows, options.rows ?? 0);
2544
3371
  const requested = dimension(node.attributes?.columns ?? node.attributes?.cols, options.columns ?? 0) || (rows > 0 ? Math.ceil(panels.length / rows) : void 0);
2545
3372
  const responsive = String(node.attributes?.responsive ?? options.responsive ?? true) !== "false";
2546
- useEffect7(() => {
3373
+ useEffect8(() => {
2547
3374
  const element = grid.current;
2548
3375
  if (!element) return;
2549
3376
  const measure = () => {
@@ -2566,22 +3393,22 @@ function figures(options = {}) {
2566
3393
  element.removeEventListener("error", measure, true);
2567
3394
  };
2568
3395
  }, [node, limits, gap, requested, responsive]);
2569
- return /* @__PURE__ */ jsxs11("figure", { id: identifier(node), className: "limr-figures", "data-limr-selection-order": "visual", "data-columns": columns, "data-responsive": responsive, children: [
2570
- /* @__PURE__ */ jsx14(CopyButton, { value: source, label: locale.copyFigures, variant: "glass", className: "limr-figures-copy" }),
2571
- /* @__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) }) }),
2572
- 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
2573
3400
  ] });
2574
3401
  }
2575
3402
  function Figure({ node, renderChildren, renderAttribute }) {
2576
3403
  const locale = useLocale();
2577
3404
  const inherited = useContext2(FigureContext);
2578
3405
  const limits = figureLimits(node.attributes, inherited === defaultFigureLimits ? defaults2 : inherited);
2579
- const media = useRef10(null);
2580
- 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));
2581
3408
  const nodes = flatten(node.children ?? []);
2582
3409
  const captions = nodes.filter(captionNode);
2583
3410
  const body = nodes.filter((child) => !captionNode(child));
2584
- useEffect7(() => {
3411
+ useEffect8(() => {
2585
3412
  const element = media.current;
2586
3413
  if (!element) return;
2587
3414
  const measure = () => {
@@ -2593,23 +3420,24 @@ function figures(options = {}) {
2593
3420
  return () => element.removeEventListener("load", measure, true);
2594
3421
  }, [node]);
2595
3422
  const maximum = figureWidths({ ratio, limits }).maximum;
2596
- 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: {
2597
3424
  "--limr-figure-width": Number.isFinite(maximum) ? `${maximum}px` : "100%",
2598
3425
  "--limr-figure-min-height": `${Math.min(limits.minHeight, limits.maxHeight)}px`,
2599
3426
  "--limr-figure-max-height": Number.isFinite(limits.maxHeight) ? `${limits.maxHeight}px` : "none"
2600
3427
  }, children: [
2601
- /* @__PURE__ */ jsx14("div", { ref: media, className: "limr-figure-media", children: renderChildren(body) }),
2602
- /* @__PURE__ */ jsx14(CopyButton, { onCopy: async () => {
3428
+ /* @__PURE__ */ jsx22("div", { ref: media, className: "limr-figure-media", children: renderChildren(body) }),
3429
+ /* @__PURE__ */ jsx22(CopyButton, { onCopy: async () => {
2603
3430
  const url = media.current?.querySelector("img")?.getAttribute("src");
2604
3431
  if (!url) throw new Error("Image URL unavailable");
2605
3432
  await navigator.clipboard.writeText(url);
2606
3433
  }, label: locale.copyImage, variant: "glass", className: "limr-figure-copy" }),
2607
- 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
2608
3435
  ] });
2609
3436
  }
2610
- const Caption = ({ children }) => /* @__PURE__ */ jsx14("figcaption", { children });
3437
+ const Caption = ({ children }) => /* @__PURE__ */ jsx22("figcaption", { children });
2611
3438
  return {
2612
3439
  name: "figures",
3440
+ streaming: { Loading: BlockLoading },
2613
3441
  tags: ["figures", "figure", "figcaption", "caption", "img"],
2614
3442
  directives: ["figures", "figure", "figcaption"],
2615
3443
  scope: "local",
@@ -2619,7 +3447,7 @@ function figures(options = {}) {
2619
3447
  figure: Figure,
2620
3448
  figcaption: Caption,
2621
3449
  caption: Caption,
2622
- 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" })
2623
3451
  },
2624
3452
  transform(root) {
2625
3453
  const visit = (node) => {
@@ -2639,7 +3467,7 @@ function figures(options = {}) {
2639
3467
  }
2640
3468
 
2641
3469
  // src/extensions/blocks.tsx
2642
- 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";
2643
3471
  function parts(node, name) {
2644
3472
  const result = [];
2645
3473
  walk(node, (child) => {
@@ -2647,25 +3475,25 @@ function parts(node, name) {
2647
3475
  });
2648
3476
  return result;
2649
3477
  }
2650
- var PassThrough = ({ children }) => /* @__PURE__ */ jsx15(Fragment5, { children });
3478
+ var PassThrough = ({ children }) => /* @__PURE__ */ jsx23(Fragment9, { children });
2651
3479
  function definition(options = {}) {
2652
3480
  function Definition({ node, children, renderChildren, renderAttribute }) {
2653
3481
  const runtime = useRuntime();
2654
3482
  const title = parts(node, "title")[0];
2655
3483
  const content = parts(node, "content")[0];
2656
- return /* @__PURE__ */ jsxs12("aside", { className: "limr-definition", children: [
2657
- /* @__PURE__ */ jsx15("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.definition }),
2658
- (title || node.attributes?.title) && /* @__PURE__ */ jsx15("div", { className: "limr-definition-title", children: title ? renderChildren(title.children ?? []) : renderAttribute("title") }),
2659
- /* @__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 })
2660
3488
  ] });
2661
3489
  }
2662
- 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 } };
2663
3491
  }
2664
3492
  function question(options = {}) {
2665
3493
  function Question({ node, children, renderChildren, renderAttribute }) {
2666
3494
  const runtime = useRuntime();
2667
- const [showHint, setHint] = useState7(false), [showAnswer, setAnswer] = useState7(false);
2668
- 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);
2669
3497
  const content = parts(node, "question-content")[0];
2670
3498
  const hint = parts(node, "question-hint")[0];
2671
3499
  const answer = parts(node, "question-answer")[0];
@@ -2679,22 +3507,22 @@ function question(options = {}) {
2679
3507
  } catch {
2680
3508
  }
2681
3509
  const success = correct.length === selected.length && correct.every((index) => selected.includes(index));
2682
- return /* @__PURE__ */ jsxs12("section", { className: "limr-question", children: [
2683
- /* @__PURE__ */ jsx15("div", { className: "limr-card-label", "data-limr-ignore": "true", children: runtime.locale.question }),
2684
- node.attributes?.title && /* @__PURE__ */ jsx15("div", { className: "limr-question-title", children: renderAttribute("title") }),
2685
- /* @__PURE__ */ jsx15("div", { className: "limr-block-content", children: content ? renderChildren(content.children ?? []) : !selections.length && !hint && !answer ? children : null }),
2686
- selections.length > 0 && /* @__PURE__ */ jsxs12("fieldset", { className: "limr-question-options", children: [
2687
- /* @__PURE__ */ jsx15("legend", { className: "limr-sr-only", children: String(node.attributes?.title ?? runtime.locale.question) }),
2688
- 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: [
2689
- /* @__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: () => {
2690
3518
  setSelected(multi ? selected.includes(index) ? selected.filter((item) => item !== index) : [...selected, index] : [index]);
2691
3519
  } }),
2692
- /* @__PURE__ */ jsx15(DiamondCheck, { checked: selected.includes(index) || checked && correct.includes(index) }),
2693
- /* @__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 ?? []) })
2694
3522
  ] }, index))
2695
3523
  ] }),
2696
- selections.length > 0 && correct.length > 0 && /* @__PURE__ */ jsxs12("div", { className: "limr-question-actions", "data-limr-ignore": "true", children: [
2697
- /* @__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: () => {
2698
3526
  if (checked) {
2699
3527
  setChecked(false);
2700
3528
  setSelected([]);
@@ -2703,53 +3531,53 @@ function question(options = {}) {
2703
3531
  setChecked(true);
2704
3532
  setAnswer(true);
2705
3533
  }
2706
- }, children: /* @__PURE__ */ jsx15("span", { className: "limr-button-label", children: checked ? runtime.locale.retry : runtime.locale.submit }, checked ? "retry" : "submit") }),
2707
- /* @__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 }) })
2708
3536
  ] }),
2709
- hint && /* @__PURE__ */ jsx15(Disclosure, { title: runtime.locale.hint, open: showHint, onChange: setHint, children: renderChildren(hint.children ?? []) }),
2710
- 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 ?? []) })
2711
3539
  ] });
2712
3540
  }
2713
3541
  const names2 = ["question-content", "question-hint", "question-answer", "question-sel-grp", "question-sel"];
2714
- 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])) } };
2715
3543
  }
2716
3544
  function extras(options = {}) {
2717
- const Extra = ({ node, children, renderAttribute }) => /* @__PURE__ */ jsxs12("div", { className: "limr-extra", children: [
2718
- /* @__PURE__ */ jsxs12("a", { className: "limr-extra-link", href: safeUrl(node.attributes?.url), target: "_blank", rel: "noreferrer", children: [
2719
- /* @__PURE__ */ jsx15("span", { className: "limr-underline", children: renderAttribute(node.attributes?.title ? "title" : "url") }),
2720
- 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") }) })
2721
3549
  ] }),
2722
3550
  children
2723
3551
  ] });
2724
3552
  const Extras = ({ children }) => {
2725
3553
  const runtime = useRuntime();
2726
- return /* @__PURE__ */ jsxs12("aside", { className: "limr-extras", children: [
2727
- /* @__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 }),
2728
3556
  children
2729
3557
  ] });
2730
3558
  };
2731
- 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 } };
2732
3560
  }
2733
3561
 
2734
3562
  // src/extensions/standard.ts
2735
3563
  function standard() {
2736
- 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()];
2737
3565
  }
2738
3566
 
2739
3567
  // src/react/context.tsx
2740
- import { jsx as jsx16 } from "react/jsx-runtime";
3568
+ import { jsx as jsx24 } from "react/jsx-runtime";
2741
3569
  var defaults;
2742
3570
  var RuntimeContext = createContext2(null);
2743
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect8;
3571
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect9;
2744
3572
  function DocumentProvider({ children, source = "", streaming = false, documentId, ...options }) {
2745
3573
  const generatedId = useId();
2746
- const extensions = useMemo4(() => {
3574
+ const extensions = useMemo5(() => {
2747
3575
  const base = options.extensions ?? (defaults ??= standard());
2748
3576
  return options.plugins?.length ? [...base, ...options.plugins] : base;
2749
3577
  }, [options.extensions, options.plugins]);
2750
3578
  const config = { ...options, extensions };
2751
- const [runtime] = useState8(() => new ReaderRuntime(documentId ?? `limr-${generatedId.replace(/[^a-z\d_-]/gi, "")}`, source, streaming, config));
2752
- 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 });
2753
3581
  useIsomorphicLayoutEffect(() => {
2754
3582
  const old = previous.current;
2755
3583
  const changed = Object.keys({ ...old.config, ...config }).some((key) => old.config[key] !== config[key]);
@@ -2757,11 +3585,11 @@ function DocumentProvider({ children, source = "", streaming = false, documentId
2757
3585
  if (old.source !== source || old.streaming !== streaming) runtime.document.update(source, { streaming });
2758
3586
  previous.current = { source, streaming, config };
2759
3587
  });
2760
- useEffect8(() => {
3588
+ useEffect9(() => {
2761
3589
  runtime.start();
2762
3590
  return () => runtime.dispose();
2763
3591
  }, [runtime]);
2764
- return /* @__PURE__ */ jsx16(RuntimeContext.Provider, { value: runtime, children });
3592
+ return /* @__PURE__ */ jsx24(RuntimeContext.Provider, { value: runtime, children });
2765
3593
  }
2766
3594
  function useRuntime() {
2767
3595
  const runtime = useContext3(RuntimeContext);
@@ -2780,14 +3608,14 @@ function useRendererParameter(name) {
2780
3608
  });
2781
3609
  }
2782
3610
  function useStoreSelector(store, select) {
2783
- const selectRef = useRef11(select);
3611
+ const selectRef = useRef14(select);
2784
3612
  selectRef.current = select;
2785
- const cache2 = useRef11(void 0);
2786
- const get = useCallback2(() => {
3613
+ const cache3 = useRef14(void 0);
3614
+ const get = useCallback3(() => {
2787
3615
  const state = store.get();
2788
- 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;
2789
3617
  const value = selectRef.current(state);
2790
- cache2.current = { state, select: selectRef.current, value };
3618
+ cache3.current = { state, select: selectRef.current, value };
2791
3619
  return value;
2792
3620
  }, [store]);
2793
3621
  return useSyncExternalStore(store.subscribe, get, get);
@@ -2798,7 +3626,7 @@ function useDocument() {
2798
3626
  }
2799
3627
  function useDocumentSelector(select) {
2800
3628
  const runtime = useRuntime();
2801
- 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]);
2802
3630
  return useStoreSelector(store, select);
2803
3631
  }
2804
3632
  function useSelection() {
@@ -2820,7 +3648,7 @@ function useAnnotation(id) {
2820
3648
  }
2821
3649
  function useAnnotations() {
2822
3650
  const state = useStoreSelector(useRuntime().decorations, (state2) => state2);
2823
- 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]);
2824
3652
  }
2825
3653
  function useAnnotationState() {
2826
3654
  const runtime = useRuntime();
@@ -2837,7 +3665,7 @@ function useAnchor(id) {
2837
3665
  }
2838
3666
  function useLiMR() {
2839
3667
  const runtime = useRuntime();
2840
- return useMemo4(() => ({
3668
+ return useMemo5(() => ({
2841
3669
  ...runtime.commands,
2842
3670
  document: runtime.document,
2843
3671
  get signal() {
@@ -2851,14 +3679,14 @@ function useLiMR() {
2851
3679
  }
2852
3680
 
2853
3681
  // src/components/CopyButton.tsx
2854
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
3682
+ import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
2855
3683
  function CopyButton({ value = "", onCopy, label, variant = "plain", className = "" }) {
2856
3684
  const locale = useLocale();
2857
- const [status, setStatus] = useState9("idle");
2858
- const timer = useRef12(void 0);
2859
- const mounted = useRef12(true);
3685
+ const [status, setStatus] = useState10("idle");
3686
+ const timer = useRef15(void 0);
3687
+ const mounted = useRef15(true);
2860
3688
  const title = status === "copied" ? locale.copied : status === "error" ? locale.copyFailed : label ?? locale.copy;
2861
- useEffect9(() => {
3689
+ useEffect10(() => {
2862
3690
  mounted.current = true;
2863
3691
  return () => {
2864
3692
  mounted.current = false;
@@ -2878,7 +3706,7 @@ function CopyButton({ value = "", onCopy, label, variant = "plain", className =
2878
3706
  clearTimeout(timer.current);
2879
3707
  timer.current = setTimeout(() => setStatus("idle"), 2200);
2880
3708
  };
2881
- return /* @__PURE__ */ jsxs13(
3709
+ return /* @__PURE__ */ jsxs17(
2882
3710
  "button",
2883
3711
  {
2884
3712
  type: "button",
@@ -2890,21 +3718,21 @@ function CopyButton({ value = "", onCopy, label, variant = "plain", className =
2890
3718
  title,
2891
3719
  onClick: copy,
2892
3720
  children: [
2893
- /* @__PURE__ */ jsxs13("svg", { className: "limr-copy-glyph", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
2894
- /* @__PURE__ */ jsxs13("g", { className: "limr-copy-sheets", children: [
2895
- /* @__PURE__ */ jsx17("rect", { x: "8.5", y: "8", width: "10.5", height: "12", rx: "3" }),
2896
- /* @__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" })
2897
3725
  ] }),
2898
- /* @__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" })
2899
3727
  ] }),
2900
- /* @__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 : "" })
2901
3729
  ]
2902
3730
  }
2903
3731
  );
2904
3732
  }
2905
3733
 
2906
3734
  // src/extensions/math.tsx
2907
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
3735
+ import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
2908
3736
  var loaded;
2909
3737
  var loading;
2910
3738
  var load = () => loading ??= import("./math-renderer-L6DOH4RX.js").then((module) => {
@@ -2918,74 +3746,21 @@ async function preloadMath() {
2918
3746
  function math(options = {}) {
2919
3747
  function MathNode({ node }) {
2920
3748
  const display = node.type === "math";
2921
- 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 });
2922
3750
  const Renderer = loaded ?? MathContent;
2923
- const content = /* @__PURE__ */ jsx18(Suspense, { fallback, children: /* @__PURE__ */ jsx18(Renderer, { value: node.value ?? "", display, options }) });
2924
- 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: [
2925
3753
  content,
2926
- /* @__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: `$$
2927
3755
  ${node.value ?? ""}
2928
3756
  $$` })
2929
3757
  ] }) : content;
2930
3758
  }
2931
- return { name: "math", micromark: [syntax2()], mdast: [mathFromMarkdown()], scope: "local", components: { math: MathNode, inlineMath: MathNode } };
2932
- }
2933
-
2934
- // src/components/ToolIcon.tsx
2935
- import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
2936
- function ToolIcon({ name, ...props }) {
2937
- 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: [
2938
- (name === "eye" || name === "eye-off") && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2939
- /* @__PURE__ */ jsx19("path", { d: "M3 12s3.3-6 9-6 9 6 9 6-3.3 6-9 6-9-6-9-6Z" }),
2940
- /* @__PURE__ */ jsx19("circle", { cx: "12", cy: "12", r: "2.7" }),
2941
- name === "eye-off" && /* @__PURE__ */ jsx19("path", { d: "m4 4 16 16" })
2942
- ] }),
2943
- name === "question" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2944
- /* @__PURE__ */ jsx19("circle", { cx: "12", cy: "12", r: "8.5" }),
2945
- /* @__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" })
2946
- ] }),
2947
- name === "highlight" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2948
- /* @__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" }),
2949
- /* @__PURE__ */ jsx19("path", { d: "m8 15-2 3 3 1 3-1M4 21h10M14 8l4 3.5" })
2950
- ] }),
2951
- 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" }) }),
2952
- name === "close" && /* @__PURE__ */ jsx19("path", { d: "m6 6 12 12M18 6 6 18" }),
2953
- name === "copy" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2954
- /* @__PURE__ */ jsx19("rect", { x: "8.5", y: "8", width: "10.5", height: "12", rx: "3" }),
2955
- /* @__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" })
2956
- ] }),
2957
- ["background", "underline", "dotted", "wavy"].includes(name) && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2958
- name === "background" && /* @__PURE__ */ jsx19("rect", { x: "3", y: "3", width: "18", height: "18", rx: "5", fill: "currentColor", fillOpacity: ".13", stroke: "none" }),
2959
- /* @__PURE__ */ jsx19("path", { d: "m8 15 3-8a1.05 1.05 0 0 1 2 0l3 8M9.2 12h5.6" }),
2960
- name === "background" && /* @__PURE__ */ jsx19("path", { d: "M7 19h10", strokeWidth: "2.5" }),
2961
- name === "underline" && /* @__PURE__ */ jsx19("path", { d: "M5 20h14" }),
2962
- name === "dotted" && /* @__PURE__ */ jsx19("path", { d: "M5 20h14", strokeDasharray: ".1 3.4", strokeWidth: "2.4" }),
2963
- 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" })
2964
- ] }),
2965
- name === "chevron" && /* @__PURE__ */ jsx19("path", { d: "m8 10 4 4 4-4" }),
2966
- name === "check" && /* @__PURE__ */ jsx19("path", { d: "m5.5 12 4.2 4.2 8.8-9" }),
2967
- 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" }) }),
2968
- name === "note" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2969
- /* @__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" }),
2970
- /* @__PURE__ */ jsx19("path", { d: "M14 3v4a2 2 0 0 0 2 2h4M8 13h8M8 17h5" })
2971
- ] }),
2972
- name === "comment" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2973
- /* @__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" }),
2974
- /* @__PURE__ */ jsx19("path", { d: "M8 9h8M8 13h5" })
2975
- ] }),
2976
- name === "footnote" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2977
- /* @__PURE__ */ jsx19("path", { d: "M5 7h9M5 11h12M5 15h8M5 20h14M18 4h2v5" }),
2978
- /* @__PURE__ */ jsx19("path", { d: "M17.5 9h4" })
2979
- ] }),
2980
- name === "sparkles" && /* @__PURE__ */ jsxs15(Fragment6, { children: [
2981
- /* @__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" }),
2982
- /* @__PURE__ */ jsx19("path", { d: "m19 15 .8 2.2L22 18l-2.2.8L19 21l-.8-2.2L16 18l2.2-.8L19 15ZM19 3v4M17 5h4" })
2983
- ] })
2984
- ] }) });
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") };
2985
3760
  }
2986
3761
 
2987
3762
  // src/extensions/selection-actions.tsx
2988
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
3763
+ import { jsx as jsx27, jsxs as jsxs19 } from "react/jsx-runtime";
2989
3764
  var highlightColors = [
2990
3765
  { color: "#E57373", label: "Rose", labelZh: "\u73AB\u7470" },
2991
3766
  { color: "#F2C744", label: "Gold", labelZh: "\u6696\u9EC4" },
@@ -2999,9 +3774,9 @@ function copyText(options = {}) {
2999
3774
  return {
3000
3775
  id: `copy-${options.format ?? "text"}`,
3001
3776
  label: options.label ?? ((context) => context.locale.copy),
3002
- icon: /* @__PURE__ */ jsx20(ToolIcon, { name: "copy" }),
3777
+ icon: /* @__PURE__ */ jsx27(ToolIcon, { name: "copy" }),
3003
3778
  successLabel: (context) => context.locale.copied,
3004
- successIcon: /* @__PURE__ */ jsx20(ToolIcon, { name: "check" }),
3779
+ successIcon: /* @__PURE__ */ jsx27(ToolIcon, { name: "check" }),
3005
3780
  run: async ({ selection }) => {
3006
3781
  const format = options.format ?? "text";
3007
3782
  await navigator.clipboard.writeText(format === "text" ? selection.text : selection[format].sourceText);
@@ -3023,17 +3798,17 @@ function HighlightSettings({ context, options }) {
3023
3798
  context.setHighlightAppearance(appearance);
3024
3799
  if (highlight) void context.run((current) => current.updateAnnotation(highlight.id, { data: appearance }));
3025
3800
  };
3026
- return /* @__PURE__ */ jsxs16("div", { className: "limr-highlight-settings", children: [
3027
- /* @__PURE__ */ jsxs16("fieldset", { disabled: context.busy, children: [
3028
- /* @__PURE__ */ jsx20("legend", { children: locale.highlightStyle }),
3029
- /* @__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: [
3030
- /* @__PURE__ */ jsx20(ToolIcon, { name: value }),
3031
- /* @__PURE__ */ jsx20("span", { className: "limr-highlight-style-label", children: /* @__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 }) })
3032
3807
  ] }, value)) })
3033
3808
  ] }),
3034
- /* @__PURE__ */ jsxs16("fieldset", { disabled: context.busy, children: [
3035
- /* @__PURE__ */ jsx20("legend", { children: locale.highlightColor }),
3036
- /* @__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)) })
3037
3812
  ] })
3038
3813
  ] });
3039
3814
  }
@@ -3041,17 +3816,17 @@ function highlightPalette(options = {}) {
3041
3816
  return {
3042
3817
  id: "highlight-palette",
3043
3818
  label: (context) => context.locale.editHighlight,
3044
- icon: /* @__PURE__ */ jsx20(ToolIcon, { name: "background" }),
3819
+ icon: /* @__PURE__ */ jsx27(ToolIcon, { name: "background" }),
3045
3820
  placement: "start",
3046
- 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 } }),
3047
- 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 })
3048
3823
  };
3049
3824
  }
3050
3825
  function highlightSelection() {
3051
3826
  return {
3052
3827
  id: "highlight",
3053
3828
  label: (context) => !context.highlight ? context.locale.addHighlight : context.highlight.data?.hidden ? context.locale.showHighlight : context.locale.hideHighlight,
3054
- 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" }),
3055
3830
  visible: (context) => !context.highlight || context.annotations.some((annotation) => annotation.type !== "highlight"),
3056
3831
  run: (context) => {
3057
3832
  if (context.highlight) context.updateAnnotation(context.highlight.id, { data: { ...context.highlight.data, hidden: !context.highlight.data?.hidden } });
@@ -3066,7 +3841,7 @@ function deleteAnnotations() {
3066
3841
  return {
3067
3842
  id: "delete-annotations",
3068
3843
  label: (context) => context.locale.deleteAnnotations,
3069
- icon: /* @__PURE__ */ jsx20(ToolIcon, { name: "trash" }),
3844
+ icon: /* @__PURE__ */ jsx27(ToolIcon, { name: "trash" }),
3070
3845
  placement: "end",
3071
3846
  tone: "danger",
3072
3847
  visible: (context) => context.annotations.length > 0,
@@ -3086,7 +3861,6 @@ export {
3086
3861
  preloadMath,
3087
3862
  math,
3088
3863
  codeHighlight,
3089
- sourceRangeStartRect,
3090
3864
  Markdown,
3091
3865
  OverlayScrollArea,
3092
3866
  AnnotationCard,
@@ -3095,9 +3869,15 @@ export {
3095
3869
  InlineMarker,
3096
3870
  Collapse,
3097
3871
  translations,
3872
+ ToolIcon,
3873
+ mermaid,
3098
3874
  defineBlock,
3099
3875
  defineAnnotation,
3100
3876
  defineExtension,
3877
+ calloutType,
3878
+ callout,
3879
+ inlineHighlights,
3880
+ footnotes,
3101
3881
  video,
3102
3882
  figures,
3103
3883
  definition,
@@ -3120,11 +3900,10 @@ export {
3120
3900
  useAnnotationState,
3121
3901
  useAnchor,
3122
3902
  useLiMR,
3123
- ToolIcon,
3124
3903
  highlightColors,
3125
3904
  copyText,
3126
3905
  highlightPalette,
3127
3906
  highlightSelection,
3128
3907
  deleteAnnotations
3129
3908
  };
3130
- //# sourceMappingURL=chunk-3ILUZKDB.js.map
3909
+ //# sourceMappingURL=chunk-SUAAUGAX.js.map