@stream-mdx/react 0.1.1 → 0.3.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 (45) hide show
  1. package/dist/components/index.cjs +497 -163
  2. package/dist/components/index.d.cts +1 -1
  3. package/dist/components/index.d.ts +1 -1
  4. package/dist/components/index.mjs +496 -163
  5. package/dist/{index-Bt1opGCs.d.cts → index-D7px9jug.d.cts} +27 -2
  6. package/dist/{index-Bt1opGCs.d.ts → index-D7px9jug.d.ts} +27 -2
  7. package/dist/index.cjs +3767 -2043
  8. package/dist/index.d.cts +43 -5
  9. package/dist/index.d.ts +43 -5
  10. package/dist/index.mjs +3991 -2265
  11. package/dist/mdx-client.cjs +60 -18
  12. package/dist/mdx-client.d.cts +11 -0
  13. package/dist/mdx-client.d.ts +11 -0
  14. package/dist/mdx-client.mjs +60 -18
  15. package/dist/mdx-coordinator.cjs +60 -18
  16. package/dist/mdx-coordinator.mjs +60 -18
  17. package/dist/renderer/node-views.cjs +466 -133
  18. package/dist/renderer/node-views.d.cts +1 -1
  19. package/dist/renderer/node-views.d.ts +1 -1
  20. package/dist/renderer/node-views.mjs +409 -68
  21. package/dist/renderer/patch-commit-scheduler.cjs +68 -7
  22. package/dist/renderer/patch-commit-scheduler.d.cts +6 -5
  23. package/dist/renderer/patch-commit-scheduler.d.ts +6 -5
  24. package/dist/renderer/patch-commit-scheduler.mjs +68 -7
  25. package/dist/renderer/store.cjs +481 -56
  26. package/dist/renderer/store.d.cts +2 -1
  27. package/dist/renderer/store.d.ts +2 -1
  28. package/dist/renderer/store.mjs +479 -47
  29. package/dist/renderer/virtualized-code.cjs +8 -2
  30. package/dist/renderer/virtualized-code.d.cts +4 -0
  31. package/dist/renderer/virtualized-code.d.ts +4 -0
  32. package/dist/renderer/virtualized-code.mjs +8 -2
  33. package/dist/renderer.cjs +3199 -2208
  34. package/dist/renderer.d.cts +4 -2
  35. package/dist/renderer.d.ts +4 -2
  36. package/dist/renderer.mjs +2956 -1957
  37. package/dist/streaming-markdown-DSC4L0xR.d.cts +157 -0
  38. package/dist/streaming-markdown-Dp1IDgMT.d.ts +157 -0
  39. package/dist/streaming-markdown.cjs +3950 -2248
  40. package/dist/streaming-markdown.d.cts +6 -95
  41. package/dist/streaming-markdown.d.ts +6 -95
  42. package/dist/streaming-markdown.mjs +3962 -2252
  43. package/dist/utils/inline-html.d.cts +1 -1
  44. package/dist/utils/inline-html.d.ts +1 -1
  45. package/package.json +3 -3
@@ -34,16 +34,74 @@ __export(node_views_exports, {
34
34
  BlockNodeRenderer: () => BlockNodeRenderer
35
35
  });
36
36
  module.exports = __toCommonJS(node_views_exports);
37
- var import_core3 = require("@stream-mdx/core");
38
- var import_react5 = __toESM(require("react"), 1);
37
+ var import_core4 = require("@stream-mdx/core");
38
+ var import_react8 = __toESM(require("react"), 1);
39
39
 
40
40
  // src/components/index.ts
41
41
  var import_core = require("@stream-mdx/core");
42
+
43
+ // src/renderer/use-deferred-render.ts
44
+ var import_react = require("react");
45
+ function useDeferredRender(ref, options) {
46
+ const enabled = options?.enabled ?? true;
47
+ const [shouldRender, setShouldRender] = (0, import_react.useState)(!enabled);
48
+ (0, import_react.useEffect)(() => {
49
+ if (!enabled) {
50
+ setShouldRender(true);
51
+ return;
52
+ }
53
+ const target = ref.current;
54
+ if (!target) {
55
+ return;
56
+ }
57
+ let cancelled = false;
58
+ let idleId = null;
59
+ let timerId = null;
60
+ const scheduleRender = () => {
61
+ if (cancelled) return;
62
+ setShouldRender(true);
63
+ };
64
+ const runWithIdle = () => {
65
+ if (typeof globalThis.requestIdleCallback === "function") {
66
+ idleId = globalThis.requestIdleCallback(scheduleRender, { timeout: options?.idleTimeoutMs ?? 200 });
67
+ } else {
68
+ timerId = window.setTimeout(scheduleRender, options?.idleTimeoutMs ?? 200);
69
+ }
70
+ };
71
+ const observer = new IntersectionObserver(
72
+ (entries) => {
73
+ for (const entry of entries) {
74
+ if (entry.isIntersecting || entry.intersectionRatio > 0) {
75
+ observer.disconnect();
76
+ const debounceMs = options?.debounceMs ?? 80;
77
+ timerId = window.setTimeout(runWithIdle, debounceMs);
78
+ return;
79
+ }
80
+ }
81
+ },
82
+ { rootMargin: options?.rootMargin ?? "200px 0px" }
83
+ );
84
+ observer.observe(target);
85
+ return () => {
86
+ cancelled = true;
87
+ observer.disconnect();
88
+ if (idleId !== null && typeof globalThis.cancelIdleCallback === "function") {
89
+ globalThis.cancelIdleCallback(idleId);
90
+ }
91
+ if (timerId !== null) {
92
+ window.clearTimeout(timerId);
93
+ }
94
+ };
95
+ }, [enabled, options?.debounceMs, options?.idleTimeoutMs, options?.rootMargin, ref]);
96
+ return shouldRender;
97
+ }
98
+
99
+ // src/components/index.ts
42
100
  var import_core2 = require("@stream-mdx/core");
43
- var import_react2 = __toESM(require("react"), 1);
101
+ var import_react3 = __toESM(require("react"), 1);
44
102
 
45
103
  // src/utils/inline-html.ts
46
- var import_react = __toESM(require("react"), 1);
104
+ var import_react2 = __toESM(require("react"), 1);
47
105
  var BACKTICK_CONTENT = /^`([\s\S]+)`$/;
48
106
  var DEFAULT_INLINE_HTML_RENDERERS = {
49
107
  kbd: (descriptor, ctx) => {
@@ -55,7 +113,7 @@ var DEFAULT_INLINE_HTML_RENDERERS = {
55
113
  if (ctx.key !== void 0 && ctx.key !== null) {
56
114
  props.key = ctx.key;
57
115
  }
58
- return import_react.default.createElement("kbd", props, import_react.default.createElement("code", { className: "inline-code" }, codeText));
116
+ return import_react2.default.createElement("kbd", props, import_react2.default.createElement("code", { className: "inline-code" }, codeText));
59
117
  }
60
118
  return ctx.defaultRender();
61
119
  }
@@ -73,9 +131,9 @@ function renderInlineHtmlSegment(raw, sanitized, options = {}) {
73
131
  return null;
74
132
  }
75
133
  if (options.key !== void 0 && options.key !== null) {
76
- return import_react.default.createElement(import_react.default.Fragment, { key: options.key }, descriptor.text);
134
+ return import_react2.default.createElement(import_react2.default.Fragment, { key: options.key }, descriptor.text);
77
135
  }
78
- return import_react.default.createElement(import_react.default.Fragment, {}, descriptor.text);
136
+ return import_react2.default.createElement(import_react2.default.Fragment, {}, descriptor.text);
79
137
  }
80
138
  if (isInlineTag) {
81
139
  const parsedElement = renderSanitizedHtmlTree(descriptor.sanitized, options.key);
@@ -90,15 +148,15 @@ function renderInlineHtmlSegment(raw, sanitized, options = {}) {
90
148
  if (options.key !== void 0 && options.key !== null) {
91
149
  props.key = options.key;
92
150
  }
93
- return import_react.default.createElement("span", props);
151
+ return import_react2.default.createElement("span", props);
94
152
  };
95
153
  const allRenderers = options.renderers ? { ...DEFAULT_INLINE_HTML_RENDERERS, ...options.renderers } : DEFAULT_INLINE_HTML_RENDERERS;
96
154
  const renderer = allRenderers[descriptor.tagName];
97
155
  if (renderer) {
98
156
  const rendered = renderer(descriptor, { key: options.key, defaultRender });
99
157
  if (rendered) {
100
- if (options.key !== void 0 && options.key !== null && import_react.default.isValidElement(rendered) && rendered.key === null) {
101
- return import_react.default.cloneElement(rendered, { key: options.key });
158
+ if (options.key !== void 0 && options.key !== null && import_react2.default.isValidElement(rendered) && rendered.key === null) {
159
+ return import_react2.default.cloneElement(rendered, { key: options.key });
102
160
  }
103
161
  return rendered;
104
162
  }
@@ -210,21 +268,21 @@ function renderSanitizedHtmlTree(html, key) {
210
268
  }
211
269
  if (children.length === 1) {
212
270
  const [child] = children;
213
- if (import_react.default.isValidElement(child)) {
271
+ if (import_react2.default.isValidElement(child)) {
214
272
  if (key !== void 0 && key !== null) {
215
- return import_react.default.cloneElement(child, { key });
273
+ return import_react2.default.cloneElement(child, { key });
216
274
  }
217
275
  return child;
218
276
  }
219
277
  if (typeof child === "string" || typeof child === "number") {
220
278
  if (key !== void 0 && key !== null) {
221
- return import_react.default.createElement(import_react.default.Fragment, { key }, child);
279
+ return import_react2.default.createElement(import_react2.default.Fragment, { key }, child);
222
280
  }
223
- return import_react.default.createElement(import_react.default.Fragment, {}, child);
281
+ return import_react2.default.createElement(import_react2.default.Fragment, {}, child);
224
282
  }
225
- return import_react.default.createElement(import_react.default.Fragment, { key }, child);
283
+ return import_react2.default.createElement(import_react2.default.Fragment, { key }, child);
226
284
  }
227
- return import_react.default.createElement(import_react.default.Fragment, { key }, ...children);
285
+ return import_react2.default.createElement(import_react2.default.Fragment, { key }, ...children);
228
286
  } catch {
229
287
  return null;
230
288
  }
@@ -249,9 +307,9 @@ function convertDomNode(node, key) {
249
307
  }
250
308
  });
251
309
  if (children.length === 0) {
252
- return import_react.default.createElement(tagName, props);
310
+ return import_react2.default.createElement(tagName, props);
253
311
  }
254
- return import_react.default.createElement(tagName, props, ...children);
312
+ return import_react2.default.createElement(tagName, props, ...children);
255
313
  }
256
314
  function getElementAttributes(element) {
257
315
  const attrs = {};
@@ -312,6 +370,29 @@ var INLINE_ELEMENTS = /* @__PURE__ */ new Set([
312
370
  ]);
313
371
 
314
372
  // src/components/index.ts
373
+ var MdxRuntimeBoundary = class extends import_react3.default.Component {
374
+ constructor() {
375
+ super(...arguments);
376
+ this.state = { hasError: false };
377
+ }
378
+ static getDerivedStateFromError() {
379
+ return { hasError: true };
380
+ }
381
+ componentDidCatch(error) {
382
+ this.props.onError(error);
383
+ }
384
+ componentDidUpdate(prevProps) {
385
+ if (prevProps.resetKey !== this.props.resetKey && this.state.hasError) {
386
+ this.setState({ hasError: false });
387
+ }
388
+ }
389
+ render() {
390
+ if (this.state.hasError) {
391
+ return null;
392
+ }
393
+ return this.props.children;
394
+ }
395
+ };
315
396
  function renderInlineNodes(nodes, components) {
316
397
  return nodes.map((node, index) => {
317
398
  const key = `${node.kind}-${index}`;
@@ -319,31 +400,31 @@ function renderInlineNodes(nodes, components) {
319
400
  case "text": {
320
401
  const textComponent = components.text;
321
402
  if (textComponent && textComponent !== defaultInlineComponents.text) {
322
- return import_react2.default.createElement(textComponent, { key, text: node.text });
403
+ return import_react3.default.createElement(textComponent, { key, text: node.text });
323
404
  }
324
- return import_react2.default.createElement(import_react2.default.Fragment, { key, children: node.text });
405
+ return import_react3.default.createElement(import_react3.default.Fragment, { key, children: node.text });
325
406
  }
326
407
  case "strong":
327
- return import_react2.default.createElement(components.strong, {
408
+ return import_react3.default.createElement(components.strong, {
328
409
  key,
329
410
  children: renderInlineNodes(node.children, components)
330
411
  });
331
412
  case "em":
332
- return import_react2.default.createElement(components.em, {
413
+ return import_react3.default.createElement(components.em, {
333
414
  key,
334
415
  children: renderInlineNodes(node.children, components)
335
416
  });
336
417
  case "strike": {
337
- const StrikeComponent = components.strike ?? ((props) => import_react2.default.createElement("del", {}, props.children));
338
- return import_react2.default.createElement(StrikeComponent, {
418
+ const StrikeComponent = components.strike ?? ((props) => import_react3.default.createElement("del", {}, props.children));
419
+ return import_react3.default.createElement(StrikeComponent, {
339
420
  key,
340
421
  children: renderInlineNodes(node.children, components)
341
422
  });
342
423
  }
343
424
  case "code":
344
- return import_react2.default.createElement(components.code, { key, text: node.text });
425
+ return import_react3.default.createElement(components.code, { key, text: node.text });
345
426
  case "link":
346
- return import_react2.default.createElement(
427
+ return import_react3.default.createElement(
347
428
  components.link,
348
429
  {
349
430
  key,
@@ -353,18 +434,18 @@ function renderInlineNodes(nodes, components) {
353
434
  }
354
435
  );
355
436
  case "image":
356
- return import_react2.default.createElement(components.image, {
437
+ return import_react3.default.createElement(components.image, {
357
438
  key,
358
439
  src: node.src,
359
440
  alt: node.alt,
360
441
  title: node.title
361
442
  });
362
443
  case "br":
363
- return import_react2.default.createElement(import_react2.default.Fragment, { key }, import_react2.default.createElement(components.br, {}));
444
+ return import_react3.default.createElement(import_react3.default.Fragment, { key }, import_react3.default.createElement(components.br, {}));
364
445
  default: {
365
446
  const component = components[node.kind];
366
447
  if (component) {
367
- return import_react2.default.createElement(component, { key, ...node });
448
+ return import_react3.default.createElement(component, { key, ...node });
368
449
  }
369
450
  return null;
370
451
  }
@@ -372,18 +453,18 @@ function renderInlineNodes(nodes, components) {
372
453
  });
373
454
  }
374
455
  var defaultInlineComponents = {
375
- text: ({ text }) => import_react2.default.createElement(import_react2.default.Fragment, {}, text),
376
- strong: ({ children }) => import_react2.default.createElement("strong", {}, children),
377
- em: ({ children }) => import_react2.default.createElement("em", {}, children),
378
- strike: ({ children }) => import_react2.default.createElement("del", {}, children),
379
- code: ({ text }) => import_react2.default.createElement(
456
+ text: ({ text }) => import_react3.default.createElement(import_react3.default.Fragment, {}, text),
457
+ strong: ({ children }) => import_react3.default.createElement("strong", {}, children),
458
+ em: ({ children }) => import_react3.default.createElement("em", {}, children),
459
+ strike: ({ children }) => import_react3.default.createElement("del", {}, children),
460
+ code: ({ text }) => import_react3.default.createElement(
380
461
  "code",
381
462
  {
382
463
  className: "inline-code"
383
464
  },
384
465
  text
385
466
  ),
386
- link: ({ href, title, children }) => import_react2.default.createElement(
467
+ link: ({ href, title, children }) => import_react3.default.createElement(
387
468
  "a",
388
469
  {
389
470
  href,
@@ -394,15 +475,15 @@ var defaultInlineComponents = {
394
475
  },
395
476
  children
396
477
  ),
397
- image: ({ src, alt, title }) => import_react2.default.createElement("img", {
478
+ image: ({ src, alt, title }) => import_react3.default.createElement("img", {
398
479
  src,
399
480
  alt,
400
481
  title,
401
482
  className: "markdown-image"
402
483
  }),
403
- br: () => import_react2.default.createElement("br"),
484
+ br: () => import_react3.default.createElement("br"),
404
485
  // Custom extensible components
405
- mention: ({ handle }) => import_react2.default.createElement(
486
+ mention: ({ handle }) => import_react3.default.createElement(
406
487
  "span",
407
488
  {
408
489
  className: "mention",
@@ -410,7 +491,7 @@ var defaultInlineComponents = {
410
491
  },
411
492
  `@${handle}`
412
493
  ),
413
- citation: ({ id }) => import_react2.default.createElement(
494
+ citation: ({ id }) => import_react3.default.createElement(
414
495
  "span",
415
496
  {
416
497
  className: "citation",
@@ -418,7 +499,7 @@ var defaultInlineComponents = {
418
499
  },
419
500
  `[${id}]`
420
501
  ),
421
- "math-inline": ({ tex }) => import_react2.default.createElement(
502
+ "math-inline": ({ tex }) => import_react3.default.createElement(
422
503
  "span",
423
504
  {
424
505
  className: "math-inline",
@@ -426,21 +507,21 @@ var defaultInlineComponents = {
426
507
  },
427
508
  `$${tex}$`
428
509
  ),
429
- "math-display": ({ tex }) => import_react2.default.createElement(
510
+ "math-display": ({ tex }) => import_react3.default.createElement(
430
511
  "div",
431
512
  {
432
513
  className: "math-display markdown-math-display",
433
514
  "data-tex": tex,
434
515
  style: { overflowX: "auto" }
435
516
  },
436
- import_react2.default.createElement("span", { className: "math-display-content" }, `$$${tex}$$`)
517
+ import_react3.default.createElement("span", { className: "math-display-content" }, `$$${tex}$$`)
437
518
  ),
438
519
  // Footnote inline reference (superscript anchor)
439
520
  "footnote-ref": ({ label, number }) => {
440
521
  const n = number ?? "?";
441
522
  const href = number ? `#fn:${n}` : void 0;
442
523
  const id = number ? `fnref:${n}` : void 0;
443
- return import_react2.default.createElement("sup", { className: "footnote-ref" }, import_react2.default.createElement("a", { href, id, "data-label": label }, String(n)));
524
+ return import_react3.default.createElement("sup", { className: "footnote-ref" }, import_react3.default.createElement("a", { href, id, "data-label": label }, String(n)));
444
525
  }
445
526
  };
446
527
  function renderParagraphMixedSegments(segments, inlineComponents, inlineHtmlRenderers) {
@@ -451,12 +532,12 @@ function renderParagraphMixedSegments(segments, inlineComponents, inlineHtmlRend
451
532
  nodes.forEach((node, idx) => {
452
533
  if (node === null || node === void 0) return;
453
534
  const key = `${baseKey}-${idx}`;
454
- if (import_react2.default.isValidElement(node)) {
455
- target.push(node.key === key ? node : import_react2.default.cloneElement(node, { key }));
535
+ if (import_react3.default.isValidElement(node)) {
536
+ target.push(node.key === key ? node : import_react3.default.cloneElement(node, { key }));
456
537
  } else if (typeof node === "string" || typeof node === "number") {
457
- target.push(import_react2.default.createElement(import_react2.default.Fragment, { key }, node));
538
+ target.push(import_react3.default.createElement(import_react3.default.Fragment, { key }, node));
458
539
  } else {
459
- target.push(import_react2.default.createElement(import_react2.default.Fragment, { key }, node));
540
+ target.push(import_react3.default.createElement(import_react3.default.Fragment, { key }, node));
460
541
  }
461
542
  });
462
543
  };
@@ -472,7 +553,7 @@ function renderParagraphMixedSegments(segments, inlineComponents, inlineHtmlRend
472
553
  }
473
554
  const key = `paragraph-inline-group-${inlineGroupIndex++}`;
474
555
  result.push(
475
- import_react2.default.createElement(
556
+ import_react3.default.createElement(
476
557
  "p",
477
558
  {
478
559
  key,
@@ -490,7 +571,7 @@ function renderParagraphMixedSegments(segments, inlineComponents, inlineHtmlRend
490
571
  const inlineNodes = Array.isArray(segment.inline) ? segment.inline : [];
491
572
  if (inlineNodes.length === 0) {
492
573
  if (segment.value) {
493
- inlineBuffer.push(import_react2.default.createElement(import_react2.default.Fragment, { key }, segment.value));
574
+ inlineBuffer.push(import_react3.default.createElement(import_react3.default.Fragment, { key }, segment.value));
494
575
  }
495
576
  break;
496
577
  }
@@ -518,14 +599,14 @@ function renderParagraphMixedSegments(segments, inlineComponents, inlineHtmlRend
518
599
  const status = segment.status ?? "pending";
519
600
  let element;
520
601
  if (status === "compiled") {
521
- element = import_react2.default.createElement("span", { className: "markdown-mdx-inline", "data-mdx-status": "compiled" });
602
+ element = import_react3.default.createElement("span", { className: "markdown-mdx-inline", "data-mdx-status": "compiled" });
522
603
  } else if (status === "error") {
523
604
  const message = segment.value || "MDX error";
524
- element = import_react2.default.createElement("span", { className: "markdown-mdx-inline text-destructive", "data-mdx-status": "error" }, message);
605
+ element = import_react3.default.createElement("span", { className: "markdown-mdx-inline text-destructive", "data-mdx-status": "error" }, message);
525
606
  } else {
526
- element = import_react2.default.createElement("span", { className: "markdown-mdx-inline", "data-mdx-status": status }, segment.value);
607
+ element = import_react3.default.createElement("span", { className: "markdown-mdx-inline", "data-mdx-status": status }, segment.value);
527
608
  }
528
- inlineBuffer.push(import_react2.default.createElement(import_react2.default.Fragment, { key }, element));
609
+ inlineBuffer.push(import_react3.default.createElement(import_react3.default.Fragment, { key }, element));
529
610
  break;
530
611
  }
531
612
  case "html": {
@@ -541,7 +622,7 @@ function renderParagraphMixedSegments(segments, inlineComponents, inlineHtmlRend
541
622
  result.push(element);
542
623
  } else if (html) {
543
624
  result.push(
544
- import_react2.default.createElement("div", {
625
+ import_react3.default.createElement("div", {
545
626
  key,
546
627
  className: "markdown-inline-html-block",
547
628
  dangerouslySetInnerHTML: { __html: html }
@@ -560,7 +641,7 @@ function renderParagraphMixedSegments(segments, inlineComponents, inlineHtmlRend
560
641
  flushInline();
561
642
  if (result.length === 0) {
562
643
  return [
563
- import_react2.default.createElement(
644
+ import_react3.default.createElement(
564
645
  "p",
565
646
  {
566
647
  key: "paragraph-inline-group-0",
@@ -619,24 +700,80 @@ var BLOCK_LEVEL_TAGS = /* @__PURE__ */ new Set([
619
700
  ]);
620
701
 
621
702
  // src/renderer/hooks.ts
622
- var import_react3 = require("react");
703
+ var import_react4 = require("react");
623
704
  function useRendererNode(store, id) {
624
- const selector = (0, import_react3.useMemo)(() => {
705
+ const selector = (0, import_react4.useMemo)(() => {
625
706
  return () => store.getNodeWithVersion(id);
626
707
  }, [store, id]);
627
- const snapshot = (0, import_react3.useSyncExternalStore)(store.subscribe, selector, selector);
708
+ const snapshot = (0, import_react4.useSyncExternalStore)(store.subscribe, selector, selector);
628
709
  return snapshot.node;
629
710
  }
630
711
  function useRendererChildren(store, id) {
631
- const selector = (0, import_react3.useMemo)(() => {
712
+ const selector = (0, import_react4.useMemo)(() => {
632
713
  return () => store.getChildrenWithVersion(id);
633
714
  }, [store, id]);
634
- const snapshot = (0, import_react3.useSyncExternalStore)(store.subscribe, selector, selector);
715
+ const snapshot = (0, import_react4.useSyncExternalStore)(store.subscribe, selector, selector);
635
716
  return snapshot.children;
636
717
  }
637
718
 
719
+ // src/renderer/code-highlight-context.tsx
720
+ var import_react5 = __toESM(require("react"), 1);
721
+ var CodeHighlightRequestContext = import_react5.default.createContext(null);
722
+ function useCodeHighlightRequester() {
723
+ return import_react5.default.useContext(CodeHighlightRequestContext);
724
+ }
725
+
726
+ // src/renderer/deferred-render-context.tsx
727
+ var import_react6 = __toESM(require("react"), 1);
728
+ var DeferredRenderContext = import_react6.default.createContext(null);
729
+
730
+ // src/renderer/store.ts
731
+ var import_core3 = require("@stream-mdx/core");
732
+ var NODE_SNAPSHOT_CACHE_LIMIT = 5e4;
733
+ var CHILDREN_SNAPSHOT_CACHE_LIMIT = 5e4;
734
+ var NODE_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(NODE_SNAPSHOT_CACHE_LIMIT * 0.1));
735
+ var CHILDREN_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(CHILDREN_SNAPSHOT_CACHE_LIMIT * 0.1));
736
+ var EMPTY_CHILDREN = Object.freeze([]);
737
+ var EMPTY_NODE_SNAPSHOT = Object.freeze({ version: -1, node: void 0 });
738
+ var EMPTY_CHILDREN_SNAPSHOT = Object.freeze({
739
+ version: -1,
740
+ children: EMPTY_CHILDREN
741
+ });
742
+ function isListPatchDebugEnabled() {
743
+ if (typeof globalThis !== "undefined") {
744
+ const flag = globalThis.__STREAMING_LIST_DEBUG__;
745
+ if (flag === true) return true;
746
+ }
747
+ if (typeof process !== "undefined" && process.env.NEXT_PUBLIC_STREAMING_LIST_DEBUG === "true") {
748
+ return true;
749
+ }
750
+ return false;
751
+ }
752
+ var CODE_BLOCK_DEBUG_ENABLED = (() => {
753
+ try {
754
+ if (typeof process !== "undefined" && process.env && process.env.NEXT_PUBLIC_STREAMING_DEBUG_CODELINES === "1") {
755
+ return true;
756
+ }
757
+ if (typeof globalThis !== "undefined") {
758
+ const debug = globalThis?.__STREAMING_DEBUG__;
759
+ if (debug?.codeLines) {
760
+ return true;
761
+ }
762
+ }
763
+ } catch {
764
+ }
765
+ return false;
766
+ })() || false;
767
+ var CODE_BLOCK_VALIDATION_ENABLED = typeof process !== "undefined" && process.env ? process.env.NODE_ENV !== "production" : true;
768
+ var getPerfNow = (() => {
769
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
770
+ return () => performance.now();
771
+ }
772
+ return () => Date.now();
773
+ })();
774
+
638
775
  // src/renderer/virtualized-code.tsx
639
- var import_react4 = require("react");
776
+ var import_react7 = require("react");
640
777
  var DEFAULT_VIRTUALIZED_CODE_CONFIG = {
641
778
  enabled: true,
642
779
  windowSize: 100,
@@ -652,6 +789,8 @@ function calculateCodeWindow(lines, scrollTop, containerHeight, lineHeight, conf
652
789
  return {
653
790
  startIndex: 0,
654
791
  endIndex: totalLines,
792
+ visibleStart: 0,
793
+ visibleEnd: totalLines,
655
794
  visibleLines: lines,
656
795
  totalLines,
657
796
  mountedLines: totalLines
@@ -659,22 +798,26 @@ function calculateCodeWindow(lines, scrollTop, containerHeight, lineHeight, conf
659
798
  }
660
799
  const firstVisibleLine = Math.floor(scrollTop / lineHeight);
661
800
  const lastVisibleLine = Math.ceil((scrollTop + containerHeight) / lineHeight);
662
- const startIndex = Math.max(0, firstVisibleLine - config.bufferSize);
663
- const endIndex = Math.min(totalLines, lastVisibleLine + config.bufferSize);
801
+ const visibleStart = Math.max(0, firstVisibleLine);
802
+ const visibleEnd = Math.min(totalLines, Math.max(visibleStart, lastVisibleLine));
803
+ const startIndex = Math.max(0, visibleStart - config.bufferSize);
804
+ const endIndex = Math.min(totalLines, visibleEnd + config.bufferSize);
664
805
  return {
665
806
  startIndex,
666
807
  endIndex,
808
+ visibleStart,
809
+ visibleEnd,
667
810
  visibleLines: lines.slice(startIndex, endIndex),
668
811
  totalLines,
669
812
  mountedLines: endIndex - startIndex
670
813
  };
671
814
  }
672
815
  function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
673
- const containerRef = (0, import_react4.useRef)(null);
674
- const [scrollTop, setScrollTop] = (0, import_react4.useState)(0);
675
- const [containerHeight, setContainerHeight] = (0, import_react4.useState)(0);
676
- const lineHeightRef = (0, import_react4.useRef)(20);
677
- (0, import_react4.useEffect)(() => {
816
+ const containerRef = (0, import_react7.useRef)(null);
817
+ const [scrollTop, setScrollTop] = (0, import_react7.useState)(0);
818
+ const [containerHeight, setContainerHeight] = (0, import_react7.useState)(0);
819
+ const lineHeightRef = (0, import_react7.useRef)(20);
820
+ (0, import_react7.useEffect)(() => {
678
821
  if (!containerRef.current || lines.length === 0) return;
679
822
  const timeoutId = setTimeout(() => {
680
823
  if (!containerRef.current) return;
@@ -688,12 +831,12 @@ function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
688
831
  }, 0);
689
832
  return () => clearTimeout(timeoutId);
690
833
  }, [lines.length]);
691
- const handleScroll = (0, import_react4.useCallback)((e) => {
834
+ const handleScroll = (0, import_react7.useCallback)((e) => {
692
835
  const target = e.currentTarget;
693
836
  setScrollTop(target.scrollTop);
694
837
  setContainerHeight(target.clientHeight);
695
838
  }, []);
696
- (0, import_react4.useEffect)(() => {
839
+ (0, import_react7.useEffect)(() => {
697
840
  if (!containerRef.current) return;
698
841
  const resizeObserver = new ResizeObserver((entries) => {
699
842
  for (const entry of entries) {
@@ -703,10 +846,10 @@ function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
703
846
  resizeObserver.observe(containerRef.current);
704
847
  return () => resizeObserver.disconnect();
705
848
  }, []);
706
- const window2 = (0, import_react4.useMemo)(() => {
849
+ const window2 = (0, import_react7.useMemo)(() => {
707
850
  return calculateCodeWindow(lines, scrollTop, containerHeight, lineHeightRef.current, config);
708
851
  }, [lines, scrollTop, containerHeight, config]);
709
- const scrollToLine = (0, import_react4.useCallback)((lineIndex) => {
852
+ const scrollToLine = (0, import_react7.useCallback)((lineIndex) => {
710
853
  if (!containerRef.current) return;
711
854
  const targetScrollTop = lineIndex * lineHeightRef.current;
712
855
  containerRef.current.scrollTop = targetScrollTop;
@@ -747,7 +890,7 @@ var BlockNodeRenderer = ({ store, blockId, registry }) => {
747
890
  return registry.renderBlock(block);
748
891
  }
749
892
  };
750
- var ParagraphBlockView = import_react5.default.memo(({ store, blockId, registry }) => {
893
+ var ParagraphBlockView = import_react8.default.memo(({ store, blockId, registry }) => {
751
894
  const node = useRendererNode(store, blockId);
752
895
  const block = node?.block;
753
896
  const childIds = useRendererChildren(store, blockId);
@@ -767,7 +910,7 @@ var ParagraphBlockView = import_react5.default.memo(({ store, blockId, registry
767
910
  const structured = renderParagraphMixedSegments(segments2, inlineComponents, DEFAULT_INLINE_HTML_RENDERERS);
768
911
  if (structured.length === 1) {
769
912
  const [single] = structured;
770
- return import_react5.default.isValidElement(single) ? single : single;
913
+ return import_react8.default.isValidElement(single) ? single : single;
771
914
  }
772
915
  return structured;
773
916
  }
@@ -785,7 +928,7 @@ var ParagraphBlockView = import_react5.default.memo(({ store, blockId, registry
785
928
  const structured = renderParagraphMixedSegments(derivedSegments, inlineComponents, DEFAULT_INLINE_HTML_RENDERERS);
786
929
  if (structured.length === 1) {
787
930
  const [single] = structured;
788
- return import_react5.default.isValidElement(single) ? single : single;
931
+ return import_react8.default.isValidElement(single) ? single : single;
789
932
  }
790
933
  return structured;
791
934
  }
@@ -804,7 +947,7 @@ var ParagraphBlockView = import_react5.default.memo(({ store, blockId, registry
804
947
  const structured = renderParagraphMixedSegments(segments, inlineComponents, DEFAULT_INLINE_HTML_RENDERERS);
805
948
  if (structured.length === 1) {
806
949
  const [single] = structured;
807
- return import_react5.default.isValidElement(single) ? single : single;
950
+ return import_react8.default.isValidElement(single) ? single : single;
808
951
  }
809
952
  return structured;
810
953
  }
@@ -821,31 +964,31 @@ var ParagraphBlockView = import_react5.default.memo(({ store, blockId, registry
821
964
  const structured = renderParagraphMixedSegments(derivedSegments, inlineComponents, DEFAULT_INLINE_HTML_RENDERERS);
822
965
  if (structured.length === 1) {
823
966
  const [single] = structured;
824
- return import_react5.default.isValidElement(single) ? single : single;
967
+ return import_react8.default.isValidElement(single) ? single : single;
825
968
  }
826
969
  return structured;
827
970
  }
828
971
  const ParagraphComponent = registry.getBlockComponent("paragraph");
829
- const baseElement = import_react5.default.createElement(ParagraphComponent, {
972
+ const baseElement = import_react8.default.createElement(ParagraphComponent, {
830
973
  inlines: block.payload.inline ?? [],
831
974
  raw: block.payload.raw,
832
975
  meta: block.payload.meta
833
976
  });
834
977
  if (childIds.length > 0) {
835
978
  const renderedChildren = childIds.map((childId) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MixedSegmentView, { store, nodeId: childId, inlineComponents }, childId));
836
- if (import_react5.default.isValidElement(baseElement)) {
979
+ if (import_react8.default.isValidElement(baseElement)) {
837
980
  const { children: _ignored, className = "markdown-paragraph", inlines: _inlines, raw: _raw, meta: _meta, ...rest } = baseElement.props ?? {};
838
- return import_react5.default.createElement("p", { ...rest, className }, renderedChildren);
981
+ return import_react8.default.createElement("p", { ...rest, className }, renderedChildren);
839
982
  }
840
983
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "markdown-paragraph", children: renderedChildren });
841
984
  }
842
- if (import_react5.default.isValidElement(baseElement)) {
985
+ if (import_react8.default.isValidElement(baseElement)) {
843
986
  return baseElement;
844
987
  }
845
988
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "markdown-paragraph", children: renderInlineNodes(block.payload.inline ?? [], inlineComponents) });
846
989
  });
847
990
  ParagraphBlockView.displayName = "ParagraphBlockView";
848
- var BlockquoteBlockView = import_react5.default.memo(({ store, blockId, registry }) => {
991
+ var BlockquoteBlockView = import_react8.default.memo(({ store, blockId, registry }) => {
849
992
  const node = useRendererNode(store, blockId);
850
993
  const block = node?.block;
851
994
  const childIds = useRendererChildren(store, blockId);
@@ -891,7 +1034,7 @@ var BlockquoteBlockView = import_react5.default.memo(({ store, blockId, registry
891
1034
  elements.push(htmlNode);
892
1035
  } else {
893
1036
  elements.push(
894
- import_react5.default.createElement("span", {
1037
+ import_react8.default.createElement("span", {
895
1038
  key: `blockquote-html-${block.id}-${segmentIndex}`,
896
1039
  className: "markdown-inline-html",
897
1040
  /* biome-ignore lint/security/noDangerouslySetInnerHtml: segment content is sanitized by the worker before streaming */
@@ -909,10 +1052,10 @@ var BlockquoteBlockView = import_react5.default.memo(({ store, blockId, registry
909
1052
  default: {
910
1053
  const inline = renderInlineContent(segment.inline, `blockquote-text-inline-${block.id}-${segmentIndex}-`);
911
1054
  if (inline) {
912
- elements.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react5.default.Fragment, { children: inline }, `blockquote-inline-${block.id}-${segmentIndex}`));
1055
+ elements.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react8.default.Fragment, { children: inline }, `blockquote-inline-${block.id}-${segmentIndex}`));
913
1056
  } else {
914
1057
  elements.push(
915
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react5.default.Fragment, { children: renderTextWithBreaks(segment.value, `blockquote-text-${block.id}-${segmentIndex}-`) }, `blockquote-text-${block.id}-${segmentIndex}`)
1058
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react8.default.Fragment, { children: renderTextWithBreaks(segment.value, `blockquote-text-${block.id}-${segmentIndex}-`) }, `blockquote-text-${block.id}-${segmentIndex}`)
916
1059
  );
917
1060
  }
918
1061
  break;
@@ -951,7 +1094,7 @@ var BlockquoteBlockView = import_react5.default.memo(({ store, blockId, registry
951
1094
  return null;
952
1095
  })();
953
1096
  const BlockquoteComponent = registry.getBlockComponent("blockquote");
954
- const baseElement = import_react5.default.createElement(
1097
+ const baseElement = import_react8.default.createElement(
955
1098
  BlockquoteComponent,
956
1099
  {
957
1100
  inlines: block.payload.inline ?? [],
@@ -959,13 +1102,13 @@ var BlockquoteBlockView = import_react5.default.memo(({ store, blockId, registry
959
1102
  },
960
1103
  renderedContent
961
1104
  );
962
- if (import_react5.default.isValidElement(baseElement)) {
1105
+ if (import_react8.default.isValidElement(baseElement)) {
963
1106
  return baseElement;
964
1107
  }
965
1108
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("blockquote", { className: "markdown-blockquote", children: renderedContent });
966
1109
  });
967
1110
  BlockquoteBlockView.displayName = "BlockquoteBlockView";
968
- var MixedSegmentView = import_react5.default.memo(({ store, nodeId, inlineComponents }) => {
1111
+ var MixedSegmentView = import_react8.default.memo(({ store, nodeId, inlineComponents }) => {
969
1112
  const node = useRendererNode(store, nodeId);
970
1113
  if (!node) return null;
971
1114
  switch (node.type) {
@@ -993,7 +1136,7 @@ var MixedSegmentView = import_react5.default.memo(({ store, nodeId, inlineCompon
993
1136
  return rendered;
994
1137
  }
995
1138
  if (!html) return null;
996
- return import_react5.default.createElement("span", {
1139
+ return import_react8.default.createElement("span", {
997
1140
  className: "markdown-inline-html",
998
1141
  /* biome-ignore lint/security/noDangerouslySetInnerHtml: html is sanitized upstream before reaching the renderer */
999
1142
  dangerouslySetInnerHTML: { __html: html }
@@ -1018,19 +1161,19 @@ var MixedSegmentView = import_react5.default.memo(({ store, nodeId, inlineCompon
1018
1161
  }
1019
1162
  });
1020
1163
  MixedSegmentView.displayName = "MixedSegmentView";
1021
- var HtmlBlockView = import_react5.default.memo(({ store, blockId, registry }) => {
1164
+ var HtmlBlockView = import_react8.default.memo(({ store, blockId, registry }) => {
1022
1165
  const node = useRendererNode(store, blockId);
1023
1166
  if (!node || !node.block) return null;
1024
1167
  return registry.renderBlock(node.block);
1025
1168
  });
1026
1169
  HtmlBlockView.displayName = "HtmlBlockView";
1027
- var MdxBlockView = import_react5.default.memo(({ store, blockId, registry }) => {
1170
+ var MdxBlockView = import_react8.default.memo(({ store, blockId, registry }) => {
1028
1171
  const node = useRendererNode(store, blockId);
1029
1172
  if (!node || !node.block) return null;
1030
1173
  return registry.renderBlock(node.block);
1031
1174
  });
1032
1175
  MdxBlockView.displayName = "MdxBlockView";
1033
- var ListBlockView = import_react5.default.memo(
1176
+ var ListBlockView = import_react8.default.memo(
1034
1177
  ({ store, blockId, registry, depth = 0 }) => {
1035
1178
  const node = useRendererNode(store, blockId);
1036
1179
  const block = node?.block;
@@ -1038,10 +1181,36 @@ var ListBlockView = import_react5.default.memo(
1038
1181
  const inlineComponents = registry.getInlineComponents();
1039
1182
  const ordered = Boolean(node?.props?.ordered ?? block?.payload.meta?.ordered);
1040
1183
  const Tag = ordered ? "ol" : "ul";
1041
- const listStyle = import_react5.default.useMemo(() => {
1042
- if (!ordered || childIds.length === 0) return void 0;
1184
+ const listDebugEnabled = isListPatchDebugEnabled();
1185
+ const listItemIds = import_react8.default.useMemo(() => {
1186
+ const ids = [];
1187
+ for (const childId of childIds) {
1188
+ const childNode = store.getNode(childId);
1189
+ if (childNode?.type === "list-item") {
1190
+ ids.push(childId);
1191
+ }
1192
+ }
1193
+ return ids;
1194
+ }, [childIds, store]);
1195
+ import_react8.default.useEffect(() => {
1196
+ if (!listDebugEnabled) return;
1197
+ const childTypes = childIds.map((childId) => store.getNode(childId)?.type ?? "missing");
1198
+ console.info("[stream-mdx:list-render]", {
1199
+ listId: blockId,
1200
+ depth,
1201
+ ordered,
1202
+ childIds,
1203
+ childTypes,
1204
+ listItemIds,
1205
+ listItems: listItemIds.length,
1206
+ totalChildren: childIds.length,
1207
+ isFinalized: block?.isFinalized ?? false
1208
+ });
1209
+ }, [listDebugEnabled, blockId, depth, ordered, childIds, listItemIds, store, block?.isFinalized]);
1210
+ const listStyle = import_react8.default.useMemo(() => {
1211
+ if (!ordered || listItemIds.length === 0) return void 0;
1043
1212
  let maxDigits = 1;
1044
- childIds.forEach((childId, index) => {
1213
+ listItemIds.forEach((childId, index) => {
1045
1214
  const childNode = store.getNode(childId);
1046
1215
  const raw = typeof childNode?.block?.payload?.raw === "string" ? childNode.block.payload.raw : void 0;
1047
1216
  const markerMatch = raw ? raw.match(/^([^\s]+)\s+/) : null;
@@ -1055,24 +1224,34 @@ var ListBlockView = import_react5.default.memo(
1055
1224
  return {
1056
1225
  ["--list-indent"]: `calc(${baseIndent} + ${maxDigits}ch)`
1057
1226
  };
1058
- }, [ordered, childIds, store, depth]);
1059
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { className: `markdown-list ${ordered ? "ordered" : "unordered"}`, "data-list-depth": depth, style: listStyle, children: childIds.map((childId, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1060
- ListItemView,
1227
+ }, [ordered, listItemIds, store, depth]);
1228
+ if (listItemIds.length === 0) return null;
1229
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1230
+ Tag,
1061
1231
  {
1062
- store,
1063
- nodeId: childId,
1064
- inlineComponents,
1065
- registry,
1066
- ordered,
1067
- index,
1068
- depth
1069
- },
1070
- childId
1071
- )) });
1232
+ className: `markdown-list ${ordered ? "ordered" : "unordered"}`,
1233
+ "data-list-depth": depth,
1234
+ "data-list-id": listDebugEnabled ? blockId : void 0,
1235
+ style: listStyle,
1236
+ children: listItemIds.map((childId, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1237
+ ListItemView,
1238
+ {
1239
+ store,
1240
+ nodeId: childId,
1241
+ inlineComponents,
1242
+ registry,
1243
+ ordered,
1244
+ index,
1245
+ depth
1246
+ },
1247
+ childId
1248
+ ))
1249
+ }
1250
+ );
1072
1251
  }
1073
1252
  );
1074
1253
  ListBlockView.displayName = "ListBlockView";
1075
- var ListItemView = import_react5.default.memo(({ store, nodeId, inlineComponents, registry, ordered, index, depth }) => {
1254
+ var ListItemView = import_react8.default.memo(({ store, nodeId, inlineComponents, registry, ordered, index, depth }) => {
1076
1255
  const node = useRendererNode(store, nodeId);
1077
1256
  const childIds = useRendererChildren(store, nodeId);
1078
1257
  const inline = node?.props?.inline ?? [];
@@ -1085,7 +1264,9 @@ var ListItemView = import_react5.default.memo(({ store, nodeId, inlineComponents
1085
1264
  const inferredIndex = typeof node?.props?.index === "number" ? Number(node?.props?.index) : index;
1086
1265
  const isOrdered = Boolean(node?.props?.ordered ?? ordered);
1087
1266
  const counterText = !isTask ? isOrdered ? marker ? marker : `${inferredIndex + 1}.` : "\u2022" : void 0;
1088
- const [segmentChildIds, contentChildIds] = import_react5.default.useMemo(() => {
1267
+ const listDebugEnabled = isListPatchDebugEnabled();
1268
+ const inlineLength = inline.length;
1269
+ const [segmentChildIds, contentChildIds] = import_react8.default.useMemo(() => {
1089
1270
  const segments = [];
1090
1271
  const structural = [];
1091
1272
  for (const childId of childIds) {
@@ -1099,16 +1280,16 @@ var ListItemView = import_react5.default.memo(({ store, nodeId, inlineComponents
1099
1280
  }
1100
1281
  return [segments, structural];
1101
1282
  }, [childIds, store]);
1102
- const segmentElements = import_react5.default.useMemo(() => {
1283
+ const segmentElements = import_react8.default.useMemo(() => {
1103
1284
  return segmentChildIds.map((childId) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MixedSegmentView, { store, nodeId: childId, inlineComponents }, childId));
1104
1285
  }, [segmentChildIds, store, inlineComponents]);
1105
- const primaryContent = import_react5.default.useMemo(() => {
1286
+ const primaryContent = import_react8.default.useMemo(() => {
1106
1287
  if (segmentChildIds.length > 0) {
1107
1288
  return segmentElements;
1108
1289
  }
1109
1290
  return renderInlineNodes(inline, inlineComponents);
1110
1291
  }, [segmentChildIds, segmentElements, inline, inlineComponents]);
1111
- const trailingChildren = import_react5.default.useMemo(() => {
1292
+ const trailingChildren = import_react8.default.useMemo(() => {
1112
1293
  return contentChildIds.map((childId) => {
1113
1294
  const childNode = store.getNode(childId);
1114
1295
  if (!childNode) return null;
@@ -1126,22 +1307,67 @@ var ListItemView = import_react5.default.memo(({ store, nodeId, inlineComponents
1126
1307
  }, [contentChildIds, store, inlineComponents, registry, depth]);
1127
1308
  const filteredChildren = trailingChildren.filter((child) => Boolean(child));
1128
1309
  const hasChildren = filteredChildren.length > 0;
1129
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: `markdown-list-item${isTask ? " markdown-list-item-task" : ""}`, "data-counter-text": counterText, "data-list-depth": depth, children: [
1130
- isTask ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "markdown-task", children: [
1131
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", className: "markdown-task-checkbox", checked: isChecked, readOnly: true, disabled: true, tabIndex: -1, "aria-checked": isChecked }),
1132
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "markdown-task-content", children: primaryContent })
1133
- ] }) : primaryContent,
1134
- hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "markdown-list-item-children", children: filteredChildren }) : null
1135
- ] });
1310
+ import_react8.default.useEffect(() => {
1311
+ if (!listDebugEnabled) return;
1312
+ const childTypes = childIds.map((childId) => store.getNode(childId)?.type ?? "missing");
1313
+ console.info("[stream-mdx:list-item-render]", {
1314
+ itemId: nodeId,
1315
+ depth,
1316
+ ordered: isOrdered,
1317
+ inferredIndex,
1318
+ marker,
1319
+ isTask,
1320
+ isChecked,
1321
+ childIds,
1322
+ childTypes,
1323
+ segmentChildIds,
1324
+ contentChildIds,
1325
+ inlineLength,
1326
+ hasChildren,
1327
+ isFinalized: block?.isFinalized ?? false
1328
+ });
1329
+ }, [
1330
+ listDebugEnabled,
1331
+ nodeId,
1332
+ depth,
1333
+ isOrdered,
1334
+ inferredIndex,
1335
+ marker,
1336
+ isTask,
1337
+ isChecked,
1338
+ childIds,
1339
+ segmentChildIds,
1340
+ contentChildIds,
1341
+ inlineLength,
1342
+ hasChildren,
1343
+ store,
1344
+ block?.isFinalized
1345
+ ]);
1346
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1347
+ "li",
1348
+ {
1349
+ className: `markdown-list-item${isTask ? " markdown-list-item-task" : ""}`,
1350
+ "data-counter-text": counterText,
1351
+ "data-list-depth": depth,
1352
+ "data-list-item-id": listDebugEnabled ? nodeId : void 0,
1353
+ children: [
1354
+ isTask ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "markdown-task", children: [
1355
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", className: "markdown-task-checkbox", checked: isChecked, readOnly: true, disabled: true, tabIndex: -1, "aria-checked": isChecked }),
1356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "markdown-task-content", children: primaryContent })
1357
+ ] }) : primaryContent,
1358
+ hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "markdown-list-item-children", children: filteredChildren }) : null
1359
+ ]
1360
+ }
1361
+ );
1136
1362
  });
1137
1363
  ListItemView.displayName = "ListItemView";
1138
- var TableBlockView = import_react5.default.memo(({ store, blockId, registry }) => {
1364
+ var TableBlockView = import_react8.default.memo(({ store, blockId, registry }) => {
1139
1365
  const tableElements = registry.getTableElements();
1140
1366
  const childIds = useRendererChildren(store, blockId);
1141
1367
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(tableElements.Table, { className: "markdown-table w-full caption-bottom text-base", children: childIds.map((sectionId) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableSectionView, { store, nodeId: sectionId, registry }, sectionId)) });
1142
1368
  });
1143
1369
  TableBlockView.displayName = "TableBlockView";
1144
- var TableSectionView = import_react5.default.memo(({ store, nodeId, registry }) => {
1370
+ var TableSectionView = import_react8.default.memo(({ store, nodeId, registry }) => {
1145
1371
  const node = useRendererNode(store, nodeId);
1146
1372
  if (!node) return null;
1147
1373
  const tableElements = registry.getTableElements();
@@ -1156,13 +1382,13 @@ var TableSectionView = import_react5.default.memo(({ store, nodeId, registry })
1156
1382
  return null;
1157
1383
  });
1158
1384
  TableSectionView.displayName = "TableSectionView";
1159
- var TableRowView = import_react5.default.memo(({ store, nodeId, registry }) => {
1385
+ var TableRowView = import_react8.default.memo(({ store, nodeId, registry }) => {
1160
1386
  const tableElements = registry.getTableElements();
1161
1387
  const cellIds = useRendererChildren(store, nodeId);
1162
1388
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(tableElements.Tr, { children: cellIds.map((cellId) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCellView, { store, nodeId: cellId, registry }, cellId)) });
1163
1389
  });
1164
1390
  TableRowView.displayName = "TableRowView";
1165
- var TableCellView = import_react5.default.memo(({ store, nodeId, registry }) => {
1391
+ var TableCellView = import_react8.default.memo(({ store, nodeId, registry }) => {
1166
1392
  const node = useRendererNode(store, nodeId);
1167
1393
  const inlineComponents = registry.getInlineComponents();
1168
1394
  const tableElements = registry.getTableElements();
@@ -1175,13 +1401,19 @@ var TableCellView = import_react5.default.memo(({ store, nodeId, registry }) =>
1175
1401
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { ...alignProps, children: content });
1176
1402
  });
1177
1403
  TableCellView.displayName = "TableCellView";
1178
- var CodeBlockView = import_react5.default.memo(({ store, blockId, registry }) => {
1404
+ var CodeBlockView = import_react8.default.memo(({ store, blockId, registry }) => {
1179
1405
  const node = useRendererNode(store, blockId);
1180
1406
  const childIds = useRendererChildren(store, blockId);
1181
1407
  const CodeComponent = registry.getBlockComponent("code");
1408
+ const deferredConfig = import_react8.default.useContext(DeferredRenderContext);
1409
+ const deferredRef = import_react8.default.useRef(null);
1410
+ const shouldRenderDeferred = useDeferredRender(
1411
+ deferredRef,
1412
+ deferredConfig ? { ...deferredConfig, enabled: true } : { enabled: false }
1413
+ );
1182
1414
  if (!node || !node.block) return null;
1183
1415
  const nodeVersion = node.version;
1184
- const lines = import_react5.default.useMemo(() => {
1416
+ const lines = import_react8.default.useMemo(() => {
1185
1417
  const seenIds = /* @__PURE__ */ new Set();
1186
1418
  const seenIndices = /* @__PURE__ */ new Set();
1187
1419
  const deduped = [];
@@ -1191,6 +1423,7 @@ var CodeBlockView = import_react5.default.memo(({ store, blockId, registry }) =>
1191
1423
  const index = typeof child.props?.index === "number" ? child.props?.index : deduped.length;
1192
1424
  const text = typeof child.props?.text === "string" ? child.props?.text : "";
1193
1425
  const html = typeof child.props?.html === "string" ? child.props?.html : null;
1426
+ const tokens = Object.prototype.hasOwnProperty.call(child.props ?? {}, "tokens") ? child.props?.tokens : void 0;
1194
1427
  if (seenIds.has(child.id) || seenIndices.has(index)) {
1195
1428
  console.warn("[renderer-view] duplicate code line detected", {
1196
1429
  blockId,
@@ -1201,7 +1434,7 @@ var CodeBlockView = import_react5.default.memo(({ store, blockId, registry }) =>
1201
1434
  }
1202
1435
  seenIds.add(child.id);
1203
1436
  seenIndices.add(index);
1204
- deduped.push({ id: child.id, index, text, html });
1437
+ deduped.push({ id: child.id, index, text, html, tokens });
1205
1438
  }
1206
1439
  deduped.sort((a, b) => a.index - b.index);
1207
1440
  return deduped;
@@ -1210,9 +1443,93 @@ var CodeBlockView = import_react5.default.memo(({ store, blockId, registry }) =>
1210
1443
  const preAttrs = node.props?.preAttrs ?? void 0;
1211
1444
  const codeAttrs = node.props?.codeAttrs ?? void 0;
1212
1445
  const virtualizationConfig = DEFAULT_VIRTUALIZED_CODE_CONFIG;
1213
- const shouldVirtualize = virtualizationConfig.enabled && lines.length >= virtualizationConfig.virtualizeThreshold;
1446
+ const virtualizationDisabled = typeof process !== "undefined" && typeof process.env === "object" && process.env.STREAM_MDX_DISABLE_VIRTUALIZED_CODE === "true";
1447
+ const shouldVirtualize = !virtualizationDisabled && virtualizationConfig.enabled && lines.length >= virtualizationConfig.virtualizeThreshold;
1214
1448
  const virtualization = useVirtualizedCode(lines, shouldVirtualize ? virtualizationConfig : { ...virtualizationConfig, enabled: false });
1215
- const highlightedHtml = import_react5.default.useMemo(() => {
1449
+ const highlightRequester = useCodeHighlightRequester();
1450
+ const lazyEnabled = Boolean(node.block.payload.meta?.lazyTokenization);
1451
+ const lazyTokenizedUntil = typeof node.block.payload.meta?.lazyTokenizedUntil === "number" ? node.block.payload.meta.lazyTokenizedUntil : 0;
1452
+ const lastRangeRef = import_react8.default.useRef(null);
1453
+ const rafRef = import_react8.default.useRef(null);
1454
+ const idleRef = import_react8.default.useRef(null);
1455
+ import_react8.default.useEffect(() => {
1456
+ if (!shouldVirtualize || !lazyEnabled || !highlightRequester) return;
1457
+ const { visibleStart, visibleEnd, startIndex, endIndex } = virtualization.window;
1458
+ if (endIndex <= lazyTokenizedUntil) return;
1459
+ const nextRange = {
1460
+ visibleStart,
1461
+ visibleEnd,
1462
+ prefetchStart: startIndex,
1463
+ prefetchEnd: endIndex,
1464
+ tokenizedUntil: lazyTokenizedUntil
1465
+ };
1466
+ const last = lastRangeRef.current;
1467
+ if (last && last.visibleStart === nextRange.visibleStart && last.visibleEnd === nextRange.visibleEnd && last.prefetchStart === nextRange.prefetchStart && last.prefetchEnd === nextRange.prefetchEnd && last.tokenizedUntil === nextRange.tokenizedUntil) {
1468
+ return;
1469
+ }
1470
+ lastRangeRef.current = nextRange;
1471
+ if (rafRef.current !== null && typeof cancelAnimationFrame === "function") {
1472
+ cancelAnimationFrame(rafRef.current);
1473
+ }
1474
+ if (typeof requestAnimationFrame === "function") {
1475
+ rafRef.current = requestAnimationFrame(() => {
1476
+ highlightRequester({
1477
+ blockId,
1478
+ startLine: visibleStart,
1479
+ endLine: visibleEnd,
1480
+ priority: "visible",
1481
+ reason: "scroll"
1482
+ });
1483
+ rafRef.current = null;
1484
+ });
1485
+ } else {
1486
+ highlightRequester({
1487
+ blockId,
1488
+ startLine: visibleStart,
1489
+ endLine: visibleEnd,
1490
+ priority: "visible",
1491
+ reason: "scroll"
1492
+ });
1493
+ }
1494
+ const prefetchNeeded = startIndex < visibleStart || endIndex > visibleEnd;
1495
+ if (prefetchNeeded) {
1496
+ const idleCallback = globalThis.requestIdleCallback;
1497
+ const cancelIdle = globalThis.cancelIdleCallback;
1498
+ if (idleRef.current !== null) {
1499
+ if (typeof cancelIdle === "function") {
1500
+ cancelIdle(idleRef.current);
1501
+ } else if (typeof clearTimeout === "function") {
1502
+ clearTimeout(idleRef.current);
1503
+ }
1504
+ }
1505
+ const schedule = () => {
1506
+ highlightRequester({
1507
+ blockId,
1508
+ startLine: startIndex,
1509
+ endLine: endIndex,
1510
+ priority: "prefetch",
1511
+ reason: "buffer"
1512
+ });
1513
+ idleRef.current = null;
1514
+ };
1515
+ if (typeof idleCallback === "function") {
1516
+ idleRef.current = idleCallback(() => schedule());
1517
+ } else if (typeof setTimeout === "function") {
1518
+ idleRef.current = setTimeout(schedule, 80);
1519
+ }
1520
+ }
1521
+ }, [
1522
+ blockId,
1523
+ highlightRequester,
1524
+ lazyEnabled,
1525
+ lazyTokenizedUntil,
1526
+ shouldVirtualize,
1527
+ virtualization.window.visibleStart,
1528
+ virtualization.window.visibleEnd,
1529
+ virtualization.window.startIndex,
1530
+ virtualization.window.endIndex
1531
+ ]);
1532
+ const highlightedHtml = import_react8.default.useMemo(() => {
1216
1533
  if (!shouldVirtualize && node.block?.payload.highlightedHtml) {
1217
1534
  return node.block.payload.highlightedHtml;
1218
1535
  }
@@ -1243,11 +1560,19 @@ var CodeBlockView = import_react5.default.memo(({ store, blockId, registry }) =>
1243
1560
  }
1244
1561
  );
1245
1562
  const codeFrameClass = "not-prose flex flex-col rounded-lg border border-input pt-1 font-mono text-sm";
1563
+ const codeMetricsAttrs = {
1564
+ "data-code-block": "true",
1565
+ "data-block-id": blockId,
1566
+ "data-code-virtualized": shouldVirtualize ? "true" : "false",
1567
+ "data-code-total-lines": String(lines.length),
1568
+ "data-code-mounted-lines": String(shouldVirtualize ? virtualization.window.mountedLines : lines.length),
1569
+ "data-code-window-size": String(virtualization.config.windowSize)
1570
+ };
1246
1571
  const codeView = shouldVirtualize ? (() => {
1247
1572
  const { containerRef, window: window2, handleScroll, lineHeight } = virtualization;
1248
1573
  const spacerTop = window2.startIndex * lineHeight;
1249
1574
  const spacerBottom = (window2.totalLines - window2.endIndex) * lineHeight;
1250
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: codeFrameClass, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1575
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: codeFrameClass, ...codeMetricsAttrs, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1251
1576
  "div",
1252
1577
  {
1253
1578
  ref: containerRef,
@@ -1261,14 +1586,22 @@ var CodeBlockView = import_react5.default.memo(({ store, blockId, registry }) =>
1261
1586
  ]
1262
1587
  }
1263
1588
  ) });
1264
- })() : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: codeFrameClass, children: rendered });
1589
+ })() : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: codeFrameClass, ...codeMetricsAttrs, children: rendered });
1265
1590
  const blockComponentMap = registry.getBlockComponentMap();
1266
1591
  const MermaidComponent = Object.prototype.hasOwnProperty.call(blockComponentMap, "mermaid") ? blockComponentMap.mermaid : null;
1267
1592
  if ((lang ?? "").toLowerCase() === "mermaid" && MermaidComponent) {
1268
1593
  const raw = typeof node.block.payload.raw === "string" ? node.block.payload.raw : "";
1269
- const fenced = (0, import_core3.stripCodeFence)(raw);
1594
+ const fenced = (0, import_core4.stripCodeFence)(raw);
1270
1595
  const code = fenced.hadFence ? fenced.code : raw;
1271
- return import_react5.default.createElement(MermaidComponent, {
1596
+ if (deferredConfig) {
1597
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: deferredRef, children: shouldRenderDeferred ? import_react8.default.createElement(MermaidComponent, {
1598
+ code,
1599
+ renderCode: codeView,
1600
+ meta: node.block.payload.meta,
1601
+ isFinalized: node.block.isFinalized
1602
+ }) : codeView });
1603
+ }
1604
+ return import_react8.default.createElement(MermaidComponent, {
1272
1605
  code,
1273
1606
  renderCode: codeView,
1274
1607
  meta: node.block.payload.meta,