@stream-mdx/react 0.4.0 → 0.5.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +14 -4
  2. package/README.md +65 -15
  3. package/dist/components/bottom-stick-scroll-area.cjs +344 -0
  4. package/dist/components/bottom-stick-scroll-area.d.cts +36 -0
  5. package/dist/components/bottom-stick-scroll-area.d.ts +36 -0
  6. package/dist/components/bottom-stick-scroll-area.mjs +320 -0
  7. package/dist/components/index.cjs +503 -172
  8. package/dist/components/index.d.cts +3 -1
  9. package/dist/components/index.d.ts +3 -1
  10. package/dist/components/index.mjs +350 -22
  11. package/dist/{index-D2MYusAM.d.cts → index-BVhp_pcF.d.ts} +3 -0
  12. package/dist/{index-D2MYusAM.d.ts → index-EaeouYp2.d.cts} +3 -0
  13. package/dist/index.cjs +1472 -486
  14. package/dist/index.d.cts +6 -4
  15. package/dist/index.d.ts +6 -4
  16. package/dist/index.mjs +1256 -272
  17. package/dist/mdx-client.cjs +1 -1
  18. package/dist/mdx-client.mjs +1 -1
  19. package/dist/mdx-coordinator.cjs +28 -7
  20. package/dist/mdx-coordinator.d.cts +2 -2
  21. package/dist/mdx-coordinator.d.ts +2 -2
  22. package/dist/mdx-coordinator.mjs +28 -7
  23. package/dist/renderer/node-views.cjs +271 -26
  24. package/dist/renderer/node-views.d.cts +3 -1
  25. package/dist/renderer/node-views.d.ts +3 -1
  26. package/dist/renderer/node-views.mjs +272 -27
  27. package/dist/renderer/patch-batching.cjs +2 -0
  28. package/dist/renderer/patch-batching.d.cts +1 -1
  29. package/dist/renderer/patch-batching.d.ts +1 -1
  30. package/dist/renderer/patch-batching.mjs +2 -1
  31. package/dist/renderer/patch-commit-scheduler.cjs +82 -30
  32. package/dist/renderer/patch-commit-scheduler.d.cts +15 -3
  33. package/dist/renderer/patch-commit-scheduler.d.ts +15 -3
  34. package/dist/renderer/patch-commit-scheduler.mjs +84 -30
  35. package/dist/renderer/store.cjs +382 -36
  36. package/dist/renderer/store.d.cts +3 -0
  37. package/dist/renderer/store.d.ts +3 -0
  38. package/dist/renderer/store.mjs +382 -36
  39. package/dist/renderer.cjs +758 -114
  40. package/dist/renderer.d.cts +3 -1
  41. package/dist/renderer.d.ts +3 -1
  42. package/dist/renderer.mjs +760 -116
  43. package/dist/server.cjs +669 -71
  44. package/dist/server.d.cts +5 -0
  45. package/dist/server.d.ts +5 -0
  46. package/dist/server.mjs +670 -72
  47. package/dist/{streaming-markdown-DOc2uLiP.d.ts → streaming-markdown-CEI9eDZm.d.ts} +3 -1
  48. package/dist/{streaming-markdown-D-JcByd9.d.cts → streaming-markdown-DRisClXb.d.cts} +3 -1
  49. package/dist/streaming-markdown.cjs +788 -121
  50. package/dist/streaming-markdown.d.cts +4 -2
  51. package/dist/streaming-markdown.d.ts +4 -2
  52. package/dist/streaming-markdown.mjs +790 -123
  53. package/dist/utils/inline-html.d.cts +3 -1
  54. package/dist/utils/inline-html.d.ts +3 -1
  55. package/package.json +14 -3
  56. package/shadcn/README.md +28 -0
  57. package/shadcn/bottom-stick-scroll-area.tsx +358 -0
package/dist/index.mjs CHANGED
@@ -81,7 +81,7 @@ var init_mdx_client = __esm({
81
81
  if (block.type !== "mdx") {
82
82
  throw new Error("Block is not MDX type");
83
83
  }
84
- const cacheKey = block.id;
84
+ const cacheKey = `${block.id}\0${block.payload.raw ?? ""}`;
85
85
  const cachedPromise = this.cache.get(cacheKey);
86
86
  if (cachedPromise) {
87
87
  return cachedPromise;
@@ -384,6 +384,321 @@ var init_mdx_client = __esm({
384
384
  }
385
385
  });
386
386
 
387
+ // src/components/bottom-stick-scroll-area.tsx
388
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
389
+ import { jsx, jsxs } from "react/jsx-runtime";
390
+ var EPS_BOTTOM_PX = 2;
391
+ var EPS_OVERFLOW_PX = 1;
392
+ var DETACH_SCROLL_DELTA_PX = 1;
393
+ var RETURN_MIN_MS = 180;
394
+ var RETURN_MAX_MS = 520;
395
+ var RETURN_PX_PER_MS = 2.2;
396
+ var RETURN_SETTLE_FRAMES = 2;
397
+ function clamp01(value) {
398
+ if (value < 0) return 0;
399
+ if (value > 1) return 1;
400
+ return value;
401
+ }
402
+ function easeOutCubic(value) {
403
+ return 1 - (1 - value) ** 3;
404
+ }
405
+ function joinClasses(...parts) {
406
+ return parts.filter(Boolean).join(" ");
407
+ }
408
+ function measure(viewport) {
409
+ const scrollTop = viewport.scrollTop;
410
+ const scrollHeight = viewport.scrollHeight;
411
+ const clientHeight = viewport.clientHeight;
412
+ const maxScrollTop = Math.max(0, scrollHeight - clientHeight);
413
+ const distanceToBottom = Math.max(0, maxScrollTop - scrollTop);
414
+ const isOverflowing = scrollHeight > clientHeight + EPS_OVERFLOW_PX;
415
+ return { scrollTop, scrollHeight, clientHeight, maxScrollTop, distanceToBottom, isOverflowing };
416
+ }
417
+ function DefaultJumpButton({ canJump, jumpToBottom }) {
418
+ return /* @__PURE__ */ jsx(
419
+ "button",
420
+ {
421
+ type: "button",
422
+ onClick: jumpToBottom,
423
+ "aria-label": "Scroll to bottom",
424
+ style: {
425
+ pointerEvents: canJump ? "auto" : "none",
426
+ borderRadius: 9999,
427
+ border: "1px solid color-mix(in srgb, currentColor 20%, transparent)",
428
+ width: 40,
429
+ height: 40,
430
+ display: "grid",
431
+ placeItems: "center",
432
+ boxShadow: "0 8px 20px rgba(0,0,0,0.18)",
433
+ background: "color-mix(in srgb, var(--background, #fff) 92%, transparent)",
434
+ color: "var(--foreground, #111)",
435
+ cursor: canJump ? "pointer" : "default"
436
+ },
437
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u2193" })
438
+ }
439
+ );
440
+ }
441
+ function BottomStickScrollArea({
442
+ children,
443
+ className,
444
+ viewportClassName,
445
+ contentClassName,
446
+ showJumpToBottom = true,
447
+ onDebugStateChange,
448
+ onModeChange,
449
+ debugDomAttributes = false,
450
+ renderJumpToBottom,
451
+ jumpContainerClassName
452
+ }) {
453
+ const viewportRef = useRef(null);
454
+ const contentRef = useRef(null);
455
+ const modeRef = useRef("STICKY_INSTANT");
456
+ const [mode, setMode] = useState("STICKY_INSTANT");
457
+ const [isOverflowing, setIsOverflowing] = useState(false);
458
+ const [distanceToBottom, setDistanceToBottom] = useState(0);
459
+ const prevScrollTopRef = useRef(0);
460
+ const lastProgrammaticWriteRef = useRef(null);
461
+ const programmaticWriteCountRef = useRef(0);
462
+ const pendingStickRafRef = useRef(null);
463
+ const stickRequestedRef = useRef(false);
464
+ const returnRafRef = useRef(null);
465
+ const returnAnimRef = useRef(null);
466
+ const setModeBoth = useCallback(
467
+ (next) => {
468
+ modeRef.current = next;
469
+ setMode(next);
470
+ onModeChange?.(next);
471
+ },
472
+ [onModeChange]
473
+ );
474
+ const updateDebug = useCallback(() => {
475
+ const viewport = viewportRef.current;
476
+ if (!viewport) return;
477
+ const m = measure(viewport);
478
+ if (debugDomAttributes) {
479
+ viewport.dataset.stickyMode = modeRef.current;
480
+ viewport.dataset.distanceToBottom = String(m.distanceToBottom);
481
+ viewport.dataset.isOverflowing = m.isOverflowing ? "1" : "0";
482
+ viewport.dataset.programmaticWrites = String(programmaticWriteCountRef.current);
483
+ }
484
+ onDebugStateChange?.({
485
+ mode: modeRef.current,
486
+ isOverflowing: m.isOverflowing,
487
+ distanceToBottom: m.distanceToBottom,
488
+ scrollTop: m.scrollTop,
489
+ maxScrollTop: m.maxScrollTop,
490
+ scrollHeight: m.scrollHeight,
491
+ clientHeight: m.clientHeight,
492
+ programmaticWrites: programmaticWriteCountRef.current
493
+ });
494
+ setDistanceToBottom(
495
+ (previous) => Math.abs(previous - m.distanceToBottom) <= 0.1 ? previous : m.distanceToBottom
496
+ );
497
+ }, [debugDomAttributes, onDebugStateChange]);
498
+ const writeScrollTop = useCallback((nextTop) => {
499
+ const viewport = viewportRef.current;
500
+ if (!viewport) return;
501
+ viewport.scrollTop = nextTop;
502
+ programmaticWriteCountRef.current += 1;
503
+ lastProgrammaticWriteRef.current = { at: performance.now(), top: nextTop };
504
+ }, []);
505
+ const cancelPendingStick = useCallback(() => {
506
+ if (pendingStickRafRef.current !== null) {
507
+ cancelAnimationFrame(pendingStickRafRef.current);
508
+ pendingStickRafRef.current = null;
509
+ }
510
+ stickRequestedRef.current = false;
511
+ }, []);
512
+ const cancelReturn = useCallback(() => {
513
+ if (returnRafRef.current !== null) {
514
+ cancelAnimationFrame(returnRafRef.current);
515
+ returnRafRef.current = null;
516
+ }
517
+ returnAnimRef.current = null;
518
+ }, []);
519
+ const stickToBottomInstant = useCallback(() => {
520
+ const viewport = viewportRef.current;
521
+ if (!viewport) return;
522
+ stickRequestedRef.current = true;
523
+ if (pendingStickRafRef.current !== null) return;
524
+ pendingStickRafRef.current = requestAnimationFrame(() => {
525
+ pendingStickRafRef.current = null;
526
+ if (!stickRequestedRef.current) return;
527
+ stickRequestedRef.current = false;
528
+ if (modeRef.current !== "STICKY_INSTANT") return;
529
+ const m = measure(viewport);
530
+ writeScrollTop(m.maxScrollTop);
531
+ updateDebug();
532
+ });
533
+ }, [updateDebug, writeScrollTop]);
534
+ const startReturnToBottomSmooth = useCallback(() => {
535
+ const viewport = viewportRef.current;
536
+ if (!viewport) return;
537
+ cancelPendingStick();
538
+ cancelReturn();
539
+ const initial = measure(viewport);
540
+ if (initial.distanceToBottom <= EPS_BOTTOM_PX) {
541
+ setModeBoth("STICKY_INSTANT");
542
+ stickToBottomInstant();
543
+ updateDebug();
544
+ return;
545
+ }
546
+ const durationMs = Math.max(
547
+ RETURN_MIN_MS,
548
+ Math.min(RETURN_MAX_MS, initial.distanceToBottom / RETURN_PX_PER_MS)
549
+ );
550
+ returnAnimRef.current = {
551
+ startTime: performance.now(),
552
+ startTop: initial.scrollTop,
553
+ durationMs,
554
+ settleFrames: 0
555
+ };
556
+ setModeBoth("RETURNING_SMOOTH");
557
+ const tick = (now2) => {
558
+ const viewportCurrent = viewportRef.current;
559
+ const anim = returnAnimRef.current;
560
+ if (!viewportCurrent || !anim || modeRef.current !== "RETURNING_SMOOTH") {
561
+ cancelReturn();
562
+ return;
563
+ }
564
+ const before = measure(viewportCurrent);
565
+ const progress = clamp01((now2 - anim.startTime) / anim.durationMs);
566
+ const eased = easeOutCubic(progress);
567
+ const targetBottom = before.maxScrollTop;
568
+ let nextTop = anim.startTop + (targetBottom - anim.startTop) * eased;
569
+ if (nextTop < before.scrollTop) {
570
+ nextTop = before.scrollTop;
571
+ }
572
+ writeScrollTop(nextTop);
573
+ const after = measure(viewportCurrent);
574
+ if (after.distanceToBottom <= EPS_BOTTOM_PX) {
575
+ anim.settleFrames += 1;
576
+ } else {
577
+ anim.settleFrames = 0;
578
+ }
579
+ const doneByTime = progress >= 1;
580
+ const doneBySettle = anim.settleFrames >= RETURN_SETTLE_FRAMES && progress >= 0.6;
581
+ if (doneByTime && doneBySettle) {
582
+ cancelReturn();
583
+ setModeBoth("STICKY_INSTANT");
584
+ stickToBottomInstant();
585
+ updateDebug();
586
+ return;
587
+ }
588
+ updateDebug();
589
+ returnRafRef.current = requestAnimationFrame(tick);
590
+ };
591
+ returnRafRef.current = requestAnimationFrame(tick);
592
+ }, [cancelPendingStick, cancelReturn, setModeBoth, stickToBottomInstant, updateDebug, writeScrollTop]);
593
+ const onViewportScroll = useCallback(
594
+ (event) => {
595
+ const viewport = event.currentTarget;
596
+ const m = measure(viewport);
597
+ setIsOverflowing((previous) => previous === m.isOverflowing ? previous : m.isOverflowing);
598
+ const previousTop = prevScrollTopRef.current;
599
+ const delta = m.scrollTop - previousTop;
600
+ const lastWrite = lastProgrammaticWriteRef.current;
601
+ const wroteRecently = lastWrite !== null && performance.now() - lastWrite.at < 60;
602
+ const isLikelyOwnWrite = wroteRecently && lastWrite !== null && Math.abs(m.scrollTop - lastWrite.top) <= 2;
603
+ if (modeRef.current === "RETURNING_SMOOTH") {
604
+ if (!isLikelyOwnWrite && delta < -DETACH_SCROLL_DELTA_PX) {
605
+ cancelReturn();
606
+ setModeBoth("DETACHED");
607
+ }
608
+ } else if (modeRef.current === "STICKY_INSTANT") {
609
+ if (!isLikelyOwnWrite && delta < -DETACH_SCROLL_DELTA_PX && m.isOverflowing) {
610
+ cancelPendingStick();
611
+ setModeBoth("DETACHED");
612
+ }
613
+ } else if (modeRef.current === "DETACHED") {
614
+ if (m.distanceToBottom <= EPS_BOTTOM_PX) {
615
+ setModeBoth("STICKY_INSTANT");
616
+ stickToBottomInstant();
617
+ }
618
+ }
619
+ prevScrollTopRef.current = m.scrollTop;
620
+ updateDebug();
621
+ },
622
+ [cancelPendingStick, cancelReturn, setModeBoth, stickToBottomInstant, updateDebug]
623
+ );
624
+ useEffect(() => {
625
+ const viewport = viewportRef.current;
626
+ const content = contentRef.current;
627
+ if (!viewport || !content) return;
628
+ const observer = new ResizeObserver(() => {
629
+ const m = measure(viewport);
630
+ setIsOverflowing((previous) => previous === m.isOverflowing ? previous : m.isOverflowing);
631
+ if (modeRef.current === "STICKY_INSTANT") {
632
+ stickToBottomInstant();
633
+ }
634
+ updateDebug();
635
+ });
636
+ observer.observe(content);
637
+ observer.observe(viewport);
638
+ return () => observer.disconnect();
639
+ }, [stickToBottomInstant, updateDebug]);
640
+ useLayoutEffect(() => {
641
+ const viewport = viewportRef.current;
642
+ if (!viewport) return;
643
+ setModeBoth("STICKY_INSTANT");
644
+ const initial = measure(viewport);
645
+ writeScrollTop(initial.maxScrollTop);
646
+ prevScrollTopRef.current = viewport.scrollTop;
647
+ setIsOverflowing(initial.isOverflowing);
648
+ setDistanceToBottom(initial.distanceToBottom);
649
+ updateDebug();
650
+ }, [setModeBoth, updateDebug, writeScrollTop]);
651
+ useEffect(() => {
652
+ return () => {
653
+ cancelPendingStick();
654
+ cancelReturn();
655
+ };
656
+ }, [cancelPendingStick, cancelReturn]);
657
+ const canJump = isOverflowing && mode === "DETACHED";
658
+ const jumpButton = renderJumpToBottom?.({
659
+ mode,
660
+ canJump,
661
+ isOverflowing,
662
+ distanceToBottom,
663
+ jumpToBottom: startReturnToBottomSmooth
664
+ }) ?? /* @__PURE__ */ jsx(DefaultJumpButton, { canJump, jumpToBottom: startReturnToBottomSmooth });
665
+ const jumpContainerClassNameBase = "pointer-events-none absolute z-20";
666
+ const jumpContainerStyle = {
667
+ position: "absolute",
668
+ zIndex: 20,
669
+ left: 0,
670
+ right: 0,
671
+ bottom: 12,
672
+ display: "flex",
673
+ justifyContent: "center",
674
+ opacity: canJump ? 1 : 0,
675
+ transform: canJump ? "translateY(0px)" : "translateY(4px)",
676
+ transition: "opacity 150ms ease, transform 150ms ease",
677
+ pointerEvents: "none"
678
+ };
679
+ return /* @__PURE__ */ jsxs("div", { className: joinClasses("relative h-full w-full overflow-hidden", className), children: [
680
+ /* @__PURE__ */ jsx(
681
+ "div",
682
+ {
683
+ ref: viewportRef,
684
+ className: joinClasses("h-full w-full overflow-auto", viewportClassName),
685
+ onScroll: onViewportScroll,
686
+ "data-testid": "sticky-scroll-viewport",
687
+ children: /* @__PURE__ */ jsx("div", { ref: contentRef, className: joinClasses("flex min-h-full w-full flex-col", contentClassName), children })
688
+ }
689
+ ),
690
+ showJumpToBottom ? /* @__PURE__ */ jsx(
691
+ "div",
692
+ {
693
+ className: joinClasses(jumpContainerClassNameBase, jumpContainerClassName),
694
+ style: jumpContainerStyle,
695
+ "aria-hidden": !canJump,
696
+ children: /* @__PURE__ */ jsx("div", { className: canJump ? "pointer-events-auto" : "pointer-events-none", children: jumpButton })
697
+ }
698
+ ) : null
699
+ ] });
700
+ }
701
+
387
702
  // src/components/index.ts
388
703
  import { createTrustedHTML, sanitizeHTML } from "@stream-mdx/core";
389
704
 
@@ -619,11 +934,11 @@ function prefetchMdxRuntime() {
619
934
  }
620
935
 
621
936
  // src/renderer/use-deferred-render.ts
622
- import { useEffect, useState } from "react";
937
+ import { useEffect as useEffect2, useState as useState2 } from "react";
623
938
  function useDeferredRender(ref, options) {
624
939
  const enabled = options?.enabled ?? true;
625
- const [shouldRender, setShouldRender] = useState(!enabled);
626
- useEffect(() => {
940
+ const [shouldRender, setShouldRender] = useState2(!enabled);
941
+ useEffect2(() => {
627
942
  if (!enabled) {
628
943
  setShouldRender(true);
629
944
  return;
@@ -948,12 +1263,12 @@ var INLINE_ELEMENTS = /* @__PURE__ */ new Set([
948
1263
  ]);
949
1264
 
950
1265
  // src/components/link-safety-modal.tsx
951
- import { jsx, jsxs } from "react/jsx-runtime";
1266
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
952
1267
  var DefaultLinkSafetyModal = ({ url, isOpen, isChecking, onClose, onConfirm, onCopy }) => {
953
1268
  if (!isOpen) {
954
1269
  return null;
955
1270
  }
956
- return /* @__PURE__ */ jsx(
1271
+ return /* @__PURE__ */ jsx2(
957
1272
  "div",
958
1273
  {
959
1274
  className: "stream-mdx-link-modal-backdrop",
@@ -971,7 +1286,7 @@ var DefaultLinkSafetyModal = ({ url, isOpen, isChecking, onClose, onConfirm, onC
971
1286
  padding: "24px"
972
1287
  },
973
1288
  onClick: onClose,
974
- children: /* @__PURE__ */ jsxs(
1289
+ children: /* @__PURE__ */ jsxs2(
975
1290
  "div",
976
1291
  {
977
1292
  className: "stream-mdx-link-modal",
@@ -986,12 +1301,12 @@ var DefaultLinkSafetyModal = ({ url, isOpen, isChecking, onClose, onConfirm, onC
986
1301
  },
987
1302
  onClick: (event) => event.stopPropagation(),
988
1303
  children: [
989
- /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Open external link?" }),
990
- /* @__PURE__ */ jsx("div", { style: { fontSize: 13, opacity: 0.8, wordBreak: "break-all", marginBottom: 16 }, children: url }),
991
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
992
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onCopy, disabled: isChecking, style: buttonStyle, children: "Copy link" }),
993
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onConfirm, disabled: isChecking, style: primaryButtonStyle, children: "Open link" }),
994
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onClose, style: buttonStyle, children: "Cancel" })
1304
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Open external link?" }),
1305
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 13, opacity: 0.8, wordBreak: "break-all", marginBottom: 16 }, children: url }),
1306
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
1307
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: onCopy, disabled: isChecking, style: buttonStyle, children: "Copy link" }),
1308
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: onConfirm, disabled: isChecking, style: primaryButtonStyle, children: "Open link" }),
1309
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: onClose, style: buttonStyle, children: "Cancel" })
995
1310
  ] })
996
1311
  ]
997
1312
  }
@@ -1166,7 +1481,11 @@ var defaultInlineComponents = {
1166
1481
  const n = number ?? "?";
1167
1482
  const href = number ? `#fn:${n}` : void 0;
1168
1483
  const id = number ? `fnref:${n}` : void 0;
1169
- return React4.createElement("sup", { className: "footnote-ref" }, React4.createElement("a", { href, id, "data-label": label }, String(n)));
1484
+ return React4.createElement(
1485
+ "sup",
1486
+ { className: "footnote-ref" },
1487
+ React4.createElement("a", { href, id, "data-label": label, style: { color: "var(--foreground)", textDecoration: "underline" } }, String(n))
1488
+ );
1170
1489
  }
1171
1490
  };
1172
1491
  var defaultBlockComponents = {
@@ -1309,7 +1628,7 @@ var defaultBlockComponents = {
1309
1628
  dangerouslySetInnerHTML: { __html: htmlString }
1310
1629
  });
1311
1630
  },
1312
- mdx: ({ compiledRef, compiledModule, status, errorMessage, raw }) => {
1631
+ mdx: ({ compiledRef, compiledModule, status, errorMessage }) => {
1313
1632
  const [Comp, setComp] = React4.useState(null);
1314
1633
  const [internalError, setInternalError] = React4.useState(null);
1315
1634
  const [renderError, setRenderError] = React4.useState(null);
@@ -1410,7 +1729,6 @@ var defaultBlockComponents = {
1410
1729
  const waitingForVisibility = deferHydration && !shouldHydrate && Boolean(resolvedId);
1411
1730
  const pendingLabel = waitingForVisibility ? "MDX ready (hydrate on view)\u2026" : compileMode === "worker" ? "Compiling MDX (client)\u2026" : "Compiling MDX (server)\u2026";
1412
1731
  const message = failureMessage ?? (compileMode === "worker" ? "MDX compilation failed in client mode. Try switching back to server rendering or restarting the stream." : "MDX compilation failed. Try restarting the stream.");
1413
- const rawFallback = typeof raw === "string" && raw.trim().length > 0 ? React4.createElement("pre", { className: "markdown-mdx-fallback" }, raw) : null;
1414
1732
  const allowRetry = Boolean(internalError || renderError);
1415
1733
  const retryButton = allowRetry ? React4.createElement(
1416
1734
  "button",
@@ -1422,9 +1740,10 @@ var defaultBlockComponents = {
1422
1740
  "Retry hydration"
1423
1741
  ) : null;
1424
1742
  const errorPanel = React4.createElement(
1425
- "div",
1426
- { className: "markdown-mdx-error" },
1427
- React4.createElement(React4.Fragment, null, React4.createElement("strong", null, "MDX failed"), React4.createElement("div", null, message))
1743
+ React4.Fragment,
1744
+ null,
1745
+ React4.createElement("strong", null, "MDX failed"),
1746
+ React4.createElement("div", null, message)
1428
1747
  );
1429
1748
  if (Comp && !failureMessage) {
1430
1749
  return React4.createElement(
@@ -1447,14 +1766,13 @@ var defaultBlockComponents = {
1447
1766
  return React4.createElement(
1448
1767
  "div",
1449
1768
  {
1450
- className: "markdown-mdx",
1769
+ className: "markdown-mdx error",
1451
1770
  ref: containerRef,
1452
1771
  "data-mdx-ref": pendingId,
1453
1772
  "data-mdx-status": "error",
1454
1773
  "data-mdx-mode": compileMode
1455
1774
  },
1456
1775
  errorPanel,
1457
- rawFallback,
1458
1776
  retryButton
1459
1777
  );
1460
1778
  }
@@ -1481,7 +1799,16 @@ var defaultBlockComponents = {
1481
1799
  { key: item.number, id: `fn:${item.number}` },
1482
1800
  renderInlineNodes(item.inlines, defaultInlineComponents),
1483
1801
  " ",
1484
- React4.createElement("a", { href: `#fnref:${item.number}`, className: "footnote-backref", "aria-label": "Back to content" }, "\u21A9")
1802
+ React4.createElement(
1803
+ "a",
1804
+ {
1805
+ href: `#fnref:${item.number}`,
1806
+ className: "footnote-backref",
1807
+ "aria-label": "Back to content",
1808
+ style: { color: "var(--foreground)", textDecoration: "underline" }
1809
+ },
1810
+ "\u21A9"
1811
+ )
1485
1812
  )
1486
1813
  );
1487
1814
  return React4.createElement("section", { className: "footnotes" }, React4.createElement("hr", {}), React4.createElement("ol", {}, listItems));
@@ -2021,19 +2348,20 @@ init_mdx_client();
2021
2348
 
2022
2349
  // src/mdx-coordinator.ts
2023
2350
  init_mdx_client();
2024
- import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
2351
+ import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
2025
2352
  function useMdxCoordinator(blocks, compileEndpoint, options) {
2026
- const mdxClientRef = useRef(getMDXClient(compileEndpoint));
2353
+ const mdxClientRef = useRef2(getMDXClient(compileEndpoint));
2027
2354
  const mode = options?.mode ?? "server";
2028
- const [compiledMap, setCompiledMap] = useState2(/* @__PURE__ */ new Map());
2029
- const blockSignatureRef = useRef(/* @__PURE__ */ new Map());
2030
- const applyCompiledRef = useCallback(
2355
+ const [compiledMap, setCompiledMap] = useState3(/* @__PURE__ */ new Map());
2356
+ const blockSignatureRef = useRef2(/* @__PURE__ */ new Map());
2357
+ const failedSignatureRef = useRef2(/* @__PURE__ */ new Map());
2358
+ const applyCompiledRef = useCallback2(
2031
2359
  (block, compiledId) => {
2032
2360
  if (mode === "worker") {
2033
2361
  return;
2034
2362
  }
2035
2363
  if (options?.workerClient) {
2036
- options.workerClient.setMdxCompiled(block.id, compiledId);
2364
+ options.workerClient.setMdxCompiled(block.id, compiledId, block.payload.raw ?? "");
2037
2365
  return;
2038
2366
  }
2039
2367
  const store = options?.store;
@@ -2044,6 +2372,9 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
2044
2372
  const index = currentBlocks.findIndex((candidate) => candidate.id === block.id);
2045
2373
  if (index === -1) return;
2046
2374
  const current = currentBlocks[index];
2375
+ if ((current.payload.raw ?? "") !== (block.payload.raw ?? "")) {
2376
+ return;
2377
+ }
2047
2378
  if (current.payload.compiledMdxRef?.id === compiledId) {
2048
2379
  return;
2049
2380
  }
@@ -2069,11 +2400,17 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
2069
2400
  },
2070
2401
  [options?.workerClient, options?.store, mode]
2071
2402
  );
2072
- useEffect2(() => {
2403
+ useEffect3(() => {
2073
2404
  if (mode === "worker") {
2074
2405
  return;
2075
2406
  }
2076
- const targets = blocks.filter((b) => b.type === "mdx" && b.isFinalized && !b.payload.compiledMdxRef && !compiledMap.has(b.id));
2407
+ const targets = blocks.filter((b) => {
2408
+ if (b.type !== "mdx" || !b.isFinalized || b.payload.compiledMdxRef || compiledMap.has(b.id)) {
2409
+ return false;
2410
+ }
2411
+ const raw = b.payload.raw ?? "";
2412
+ return failedSignatureRef.current.get(b.id) !== raw;
2413
+ });
2077
2414
  if (targets.length === 0) return;
2078
2415
  let cancelled = false;
2079
2416
  (async () => {
@@ -2092,22 +2429,29 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
2092
2429
  next.set(block.id, compiled.id);
2093
2430
  return next;
2094
2431
  });
2432
+ failedSignatureRef.current.delete(block.id);
2095
2433
  applyCompiledRef(block, compiled.id);
2096
2434
  } catch (e) {
2097
2435
  console.warn("MDX compile failed for block", block.id, e);
2436
+ failedSignatureRef.current.set(block.id, block.payload.raw ?? "");
2098
2437
  if (options?.workerClient) {
2099
- options.workerClient.setMdxError(block.id, e instanceof Error ? e.message : String(e));
2438
+ options.workerClient.setMdxError(block.id, e instanceof Error ? e.message : String(e), block.payload.raw ?? "");
2100
2439
  } else if (options?.store) {
2440
+ const currentBlocks = options.store.getBlocks();
2441
+ const current = currentBlocks.find((candidate) => candidate.id === block.id);
2442
+ if (!current || (current.payload.raw ?? "") !== (block.payload.raw ?? "")) {
2443
+ continue;
2444
+ }
2101
2445
  options.store.applyPatches([
2102
2446
  {
2103
2447
  op: "setProps",
2104
2448
  at: { blockId: block.id, nodeId: block.id },
2105
2449
  props: {
2106
2450
  block: {
2107
- ...block,
2451
+ ...current,
2108
2452
  payload: {
2109
- ...block.payload,
2110
- meta: { ...block.payload.meta ?? {}, mdxStatus: "error", mdxError: e instanceof Error ? e.message : String(e) }
2453
+ ...current.payload,
2454
+ meta: { ...current.payload.meta ?? {}, mdxStatus: "error", mdxError: e instanceof Error ? e.message : String(e) }
2111
2455
  }
2112
2456
  }
2113
2457
  }
@@ -2121,7 +2465,7 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
2121
2465
  cancelled = true;
2122
2466
  };
2123
2467
  }, [blocks, compiledMap, applyCompiledRef, options?.workerClient, options?.store, mode]);
2124
- useEffect2(() => {
2468
+ useEffect3(() => {
2125
2469
  if (mode === "worker") {
2126
2470
  return;
2127
2471
  }
@@ -2140,6 +2484,7 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
2140
2484
  const currentSignature = candidate.payload.raw ?? "";
2141
2485
  if (previousSignature !== void 0 && previousSignature !== currentSignature) {
2142
2486
  next.delete(blockId);
2487
+ failedSignatureRef.current.delete(blockId);
2143
2488
  mutated = true;
2144
2489
  }
2145
2490
  }
@@ -2149,11 +2494,14 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
2149
2494
  for (const block of blocks) {
2150
2495
  if (block.type === "mdx") {
2151
2496
  signatureMap.set(block.id, block.payload.raw ?? "");
2497
+ if (!block.isFinalized || block.payload.compiledMdxRef) {
2498
+ failedSignatureRef.current.delete(block.id);
2499
+ }
2152
2500
  }
2153
2501
  }
2154
2502
  blockSignatureRef.current = signatureMap;
2155
2503
  }, [blocks, mode]);
2156
- const derivedBlocks = useMemo(() => {
2504
+ const derivedBlocks = useMemo2(() => {
2157
2505
  if (mode === "worker") {
2158
2506
  return blocks;
2159
2507
  }
@@ -2336,7 +2684,7 @@ async function prefetchMdxBundle(request) {
2336
2684
  }
2337
2685
 
2338
2686
  // src/math/display-wrapper.tsx
2339
- import { jsx as jsx2 } from "react/jsx-runtime";
2687
+ import { jsx as jsx3 } from "react/jsx-runtime";
2340
2688
  var BASE_CLASS = "katex-block-wrapper my-2";
2341
2689
  var SCROLL_ROOT_CLASS = "katex-scroll relative overflow-x-hidden w-full max-w-full";
2342
2690
  var VIEWPORT_CLASS = "katex-scroll-viewport h-full w-full rounded-[inherit]";
@@ -2351,14 +2699,14 @@ function combineClassNames(base, extra) {
2351
2699
  }
2352
2700
  function DisplayMathWrapper({ children, className = "" }) {
2353
2701
  const wrapperClass = combineClassNames(BASE_CLASS, className);
2354
- return /* @__PURE__ */ jsx2("div", { className: wrapperClass, children: /* @__PURE__ */ jsx2("div", { dir: "ltr", className: SCROLL_ROOT_CLASS, children: /* @__PURE__ */ jsx2("div", { className: VIEWPORT_CLASS, style: VIEWPORT_STYLE, children: /* @__PURE__ */ jsx2("div", { style: SHIM_STYLE, children }) }) }) });
2702
+ return /* @__PURE__ */ jsx3("div", { className: wrapperClass, children: /* @__PURE__ */ jsx3("div", { dir: "ltr", className: SCROLL_ROOT_CLASS, children: /* @__PURE__ */ jsx3("div", { className: VIEWPORT_CLASS, style: VIEWPORT_STYLE, children: /* @__PURE__ */ jsx3("div", { style: SHIM_STYLE, children }) }) }) });
2355
2703
  }
2356
2704
  function renderDisplayMathWithHtml(html, className) {
2357
- return /* @__PURE__ */ jsx2(DisplayMathWrapper, { className, children: /* @__PURE__ */ jsx2("div", { className: "katex-block", dangerouslySetInnerHTML: { __html: html } }) });
2705
+ return /* @__PURE__ */ jsx3(DisplayMathWrapper, { className, children: /* @__PURE__ */ jsx3("div", { className: "katex-block", dangerouslySetInnerHTML: { __html: html } }) });
2358
2706
  }
2359
2707
 
2360
2708
  // src/renderer.tsx
2361
- import React9, { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
2709
+ import React9, { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "react";
2362
2710
  import { createDefaultWorker, releaseDefaultWorker } from "@stream-mdx/worker";
2363
2711
  init_mdx_client();
2364
2712
 
@@ -2370,7 +2718,10 @@ function useCodeHighlightRequester() {
2370
2718
  }
2371
2719
 
2372
2720
  // src/renderer/patch-batching.ts
2373
- import { isHeavyPatch, splitPatchBatch } from "@stream-mdx/core/perf/patch-batching";
2721
+ import { getPatchKind, isHeavyPatch, splitPatchBatch } from "@stream-mdx/core/perf/patch-batching";
2722
+
2723
+ // src/renderer/patch-commit-scheduler.ts
2724
+ import { getPatchKind as getPatchKind2 } from "@stream-mdx/core/perf/patch-batching";
2374
2725
 
2375
2726
  // src/renderer/patch-coalescing.ts
2376
2727
  import {
@@ -2388,6 +2739,9 @@ var COALESCING_DURATION_ACTIVATE_MS = 6;
2388
2739
  var COALESCING_DURATION_DEACTIVATE_MS = 4;
2389
2740
  var RETAIN_TOUCHED_IN_HISTORY = typeof process !== "undefined" && process.env ? process.env.NODE_ENV !== "production" : true;
2390
2741
  var EMPTY_TOUCHED_SET = /* @__PURE__ */ new Set();
2742
+ function shouldSkipCoalescing(patches) {
2743
+ return patches.length <= 1;
2744
+ }
2391
2745
  function getDefaultNow() {
2392
2746
  if (typeof performance !== "undefined" && typeof performance.now === "function") {
2393
2747
  return () => performance.now();
@@ -2414,8 +2768,9 @@ function getDefaultCancelRaf() {
2414
2768
  }
2415
2769
  var PatchCommitScheduler = class {
2416
2770
  constructor(params) {
2417
- this.highQueue = [];
2418
- this.lowQueue = [];
2771
+ this.semanticQueue = [];
2772
+ this.enrichmentHighQueue = [];
2773
+ this.enrichmentLowQueue = [];
2419
2774
  this.scheduled = false;
2420
2775
  this.flushing = false;
2421
2776
  this.scheduledHandle = null;
@@ -2427,6 +2782,7 @@ var PatchCommitScheduler = class {
2427
2782
  this.scheduleToken = 0;
2428
2783
  this.coalescingDurationSamples = [];
2429
2784
  this.adaptiveBudgetActive = false;
2785
+ this.flushCount = 0;
2430
2786
  this.store = params.store;
2431
2787
  this.onFlush = params.onFlush;
2432
2788
  this.frameBudgetMs = Math.max(1, params.options?.frameBudgetMs ?? 10);
@@ -2442,6 +2798,8 @@ var PatchCommitScheduler = class {
2442
2798
  this.maxLowPriorityBatchesPerFlush = params.options?.maxLowPriorityBatchesPerFlush ?? 2;
2443
2799
  this.urgentQueueThreshold = Math.max(1, params.options?.urgentQueueThreshold ?? 4);
2444
2800
  this.historyLimit = Math.max(1, params.options?.historyLimit ?? 200);
2801
+ this.startupMicrotaskFlushes = Math.max(0, Math.floor(params.options?.startupMicrotaskFlushes ?? 0));
2802
+ this.adaptiveBudgeting = params.options?.adaptiveBudgeting ?? true;
2445
2803
  const requestedBatch = params.options?.batch;
2446
2804
  if (requestedBatch === "microtask") {
2447
2805
  this.batchStrategy = "microtask";
@@ -2468,19 +2826,23 @@ var PatchCommitScheduler = class {
2468
2826
  patches: input.patches,
2469
2827
  meta: input.meta ?? {},
2470
2828
  receivedAt: input.meta?.receivedAt ?? this.now(),
2829
+ kind: input.meta?.kind === "semantic" || input.meta?.kind === "enrichment" ? input.meta.kind : input.patches.some((patch) => getPatchKind2(patch) === "semantic") ? "semantic" : "enrichment",
2471
2830
  priority: input.meta?.priority === "low" ? "low" : "high"
2472
2831
  };
2473
- if (batch.priority === "low") {
2474
- this.lowQueue.push(batch);
2832
+ if (batch.kind === "semantic") {
2833
+ batch.priority = "high";
2834
+ this.semanticQueue.push(batch);
2835
+ } else if (batch.priority === "low") {
2836
+ this.enrichmentLowQueue.push(batch);
2475
2837
  } else {
2476
- this.highQueue.push(batch);
2838
+ this.enrichmentHighQueue.push(batch);
2477
2839
  }
2478
2840
  if (!this.paused) {
2479
2841
  this.schedule();
2480
2842
  }
2481
2843
  }
2482
2844
  flushAll() {
2483
- if (this.highQueue.length === 0 && this.lowQueue.length === 0 && !this.flushing) {
2845
+ if (this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0 && !this.flushing) {
2484
2846
  return null;
2485
2847
  }
2486
2848
  this.cancelScheduled();
@@ -2490,9 +2852,11 @@ var PatchCommitScheduler = class {
2490
2852
  }
2491
2853
  clear() {
2492
2854
  this.cancelScheduled();
2493
- this.highQueue = [];
2494
- this.lowQueue = [];
2855
+ this.semanticQueue = [];
2856
+ this.enrichmentHighQueue = [];
2857
+ this.enrichmentLowQueue = [];
2495
2858
  this.flushing = false;
2859
+ this.flushCount = 0;
2496
2860
  this.coalescingDurationSamples = [];
2497
2861
  this.adaptiveBudgetActive = false;
2498
2862
  this.adaptiveHighBatchCap = void 0;
@@ -2500,10 +2864,10 @@ var PatchCommitScheduler = class {
2500
2864
  this.maybeResolveIdle();
2501
2865
  }
2502
2866
  getPendingCount() {
2503
- return this.highQueue.length + this.lowQueue.length;
2867
+ return this.semanticQueue.length + this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
2504
2868
  }
2505
2869
  isIdle() {
2506
- return !this.flushing && !this.scheduled && this.highQueue.length === 0 && this.lowQueue.length === 0;
2870
+ return !this.flushing && !this.scheduled && this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0;
2507
2871
  }
2508
2872
  awaitIdle() {
2509
2873
  if (this.isIdle()) {
@@ -2543,7 +2907,8 @@ var PatchCommitScheduler = class {
2543
2907
  this.runFlush(this.frameBudgetMs, false);
2544
2908
  this.maybeResolveIdle();
2545
2909
  };
2546
- if (this.batchStrategy === "microtask" && typeof queueMicrotask === "function") {
2910
+ const useStartupMicrotask = this.batchStrategy !== "microtask" && this.startupMicrotaskFlushes > 0 && this.flushCount < this.startupMicrotaskFlushes && typeof queueMicrotask === "function";
2911
+ if ((this.batchStrategy === "microtask" || useStartupMicrotask) && typeof queueMicrotask === "function") {
2547
2912
  this.scheduledViaRaf = false;
2548
2913
  this.scheduledHandle = token;
2549
2914
  queueMicrotask(execute);
@@ -2573,12 +2938,14 @@ var PatchCommitScheduler = class {
2573
2938
  if (this.paused && !manual) {
2574
2939
  return null;
2575
2940
  }
2576
- if (this.highQueue.length === 0 && this.lowQueue.length === 0) {
2941
+ if (this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0) {
2577
2942
  return null;
2578
2943
  }
2579
2944
  this.flushing = true;
2580
2945
  const startedAt = this.now();
2581
- const initialQueueSize = this.highQueue.length + this.lowQueue.length;
2946
+ const initialSemanticQueueSize = this.semanticQueue.length;
2947
+ const initialEnrichmentQueueSize = this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
2948
+ const initialQueueSize = initialSemanticQueueSize + initialEnrichmentQueueSize;
2582
2949
  const batches = [];
2583
2950
  let totalInputPatches = 0;
2584
2951
  let totalAppliedPatches = 0;
@@ -2593,7 +2960,15 @@ var PatchCommitScheduler = class {
2593
2960
  consumed += 1;
2594
2961
  const applyStart = this.now();
2595
2962
  const queueDelayMs = Math.max(0, applyStart - next.receivedAt);
2596
- const { patches: coalesced, metrics: coalescingMetrics } = coalescePatchesWithMetrics(next.patches, DEFAULT_COALESCE_CONFIG);
2963
+ let coalesced;
2964
+ let coalescingMetrics;
2965
+ if (shouldSkipCoalescing(next.patches)) {
2966
+ coalesced = next.patches;
2967
+ } else {
2968
+ const result = coalescePatchesWithMetrics(next.patches, DEFAULT_COALESCE_CONFIG);
2969
+ coalesced = result.patches;
2970
+ coalescingMetrics = result.metrics;
2971
+ }
2597
2972
  const touched = this.store.applyPatches(coalesced, {
2598
2973
  coalesced: true,
2599
2974
  metrics: coalescingMetrics
@@ -2613,6 +2988,7 @@ var PatchCommitScheduler = class {
2613
2988
  receivedAt: next.receivedAt,
2614
2989
  appliedAt: applyStart,
2615
2990
  priority: next.priority,
2991
+ kind: next.kind,
2616
2992
  coalescing: coalescingMetrics
2617
2993
  });
2618
2994
  if (maxBatches && batches.length - startingBatchCount >= maxBatches) {
@@ -2629,27 +3005,32 @@ var PatchCommitScheduler = class {
2629
3005
  queue.splice(0, consumed);
2630
3006
  }
2631
3007
  };
2632
- const initialHighQueueSize = this.highQueue.length;
3008
+ processQueue(this.semanticQueue, void 0, Number.POSITIVE_INFINITY);
3009
+ const initialHighQueueSize = this.enrichmentHighQueue.length;
2633
3010
  const configuredHighLimit = manual || !this.maxBatchesPerFlush ? void 0 : initialHighQueueSize > this.maxBatchesPerFlush ? void 0 : this.maxBatchesPerFlush;
2634
- const highBatchLimit = this.adaptiveHighBatchCap ?? configuredHighLimit;
2635
- processQueue(this.highQueue, highBatchLimit, budgetMs);
3011
+ const highBatchLimit = this.adaptiveBudgeting ? this.adaptiveHighBatchCap ?? configuredHighLimit : configuredHighLimit;
3012
+ processQueue(this.enrichmentHighQueue, highBatchLimit, budgetMs);
2636
3013
  if (manual) {
2637
- processQueue(this.lowQueue, void 0, Number.POSITIVE_INFINITY);
2638
- } else if (this.lowQueue.length > 0) {
3014
+ processQueue(this.enrichmentLowQueue, void 0, Number.POSITIVE_INFINITY);
3015
+ } else if (this.enrichmentLowQueue.length > 0) {
2639
3016
  elapsed = this.now() - startedAt;
2640
3017
  const budgetForLow = Math.max(0, this.lowPriorityFrameBudgetMs - elapsed);
2641
- if (budgetForLow > 0) {
2642
- const initialLowQueueSize = this.lowQueue.length;
3018
+ const processedHigherPriorityWork = batches.length > 0;
3019
+ if (budgetForLow > 0 || !processedHigherPriorityWork) {
3020
+ const initialLowQueueSize = this.enrichmentLowQueue.length;
2643
3021
  const configuredLowLimit = manual || !this.maxLowPriorityBatchesPerFlush ? void 0 : initialLowQueueSize > this.maxLowPriorityBatchesPerFlush ? void 0 : this.maxLowPriorityBatchesPerFlush;
2644
- const lowBatchLimit = this.adaptiveLowBatchCap ?? configuredLowLimit;
2645
- processQueue(this.lowQueue, lowBatchLimit, budgetForLow);
3022
+ const lowBatchLimit = this.adaptiveBudgeting ? this.adaptiveLowBatchCap ?? configuredLowLimit : configuredLowLimit;
3023
+ processQueue(this.enrichmentLowQueue, lowBatchLimit, budgetForLow);
2646
3024
  }
2647
3025
  }
2648
3026
  this.flushing = false;
2649
3027
  const completedAt = this.now();
2650
3028
  const totalDurationMs = completedAt - startedAt;
2651
- const remaining = this.highQueue.length + this.lowQueue.length;
3029
+ const remainingSemantic = this.semanticQueue.length;
3030
+ const remainingEnrichment = this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
3031
+ const remaining = remainingSemantic + remainingEnrichment;
2652
3032
  if (batches.length > 0) {
3033
+ this.flushCount += 1;
2653
3034
  try {
2654
3035
  this.onFlush({
2655
3036
  batches,
@@ -2657,7 +3038,11 @@ var PatchCommitScheduler = class {
2657
3038
  totalAppliedPatches,
2658
3039
  totalDurationMs,
2659
3040
  remainingQueueSize: remaining,
3041
+ remainingSemanticQueueSize: remainingSemantic,
3042
+ remainingEnrichmentQueueSize: remainingEnrichment,
2660
3043
  queueDepthBefore: initialQueueSize,
3044
+ semanticQueueDepthBefore: initialSemanticQueueSize,
3045
+ enrichmentQueueDepthBefore: initialEnrichmentQueueSize,
2661
3046
  flushStartedAt: startedAt,
2662
3047
  flushCompletedAt: completedAt
2663
3048
  });
@@ -2668,21 +3053,27 @@ var PatchCommitScheduler = class {
2668
3053
  if (remaining > 0 && !this.paused) {
2669
3054
  this.schedule();
2670
3055
  }
2671
- const coalescingStats = this.computeCoalescingDurationStats();
2672
- this.updateAdaptiveBudget(coalescingStats.p95);
2673
- const adaptiveState = this.snapshotAdaptiveBudget(coalescingStats.p95 ?? null);
3056
+ const coalescingStats = this.adaptiveBudgeting ? this.computeCoalescingDurationStats() : { sampleCount: this.coalescingDurationSamples.length, p95: null };
3057
+ if (this.adaptiveBudgeting) {
3058
+ this.updateAdaptiveBudget(coalescingStats.p95);
3059
+ }
3060
+ const adaptiveState = this.adaptiveBudgeting ? this.snapshotAdaptiveBudget(coalescingStats.p95 ?? null) : void 0;
2674
3061
  const flushResult = batches.length > 0 ? {
2675
3062
  batches,
2676
3063
  totalPatches: totalInputPatches,
2677
3064
  totalAppliedPatches,
2678
3065
  totalDurationMs,
2679
3066
  remainingQueueSize: remaining,
3067
+ remainingSemanticQueueSize: remainingSemantic,
3068
+ remainingEnrichmentQueueSize: remainingEnrichment,
2680
3069
  queueDepthBefore: initialQueueSize,
3070
+ semanticQueueDepthBefore: initialSemanticQueueSize,
3071
+ enrichmentQueueDepthBefore: initialEnrichmentQueueSize,
2681
3072
  flushStartedAt: startedAt,
2682
3073
  flushCompletedAt: completedAt,
2683
3074
  coalescingDurationP95: coalescingStats.p95 ?? void 0,
2684
3075
  coalescingDurationSampleCount: coalescingStats.sampleCount,
2685
- adaptiveBudgetActive: adaptiveState.active,
3076
+ adaptiveBudgetActive: adaptiveState?.active,
2686
3077
  adaptiveBudgetState: adaptiveState
2687
3078
  } : null;
2688
3079
  if (flushResult) {
@@ -2718,6 +3109,9 @@ var PatchCommitScheduler = class {
2718
3109
  };
2719
3110
  }
2720
3111
  updateAdaptiveBudget(p95) {
3112
+ if (!this.adaptiveBudgeting) {
3113
+ return;
3114
+ }
2721
3115
  if (p95 !== null && p95 > COALESCING_DURATION_ACTIVATE_MS) {
2722
3116
  if (!this.adaptiveBudgetActive) {
2723
3117
  this.adaptiveBudgetActive = true;
@@ -2791,11 +3185,13 @@ var PatchCommitScheduler = class {
2791
3185
  }
2792
3186
  restart() {
2793
3187
  this.cancelScheduled();
2794
- this.highQueue = [];
2795
- this.lowQueue = [];
3188
+ this.semanticQueue = [];
3189
+ this.enrichmentHighQueue = [];
3190
+ this.enrichmentLowQueue = [];
2796
3191
  this.flushing = false;
2797
3192
  this.sequence = 0;
2798
3193
  this.history = [];
3194
+ this.flushCount = 0;
2799
3195
  this.scheduleToken++;
2800
3196
  this.paused = false;
2801
3197
  this.coalescingDurationSamples = [];
@@ -2842,6 +3238,12 @@ var PatchCommitScheduler = class {
2842
3238
  if (typeof options.historyLimit !== "undefined") {
2843
3239
  this.setHistoryLimit(options.historyLimit);
2844
3240
  }
3241
+ if (typeof options.startupMicrotaskFlushes !== "undefined") {
3242
+ this.startupMicrotaskFlushes = Math.max(0, Math.floor(options.startupMicrotaskFlushes));
3243
+ }
3244
+ if (typeof options.adaptiveBudgeting !== "undefined") {
3245
+ this.adaptiveBudgeting = options.adaptiveBudgeting;
3246
+ }
2845
3247
  if (typeof options.batch !== "undefined") {
2846
3248
  const requestedBatch = options.batch;
2847
3249
  if (requestedBatch === "microtask") {
@@ -2876,6 +3278,7 @@ import {
2876
3278
  sanitizeHTML as sanitizeHTML2,
2877
3279
  PATCH_ROOT_ID
2878
3280
  } from "@stream-mdx/core";
3281
+ import { getPatchKind as getPatchKind3 } from "@stream-mdx/core/perf/patch-batching";
2879
3282
 
2880
3283
  // src/renderer/list-utils.ts
2881
3284
  function updateNodeDepth(node, depth) {
@@ -2971,12 +3374,28 @@ function snapshotToBlock(node) {
2971
3374
  }
2972
3375
  return null;
2973
3376
  }
2974
- var nodeSnapshotCache = /* @__PURE__ */ new Map();
2975
- var childrenSnapshotCache = /* @__PURE__ */ new Map();
3377
+ var nodeSnapshotCacheByMap = /* @__PURE__ */ new WeakMap();
3378
+ var childrenSnapshotCacheByMap = /* @__PURE__ */ new WeakMap();
2976
3379
  var NODE_SNAPSHOT_CACHE_LIMIT = 5e4;
2977
3380
  var CHILDREN_SNAPSHOT_CACHE_LIMIT = 5e4;
2978
3381
  var NODE_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(NODE_SNAPSHOT_CACHE_LIMIT * 0.1));
2979
3382
  var CHILDREN_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(CHILDREN_SNAPSHOT_CACHE_LIMIT * 0.1));
3383
+ function getNodeSnapshotCacheForMap(map) {
3384
+ let cache = nodeSnapshotCacheByMap.get(map);
3385
+ if (!cache) {
3386
+ cache = /* @__PURE__ */ new Map();
3387
+ nodeSnapshotCacheByMap.set(map, cache);
3388
+ }
3389
+ return cache;
3390
+ }
3391
+ function getChildrenSnapshotCacheForMap(map) {
3392
+ let cache = childrenSnapshotCacheByMap.get(map);
3393
+ if (!cache) {
3394
+ cache = /* @__PURE__ */ new Map();
3395
+ childrenSnapshotCacheByMap.set(map, cache);
3396
+ }
3397
+ return cache;
3398
+ }
2980
3399
  function pruneCache(cache, max, buffer) {
2981
3400
  if (cache.size <= max + buffer) return;
2982
3401
  let remaining = cache.size - max;
@@ -2986,6 +3405,14 @@ function pruneCache(cache, max, buffer) {
2986
3405
  if (remaining <= 0) break;
2987
3406
  }
2988
3407
  }
3408
+ function deleteSnapshotCacheEntry(map, id) {
3409
+ getNodeSnapshotCacheForMap(map).delete(id);
3410
+ getChildrenSnapshotCacheForMap(map).delete(id);
3411
+ }
3412
+ function clearSnapshotCaches(map) {
3413
+ getNodeSnapshotCacheForMap(map).clear();
3414
+ getChildrenSnapshotCacheForMap(map).clear();
3415
+ }
2989
3416
  var EMPTY_CHILDREN = Object.freeze([]);
2990
3417
  var EMPTY_NODE_SNAPSHOT = Object.freeze({ version: -1, node: void 0 });
2991
3418
  var EMPTY_CHILDREN_SNAPSHOT = Object.freeze({
@@ -3153,9 +3580,57 @@ function createRootRecord() {
3153
3580
  parentId: null,
3154
3581
  children: [],
3155
3582
  props: {},
3156
- version: 0
3583
+ version: 0,
3584
+ blockEpoch: 0
3157
3585
  };
3158
3586
  }
3587
+ function readNumericEpoch(value) {
3588
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
3589
+ return void 0;
3590
+ }
3591
+ return value;
3592
+ }
3593
+ function readSnapshotBlockEpoch(snapshot) {
3594
+ return readNumericEpoch(snapshot.meta?.blockEpoch);
3595
+ }
3596
+ function readPatchBlockEpoch(patch) {
3597
+ if (patch.op === "setHTML") {
3598
+ return readNumericEpoch(patch.patchMeta?.blockEpoch);
3599
+ }
3600
+ return readNumericEpoch(patch.meta?.blockEpoch);
3601
+ }
3602
+ function readPatchParseEpoch(patch) {
3603
+ if (patch.op === "setHTML") {
3604
+ return readNumericEpoch(patch.patchMeta?.parseEpoch);
3605
+ }
3606
+ return readNumericEpoch(patch.meta?.parseEpoch);
3607
+ }
3608
+ function readPatchTx(patch) {
3609
+ if (patch.op === "setHTML") {
3610
+ return readNumericEpoch(patch.patchMeta?.tx);
3611
+ }
3612
+ return readNumericEpoch(patch.meta?.tx);
3613
+ }
3614
+ function assignSubtreeBlockEpoch(map, rootId, blockEpoch, touched) {
3615
+ const root = map.get(rootId);
3616
+ if (!root) return;
3617
+ const stack = [root];
3618
+ while (stack.length > 0) {
3619
+ const node = stack.pop();
3620
+ if (!node) continue;
3621
+ if (node.blockEpoch !== blockEpoch) {
3622
+ node.blockEpoch = blockEpoch;
3623
+ node.version += 1;
3624
+ touched?.add(node.id);
3625
+ }
3626
+ for (const childId of node.children) {
3627
+ const child = map.get(childId);
3628
+ if (child) {
3629
+ stack.push(child);
3630
+ }
3631
+ }
3632
+ }
3633
+ }
3159
3634
  function ensureArray(value) {
3160
3635
  return Array.isArray(value) ? value : [];
3161
3636
  }
@@ -3215,7 +3690,7 @@ function escapeHtml2(value) {
3215
3690
  }
3216
3691
  function stripOuterLineSpan(html) {
3217
3692
  if (!html) return null;
3218
- const openTagMatch = html.match(/<span[^>]*>/i);
3693
+ const openTagMatch = html.match(/<span[^>]*class="[^"]*\bline\b[^"]*"[^>]*>/i);
3219
3694
  if (!openTagMatch) {
3220
3695
  return null;
3221
3696
  }
@@ -3264,8 +3739,7 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
3264
3739
  if (node.type !== "code-line") {
3265
3740
  anomalies.push(`type-mismatch:${node.id}:${node.type}`);
3266
3741
  nodes.delete(node.id);
3267
- nodeSnapshotCache.delete(node.id);
3268
- childrenSnapshotCache.delete(node.id);
3742
+ deleteSnapshotCacheEntry(nodes, node.id);
3269
3743
  touched.add(node.id);
3270
3744
  continue;
3271
3745
  }
@@ -3309,16 +3783,14 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
3309
3783
  const existing = nodes.get(desiredId);
3310
3784
  if (existing && existing !== node) {
3311
3785
  nodes.delete(desiredId);
3312
- nodeSnapshotCache.delete(desiredId);
3313
- childrenSnapshotCache.delete(desiredId);
3786
+ deleteSnapshotCacheEntry(nodes, desiredId);
3314
3787
  touched.add(desiredId);
3315
3788
  mutated = true;
3316
3789
  }
3317
3790
  if (node.id !== desiredId) {
3318
3791
  const previousId = node.id;
3319
3792
  nodes.delete(node.id);
3320
- nodeSnapshotCache.delete(previousId);
3321
- childrenSnapshotCache.delete(previousId);
3793
+ deleteSnapshotCacheEntry(nodes, previousId);
3322
3794
  node.id = desiredId;
3323
3795
  nodes.set(desiredId, node);
3324
3796
  touched.add(desiredId);
@@ -3349,8 +3821,7 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
3349
3821
  for (const [id, record] of nodes) {
3350
3822
  if (record.parentId === parent.id && record.type === "code-line" && !allowed.has(id)) {
3351
3823
  nodes.delete(id);
3352
- nodeSnapshotCache.delete(id);
3353
- childrenSnapshotCache.delete(id);
3824
+ deleteSnapshotCacheEntry(nodes, id);
3354
3825
  touched.add(id);
3355
3826
  mutated = true;
3356
3827
  }
@@ -3434,8 +3905,10 @@ function normalizeCodeLineProps(incoming, previous) {
3434
3905
  }
3435
3906
  function createCodeLineProps(index, text, highlight, tokens) {
3436
3907
  const safeText = typeof text === "string" ? text : "";
3437
- const sanitized = sanitizeLineInnerHtml(highlight, safeText);
3438
- const html = sanitized && sanitized.length >= safeText.length ? sanitized : escapeHtml2(safeText);
3908
+ const hasIncomingHighlight = typeof highlight === "string" && highlight.trim().length > 0;
3909
+ const sanitized = hasIncomingHighlight ? sanitizeLineInnerHtml(highlight, safeText) : null;
3910
+ const hasRenderableTokens = tokens !== void 0 && tokens !== null;
3911
+ const html = sanitized && sanitized.length >= safeText.length ? sanitized : hasRenderableTokens ? null : escapeHtml2(safeText);
3439
3912
  const props = {
3440
3913
  index,
3441
3914
  text: safeText,
@@ -3546,6 +4019,18 @@ function validateCodeBlockChildren(nodes, parent) {
3546
4019
  issues.push({ kind: "dangling-node", id });
3547
4020
  }
3548
4021
  }
4022
+ if (parent.block?.isFinalized) {
4023
+ const snapshot = createBlockSnapshot(cloneBlock(parent.block));
4024
+ const expectedChildren = ensureArray(snapshot.children).filter((child) => child.type === "code-line");
4025
+ if (expectedChildren.length !== parent.children.length) {
4026
+ issues.push({
4027
+ kind: "line-count-mismatch",
4028
+ id: parent.id,
4029
+ expected: expectedChildren.length,
4030
+ actual: parent.children.length
4031
+ });
4032
+ }
4033
+ }
3549
4034
  return { ok: issues.length === 0, issues };
3550
4035
  }
3551
4036
  function rebuildCodeBlockFromSnapshot(nodes, parent, touched, issues) {
@@ -3562,7 +4047,7 @@ function rebuildCodeBlockFromSnapshot(nodes, parent, touched, issues) {
3562
4047
  }
3563
4048
  const snapshot = createBlockSnapshot(cloneBlock(parent.block));
3564
4049
  removeSubtree(nodes, parent.id, touched);
3565
- const inserted = insertSnapshotAt(nodes, ancestor, index, snapshot);
4050
+ const inserted = insertSnapshotAt(nodes, ancestor, index, snapshot, touched, parent.blockEpoch);
3566
4051
  if (!inserted) {
3567
4052
  return false;
3568
4053
  }
@@ -3585,7 +4070,55 @@ function arraysEqual(a, b) {
3585
4070
  }
3586
4071
  return true;
3587
4072
  }
3588
- function buildNodeFromSnapshot(map, snapshot, parentId) {
4073
+ function expectedCodeLineId(parentId, index) {
4074
+ return `${parentId}::line:${index}`;
4075
+ }
4076
+ function collectStoreInvariantViolations(nodes) {
4077
+ const violations = [];
4078
+ for (const [id, node] of nodes) {
4079
+ if (node.type === "__root__") continue;
4080
+ if (node.children.length > 1) {
4081
+ const seen = /* @__PURE__ */ new Set();
4082
+ for (const childId of node.children) {
4083
+ if (seen.has(childId)) {
4084
+ violations.push(`duplicate child id:${id}:${childId}`);
4085
+ } else {
4086
+ seen.add(childId);
4087
+ }
4088
+ }
4089
+ }
4090
+ if (node.type === "list") {
4091
+ const parent = node.parentId ? nodes.get(node.parentId) : void 0;
4092
+ if (parent?.type === "list-item" && node.children.length === 0) {
4093
+ violations.push(`empty nested list:${id}`);
4094
+ }
4095
+ }
4096
+ if (node.type === "code") {
4097
+ for (let index = 0; index < node.children.length; index += 1) {
4098
+ const childId = node.children[index];
4099
+ const child = nodes.get(childId);
4100
+ if (!child) {
4101
+ violations.push(`missing code-line node:${node.id}:${childId}`);
4102
+ continue;
4103
+ }
4104
+ if (child.type !== "code-line") {
4105
+ violations.push(`non-code child in code block:${node.id}:${child.id}:${child.type}`);
4106
+ continue;
4107
+ }
4108
+ const expectedId = expectedCodeLineId(node.id, index);
4109
+ if (child.id !== expectedId) {
4110
+ violations.push(`code-line id mismatch:${node.id}:${child.id}:${expectedId}`);
4111
+ }
4112
+ const indexProp = typeof child.props?.index === "number" ? child.props.index : null;
4113
+ if (indexProp !== index) {
4114
+ violations.push(`code-line index mismatch:${node.id}:${child.id}:${indexProp}:${index}`);
4115
+ }
4116
+ }
4117
+ }
4118
+ }
4119
+ return violations;
4120
+ }
4121
+ function buildNodeFromSnapshot(map, snapshot, parentId, blockEpoch) {
3589
4122
  const record = {
3590
4123
  id: snapshot.id,
3591
4124
  type: snapshot.type,
@@ -3595,6 +4128,7 @@ function buildNodeFromSnapshot(map, snapshot, parentId) {
3595
4128
  meta: snapshot.meta ? { ...snapshot.meta } : void 0,
3596
4129
  range: snapshot.range ? { ...snapshot.range } : void 0,
3597
4130
  version: 0,
4131
+ blockEpoch,
3598
4132
  block: snapshotToBlock(snapshot) ?? void 0
3599
4133
  };
3600
4134
  if (record.type === "code") {
@@ -3603,7 +4137,7 @@ function buildNodeFromSnapshot(map, snapshot, parentId) {
3603
4137
  map.set(record.id, record);
3604
4138
  const childSnapshots = ensureArray(snapshot.children);
3605
4139
  for (const child of childSnapshots) {
3606
- const childRecord = buildNodeFromSnapshot(map, child, record.id);
4140
+ const childRecord = buildNodeFromSnapshot(map, child, record.id, blockEpoch);
3607
4141
  record.children.push(childRecord.id);
3608
4142
  }
3609
4143
  return record;
@@ -3631,8 +4165,7 @@ function removeSubtree(map, id, touched) {
3631
4165
  }
3632
4166
  }
3633
4167
  map.delete(node.id);
3634
- nodeSnapshotCache.delete(node.id);
3635
- childrenSnapshotCache.delete(node.id);
4168
+ deleteSnapshotCacheEntry(map, node.id);
3636
4169
  touched.add(node.id);
3637
4170
  for (const childId of node.children) {
3638
4171
  const child = map.get(childId);
@@ -3664,13 +4197,13 @@ function resolveParent(map, path) {
3664
4197
  }
3665
4198
  return map.get(path.blockId);
3666
4199
  }
3667
- function insertSnapshotAt(map, parent, index, snapshot, touched) {
4200
+ function insertSnapshotAt(map, parent, index, snapshot, touched, blockEpoch) {
3668
4201
  if (!parent) return null;
3669
4202
  const existingIndex = parent.children.indexOf(snapshot.id);
3670
4203
  if (existingIndex !== -1) {
3671
4204
  removeSubtree(map, parent.children[existingIndex], touched ?? /* @__PURE__ */ new Set());
3672
4205
  }
3673
- const record = buildNodeFromSnapshot(map, snapshot, parent.id);
4206
+ const record = buildNodeFromSnapshot(map, snapshot, parent.id, blockEpoch ?? readSnapshotBlockEpoch(snapshot) ?? parent.blockEpoch);
3674
4207
  const insertedIds = [];
3675
4208
  const queue = [record];
3676
4209
  while (queue.length > 0) {
@@ -3688,13 +4221,13 @@ function insertSnapshotAt(map, parent, index, snapshot, touched) {
3688
4221
  ensureUniqueChildren(parent, touched);
3689
4222
  return { record, insertedIds };
3690
4223
  }
3691
- function replaceSnapshotAt(map, parent, index, snapshot, touched) {
4224
+ function replaceSnapshotAt(map, parent, index, snapshot, touched, blockEpoch) {
3692
4225
  if (!parent) return;
3693
4226
  if (index < 0 || index >= parent.children.length) return;
3694
4227
  const targetId = parent.children[index];
3695
4228
  parent.children.splice(index, 1);
3696
4229
  removeSubtree(map, targetId, touched);
3697
- const inserted = insertSnapshotAt(map, parent, index, snapshot, touched);
4230
+ const inserted = insertSnapshotAt(map, parent, index, snapshot, touched, blockEpoch);
3698
4231
  if (inserted) {
3699
4232
  for (const id of inserted.insertedIds) {
3700
4233
  touched.add(id);
@@ -3714,6 +4247,24 @@ function createRendererStore(initialBlocks = []) {
3714
4247
  const listeners = /* @__PURE__ */ new Set();
3715
4248
  let notifyScheduled = false;
3716
4249
  let lastCoalescingMetrics = null;
4250
+ let invariantViolations = [];
4251
+ let transientDiagnostics = [];
4252
+ let nextBlockEpoch = 1;
4253
+ const debugCounters = {
4254
+ appendLineGuardRejected: 0,
4255
+ staleEpochRejected: 0,
4256
+ storeInvariantViolationCount: 0
4257
+ };
4258
+ const allocateBlockEpoch = (preferred) => {
4259
+ const resolved = readNumericEpoch(preferred);
4260
+ if (resolved !== void 0) {
4261
+ nextBlockEpoch = Math.max(nextBlockEpoch, resolved + 1);
4262
+ return resolved;
4263
+ }
4264
+ const epoch = nextBlockEpoch;
4265
+ nextBlockEpoch += 1;
4266
+ return epoch;
4267
+ };
3717
4268
  const getRaf = () => {
3718
4269
  if (typeof globalThis !== "undefined" && typeof globalThis.requestAnimationFrame === "function") {
3719
4270
  return globalThis.requestAnimationFrame;
@@ -3745,9 +4296,12 @@ function createRendererStore(initialBlocks = []) {
3745
4296
  }
3746
4297
  }
3747
4298
  }
4299
+ function appendDiagnostic(message) {
4300
+ transientDiagnostics = [...transientDiagnostics, message];
4301
+ invariantViolations = [...invariantViolations, message];
4302
+ }
3748
4303
  function rebuildFromBlocks(blocks) {
3749
- nodeSnapshotCache.clear();
3750
- childrenSnapshotCache.clear();
4304
+ clearSnapshotCaches(nodes);
3751
4305
  nodes = /* @__PURE__ */ new Map();
3752
4306
  const root = createRootRecord();
3753
4307
  nodes.set(root.id, root);
@@ -3756,9 +4310,12 @@ function createRendererStore(initialBlocks = []) {
3756
4310
  for (let i = 0; i < blocks.length; i++) {
3757
4311
  const block = cloneBlock(blocks[i]);
3758
4312
  const snapshot = createBlockSnapshot(block);
3759
- insertSnapshotAt(nodes, root, root.children.length, snapshot);
4313
+ insertSnapshotAt(nodes, root, root.children.length, snapshot, void 0, allocateBlockEpoch(readSnapshotBlockEpoch(snapshot)));
3760
4314
  }
3761
4315
  normalizeAllListDepths(nodes, /* @__PURE__ */ new Set(), PATCH_ROOT_ID);
4316
+ transientDiagnostics = [];
4317
+ invariantViolations = collectStoreInvariantViolations(nodes);
4318
+ debugCounters.storeInvariantViolationCount += invariantViolations.length;
3762
4319
  version++;
3763
4320
  blocksDirty = true;
3764
4321
  scheduleNotify();
@@ -3823,11 +4380,57 @@ function createRendererStore(initialBlocks = []) {
3823
4380
  listDepthDirty = true;
3824
4381
  }
3825
4382
  };
3826
- const applyPropsUpdate = (at, props) => {
4383
+ const semanticTxEpochs = /* @__PURE__ */ new Map();
4384
+ const resolveBlockEpochTarget = (patch, at) => {
4385
+ if ((patch.op === "insertChild" || patch.op === "replaceChild") && at.blockId === PATCH_ROOT_ID) {
4386
+ const nodeId = patch.node?.id;
4387
+ return nodeId ? nodes.get(nodeId) : void 0;
4388
+ }
4389
+ return nodes.get(at.blockId) ?? (at.nodeId ? nodes.get(at.nodeId) : void 0);
4390
+ };
4391
+ const rejectStaleEpochPatch = (patch, at) => {
4392
+ const expectedBlockEpoch = readPatchBlockEpoch(patch);
4393
+ const patchTx = readPatchTx(patch);
4394
+ if (expectedBlockEpoch === void 0) {
4395
+ return false;
4396
+ }
4397
+ if (getPatchKind3(patch) !== "semantic" || !at) {
4398
+ return false;
4399
+ }
4400
+ const target = resolveBlockEpochTarget(patch, at);
4401
+ if (!target || target.blockEpoch === expectedBlockEpoch) {
4402
+ return false;
4403
+ }
4404
+ const txEpoch = at.blockId && patchTx !== void 0 ? semanticTxEpochs.get(at.blockId) : void 0;
4405
+ if (txEpoch && txEpoch.tx === patchTx && txEpoch.blockEpoch === target.blockEpoch) {
4406
+ return false;
4407
+ }
4408
+ debugCounters.staleEpochRejected += 1;
4409
+ appendDiagnostic(
4410
+ `stale epoch rejected:${patch.op}:${target.id}:expected=${expectedBlockEpoch}:actual=${target.blockEpoch}`
4411
+ );
4412
+ return true;
4413
+ };
4414
+ const advanceBlockEpoch = (blockId, touchedSet, preferredEpoch, tx) => {
4415
+ if (!blockId || blockId === PATCH_ROOT_ID) {
4416
+ return null;
4417
+ }
4418
+ const blockRoot = nodes.get(blockId);
4419
+ if (!blockRoot) {
4420
+ return null;
4421
+ }
4422
+ const nextEpoch = allocateBlockEpoch(preferredEpoch);
4423
+ assignSubtreeBlockEpoch(nodes, blockRoot.id, nextEpoch, touchedSet);
4424
+ if (tx !== void 0) {
4425
+ semanticTxEpochs.set(blockRoot.id, { tx, blockEpoch: nextEpoch });
4426
+ }
4427
+ return nextEpoch;
4428
+ };
4429
+ const applyPropsUpdate = (at, props, preferredEpoch, tx, semanticUpdate = false) => {
3827
4430
  const perfStartLocal = perfEnabled ? getPerfNow() : 0;
3828
4431
  const targetId = at.nodeId ?? at.blockId;
3829
4432
  if (!targetId) return;
3830
- const target = nodes.get(targetId);
4433
+ let target = nodes.get(targetId);
3831
4434
  if (!target) return;
3832
4435
  const parentType = target.parentId ? nodes.get(target.parentId)?.type : void 0;
3833
4436
  if (debugListPatches && (isListRelated(target.type) || isListRelated(parentType))) {
@@ -3854,6 +4457,9 @@ function createRendererStore(initialBlocks = []) {
3854
4457
  propsChanged = true;
3855
4458
  }
3856
4459
  if (propsChanged) {
4460
+ if (semanticUpdate) {
4461
+ advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
4462
+ }
3857
4463
  target.version++;
3858
4464
  touched.add(target.id);
3859
4465
  const parent = target.parentId ? nodes.get(target.parentId) : void 0;
@@ -3917,16 +4523,56 @@ function createRendererStore(initialBlocks = []) {
3917
4523
  }
3918
4524
  }
3919
4525
  let blockChanged = false;
4526
+ let typeChanged = false;
4527
+ let rebuiltFromFinalizedBlock = false;
4528
+ let blockEpochAdvanced = false;
3920
4529
  const incomingBlock = props?.block;
3921
4530
  if (incomingBlock && typeof incomingBlock === "object") {
3922
4531
  target.block = cloneBlock(incomingBlock);
4532
+ const previousType = target.type;
4533
+ const incomingType = typeof target.block.type === "string" ? target.block.type : target.type;
4534
+ if (incomingType !== target.type) {
4535
+ target.type = incomingType;
4536
+ typeChanged = true;
4537
+ if (isListRelated(previousType) || isListRelated(target.type)) {
4538
+ markListDepthDirty(target.id);
4539
+ }
4540
+ }
3923
4541
  if (target.type === "code") {
3924
4542
  applyCodeBlockMetadata(target);
3925
4543
  }
4544
+ if (semanticUpdate) {
4545
+ advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
4546
+ blockEpochAdvanced = true;
4547
+ }
4548
+ const shouldRebuildFinalizedTree = !at.nodeId && target.id === at.blockId && Boolean(target.block?.isFinalized) && target.type !== "code" && Boolean(target.parentId);
4549
+ if (shouldRebuildFinalizedTree) {
4550
+ const parent = target.parentId ? nodes.get(target.parentId) : void 0;
4551
+ const index = parent ? parent.children.indexOf(target.id) : -1;
4552
+ if (parent && index !== -1) {
4553
+ const snapshot = createBlockSnapshot(cloneBlock(target.block));
4554
+ replaceSnapshotAt(nodes, parent, index, snapshot, touched, target.blockEpoch);
4555
+ mutatedBlocks = true;
4556
+ blockChanged = true;
4557
+ rebuiltFromFinalizedBlock = true;
4558
+ const rebuilt = nodes.get(target.id);
4559
+ if (isListRelated(previousType) || isListRelated(rebuilt?.type)) {
4560
+ markListDepthDirty(target.id);
4561
+ }
4562
+ target = rebuilt ?? target;
4563
+ }
4564
+ }
3926
4565
  mutatedBlocks = true;
3927
4566
  blockChanged = true;
3928
4567
  }
3929
- if (propsChanged || blockChanged) {
4568
+ if (rebuiltFromFinalizedBlock) {
4569
+ return;
4570
+ }
4571
+ if (propsChanged || blockChanged || typeChanged) {
4572
+ if (semanticUpdate && !blockEpochAdvanced) {
4573
+ advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
4574
+ blockEpochAdvanced = true;
4575
+ }
3930
4576
  target.version++;
3931
4577
  touched.add(target.id);
3932
4578
  let parentForNormalization;
@@ -3969,13 +4615,22 @@ function createRendererStore(initialBlocks = []) {
3969
4615
  const applyPatch = (patch) => {
3970
4616
  switch (patch.op) {
3971
4617
  case "insertChild": {
4618
+ if (rejectStaleEpochPatch(patch, patch.at)) break;
3972
4619
  const parent = resolveParent(nodes, patch.at);
3973
- const inserted = insertSnapshotAt(nodes, parent, patch.index, patch.node, touched);
4620
+ const inserted = insertSnapshotAt(
4621
+ nodes,
4622
+ parent,
4623
+ patch.index,
4624
+ patch.node,
4625
+ touched,
4626
+ allocateBlockEpoch(readPatchParseEpoch(patch) ?? readSnapshotBlockEpoch(patch.node))
4627
+ );
3974
4628
  if (inserted) {
3975
4629
  for (const id of inserted.insertedIds) {
3976
4630
  touched.add(id);
3977
4631
  }
3978
4632
  mutatedBlocks = true;
4633
+ advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
3979
4634
  if (isListRelated(inserted.record.type) || isListRelated(parent?.type)) {
3980
4635
  markListDepthDirty(inserted.record.id);
3981
4636
  if (parent) {
@@ -3995,6 +4650,7 @@ function createRendererStore(initialBlocks = []) {
3995
4650
  break;
3996
4651
  }
3997
4652
  case "deleteChild": {
4653
+ if (rejectStaleEpochPatch(patch, patch.at)) break;
3998
4654
  const parent = resolveParent(nodes, patch.at);
3999
4655
  if (!parent) break;
4000
4656
  if (patch.index < 0 || patch.index >= parent.children.length) break;
@@ -4006,6 +4662,7 @@ function createRendererStore(initialBlocks = []) {
4006
4662
  touched.add(parent.id);
4007
4663
  ensureUniqueChildren(parent, touched);
4008
4664
  mutatedBlocks = true;
4665
+ advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
4009
4666
  if (isListRelated(parent.type) || isListRelated(removedNode?.type)) {
4010
4667
  markListDepthDirty(parent.id);
4011
4668
  }
@@ -4022,11 +4679,20 @@ function createRendererStore(initialBlocks = []) {
4022
4679
  break;
4023
4680
  }
4024
4681
  case "replaceChild": {
4682
+ if (rejectStaleEpochPatch(patch, patch.at)) break;
4025
4683
  const parent = resolveParent(nodes, patch.at);
4026
4684
  if (!parent) break;
4027
- replaceSnapshotAt(nodes, parent, patch.index, patch.node, touched);
4685
+ replaceSnapshotAt(
4686
+ nodes,
4687
+ parent,
4688
+ patch.index,
4689
+ patch.node,
4690
+ touched,
4691
+ allocateBlockEpoch(readPatchParseEpoch(patch) ?? readSnapshotBlockEpoch(patch.node))
4692
+ );
4028
4693
  touched.add(parent.id);
4029
4694
  mutatedBlocks = true;
4695
+ advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
4030
4696
  if (isListRelated(parent.type)) {
4031
4697
  markListDepthDirty(parent.id);
4032
4698
  }
@@ -4040,18 +4706,33 @@ function createRendererStore(initialBlocks = []) {
4040
4706
  break;
4041
4707
  }
4042
4708
  case "setProps": {
4043
- applyPropsUpdate(patch.at, patch.props);
4709
+ if (rejectStaleEpochPatch(patch, patch.at)) break;
4710
+ applyPropsUpdate(patch.at, patch.props, readPatchParseEpoch(patch), readPatchTx(patch), getPatchKind3(patch) === "semantic");
4044
4711
  break;
4045
4712
  }
4046
4713
  case "setPropsBatch": {
4047
4714
  const entries2 = Array.isArray(patch.entries) ? patch.entries : [];
4048
4715
  for (const entry of entries2) {
4049
4716
  if (!entry || !entry.at) continue;
4050
- applyPropsUpdate(entry.at, entry.props);
4717
+ const entryPatch = {
4718
+ op: "setProps",
4719
+ at: entry.at,
4720
+ props: entry.props,
4721
+ meta: entry.meta ?? patch.meta
4722
+ };
4723
+ if (rejectStaleEpochPatch(entryPatch, entry.at)) continue;
4724
+ applyPropsUpdate(
4725
+ entry.at,
4726
+ entry.props,
4727
+ readPatchParseEpoch(entryPatch),
4728
+ readPatchTx(entryPatch),
4729
+ getPatchKind3(entryPatch) === "semantic"
4730
+ );
4051
4731
  }
4052
4732
  break;
4053
4733
  }
4054
4734
  case "finalize": {
4735
+ if (rejectStaleEpochPatch(patch, patch.at)) break;
4055
4736
  const target = nodes.get(patch.at.nodeId ?? patch.at.blockId);
4056
4737
  if (!target || !target.block) break;
4057
4738
  if (!target.block.isFinalized) {
@@ -4060,6 +4741,17 @@ function createRendererStore(initialBlocks = []) {
4060
4741
  target.version++;
4061
4742
  touched.add(target.id);
4062
4743
  mutatedBlocks = true;
4744
+ if (!patch.at.nodeId && target.id === patch.at.blockId && target.type !== "code" && target.parentId) {
4745
+ const parent = nodes.get(target.parentId);
4746
+ const index = parent ? parent.children.indexOf(target.id) : -1;
4747
+ if (parent && index !== -1) {
4748
+ const snapshot = createBlockSnapshot(cloneBlock(target.block));
4749
+ replaceSnapshotAt(nodes, parent, index, snapshot, touched, target.blockEpoch);
4750
+ if (isListRelated(target.type) || isListRelated(snapshot.type)) {
4751
+ markListDepthDirty(target.id);
4752
+ }
4753
+ }
4754
+ }
4063
4755
  }
4064
4756
  if (isListRelated(target.type)) {
4065
4757
  markListDepthDirty(target.id);
@@ -4073,6 +4765,7 @@ function createRendererStore(initialBlocks = []) {
4073
4765
  break;
4074
4766
  }
4075
4767
  case "reorder": {
4768
+ if (rejectStaleEpochPatch(patch, patch.at)) break;
4076
4769
  const parent = resolveParent(nodes, patch.at);
4077
4770
  if (!parent) break;
4078
4771
  const length = parent.children.length;
@@ -4087,6 +4780,9 @@ function createRendererStore(initialBlocks = []) {
4087
4780
  parent.children.splice(to, 0, ...moved);
4088
4781
  parent.version++;
4089
4782
  touched.add(parent.id);
4783
+ if (getPatchKind3(patch) === "semantic") {
4784
+ advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
4785
+ }
4090
4786
  if (isListRelated(parent.type)) {
4091
4787
  markListDepthDirty(parent.id);
4092
4788
  }
@@ -4102,15 +4798,31 @@ function createRendererStore(initialBlocks = []) {
4102
4798
  break;
4103
4799
  }
4104
4800
  case "appendLines": {
4801
+ if (rejectStaleEpochPatch(patch, patch.at)) break;
4105
4802
  const parent = resolveParent(nodes, patch.at);
4106
4803
  if (!parent) break;
4107
- const startIndex = Math.max(0, Math.min(patch.startIndex, parent.children.length));
4804
+ if (parent.type !== "code") {
4805
+ debugCounters.appendLineGuardRejected += 1;
4806
+ appendDiagnostic(`appendLines target not code:${parent.id}:${parent.type}`);
4807
+ break;
4808
+ }
4809
+ const requestedStartIndex = Math.max(0, patch.startIndex);
4810
+ const startIndex = Math.max(0, Math.min(requestedStartIndex, parent.children.length));
4108
4811
  const lineCount = patch.lines?.length ?? 0;
4109
4812
  if (lineCount === 0) break;
4813
+ const prefixMatches = parent.children.every((childId, index) => childId === expectedCodeLineId(parent.id, index));
4814
+ const isStrictTailAppend = requestedStartIndex === parent.children.length;
4815
+ if (!isStrictTailAppend || !prefixMatches) {
4816
+ debugCounters.appendLineGuardRejected += 1;
4817
+ appendDiagnostic(
4818
+ `appendLines guard rejected:${parent.id}:requestedStart=${requestedStartIndex}:childCount=${parent.children.length}:prefixMatches=${prefixMatches}`
4819
+ );
4820
+ break;
4821
+ }
4110
4822
  const originalChildren = parent.children.slice();
4111
4823
  const insertedIds = [];
4112
4824
  const insertedSet = /* @__PURE__ */ new Set();
4113
- const isTrailingAppend = startIndex >= originalChildren.length;
4825
+ const isTrailingAppend = true;
4114
4826
  let parentChildrenChanged = false;
4115
4827
  let codeLinesMutated = false;
4116
4828
  for (let offset = 0; offset < lineCount; offset++) {
@@ -4152,7 +4864,8 @@ function createRendererStore(initialBlocks = []) {
4152
4864
  parentId: parent.id,
4153
4865
  children: [],
4154
4866
  props: nextLineProps,
4155
- version: 0
4867
+ version: 0,
4868
+ blockEpoch: parent.blockEpoch
4156
4869
  };
4157
4870
  nodes.set(lineId, record);
4158
4871
  touched.add(lineId);
@@ -4202,6 +4915,7 @@ function createRendererStore(initialBlocks = []) {
4202
4915
  break;
4203
4916
  }
4204
4917
  case "setHTML": {
4918
+ if (rejectStaleEpochPatch(patch, patch.at)) break;
4205
4919
  const target = nodes.get(patch.at.nodeId ?? patch.at.blockId);
4206
4920
  if (!target) break;
4207
4921
  const sanitized = patch.sanitized ? patch.html : sanitizeHTML2(patch.html);
@@ -4244,6 +4958,30 @@ function createRendererStore(initialBlocks = []) {
4244
4958
  applyPatch(patch);
4245
4959
  }
4246
4960
  }
4961
+ const finalizedTableRoots = /* @__PURE__ */ new Set();
4962
+ for (const touchedId of touched) {
4963
+ let cursor = nodes.get(touchedId);
4964
+ while (cursor && cursor.id !== PATCH_ROOT_ID) {
4965
+ if (cursor.type === "table" && cursor.block?.isFinalized && cursor.parentId) {
4966
+ finalizedTableRoots.add(cursor.id);
4967
+ break;
4968
+ }
4969
+ cursor = cursor.parentId ? nodes.get(cursor.parentId) : void 0;
4970
+ }
4971
+ }
4972
+ if (finalizedTableRoots.size > 0) {
4973
+ for (const tableId of finalizedTableRoots) {
4974
+ const tableNode = nodes.get(tableId);
4975
+ if (!tableNode || !tableNode.block || !tableNode.parentId) continue;
4976
+ const parent = nodes.get(tableNode.parentId);
4977
+ if (!parent) continue;
4978
+ const index = parent.children.indexOf(tableNode.id);
4979
+ if (index === -1) continue;
4980
+ const snapshot = createBlockSnapshot(cloneBlock(tableNode.block));
4981
+ replaceSnapshotAt(nodes, parent, index, snapshot, touched, tableNode.blockEpoch);
4982
+ mutatedBlocks = true;
4983
+ }
4984
+ }
4247
4985
  if (mutatedBlocks) {
4248
4986
  blocksDirty = true;
4249
4987
  }
@@ -4264,6 +5002,8 @@ function createRendererStore(initialBlocks = []) {
4264
5002
  normalizeListDepthsForIds(nodes, touched, listDepthDirtyNodes, PATCH_ROOT_ID);
4265
5003
  }
4266
5004
  }
5005
+ invariantViolations = [...collectStoreInvariantViolations(nodes), ...transientDiagnostics];
5006
+ debugCounters.storeInvariantViolationCount += invariantViolations.length;
4267
5007
  if (touched.size > 0 || mutatedBlocks) {
4268
5008
  version++;
4269
5009
  scheduleNotify();
@@ -4312,6 +5052,7 @@ function createRendererStore(initialBlocks = []) {
4312
5052
  if (!record) {
4313
5053
  return EMPTY_NODE_SNAPSHOT;
4314
5054
  }
5055
+ const nodeSnapshotCache = getNodeSnapshotCacheForMap(nodes);
4315
5056
  const cached = nodeSnapshotCache.get(id);
4316
5057
  if (cached && cached.version === record.version && cached.node === record) {
4317
5058
  return cached;
@@ -4331,6 +5072,7 @@ function createRendererStore(initialBlocks = []) {
4331
5072
  if (!record) {
4332
5073
  return EMPTY_CHILDREN_SNAPSHOT;
4333
5074
  }
5075
+ const childrenSnapshotCache = getChildrenSnapshotCacheForMap(nodes);
4334
5076
  const cached = childrenSnapshotCache.get(id);
4335
5077
  if (cached && cached.version === record.version) {
4336
5078
  return cached;
@@ -4340,6 +5082,12 @@ function createRendererStore(initialBlocks = []) {
4340
5082
  pruneCache(childrenSnapshotCache, CHILDREN_SNAPSHOT_CACHE_LIMIT, CHILDREN_SNAPSHOT_CACHE_BUFFER);
4341
5083
  return snapshot;
4342
5084
  },
5085
+ getInvariantViolations() {
5086
+ return invariantViolations.slice();
5087
+ },
5088
+ getDebugCounters() {
5089
+ return { ...debugCounters };
5090
+ },
4343
5091
  getLastCoalescingMetrics() {
4344
5092
  return lastCoalescingMetrics;
4345
5093
  },
@@ -4354,11 +5102,11 @@ function createRendererStore(initialBlocks = []) {
4354
5102
  }
4355
5103
 
4356
5104
  // src/renderer/blocks-renderer.tsx
4357
- import React8, { useMemo as useMemo4 } from "react";
5105
+ import React8, { useMemo as useMemo5 } from "react";
4358
5106
  import { getBlockKey, PATCH_ROOT_ID as PATCH_ROOT_ID2 } from "@stream-mdx/core";
4359
5107
 
4360
5108
  // src/renderer/hooks.ts
4361
- import { useMemo as useMemo2, useSyncExternalStore } from "react";
5109
+ import { useMemo as useMemo3, useSyncExternalStore } from "react";
4362
5110
  function useRendererBlocks(store) {
4363
5111
  return useSyncExternalStore(
4364
5112
  store.subscribe,
@@ -4367,14 +5115,14 @@ function useRendererBlocks(store) {
4367
5115
  );
4368
5116
  }
4369
5117
  function useRendererNode(store, id) {
4370
- const selector = useMemo2(() => {
5118
+ const selector = useMemo3(() => {
4371
5119
  return () => store.getNodeWithVersion(id);
4372
5120
  }, [store, id]);
4373
5121
  const snapshot = useSyncExternalStore(store.subscribe, selector, selector);
4374
5122
  return snapshot.node;
4375
5123
  }
4376
5124
  function useRendererChildren(store, id) {
4377
- const selector = useMemo2(() => {
5125
+ const selector = useMemo3(() => {
4378
5126
  return () => store.getChildrenWithVersion(id);
4379
5127
  }, [store, id]);
4380
5128
  const snapshot = useSyncExternalStore(store.subscribe, selector, selector);
@@ -4382,7 +5130,7 @@ function useRendererChildren(store, id) {
4382
5130
  }
4383
5131
 
4384
5132
  // src/renderer/node-views.tsx
4385
- import { stripCodeFence } from "@stream-mdx/core";
5133
+ import { getDefaultCodeWrapperAttributes as getDefaultCodeWrapperAttributes2, stripCodeFence } from "@stream-mdx/core";
4386
5134
  import React7 from "react";
4387
5135
 
4388
5136
  // src/renderer/deferred-render-context.tsx
@@ -4390,7 +5138,7 @@ import React6 from "react";
4390
5138
  var DeferredRenderContext = React6.createContext(null);
4391
5139
 
4392
5140
  // src/renderer/virtualized-code.tsx
4393
- import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
5141
+ import { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo4, useRef as useRef3, useState as useState4 } from "react";
4394
5142
  var DEFAULT_VIRTUALIZED_CODE_CONFIG = {
4395
5143
  enabled: true,
4396
5144
  windowSize: 100,
@@ -4430,11 +5178,11 @@ function calculateCodeWindow(lines, scrollTop, containerHeight, lineHeight, conf
4430
5178
  };
4431
5179
  }
4432
5180
  function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
4433
- const containerRef = useRef2(null);
4434
- const [scrollTop, setScrollTop] = useState3(0);
4435
- const [containerHeight, setContainerHeight] = useState3(0);
4436
- const lineHeightRef = useRef2(20);
4437
- useEffect3(() => {
5181
+ const containerRef = useRef3(null);
5182
+ const [scrollTop, setScrollTop] = useState4(0);
5183
+ const [containerHeight, setContainerHeight] = useState4(0);
5184
+ const lineHeightRef = useRef3(20);
5185
+ useEffect4(() => {
4438
5186
  if (!containerRef.current || lines.length === 0) return;
4439
5187
  const timeoutId = setTimeout(() => {
4440
5188
  if (!containerRef.current) return;
@@ -4448,12 +5196,12 @@ function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
4448
5196
  }, 0);
4449
5197
  return () => clearTimeout(timeoutId);
4450
5198
  }, [lines.length]);
4451
- const handleScroll = useCallback2((e) => {
5199
+ const handleScroll = useCallback3((e) => {
4452
5200
  const target = e.currentTarget;
4453
5201
  setScrollTop(target.scrollTop);
4454
5202
  setContainerHeight(target.clientHeight);
4455
5203
  }, []);
4456
- useEffect3(() => {
5204
+ useEffect4(() => {
4457
5205
  if (!containerRef.current) return;
4458
5206
  const resizeObserver = new ResizeObserver((entries2) => {
4459
5207
  for (const entry of entries2) {
@@ -4463,10 +5211,10 @@ function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
4463
5211
  resizeObserver.observe(containerRef.current);
4464
5212
  return () => resizeObserver.disconnect();
4465
5213
  }, []);
4466
- const window2 = useMemo3(() => {
5214
+ const window2 = useMemo4(() => {
4467
5215
  return calculateCodeWindow(lines, scrollTop, containerHeight, lineHeightRef.current, config);
4468
5216
  }, [lines, scrollTop, containerHeight, config]);
4469
- const scrollToLine = useCallback2((lineIndex) => {
5217
+ const scrollToLine = useCallback3((lineIndex) => {
4470
5218
  if (!containerRef.current) return;
4471
5219
  const targetScrollTop = lineIndex * lineHeightRef.current;
4472
5220
  containerRef.current.scrollTop = targetScrollTop;
@@ -4483,26 +5231,26 @@ function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
4483
5231
  }
4484
5232
 
4485
5233
  // src/renderer/node-views.tsx
4486
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
5234
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
4487
5235
  var BlockNodeRenderer = ({ store, blockId, registry }) => {
4488
5236
  const node = useRendererNode(store, blockId);
4489
5237
  const block = node?.block;
4490
5238
  if (!node || !block) return null;
4491
5239
  switch (block.type) {
4492
5240
  case "paragraph":
4493
- return /* @__PURE__ */ jsx3(ParagraphBlockView, { store, blockId: block.id, registry });
5241
+ return /* @__PURE__ */ jsx4(ParagraphBlockView, { store, blockId: block.id, registry });
4494
5242
  case "blockquote":
4495
- return /* @__PURE__ */ jsx3(BlockquoteBlockView, { store, blockId: block.id, registry });
5243
+ return /* @__PURE__ */ jsx4(BlockquoteBlockView, { store, blockId: block.id, registry });
4496
5244
  case "list":
4497
- return /* @__PURE__ */ jsx3(ListBlockView, { store, blockId: block.id, registry, depth: 0 });
5245
+ return /* @__PURE__ */ jsx4(ListBlockView, { store, blockId: block.id, registry, depth: 0 });
4498
5246
  case "table":
4499
- return /* @__PURE__ */ jsx3(TableBlockView, { store, blockId: block.id, registry });
5247
+ return /* @__PURE__ */ jsx4(TableBlockView, { store, blockId: block.id, registry });
4500
5248
  case "code":
4501
- return /* @__PURE__ */ jsx3(CodeBlockView, { store, blockId: block.id, registry });
5249
+ return /* @__PURE__ */ jsx4(CodeBlockView, { store, blockId: block.id, registry });
4502
5250
  case "html":
4503
- return /* @__PURE__ */ jsx3(HtmlBlockView, { store, blockId: block.id, registry });
5251
+ return /* @__PURE__ */ jsx4(HtmlBlockView, { store, blockId: block.id, registry });
4504
5252
  case "mdx":
4505
- return /* @__PURE__ */ jsx3(MdxBlockView, { store, blockId: block.id, registry });
5253
+ return /* @__PURE__ */ jsx4(MdxBlockView, { store, blockId: block.id, registry });
4506
5254
  default:
4507
5255
  return registry.renderBlock(block);
4508
5256
  }
@@ -4522,7 +5270,7 @@ var ParagraphBlockView = React7.memo(({ store, blockId, registry }) => {
4522
5270
  if (segments2.length > 0) {
4523
5271
  const allowMixedStreaming = Boolean(meta.allowMixedStreaming);
4524
5272
  if (!allowMixedStreaming) {
4525
- return /* @__PURE__ */ jsx3("p", { className: "markdown-paragraph streaming-partial", children: raw });
5273
+ return /* @__PURE__ */ jsx4("p", { className: "markdown-paragraph streaming-partial", children: raw });
4526
5274
  }
4527
5275
  const structured = renderParagraphMixedSegments(segments2, inlineComponents, DEFAULT_INLINE_HTML_RENDERERS);
4528
5276
  if (structured.length === 1) {
@@ -4549,15 +5297,15 @@ var ParagraphBlockView = React7.memo(({ store, blockId, registry }) => {
4549
5297
  }
4550
5298
  return structured;
4551
5299
  }
4552
- return /* @__PURE__ */ jsx3("p", { className: "markdown-paragraph streaming-partial", children: renderInlineNodes(inlineNodes2, inlineComponents) });
5300
+ return /* @__PURE__ */ jsx4("p", { className: "markdown-paragraph streaming-partial", children: renderInlineNodes(inlineNodes2, inlineComponents) });
4553
5301
  }
4554
- return /* @__PURE__ */ jsx3("p", { className: "markdown-paragraph streaming-partial", children: raw });
5302
+ return /* @__PURE__ */ jsx4("p", { className: "markdown-paragraph streaming-partial", children: raw });
4555
5303
  }
4556
5304
  const propsBlock = node.props?.block;
4557
5305
  const inlineStatus = propsBlock?.payload?.meta?.inlineStatus ?? block.payload.meta?.inlineStatus ?? node.props?.inlineStatus;
4558
5306
  if (inlineStatus && inlineStatus !== "complete") {
4559
5307
  const raw = typeof block.payload.raw === "string" ? block.payload.raw : "";
4560
- return /* @__PURE__ */ jsx3("p", { className: "markdown-paragraph", children: raw });
5308
+ return /* @__PURE__ */ jsx4("p", { className: "markdown-paragraph", children: raw });
4561
5309
  }
4562
5310
  const segments = Array.isArray(block.payload.meta?.mixedSegments) ? block.payload.meta.mixedSegments : void 0;
4563
5311
  if (segments && segments.length > 0) {
@@ -4592,17 +5340,17 @@ var ParagraphBlockView = React7.memo(({ store, blockId, registry }) => {
4592
5340
  meta: block.payload.meta
4593
5341
  });
4594
5342
  if (childIds.length > 0) {
4595
- const renderedChildren = childIds.map((childId) => /* @__PURE__ */ jsx3(MixedSegmentView, { store, nodeId: childId, inlineComponents }, childId));
5343
+ const renderedChildren = childIds.map((childId) => /* @__PURE__ */ jsx4(MixedSegmentView, { store, nodeId: childId, inlineComponents }, childId));
4596
5344
  if (React7.isValidElement(baseElement)) {
4597
5345
  const { children: _ignored, className = "markdown-paragraph", inlines: _inlines, raw: _raw, meta: _meta, ...rest } = baseElement.props ?? {};
4598
5346
  return React7.createElement("p", { ...rest, className }, renderedChildren);
4599
5347
  }
4600
- return /* @__PURE__ */ jsx3("p", { className: "markdown-paragraph", children: renderedChildren });
5348
+ return /* @__PURE__ */ jsx4("p", { className: "markdown-paragraph", children: renderedChildren });
4601
5349
  }
4602
5350
  if (React7.isValidElement(baseElement)) {
4603
5351
  return baseElement;
4604
5352
  }
4605
- return /* @__PURE__ */ jsx3("p", { className: "markdown-paragraph", children: renderInlineNodes(block.payload.inline ?? [], inlineComponents) });
5353
+ return /* @__PURE__ */ jsx4("p", { className: "markdown-paragraph", children: renderInlineNodes(block.payload.inline ?? [], inlineComponents) });
4606
5354
  });
4607
5355
  ParagraphBlockView.displayName = "ParagraphBlockView";
4608
5356
  var BlockquoteBlockView = React7.memo(({ store, blockId, registry }) => {
@@ -4623,9 +5371,9 @@ var BlockquoteBlockView = React7.memo(({ store, blockId, registry }) => {
4623
5371
  return lines.map((line, idx) => {
4624
5372
  const key = `${keyPrefix}${offset}`;
4625
5373
  offset += line.length + 1;
4626
- return /* @__PURE__ */ jsxs2("span", { className: "markdown-blockquote-line", children: [
5374
+ return /* @__PURE__ */ jsxs3("span", { className: "markdown-blockquote-line", children: [
4627
5375
  line,
4628
- idx < lastIndex ? /* @__PURE__ */ jsx3("br", {}) : null
5376
+ idx < lastIndex ? /* @__PURE__ */ jsx4("br", {}) : null
4629
5377
  ] }, key);
4630
5378
  });
4631
5379
  };
@@ -4663,16 +5411,16 @@ var BlockquoteBlockView = React7.memo(({ store, blockId, registry }) => {
4663
5411
  }
4664
5412
  case "mdx":
4665
5413
  elements.push(
4666
- /* @__PURE__ */ jsx3("span", { "data-mdx-status": segment.status ?? "pending", children: segment.value }, `blockquote-mdx-${block.id}-${segmentIndex}`)
5414
+ /* @__PURE__ */ jsx4("span", { "data-mdx-status": segment.status ?? "pending", children: segment.value }, `blockquote-mdx-${block.id}-${segmentIndex}`)
4667
5415
  );
4668
5416
  break;
4669
5417
  default: {
4670
5418
  const inline = renderInlineContent(segment.inline, `blockquote-text-inline-${block.id}-${segmentIndex}-`);
4671
5419
  if (inline) {
4672
- elements.push(/* @__PURE__ */ jsx3(React7.Fragment, { children: inline }, `blockquote-inline-${block.id}-${segmentIndex}`));
5420
+ elements.push(/* @__PURE__ */ jsx4(React7.Fragment, { children: inline }, `blockquote-inline-${block.id}-${segmentIndex}`));
4673
5421
  } else {
4674
5422
  elements.push(
4675
- /* @__PURE__ */ jsx3(React7.Fragment, { children: renderTextWithBreaks(segment.value, `blockquote-text-${block.id}-${segmentIndex}-`) }, `blockquote-text-${block.id}-${segmentIndex}`)
5423
+ /* @__PURE__ */ jsx4(React7.Fragment, { children: renderTextWithBreaks(segment.value, `blockquote-text-${block.id}-${segmentIndex}-`) }, `blockquote-text-${block.id}-${segmentIndex}`)
4676
5424
  );
4677
5425
  }
4678
5426
  break;
@@ -4689,9 +5437,9 @@ var BlockquoteBlockView = React7.memo(({ store, blockId, registry }) => {
4689
5437
  const streamingContent = mixedSegmentElements.length ? mixedSegmentElements : inlineElements ? inlineElements : fallbackElements;
4690
5438
  const shouldShowStreaming = !block.isFinalized && (!streamingContent || Array.isArray(streamingContent) && streamingContent.length === 0);
4691
5439
  const className = shouldShowStreaming ? "markdown-blockquote streaming-partial" : "markdown-blockquote";
4692
- return /* @__PURE__ */ jsx3("blockquote", { className, children: streamingContent });
5440
+ return /* @__PURE__ */ jsx4("blockquote", { className, children: streamingContent });
4693
5441
  }
4694
- const childContent = childIds.map((childId) => /* @__PURE__ */ jsx3(MixedSegmentView, { store, nodeId: childId, inlineComponents }, childId));
5442
+ const childContent = childIds.map((childId) => /* @__PURE__ */ jsx4(MixedSegmentView, { store, nodeId: childId, inlineComponents }, childId));
4695
5443
  const renderedContent = (() => {
4696
5444
  if (mixedSegmentElements.length > 0) {
4697
5445
  return mixedSegmentElements;
@@ -4722,7 +5470,7 @@ var BlockquoteBlockView = React7.memo(({ store, blockId, registry }) => {
4722
5470
  if (React7.isValidElement(baseElement)) {
4723
5471
  return baseElement;
4724
5472
  }
4725
- return /* @__PURE__ */ jsx3("blockquote", { className: "markdown-blockquote", children: renderedContent });
5473
+ return /* @__PURE__ */ jsx4("blockquote", { className: "markdown-blockquote", children: renderedContent });
4726
5474
  });
4727
5475
  BlockquoteBlockView.displayName = "BlockquoteBlockView";
4728
5476
  var MixedSegmentView = React7.memo(({ store, nodeId, inlineComponents }) => {
@@ -4765,13 +5513,13 @@ var MixedSegmentView = React7.memo(({ store, nodeId, inlineComponents }) => {
4765
5513
  const status = node.props?.status ?? "pending";
4766
5514
  const raw = typeof node.props?.raw === "string" ? node.props?.raw : "";
4767
5515
  if (status === "compiled") {
4768
- return /* @__PURE__ */ jsx3("span", { className: "markdown-mdx-inline", "data-mdx-status": "compiled" });
5516
+ return /* @__PURE__ */ jsx4("span", { className: "markdown-mdx-inline", "data-mdx-status": "compiled" });
4769
5517
  }
4770
5518
  if (status === "error") {
4771
5519
  const message = raw || "MDX error";
4772
- return /* @__PURE__ */ jsx3("span", { className: "markdown-mdx-inline text-destructive", "data-mdx-status": "error", children: message });
5520
+ return /* @__PURE__ */ jsx4("span", { className: "markdown-mdx-inline text-destructive", "data-mdx-status": "error", children: message });
4773
5521
  }
4774
- return /* @__PURE__ */ jsx3("span", { className: "markdown-mdx-inline", "data-mdx-status": status, children: status === "compiled" ? "" : raw });
5522
+ return /* @__PURE__ */ jsx4("span", { className: "markdown-mdx-inline", "data-mdx-status": status, children: status === "compiled" ? "" : raw });
4775
5523
  }
4776
5524
  default:
4777
5525
  return null;
@@ -4824,8 +5572,10 @@ var ListBlockView = React7.memo(
4824
5572
  isFinalized: block?.isFinalized ?? false
4825
5573
  });
4826
5574
  }, [listDebugEnabled, blockId, depth, ordered, childIds, listItemIds, store, block?.isFinalized]);
4827
- const listStyle = React7.useMemo(() => {
4828
- if (!ordered || listItemIds.length === 0) return void 0;
5575
+ const { listStyle, markerDigits } = React7.useMemo(() => {
5576
+ if (!ordered || listItemIds.length === 0) {
5577
+ return { listStyle: void 0, markerDigits: 1 };
5578
+ }
4829
5579
  let maxDigits = 1;
4830
5580
  listItemIds.forEach((childId, index) => {
4831
5581
  const childNode = store.getNode(childId);
@@ -4837,20 +5587,26 @@ var ListBlockView = React7.memo(
4837
5587
  const digits = digitMatch ? digitMatch.length : text.length;
4838
5588
  if (digits > maxDigits) maxDigits = digits;
4839
5589
  });
4840
- const baseIndent = depth === 0 ? "2.5rem" : depth === 1 ? "2rem" : "1.5rem";
5590
+ const baseIndent = depth === 0 ? "2rem" : depth === 1 ? "1.75rem" : "1.5rem";
5591
+ const extraDigits = Math.max(0, maxDigits - 1);
4841
5592
  return {
4842
- ["--list-indent"]: `calc(${baseIndent} + ${maxDigits}ch)`
5593
+ markerDigits: maxDigits,
5594
+ listStyle: {
5595
+ ["--list-indent"]: extraDigits > 0 ? `calc(${baseIndent} + ${extraDigits}ch)` : baseIndent,
5596
+ ["--list-marker-digits"]: String(maxDigits)
5597
+ }
4843
5598
  };
4844
5599
  }, [ordered, listItemIds, store, depth]);
4845
5600
  if (listItemIds.length === 0) return null;
4846
- return /* @__PURE__ */ jsx3(
5601
+ return /* @__PURE__ */ jsx4(
4847
5602
  Tag,
4848
5603
  {
4849
5604
  className: `markdown-list ${ordered ? "ordered" : "unordered"}`,
4850
5605
  "data-list-depth": depth,
5606
+ "data-marker-digits": ordered ? markerDigits : void 0,
4851
5607
  "data-list-id": listDebugEnabled ? blockId : void 0,
4852
5608
  style: listStyle,
4853
- children: listItemIds.map((childId, index) => /* @__PURE__ */ jsx3(
5609
+ children: listItemIds.map((childId, index) => /* @__PURE__ */ jsx4(
4854
5610
  ListItemView,
4855
5611
  {
4856
5612
  store,
@@ -4898,7 +5654,7 @@ var ListItemView = React7.memo(({ store, nodeId, inlineComponents, registry, ord
4898
5654
  return [segments, structural];
4899
5655
  }, [childIds, store]);
4900
5656
  const segmentElements = React7.useMemo(() => {
4901
- return segmentChildIds.map((childId) => /* @__PURE__ */ jsx3(MixedSegmentView, { store, nodeId: childId, inlineComponents }, childId));
5657
+ return segmentChildIds.map((childId) => /* @__PURE__ */ jsx4(MixedSegmentView, { store, nodeId: childId, inlineComponents }, childId));
4902
5658
  }, [segmentChildIds, store, inlineComponents]);
4903
5659
  const primaryContent = React7.useMemo(() => {
4904
5660
  if (segmentChildIds.length > 0) {
@@ -4913,12 +5669,12 @@ var ListItemView = React7.memo(({ store, nodeId, inlineComponents, registry, ord
4913
5669
  switch (childNode.type) {
4914
5670
  case "paragraph": {
4915
5671
  const childInline = childNode.props?.inline ?? [];
4916
- return /* @__PURE__ */ jsx3("p", { className: "markdown-list-item-paragraph", children: renderInlineNodes(childInline, inlineComponents) }, childId);
5672
+ return /* @__PURE__ */ jsx4("p", { className: "markdown-list-item-paragraph", children: renderInlineNodes(childInline, inlineComponents) }, childId);
4917
5673
  }
4918
5674
  case "list":
4919
- return /* @__PURE__ */ jsx3(ListBlockView, { store, blockId: childId, registry, depth: depth + 1 }, childId);
5675
+ return /* @__PURE__ */ jsx4(ListBlockView, { store, blockId: childId, registry, depth: depth + 1 }, childId);
4920
5676
  default:
4921
- return /* @__PURE__ */ jsx3(BlockNodeRenderer, { store, blockId: childId, registry }, childId);
5677
+ return /* @__PURE__ */ jsx4(BlockNodeRenderer, { store, blockId: childId, registry }, childId);
4922
5678
  }
4923
5679
  });
4924
5680
  }, [contentChildIds, store, inlineComponents, registry, depth]);
@@ -4960,7 +5716,7 @@ var ListItemView = React7.memo(({ store, nodeId, inlineComponents, registry, ord
4960
5716
  store,
4961
5717
  block?.isFinalized
4962
5718
  ]);
4963
- return /* @__PURE__ */ jsxs2(
5719
+ return /* @__PURE__ */ jsxs3(
4964
5720
  "li",
4965
5721
  {
4966
5722
  className: `markdown-list-item${isTask ? " markdown-list-item-task" : ""}`,
@@ -4968,11 +5724,11 @@ var ListItemView = React7.memo(({ store, nodeId, inlineComponents, registry, ord
4968
5724
  "data-list-depth": depth,
4969
5725
  "data-list-item-id": listDebugEnabled ? nodeId : void 0,
4970
5726
  children: [
4971
- isTask ? /* @__PURE__ */ jsxs2("div", { className: "markdown-task", children: [
4972
- /* @__PURE__ */ jsx3("input", { type: "checkbox", className: "markdown-task-checkbox", checked: isChecked, readOnly: true, disabled: true, tabIndex: -1, "aria-checked": isChecked }),
4973
- /* @__PURE__ */ jsx3("span", { className: "markdown-task-content", children: primaryContent })
5727
+ isTask ? /* @__PURE__ */ jsxs3("div", { className: "markdown-task", children: [
5728
+ /* @__PURE__ */ jsx4("input", { type: "checkbox", className: "markdown-task-checkbox", checked: isChecked, readOnly: true, disabled: true, tabIndex: -1, "aria-checked": isChecked }),
5729
+ /* @__PURE__ */ jsx4("span", { className: "markdown-task-content", children: primaryContent })
4974
5730
  ] }) : primaryContent,
4975
- hasChildren ? /* @__PURE__ */ jsx3("div", { className: "markdown-list-item-children", children: filteredChildren }) : null
5731
+ hasChildren ? /* @__PURE__ */ jsx4("div", { className: "markdown-list-item-children", children: filteredChildren }) : null
4976
5732
  ]
4977
5733
  }
4978
5734
  );
@@ -4981,7 +5737,7 @@ ListItemView.displayName = "ListItemView";
4981
5737
  var TableBlockView = React7.memo(({ store, blockId, registry }) => {
4982
5738
  const tableElements = registry.getTableElements();
4983
5739
  const childIds = useRendererChildren(store, blockId);
4984
- return /* @__PURE__ */ jsx3(tableElements.Table, { className: "markdown-table w-full caption-bottom text-base", children: childIds.map((sectionId) => /* @__PURE__ */ jsx3(TableSectionView, { store, nodeId: sectionId, registry }, sectionId)) });
5740
+ return /* @__PURE__ */ jsx4(tableElements.Table, { className: "markdown-table w-full caption-bottom text-base", children: childIds.map((sectionId) => /* @__PURE__ */ jsx4(TableSectionView, { store, nodeId: sectionId, registry }, sectionId)) });
4985
5741
  });
4986
5742
  TableBlockView.displayName = "TableBlockView";
4987
5743
  var TableSectionView = React7.memo(({ store, nodeId, registry }) => {
@@ -4990,11 +5746,11 @@ var TableSectionView = React7.memo(({ store, nodeId, registry }) => {
4990
5746
  const tableElements = registry.getTableElements();
4991
5747
  if (node.type === "table-header") {
4992
5748
  const childIds = useRendererChildren(store, nodeId);
4993
- return /* @__PURE__ */ jsx3(tableElements.Thead, { children: /* @__PURE__ */ jsx3(tableElements.Tr, { children: childIds.map((cellId) => /* @__PURE__ */ jsx3(TableCellView, { store, nodeId: cellId, registry }, cellId)) }) });
5749
+ return /* @__PURE__ */ jsx4(tableElements.Thead, { children: /* @__PURE__ */ jsx4(tableElements.Tr, { children: childIds.map((cellId) => /* @__PURE__ */ jsx4(TableCellView, { store, nodeId: cellId, registry }, cellId)) }) });
4994
5750
  }
4995
5751
  if (node.type === "table-body") {
4996
5752
  const rowIds = useRendererChildren(store, nodeId);
4997
- return /* @__PURE__ */ jsx3(tableElements.Tbody, { children: rowIds.map((rowId) => /* @__PURE__ */ jsx3(TableRowView, { store, nodeId: rowId, registry }, rowId)) });
5753
+ return /* @__PURE__ */ jsx4(tableElements.Tbody, { children: rowIds.map((rowId) => /* @__PURE__ */ jsx4(TableRowView, { store, nodeId: rowId, registry }, rowId)) });
4998
5754
  }
4999
5755
  return null;
5000
5756
  });
@@ -5002,7 +5758,7 @@ TableSectionView.displayName = "TableSectionView";
5002
5758
  var TableRowView = React7.memo(({ store, nodeId, registry }) => {
5003
5759
  const tableElements = registry.getTableElements();
5004
5760
  const cellIds = useRendererChildren(store, nodeId);
5005
- return /* @__PURE__ */ jsx3(tableElements.Tr, { children: cellIds.map((cellId) => /* @__PURE__ */ jsx3(TableCellView, { store, nodeId: cellId, registry }, cellId)) });
5761
+ return /* @__PURE__ */ jsx4(tableElements.Tr, { children: cellIds.map((cellId) => /* @__PURE__ */ jsx4(TableCellView, { store, nodeId: cellId, registry }, cellId)) });
5006
5762
  });
5007
5763
  TableRowView.displayName = "TableRowView";
5008
5764
  var TableCellView = React7.memo(({ store, nodeId, registry }) => {
@@ -5015,7 +5771,7 @@ var TableCellView = React7.memo(({ store, nodeId, registry }) => {
5015
5771
  const align = node.props?.align;
5016
5772
  const content = renderInlineNodes(inline, inlineComponents);
5017
5773
  const alignProps = align ? { align } : void 0;
5018
- return /* @__PURE__ */ jsx3(Tag, { ...alignProps, children: content });
5774
+ return /* @__PURE__ */ jsx4(Tag, { ...alignProps, children: content });
5019
5775
  });
5020
5776
  TableCellView.displayName = "TableCellView";
5021
5777
  var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
@@ -5029,6 +5785,7 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
5029
5785
  deferredConfig ? { ...deferredConfig, enabled: true } : { enabled: false }
5030
5786
  );
5031
5787
  if (!node || !node.block) return null;
5788
+ const blockIsFinalized = node.block.isFinalized;
5032
5789
  const nodeVersion = node.version;
5033
5790
  const lines = React7.useMemo(() => {
5034
5791
  const seenIds = /* @__PURE__ */ new Set();
@@ -5059,13 +5816,25 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
5059
5816
  const lang = typeof node.props?.lang === "string" ? node.props?.lang : node.block.payload.meta?.lang;
5060
5817
  const preAttrs = node.props?.preAttrs ?? void 0;
5061
5818
  const codeAttrs = node.props?.codeAttrs ?? void 0;
5819
+ const blockMeta = node.block.payload.meta ?? void 0;
5820
+ const lazyEnabled = Boolean(blockMeta?.lazyTokenization);
5821
+ const lazyTokenizedUntil = typeof blockMeta?.lazyTokenizedUntil === "number" ? blockMeta.lazyTokenizedUntil : 0;
5822
+ const lazyHighlightedLines = Array.isArray(blockMeta?.highlightedLines) ? blockMeta.highlightedLines.length : 0;
5823
+ const lazyTokenLines = Array.isArray(blockMeta?.tokenLines) ? blockMeta.tokenLines.length : 0;
5824
+ const lazySourceTextLineCount = React7.useMemo(() => {
5825
+ const raw = typeof node.block?.payload.raw === "string" ? node.block.payload.raw : "";
5826
+ const metaCode = typeof blockMeta?.code === "string" ? blockMeta.code : null;
5827
+ const sourceText = resolveCodeSourceText(raw, metaCode);
5828
+ if (sourceText.length === 0) return 0;
5829
+ return sourceText.split("\n").length;
5830
+ }, [blockMeta, node.block?.payload.raw]);
5831
+ const totalLazyLineCount = Math.max(lines.length, lazyHighlightedLines, lazyTokenLines, lazySourceTextLineCount);
5062
5832
  const virtualizationConfig = DEFAULT_VIRTUALIZED_CODE_CONFIG;
5063
5833
  const virtualizationDisabled = typeof process !== "undefined" && typeof process.env === "object" && process.env.STREAM_MDX_DISABLE_VIRTUALIZED_CODE === "true";
5064
- const shouldVirtualize = !virtualizationDisabled && virtualizationConfig.enabled && lines.length >= virtualizationConfig.virtualizeThreshold;
5834
+ const canVirtualize = node.block.isFinalized && !lazyEnabled;
5835
+ const shouldVirtualize = canVirtualize && !virtualizationDisabled && virtualizationConfig.enabled && lines.length >= virtualizationConfig.virtualizeThreshold;
5065
5836
  const virtualization = useVirtualizedCode(lines, shouldVirtualize ? virtualizationConfig : { ...virtualizationConfig, enabled: false });
5066
5837
  const highlightRequester = useCodeHighlightRequester();
5067
- const lazyEnabled = Boolean(node.block.payload.meta?.lazyTokenization);
5068
- const lazyTokenizedUntil = typeof node.block.payload.meta?.lazyTokenizedUntil === "number" ? node.block.payload.meta.lazyTokenizedUntil : 0;
5069
5838
  const lastRangeRef = React7.useRef(null);
5070
5839
  const rafRef = React7.useRef(null);
5071
5840
  const idleRef = React7.useRef(null);
@@ -5146,30 +5915,112 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
5146
5915
  virtualization.window.startIndex,
5147
5916
  virtualization.window.endIndex
5148
5917
  ]);
5149
- const highlightedHtml = React7.useMemo(() => {
5150
- if (!shouldVirtualize && node.block?.payload.highlightedHtml) {
5151
- return node.block.payload.highlightedHtml;
5918
+ React7.useEffect(() => {
5919
+ if (shouldVirtualize || !blockIsFinalized || !lazyEnabled || !highlightRequester) return;
5920
+ if (totalLazyLineCount <= 0 || lazyTokenizedUntil >= totalLazyLineCount) return;
5921
+ const nextRange = {
5922
+ visibleStart: 0,
5923
+ visibleEnd: totalLazyLineCount,
5924
+ prefetchStart: 0,
5925
+ prefetchEnd: totalLazyLineCount,
5926
+ tokenizedUntil: lazyTokenizedUntil
5927
+ };
5928
+ const last = lastRangeRef.current;
5929
+ if (last && last.visibleStart === nextRange.visibleStart && last.visibleEnd === nextRange.visibleEnd && last.prefetchStart === nextRange.prefetchStart && last.prefetchEnd === nextRange.prefetchEnd && last.tokenizedUntil === nextRange.tokenizedUntil) {
5930
+ return;
5152
5931
  }
5932
+ lastRangeRef.current = nextRange;
5933
+ if (rafRef.current !== null && typeof cancelAnimationFrame === "function") {
5934
+ cancelAnimationFrame(rafRef.current);
5935
+ }
5936
+ const requestFullHighlight = () => {
5937
+ highlightRequester({
5938
+ blockId,
5939
+ startLine: 0,
5940
+ endLine: totalLazyLineCount,
5941
+ priority: "visible",
5942
+ reason: "finalize-full"
5943
+ });
5944
+ rafRef.current = null;
5945
+ };
5946
+ if (typeof requestAnimationFrame === "function") {
5947
+ rafRef.current = requestAnimationFrame(requestFullHighlight);
5948
+ } else {
5949
+ requestFullHighlight();
5950
+ }
5951
+ }, [blockId, blockIsFinalized, highlightRequester, lazyEnabled, lazyTokenizedUntil, shouldVirtualize, totalLazyLineCount]);
5952
+ const highlightedHtml = React7.useMemo(() => {
5953
+ const blockHtml = node.block?.payload.highlightedHtml ?? "";
5954
+ const raw = typeof node.block?.payload.raw === "string" ? node.block.payload.raw : "";
5955
+ const metaCode = typeof node.block?.payload.meta?.code === "string" ? (node.block?.payload.meta).code : null;
5956
+ const sourceText = resolveCodeSourceText(raw, metaCode);
5957
+ const sourceEndsWithNewline = sourceText.endsWith("\n");
5958
+ const sourceLines = sourceText.length > 0 ? sourceText.split("\n") : [];
5959
+ const rawFallbackLines = buildCodeFallbackLines(raw);
5960
+ const deterministicWrapperAttrs = getDefaultCodeWrapperAttributes2(lang);
5961
+ const composedPreAttrs = deterministicWrapperAttrs.preAttrs;
5962
+ const composedCodeAttrs = deterministicWrapperAttrs.codeAttrs;
5963
+ const shouldAppendTerminalNewline = (renderedLines) => {
5964
+ if (!sourceEndsWithNewline || renderedLines.length === 0 || lines.length === 0) {
5965
+ return false;
5966
+ }
5967
+ const lastRendered = renderedLines[renderedLines.length - 1];
5968
+ const lastKnown = lines[lines.length - 1];
5969
+ if (!lastRendered || !lastKnown || lastRendered.index !== lastKnown.index || lastRendered.text === "") {
5970
+ return false;
5971
+ }
5972
+ const expectedRenderedLineCount = Math.max(0, sourceLines.length - 1);
5973
+ const expectedLastRenderedLine = sourceLines.length >= 2 ? sourceLines[sourceLines.length - 2] ?? "" : "";
5974
+ return renderedLines.length === expectedRenderedLineCount && lastRendered.text === expectedLastRenderedLine;
5975
+ };
5153
5976
  if (lines.length > 0) {
5154
5977
  const hasLineHtml = lines.some((line) => typeof line.html === "string" && line.html.length > 0);
5155
- if (!hasLineHtml && node.block?.payload.highlightedHtml) {
5156
- return node.block.payload.highlightedHtml;
5978
+ if (!hasLineHtml && blockHtml) {
5979
+ return blockHtml;
5157
5980
  }
5158
5981
  if (shouldVirtualize) {
5159
- return composeHighlightedHtml(virtualization.window.visibleLines, preAttrs, codeAttrs);
5982
+ return composeHighlightedHtml(
5983
+ virtualization.window.visibleLines,
5984
+ composedPreAttrs,
5985
+ composedCodeAttrs,
5986
+ shouldAppendTerminalNewline(virtualization.window.visibleLines)
5987
+ );
5160
5988
  }
5161
- return composeHighlightedHtml(lines, preAttrs, codeAttrs);
5989
+ return composeHighlightedHtml(lines, composedPreAttrs, composedCodeAttrs, shouldAppendTerminalNewline(lines));
5990
+ }
5991
+ if (!shouldVirtualize && blockHtml) {
5992
+ const hasHighlightedLines = blockHtml.includes('class="line"');
5993
+ if (!hasHighlightedLines && rawFallbackLines.length > 0) {
5994
+ return composeHighlightedHtml(
5995
+ rawFallbackLines,
5996
+ composedPreAttrs,
5997
+ composedCodeAttrs,
5998
+ sourceEndsWithNewline && rawFallbackLines[rawFallbackLines.length - 1]?.text !== ""
5999
+ );
6000
+ }
6001
+ return blockHtml;
5162
6002
  }
5163
- const blockHtml = node.block?.payload.highlightedHtml ?? "";
5164
6003
  if (blockHtml?.trim().match(/^```[\w-]*\s*$/)) {
5165
- const raw = node.block?.payload.raw ?? "";
5166
- if (raw.trim().length > 0) {
5167
- return composeHighlightedHtml([{ index: 0, text: raw, html: null, id: "temp" }], preAttrs, codeAttrs);
6004
+ if (rawFallbackLines.length > 0) {
6005
+ return composeHighlightedHtml(
6006
+ rawFallbackLines,
6007
+ composedPreAttrs,
6008
+ composedCodeAttrs,
6009
+ sourceEndsWithNewline && rawFallbackLines[rawFallbackLines.length - 1]?.text !== ""
6010
+ );
5168
6011
  }
5169
6012
  }
6013
+ if (!blockHtml && rawFallbackLines.length > 0) {
6014
+ return composeHighlightedHtml(
6015
+ rawFallbackLines,
6016
+ composedPreAttrs,
6017
+ composedCodeAttrs,
6018
+ sourceEndsWithNewline && rawFallbackLines[rawFallbackLines.length - 1]?.text !== ""
6019
+ );
6020
+ }
5170
6021
  return blockHtml;
5171
- }, [shouldVirtualize, node.block?.payload.highlightedHtml, lines, preAttrs, codeAttrs, virtualization.window.visibleLines, node.block?.payload.raw]);
5172
- const rendered = /* @__PURE__ */ jsx3(
6022
+ }, [blockIsFinalized, shouldVirtualize, node.block?.payload.highlightedHtml, node.block?.payload.meta, lines, lang, virtualization.window.visibleLines, node.block?.payload.raw]);
6023
+ const rendered = /* @__PURE__ */ jsx4(
5173
6024
  CodeComponent,
5174
6025
  {
5175
6026
  html: highlightedHtml,
@@ -5180,20 +6031,26 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
5180
6031
  codeAttrs
5181
6032
  }
5182
6033
  );
6034
+ const effectiveLineCount = React7.useMemo(() => {
6035
+ if (lines.length > 0) {
6036
+ return lines.length;
6037
+ }
6038
+ return 1;
6039
+ }, [lines.length]);
5183
6040
  const codeFrameClass = "not-prose flex flex-col rounded-lg border border-input pt-1 font-mono text-sm";
5184
6041
  const codeMetricsAttrs = {
5185
6042
  "data-code-block": "true",
5186
6043
  "data-block-id": blockId,
5187
6044
  "data-code-virtualized": shouldVirtualize ? "true" : "false",
5188
- "data-code-total-lines": String(lines.length),
5189
- "data-code-mounted-lines": String(shouldVirtualize ? virtualization.window.mountedLines : lines.length),
6045
+ "data-code-total-lines": String(effectiveLineCount),
6046
+ "data-code-mounted-lines": String(shouldVirtualize ? virtualization.window.mountedLines : effectiveLineCount),
5190
6047
  "data-code-window-size": String(virtualization.config.windowSize)
5191
6048
  };
5192
6049
  const codeView = shouldVirtualize ? (() => {
5193
6050
  const { containerRef, window: window2, handleScroll, lineHeight } = virtualization;
5194
6051
  const spacerTop = window2.startIndex * lineHeight;
5195
6052
  const spacerBottom = (window2.totalLines - window2.endIndex) * lineHeight;
5196
- return /* @__PURE__ */ jsx3("pre", { className: codeFrameClass, ...codeMetricsAttrs, children: /* @__PURE__ */ jsxs2(
6053
+ return /* @__PURE__ */ jsx4("pre", { className: codeFrameClass, ...codeMetricsAttrs, children: /* @__PURE__ */ jsxs3(
5197
6054
  "div",
5198
6055
  {
5199
6056
  ref: containerRef,
@@ -5201,13 +6058,13 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
5201
6058
  style: { overflowY: "auto", overflowX: "hidden", maxHeight: "600px" },
5202
6059
  onScroll: handleScroll,
5203
6060
  children: [
5204
- /* @__PURE__ */ jsx3("div", { style: { height: spacerTop }, "aria-hidden": "true" }),
6061
+ /* @__PURE__ */ jsx4("div", { style: { height: spacerTop }, "aria-hidden": "true" }),
5205
6062
  rendered,
5206
- /* @__PURE__ */ jsx3("div", { style: { height: spacerBottom }, "aria-hidden": "true" })
6063
+ /* @__PURE__ */ jsx4("div", { style: { height: spacerBottom }, "aria-hidden": "true" })
5207
6064
  ]
5208
6065
  }
5209
6066
  ) });
5210
- })() : /* @__PURE__ */ jsx3("pre", { className: codeFrameClass, ...codeMetricsAttrs, children: rendered });
6067
+ })() : /* @__PURE__ */ jsx4("pre", { className: codeFrameClass, ...codeMetricsAttrs, children: rendered });
5211
6068
  const blockComponentMap = registry.getBlockComponentMap();
5212
6069
  const MermaidComponent = Object.prototype.hasOwnProperty.call(blockComponentMap, "mermaid") ? blockComponentMap.mermaid : null;
5213
6070
  if ((lang ?? "").toLowerCase() === "mermaid" && MermaidComponent) {
@@ -5215,7 +6072,7 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
5215
6072
  const fenced = stripCodeFence(raw);
5216
6073
  const code = fenced.hadFence ? fenced.code : raw;
5217
6074
  if (deferredConfig) {
5218
- return /* @__PURE__ */ jsx3("div", { ref: deferredRef, children: shouldRenderDeferred ? React7.createElement(MermaidComponent, {
6075
+ return /* @__PURE__ */ jsx4("div", { ref: deferredRef, children: shouldRenderDeferred ? React7.createElement(MermaidComponent, {
5219
6076
  code,
5220
6077
  renderCode: codeView,
5221
6078
  meta: node.block.payload.meta,
@@ -5232,21 +6089,152 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
5232
6089
  return codeView;
5233
6090
  });
5234
6091
  CodeBlockView.displayName = "CodeBlockView";
5235
- function composeHighlightedHtml(lines, preAttrs, codeAttrs) {
6092
+ function composeHighlightedHtml(lines, preAttrs, codeAttrs, appendTerminalNewline = false) {
5236
6093
  const lineMarkup = lines.map((line) => {
5237
- const content = line.html ?? escapeHtml3(line.text);
6094
+ const content = line.html ?? renderTokenLineHtml(line.tokens) ?? escapeHtml3(line.text);
5238
6095
  const dataLine = Number.isFinite(line.index) ? ` data-line="${line.index + 1}"` : "";
5239
6096
  return `<span class="line"${dataLine}>${content}</span>`;
5240
6097
  }).join("\n");
5241
6098
  const preAttr = attrsToString(preAttrs);
5242
6099
  const codeAttr = attrsToString(codeAttrs);
5243
- return `<pre${preAttr}><code${codeAttr}>${lineMarkup}
5244
- </code></pre>`;
6100
+ return `<pre${preAttr}><code${codeAttr}>${lineMarkup}${appendTerminalNewline ? "\n" : ""}</code></pre>`;
6101
+ }
6102
+ var TOKEN_FONT_STYLE_ITALIC = 1;
6103
+ var TOKEN_FONT_STYLE_BOLD = 2;
6104
+ var TOKEN_FONT_STYLE_UNDERLINE = 4;
6105
+ var TOKEN_FONT_STYLE_STRIKETHROUGH = 8;
6106
+ function renderTokenLineHtml(tokens) {
6107
+ if (!tokens || !Array.isArray(tokens.spans)) {
6108
+ return null;
6109
+ }
6110
+ return mergeRenderableTokenSpans(tokens.spans).map((span) => renderTokenSpanHtml(span)).join("");
6111
+ }
6112
+ function renderTokenSpanHtml(span) {
6113
+ const dark = span.v?.dark;
6114
+ const light = span.v?.light;
6115
+ const single = span.s;
6116
+ const styles = [];
6117
+ if (dark?.fg) {
6118
+ styles.push(`--shiki-dark:${dark.fg}`);
6119
+ }
6120
+ if (light?.fg) {
6121
+ styles.push(`--shiki-light:${light.fg}`);
6122
+ }
6123
+ if (!light?.fg && !dark?.fg && single?.fg) {
6124
+ styles.push(`color:${single.fg}`);
6125
+ }
6126
+ const fontStyle = light?.fs ?? dark?.fs ?? single?.fs;
6127
+ if (typeof fontStyle === "number" && fontStyle > 0) {
6128
+ if (fontStyle & TOKEN_FONT_STYLE_ITALIC) {
6129
+ styles.push("font-style: italic");
6130
+ }
6131
+ if (fontStyle & TOKEN_FONT_STYLE_BOLD) {
6132
+ styles.push("font-weight: bold");
6133
+ }
6134
+ const decorations = [];
6135
+ if (fontStyle & TOKEN_FONT_STYLE_UNDERLINE) {
6136
+ decorations.push("underline");
6137
+ }
6138
+ if (fontStyle & TOKEN_FONT_STYLE_STRIKETHROUGH) {
6139
+ decorations.push("line-through");
6140
+ }
6141
+ if (decorations.length > 0) {
6142
+ styles.push(`text-decoration: ${decorations.join(" ")}`);
6143
+ }
6144
+ }
6145
+ const content = escapeHtml3(span.t ?? "");
6146
+ if (styles.length === 0) {
6147
+ return content;
6148
+ }
6149
+ return `<span style="${styles.join(";")}">${content}</span>`;
6150
+ }
6151
+ function mergeRenderableTokenSpans(spans) {
6152
+ if (spans.length <= 1) {
6153
+ return spans.slice();
6154
+ }
6155
+ const merged = [];
6156
+ let pendingWhitespace = "";
6157
+ for (const span of spans) {
6158
+ const text = typeof span.t === "string" ? span.t : "";
6159
+ if (/^[\t ]+$/.test(text)) {
6160
+ pendingWhitespace += text;
6161
+ continue;
6162
+ }
6163
+ const nextSpan = pendingWhitespace ? { ...span, t: `${pendingWhitespace}${text}` } : span;
6164
+ pendingWhitespace = "";
6165
+ const previous = merged[merged.length - 1];
6166
+ if (previous && sameTokenSpanStyle(previous, nextSpan)) {
6167
+ previous.t += nextSpan.t;
6168
+ } else {
6169
+ merged.push({ ...nextSpan });
6170
+ }
6171
+ }
6172
+ if (pendingWhitespace) {
6173
+ const previous = merged[merged.length - 1];
6174
+ if (previous) {
6175
+ previous.t += pendingWhitespace;
6176
+ } else {
6177
+ merged.push({ t: pendingWhitespace });
6178
+ }
6179
+ }
6180
+ return merged;
6181
+ }
6182
+ function sameTokenSpanStyle(a, b) {
6183
+ const variantKeys = /* @__PURE__ */ new Set([
6184
+ ...Object.keys(a.v ?? {}),
6185
+ ...Object.keys(b.v ?? {})
6186
+ ]);
6187
+ for (const key of variantKeys) {
6188
+ const aStyle = a.v?.[key];
6189
+ const bStyle = b.v?.[key];
6190
+ if ((aStyle?.fg ?? null) !== (bStyle?.fg ?? null)) return false;
6191
+ if ((aStyle?.bg ?? null) !== (bStyle?.bg ?? null)) return false;
6192
+ if ((aStyle?.fs ?? null) !== (bStyle?.fs ?? null)) return false;
6193
+ }
6194
+ if ((a.s?.fg ?? null) !== (b.s?.fg ?? null)) return false;
6195
+ if ((a.s?.bg ?? null) !== (b.s?.bg ?? null)) return false;
6196
+ if ((a.s?.fs ?? null) !== (b.s?.fs ?? null)) return false;
6197
+ return true;
6198
+ }
6199
+ function resolveCodeSourceText(raw, metaCode) {
6200
+ if (metaCode !== null) {
6201
+ return metaCode.replace(/\r\n?/g, "\n");
6202
+ }
6203
+ if (!raw) return "";
6204
+ const normalized = raw.replace(/\r\n?/g, "\n");
6205
+ const fenced = stripCodeFence(normalized);
6206
+ return fenced.hadFence ? fenced.code : normalized;
5245
6207
  }
5246
6208
  function attrsToString(attrs) {
5247
6209
  if (!attrs) return "";
5248
6210
  return Object.entries(attrs).map(([key, value]) => ` ${key}="${escapeAttribute(value)}"`).join("");
5249
6211
  }
6212
+ function buildCodeFallbackLines(raw) {
6213
+ const normalized = normalizeCodeFallbackRaw(raw);
6214
+ if (normalized.length === 0) {
6215
+ if (/^\s*```[^\n]*\n?\s*$/.test(raw)) {
6216
+ return [{ id: "temp-0", index: 0, text: "", html: null }];
6217
+ }
6218
+ return [];
6219
+ }
6220
+ return normalized.split("\n").map((line, index) => ({
6221
+ id: `temp-${index}`,
6222
+ index,
6223
+ text: line,
6224
+ html: null
6225
+ }));
6226
+ }
6227
+ function normalizeCodeFallbackRaw(raw) {
6228
+ if (!raw) return "";
6229
+ const normalized = raw.replace(/\r\n?/g, "\n");
6230
+ const fenced = stripCodeFence(normalized);
6231
+ if (!fenced.hadFence) {
6232
+ return normalized.replace(/\n+$/, "");
6233
+ }
6234
+ let code = fenced.code;
6235
+ code = code.replace(/\n+$/, "");
6236
+ return code;
6237
+ }
5250
6238
  function escapeHtml3(value) {
5251
6239
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5252
6240
  }
@@ -5267,7 +6255,7 @@ var MarkdownBlocksRenderer = React8.memo(({ blocks, componentRegistry, className
5267
6255
  React8.createElement(StoreBackedBlocks, { store, componentRegistry })
5268
6256
  );
5269
6257
  }
5270
- const renderedBlocks = useMemo4(() => {
6258
+ const renderedBlocks = useMemo5(() => {
5271
6259
  return blocks.map((block) => {
5272
6260
  const key = getBlockKey(block);
5273
6261
  return React8.createElement(BlockRenderer, {
@@ -5300,7 +6288,7 @@ var StoreBackedBlocks = React8.memo(
5300
6288
  );
5301
6289
  StoreBackedBlocks.displayName = "StoreBackedBlocks";
5302
6290
  var BlockRenderer = React8.memo(({ block, componentRegistry, isFinalized }) => {
5303
- const element = useMemo4(() => {
6291
+ const element = useMemo5(() => {
5304
6292
  return componentRegistry.renderBlock(block);
5305
6293
  }, [block, componentRegistry]);
5306
6294
  const className = `markdown-block markdown-block-${block.type} ${isFinalized ? "finalized" : "dirty"}`;
@@ -5370,7 +6358,9 @@ var MarkdownRenderer = class {
5370
6358
  lowPriorityFrameBudgetMs: this.config.performance?.lowPriorityFrameBudgetMs,
5371
6359
  urgentQueueThreshold: this.config.performance?.urgentQueueThreshold,
5372
6360
  batch: this.config.performance?.batch,
5373
- historyLimit: this.config.performance?.historyLimit
6361
+ historyLimit: this.config.performance?.historyLimit,
6362
+ startupMicrotaskFlushes: this.config.performance?.startupMicrotaskFlushes,
6363
+ adaptiveBudgeting: this.config.performance?.adaptiveBudgeting
5374
6364
  }
5375
6365
  });
5376
6366
  }
@@ -5564,18 +6554,8 @@ var MarkdownRenderer = class {
5564
6554
  const receivedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
5565
6555
  const batches = splitPatchBatch(message.patches);
5566
6556
  batches.forEach((batch, index) => {
5567
- const hasStructural = batch.some((patch) => {
5568
- switch (patch.op) {
5569
- case "insertChild":
5570
- case "replaceChild":
5571
- case "deleteChild":
5572
- case "reorder":
5573
- return true;
5574
- default:
5575
- return false;
5576
- }
5577
- });
5578
- const priority = hasStructural ? "high" : batch.some(isHeavyPatch) ? "low" : "high";
6557
+ const hasSemantic = batch.some((patch) => getPatchKind(patch) === "semantic");
6558
+ const priority = hasSemantic ? "high" : batch.some(isHeavyPatch) ? "low" : "high";
5579
6559
  this.patchScheduler.enqueue({
5580
6560
  patches: batch,
5581
6561
  meta: {
@@ -5712,10 +6692,10 @@ var MarkdownRenderer = class {
5712
6692
  }
5713
6693
  };
5714
6694
  function useMarkdownRenderer(config = {}) {
5715
- const [renderer] = useState4(() => new MarkdownRenderer(config));
5716
- const [blocks, setBlocks] = useState4([]);
5717
- const workerRef = useRef3(null);
5718
- useEffect4(() => {
6695
+ const [renderer] = useState5(() => new MarkdownRenderer(config));
6696
+ const [blocks, setBlocks] = useState5([]);
6697
+ const workerRef = useRef4(null);
6698
+ useEffect5(() => {
5719
6699
  if (!workerRef.current) {
5720
6700
  const fallbackFactory = () => new Worker("/workers/markdown-worker.js", { type: "module", name: "markdown-v2" });
5721
6701
  const worker = createDefaultWorker() ?? fallbackFactory();
@@ -5730,41 +6710,41 @@ function useMarkdownRenderer(config = {}) {
5730
6710
  }
5731
6711
  };
5732
6712
  }, [renderer]);
5733
- useEffect4(() => {
6713
+ useEffect5(() => {
5734
6714
  const unsubscribe = renderer.onUpdate(setBlocks);
5735
6715
  return unsubscribe;
5736
6716
  }, [renderer]);
5737
6717
  const mdxComponents = config.mdx?.components;
5738
- useEffect4(() => {
6718
+ useEffect5(() => {
5739
6719
  if (mdxComponents && Object.keys(mdxComponents).length > 0) {
5740
6720
  renderer.setMdxComponents(mdxComponents);
5741
6721
  }
5742
6722
  }, [renderer, mdxComponents]);
5743
- const append = useCallback3(
6723
+ const append = useCallback4(
5744
6724
  (text) => {
5745
6725
  renderer.append(text);
5746
6726
  },
5747
6727
  [renderer]
5748
6728
  );
5749
- const renderStatic = useCallback3(
6729
+ const renderStatic = useCallback4(
5750
6730
  (text) => {
5751
6731
  return renderer.renderStatic(text);
5752
6732
  },
5753
6733
  [renderer]
5754
6734
  );
5755
- const setBlockComponents = useCallback3(
6735
+ const setBlockComponents = useCallback4(
5756
6736
  (components) => {
5757
6737
  renderer.setBlockComponents(components);
5758
6738
  },
5759
6739
  [renderer]
5760
6740
  );
5761
- const setInlineComponents = useCallback3(
6741
+ const setInlineComponents = useCallback4(
5762
6742
  (components) => {
5763
6743
  renderer.setInlineComponents(components);
5764
6744
  },
5765
6745
  [renderer]
5766
6746
  );
5767
- const setMdxComponents = useCallback3(
6747
+ const setMdxComponents = useCallback4(
5768
6748
  (components) => {
5769
6749
  renderer.setMdxComponents(components);
5770
6750
  },
@@ -5783,13 +6763,13 @@ function useMarkdownRenderer(config = {}) {
5783
6763
  }
5784
6764
  var MarkdownRenderer2 = ({ config, className, onBlocksChange, children }) => {
5785
6765
  const api = useMarkdownRenderer(config);
5786
- const highlightRequester = useCallback3(
6766
+ const highlightRequester = useCallback4(
5787
6767
  (request) => {
5788
6768
  api.renderer.requestCodeHighlightRange(request);
5789
6769
  },
5790
6770
  [api.renderer]
5791
6771
  );
5792
- useEffect4(() => {
6772
+ useEffect5(() => {
5793
6773
  if (onBlocksChange) {
5794
6774
  onBlocksChange(api.blocks);
5795
6775
  }
@@ -5813,9 +6793,9 @@ function createMarkdownRenderer(config = {}) {
5813
6793
  }
5814
6794
 
5815
6795
  // src/streaming-markdown.tsx
5816
- import React10, { forwardRef, useCallback as useCallback4, useEffect as useEffect5, useImperativeHandle, useMemo as useMemo5, useRef as useRef4, useState as useState5 } from "react";
6796
+ import React10, { forwardRef, useCallback as useCallback5, useEffect as useEffect6, useImperativeHandle, useMemo as useMemo6, useRef as useRef5, useState as useState6 } from "react";
5817
6797
  import { createDefaultWorker as createDefaultWorker2, releaseDefaultWorker as releaseDefaultWorker2 } from "@stream-mdx/worker";
5818
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
6798
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
5819
6799
  var EMPTY_BLOCKS = [];
5820
6800
  function createConfigSignature(input) {
5821
6801
  return JSON.stringify(input);
@@ -5827,13 +6807,13 @@ var MdxCoordinatorBridge = ({ store, mode }) => {
5827
6807
  };
5828
6808
  var MdxPrefetchBridge = ({ store }) => {
5829
6809
  const blocks = useRendererBlocks(store);
5830
- const prefetchedRuntimeRef = useRef4(false);
5831
- const trackedIdsRef = useRef4(/* @__PURE__ */ new Set());
5832
- const queueRef = useRef4(null);
6810
+ const prefetchedRuntimeRef = useRef5(false);
6811
+ const trackedIdsRef = useRef5(/* @__PURE__ */ new Set());
6812
+ const queueRef = useRef5(null);
5833
6813
  if (!queueRef.current) {
5834
6814
  queueRef.current = new MdxPrefetchQueue();
5835
6815
  }
5836
- useEffect5(() => {
6816
+ useEffect6(() => {
5837
6817
  const queue = queueRef.current;
5838
6818
  if (!queue) return;
5839
6819
  const nextIds = /* @__PURE__ */ new Map();
@@ -5871,7 +6851,7 @@ var MdxPrefetchBridge = ({ store }) => {
5871
6851
  }
5872
6852
  trackedIdsRef.current = new Set(nextIds.keys());
5873
6853
  }, [blocks]);
5874
- useEffect5(() => {
6854
+ useEffect6(() => {
5875
6855
  return () => {
5876
6856
  queueRef.current?.cancelAll();
5877
6857
  };
@@ -5945,7 +6925,7 @@ function StreamingMarkdownComponent({
5945
6925
  const effectiveScheduling = scheduling ?? DEFAULT_SCHEDULING;
5946
6926
  const adaptiveSwitchEnabled = effectiveScheduling.batch === "microtask" && (scheduling?.adaptiveSwitch ?? true);
5947
6927
  const adaptiveQueueThreshold = scheduling?.adaptiveQueueThreshold ?? (typeof effectiveScheduling.maxBatchesPerFlush === "number" ? effectiveScheduling.maxBatchesPerFlush : DEFAULT_ADAPTIVE_QUEUE_THRESHOLD);
5948
- const configSignature = useMemo5(
6928
+ const configSignature = useMemo6(
5949
6929
  () => createConfigSignature({
5950
6930
  features,
5951
6931
  prewarm: prewarmLangs,
@@ -5956,16 +6936,16 @@ function StreamingMarkdownComponent({
5956
6936
  }),
5957
6937
  [features, prewarmLangs, effectiveScheduling, mdxCompileMode, mdxComponents, mdxHydration]
5958
6938
  );
5959
- const mdxHydrationController = useMemo5(() => createMdxHydrationController(mdxHydration), [mdxHydration]);
5960
- const mdxHydrationContextValue = useMemo5(
6939
+ const mdxHydrationController = useMemo6(() => createMdxHydrationController(mdxHydration), [mdxHydration]);
6940
+ const mdxHydrationContextValue = useMemo6(
5961
6941
  () => ({
5962
6942
  controller: mdxHydrationController,
5963
6943
  options: mdxHydration
5964
6944
  }),
5965
6945
  [mdxHydrationController, mdxHydration]
5966
6946
  );
5967
- const rendererRef = useRef4();
5968
- const [session, setSession] = useState5(0);
6947
+ const rendererRef = useRef5();
6948
+ const [session, setSession] = useState6(0);
5969
6949
  const rendererKey = `${configSignature}:${session}`;
5970
6950
  const historyLimit = effectiveScheduling.historyLimit ?? DEFAULT_HISTORY_LIMIT;
5971
6951
  if (!rendererRef.current || rendererRef.current.signature !== rendererKey) {
@@ -5984,7 +6964,9 @@ function StreamingMarkdownComponent({
5984
6964
  lowPriorityFrameBudgetMs: effectiveScheduling.lowPriorityFrameBudgetMs,
5985
6965
  urgentQueueThreshold: effectiveScheduling.urgentQueueThreshold,
5986
6966
  batch: effectiveScheduling.batch,
5987
- historyLimit
6967
+ historyLimit,
6968
+ startupMicrotaskFlushes: effectiveScheduling.startupMicrotaskFlushes,
6969
+ adaptiveBudgeting: effectiveScheduling.adaptiveBudgeting
5988
6970
  },
5989
6971
  mdx: mdxConfig
5990
6972
  };
@@ -5994,7 +6976,7 @@ function StreamingMarkdownComponent({
5994
6976
  };
5995
6977
  }
5996
6978
  const renderer = rendererRef.current.renderer;
5997
- const highlightRequester = useCallback4(
6979
+ const highlightRequester = useCallback5(
5998
6980
  (request) => {
5999
6981
  renderer.requestCodeHighlightRange(request);
6000
6982
  },
@@ -6002,15 +6984,15 @@ function StreamingMarkdownComponent({
6002
6984
  );
6003
6985
  const store = renderer.getStore();
6004
6986
  const blocks = useRendererBlocks(store);
6005
- const adaptiveSwitchedRef = useRef4(false);
6006
- const adaptiveFirstFlushRef = useRef4(false);
6987
+ const adaptiveSwitchedRef = useRef5(false);
6988
+ const adaptiveFirstFlushRef = useRef5(false);
6007
6989
  const mdxMode = mdxCompileMode ?? "server";
6008
- const [workerReady, setWorkerReady] = useState5(false);
6009
- const workerReadyRef = useRef4(false);
6010
- const [streamStatus, setStreamStatus] = useState5("idle");
6011
- const lastMetricsRef = useRef4(null);
6012
- const patchHistoryRef = useRef4([]);
6013
- useEffect5(() => {
6990
+ const [workerReady, setWorkerReady] = useState6(false);
6991
+ const workerReadyRef = useRef5(false);
6992
+ const [streamStatus, setStreamStatus] = useState6("idle");
6993
+ const lastMetricsRef = useRef5(null);
6994
+ const patchHistoryRef = useRef5([]);
6995
+ useEffect6(() => {
6014
6996
  if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") {
6015
6997
  return void 0;
6016
6998
  }
@@ -6034,27 +7016,27 @@ function StreamingMarkdownComponent({
6034
7016
  observer?.disconnect();
6035
7017
  };
6036
7018
  }, []);
6037
- const previousTextRef = useRef4();
6038
- useEffect5(() => {
7019
+ const previousTextRef = useRef5();
7020
+ useEffect6(() => {
6039
7021
  if (text !== void 0 && text !== previousTextRef.current) {
6040
7022
  previousTextRef.current = text;
6041
7023
  setSession((prev) => prev + 1);
6042
7024
  }
6043
7025
  }, [text]);
6044
- const previousStreamRef = useRef4();
6045
- useEffect5(() => {
7026
+ const previousStreamRef = useRef5();
7027
+ useEffect6(() => {
6046
7028
  if (stream && stream !== previousStreamRef.current) {
6047
7029
  previousStreamRef.current = stream;
6048
7030
  setSession((prev) => prev + 1);
6049
7031
  }
6050
7032
  }, [stream]);
6051
7033
  const linkSafetyEnabled = Boolean(linkSafety?.enabled);
6052
- const [linkModalState, setLinkModalState] = useState5({
7034
+ const [linkModalState, setLinkModalState] = useState6({
6053
7035
  url: "",
6054
7036
  isOpen: false,
6055
7037
  isChecking: false
6056
7038
  });
6057
- const openLink = useMemo5(() => {
7039
+ const openLink = useMemo6(() => {
6058
7040
  return (url) => {
6059
7041
  if (typeof window === "undefined") return;
6060
7042
  if (url.startsWith("http://") || url.startsWith("https://")) {
@@ -6064,7 +7046,7 @@ function StreamingMarkdownComponent({
6064
7046
  }
6065
7047
  };
6066
7048
  }, []);
6067
- const handleLinkClick = useMemo5(() => {
7049
+ const handleLinkClick = useMemo6(() => {
6068
7050
  return async (url) => {
6069
7051
  if (!linkSafetyEnabled) {
6070
7052
  openLink(url);
@@ -6086,10 +7068,10 @@ function StreamingMarkdownComponent({
6086
7068
  setLinkModalState({ url, isOpen: true, isChecking: false });
6087
7069
  };
6088
7070
  }, [linkSafety, linkSafetyEnabled, openLink]);
6089
- const handleLinkClose = useMemo5(() => {
7071
+ const handleLinkClose = useMemo6(() => {
6090
7072
  return () => setLinkModalState((prev) => ({ ...prev, isOpen: false }));
6091
7073
  }, []);
6092
- const handleLinkConfirm = useMemo5(() => {
7074
+ const handleLinkConfirm = useMemo6(() => {
6093
7075
  return () => {
6094
7076
  const url = linkModalState.url;
6095
7077
  setLinkModalState((prev) => ({ ...prev, isOpen: false }));
@@ -6098,7 +7080,7 @@ function StreamingMarkdownComponent({
6098
7080
  }
6099
7081
  };
6100
7082
  }, [linkModalState.url, openLink]);
6101
- const handleLinkCopy = useMemo5(() => {
7083
+ const handleLinkCopy = useMemo6(() => {
6102
7084
  return () => {
6103
7085
  const url = linkModalState.url;
6104
7086
  if (!url || typeof navigator === "undefined") return;
@@ -6119,7 +7101,7 @@ function StreamingMarkdownComponent({
6119
7101
  }
6120
7102
  };
6121
7103
  }, [linkModalState.url]);
6122
- const resolvedInlineComponents = useMemo5(() => {
7104
+ const resolvedInlineComponents = useMemo6(() => {
6123
7105
  if (!linkSafetyEnabled) {
6124
7106
  return inlineComponents;
6125
7107
  }
@@ -6146,14 +7128,14 @@ function StreamingMarkdownComponent({
6146
7128
  };
6147
7129
  return { ...inlineComponents ?? {}, link: LinkComponent };
6148
7130
  }, [handleLinkClick, inlineComponents, linkSafetyEnabled]);
6149
- useEffect5(() => {
7131
+ useEffect6(() => {
6150
7132
  setStreamStatus("idle");
6151
7133
  }, [session]);
6152
- useEffect5(() => {
7134
+ useEffect6(() => {
6153
7135
  adaptiveSwitchedRef.current = false;
6154
7136
  adaptiveFirstFlushRef.current = false;
6155
7137
  }, [renderer]);
6156
- useEffect5(() => {
7138
+ useEffect6(() => {
6157
7139
  if (!adaptiveSwitchEnabled) return void 0;
6158
7140
  const unsubscribe = renderer.addFlushListener((result) => {
6159
7141
  if (adaptiveSwitchedRef.current) return;
@@ -6175,12 +7157,12 @@ function StreamingMarkdownComponent({
6175
7157
  });
6176
7158
  return unsubscribe;
6177
7159
  }, [renderer, adaptiveSwitchEnabled, adaptiveQueueThreshold]);
6178
- useEffect5(() => {
7160
+ useEffect6(() => {
6179
7161
  lastMetricsRef.current = null;
6180
7162
  patchHistoryRef.current = [];
6181
7163
  void renderer;
6182
7164
  }, [renderer]);
6183
- useEffect5(() => {
7165
+ useEffect6(() => {
6184
7166
  if (components) {
6185
7167
  renderer.setBlockComponents(components);
6186
7168
  }
@@ -6199,7 +7181,7 @@ function StreamingMarkdownComponent({
6199
7181
  renderer.getComponentRegistry().setHtmlElements(htmlElements);
6200
7182
  }
6201
7183
  }, [renderer, components, mdxComponents, inlineComponents, resolvedInlineComponents, tableElements, htmlElements]);
6202
- useEffect5(() => {
7184
+ useEffect6(() => {
6203
7185
  const removeListener = renderer.addFlushListener((result) => {
6204
7186
  const metrics = summarizeFlush(result);
6205
7187
  lastMetricsRef.current = metrics;
@@ -6217,12 +7199,12 @@ function StreamingMarkdownComponent({
6217
7199
  });
6218
7200
  return removeListener;
6219
7201
  }, [renderer, historyLimit, onMetrics]);
6220
- useEffect5(() => {
7202
+ useEffect6(() => {
6221
7203
  if (patchHistoryRef.current.length > historyLimit) {
6222
7204
  patchHistoryRef.current.splice(0, patchHistoryRef.current.length - historyLimit);
6223
7205
  }
6224
7206
  }, [historyLimit]);
6225
- useEffect5(() => {
7207
+ useEffect6(() => {
6226
7208
  const restartToken = session;
6227
7209
  void restartToken;
6228
7210
  if (process.env.NODE_ENV !== "production" && DEBUG_WORKER) {
@@ -6278,7 +7260,7 @@ function StreamingMarkdownComponent({
6278
7260
  }
6279
7261
  };
6280
7262
  }, [renderer, workerSource, session, onError, managedWorker]);
6281
- useEffect5(() => {
7263
+ useEffect6(() => {
6282
7264
  const restartToken = session;
6283
7265
  void restartToken;
6284
7266
  let cancelled = false;
@@ -6395,8 +7377,8 @@ function StreamingMarkdownComponent({
6395
7377
  }),
6396
7378
  [renderer, store]
6397
7379
  );
6398
- const hasActiveBlocks = useMemo5(() => blocks.some((block) => !block.isFinalized), [blocks]);
6399
- const caretValue = useMemo5(() => {
7380
+ const hasActiveBlocks = useMemo6(() => blocks.some((block) => !block.isFinalized), [blocks]);
7381
+ const caretValue = useMemo6(() => {
6400
7382
  if (!caret) {
6401
7383
  return null;
6402
7384
  }
@@ -6417,7 +7399,7 @@ function StreamingMarkdownComponent({
6417
7399
  ...style ?? {},
6418
7400
  ["--stream-mdx-caret"]: JSON.stringify(caretValue)
6419
7401
  } : style;
6420
- const deferredConfig = useMemo5(() => {
7402
+ const deferredConfig = useMemo6(() => {
6421
7403
  if (!deferHeavyBlocks) {
6422
7404
  return null;
6423
7405
  }
@@ -6434,10 +7416,10 @@ function StreamingMarkdownComponent({
6434
7416
  onConfirm: handleLinkConfirm,
6435
7417
  onCopy: handleLinkCopy
6436
7418
  }) : null;
6437
- return /* @__PURE__ */ jsx4(MdxHydrationContext.Provider, { value: mdxHydrationContextValue, children: /* @__PURE__ */ jsxs3(DeferredRenderContext.Provider, { value: deferredConfig, children: [
6438
- /* @__PURE__ */ jsx4(MdxPrefetchBridge, { store }),
6439
- mdxMode === "server" ? /* @__PURE__ */ jsx4(MdxCoordinatorBridge, { store, mode: mdxMode }) : null,
6440
- /* @__PURE__ */ jsx4(CodeHighlightRequestContext.Provider, { value: highlightRequester, children: /* @__PURE__ */ jsx4(
7419
+ return /* @__PURE__ */ jsx5(MdxHydrationContext.Provider, { value: mdxHydrationContextValue, children: /* @__PURE__ */ jsxs4(DeferredRenderContext.Provider, { value: deferredConfig, children: [
7420
+ /* @__PURE__ */ jsx5(MdxPrefetchBridge, { store }),
7421
+ mdxMode === "server" ? /* @__PURE__ */ jsx5(MdxCoordinatorBridge, { store, mode: mdxMode }) : null,
7422
+ /* @__PURE__ */ jsx5(CodeHighlightRequestContext.Provider, { value: highlightRequester, children: /* @__PURE__ */ jsx5(
6441
7423
  MarkdownBlocksRenderer,
6442
7424
  {
6443
7425
  blocks: EMPTY_BLOCKS,
@@ -6556,6 +7538,7 @@ function percentile2(values, ratio) {
6556
7538
  return sorted[index];
6557
7539
  }
6558
7540
  export {
7541
+ BottomStickScrollArea,
6559
7542
  ComponentRegistry,
6560
7543
  DEFAULT_COALESCE_CONFIG,
6561
7544
  DEFAULT_VIRTUALIZED_CODE_CONFIG,
@@ -6581,6 +7564,7 @@ export {
6581
7564
  defaultInlineComponents,
6582
7565
  getMDXClient,
6583
7566
  getMDXComponentFactory,
7567
+ getPatchKind,
6584
7568
  isHeavyPatch,
6585
7569
  isListPatchDebugEnabled,
6586
7570
  mapHtmlToReact,