@incremark/react 0.3.0 → 0.3.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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ParserOptions, AnimationEffect, TransformerPlugin, ParsedBlock, Root, IncrementalUpdate, IncremarkParser, SourceBlock, TransformerOptions, DisplayBlock, BlockTransformer } from '@incremark/core';
1
+ import { IncremarkParserOptions, AnimationEffect, TransformerPlugin, ParsedBlock, Root, IncrementalUpdate, IncremarkParser, SourceBlock, TransformerOptions, DisplayBlock, BlockTransformer } from '@incremark/core';
2
2
  export { AnimationEffect, BlockTransformer, DisplayBlock, IncrementalUpdate, ParsedBlock, ParserOptions, Root, RootContent, SourceBlock, TransformerOptions, TransformerPlugin, TransformerState, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin } from '@incremark/core';
3
3
  import React$1, { ReactNode, ComponentType } from 'react';
4
4
  import { Definition, FootnoteDefinition, RootContent, PhrasingContent } from 'mdast';
@@ -110,7 +110,7 @@ interface TypewriterOptions {
110
110
  /** 自定义插件 */
111
111
  plugins?: TransformerPlugin[];
112
112
  }
113
- interface UseIncremarkOptions extends ParserOptions {
113
+ interface UseIncremarkOptions extends IncremarkParserOptions {
114
114
  /** 打字机配置,传入即创建 transformer(可通过 enabled 控制是否启用) */
115
115
  typewriter?: TypewriterOptions;
116
116
  }
@@ -199,6 +199,8 @@ declare function useIncremark(options?: UseIncremarkOptions): {
199
199
  reset: () => void;
200
200
  /** 一次性渲染(reset + append + finalize) */
201
201
  render: (content: string) => IncrementalUpdate;
202
+ /** 更新解析器配置(动态更新,不需要重建实例) */
203
+ updateOptions: (newOptions: Partial<IncremarkParserOptions>) => void;
202
204
  /** 解析器实例 */
203
205
  parser: IncremarkParser;
204
206
  /** 打字机控制 */
package/dist/index.js CHANGED
@@ -389,6 +389,19 @@ function useIncremark(options = {}) {
389
389
  },
390
390
  [parser, setFootnoteReferenceOrder]
391
391
  );
392
+ const updateOptions = useCallback3(
393
+ (newOptions) => {
394
+ parser.updateOptions(newOptions);
395
+ setCompletedBlocks([]);
396
+ setPendingBlocks([]);
397
+ setMarkdown("");
398
+ setIsLoading(false);
399
+ setIsFinalized(false);
400
+ setFootnoteReferenceOrder([]);
401
+ transformer?.reset();
402
+ },
403
+ [parser, transformer, setFootnoteReferenceOrder]
404
+ );
392
405
  return {
393
406
  /** 已收集的完整 Markdown 字符串 */
394
407
  markdown,
@@ -421,6 +434,8 @@ function useIncremark(options = {}) {
421
434
  reset,
422
435
  /** 一次性渲染(reset + append + finalize) */
423
436
  render,
437
+ /** 更新解析器配置(动态更新,不需要重建实例) */
438
+ updateOptions,
424
439
  /** 解析器实例 */
425
440
  parser,
426
441
  /** 打字机控制 */
@@ -575,7 +590,7 @@ function useLocale() {
575
590
  }
576
591
 
577
592
  // src/components/IncremarkInline.tsx
578
- import React3 from "react";
593
+ import React4 from "react";
579
594
  import {
580
595
  hasChunks,
581
596
  getStableText,
@@ -734,8 +749,73 @@ var IncremarkHtmlElement = ({ node }) => {
734
749
  return /* @__PURE__ */ jsx3(Tag, { ...reactProps, className: `incremark-html-element incremark-${tagName} ${reactProps.className || ""}`, children: renderChildren(children) });
735
750
  };
736
751
 
752
+ // src/components/IncremarkMath.tsx
753
+ import { useState as useState5, useEffect as useEffect4, useRef as useRef5, useCallback as useCallback5 } from "react";
754
+ import { jsx as jsx4 } from "react/jsx-runtime";
755
+ var IncremarkMath = ({
756
+ node,
757
+ renderDelay = 300
758
+ }) => {
759
+ const [renderedHtml, setRenderedHtml] = useState5("");
760
+ const [isLoading, setIsLoading] = useState5(false);
761
+ const katexRef = useRef5(null);
762
+ const renderTimerRef = useRef5(null);
763
+ const isInline = node.type === "inlineMath";
764
+ const formula = node.value;
765
+ const doRender = useCallback5(async () => {
766
+ if (!formula) return;
767
+ try {
768
+ if (!katexRef.current) {
769
+ const katexModule = await import("katex");
770
+ katexRef.current = katexModule.default;
771
+ }
772
+ const katex = katexRef.current;
773
+ const html = katex.renderToString(formula, {
774
+ displayMode: !isInline,
775
+ throwOnError: false,
776
+ strict: false
777
+ });
778
+ setRenderedHtml(html);
779
+ } catch {
780
+ setRenderedHtml("");
781
+ } finally {
782
+ setIsLoading(false);
783
+ }
784
+ }, [formula, isInline]);
785
+ const scheduleRender = useCallback5(() => {
786
+ if (!formula) {
787
+ setRenderedHtml("");
788
+ return;
789
+ }
790
+ if (renderTimerRef.current) {
791
+ clearTimeout(renderTimerRef.current);
792
+ }
793
+ setIsLoading(true);
794
+ renderTimerRef.current = setTimeout(() => {
795
+ doRender();
796
+ }, renderDelay);
797
+ }, [formula, renderDelay, doRender]);
798
+ useEffect4(() => {
799
+ scheduleRender();
800
+ }, [scheduleRender]);
801
+ useEffect4(() => {
802
+ return () => {
803
+ if (renderTimerRef.current) {
804
+ clearTimeout(renderTimerRef.current);
805
+ }
806
+ };
807
+ }, []);
808
+ if (isInline) {
809
+ return /* @__PURE__ */ jsx4("span", { className: "incremark-math-inline", children: renderedHtml && !isLoading ? /* @__PURE__ */ jsx4("span", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx4("code", { className: "math-source", children: formula }) });
810
+ }
811
+ return /* @__PURE__ */ jsx4("div", { className: "incremark-math-block", children: renderedHtml && !isLoading ? /* @__PURE__ */ jsx4("div", { className: "math-rendered", dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx4("pre", { className: "math-source-block", children: /* @__PURE__ */ jsx4("code", { children: formula }) }) });
812
+ };
813
+
737
814
  // src/components/IncremarkInline.tsx
738
- import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime";
815
+ import { Fragment, jsx as jsx5, jsxs } from "react/jsx-runtime";
816
+ function isInlineMath(node) {
817
+ return node.type === "inlineMath";
818
+ }
739
819
  function isHtmlElementNode(node) {
740
820
  return node.type === "htmlElement";
741
821
  }
@@ -748,22 +828,25 @@ function isLinkReference(node) {
748
828
  var IncremarkInline = ({ nodes }) => {
749
829
  if (!nodes || nodes.length === 0) return null;
750
830
  const { definitions, footnoteDefinitions } = useDefinitions();
751
- return /* @__PURE__ */ jsx4(Fragment, { children: nodes.map((node, i) => {
831
+ return /* @__PURE__ */ jsx5(Fragment, { children: nodes.map((node, i) => {
752
832
  if (node.type === "text") {
753
833
  const textNode = node;
754
834
  if (hasChunks(node) && textNode.chunks && textNode.chunks.length > 0) {
755
- return /* @__PURE__ */ jsxs(React3.Fragment, { children: [
835
+ return /* @__PURE__ */ jsxs(React4.Fragment, { children: [
756
836
  getStableText(textNode),
757
- textNode.chunks.map((chunk) => /* @__PURE__ */ jsx4("span", { "data-chunk-key": chunk.createdAt, className: "incremark-fade-in", children: chunk.text }, chunk.createdAt))
837
+ textNode.chunks.map((chunk) => /* @__PURE__ */ jsx5("span", { "data-chunk-key": chunk.createdAt, className: "incremark-fade-in", children: chunk.text }, chunk.createdAt))
758
838
  ] }, i);
759
839
  }
760
- return /* @__PURE__ */ jsx4(React3.Fragment, { children: node.value }, i);
840
+ return /* @__PURE__ */ jsx5(React4.Fragment, { children: node.value }, i);
841
+ }
842
+ if (isInlineMath(node)) {
843
+ return /* @__PURE__ */ jsx5(IncremarkMath, { node }, i);
761
844
  }
762
845
  if (isHtmlElementNode(node)) {
763
- return /* @__PURE__ */ jsx4(IncremarkHtmlElement, { node }, i);
846
+ return /* @__PURE__ */ jsx5(IncremarkHtmlElement, { node }, i);
764
847
  }
765
848
  if (isHtmlNode(node)) {
766
- return /* @__PURE__ */ jsx4(
849
+ return /* @__PURE__ */ jsx5(
767
850
  "span",
768
851
  {
769
852
  className: "incremark-inline-html",
@@ -773,20 +856,20 @@ var IncremarkInline = ({ nodes }) => {
773
856
  );
774
857
  }
775
858
  if (node.type === "strong") {
776
- return /* @__PURE__ */ jsx4("strong", { children: /* @__PURE__ */ jsx4(IncremarkInline, { nodes: node.children }) }, i);
859
+ return /* @__PURE__ */ jsx5("strong", { children: /* @__PURE__ */ jsx5(IncremarkInline, { nodes: node.children }) }, i);
777
860
  }
778
861
  if (node.type === "emphasis") {
779
- return /* @__PURE__ */ jsx4("em", { children: /* @__PURE__ */ jsx4(IncremarkInline, { nodes: node.children }) }, i);
862
+ return /* @__PURE__ */ jsx5("em", { children: /* @__PURE__ */ jsx5(IncremarkInline, { nodes: node.children }) }, i);
780
863
  }
781
864
  if (node.type === "inlineCode") {
782
- return /* @__PURE__ */ jsx4("code", { className: "incremark-inline-code", children: node.value }, i);
865
+ return /* @__PURE__ */ jsx5("code", { className: "incremark-inline-code", children: node.value }, i);
783
866
  }
784
867
  if (node.type === "link") {
785
- return /* @__PURE__ */ jsx4("a", { href: node.url, target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsx4(IncremarkInline, { nodes: node.children }) }, i);
868
+ return /* @__PURE__ */ jsx5("a", { href: node.url, target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsx5(IncremarkInline, { nodes: node.children }) }, i);
786
869
  }
787
870
  if (node.type === "image") {
788
871
  const imageNode = node;
789
- return /* @__PURE__ */ jsx4(
872
+ return /* @__PURE__ */ jsx5(
790
873
  "img",
791
874
  {
792
875
  src: imageNode.url,
@@ -800,7 +883,7 @@ var IncremarkInline = ({ nodes }) => {
800
883
  if (isImageReference(node)) {
801
884
  const definition = definitions[node.identifier];
802
885
  if (definition) {
803
- return /* @__PURE__ */ jsx4(
886
+ return /* @__PURE__ */ jsx5(
804
887
  "img",
805
888
  {
806
889
  src: definition.url,
@@ -822,14 +905,14 @@ var IncremarkInline = ({ nodes }) => {
822
905
  if (isLinkReference(node)) {
823
906
  const definition = definitions[node.identifier];
824
907
  if (definition) {
825
- return /* @__PURE__ */ jsx4(
908
+ return /* @__PURE__ */ jsx5(
826
909
  "a",
827
910
  {
828
911
  href: definition.url,
829
912
  title: definition.title || void 0,
830
913
  target: "_blank",
831
914
  rel: "noopener noreferrer",
832
- children: /* @__PURE__ */ jsx4(IncremarkInline, { nodes: node.children })
915
+ children: /* @__PURE__ */ jsx5(IncremarkInline, { nodes: node.children })
833
916
  },
834
917
  i
835
918
  );
@@ -845,47 +928,47 @@ var IncremarkInline = ({ nodes }) => {
845
928
  if (node.type === "footnoteReference") {
846
929
  const footnoteRef = node;
847
930
  const hasDefinition = footnoteDefinitions[footnoteRef.identifier];
848
- return /* @__PURE__ */ jsx4("sup", { className: "incremark-footnote-ref", children: /* @__PURE__ */ jsx4("a", { href: `#fn-${footnoteRef.identifier}`, id: `fnref-${footnoteRef.identifier}`, children: hasDefinition ? `[${footnoteRef.identifier}]` : `[^${footnoteRef.identifier}]` }) }, i);
931
+ return /* @__PURE__ */ jsx5("sup", { className: "incremark-footnote-ref", children: /* @__PURE__ */ jsx5("a", { href: `#fn-${footnoteRef.identifier}`, id: `fnref-${footnoteRef.identifier}`, children: hasDefinition ? `[${footnoteRef.identifier}]` : `[^${footnoteRef.identifier}]` }) }, i);
849
932
  }
850
933
  if (node.type === "break") {
851
- return /* @__PURE__ */ jsx4("br", {}, i);
934
+ return /* @__PURE__ */ jsx5("br", {}, i);
852
935
  }
853
936
  if (node.type === "delete") {
854
- return /* @__PURE__ */ jsx4("del", { children: /* @__PURE__ */ jsx4(IncremarkInline, { nodes: node.children }) }, i);
937
+ return /* @__PURE__ */ jsx5("del", { children: /* @__PURE__ */ jsx5(IncremarkInline, { nodes: node.children }) }, i);
855
938
  }
856
- return /* @__PURE__ */ jsx4("span", { children: node.value || "" }, i);
939
+ return /* @__PURE__ */ jsx5("span", { children: node.value || "" }, i);
857
940
  }) });
858
941
  };
859
942
 
860
943
  // src/components/IncremarkHeading.tsx
861
- import { jsx as jsx5 } from "react/jsx-runtime";
944
+ import { jsx as jsx6 } from "react/jsx-runtime";
862
945
  var IncremarkHeading = ({ node }) => {
863
946
  const Tag = `h${node.depth}`;
864
- return /* @__PURE__ */ jsx5(Tag, { className: `incremark-heading h${node.depth}`, children: /* @__PURE__ */ jsx5(IncremarkInline, { nodes: node.children }) });
947
+ return /* @__PURE__ */ jsx6(Tag, { className: `incremark-heading h${node.depth}`, children: /* @__PURE__ */ jsx6(IncremarkInline, { nodes: node.children }) });
865
948
  };
866
949
 
867
950
  // src/components/IncremarkParagraph.tsx
868
- import { jsx as jsx6 } from "react/jsx-runtime";
951
+ import { jsx as jsx7 } from "react/jsx-runtime";
869
952
  var IncremarkParagraph = ({ node }) => {
870
- return /* @__PURE__ */ jsx6("p", { className: "incremark-paragraph", children: /* @__PURE__ */ jsx6(IncremarkInline, { nodes: node.children }) });
953
+ return /* @__PURE__ */ jsx7("p", { className: "incremark-paragraph", children: /* @__PURE__ */ jsx7(IncremarkInline, { nodes: node.children }) });
871
954
  };
872
955
 
873
956
  // src/components/IncremarkCode.tsx
874
- import React7 from "react";
957
+ import React8 from "react";
875
958
 
876
959
  // src/components/IncremarkCodeMermaid.tsx
877
- import { useState as useState5, useEffect as useEffect4, useRef as useRef5, useCallback as useCallback5 } from "react";
960
+ import { useState as useState6, useEffect as useEffect5, useRef as useRef6, useCallback as useCallback6 } from "react";
878
961
  import { GravityMermaid, LucideCode, LucideEye, LucideCopy, LucideCopyCheck } from "@incremark/icons";
879
962
  import { isClipboardAvailable } from "@incremark/shared";
880
963
 
881
964
  // src/components/SvgIcon.tsx
882
- import { jsx as jsx7 } from "react/jsx-runtime";
965
+ import { jsx as jsx8 } from "react/jsx-runtime";
883
966
  var SvgIcon = ({
884
967
  svg,
885
968
  sizeClass,
886
969
  className
887
970
  }) => {
888
- return /* @__PURE__ */ jsx7(
971
+ return /* @__PURE__ */ jsx8(
889
972
  "span",
890
973
  {
891
974
  className: `incremark-icon ${sizeClass || ""} ${className || ""}`.trim(),
@@ -896,24 +979,24 @@ var SvgIcon = ({
896
979
  };
897
980
 
898
981
  // src/components/IncremarkCodeMermaid.tsx
899
- import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
982
+ import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
900
983
  var IncremarkCodeMermaid = ({
901
984
  node,
902
985
  mermaidDelay = 500
903
986
  }) => {
904
- const [copied, setCopied] = useState5(false);
987
+ const [copied, setCopied] = useState6(false);
905
988
  const { t } = useLocale();
906
- const [mermaidSvg, setMermaidSvg] = useState5("");
907
- const [mermaidLoading, setMermaidLoading] = useState5(false);
908
- const [mermaidViewMode, setMermaidViewMode] = useState5("preview");
909
- const mermaidRef = useRef5(null);
910
- const mermaidTimerRef = useRef5(null);
911
- const copyTimeoutRef = useRef5(null);
989
+ const [mermaidSvg, setMermaidSvg] = useState6("");
990
+ const [mermaidLoading, setMermaidLoading] = useState6(false);
991
+ const [mermaidViewMode, setMermaidViewMode] = useState6("preview");
992
+ const mermaidRef = useRef6(null);
993
+ const mermaidTimerRef = useRef6(null);
994
+ const copyTimeoutRef = useRef6(null);
912
995
  const code = node.value;
913
- const toggleMermaidView = useCallback5(() => {
996
+ const toggleMermaidView = useCallback6(() => {
914
997
  setMermaidViewMode((prev) => prev === "preview" ? "source" : "preview");
915
998
  }, []);
916
- const copyCode = useCallback5(async () => {
999
+ const copyCode = useCallback6(async () => {
917
1000
  if (!isClipboardAvailable()) return;
918
1001
  try {
919
1002
  await navigator.clipboard.writeText(code);
@@ -925,7 +1008,7 @@ var IncremarkCodeMermaid = ({
925
1008
  } catch {
926
1009
  }
927
1010
  }, [code]);
928
- const doRenderMermaid = useCallback5(async () => {
1011
+ const doRenderMermaid = useCallback6(async () => {
929
1012
  if (!code) return;
930
1013
  try {
931
1014
  if (!mermaidRef.current) {
@@ -948,7 +1031,7 @@ var IncremarkCodeMermaid = ({
948
1031
  setMermaidLoading(false);
949
1032
  }
950
1033
  }, [code]);
951
- const scheduleRenderMermaid = useCallback5(() => {
1034
+ const scheduleRenderMermaid = useCallback6(() => {
952
1035
  if (!code) return;
953
1036
  if (mermaidTimerRef.current) {
954
1037
  clearTimeout(mermaidTimerRef.current);
@@ -958,10 +1041,10 @@ var IncremarkCodeMermaid = ({
958
1041
  doRenderMermaid();
959
1042
  }, mermaidDelay);
960
1043
  }, [code, mermaidDelay, doRenderMermaid]);
961
- useEffect4(() => {
1044
+ useEffect5(() => {
962
1045
  scheduleRenderMermaid();
963
1046
  }, [scheduleRenderMermaid]);
964
- useEffect4(() => {
1047
+ useEffect5(() => {
965
1048
  return () => {
966
1049
  if (mermaidTimerRef.current) {
967
1050
  clearTimeout(mermaidTimerRef.current);
@@ -974,11 +1057,11 @@ var IncremarkCodeMermaid = ({
974
1057
  return /* @__PURE__ */ jsxs2("div", { className: "incremark-mermaid", children: [
975
1058
  /* @__PURE__ */ jsxs2("div", { className: "mermaid-header", children: [
976
1059
  /* @__PURE__ */ jsxs2("span", { className: "language", children: [
977
- /* @__PURE__ */ jsx8(SvgIcon, { svg: GravityMermaid, className: "language-icon" }),
1060
+ /* @__PURE__ */ jsx9(SvgIcon, { svg: GravityMermaid, className: "language-icon" }),
978
1061
  "MERMAID"
979
1062
  ] }),
980
1063
  /* @__PURE__ */ jsxs2("div", { className: "mermaid-actions", children: [
981
- /* @__PURE__ */ jsx8(
1064
+ /* @__PURE__ */ jsx9(
982
1065
  "button",
983
1066
  {
984
1067
  className: "code-btn",
@@ -987,10 +1070,10 @@ var IncremarkCodeMermaid = ({
987
1070
  disabled: !mermaidSvg,
988
1071
  "aria-label": mermaidViewMode === "preview" ? t("mermaid.viewSource") : t("mermaid.preview"),
989
1072
  title: mermaidViewMode === "preview" ? "View Source" : "Preview",
990
- children: /* @__PURE__ */ jsx8(SvgIcon, { svg: mermaidViewMode === "preview" ? LucideCode : LucideEye })
1073
+ children: /* @__PURE__ */ jsx9(SvgIcon, { svg: mermaidViewMode === "preview" ? LucideCode : LucideEye })
991
1074
  }
992
1075
  ),
993
- /* @__PURE__ */ jsx8(
1076
+ /* @__PURE__ */ jsx9(
994
1077
  "button",
995
1078
  {
996
1079
  className: "code-btn",
@@ -998,22 +1081,22 @@ var IncremarkCodeMermaid = ({
998
1081
  type: "button",
999
1082
  "aria-label": copied ? t("mermaid.copied") : t("mermaid.copy"),
1000
1083
  title: copied ? "Copied!" : "Copy",
1001
- children: /* @__PURE__ */ jsx8(SvgIcon, { svg: copied ? LucideCopyCheck : LucideCopy })
1084
+ children: /* @__PURE__ */ jsx9(SvgIcon, { svg: copied ? LucideCopyCheck : LucideCopy })
1002
1085
  }
1003
1086
  )
1004
1087
  ] })
1005
1088
  ] }),
1006
- /* @__PURE__ */ jsx8("div", { className: "mermaid-content", children: mermaidLoading && !mermaidSvg ? /* @__PURE__ */ jsx8("div", { className: "mermaid-loading", children: /* @__PURE__ */ jsx8("pre", { className: "mermaid-source-code", children: code }) }) : mermaidViewMode === "source" ? /* @__PURE__ */ jsx8("pre", { className: "mermaid-source-code", children: code }) : mermaidSvg ? /* @__PURE__ */ jsx8("div", { className: "mermaid-svg", dangerouslySetInnerHTML: { __html: mermaidSvg } }) : /* @__PURE__ */ jsx8("pre", { className: "mermaid-source-code", children: code }) })
1089
+ /* @__PURE__ */ jsx9("div", { className: "mermaid-content", children: mermaidLoading && !mermaidSvg ? /* @__PURE__ */ jsx9("div", { className: "mermaid-loading", children: /* @__PURE__ */ jsx9("pre", { className: "mermaid-source-code", children: code }) }) : mermaidViewMode === "source" ? /* @__PURE__ */ jsx9("pre", { className: "mermaid-source-code", children: code }) : mermaidSvg ? /* @__PURE__ */ jsx9("div", { className: "mermaid-svg", dangerouslySetInnerHTML: { __html: mermaidSvg } }) : /* @__PURE__ */ jsx9("pre", { className: "mermaid-source-code", children: code }) })
1007
1090
  ] });
1008
1091
  };
1009
1092
 
1010
1093
  // src/components/IncremarkCodeDefault.tsx
1011
- import { useState as useState6, useEffect as useEffect5, useCallback as useCallback6, useRef as useRef6 } from "react";
1094
+ import { useState as useState7, useEffect as useEffect6, useCallback as useCallback7, useRef as useRef7 } from "react";
1012
1095
  import { LucideCopy as LucideCopy2, LucideCopyCheck as LucideCopyCheck2 } from "@incremark/icons";
1013
1096
  import { isClipboardAvailable as isClipboardAvailable2 } from "@incremark/shared";
1014
1097
 
1015
1098
  // src/hooks/useShiki.ts
1016
- import React5 from "react";
1099
+ import React6 from "react";
1017
1100
  var ShikiManager = class _ShikiManager {
1018
1101
  static instance = null;
1019
1102
  /** 存储 highlighter 实例,key 为主题名称 */
@@ -1113,15 +1196,15 @@ function getShikiManager() {
1113
1196
  return shikiManagerInstance;
1114
1197
  }
1115
1198
  function useShiki(theme) {
1116
- const [isHighlighting, setIsHighlighting] = React5.useState(false);
1117
- const highlighterInfoRef = React5.useRef(null);
1118
- const getHighlighter = React5.useCallback(async () => {
1199
+ const [isHighlighting, setIsHighlighting] = React6.useState(false);
1200
+ const highlighterInfoRef = React6.useRef(null);
1201
+ const getHighlighter = React6.useCallback(async () => {
1119
1202
  if (!highlighterInfoRef.current) {
1120
1203
  highlighterInfoRef.current = await getShikiManager().getHighlighter(theme);
1121
1204
  }
1122
1205
  return highlighterInfoRef.current;
1123
1206
  }, [theme]);
1124
- const highlight = React5.useCallback(async (code, lang, fallbackTheme) => {
1207
+ const highlight = React6.useCallback(async (code, lang, fallbackTheme) => {
1125
1208
  setIsHighlighting(true);
1126
1209
  try {
1127
1210
  const info = await getHighlighter();
@@ -1146,21 +1229,21 @@ function useShiki(theme) {
1146
1229
  }
1147
1230
 
1148
1231
  // src/components/IncremarkCodeDefault.tsx
1149
- import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
1232
+ import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
1150
1233
  var IncremarkCodeDefault = ({
1151
1234
  node,
1152
1235
  theme = "github-dark",
1153
1236
  fallbackTheme = "github-dark",
1154
1237
  disableHighlight = false
1155
1238
  }) => {
1156
- const [copied, setCopied] = useState6(false);
1157
- const [highlightedHtml, setHighlightedHtml] = useState6("");
1239
+ const [copied, setCopied] = useState7(false);
1240
+ const [highlightedHtml, setHighlightedHtml] = useState7("");
1158
1241
  const language = node.lang || "text";
1159
1242
  const code = node.value;
1160
1243
  const { t } = useLocale();
1161
1244
  const { isHighlighting, highlight } = useShiki(theme);
1162
- const copyTimeoutRef = useRef6(null);
1163
- const copyCode = useCallback6(async () => {
1245
+ const copyTimeoutRef = useRef7(null);
1246
+ const copyCode = useCallback7(async () => {
1164
1247
  if (!isClipboardAvailable2()) return;
1165
1248
  try {
1166
1249
  await navigator.clipboard.writeText(code);
@@ -1172,14 +1255,14 @@ var IncremarkCodeDefault = ({
1172
1255
  } catch {
1173
1256
  }
1174
1257
  }, [code]);
1175
- useEffect5(() => {
1258
+ useEffect6(() => {
1176
1259
  return () => {
1177
1260
  if (copyTimeoutRef.current) {
1178
1261
  clearTimeout(copyTimeoutRef.current);
1179
1262
  }
1180
1263
  };
1181
1264
  }, []);
1182
- const doHighlight = useCallback6(async () => {
1265
+ const doHighlight = useCallback7(async () => {
1183
1266
  if (!code || disableHighlight) {
1184
1267
  setHighlightedHtml("");
1185
1268
  return;
@@ -1191,13 +1274,13 @@ var IncremarkCodeDefault = ({
1191
1274
  setHighlightedHtml("");
1192
1275
  }
1193
1276
  }, [code, language, fallbackTheme, disableHighlight, highlight]);
1194
- useEffect5(() => {
1277
+ useEffect6(() => {
1195
1278
  doHighlight();
1196
1279
  }, [doHighlight]);
1197
1280
  return /* @__PURE__ */ jsxs3("div", { className: "incremark-code", children: [
1198
1281
  /* @__PURE__ */ jsxs3("div", { className: "code-header", children: [
1199
- /* @__PURE__ */ jsx9("span", { className: "language", children: language }),
1200
- /* @__PURE__ */ jsx9(
1282
+ /* @__PURE__ */ jsx10("span", { className: "language", children: language }),
1283
+ /* @__PURE__ */ jsx10(
1201
1284
  "button",
1202
1285
  {
1203
1286
  className: "code-btn",
@@ -1205,16 +1288,16 @@ var IncremarkCodeDefault = ({
1205
1288
  type: "button",
1206
1289
  "aria-label": copied ? t("code.copied") : t("code.copy"),
1207
1290
  title: copied ? "Copied!" : "Copy",
1208
- children: /* @__PURE__ */ jsx9(SvgIcon, { svg: copied ? LucideCopyCheck2 : LucideCopy2 })
1291
+ children: /* @__PURE__ */ jsx10(SvgIcon, { svg: copied ? LucideCopyCheck2 : LucideCopy2 })
1209
1292
  }
1210
1293
  )
1211
1294
  ] }),
1212
- /* @__PURE__ */ jsx9("div", { className: "code-content", children: isHighlighting && !highlightedHtml ? /* @__PURE__ */ jsx9("div", { className: "code-loading", children: /* @__PURE__ */ jsx9("pre", { children: /* @__PURE__ */ jsx9("code", { children: code }) }) }) : highlightedHtml ? /* @__PURE__ */ jsx9("div", { className: "shiki-wrapper", dangerouslySetInnerHTML: { __html: highlightedHtml } }) : /* @__PURE__ */ jsx9("pre", { className: "code-fallback", children: /* @__PURE__ */ jsx9("code", { children: code }) }) })
1295
+ /* @__PURE__ */ jsx10("div", { className: "code-content", children: isHighlighting && !highlightedHtml ? /* @__PURE__ */ jsx10("div", { className: "code-loading", children: /* @__PURE__ */ jsx10("pre", { children: /* @__PURE__ */ jsx10("code", { children: code }) }) }) : highlightedHtml ? /* @__PURE__ */ jsx10("div", { className: "shiki-wrapper", dangerouslySetInnerHTML: { __html: highlightedHtml } }) : /* @__PURE__ */ jsx10("pre", { className: "code-fallback", children: /* @__PURE__ */ jsx10("code", { children: code }) }) })
1213
1296
  ] });
1214
1297
  };
1215
1298
 
1216
1299
  // src/components/IncremarkCode.tsx
1217
- import { jsx as jsx10 } from "react/jsx-runtime";
1300
+ import { jsx as jsx11 } from "react/jsx-runtime";
1218
1301
  var IncremarkCode = ({
1219
1302
  node,
1220
1303
  theme = "github-dark",
@@ -1229,7 +1312,7 @@ var IncremarkCode = ({
1229
1312
  const language = node.lang || "text";
1230
1313
  const code = node.value;
1231
1314
  const isMermaid = language === "mermaid";
1232
- const CustomCodeBlock = React7.useMemo(() => {
1315
+ const CustomCodeBlock = React8.useMemo(() => {
1233
1316
  const component = customCodeBlocks?.[language];
1234
1317
  if (!component) return null;
1235
1318
  const config = codeBlockConfigs?.[language];
@@ -1243,7 +1326,7 @@ var IncremarkCode = ({
1243
1326
  }, [customCodeBlocks, language, blockStatus, codeBlockConfigs]);
1244
1327
  if (CustomCodeBlock) {
1245
1328
  const config = codeBlockConfigs?.[language];
1246
- return /* @__PURE__ */ jsx10(
1329
+ return /* @__PURE__ */ jsx11(
1247
1330
  CustomCodeBlock,
1248
1331
  {
1249
1332
  codeStr: code,
@@ -1254,7 +1337,7 @@ var IncremarkCode = ({
1254
1337
  );
1255
1338
  }
1256
1339
  if (isMermaid) {
1257
- return /* @__PURE__ */ jsx10(
1340
+ return /* @__PURE__ */ jsx11(
1258
1341
  IncremarkCodeMermaid,
1259
1342
  {
1260
1343
  node,
@@ -1262,7 +1345,7 @@ var IncremarkCode = ({
1262
1345
  }
1263
1346
  );
1264
1347
  }
1265
- return /* @__PURE__ */ jsx10(
1348
+ return /* @__PURE__ */ jsx11(
1266
1349
  DefaultCodeComponent,
1267
1350
  {
1268
1351
  node,
@@ -1274,8 +1357,8 @@ var IncremarkCode = ({
1274
1357
  };
1275
1358
 
1276
1359
  // src/components/IncremarkList.tsx
1277
- import React8 from "react";
1278
- import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
1360
+ import React9 from "react";
1361
+ import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
1279
1362
  function getItemInlineContent(item) {
1280
1363
  const firstChild = item.children[0];
1281
1364
  if (firstChild?.type === "paragraph") {
@@ -1294,13 +1377,13 @@ function getItemBlockChildren(item) {
1294
1377
  var IncremarkList = ({ node }) => {
1295
1378
  const Tag = node.ordered ? "ol" : "ul";
1296
1379
  const isTaskList = node.children?.some((item) => item.checked !== null && item.checked !== void 0);
1297
- return /* @__PURE__ */ jsx11(Tag, { className: `incremark-list ${isTaskList ? "task-list" : ""}`, start: node.start || void 0, children: node.children?.map((item, index) => {
1380
+ return /* @__PURE__ */ jsx12(Tag, { className: `incremark-list ${isTaskList ? "task-list" : ""}`, start: node.start || void 0, children: node.children?.map((item, index) => {
1298
1381
  const isTaskItem = item.checked !== null && item.checked !== void 0;
1299
1382
  const inlineContent = getItemInlineContent(item);
1300
1383
  const blockChildren = getItemBlockChildren(item);
1301
1384
  if (isTaskItem) {
1302
- return /* @__PURE__ */ jsx11("li", { className: "incremark-list-item task-item", children: /* @__PURE__ */ jsxs4("label", { className: "task-label", children: [
1303
- /* @__PURE__ */ jsx11(
1385
+ return /* @__PURE__ */ jsx12("li", { className: "incremark-list-item task-item", children: /* @__PURE__ */ jsxs4("label", { className: "task-label", children: [
1386
+ /* @__PURE__ */ jsx12(
1304
1387
  "input",
1305
1388
  {
1306
1389
  type: "checkbox",
@@ -1309,43 +1392,43 @@ var IncremarkList = ({ node }) => {
1309
1392
  className: "checkbox"
1310
1393
  }
1311
1394
  ),
1312
- /* @__PURE__ */ jsx11("span", { className: "task-content", children: /* @__PURE__ */ jsx11(IncremarkInline, { nodes: inlineContent }) })
1395
+ /* @__PURE__ */ jsx12("span", { className: "task-content", children: /* @__PURE__ */ jsx12(IncremarkInline, { nodes: inlineContent }) })
1313
1396
  ] }) }, index);
1314
1397
  }
1315
1398
  return /* @__PURE__ */ jsxs4("li", { className: "incremark-list-item", children: [
1316
- /* @__PURE__ */ jsx11(IncremarkInline, { nodes: inlineContent }),
1317
- blockChildren.map((child, childIndex) => /* @__PURE__ */ jsx11(React8.Fragment, { children: /* @__PURE__ */ jsx11(IncremarkRenderer, { node: child }) }, childIndex))
1399
+ /* @__PURE__ */ jsx12(IncremarkInline, { nodes: inlineContent }),
1400
+ blockChildren.map((child, childIndex) => /* @__PURE__ */ jsx12(React9.Fragment, { children: /* @__PURE__ */ jsx12(IncremarkRenderer, { node: child }) }, childIndex))
1318
1401
  ] }, index);
1319
1402
  }) });
1320
1403
  };
1321
1404
 
1322
1405
  // src/components/IncremarkBlockquote.tsx
1323
- import React9 from "react";
1324
- import { jsx as jsx12 } from "react/jsx-runtime";
1406
+ import React10 from "react";
1407
+ import { jsx as jsx13 } from "react/jsx-runtime";
1325
1408
  var IncremarkBlockquote = ({ node }) => {
1326
- return /* @__PURE__ */ jsx12("blockquote", { className: "incremark-blockquote", children: node.children.map((child, index) => /* @__PURE__ */ jsx12(React9.Fragment, { children: /* @__PURE__ */ jsx12(IncremarkRenderer, { node: child }) }, index)) });
1409
+ return /* @__PURE__ */ jsx13("blockquote", { className: "incremark-blockquote", children: node.children.map((child, index) => /* @__PURE__ */ jsx13(React10.Fragment, { children: /* @__PURE__ */ jsx13(IncremarkRenderer, { node: child }) }, index)) });
1327
1410
  };
1328
1411
 
1329
1412
  // src/components/IncremarkTable.tsx
1330
- import { jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
1413
+ import { jsx as jsx14, jsxs as jsxs5 } from "react/jsx-runtime";
1331
1414
  function getCellContent(cell) {
1332
1415
  return cell.children;
1333
1416
  }
1334
1417
  var IncremarkTable = ({ node }) => {
1335
- return /* @__PURE__ */ jsx13("div", { className: "incremark-table-wrapper", children: /* @__PURE__ */ jsxs5("table", { className: "incremark-table", children: [
1336
- /* @__PURE__ */ jsx13("thead", { children: node.children?.[0] && /* @__PURE__ */ jsx13("tr", { children: node.children[0].children.map((cell, cellIndex) => /* @__PURE__ */ jsx13(
1418
+ return /* @__PURE__ */ jsx14("div", { className: "incremark-table-wrapper", children: /* @__PURE__ */ jsxs5("table", { className: "incremark-table", children: [
1419
+ /* @__PURE__ */ jsx14("thead", { children: node.children?.[0] && /* @__PURE__ */ jsx14("tr", { children: node.children[0].children.map((cell, cellIndex) => /* @__PURE__ */ jsx14(
1337
1420
  "th",
1338
1421
  {
1339
1422
  className: `incremark-table-align-${node.align?.[cellIndex] || "left"}`,
1340
- children: /* @__PURE__ */ jsx13(IncremarkInline, { nodes: getCellContent(cell) })
1423
+ children: /* @__PURE__ */ jsx14(IncremarkInline, { nodes: getCellContent(cell) })
1341
1424
  },
1342
1425
  cellIndex
1343
1426
  )) }) }),
1344
- /* @__PURE__ */ jsx13("tbody", { children: node.children?.slice(1).map((row, rowIndex) => /* @__PURE__ */ jsx13("tr", { children: row.children.map((cell, cellIndex) => /* @__PURE__ */ jsx13(
1427
+ /* @__PURE__ */ jsx14("tbody", { children: node.children?.slice(1).map((row, rowIndex) => /* @__PURE__ */ jsx14("tr", { children: row.children.map((cell, cellIndex) => /* @__PURE__ */ jsx14(
1345
1428
  "td",
1346
1429
  {
1347
1430
  className: `incremark-table-align-${node.align?.[cellIndex] || "left"}`,
1348
- children: /* @__PURE__ */ jsx13(IncremarkInline, { nodes: getCellContent(cell) })
1431
+ children: /* @__PURE__ */ jsx14(IncremarkInline, { nodes: getCellContent(cell) })
1349
1432
  },
1350
1433
  cellIndex
1351
1434
  )) }, rowIndex)) })
@@ -1353,71 +1436,9 @@ var IncremarkTable = ({ node }) => {
1353
1436
  };
1354
1437
 
1355
1438
  // src/components/IncremarkThematicBreak.tsx
1356
- import { jsx as jsx14 } from "react/jsx-runtime";
1357
- var IncremarkThematicBreak = () => {
1358
- return /* @__PURE__ */ jsx14("hr", { className: "incremark-hr" });
1359
- };
1360
-
1361
- // src/components/IncremarkMath.tsx
1362
- import { useState as useState7, useEffect as useEffect6, useRef as useRef7, useCallback as useCallback7 } from "react";
1363
1439
  import { jsx as jsx15 } from "react/jsx-runtime";
1364
- var IncremarkMath = ({
1365
- node,
1366
- renderDelay = 300
1367
- }) => {
1368
- const [renderedHtml, setRenderedHtml] = useState7("");
1369
- const [isLoading, setIsLoading] = useState7(false);
1370
- const katexRef = useRef7(null);
1371
- const renderTimerRef = useRef7(null);
1372
- const isInline = node.type === "inlineMath";
1373
- const formula = node.value;
1374
- const doRender = useCallback7(async () => {
1375
- if (!formula) return;
1376
- try {
1377
- if (!katexRef.current) {
1378
- const katexModule = await import("katex");
1379
- katexRef.current = katexModule.default;
1380
- }
1381
- const katex = katexRef.current;
1382
- const html = katex.renderToString(formula, {
1383
- displayMode: !isInline,
1384
- throwOnError: false,
1385
- strict: false
1386
- });
1387
- setRenderedHtml(html);
1388
- } catch {
1389
- setRenderedHtml("");
1390
- } finally {
1391
- setIsLoading(false);
1392
- }
1393
- }, [formula, isInline]);
1394
- const scheduleRender = useCallback7(() => {
1395
- if (!formula) {
1396
- setRenderedHtml("");
1397
- return;
1398
- }
1399
- if (renderTimerRef.current) {
1400
- clearTimeout(renderTimerRef.current);
1401
- }
1402
- setIsLoading(true);
1403
- renderTimerRef.current = setTimeout(() => {
1404
- doRender();
1405
- }, renderDelay);
1406
- }, [formula, renderDelay, doRender]);
1407
- useEffect6(() => {
1408
- scheduleRender();
1409
- }, [scheduleRender]);
1410
- useEffect6(() => {
1411
- return () => {
1412
- if (renderTimerRef.current) {
1413
- clearTimeout(renderTimerRef.current);
1414
- }
1415
- };
1416
- }, []);
1417
- if (isInline) {
1418
- return /* @__PURE__ */ jsx15("span", { className: "incremark-math-inline", children: renderedHtml && !isLoading ? /* @__PURE__ */ jsx15("span", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx15("code", { className: "math-source", children: formula }) });
1419
- }
1420
- return /* @__PURE__ */ jsx15("div", { className: "incremark-math-block", children: renderedHtml && !isLoading ? /* @__PURE__ */ jsx15("div", { className: "math-rendered", dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx15("pre", { className: "math-source-block", children: /* @__PURE__ */ jsx15("code", { children: formula }) }) });
1440
+ var IncremarkThematicBreak = () => {
1441
+ return /* @__PURE__ */ jsx15("hr", { className: "incremark-hr" });
1421
1442
  };
1422
1443
 
1423
1444
  // src/components/IncremarkContainer.tsx
@@ -1680,12 +1701,30 @@ var IncremarkContent = (props) => {
1680
1701
  math: true,
1681
1702
  ...incremarkOptions
1682
1703
  });
1683
- const { blocks, append, finalize, render, reset, isDisplayComplete, markdown, typewriter, _definitionsContextValue } = useIncremark(initialOptionsRef.current);
1704
+ const { blocks, append, finalize, render, reset, updateOptions, isDisplayComplete, markdown, typewriter, _definitionsContextValue } = useIncremark(initialOptionsRef.current);
1705
+ const prevOptionsRef = useRef8(incremarkOptions);
1684
1706
  useEffect7(() => {
1685
- if (incremarkOptions?.typewriter) {
1686
- typewriter.setOptions(incremarkOptions.typewriter);
1707
+ const prev = prevOptionsRef.current;
1708
+ const current = incremarkOptions;
1709
+ if (prev === current) return;
1710
+ prevOptionsRef.current = current;
1711
+ const prevMath = typeof prev?.math === "object" ? JSON.stringify(prev.math) : String(prev?.math ?? true);
1712
+ const currentMath = typeof current?.math === "object" ? JSON.stringify(current.math) : String(current?.math ?? true);
1713
+ const prevAstBuilder = prev?.astBuilder?.name ?? "default";
1714
+ const currentAstBuilder = current?.astBuilder?.name ?? "default";
1715
+ if (prevMath !== currentMath || prev?.gfm !== current?.gfm || prev?.htmlTree !== current?.htmlTree || prev?.containers !== current?.containers || prevAstBuilder !== currentAstBuilder) {
1716
+ updateOptions({
1717
+ gfm: current?.gfm ?? true,
1718
+ math: current?.math ?? true,
1719
+ htmlTree: current?.htmlTree ?? true,
1720
+ containers: current?.containers ?? true,
1721
+ astBuilder: current?.astBuilder
1722
+ });
1723
+ }
1724
+ if (current?.typewriter) {
1725
+ typewriter.setOptions(current.typewriter);
1687
1726
  }
1688
- }, [incremarkOptions?.typewriter, typewriter]);
1727
+ }, [incremarkOptions, updateOptions, typewriter]);
1689
1728
  const isStreamMode = useMemo4(() => typeof stream === "function", [stream]);
1690
1729
  const prevContentRef = useRef8(void 0);
1691
1730
  const isStreamingRef = useRef8(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@incremark/react",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "license": "MIT",
5
5
  "description": "High-performance streaming markdown renderer for React ecosystem.",
6
6
  "type": "module",
@@ -21,14 +21,14 @@
21
21
  "mermaid": "^10.0.0 || ^11.0.0",
22
22
  "katex": "^0.16.0",
23
23
  "react": ">=18.0.0",
24
- "@incremark/core": "0.3.0"
24
+ "@incremark/core": "0.3.1"
25
25
  },
26
26
  "dependencies": {
27
27
  "shiki": "^3.20.0",
28
- "@incremark/devtools": "0.3.0",
29
- "@incremark/icons": "0.3.0",
30
- "@incremark/shared": "0.3.0",
31
- "@incremark/theme": "0.3.0"
28
+ "@incremark/devtools": "0.3.1",
29
+ "@incremark/shared": "0.3.1",
30
+ "@incremark/theme": "0.3.1",
31
+ "@incremark/icons": "0.3.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/mdast": "^4.0.0",