@stream-mdx/react 0.3.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 -14
  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 +511 -176
  8. package/dist/components/index.d.cts +3 -1
  9. package/dist/components/index.d.ts +3 -1
  10. package/dist/components/index.mjs +358 -26
  11. package/dist/{index-D7px9jug.d.cts → index-BVhp_pcF.d.ts} +4 -1
  12. package/dist/{index-D7px9jug.d.ts → index-EaeouYp2.d.cts} +4 -1
  13. package/dist/index.cjs +2146 -1145
  14. package/dist/index.d.cts +6 -4
  15. package/dist/index.d.ts +6 -4
  16. package/dist/index.mjs +3835 -2836
  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 +273 -24
  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 +274 -25
  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 +3386 -2727
  40. package/dist/renderer.d.cts +15 -12
  41. package/dist/renderer.d.ts +15 -12
  42. package/dist/renderer.mjs +3410 -2751
  43. package/dist/server.cjs +4842 -0
  44. package/dist/server.d.cts +254 -0
  45. package/dist/server.d.ts +254 -0
  46. package/dist/server.mjs +4824 -0
  47. package/dist/{streaming-markdown-Dp1IDgMT.d.ts → streaming-markdown-CEI9eDZm.d.ts} +3 -1
  48. package/dist/{streaming-markdown-DSC4L0xR.d.cts → streaming-markdown-DRisClXb.d.cts} +3 -1
  49. package/dist/streaming-markdown.cjs +3476 -2794
  50. package/dist/streaming-markdown.d.cts +4 -2
  51. package/dist/streaming-markdown.d.ts +4 -2
  52. package/dist/streaming-markdown.mjs +3436 -2754
  53. package/dist/utils/inline-html.d.cts +3 -1
  54. package/dist/utils/inline-html.d.ts +3 -1
  55. package/package.json +19 -3
  56. package/shadcn/README.md +28 -0
  57. package/shadcn/bottom-stick-scroll-area.tsx +358 -0
@@ -1,3 +1,5 @@
1
+ export { BottomStickDebugState, BottomStickJumpButtonRenderState, BottomStickMode, BottomStickScrollArea, BottomStickScrollAreaProps } from './bottom-stick-scroll-area.cjs';
1
2
  import '@stream-mdx/core';
2
3
  import 'react';
3
- export { C as ComponentRegistry, D as DefaultLinkSafetyModal, L as LinkSafetyModalProps, f as defaultBlockComponents, e as defaultInlineComponents, m as mapHtmlToReact, r as renderInlineNodes, g as renderParagraphMixedSegments } from '../index-D7px9jug.cjs';
4
+ export { C as ComponentRegistry, D as DefaultLinkSafetyModal, L as LinkSafetyModalProps, e as defaultBlockComponents, d as defaultInlineComponents, m as mapHtmlToReact, r as renderInlineNodes, f as renderParagraphMixedSegments } from '../index-EaeouYp2.cjs';
5
+ import 'react/jsx-runtime';
@@ -1,3 +1,5 @@
1
+ export { BottomStickDebugState, BottomStickJumpButtonRenderState, BottomStickMode, BottomStickScrollArea, BottomStickScrollAreaProps } from './bottom-stick-scroll-area.js';
1
2
  import '@stream-mdx/core';
2
3
  import 'react';
3
- export { C as ComponentRegistry, D as DefaultLinkSafetyModal, L as LinkSafetyModalProps, f as defaultBlockComponents, e as defaultInlineComponents, m as mapHtmlToReact, r as renderInlineNodes, g as renderParagraphMixedSegments } from '../index-D7px9jug.js';
4
+ export { C as ComponentRegistry, D as DefaultLinkSafetyModal, L as LinkSafetyModalProps, e as defaultBlockComponents, d as defaultInlineComponents, m as mapHtmlToReact, r as renderInlineNodes, f as renderParagraphMixedSegments } from '../index-BVhp_pcF.js';
5
+ import 'react/jsx-runtime';
@@ -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
 
@@ -437,11 +752,11 @@ function loadMdxRuntime() {
437
752
  }
438
753
 
439
754
  // src/renderer/use-deferred-render.ts
440
- import { useEffect, useState } from "react";
755
+ import { useEffect as useEffect2, useState as useState2 } from "react";
441
756
  function useDeferredRender(ref, options) {
442
757
  const enabled = options?.enabled ?? true;
443
- const [shouldRender, setShouldRender] = useState(!enabled);
444
- useEffect(() => {
758
+ const [shouldRender, setShouldRender] = useState2(!enabled);
759
+ useEffect2(() => {
445
760
  if (!enabled) {
446
761
  setShouldRender(true);
447
762
  return;
@@ -766,12 +1081,12 @@ var INLINE_ELEMENTS = /* @__PURE__ */ new Set([
766
1081
  ]);
767
1082
 
768
1083
  // src/components/link-safety-modal.tsx
769
- import { jsx, jsxs } from "react/jsx-runtime";
1084
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
770
1085
  var DefaultLinkSafetyModal = ({ url, isOpen, isChecking, onClose, onConfirm, onCopy }) => {
771
1086
  if (!isOpen) {
772
1087
  return null;
773
1088
  }
774
- return /* @__PURE__ */ jsx(
1089
+ return /* @__PURE__ */ jsx2(
775
1090
  "div",
776
1091
  {
777
1092
  className: "stream-mdx-link-modal-backdrop",
@@ -789,7 +1104,7 @@ var DefaultLinkSafetyModal = ({ url, isOpen, isChecking, onClose, onConfirm, onC
789
1104
  padding: "24px"
790
1105
  },
791
1106
  onClick: onClose,
792
- children: /* @__PURE__ */ jsxs(
1107
+ children: /* @__PURE__ */ jsxs2(
793
1108
  "div",
794
1109
  {
795
1110
  className: "stream-mdx-link-modal",
@@ -804,12 +1119,12 @@ var DefaultLinkSafetyModal = ({ url, isOpen, isChecking, onClose, onConfirm, onC
804
1119
  },
805
1120
  onClick: (event) => event.stopPropagation(),
806
1121
  children: [
807
- /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Open external link?" }),
808
- /* @__PURE__ */ jsx("div", { style: { fontSize: 13, opacity: 0.8, wordBreak: "break-all", marginBottom: 16 }, children: url }),
809
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
810
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onCopy, disabled: isChecking, style: buttonStyle, children: "Copy link" }),
811
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onConfirm, disabled: isChecking, style: primaryButtonStyle, children: "Open link" }),
812
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onClose, style: buttonStyle, children: "Cancel" })
1122
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Open external link?" }),
1123
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 13, opacity: 0.8, wordBreak: "break-all", marginBottom: 16 }, children: url }),
1124
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [
1125
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: onCopy, disabled: isChecking, style: buttonStyle, children: "Copy link" }),
1126
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: onConfirm, disabled: isChecking, style: primaryButtonStyle, children: "Open link" }),
1127
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: onClose, style: buttonStyle, children: "Cancel" })
813
1128
  ] })
814
1129
  ]
815
1130
  }
@@ -984,7 +1299,11 @@ var defaultInlineComponents = {
984
1299
  const n = number ?? "?";
985
1300
  const href = number ? `#fn:${n}` : void 0;
986
1301
  const id = number ? `fnref:${n}` : void 0;
987
- return React4.createElement("sup", { className: "footnote-ref" }, React4.createElement("a", { href, id, "data-label": label }, String(n)));
1302
+ return React4.createElement(
1303
+ "sup",
1304
+ { className: "footnote-ref" },
1305
+ React4.createElement("a", { href, id, "data-label": label, style: { color: "var(--foreground)", textDecoration: "underline" } }, String(n))
1306
+ );
988
1307
  }
989
1308
  };
990
1309
  var defaultBlockComponents = {
@@ -1021,15 +1340,17 @@ var defaultBlockComponents = {
1021
1340
  effectiveInlines.length > 0 ? renderInlineNodes(effectiveInlines, defaultInlineComponents) : raw ?? ""
1022
1341
  );
1023
1342
  },
1024
- heading: ({ level, inlines, text }) => {
1343
+ heading: ({ level, inlines, text, meta }) => {
1025
1344
  const tag = `h${level}`;
1026
1345
  const onlyPlainText = Array.isArray(inlines) && inlines.length > 0 && inlines.every((node) => node.kind === "text");
1027
1346
  const children = onlyPlainText ? inlines.map((node) => node.kind === "text" ? node.text : "").join("") : renderInlineNodes(inlines, defaultInlineComponents);
1347
+ const headingId = typeof meta?.headingId === "string" && meta.headingId.length > 0 ? meta.headingId : void 0;
1028
1348
  return React4.createElement(
1029
1349
  tag,
1030
1350
  {
1031
1351
  className: `markdown-heading markdown-h${level}`,
1032
- ...text ? { "data-heading-text": text } : {}
1352
+ ...text ? { "data-heading-text": text } : {},
1353
+ ...headingId ? { id: headingId, "data-heading-id": headingId } : {}
1033
1354
  },
1034
1355
  children
1035
1356
  );
@@ -1114,7 +1435,7 @@ var defaultBlockComponents = {
1114
1435
  },
1115
1436
  html: ({ __trustedHtml, elements }) => {
1116
1437
  const raw = typeof __trustedHtml === "string" ? __trustedHtml : __trustedHtml.toString();
1117
- const htmlString = sanitizeHTML(raw);
1438
+ const htmlString = typeof window === "undefined" ? raw : sanitizeHTML(raw);
1118
1439
  if (typeof window !== "undefined" && elements) {
1119
1440
  const content = mapHtmlToReact(htmlString, elements);
1120
1441
  return React4.createElement("div", { className: "markdown-html" }, content);
@@ -1125,7 +1446,7 @@ var defaultBlockComponents = {
1125
1446
  dangerouslySetInnerHTML: { __html: htmlString }
1126
1447
  });
1127
1448
  },
1128
- mdx: ({ compiledRef, compiledModule, status, errorMessage, raw }) => {
1449
+ mdx: ({ compiledRef, compiledModule, status, errorMessage }) => {
1129
1450
  const [Comp, setComp] = React4.useState(null);
1130
1451
  const [internalError, setInternalError] = React4.useState(null);
1131
1452
  const [renderError, setRenderError] = React4.useState(null);
@@ -1226,7 +1547,6 @@ var defaultBlockComponents = {
1226
1547
  const waitingForVisibility = deferHydration && !shouldHydrate && Boolean(resolvedId);
1227
1548
  const pendingLabel = waitingForVisibility ? "MDX ready (hydrate on view)\u2026" : compileMode === "worker" ? "Compiling MDX (client)\u2026" : "Compiling MDX (server)\u2026";
1228
1549
  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.");
1229
- const rawFallback = typeof raw === "string" && raw.trim().length > 0 ? React4.createElement("pre", { className: "markdown-mdx-fallback" }, raw) : null;
1230
1550
  const allowRetry = Boolean(internalError || renderError);
1231
1551
  const retryButton = allowRetry ? React4.createElement(
1232
1552
  "button",
@@ -1238,9 +1558,10 @@ var defaultBlockComponents = {
1238
1558
  "Retry hydration"
1239
1559
  ) : null;
1240
1560
  const errorPanel = React4.createElement(
1241
- "div",
1242
- { className: "markdown-mdx-error" },
1243
- React4.createElement(React4.Fragment, null, React4.createElement("strong", null, "MDX failed"), React4.createElement("div", null, message))
1561
+ React4.Fragment,
1562
+ null,
1563
+ React4.createElement("strong", null, "MDX failed"),
1564
+ React4.createElement("div", null, message)
1244
1565
  );
1245
1566
  if (Comp && !failureMessage) {
1246
1567
  return React4.createElement(
@@ -1263,14 +1584,13 @@ var defaultBlockComponents = {
1263
1584
  return React4.createElement(
1264
1585
  "div",
1265
1586
  {
1266
- className: "markdown-mdx",
1587
+ className: "markdown-mdx error",
1267
1588
  ref: containerRef,
1268
1589
  "data-mdx-ref": pendingId,
1269
1590
  "data-mdx-status": "error",
1270
1591
  "data-mdx-mode": compileMode
1271
1592
  },
1272
1593
  errorPanel,
1273
- rawFallback,
1274
1594
  retryButton
1275
1595
  );
1276
1596
  }
@@ -1297,7 +1617,16 @@ var defaultBlockComponents = {
1297
1617
  { key: item.number, id: `fn:${item.number}` },
1298
1618
  renderInlineNodes(item.inlines, defaultInlineComponents),
1299
1619
  " ",
1300
- React4.createElement("a", { href: `#fnref:${item.number}`, className: "footnote-backref", "aria-label": "Back to content" }, "\u21A9")
1620
+ React4.createElement(
1621
+ "a",
1622
+ {
1623
+ href: `#fnref:${item.number}`,
1624
+ className: "footnote-backref",
1625
+ "aria-label": "Back to content",
1626
+ style: { color: "var(--foreground)", textDecoration: "underline" }
1627
+ },
1628
+ "\u21A9"
1629
+ )
1301
1630
  )
1302
1631
  );
1303
1632
  return React4.createElement("section", { className: "footnotes" }, React4.createElement("hr", {}), React4.createElement("ol", {}, listItems));
@@ -1472,7 +1801,9 @@ var ComponentRegistry = class {
1472
1801
  case "html": {
1473
1802
  const trusted = block.payload.sanitizedHtml ?? block.payload.raw;
1474
1803
  return {
1475
- __trustedHtml: createTrustedHTML(trusted),
1804
+ // Server render should not depend on DOMPurify (which requires a DOM `window`).
1805
+ // In worker-compiled flows, `sanitizedHtml` is already sanitized at the source.
1806
+ __trustedHtml: typeof window === "undefined" ? String(trusted ?? "") : createTrustedHTML(String(trusted ?? "")),
1476
1807
  elements: this.htmlElements
1477
1808
  };
1478
1809
  }
@@ -1830,6 +2161,7 @@ var BLOCK_LEVEL_TAGS = /* @__PURE__ */ new Set([
1830
2161
  "math"
1831
2162
  ]);
1832
2163
  export {
2164
+ BottomStickScrollArea,
1833
2165
  ComponentRegistry,
1834
2166
  DefaultLinkSafetyModal,
1835
2167
  defaultBlockComponents,
@@ -1,3 +1,4 @@
1
+ import './components/bottom-stick-scroll-area.js';
1
2
  import { InlineNode, TokenLineV1, CompiledMdxModule, InlineHtmlDescriptor, Block, LazyTokenizationPriority, FormatAnticipationConfig, CodeHighlightingMode, CodeHighlightOutputMode, MixedContentSegment } from '@stream-mdx/core';
2
3
  import React from 'react';
3
4
 
@@ -144,6 +145,8 @@ interface RendererConfig {
144
145
  urgentQueueThreshold?: number;
145
146
  batch?: "rAF" | "microtask" | "timeout";
146
147
  historyLimit?: number;
148
+ startupMicrotaskFlushes?: number;
149
+ adaptiveBudgeting?: boolean;
147
150
  };
148
151
  }
149
152
  interface RendererStore {
@@ -245,4 +248,4 @@ declare class ComponentRegistry {
245
248
  declare function mapHtmlToReact(html: string, elements: HtmlElements): React.ReactNode;
246
249
  declare function renderParagraphMixedSegments(segments: MixedContentSegment[], inlineComponents: InlineComponents, inlineHtmlRenderers: InlineHtmlRendererMap): React.ReactNode[];
247
250
 
248
- export { type BlockComponents as B, ComponentRegistry as C, DefaultLinkSafetyModal as D, type HtmlElements as H, type InlineComponents as I, type LinkSafetyModalProps as L, type Renderer as R, type TableElements as T, type RendererConfig as a, type CodeHighlightRangeRequest as b, type InlineHtmlRendererMap as c, type InlineHtmlRenderer as d, defaultInlineComponents as e, defaultBlockComponents as f, renderParagraphMixedSegments as g, mapHtmlToReact as m, renderInlineNodes as r };
251
+ export { type BlockComponents as B, ComponentRegistry as C, DefaultLinkSafetyModal as D, type HtmlElements as H, type InlineHtmlRendererMap as I, type LinkSafetyModalProps as L, type Renderer as R, type TableElements as T, type InlineComponents as a, type InlineHtmlRenderer as b, type RendererConfig as c, defaultInlineComponents as d, defaultBlockComponents as e, renderParagraphMixedSegments as f, type CodeHighlightRangeRequest as g, mapHtmlToReact as m, renderInlineNodes as r };
@@ -1,3 +1,4 @@
1
+ import './components/bottom-stick-scroll-area.cjs';
1
2
  import { InlineNode, TokenLineV1, CompiledMdxModule, InlineHtmlDescriptor, Block, LazyTokenizationPriority, FormatAnticipationConfig, CodeHighlightingMode, CodeHighlightOutputMode, MixedContentSegment } from '@stream-mdx/core';
2
3
  import React from 'react';
3
4
 
@@ -144,6 +145,8 @@ interface RendererConfig {
144
145
  urgentQueueThreshold?: number;
145
146
  batch?: "rAF" | "microtask" | "timeout";
146
147
  historyLimit?: number;
148
+ startupMicrotaskFlushes?: number;
149
+ adaptiveBudgeting?: boolean;
147
150
  };
148
151
  }
149
152
  interface RendererStore {
@@ -245,4 +248,4 @@ declare class ComponentRegistry {
245
248
  declare function mapHtmlToReact(html: string, elements: HtmlElements): React.ReactNode;
246
249
  declare function renderParagraphMixedSegments(segments: MixedContentSegment[], inlineComponents: InlineComponents, inlineHtmlRenderers: InlineHtmlRendererMap): React.ReactNode[];
247
250
 
248
- export { type BlockComponents as B, ComponentRegistry as C, DefaultLinkSafetyModal as D, type HtmlElements as H, type InlineComponents as I, type LinkSafetyModalProps as L, type Renderer as R, type TableElements as T, type RendererConfig as a, type CodeHighlightRangeRequest as b, type InlineHtmlRendererMap as c, type InlineHtmlRenderer as d, defaultInlineComponents as e, defaultBlockComponents as f, renderParagraphMixedSegments as g, mapHtmlToReact as m, renderInlineNodes as r };
251
+ export { type BlockComponents as B, ComponentRegistry as C, DefaultLinkSafetyModal as D, type HtmlElements as H, type InlineHtmlRendererMap as I, type LinkSafetyModalProps as L, type Renderer as R, type TableElements as T, type InlineComponents as a, type InlineHtmlRenderer as b, type RendererConfig as c, defaultInlineComponents as d, defaultBlockComponents as e, renderParagraphMixedSegments as f, type CodeHighlightRangeRequest as g, mapHtmlToReact as m, renderInlineNodes as r };