@ai-react-markdown/mantine 2.11.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 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.11.0",
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,10 +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. |
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. |
157
160
 
158
161
  ### Example: Collapsed Code Blocks
159
162
 
@@ -237,13 +240,15 @@ Activated by importing `@ai-react-markdown/mantine/styles.css` in your app entry
237
240
 
238
241
  The Mantine package installs a default `<pre>` renderer (`MantineAIMPreCode`) that powers all code-block features. Behavior by code-block flavor:
239
242
 
240
- | Code-block flavor | Rendered as | Notes |
241
- | ------------------------------------------ | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
242
- | Annotated, known language (e.g. ` ```ts `) | `<CodeHighlightTabs>` | Tab label = language name (lower-cased) |
243
- | Annotated, unknown language identifier | `<CodeHighlightTabs>` | Tab label = the identifier (lower-cased); Mantine's highlight adapter degrades an unknown language to plaintext |
244
- | No language annotation | `<CodeHighlight>` plaintext | Label = `"unknown"`. With `codeBlock.autoDetectUnknownLanguage: true`, `hljs.highlightAuto` guesses early, re-checks as the block grows, and settles at end of stream — label/highlighting upgrade in place |
245
- | ` ```mermaid ` (any case) | Interactive Mermaid diagram | See [Mermaid Diagrams](#mermaid-diagrams); the language match is case-insensitive |
246
- | ` ```json ` (any case) | Pretty-printed JSON | As soon as the block looks complete (ends in `}`/`]` with balanced brackets outside strings), parsed, string values holding a nested JSON object/array expanded (primitive-looking strings such as `"true"` stay strings), then re-serialized with 2-space indent before rendering |
243
+ | Code-block flavor | Rendered as | Notes |
244
+ | ------------------------------------------ | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
245
+ | Annotated, known language (e.g. ` ```ts `) | `<CodeHighlightTabs>` | Tab label = language name (lower-cased) |
246
+ | Annotated, unknown language identifier | `<CodeHighlightTabs>` | Tab label = the identifier (lower-cased); Mantine's highlight adapter degrades an unknown language to plaintext |
247
+ | No language annotation | `<CodeHighlight>` plaintext | Label = `"unknown"`. With `codeBlock.autoDetectUnknownLanguage: true`, `hljs.highlightAuto` guesses early, re-checks as the block grows, and settles at end of stream — label/highlighting upgrade in place |
248
+ | ` ```mermaid ` (any case) | Interactive Mermaid diagram | See [Mermaid Diagrams](#mermaid-diagrams); the language match is case-insensitive |
249
+ | ` ```json ` (any case) | Pretty-printed JSON | As soon as the block looks complete (ends in `}`/`]` with balanced brackets outside strings), parsed, string values holding a nested JSON object/array expanded (primitive-looking strings such as `"true"` stay strings), then formatted with 2-space indent while retaining exact numeric tokens; both formatting and nested expansion can be disabled |
250
+
251
+ The copy control copies the original code text, including its trailing newline, independently of JSON display formatting. Raw HTML `<pre>` structures with nested elements, sibling text, or additional attributes retain their original rendering instead of entering the code highlighter.
247
252
 
248
253
  All non-special blocks render with `withBorder` and `withExpandButton`, collapsing to `maxCollapsedHeight="320px"` until expanded.
249
254
 
package/dist/index.cjs CHANGED
@@ -42,9 +42,9 @@ __export(index_exports, {
42
42
  module.exports = __toCommonJS(index_exports);
43
43
 
44
44
  // src/MantineAIMarkdown.tsx
45
- var import_react5 = require("react");
46
- var import_core6 = __toESM(require("@ai-react-markdown/core"), 1);
47
- var import_core7 = require("@ai-react-markdown/core");
45
+ var import_react6 = require("react");
46
+ var import_core7 = __toESM(require("@ai-react-markdown/core"), 1);
47
+ var import_core8 = require("@ai-react-markdown/core");
48
48
 
49
49
  // src/components/typography/MantineTypography.tsx
50
50
  var import_react = require("react");
@@ -62,7 +62,7 @@ var MantineAIMDefaultExtraStyles = ({ children }) => {
62
62
  var DefaultExtraStyles_default = MantineAIMDefaultExtraStyles;
63
63
 
64
64
  // src/components/customized/PreCode.tsx
65
- var import_react4 = require("react");
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
 
@@ -73,7 +73,10 @@ var import_core2 = require("@ai-react-markdown/core");
73
73
  // src/defs.tsx
74
74
  var defaultMantineCodeBlockOptions = Object.freeze({
75
75
  defaultExpanded: true,
76
- autoDetectUnknownLanguage: false
76
+ autoDetectUnknownLanguage: false,
77
+ formatJson: true,
78
+ expandNestedJson: true,
79
+ highlightIntervalMs: 50
77
80
  });
78
81
 
79
82
  // src/hooks/useMantineCodeBlockOptions.ts
@@ -86,10 +89,50 @@ function useMantineCodeBlockOptions() {
86
89
  const value = group?.[key];
87
90
  if (value !== void 0) resolved[key] = value;
88
91
  }
92
+ if (!Number.isFinite(resolved.highlightIntervalMs) || resolved.highlightIntervalMs < 0) {
93
+ resolved.highlightIntervalMs = defaultMantineCodeBlockOptions.highlightIntervalMs;
94
+ }
89
95
  return resolved;
90
96
  }, [group]);
91
97
  }
92
98
 
99
+ // src/components/customized/MermaidCode/renderQueue.ts
100
+ function createRenderQueue() {
101
+ const pending = /* @__PURE__ */ new Map();
102
+ let running = false;
103
+ async function drain() {
104
+ if (running) return;
105
+ running = true;
106
+ try {
107
+ while (pending.size) {
108
+ const [owner, job] = pending.entries().next().value;
109
+ pending.delete(owner);
110
+ try {
111
+ await job.run();
112
+ job.resolve();
113
+ } catch (error) {
114
+ job.reject(error);
115
+ }
116
+ }
117
+ } finally {
118
+ running = false;
119
+ }
120
+ }
121
+ return {
122
+ enqueue(owner, run) {
123
+ pending.get(owner)?.resolve();
124
+ const promise = new Promise((resolve, reject) => pending.set(owner, { run, resolve, reject }));
125
+ void drain();
126
+ return promise;
127
+ },
128
+ cancel(owner) {
129
+ pending.get(owner)?.resolve();
130
+ pending.delete(owner);
131
+ }
132
+ };
133
+ }
134
+ var mermaidRenderQueue = createRenderQueue();
135
+
93
136
  // src/components/customized/MermaidCode/index.tsx
94
137
  var import_react3 = require("react");
95
138
  var import_code_highlight = require("@mantine/code-highlight");
@@ -211,14 +254,7 @@ var MantineAIMMermaidCode = (0, import_react3.memo)((props) => {
211
254
  if (!ref.current || cancelled || renderVersion !== renderVersionRef.current) {
212
255
  return;
213
256
  }
214
- let rendered = await mermaid.render(generateMermaidUUID(), props.code);
215
- if (initializedTheme !== (isDark ? "dark" : "light")) {
216
- if (!ref.current || cancelled || renderVersion !== renderVersionRef.current) {
217
- return;
218
- }
219
- ensureMermaidInitialized(mermaid, isDark);
220
- rendered = await mermaid.render(generateMermaidUUID(), props.code);
221
- }
257
+ const rendered = await mermaid.render(generateMermaidUUID(), props.code);
222
258
  const { svg, bindFunctions, diagramType } = rendered;
223
259
  if (!ref.current || cancelled || renderVersion !== renderVersionRef.current) {
224
260
  return;
@@ -246,9 +282,11 @@ var MantineAIMMermaidCode = (0, import_react3.memo)((props) => {
246
282
  applyView({ kind: "error" });
247
283
  }
248
284
  };
249
- void renderMermaid();
285
+ const owner = renderVersionRef;
286
+ void mermaidRenderQueue.enqueue(owner, renderMermaid);
250
287
  return () => {
251
288
  cancelled = true;
289
+ mermaidRenderQueue.cancel(owner);
252
290
  if (retryTimer !== void 0) clearTimeout(retryTimer);
253
291
  };
254
292
  }, [props.code, isDark, showOriginalCode, streaming, loadAttempt]);
@@ -398,6 +436,140 @@ var MantineAIMMermaidCode = (0, import_react3.memo)((props) => {
398
436
  MantineAIMMermaidCode.displayName = "MantineAIMMermaidCode";
399
437
  var MermaidCode_default = MantineAIMMermaidCode;
400
438
 
439
+ // src/components/customized/PreCode.tsx
440
+ var import_core6 = require("@mantine/core");
441
+
442
+ // src/components/customized/formatJson.ts
443
+ function prettyPrintJson(text, expandNested = true) {
444
+ try {
445
+ return format(text, expandNested, 0);
446
+ } catch {
447
+ return text;
448
+ }
449
+ }
450
+ function format(text, expandNested, baseIndent) {
451
+ JSON.parse(text);
452
+ const tokens = text.match(/"(?:\\[\s\S]|[^"\\])*"|-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null|[{}[\],:]/g) ?? [];
453
+ const out = [];
454
+ let indent = baseIndent;
455
+ const newline = () => out.push("\n", " ".repeat(indent));
456
+ for (let i = 0; i < tokens.length; i++) {
457
+ const token = tokens[i];
458
+ if (token === "{" || token === "[") {
459
+ out.push(token);
460
+ if (tokens[i + 1] !== (token === "{" ? "}" : "]")) {
461
+ indent++;
462
+ newline();
463
+ }
464
+ } else if (token === "}" || token === "]") {
465
+ if (tokens[i - 1] !== (token === "}" ? "{" : "[")) {
466
+ indent--;
467
+ newline();
468
+ }
469
+ out.push(token);
470
+ } else if (token === ",") {
471
+ out.push(",");
472
+ newline();
473
+ } else if (token === ":") {
474
+ out.push(": ");
475
+ } else if (token.startsWith('"')) {
476
+ const value = JSON.parse(token);
477
+ const trimmed = value.trim();
478
+ if (expandNested && tokens[i + 1] !== ":" && /^[{[]/.test(trimmed) && indent < 100) {
479
+ try {
480
+ out.push(format(trimmed, true, indent));
481
+ continue;
482
+ } catch {
483
+ }
484
+ }
485
+ out.push(JSON.stringify(value));
486
+ } else {
487
+ out.push(token);
488
+ }
489
+ }
490
+ return out.join("");
491
+ }
492
+
493
+ // src/components/customized/jsonCompleteness.ts
494
+ function createJsonCompletenessScanner() {
495
+ let previous = "";
496
+ let depth = 0;
497
+ let quoted = false;
498
+ let escaped = false;
499
+ let invalid = false;
500
+ let last = "";
501
+ return (text) => {
502
+ let from = previous.length;
503
+ if (!text.startsWith(previous)) {
504
+ from = 0;
505
+ depth = 0;
506
+ quoted = escaped = invalid = false;
507
+ last = "";
508
+ }
509
+ previous = text;
510
+ for (let i = from; i < text.length; i++) {
511
+ const c = text[i];
512
+ if (!/\s/.test(c)) last = c;
513
+ if (quoted) {
514
+ if (escaped) escaped = false;
515
+ else if (c === "\\") escaped = true;
516
+ else if (c === '"') quoted = false;
517
+ } else if (c === '"') quoted = true;
518
+ else if (c === "{" || c === "[") depth++;
519
+ else if (c === "}" || c === "]") {
520
+ depth--;
521
+ if (depth < 0) invalid = true;
522
+ }
523
+ }
524
+ return !invalid && !quoted && depth === 0 && (last === "}" || last === "]");
525
+ };
526
+ }
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
+
401
573
  // src/components/customized/PreCode.tsx
402
574
  var import_jsx_runtime4 = require("react/jsx-runtime");
403
575
  var hljsAutoPromise = null;
@@ -415,14 +587,20 @@ var AUTODETECT_MIN_CHARS = 32;
415
587
  var HLJS_LOAD_RETRIES = 3;
416
588
  var HLJS_LOAD_RETRY_MS = 1500;
417
589
  function useAutoDetectedLanguage(codeText, enabled, streaming) {
418
- const [detected, setDetected] = (0, import_react4.useState)(
590
+ const [detected, setDetected] = (0, import_react5.useState)(
419
591
  null
420
592
  );
421
- const [loadAttempt, setLoadAttempt] = (0, import_react4.useState)(0);
422
- const loadFailuresRef = (0, import_react4.useRef)(0);
423
- (0, import_react4.useEffect)(() => {
424
- if (!enabled) return;
425
- const prior = detected !== null && codeText.length < detected.atLength ? null : detected;
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)(() => {
597
+ const appended = codeText.startsWith(previousTextRef.current);
598
+ previousTextRef.current = codeText;
599
+ if (!enabled) {
600
+ if (detected) setDetected(null);
601
+ return;
602
+ }
603
+ const prior = !appended || detected !== null && codeText.length < detected.atLength ? null : detected;
426
604
  if (prior !== detected) setDetected(null);
427
605
  if (codeText.length < AUTODETECT_MIN_CHARS) return;
428
606
  const due = prior === null || // Not streaming: the verdict must be for THIS text (end-of-stream, or a
@@ -461,115 +639,133 @@ function preloadMantineCodeAssets() {
461
639
  () => void 0
462
640
  );
463
641
  }
464
- function prettyPrintJson(text) {
465
- let parsed;
466
- try {
467
- parsed = JSON.parse(text);
468
- } catch {
469
- return text;
470
- }
471
- return JSON.stringify(expandNestedJson(parsed), null, 2);
642
+ var RawCodeContext = (0, import_react5.createContext)("");
643
+ function RawCodeCopy() {
644
+ const code = (0, import_react5.useContext)(RawCodeContext);
645
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_core6.CopyButton, { value: code, children: ({ copied, copy }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
646
+ import_code_highlight2.CodeHighlightControl,
647
+ {
648
+ tooltipLabel: copied ? "Copied" : "Copy",
649
+ "aria-label": copied ? "Copied" : "Copy code",
650
+ onClick: copy,
651
+ children: copied ? "\u2713" : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
652
+ "svg",
653
+ {
654
+ width: "16",
655
+ height: "16",
656
+ viewBox: "0 0 24 24",
657
+ fill: "none",
658
+ stroke: "currentColor",
659
+ strokeWidth: "1.5",
660
+ "aria-hidden": "true",
661
+ children: [
662
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "8", y: "8", width: "12", height: "12", rx: "2" }),
663
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M16 8V4H4v12h4" })
664
+ ]
665
+ }
666
+ )
667
+ }
668
+ ) });
472
669
  }
473
- function expandNestedJson(value) {
474
- if (typeof value === "string") {
475
- const t = value.trim();
476
- if (t.length > 1 && (t[0] === "{" || t[0] === "[")) {
477
- try {
478
- const inner = JSON.parse(t);
479
- if (inner !== null && typeof inner === "object") return expandNestedJson(inner);
480
- } catch {
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;
481
678
  }
679
+ return result;
482
680
  }
483
- return value;
484
- }
485
- if (Array.isArray(value)) return value.map(expandNestedJson);
486
- if (value !== null && typeof value === "object") {
487
- const out = /* @__PURE__ */ Object.create(null);
488
- for (const [k, v] of Object.entries(value)) out[k] = expandNestedJson(v);
489
- return out;
490
- }
491
- return value;
681
+ };
492
682
  }
493
- function jsonLooksComplete(text) {
494
- if (!/[}\]]\s*$/.test(text)) return false;
495
- let depth = 0;
496
- let inString = false;
497
- for (let i = 0; i < text.length; i++) {
498
- const c = text.charCodeAt(i);
499
- if (inString) {
500
- if (c === 92) i += 1;
501
- else if (c === 34) inString = false;
502
- } else if (c === 34) {
503
- inString = true;
504
- } else if (c === 123 || c === 91) {
505
- depth += 1;
506
- } else if (c === 125 || c === 93) {
507
- depth -= 1;
508
- if (depth < 0) return false;
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")]
509
705
  }
510
- }
511
- return depth === 0 && !inString;
512
- }
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
+ });
513
728
  var SpecialCodeLanguage = /* @__PURE__ */ ((SpecialCodeLanguage2) => {
514
729
  SpecialCodeLanguage2["Mermaid"] = "mermaid";
515
730
  return SpecialCodeLanguage2;
516
731
  })(SpecialCodeLanguage || {});
517
732
  var SPECIAL_LANGUAGES = new Set(Object.values(SpecialCodeLanguage));
518
- var MantineAIMPreCode = (0, import_react4.memo)(
733
+ var MantineAIMPreCode = (0, import_react5.memo)(
519
734
  (props) => {
520
735
  const { fontSize } = (0, import_core5.useAIMarkdownTheme)();
521
736
  const { streaming } = (0, import_core5.useAIMarkdownState)();
522
- const { autoDetectUnknownLanguage, defaultExpanded } = useMantineCodeBlockOptions();
737
+ const { autoDetectUnknownLanguage, defaultExpanded, formatJson, expandNestedJson, highlightIntervalMs } = useMantineCodeBlockOptions();
523
738
  const detectedLanguage = useAutoDetectedLanguage(
524
739
  props.codeText,
525
740
  autoDetectUnknownLanguage && !props.existLanguage,
526
741
  streaming
527
742
  );
528
743
  const codeLanguage = (props.existLanguage || detectedLanguage).toLowerCase();
529
- const [usedCodeLanguage, usedFileName] = (0, import_react4.useMemo)(
744
+ const [usedCodeLanguage, usedFileName] = (0, import_react5.useMemo)(
530
745
  () => codeLanguage ? [codeLanguage, codeLanguage] : ["plaintext", "unknown"],
531
746
  [codeLanguage]
532
747
  );
533
748
  const isSpecialCodeBlock = SPECIAL_LANGUAGES.has(codeLanguage);
534
- const normalCodeBlockContent = (0, import_react4.useMemo)(() => {
749
+ const [scanJson] = (0, import_react5.useState)(createJsonCompletenessScanner);
750
+ const jsonComplete = (0, import_react5.useMemo)(
751
+ () => usedCodeLanguage === "json" && formatJson && streaming ? scanJson(props.codeText) : false,
752
+ [usedCodeLanguage, formatJson, streaming, scanJson, props.codeText]
753
+ );
754
+ const normalCodeBlockContent = (0, import_react5.useMemo)(() => {
535
755
  if (isSpecialCodeBlock) return null;
536
756
  let usedCodeStr = props.codeText;
537
- if (usedCodeStr && usedCodeLanguage === "json" && (!streaming || jsonLooksComplete(usedCodeStr))) {
538
- usedCodeStr = prettyPrintJson(usedCodeStr);
757
+ if (formatJson && usedCodeStr && usedCodeLanguage === "json" && (!streaming || jsonComplete)) {
758
+ usedCodeStr = prettyPrintJson(usedCodeStr, expandNestedJson);
539
759
  }
540
- return usedFileName === "unknown" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
541
- import_code_highlight2.CodeHighlight,
542
- {
543
- mb: 15,
544
- fz: fontSize,
545
- w: "100%",
546
- code: usedCodeStr,
547
- withBorder: true,
548
- withExpandButton: true,
549
- defaultExpanded,
550
- maxCollapsedHeight: "320px"
551
- }
552
- ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
553
- import_code_highlight2.CodeHighlightTabs,
554
- {
555
- mb: 15,
556
- fz: fontSize,
557
- w: "100%",
558
- code: [
559
- {
560
- fileName: usedFileName,
561
- code: usedCodeStr,
562
- language: usedCodeLanguage
563
- }
564
- ],
565
- withBorder: true,
566
- withExpandButton: true,
567
- defaultExpanded,
568
- maxCollapsedHeight: "320px"
569
- }
570
- );
571
- }, [isSpecialCodeBlock, props.codeText, usedCodeLanguage, usedFileName, fontSize, defaultExpanded, streaming]);
572
- const specialCodeBlockContent = (0, import_react4.useMemo)(() => {
760
+ return usedCodeStr;
761
+ }, [isSpecialCodeBlock, props.codeText, usedCodeLanguage, streaming, formatJson, expandNestedJson, jsonComplete]);
762
+ const displayedCode = useCodeFrame(
763
+ normalCodeBlockContent ?? "",
764
+ usedCodeLanguage,
765
+ streaming && !isSpecialCodeBlock,
766
+ highlightIntervalMs
767
+ );
768
+ const specialCodeBlockContent = (0, import_react5.useMemo)(() => {
573
769
  switch (codeLanguage) {
574
770
  case "mermaid" /* Mermaid */:
575
771
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MermaidCode_default, { code: props.codeText });
@@ -577,29 +773,39 @@ var MantineAIMPreCode = (0, import_react4.memo)(
577
773
  return null;
578
774
  }
579
775
  }, [codeLanguage, props.codeText]);
580
- return isSpecialCodeBlock ? specialCodeBlockContent : normalCodeBlockContent;
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
+ ) });
581
786
  }
582
787
  );
583
788
  MantineAIMPreCode.displayName = "MantineAIMPreCode";
584
789
  var PreCode_default = MantineAIMPreCode;
585
790
 
586
791
  // src/MantineAIMarkdown.tsx
587
- var import_core8 = require("@mantine/core");
792
+ var import_core9 = require("@mantine/core");
588
793
  var import_jsx_runtime5 = require("react/jsx-runtime");
589
794
  var NO_GROUPS = Object.freeze({});
590
795
  var MANTINE_STABILITY_TABLE = {
591
- codeBlock: import_core7.AIMarkdownStabilityPolicy.DEEP_EQUAL
796
+ codeBlock: import_core8.AIMarkdownStabilityPolicy.DEEP_EQUAL
592
797
  };
593
798
  var DefaultCustomComponents = {
594
799
  pre: ({ node, ...usefulProps }) => {
595
800
  const code = node?.children[0];
596
- if (!code || code.type !== "element" || code.tagName !== "code" || !code.position) {
801
+ if (!code || code.type !== "element" || code.tagName !== "code" || !code.position || node?.children.length !== 1 || code.children.some((child) => !("value" in child) || typeof child.value !== "string") || Object.keys(code.properties ?? {}).some((key2) => key2 !== "className") || Object.keys(node.properties ?? {}).length > 0) {
597
802
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { ...usefulProps });
598
803
  }
599
804
  const key = `pre-code-${node?.position?.start?.offset || 0}`;
600
805
  const classNames = code.properties?.className;
601
806
  const classList = Array.isArray(classNames) ? classNames.filter((c) => typeof c === "string") : typeof classNames === "string" ? classNames.split(/\s+/) : [];
602
807
  const detectedLanguage = classList.find((className) => className.startsWith("language-"))?.substring("language-".length);
808
+ if (classList.some((className) => !className.startsWith("language-"))) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { ...usefulProps });
603
809
  const codeText = code.children.map((child) => child.value ?? "").join("");
604
810
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PreCode_default, { codeText, existLanguage: detectedLanguage }, key);
605
811
  }
@@ -612,18 +818,18 @@ var MantineAIMarkdownComponent = ({
612
818
  codeBlock,
613
819
  ...props
614
820
  }) => {
615
- const stableCustomComponents = (0, import_core7.useStableValue)(customComponents);
616
- const usedComponents = (0, import_react5.useMemo)(() => {
821
+ const stableCustomComponents = (0, import_core8.useStableValue)(customComponents);
822
+ const usedComponents = (0, import_react6.useMemo)(() => {
617
823
  return stableCustomComponents ? { ...DefaultCustomComponents, ...stableCustomComponents } : DefaultCustomComponents;
618
824
  }, [stableCustomComponents]);
619
- const computedColorScheme = (0, import_core8.useComputedColorScheme)("light");
620
- const stable = (0, import_core7.useStableRecord)({ codeBlock }, MANTINE_STABILITY_TABLE);
621
- const behaviorGroups = (0, import_react5.useMemo)(
825
+ const computedColorScheme = (0, import_core9.useComputedColorScheme)("light");
826
+ const stable = (0, import_core8.useStableRecord)({ codeBlock }, MANTINE_STABILITY_TABLE);
827
+ const behaviorGroups = (0, import_react6.useMemo)(
622
828
  () => stable.codeBlock != null ? { codeBlock: stable.codeBlock } : NO_GROUPS,
623
829
  [stable.codeBlock]
624
830
  );
625
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_core7.AIMarkdownBehaviorsProvider, { value: behaviorGroups, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
626
- import_core6.default,
831
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_core8.AIMarkdownBehaviorsProvider, { value: behaviorGroups, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
832
+ import_core7.default,
627
833
  {
628
834
  Typography: Typography2,
629
835
  ExtraStyles,
@@ -633,7 +839,7 @@ var MantineAIMarkdownComponent = ({
633
839
  }
634
840
  ) });
635
841
  };
636
- var MantineAIMarkdown = (0, import_react5.memo)(MantineAIMarkdownComponent);
842
+ var MantineAIMarkdown = (0, import_react6.memo)(MantineAIMarkdownComponent);
637
843
  MantineAIMarkdown.displayName = "MantineAIMarkdown";
638
844
  var MantineAIMarkdown_default = MantineAIMarkdown;
639
845
 
@@ -643,9 +849,9 @@ function defineMantineBehaviors(values) {
643
849
  }
644
850
 
645
851
  // src/hooks/useMantineAIMarkdownMetadata.ts
646
- var import_core9 = require("@ai-react-markdown/core");
852
+ var import_core10 = require("@ai-react-markdown/core");
647
853
  var useMantineAIMarkdownMetadata = () => {
648
- return (0, import_core9.useAIMarkdownMetadata)();
854
+ return (0, import_core10.useAIMarkdownMetadata)();
649
855
  };
650
856
  // Annotate the CommonJS export names for ESM import in node:
651
857
  0 && (module.exports = {