@ai-react-markdown/mantine 2.12.0 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -7
- package/dist/index.cjs +145 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +141 -56
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ yarn add @ai-react-markdown/mantine @ai-react-markdown/core
|
|
|
69
69
|
{
|
|
70
70
|
"react": ">=19",
|
|
71
71
|
"react-dom": ">=19",
|
|
72
|
-
"@ai-react-markdown/core": "^2.
|
|
72
|
+
"@ai-react-markdown/core": "^2.13.0",
|
|
73
73
|
"@mantine/core": "^9.0.0",
|
|
74
74
|
"@mantine/code-highlight": "^9.0.0",
|
|
75
75
|
"highlight.js": "^11.11.2"
|
|
@@ -150,12 +150,13 @@ The Mantine package adds one behavior group on top of core's flat props: the `co
|
|
|
150
150
|
|
|
151
151
|
### `codeBlock` (`Partial<MantineCodeBlockOptions>`)
|
|
152
152
|
|
|
153
|
-
| Field | Type | Default | Description
|
|
154
|
-
| --------------------------- | --------- | ------- |
|
|
155
|
-
| `defaultExpanded` | `boolean` | `true` | Whether code blocks start in their expanded state. When `false`, long blocks are collapsed with an expand button.
|
|
156
|
-
| `autoDetectUnknownLanguage` | `boolean` | `false` | When `true`, uses `highlight.js`'s `highlightAuto` to determine the language of code blocks lacking an explicit annotation.
|
|
157
|
-
| `formatJson` | `boolean` | `true` | Format JSON while preserving number tokens, key order and duplicate keys. Set `false` to display source text.
|
|
158
|
-
| `expandNestedJson` | `boolean` | `true` | When JSON formatting is enabled, expand string values containing JSON objects or arrays. Set `false` for whitespace formatting only.
|
|
153
|
+
| Field | Type | Default | Description |
|
|
154
|
+
| --------------------------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
155
|
+
| `defaultExpanded` | `boolean` | `true` | Whether code blocks start in their expanded state. When `false`, long blocks are collapsed with an expand button. |
|
|
156
|
+
| `autoDetectUnknownLanguage` | `boolean` | `false` | When `true`, uses `highlight.js`'s `highlightAuto` to determine the language of code blocks lacking an explicit annotation. |
|
|
157
|
+
| `formatJson` | `boolean` | `true` | Format JSON while preserving number tokens, key order and duplicate keys. Set `false` to display source text. |
|
|
158
|
+
| `expandNestedJson` | `boolean` | `true` | When JSON formatting is enabled, expand string values containing JSON objects or arrays. Set `false` for whitespace formatting only. |
|
|
159
|
+
| `highlightIntervalMs` | `number` | `50` | Coalesce streaming code display updates over this interval in milliseconds; `0` updates every frame. Completion and replacement update immediately; copy always uses latest source. |
|
|
159
160
|
|
|
160
161
|
### Example: Collapsed Code Blocks
|
|
161
162
|
|
package/dist/index.cjs
CHANGED
|
@@ -42,7 +42,7 @@ __export(index_exports, {
|
|
|
42
42
|
module.exports = __toCommonJS(index_exports);
|
|
43
43
|
|
|
44
44
|
// src/MantineAIMarkdown.tsx
|
|
45
|
-
var
|
|
45
|
+
var import_react6 = require("react");
|
|
46
46
|
var import_core7 = __toESM(require("@ai-react-markdown/core"), 1);
|
|
47
47
|
var import_core8 = require("@ai-react-markdown/core");
|
|
48
48
|
|
|
@@ -62,7 +62,7 @@ var MantineAIMDefaultExtraStyles = ({ children }) => {
|
|
|
62
62
|
var DefaultExtraStyles_default = MantineAIMDefaultExtraStyles;
|
|
63
63
|
|
|
64
64
|
// src/components/customized/PreCode.tsx
|
|
65
|
-
var
|
|
65
|
+
var import_react5 = require("react");
|
|
66
66
|
var import_code_highlight2 = require("@mantine/code-highlight");
|
|
67
67
|
var import_core5 = require("@ai-react-markdown/core");
|
|
68
68
|
|
|
@@ -75,7 +75,8 @@ var defaultMantineCodeBlockOptions = Object.freeze({
|
|
|
75
75
|
defaultExpanded: true,
|
|
76
76
|
autoDetectUnknownLanguage: false,
|
|
77
77
|
formatJson: true,
|
|
78
|
-
expandNestedJson: true
|
|
78
|
+
expandNestedJson: true,
|
|
79
|
+
highlightIntervalMs: 50
|
|
79
80
|
});
|
|
80
81
|
|
|
81
82
|
// src/hooks/useMantineCodeBlockOptions.ts
|
|
@@ -88,6 +89,9 @@ function useMantineCodeBlockOptions() {
|
|
|
88
89
|
const value = group?.[key];
|
|
89
90
|
if (value !== void 0) resolved[key] = value;
|
|
90
91
|
}
|
|
92
|
+
if (!Number.isFinite(resolved.highlightIntervalMs) || resolved.highlightIntervalMs < 0) {
|
|
93
|
+
resolved.highlightIntervalMs = defaultMantineCodeBlockOptions.highlightIntervalMs;
|
|
94
|
+
}
|
|
91
95
|
return resolved;
|
|
92
96
|
}, [group]);
|
|
93
97
|
}
|
|
@@ -521,6 +525,51 @@ function createJsonCompletenessScanner() {
|
|
|
521
525
|
};
|
|
522
526
|
}
|
|
523
527
|
|
|
528
|
+
// src/components/customized/useCodeFrame.ts
|
|
529
|
+
var import_react4 = require("react");
|
|
530
|
+
|
|
531
|
+
// src/components/customized/codeFrame.ts
|
|
532
|
+
function createCodeFrame(initial, publish) {
|
|
533
|
+
let shown = initial;
|
|
534
|
+
let latest = initial;
|
|
535
|
+
let delay = 0;
|
|
536
|
+
let timer;
|
|
537
|
+
const cancel = () => {
|
|
538
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
539
|
+
timer = void 0;
|
|
540
|
+
};
|
|
541
|
+
const flush = () => {
|
|
542
|
+
cancel();
|
|
543
|
+
if (shown.code === latest.code && shown.language === latest.language) return;
|
|
544
|
+
shown = latest;
|
|
545
|
+
publish(shown);
|
|
546
|
+
};
|
|
547
|
+
return {
|
|
548
|
+
update(next, streaming, interval) {
|
|
549
|
+
const replaced = next.language !== latest.language || !next.code.startsWith(latest.code);
|
|
550
|
+
latest = next;
|
|
551
|
+
if (delay !== interval) cancel();
|
|
552
|
+
delay = interval;
|
|
553
|
+
if (!streaming || interval === 0 || replaced) return flush();
|
|
554
|
+
if (shown.code === next.code && shown.language === next.language) return cancel();
|
|
555
|
+
timer ??= setTimeout(flush, interval);
|
|
556
|
+
},
|
|
557
|
+
// Cancellation is reversible: Strict Mode replays the update effect.
|
|
558
|
+
dispose: cancel
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// src/components/customized/useCodeFrame.ts
|
|
563
|
+
function useCodeFrame(code, language, streaming, interval) {
|
|
564
|
+
const [frame, setFrame] = (0, import_react4.useState)(() => ({ code, language }));
|
|
565
|
+
const [controller] = (0, import_react4.useState)(() => createCodeFrame({ code, language }, setFrame));
|
|
566
|
+
(0, import_react4.useEffect)(() => {
|
|
567
|
+
controller.update({ code, language }, streaming, interval);
|
|
568
|
+
}, [controller, code, language, streaming, interval]);
|
|
569
|
+
(0, import_react4.useEffect)(() => () => controller.dispose(), [controller]);
|
|
570
|
+
return !streaming || interval === 0 || language !== frame.language || !code.startsWith(frame.code) ? code : frame.code;
|
|
571
|
+
}
|
|
572
|
+
|
|
524
573
|
// src/components/customized/PreCode.tsx
|
|
525
574
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
526
575
|
var hljsAutoPromise = null;
|
|
@@ -538,13 +587,13 @@ var AUTODETECT_MIN_CHARS = 32;
|
|
|
538
587
|
var HLJS_LOAD_RETRIES = 3;
|
|
539
588
|
var HLJS_LOAD_RETRY_MS = 1500;
|
|
540
589
|
function useAutoDetectedLanguage(codeText, enabled, streaming) {
|
|
541
|
-
const [detected, setDetected] = (0,
|
|
590
|
+
const [detected, setDetected] = (0, import_react5.useState)(
|
|
542
591
|
null
|
|
543
592
|
);
|
|
544
|
-
const [loadAttempt, setLoadAttempt] = (0,
|
|
545
|
-
const loadFailuresRef = (0,
|
|
546
|
-
const previousTextRef = (0,
|
|
547
|
-
(0,
|
|
593
|
+
const [loadAttempt, setLoadAttempt] = (0, import_react5.useState)(0);
|
|
594
|
+
const loadFailuresRef = (0, import_react5.useRef)(0);
|
|
595
|
+
const previousTextRef = (0, import_react5.useRef)(codeText);
|
|
596
|
+
(0, import_react5.useEffect)(() => {
|
|
548
597
|
const appended = codeText.startsWith(previousTextRef.current);
|
|
549
598
|
previousTextRef.current = codeText;
|
|
550
599
|
if (!enabled) {
|
|
@@ -590,7 +639,9 @@ function preloadMantineCodeAssets() {
|
|
|
590
639
|
() => void 0
|
|
591
640
|
);
|
|
592
641
|
}
|
|
593
|
-
|
|
642
|
+
var RawCodeContext = (0, import_react5.createContext)("");
|
|
643
|
+
function RawCodeCopy() {
|
|
644
|
+
const code = (0, import_react5.useContext)(RawCodeContext);
|
|
594
645
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_core6.CopyButton, { value: code, children: ({ copied, copy }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
595
646
|
import_code_highlight2.CodeHighlightControl,
|
|
596
647
|
{
|
|
@@ -616,86 +667,105 @@ function RawCodeCopy({ code }) {
|
|
|
616
667
|
}
|
|
617
668
|
) });
|
|
618
669
|
}
|
|
670
|
+
function createCachedHighlightAdapter(highlight) {
|
|
671
|
+
let previous;
|
|
672
|
+
let result;
|
|
673
|
+
return {
|
|
674
|
+
getHighlighter: () => (input) => {
|
|
675
|
+
if (!previous || input.code !== previous.code || input.language !== previous.language || input.colorScheme !== previous.colorScheme) {
|
|
676
|
+
result = highlight(input);
|
|
677
|
+
previous = input;
|
|
678
|
+
}
|
|
679
|
+
return result;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
var OrdinaryCodeHighlight = (0, import_react5.memo)(function OrdinaryCodeHighlight2({
|
|
684
|
+
code,
|
|
685
|
+
language,
|
|
686
|
+
fileName,
|
|
687
|
+
fontSize,
|
|
688
|
+
defaultExpanded
|
|
689
|
+
}) {
|
|
690
|
+
const highlight = (0, import_code_highlight2.useHighlight)();
|
|
691
|
+
const adapter = (0, import_react5.useMemo)(() => createCachedHighlightAdapter(highlight), [highlight]);
|
|
692
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_code_highlight2.CodeHighlightAdapterProvider, { adapter, children: fileName === "unknown" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
693
|
+
import_code_highlight2.CodeHighlight,
|
|
694
|
+
{
|
|
695
|
+
mb: 15,
|
|
696
|
+
fz: fontSize,
|
|
697
|
+
w: "100%",
|
|
698
|
+
code,
|
|
699
|
+
withBorder: true,
|
|
700
|
+
withExpandButton: true,
|
|
701
|
+
defaultExpanded,
|
|
702
|
+
maxCollapsedHeight: "320px",
|
|
703
|
+
withCopyButton: false,
|
|
704
|
+
controls: [/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RawCodeCopy, {}, "copy")]
|
|
705
|
+
}
|
|
706
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
707
|
+
import_code_highlight2.CodeHighlightTabs,
|
|
708
|
+
{
|
|
709
|
+
mb: 15,
|
|
710
|
+
fz: fontSize,
|
|
711
|
+
w: "100%",
|
|
712
|
+
code: [
|
|
713
|
+
{
|
|
714
|
+
fileName,
|
|
715
|
+
code,
|
|
716
|
+
language
|
|
717
|
+
}
|
|
718
|
+
],
|
|
719
|
+
withBorder: true,
|
|
720
|
+
withExpandButton: true,
|
|
721
|
+
defaultExpanded,
|
|
722
|
+
maxCollapsedHeight: "320px",
|
|
723
|
+
withCopyButton: false,
|
|
724
|
+
controls: [/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RawCodeCopy, {}, "copy")]
|
|
725
|
+
}
|
|
726
|
+
) });
|
|
727
|
+
});
|
|
619
728
|
var SpecialCodeLanguage = /* @__PURE__ */ ((SpecialCodeLanguage2) => {
|
|
620
729
|
SpecialCodeLanguage2["Mermaid"] = "mermaid";
|
|
621
730
|
return SpecialCodeLanguage2;
|
|
622
731
|
})(SpecialCodeLanguage || {});
|
|
623
732
|
var SPECIAL_LANGUAGES = new Set(Object.values(SpecialCodeLanguage));
|
|
624
|
-
var MantineAIMPreCode = (0,
|
|
733
|
+
var MantineAIMPreCode = (0, import_react5.memo)(
|
|
625
734
|
(props) => {
|
|
626
735
|
const { fontSize } = (0, import_core5.useAIMarkdownTheme)();
|
|
627
736
|
const { streaming } = (0, import_core5.useAIMarkdownState)();
|
|
628
|
-
const { autoDetectUnknownLanguage, defaultExpanded, formatJson, expandNestedJson } = useMantineCodeBlockOptions();
|
|
737
|
+
const { autoDetectUnknownLanguage, defaultExpanded, formatJson, expandNestedJson, highlightIntervalMs } = useMantineCodeBlockOptions();
|
|
629
738
|
const detectedLanguage = useAutoDetectedLanguage(
|
|
630
739
|
props.codeText,
|
|
631
740
|
autoDetectUnknownLanguage && !props.existLanguage,
|
|
632
741
|
streaming
|
|
633
742
|
);
|
|
634
743
|
const codeLanguage = (props.existLanguage || detectedLanguage).toLowerCase();
|
|
635
|
-
const [usedCodeLanguage, usedFileName] = (0,
|
|
744
|
+
const [usedCodeLanguage, usedFileName] = (0, import_react5.useMemo)(
|
|
636
745
|
() => codeLanguage ? [codeLanguage, codeLanguage] : ["plaintext", "unknown"],
|
|
637
746
|
[codeLanguage]
|
|
638
747
|
);
|
|
639
748
|
const isSpecialCodeBlock = SPECIAL_LANGUAGES.has(codeLanguage);
|
|
640
|
-
const [scanJson] = (0,
|
|
641
|
-
const jsonComplete = (0,
|
|
749
|
+
const [scanJson] = (0, import_react5.useState)(createJsonCompletenessScanner);
|
|
750
|
+
const jsonComplete = (0, import_react5.useMemo)(
|
|
642
751
|
() => usedCodeLanguage === "json" && formatJson && streaming ? scanJson(props.codeText) : false,
|
|
643
752
|
[usedCodeLanguage, formatJson, streaming, scanJson, props.codeText]
|
|
644
753
|
);
|
|
645
|
-
const normalCodeBlockContent = (0,
|
|
754
|
+
const normalCodeBlockContent = (0, import_react5.useMemo)(() => {
|
|
646
755
|
if (isSpecialCodeBlock) return null;
|
|
647
756
|
let usedCodeStr = props.codeText;
|
|
648
757
|
if (formatJson && usedCodeStr && usedCodeLanguage === "json" && (!streaming || jsonComplete)) {
|
|
649
758
|
usedCodeStr = prettyPrintJson(usedCodeStr, expandNestedJson);
|
|
650
759
|
}
|
|
651
|
-
return
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
fz: fontSize,
|
|
656
|
-
w: "100%",
|
|
657
|
-
code: usedCodeStr,
|
|
658
|
-
withBorder: true,
|
|
659
|
-
withExpandButton: true,
|
|
660
|
-
defaultExpanded,
|
|
661
|
-
maxCollapsedHeight: "320px",
|
|
662
|
-
withCopyButton: false,
|
|
663
|
-
controls: [/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RawCodeCopy, { code: props.codeText }, "copy")]
|
|
664
|
-
}
|
|
665
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
666
|
-
import_code_highlight2.CodeHighlightTabs,
|
|
667
|
-
{
|
|
668
|
-
mb: 15,
|
|
669
|
-
fz: fontSize,
|
|
670
|
-
w: "100%",
|
|
671
|
-
code: [
|
|
672
|
-
{
|
|
673
|
-
fileName: usedFileName,
|
|
674
|
-
code: usedCodeStr,
|
|
675
|
-
language: usedCodeLanguage
|
|
676
|
-
}
|
|
677
|
-
],
|
|
678
|
-
withBorder: true,
|
|
679
|
-
withExpandButton: true,
|
|
680
|
-
defaultExpanded,
|
|
681
|
-
maxCollapsedHeight: "320px",
|
|
682
|
-
withCopyButton: false,
|
|
683
|
-
controls: [/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RawCodeCopy, { code: props.codeText }, "copy")]
|
|
684
|
-
}
|
|
685
|
-
);
|
|
686
|
-
}, [
|
|
687
|
-
isSpecialCodeBlock,
|
|
688
|
-
props.codeText,
|
|
760
|
+
return usedCodeStr;
|
|
761
|
+
}, [isSpecialCodeBlock, props.codeText, usedCodeLanguage, streaming, formatJson, expandNestedJson, jsonComplete]);
|
|
762
|
+
const displayedCode = useCodeFrame(
|
|
763
|
+
normalCodeBlockContent ?? "",
|
|
689
764
|
usedCodeLanguage,
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
formatJson,
|
|
695
|
-
expandNestedJson,
|
|
696
|
-
jsonComplete
|
|
697
|
-
]);
|
|
698
|
-
const specialCodeBlockContent = (0, import_react4.useMemo)(() => {
|
|
765
|
+
streaming && !isSpecialCodeBlock,
|
|
766
|
+
highlightIntervalMs
|
|
767
|
+
);
|
|
768
|
+
const specialCodeBlockContent = (0, import_react5.useMemo)(() => {
|
|
699
769
|
switch (codeLanguage) {
|
|
700
770
|
case "mermaid" /* Mermaid */:
|
|
701
771
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MermaidCode_default, { code: props.codeText });
|
|
@@ -703,7 +773,16 @@ var MantineAIMPreCode = (0, import_react4.memo)(
|
|
|
703
773
|
return null;
|
|
704
774
|
}
|
|
705
775
|
}, [codeLanguage, props.codeText]);
|
|
706
|
-
return isSpecialCodeBlock ? specialCodeBlockContent :
|
|
776
|
+
return isSpecialCodeBlock ? specialCodeBlockContent : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RawCodeContext.Provider, { value: props.codeText, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
777
|
+
OrdinaryCodeHighlight,
|
|
778
|
+
{
|
|
779
|
+
code: displayedCode,
|
|
780
|
+
language: usedCodeLanguage,
|
|
781
|
+
fileName: usedFileName,
|
|
782
|
+
fontSize,
|
|
783
|
+
defaultExpanded
|
|
784
|
+
}
|
|
785
|
+
) });
|
|
707
786
|
}
|
|
708
787
|
);
|
|
709
788
|
MantineAIMPreCode.displayName = "MantineAIMPreCode";
|
|
@@ -740,12 +819,12 @@ var MantineAIMarkdownComponent = ({
|
|
|
740
819
|
...props
|
|
741
820
|
}) => {
|
|
742
821
|
const stableCustomComponents = (0, import_core8.useStableValue)(customComponents);
|
|
743
|
-
const usedComponents = (0,
|
|
822
|
+
const usedComponents = (0, import_react6.useMemo)(() => {
|
|
744
823
|
return stableCustomComponents ? { ...DefaultCustomComponents, ...stableCustomComponents } : DefaultCustomComponents;
|
|
745
824
|
}, [stableCustomComponents]);
|
|
746
825
|
const computedColorScheme = (0, import_core9.useComputedColorScheme)("light");
|
|
747
826
|
const stable = (0, import_core8.useStableRecord)({ codeBlock }, MANTINE_STABILITY_TABLE);
|
|
748
|
-
const behaviorGroups = (0,
|
|
827
|
+
const behaviorGroups = (0, import_react6.useMemo)(
|
|
749
828
|
() => stable.codeBlock != null ? { codeBlock: stable.codeBlock } : NO_GROUPS,
|
|
750
829
|
[stable.codeBlock]
|
|
751
830
|
);
|
|
@@ -760,7 +839,7 @@ var MantineAIMarkdownComponent = ({
|
|
|
760
839
|
}
|
|
761
840
|
) });
|
|
762
841
|
};
|
|
763
|
-
var MantineAIMarkdown = (0,
|
|
842
|
+
var MantineAIMarkdown = (0, import_react6.memo)(MantineAIMarkdownComponent);
|
|
764
843
|
MantineAIMarkdown.displayName = "MantineAIMarkdown";
|
|
765
844
|
var MantineAIMarkdown_default = MantineAIMarkdown;
|
|
766
845
|
|