@lobehub/ui 5.6.1 → 5.6.3

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 (163) hide show
  1. package/es/Accordion/Accordion.d.mts +2 -2
  2. package/es/Accordion/AccordionItem.d.mts +2 -2
  3. package/es/ActionIcon/ActionIcon.d.mts +2 -2
  4. package/es/Alert/Alert.d.mts +2 -2
  5. package/es/AutoComplete/Select.d.mts +2 -2
  6. package/es/Avatar/AvatarGroup/index.d.mts +2 -2
  7. package/es/Burger/Burger.d.mts +2 -2
  8. package/es/CodeDiff/CodeDiff.d.mts +2 -2
  9. package/es/CodeDiff/PatchDiff.d.mts +2 -2
  10. package/es/CodeEditor/CodeEditor.d.mts +2 -2
  11. package/es/Collapse/Collapse.d.mts +2 -2
  12. package/es/ConfigProvider/index.d.mts +2 -2
  13. package/es/CopyButton/CopyButton.d.mts +2 -2
  14. package/es/DatePicker/DatePicker.d.mts +2 -2
  15. package/es/DraggablePanel/components/DraggablePanelBody.d.mts +2 -2
  16. package/es/DraggablePanel/components/DraggablePanelContainer.d.mts +2 -2
  17. package/es/DraggablePanel/components/DraggablePanelFooter.d.mts +2 -2
  18. package/es/DraggablePanel/components/DraggablePanelHeader.d.mts +2 -2
  19. package/es/DraggableSideNav/DraggableSideNav.d.mts +2 -2
  20. package/es/Drawer/Drawer.d.mts +2 -2
  21. package/es/Dropdown/Dropdown.d.mts +2 -2
  22. package/es/EditableText/EditableText.d.mts +2 -2
  23. package/es/EditorSlashMenu/atoms.d.mts +13 -13
  24. package/es/EmojiPicker/EmojiPicker.d.mts +2 -2
  25. package/es/Flex/FlexBasic.d.mts +2 -2
  26. package/es/FontLoader/index.d.mts +2 -2
  27. package/es/Footer/Footer.d.mts +2 -2
  28. package/es/Form/components/FormGroup.d.mts +2 -2
  29. package/es/Form/components/FormItem.d.mts +2 -2
  30. package/es/Form/components/FormSubmitFooter.d.mts +2 -2
  31. package/es/FormModal/FormModal.d.mts +2 -2
  32. package/es/GuideCard/GuideCard.d.mts +2 -2
  33. package/es/Header/Header.d.mts +2 -2
  34. package/es/Highlighter/Highlighter.d.mts +2 -2
  35. package/es/Highlighter/SyntaxHighlighter/index.d.mts +2 -2
  36. package/es/Hotkey/Hotkey.d.mts +2 -2
  37. package/es/HotkeyInput/HotkeyInput.d.mts +2 -2
  38. package/es/Icon/Icon.d.mts +2 -2
  39. package/es/Icon/components/IconProvider.d.mts +3 -3
  40. package/es/Image/PreviewGroup.d.mts +2 -2
  41. package/es/ImageSelect/ImageSelect.d.mts +2 -2
  42. package/es/Input/Input.d.mts +2 -2
  43. package/es/Input/InputNumber.d.mts +2 -2
  44. package/es/Input/InputOPT.d.mts +2 -2
  45. package/es/Input/InputPassword.d.mts +2 -2
  46. package/es/Input/TextArea.d.mts +2 -2
  47. package/es/Layout/components/LayoutFooter.d.mts +2 -2
  48. package/es/Layout/components/LayoutHeader.d.mts +2 -2
  49. package/es/Layout/components/LayoutMain.d.mts +2 -2
  50. package/es/Layout/components/LayoutSidebar.d.mts +2 -2
  51. package/es/Layout/components/LayoutSidebarInner.d.mts +2 -2
  52. package/es/Layout/components/LayoutToc.d.mts +2 -2
  53. package/es/List/ListItem/index.d.mts +2 -2
  54. package/es/Markdown/Markdown.d.mts +2 -2
  55. package/es/Markdown/SyntaxMarkdown/StreamdownRender.mjs +173 -33
  56. package/es/Markdown/SyntaxMarkdown/StreamdownRender.mjs.map +1 -1
  57. package/es/Markdown/SyntaxMarkdown/streamAnimationMeta.mjs +18 -0
  58. package/es/Markdown/SyntaxMarkdown/streamAnimationMeta.mjs.map +1 -0
  59. package/es/Markdown/SyntaxMarkdown/useSmoothStreamContent.mjs +62 -10
  60. package/es/Markdown/SyntaxMarkdown/useSmoothStreamContent.mjs.map +1 -1
  61. package/es/Markdown/Typography.d.mts +2 -2
  62. package/es/Markdown/components/SearchResultCards/index.d.mts +2 -2
  63. package/es/Markdown/streamProfiler/StreamdownProfilerProvider.mjs +21 -0
  64. package/es/Markdown/streamProfiler/StreamdownProfilerProvider.mjs.map +1 -0
  65. package/es/MaskShadow/MaskShadow.d.mts +2 -2
  66. package/es/Menu/Menu.d.mts +2 -2
  67. package/es/Mermaid/Mermaid.d.mts +2 -2
  68. package/es/Mermaid/SyntaxMermaid/index.d.mts +2 -2
  69. package/es/Modal/Modal.d.mts +2 -2
  70. package/es/Modal/ModalProvider.d.mts +2 -2
  71. package/es/Modal/imperative.d.mts +2 -2
  72. package/es/MotionProvider/index.d.mts +2 -2
  73. package/es/SearchBar/SearchBar.d.mts +2 -2
  74. package/es/Segmented/Segmented.d.mts +2 -2
  75. package/es/Select/Select.d.mts +2 -2
  76. package/es/SideNav/SideNav.d.mts +2 -2
  77. package/es/SliderWithInput/SliderWithInput.d.mts +2 -2
  78. package/es/SortableList/components/DragHandle.d.mts +2 -2
  79. package/es/SortableList/components/SortableItem.d.mts +2 -2
  80. package/es/ThemeProvider/ThemeProvider.d.mts +2 -2
  81. package/es/Toc/Toc.d.mts +2 -2
  82. package/es/Video/index.d.mts +2 -2
  83. package/es/awesome/AuroraBackground/AuroraBackground.d.mts +2 -2
  84. package/es/awesome/BottomGradientButton/BottomGradientButton.d.mts +2 -2
  85. package/es/awesome/Features/Features.d.mts +2 -2
  86. package/es/awesome/Giscus/Giscus.d.mts +2 -2
  87. package/es/awesome/GradientButton/GradientButton.d.mts +2 -2
  88. package/es/awesome/GridBackground/GridBackground.d.mts +2 -2
  89. package/es/awesome/GridBackground/GridShowcase.d.mts +2 -2
  90. package/es/awesome/Hero/Hero.d.mts +2 -2
  91. package/es/awesome/Spline/Spine.d.mts +2 -2
  92. package/es/awesome/Spotlight/Spotlight.d.mts +2 -2
  93. package/es/awesome/SpotlightCard/SpotlightCard.d.mts +2 -2
  94. package/es/awesome/TypewriterEffect/TypewriterEffect.d.mts +2 -2
  95. package/es/base-ui/DropdownMenu/DropdownMenu.d.mts +2 -2
  96. package/es/base-ui/Modal/context.d.mts +2 -2
  97. package/es/base-ui/Modal/imperative.d.mts +2 -2
  98. package/es/base-ui/Popover/atoms.d.mts +2 -2
  99. package/es/base-ui/Popover/context.d.mts +2 -2
  100. package/es/base-ui/Select/Select.d.mts +2 -2
  101. package/es/base-ui/Select/atoms.d.mts +19 -19
  102. package/es/base-ui/Switch/Switch.d.mts +2 -2
  103. package/es/base-ui/Switch/atoms.d.mts +4 -4
  104. package/es/base-ui/Toast/Toast.mjs +32 -24
  105. package/es/base-ui/Toast/Toast.mjs.map +1 -1
  106. package/es/base-ui/Toast/imperative.d.mts +2 -2
  107. package/es/base-ui/Toast/style.mjs +5 -0
  108. package/es/base-ui/Toast/style.mjs.map +1 -1
  109. package/es/brand/LobeChat/index.d.mts +2 -2
  110. package/es/brand/LobeHub/index.d.mts +2 -2
  111. package/es/brand/LogoThree/LogoSpline.d.mts +2 -2
  112. package/es/brand/LogoThree/index.d.mts +2 -2
  113. package/es/chat/BackBottom/BackBottom.d.mts +2 -2
  114. package/es/chat/ChatInputArea/components/ChatInputAreaInner.d.mts +2 -2
  115. package/es/chat/ChatItem/ChatItem.d.mts +2 -2
  116. package/es/chat/ChatList/ChatList.d.mts +2 -2
  117. package/es/chat/EditableMessage/EditableMessage.d.mts +2 -2
  118. package/es/color/ColorScales/index.d.mts +2 -2
  119. package/es/color/CssVar/index.d.mts +2 -2
  120. package/es/i18n/context.d.mts +2 -2
  121. package/es/icons/lucideExtra/AndroidIcon.d.mts +2 -2
  122. package/es/icons/lucideExtra/AppleIcon.d.mts +2 -2
  123. package/es/icons/lucideExtra/AppstoreIcon.d.mts +2 -2
  124. package/es/icons/lucideExtra/BotPromptIcon.d.mts +3 -3
  125. package/es/icons/lucideExtra/BrainOffIcon.d.mts +2 -2
  126. package/es/icons/lucideExtra/ChromeIcon.d.mts +3 -3
  127. package/es/icons/lucideExtra/CodepenIcon.d.mts +3 -3
  128. package/es/icons/lucideExtra/CodesandboxIcon.d.mts +3 -3
  129. package/es/icons/lucideExtra/CreateBotIcon.d.mts +3 -3
  130. package/es/icons/lucideExtra/DiscordIcon.d.mts +3 -3
  131. package/es/icons/lucideExtra/FacebookIcon.d.mts +3 -3
  132. package/es/icons/lucideExtra/FigmaIcon.d.mts +3 -3
  133. package/es/icons/lucideExtra/FramerIcon.d.mts +3 -3
  134. package/es/icons/lucideExtra/GithubIcon.d.mts +3 -3
  135. package/es/icons/lucideExtra/GitlabIcon.d.mts +3 -3
  136. package/es/icons/lucideExtra/GlobeOffIcon.d.mts +3 -3
  137. package/es/icons/lucideExtra/GooglePlayIcon.d.mts +3 -3
  138. package/es/icons/lucideExtra/GroupBotIcon.d.mts +3 -3
  139. package/es/icons/lucideExtra/GroupBotSquareIcon.d.mts +3 -3
  140. package/es/icons/lucideExtra/InstagramIcon.d.mts +3 -3
  141. package/es/icons/lucideExtra/LeftClickIcon.d.mts +3 -3
  142. package/es/icons/lucideExtra/LeftDoubleClickIcon.d.mts +3 -3
  143. package/es/icons/lucideExtra/LinkedinIcon.d.mts +3 -3
  144. package/es/icons/lucideExtra/McpIcon.d.mts +3 -3
  145. package/es/icons/lucideExtra/NotionIcon.d.mts +2 -2
  146. package/es/icons/lucideExtra/PocketIcon.d.mts +3 -3
  147. package/es/icons/lucideExtra/ProviderIcon.d.mts +3 -3
  148. package/es/icons/lucideExtra/RailSymbolIcon.d.mts +3 -3
  149. package/es/icons/lucideExtra/RedditIcon.d.mts +3 -3
  150. package/es/icons/lucideExtra/RightClickIcon.d.mts +3 -3
  151. package/es/icons/lucideExtra/RightDoubleClickIcon.d.mts +3 -3
  152. package/es/icons/lucideExtra/ShapesUploadIcon.d.mts +2 -2
  153. package/es/icons/lucideExtra/SkillsIcon.d.mts +3 -3
  154. package/es/icons/lucideExtra/SlackIcon.d.mts +3 -3
  155. package/es/icons/lucideExtra/ThinkIcon.d.mts +3 -3
  156. package/es/icons/lucideExtra/TreeDownRightIcon.d.mts +3 -3
  157. package/es/icons/lucideExtra/TreeUpDownRightIcon.d.mts +3 -3
  158. package/es/mdx/Mdx/index.d.mts +2 -2
  159. package/es/mobile/ChatHeader/ChatHeaderTitle.d.mts +2 -2
  160. package/es/mobile/ChatInputArea/components/ChatSendButton.d.mts +2 -2
  161. package/es/mobile/TabBar/TabBar.d.mts +2 -2
  162. package/es/storybook/StoryBook/index.d.mts +2 -2
  163. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import { InputNumberProps } from "./type.mjs";
2
- import * as react179 from "react";
2
+ import * as react182 from "react";
3
3
 
4
4
  //#region src/Input/InputNumber.d.ts
5
- declare const InputNumber: react179.NamedExoticComponent<InputNumberProps>;
5
+ declare const InputNumber: react182.NamedExoticComponent<InputNumberProps>;
6
6
  //#endregion
7
7
  export { InputNumber };
8
8
  //# sourceMappingURL=InputNumber.d.mts.map
@@ -1,8 +1,8 @@
1
1
  import { InputOPTProps } from "./type.mjs";
2
- import * as react180 from "react";
2
+ import * as react183 from "react";
3
3
 
4
4
  //#region src/Input/InputOPT.d.ts
5
- declare const InputOPT: react180.NamedExoticComponent<InputOPTProps>;
5
+ declare const InputOPT: react183.NamedExoticComponent<InputOPTProps>;
6
6
  //#endregion
7
7
  export { InputOPT };
8
8
  //# sourceMappingURL=InputOPT.d.mts.map
@@ -1,8 +1,8 @@
1
1
  import { InputPasswordProps } from "./type.mjs";
2
- import * as react181 from "react";
2
+ import * as react184 from "react";
3
3
 
4
4
  //#region src/Input/InputPassword.d.ts
5
- declare const InputPassword: react181.NamedExoticComponent<InputPasswordProps>;
5
+ declare const InputPassword: react184.NamedExoticComponent<InputPasswordProps>;
6
6
  //#endregion
7
7
  export { InputPassword };
8
8
  //# sourceMappingURL=InputPassword.d.mts.map
@@ -1,8 +1,8 @@
1
1
  import { TextAreaProps } from "./type.mjs";
2
- import * as react182 from "react";
2
+ import * as react185 from "react";
3
3
 
4
4
  //#region src/Input/TextArea.d.ts
5
- declare const TextArea: react182.NamedExoticComponent<TextAreaProps>;
5
+ declare const TextArea: react185.NamedExoticComponent<TextAreaProps>;
6
6
  //#endregion
7
7
  export { TextArea };
8
8
  //# sourceMappingURL=TextArea.d.mts.map
@@ -1,9 +1,9 @@
1
1
  import { DivProps } from "../../types/index.mjs";
2
2
  import "../../index.mjs";
3
- import * as react37 from "react";
3
+ import * as react28 from "react";
4
4
 
5
5
  //#region src/Layout/components/LayoutFooter.d.ts
6
- declare const LayoutFooter: react37.NamedExoticComponent<DivProps>;
6
+ declare const LayoutFooter: react28.NamedExoticComponent<DivProps>;
7
7
  //#endregion
8
8
  export { LayoutFooter };
9
9
  //# sourceMappingURL=LayoutFooter.d.mts.map
@@ -1,8 +1,8 @@
1
1
  import { LayoutHeaderProps } from "../type.mjs";
2
- import * as react38 from "react";
2
+ import * as react29 from "react";
3
3
 
4
4
  //#region src/Layout/components/LayoutHeader.d.ts
5
- declare const LayoutHeader: react38.NamedExoticComponent<LayoutHeaderProps>;
5
+ declare const LayoutHeader: react29.NamedExoticComponent<LayoutHeaderProps>;
6
6
  //#endregion
7
7
  export { LayoutHeader };
8
8
  //# sourceMappingURL=LayoutHeader.d.mts.map
@@ -1,9 +1,9 @@
1
1
  import { DivProps } from "../../types/index.mjs";
2
2
  import "../../index.mjs";
3
- import * as react39 from "react";
3
+ import * as react30 from "react";
4
4
 
5
5
  //#region src/Layout/components/LayoutMain.d.ts
6
- declare const LayoutMain: react39.NamedExoticComponent<DivProps>;
6
+ declare const LayoutMain: react30.NamedExoticComponent<DivProps>;
7
7
  //#endregion
8
8
  export { LayoutMain };
9
9
  //# sourceMappingURL=LayoutMain.d.mts.map
@@ -1,8 +1,8 @@
1
1
  import { LayoutSidebarProps } from "../type.mjs";
2
- import * as react40 from "react";
2
+ import * as react31 from "react";
3
3
 
4
4
  //#region src/Layout/components/LayoutSidebar.d.ts
5
- declare const LayoutSidebar: react40.NamedExoticComponent<LayoutSidebarProps>;
5
+ declare const LayoutSidebar: react31.NamedExoticComponent<LayoutSidebarProps>;
6
6
  //#endregion
7
7
  export { LayoutSidebar };
8
8
  //# sourceMappingURL=LayoutSidebar.d.mts.map
@@ -1,8 +1,8 @@
1
1
  import { LayoutSidebarInnerProps } from "../type.mjs";
2
- import * as react41 from "react";
2
+ import * as react32 from "react";
3
3
 
4
4
  //#region src/Layout/components/LayoutSidebarInner.d.ts
5
- declare const LayoutSidebarInner: react41.NamedExoticComponent<LayoutSidebarInnerProps>;
5
+ declare const LayoutSidebarInner: react32.NamedExoticComponent<LayoutSidebarInnerProps>;
6
6
  //#endregion
7
7
  export { LayoutSidebarInner };
8
8
  //# sourceMappingURL=LayoutSidebarInner.d.mts.map
@@ -1,8 +1,8 @@
1
1
  import { LayoutTocProps } from "../type.mjs";
2
- import * as react42 from "react";
2
+ import * as react33 from "react";
3
3
 
4
4
  //#region src/Layout/components/LayoutToc.d.ts
5
- declare const LayoutToc: react42.NamedExoticComponent<LayoutTocProps>;
5
+ declare const LayoutToc: react33.NamedExoticComponent<LayoutTocProps>;
6
6
  //#endregion
7
7
  export { LayoutToc };
8
8
  //# sourceMappingURL=LayoutToc.d.mts.map
@@ -1,8 +1,8 @@
1
1
  import { ListItemProps } from "../type.mjs";
2
- import * as react47 from "react";
2
+ import * as react27 from "react";
3
3
 
4
4
  //#region src/List/ListItem/index.d.ts
5
- declare const ListItem: react47.NamedExoticComponent<ListItemProps>;
5
+ declare const ListItem: react27.NamedExoticComponent<ListItemProps>;
6
6
  //#endregion
7
7
  export { ListItem };
8
8
  //# sourceMappingURL=index.d.mts.map
@@ -1,8 +1,8 @@
1
1
  import { MarkdownProps } from "./type.mjs";
2
- import * as react45 from "react";
2
+ import * as react26 from "react";
3
3
 
4
4
  //#region src/Markdown/Markdown.d.ts
5
- declare const Markdown: react45.NamedExoticComponent<MarkdownProps>;
5
+ declare const Markdown: react26.NamedExoticComponent<MarkdownProps>;
6
6
  //#endregion
7
7
  export { Markdown };
8
8
  //# sourceMappingURL=Markdown.d.mts.map
@@ -6,10 +6,12 @@ import { useMarkdownContent } from "../../hooks/useMarkdown/useMarkdownContent.m
6
6
  import { useMarkdownRehypePlugins } from "../../hooks/useMarkdown/useMarkdownRehypePlugins.mjs";
7
7
  import { useMarkdownRemarkPlugins } from "../../hooks/useMarkdown/useMarkdownRemarkPlugins.mjs";
8
8
  import { rehypeStreamAnimated } from "../plugins/rehypeStreamAnimated.mjs";
9
+ import { useStreamdownProfiler } from "../streamProfiler/StreamdownProfilerProvider.mjs";
10
+ import { resolveBlockAnimationMeta } from "./streamAnimationMeta.mjs";
9
11
  import { styles } from "./style.mjs";
10
12
  import { useSmoothStreamContent } from "./useSmoothStreamContent.mjs";
11
13
  import { useStreamQueue } from "./useStreamQueue.mjs";
12
- import { createElement, memo, useEffect, useId, useMemo, useRef } from "react";
14
+ import { Profiler, createElement, memo, useCallback, useEffect, useId, useMemo, useRef } from "react";
13
15
  import { jsx } from "react/jsx-runtime";
14
16
  import Markdown from "react-markdown";
15
17
  import { marked } from "marked";
@@ -17,9 +19,13 @@ import remend from "remend";
17
19
 
18
20
  //#region src/Markdown/SyntaxMarkdown/StreamdownRender.tsx
19
21
  const STREAM_FADE_DURATION = 280;
22
+ const REVEALED_STREAM_PLUGIN = [rehypeStreamAnimated, { revealed: true }];
20
23
  function countChars(text) {
21
24
  return [...text].length;
22
25
  }
26
+ function getNow() {
27
+ return typeof performance === "undefined" ? Date.now() : performance.now();
28
+ }
23
29
  const isRecord = (value) => typeof value === "object" && value !== null;
24
30
  const isDeepEqualValue = (a, b) => {
25
31
  if (a === b) return true;
@@ -64,19 +70,27 @@ const StreamdownBlock = memo(({ children, ...rest }) => {
64
70
  StreamdownBlock.displayName = "StreamdownBlock";
65
71
  const StreamdownRender = memo(({ children, ...rest }) => {
66
72
  const { streamSmoothingPreset = "balanced" } = useMarkdownContext();
73
+ const profiler = useStreamdownProfiler();
67
74
  const escapedContent = useMarkdownContent(children || "");
68
75
  const components = useMarkdownComponents();
69
76
  const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());
70
77
  const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());
71
78
  const generatedId = useId();
72
79
  const smoothedContent = useSmoothStreamContent(typeof escapedContent === "string" ? escapedContent : "", { preset: streamSmoothingPreset });
73
- const processedContent = useMemo(() => {
74
- return remend(smoothedContent);
75
- }, [smoothedContent]);
76
- const blocks = useMemo(() => {
80
+ const processedContentResult = useMemo(() => {
81
+ const start = profiler ? getNow() : 0;
82
+ const value = remend(smoothedContent);
83
+ return {
84
+ durationMs: profiler ? getNow() - start : 0,
85
+ value
86
+ };
87
+ }, [profiler, smoothedContent]);
88
+ const processedContent = processedContentResult.value;
89
+ const blocksResult = useMemo(() => {
90
+ const start = profiler ? getNow() : 0;
77
91
  const tokens = marked.lexer(processedContent);
78
92
  let offset = 0;
79
- return tokens.map((token) => {
93
+ const value = tokens.map((token) => {
80
94
  const block = {
81
95
  content: token.raw,
82
96
  startOffset: offset
@@ -84,14 +98,21 @@ const StreamdownRender = memo(({ children, ...rest }) => {
84
98
  offset += token.raw.length;
85
99
  return block;
86
100
  });
87
- }, [processedContent]);
101
+ return {
102
+ durationMs: profiler ? getNow() - start : 0,
103
+ value
104
+ };
105
+ }, [processedContent, profiler]);
106
+ const blocks = blocksResult.value;
88
107
  const { getBlockState, charDelay } = useStreamQueue(blocks);
89
108
  const prevBlockCharCountRef = useRef(/* @__PURE__ */ new Map());
109
+ const blockCharDelayRef = useRef(/* @__PURE__ */ new Map());
90
110
  const blockTimelineRef = useRef(/* @__PURE__ */ new Map());
91
111
  const lastRenderTsRef = useRef(null);
92
- const renderTs = typeof performance === "undefined" ? Date.now() : performance.now();
112
+ const renderTs = getNow();
93
113
  const frameDt = lastRenderTsRef.current === null ? 0 : Math.max(0, Math.min(renderTs - lastRenderTsRef.current, 120));
94
- const timelineForRender = useMemo(() => {
114
+ const timelineResult = useMemo(() => {
115
+ const start = profiler ? getNow() : 0;
95
116
  const next = /* @__PURE__ */ new Map();
96
117
  const prevTimeline = blockTimelineRef.current;
97
118
  const prevCharCounts = prevBlockCharCountRef.current;
@@ -108,50 +129,169 @@ const StreamdownRender = memo(({ children, ...rest }) => {
108
129
  const minElapsed = Math.max(0, latestCharStart - charDelay * 2);
109
130
  next.set(block.startOffset, Math.max(elapsedByTime, minElapsed));
110
131
  }
111
- return next;
132
+ return {
133
+ durationMs: profiler ? getNow() - start : 0,
134
+ value: next
135
+ };
112
136
  }, [
113
137
  blocks,
114
138
  charDelay,
115
- frameDt
139
+ frameDt,
140
+ profiler
141
+ ]);
142
+ const timelineForRender = timelineResult.value;
143
+ useEffect(() => {
144
+ if (!profiler) return;
145
+ profiler.recordCalculation({
146
+ durationMs: processedContentResult.durationMs,
147
+ name: "content-normalize",
148
+ textLength: processedContent.length
149
+ });
150
+ }, [
151
+ processedContent.length,
152
+ processedContentResult.durationMs,
153
+ profiler
154
+ ]);
155
+ useEffect(() => {
156
+ if (!profiler) return;
157
+ profiler.recordCalculation({
158
+ durationMs: blocksResult.durationMs,
159
+ itemCount: blocks.length,
160
+ name: "block-lex",
161
+ textLength: processedContent.length
162
+ });
163
+ }, [
164
+ blocks.length,
165
+ blocksResult.durationMs,
166
+ processedContent.length,
167
+ profiler
168
+ ]);
169
+ useEffect(() => {
170
+ if (!profiler) return;
171
+ profiler.recordCalculation({
172
+ durationMs: timelineResult.durationMs,
173
+ itemCount: blocks.length,
174
+ name: "block-timeline",
175
+ textLength: processedContent.length
176
+ });
177
+ }, [
178
+ blocks.length,
179
+ processedContent.length,
180
+ profiler,
181
+ timelineResult.durationMs
182
+ ]);
183
+ const blockAnimationMetaResult = useMemo(() => {
184
+ const nextBlockCharDelay = /* @__PURE__ */ new Map();
185
+ const blockAnimationMeta = /* @__PURE__ */ new Map();
186
+ for (const [index, block] of blocks.entries()) {
187
+ const state = getBlockState(index);
188
+ const timelineElapsedMs = timelineForRender.get(block.startOffset) ?? 0;
189
+ const animationMeta = resolveBlockAnimationMeta({
190
+ blockCharCount: countChars(block.content),
191
+ currentCharDelay: charDelay,
192
+ fadeDuration: STREAM_FADE_DURATION,
193
+ previousCharDelay: blockCharDelayRef.current.get(block.startOffset),
194
+ state,
195
+ timelineElapsedMs
196
+ });
197
+ nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);
198
+ blockAnimationMeta.set(block.startOffset, animationMeta);
199
+ }
200
+ return {
201
+ blockAnimationMeta,
202
+ blockCharDelay: nextBlockCharDelay
203
+ };
204
+ }, [
205
+ blocks,
206
+ charDelay,
207
+ getBlockState,
208
+ timelineForRender
116
209
  ]);
117
210
  useEffect(() => {
118
211
  const nextCharCount = /* @__PURE__ */ new Map();
119
212
  for (const block of blocks) nextCharCount.set(block.startOffset, countChars(block.content));
213
+ blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;
120
214
  prevBlockCharCountRef.current = nextCharCount;
121
215
  blockTimelineRef.current = timelineForRender;
122
- lastRenderTsRef.current = typeof performance === "undefined" ? Date.now() : performance.now();
123
- }, [blocks, timelineForRender]);
124
- return /* @__PURE__ */ jsx("div", {
216
+ lastRenderTsRef.current = getNow();
217
+ }, [
218
+ blockAnimationMetaResult.blockCharDelay,
219
+ blocks,
220
+ timelineForRender
221
+ ]);
222
+ const handleRootRender = useCallback((_, phase, actualDuration, baseDuration) => {
223
+ profiler?.recordRootCommit({
224
+ actualDuration,
225
+ baseDuration,
226
+ blockCount: blocks.length,
227
+ phase,
228
+ textLength: processedContent.length
229
+ });
230
+ }, [
231
+ blocks.length,
232
+ processedContent.length,
233
+ profiler
234
+ ]);
235
+ const handleBlockRender = useCallback((id, phase, actualDuration, baseDuration) => {
236
+ if (!profiler) return;
237
+ const [, indexText, offsetText] = id.split(":");
238
+ const blockIndex = Number(indexText);
239
+ if (!Number.isFinite(blockIndex)) return;
240
+ const block = blocks[blockIndex];
241
+ if (!block) return;
242
+ profiler.recordBlockCommit({
243
+ actualDuration,
244
+ baseDuration,
245
+ blockChars: countChars(block.content),
246
+ blockIndex,
247
+ blockKey: offsetText ?? String(block.startOffset),
248
+ phase,
249
+ state: getBlockState(blockIndex)
250
+ });
251
+ }, [
252
+ blocks,
253
+ getBlockState,
254
+ profiler
255
+ ]);
256
+ const content = /* @__PURE__ */ jsx("div", {
125
257
  className: styles.animated,
126
258
  children: blocks.map((block, index) => {
127
- const state = getBlockState(index);
128
- if (state === "queued") return null;
129
- const timelineElapsedMs = timelineForRender.get(block.startOffset) ?? 0;
130
- let plugins;
131
- if (state === "streaming") plugins = [...baseRehypePlugins, [rehypeStreamAnimated, {
132
- charDelay,
259
+ if (getBlockState(index) === "queued") return null;
260
+ const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);
261
+ if (!animationMeta) return null;
262
+ const plugins = animationMeta.settled ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN] : [...baseRehypePlugins, [rehypeStreamAnimated, {
263
+ charDelay: animationMeta.charDelay,
133
264
  fadeDuration: STREAM_FADE_DURATION,
134
- timelineElapsedMs
135
- }]];
136
- else if (state === "animating") plugins = [...baseRehypePlugins, [rehypeStreamAnimated, {
137
- charDelay,
138
- fadeDuration: STREAM_FADE_DURATION,
139
- timelineElapsedMs
265
+ timelineElapsedMs: animationMeta.timelineElapsedMs
140
266
  }]];
141
- else plugins = [...baseRehypePlugins, [rehypeStreamAnimated, {
142
- charDelay,
143
- fadeDuration: STREAM_FADE_DURATION,
144
- timelineElapsedMs
145
- }]];
146
- return /* @__PURE__ */ createElement(StreamdownBlock, {
267
+ const key = `${generatedId}-${block.startOffset}`;
268
+ const blockNode = /* @__PURE__ */ jsx(StreamdownBlock, {
147
269
  ...rest,
148
270
  components,
149
- key: `${generatedId}-${block.startOffset}`,
271
+ rehypePlugins: plugins,
272
+ remarkPlugins,
273
+ children: block.content
274
+ });
275
+ if (!profiler) return /* @__PURE__ */ createElement(StreamdownBlock, {
276
+ ...rest,
277
+ components,
278
+ key,
150
279
  rehypePlugins: plugins,
151
280
  remarkPlugins
152
281
  }, block.content);
282
+ return /* @__PURE__ */ jsx(Profiler, {
283
+ id: `streamdown-block:${index}:${block.startOffset}`,
284
+ onRender: handleBlockRender,
285
+ children: blockNode
286
+ }, key);
153
287
  })
154
288
  });
289
+ if (!profiler) return content;
290
+ return /* @__PURE__ */ jsx(Profiler, {
291
+ id: "streamdown-root",
292
+ onRender: handleRootRender,
293
+ children: content
294
+ });
155
295
  });
156
296
  StreamdownRender.displayName = "StreamdownRender";
157
297
  var StreamdownRender_default = StreamdownRender;
@@ -1 +1 @@
1
- {"version":3,"file":"StreamdownRender.mjs","names":["blocks: BlockInfo[]","plugins: Pluggable[]"],"sources":["../../../src/Markdown/SyntaxMarkdown/StreamdownRender.tsx"],"sourcesContent":["'use client';\n\nimport { marked } from 'marked';\nimport { memo, useEffect, useId, useMemo, useRef } from 'react';\nimport Markdown, { type Options } from 'react-markdown';\nimport remend from 'remend';\nimport type { Pluggable, PluggableList } from 'unified';\n\nimport {\n useMarkdownComponents,\n useMarkdownContent,\n useMarkdownRehypePlugins,\n useMarkdownRemarkPlugins,\n} from '@/hooks/useMarkdown';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\nimport { rehypeStreamAnimated } from '@/Markdown/plugins/rehypeStreamAnimated';\n\nimport { styles } from './style';\nimport { useSmoothStreamContent } from './useSmoothStreamContent';\nimport { type BlockInfo, useStreamQueue } from './useStreamQueue';\n\nconst STREAM_FADE_DURATION = 280;\n\nfunction countChars(text: string): number {\n return [...text].length;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isDeepEqualValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqualValue(a[i], b[i])) return false;\n }\n return true;\n }\n\n if (!isRecord(a) || !isRecord(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!isDeepEqualValue(a[key], b[key])) return false;\n }\n\n return true;\n};\n\nconst isSamePlugin = (prevPlugin: Pluggable, nextPlugin: Pluggable): boolean => {\n const prevTuple = Array.isArray(prevPlugin) ? prevPlugin : [prevPlugin];\n const nextTuple = Array.isArray(nextPlugin) ? nextPlugin : [nextPlugin];\n\n if (prevTuple.length !== nextTuple.length) return false;\n if (prevTuple[0] !== nextTuple[0]) return false;\n\n return isDeepEqualValue(prevTuple.slice(1), nextTuple.slice(1));\n};\n\nconst isSamePlugins = (\n prevPlugins?: PluggableList | null,\n nextPlugins?: PluggableList | null,\n): boolean => {\n if (prevPlugins === nextPlugins) return true;\n if (!prevPlugins || !nextPlugins) return !prevPlugins && !nextPlugins;\n if (prevPlugins.length !== nextPlugins.length) return false;\n\n for (let i = 0; i < prevPlugins.length; i++) {\n if (!isSamePlugin(prevPlugins[i], nextPlugins[i])) return false;\n }\n\n return true;\n};\n\nconst useStablePlugins = (plugins: PluggableList): PluggableList => {\n const stableRef = useRef<PluggableList>(plugins);\n\n if (!isSamePlugins(stableRef.current, plugins)) {\n stableRef.current = plugins;\n }\n\n return stableRef.current;\n};\n\nconst StreamdownBlock = memo<Options>(\n ({ children, ...rest }) => {\n return <Markdown {...rest}>{children}</Markdown>;\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.components === nextProps.components &&\n isSamePlugins(prevProps.rehypePlugins, nextProps.rehypePlugins) &&\n isSamePlugins(prevProps.remarkPlugins, nextProps.remarkPlugins),\n);\n\nStreamdownBlock.displayName = 'StreamdownBlock';\n\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const { streamSmoothingPreset = 'balanced' } = useMarkdownContext();\n const escapedContent = useMarkdownContent(children || '');\n const components = useMarkdownComponents();\n const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());\n const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());\n const generatedId = useId();\n const smoothedContent = useSmoothStreamContent(\n typeof escapedContent === 'string' ? escapedContent : '',\n { preset: streamSmoothingPreset },\n );\n\n const processedContent = useMemo(() => {\n return remend(smoothedContent);\n }, [smoothedContent]);\n\n const blocks: BlockInfo[] = useMemo(() => {\n const tokens = marked.lexer(processedContent);\n let offset = 0;\n return tokens.map((token) => {\n const block = { content: token.raw, startOffset: offset };\n offset += token.raw.length;\n return block;\n });\n }, [processedContent]);\n\n const { getBlockState, charDelay } = useStreamQueue(blocks);\n const prevBlockCharCountRef = useRef<Map<number, number>>(new Map());\n const blockTimelineRef = useRef<Map<number, number>>(new Map());\n const lastRenderTsRef = useRef<number | null>(null);\n\n const renderTs = typeof performance === 'undefined' ? Date.now() : performance.now();\n const frameDt =\n lastRenderTsRef.current === null\n ? 0\n : Math.max(0, Math.min(renderTs - lastRenderTsRef.current, 120));\n\n const timelineForRender = useMemo(() => {\n const next = new Map<number, number>();\n const prevTimeline = blockTimelineRef.current;\n const prevCharCounts = prevBlockCharCountRef.current;\n\n for (const block of blocks) {\n const blockCharCount = countChars(block.content);\n const prevCharCount = prevCharCounts.get(block.startOffset) ?? 0;\n const prevElapsed = prevTimeline.get(block.startOffset);\n const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);\n\n if (prevElapsed === undefined || blockCharCount < prevCharCount) {\n next.set(block.startOffset, latestCharStart);\n continue;\n }\n\n const elapsedByTime = prevElapsed + frameDt;\n // Avoid huge hidden backlog when stream updates in bursts.\n const minElapsed = Math.max(0, latestCharStart - charDelay * 2);\n next.set(block.startOffset, Math.max(elapsedByTime, minElapsed));\n }\n\n return next;\n }, [blocks, charDelay, frameDt]);\n\n useEffect(() => {\n const nextCharCount = new Map<number, number>();\n for (const block of blocks) {\n nextCharCount.set(block.startOffset, countChars(block.content));\n }\n prevBlockCharCountRef.current = nextCharCount;\n blockTimelineRef.current = timelineForRender;\n lastRenderTsRef.current = typeof performance === 'undefined' ? Date.now() : performance.now();\n }, [blocks, timelineForRender]);\n\n return (\n <div className={styles.animated}>\n {blocks.map((block, index) => {\n const state = getBlockState(index);\n if (state === 'queued') return null;\n const timelineElapsedMs = timelineForRender.get(block.startOffset) ?? 0;\n\n let plugins: Pluggable[];\n if (state === 'streaming') {\n plugins = [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n { charDelay, fadeDuration: STREAM_FADE_DURATION, timelineElapsedMs },\n ],\n ];\n } else if (state === 'animating') {\n // Continue from previously rendered progress instead of restarting\n // or force-switching to fully revealed.\n plugins = [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n { charDelay, fadeDuration: STREAM_FADE_DURATION, timelineElapsedMs },\n ],\n ];\n } else {\n // Keep fade continuity for just-finished chars; avoid instant class\n // switch to stream-char-revealed that would cancel in-flight fades.\n plugins = [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n { charDelay, fadeDuration: STREAM_FADE_DURATION, timelineElapsedMs },\n ],\n ];\n }\n\n return (\n <StreamdownBlock\n {...rest}\n components={components}\n key={`${generatedId}-${block.startOffset}`}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n })}\n </div>\n );\n});\n\nStreamdownRender.displayName = 'StreamdownRender';\n\nexport default StreamdownRender;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,MAAM,uBAAuB;AAE7B,SAAS,WAAW,MAAsB;AACxC,QAAO,CAAC,GAAG,KAAK,CAAC;;AAGnB,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU;AAEzC,MAAM,oBAAoB,GAAY,MAAwB;AAC5D,KAAI,MAAM,EAAG,QAAO;AAEpB,KAAI,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,EAAE,EAAE;AACxC,MAAI,CAAC,MAAM,QAAQ,EAAE,IAAI,CAAC,MAAM,QAAQ,EAAE,CAAE,QAAO;AACnD,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,OAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,IAC5B,KAAI,CAAC,iBAAiB,EAAE,IAAI,EAAE,GAAG,CAAE,QAAO;AAE5C,SAAO;;AAGT,KAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAE,QAAO;CAEzC,MAAM,QAAQ,OAAO,KAAK,EAAE;CAC5B,MAAM,QAAQ,OAAO,KAAK,EAAE;AAC5B,KAAI,MAAM,WAAW,MAAM,OAAQ,QAAO;AAE1C,MAAK,MAAM,OAAO,MAChB,KAAI,CAAC,iBAAiB,EAAE,MAAM,EAAE,KAAK,CAAE,QAAO;AAGhD,QAAO;;AAGT,MAAM,gBAAgB,YAAuB,eAAmC;CAC9E,MAAM,YAAY,MAAM,QAAQ,WAAW,GAAG,aAAa,CAAC,WAAW;CACvE,MAAM,YAAY,MAAM,QAAQ,WAAW,GAAG,aAAa,CAAC,WAAW;AAEvE,KAAI,UAAU,WAAW,UAAU,OAAQ,QAAO;AAClD,KAAI,UAAU,OAAO,UAAU,GAAI,QAAO;AAE1C,QAAO,iBAAiB,UAAU,MAAM,EAAE,EAAE,UAAU,MAAM,EAAE,CAAC;;AAGjE,MAAM,iBACJ,aACA,gBACY;AACZ,KAAI,gBAAgB,YAAa,QAAO;AACxC,KAAI,CAAC,eAAe,CAAC,YAAa,QAAO,CAAC,eAAe,CAAC;AAC1D,KAAI,YAAY,WAAW,YAAY,OAAQ,QAAO;AAEtD,MAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,IACtC,KAAI,CAAC,aAAa,YAAY,IAAI,YAAY,GAAG,CAAE,QAAO;AAG5D,QAAO;;AAGT,MAAM,oBAAoB,YAA0C;CAClE,MAAM,YAAY,OAAsB,QAAQ;AAEhD,KAAI,CAAC,cAAc,UAAU,SAAS,QAAQ,CAC5C,WAAU,UAAU;AAGtB,QAAO,UAAU;;AAGnB,MAAM,kBAAkB,MACrB,EAAE,UAAU,GAAG,WAAW;AACzB,QAAO,oBAAC;EAAS,GAAI;EAAO;GAAoB;IAEjD,WAAW,cACV,UAAU,aAAa,UAAU,YACjC,UAAU,eAAe,UAAU,cACnC,cAAc,UAAU,eAAe,UAAU,cAAc,IAC/D,cAAc,UAAU,eAAe,UAAU,cAAc,CAClE;AAED,gBAAgB,cAAc;AAE9B,MAAa,mBAAmB,MAAe,EAAE,UAAU,GAAG,WAAW;CACvE,MAAM,EAAE,wBAAwB,eAAe,oBAAoB;CACnE,MAAM,iBAAiB,mBAAmB,YAAY,GAAG;CACzD,MAAM,aAAa,uBAAuB;CAC1C,MAAM,oBAAoB,iBAAiB,0BAA0B,CAAC;CACtE,MAAM,gBAAgB,iBAAiB,0BAA0B,CAAC;CAClE,MAAM,cAAc,OAAO;CAC3B,MAAM,kBAAkB,uBACtB,OAAO,mBAAmB,WAAW,iBAAiB,IACtD,EAAE,QAAQ,uBAAuB,CAClC;CAED,MAAM,mBAAmB,cAAc;AACrC,SAAO,OAAO,gBAAgB;IAC7B,CAAC,gBAAgB,CAAC;CAErB,MAAMA,SAAsB,cAAc;EACxC,MAAM,SAAS,OAAO,MAAM,iBAAiB;EAC7C,IAAI,SAAS;AACb,SAAO,OAAO,KAAK,UAAU;GAC3B,MAAM,QAAQ;IAAE,SAAS,MAAM;IAAK,aAAa;IAAQ;AACzD,aAAU,MAAM,IAAI;AACpB,UAAO;IACP;IACD,CAAC,iBAAiB,CAAC;CAEtB,MAAM,EAAE,eAAe,cAAc,eAAe,OAAO;CAC3D,MAAM,wBAAwB,uBAA4B,IAAI,KAAK,CAAC;CACpE,MAAM,mBAAmB,uBAA4B,IAAI,KAAK,CAAC;CAC/D,MAAM,kBAAkB,OAAsB,KAAK;CAEnD,MAAM,WAAW,OAAO,gBAAgB,cAAc,KAAK,KAAK,GAAG,YAAY,KAAK;CACpF,MAAM,UACJ,gBAAgB,YAAY,OACxB,IACA,KAAK,IAAI,GAAG,KAAK,IAAI,WAAW,gBAAgB,SAAS,IAAI,CAAC;CAEpE,MAAM,oBAAoB,cAAc;EACtC,MAAM,uBAAO,IAAI,KAAqB;EACtC,MAAM,eAAe,iBAAiB;EACtC,MAAM,iBAAiB,sBAAsB;AAE7C,OAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,iBAAiB,WAAW,MAAM,QAAQ;GAChD,MAAM,gBAAgB,eAAe,IAAI,MAAM,YAAY,IAAI;GAC/D,MAAM,cAAc,aAAa,IAAI,MAAM,YAAY;GACvD,MAAM,kBAAkB,KAAK,IAAI,IAAI,iBAAiB,KAAK,UAAU;AAErE,OAAI,gBAAgB,UAAa,iBAAiB,eAAe;AAC/D,SAAK,IAAI,MAAM,aAAa,gBAAgB;AAC5C;;GAGF,MAAM,gBAAgB,cAAc;GAEpC,MAAM,aAAa,KAAK,IAAI,GAAG,kBAAkB,YAAY,EAAE;AAC/D,QAAK,IAAI,MAAM,aAAa,KAAK,IAAI,eAAe,WAAW,CAAC;;AAGlE,SAAO;IACN;EAAC;EAAQ;EAAW;EAAQ,CAAC;AAEhC,iBAAgB;EACd,MAAM,gCAAgB,IAAI,KAAqB;AAC/C,OAAK,MAAM,SAAS,OAClB,eAAc,IAAI,MAAM,aAAa,WAAW,MAAM,QAAQ,CAAC;AAEjE,wBAAsB,UAAU;AAChC,mBAAiB,UAAU;AAC3B,kBAAgB,UAAU,OAAO,gBAAgB,cAAc,KAAK,KAAK,GAAG,YAAY,KAAK;IAC5F,CAAC,QAAQ,kBAAkB,CAAC;AAE/B,QACE,oBAAC;EAAI,WAAW,OAAO;YACpB,OAAO,KAAK,OAAO,UAAU;GAC5B,MAAM,QAAQ,cAAc,MAAM;AAClC,OAAI,UAAU,SAAU,QAAO;GAC/B,MAAM,oBAAoB,kBAAkB,IAAI,MAAM,YAAY,IAAI;GAEtE,IAAIC;AACJ,OAAI,UAAU,YACZ,WAAU,CACR,GAAG,mBACH,CACE,sBACA;IAAE;IAAW,cAAc;IAAsB;IAAmB,CACrE,CACF;YACQ,UAAU,YAGnB,WAAU,CACR,GAAG,mBACH,CACE,sBACA;IAAE;IAAW,cAAc;IAAsB;IAAmB,CACrE,CACF;OAID,WAAU,CACR,GAAG,mBACH,CACE,sBACA;IAAE;IAAW,cAAc;IAAsB;IAAmB,CACrE,CACF;AAGH,UACE,8BAAC;IACC,GAAI;IACQ;IACZ,KAAK,GAAG,YAAY,GAAG,MAAM;IAC7B,eAAe;IACA;MAEd,MAAM,QACS;IAEpB;GACE;EAER;AAEF,iBAAiB,cAAc;AAE/B,+BAAe"}
1
+ {"version":3,"file":"StreamdownRender.mjs","names":["REVEALED_STREAM_PLUGIN: Pluggable","blocks: BlockInfo[]","plugins: Pluggable[]"],"sources":["../../../src/Markdown/SyntaxMarkdown/StreamdownRender.tsx"],"sourcesContent":["'use client';\n\nimport { marked } from 'marked';\nimport {\n memo,\n Profiler,\n type ProfilerOnRenderCallback,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n} from 'react';\nimport Markdown, { type Options } from 'react-markdown';\nimport remend from 'remend';\nimport type { Pluggable, PluggableList } from 'unified';\n\nimport {\n useMarkdownComponents,\n useMarkdownContent,\n useMarkdownRehypePlugins,\n useMarkdownRemarkPlugins,\n} from '@/hooks/useMarkdown';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\nimport { rehypeStreamAnimated } from '@/Markdown/plugins/rehypeStreamAnimated';\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\n\nimport { resolveBlockAnimationMeta } from './streamAnimationMeta';\nimport { styles } from './style';\nimport { useSmoothStreamContent } from './useSmoothStreamContent';\nimport { type BlockInfo, useStreamQueue } from './useStreamQueue';\n\nconst STREAM_FADE_DURATION = 280;\nconst REVEALED_STREAM_PLUGIN: Pluggable = [rehypeStreamAnimated, { revealed: true }];\n\nfunction countChars(text: string): number {\n return [...text].length;\n}\n\nfunction getNow(): number {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isDeepEqualValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqualValue(a[i], b[i])) return false;\n }\n return true;\n }\n\n if (!isRecord(a) || !isRecord(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!isDeepEqualValue(a[key], b[key])) return false;\n }\n\n return true;\n};\n\nconst isSamePlugin = (prevPlugin: Pluggable, nextPlugin: Pluggable): boolean => {\n const prevTuple = Array.isArray(prevPlugin) ? prevPlugin : [prevPlugin];\n const nextTuple = Array.isArray(nextPlugin) ? nextPlugin : [nextPlugin];\n\n if (prevTuple.length !== nextTuple.length) return false;\n if (prevTuple[0] !== nextTuple[0]) return false;\n\n return isDeepEqualValue(prevTuple.slice(1), nextTuple.slice(1));\n};\n\nconst isSamePlugins = (\n prevPlugins?: PluggableList | null,\n nextPlugins?: PluggableList | null,\n): boolean => {\n if (prevPlugins === nextPlugins) return true;\n if (!prevPlugins || !nextPlugins) return !prevPlugins && !nextPlugins;\n if (prevPlugins.length !== nextPlugins.length) return false;\n\n for (let i = 0; i < prevPlugins.length; i++) {\n if (!isSamePlugin(prevPlugins[i], nextPlugins[i])) return false;\n }\n\n return true;\n};\n\nconst useStablePlugins = (plugins: PluggableList): PluggableList => {\n const stableRef = useRef<PluggableList>(plugins);\n\n if (!isSamePlugins(stableRef.current, plugins)) {\n stableRef.current = plugins;\n }\n\n return stableRef.current;\n};\n\nconst StreamdownBlock = memo<Options>(\n ({ children, ...rest }) => {\n return <Markdown {...rest}>{children}</Markdown>;\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.components === nextProps.components &&\n isSamePlugins(prevProps.rehypePlugins, nextProps.rehypePlugins) &&\n isSamePlugins(prevProps.remarkPlugins, nextProps.remarkPlugins),\n);\n\nStreamdownBlock.displayName = 'StreamdownBlock';\n\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const { streamSmoothingPreset = 'balanced' } = useMarkdownContext();\n const profiler = useStreamdownProfiler();\n const escapedContent = useMarkdownContent(children || '');\n const components = useMarkdownComponents();\n const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());\n const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());\n const generatedId = useId();\n const smoothedContent = useSmoothStreamContent(\n typeof escapedContent === 'string' ? escapedContent : '',\n { preset: streamSmoothingPreset },\n );\n\n const processedContentResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const value = remend(smoothedContent);\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [profiler, smoothedContent]);\n const processedContent = processedContentResult.value;\n\n const blocksResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const tokens = marked.lexer(processedContent);\n let offset = 0;\n\n const value = tokens.map((token) => {\n const block = { content: token.raw, startOffset: offset };\n offset += token.raw.length;\n return block;\n });\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [processedContent, profiler]);\n const blocks: BlockInfo[] = blocksResult.value;\n\n const { getBlockState, charDelay } = useStreamQueue(blocks);\n const prevBlockCharCountRef = useRef<Map<number, number>>(new Map());\n const blockCharDelayRef = useRef<Map<number, number>>(new Map());\n const blockTimelineRef = useRef<Map<number, number>>(new Map());\n const lastRenderTsRef = useRef<number | null>(null);\n\n const renderTs = getNow();\n const frameDt =\n lastRenderTsRef.current === null\n ? 0\n : Math.max(0, Math.min(renderTs - lastRenderTsRef.current, 120));\n\n const timelineResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const next = new Map<number, number>();\n const prevTimeline = blockTimelineRef.current;\n const prevCharCounts = prevBlockCharCountRef.current;\n\n for (const block of blocks) {\n const blockCharCount = countChars(block.content);\n const prevCharCount = prevCharCounts.get(block.startOffset) ?? 0;\n const prevElapsed = prevTimeline.get(block.startOffset);\n const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);\n\n if (prevElapsed === undefined || blockCharCount < prevCharCount) {\n next.set(block.startOffset, latestCharStart);\n continue;\n }\n\n const elapsedByTime = prevElapsed + frameDt;\n // Avoid huge hidden backlog when stream updates in bursts.\n const minElapsed = Math.max(0, latestCharStart - charDelay * 2);\n next.set(block.startOffset, Math.max(elapsedByTime, minElapsed));\n }\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value: next,\n };\n }, [blocks, charDelay, frameDt, profiler]);\n const timelineForRender = timelineResult.value;\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: processedContentResult.durationMs,\n name: 'content-normalize',\n textLength: processedContent.length,\n });\n }, [processedContent.length, processedContentResult.durationMs, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: blocksResult.durationMs,\n itemCount: blocks.length,\n name: 'block-lex',\n textLength: processedContent.length,\n });\n }, [blocks.length, blocksResult.durationMs, processedContent.length, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: timelineResult.durationMs,\n itemCount: blocks.length,\n name: 'block-timeline',\n textLength: processedContent.length,\n });\n }, [blocks.length, processedContent.length, profiler, timelineResult.durationMs]);\n\n const blockAnimationMetaResult = useMemo(() => {\n const nextBlockCharDelay = new Map<number, number>();\n const blockAnimationMeta = new Map<number, ReturnType<typeof resolveBlockAnimationMeta>>();\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n const timelineElapsedMs = timelineForRender.get(block.startOffset) ?? 0;\n const animationMeta = resolveBlockAnimationMeta({\n blockCharCount: countChars(block.content),\n currentCharDelay: charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n previousCharDelay: blockCharDelayRef.current.get(block.startOffset),\n state,\n timelineElapsedMs,\n });\n\n nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);\n blockAnimationMeta.set(block.startOffset, animationMeta);\n }\n\n return {\n blockAnimationMeta,\n blockCharDelay: nextBlockCharDelay,\n };\n }, [blocks, charDelay, getBlockState, timelineForRender]);\n\n useEffect(() => {\n const nextCharCount = new Map<number, number>();\n for (const block of blocks) {\n nextCharCount.set(block.startOffset, countChars(block.content));\n }\n blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;\n prevBlockCharCountRef.current = nextCharCount;\n blockTimelineRef.current = timelineForRender;\n lastRenderTsRef.current = getNow();\n }, [blockAnimationMetaResult.blockCharDelay, blocks, timelineForRender]);\n\n const handleRootRender = useCallback<ProfilerOnRenderCallback>(\n (_, phase, actualDuration, baseDuration) => {\n profiler?.recordRootCommit({\n actualDuration,\n baseDuration,\n blockCount: blocks.length,\n phase,\n textLength: processedContent.length,\n });\n },\n [blocks.length, processedContent.length, profiler],\n );\n\n const handleBlockRender = useCallback<ProfilerOnRenderCallback>(\n (id, phase, actualDuration, baseDuration) => {\n if (!profiler) return;\n\n const [, indexText, offsetText] = id.split(':');\n const blockIndex = Number(indexText);\n\n if (!Number.isFinite(blockIndex)) return;\n\n const block = blocks[blockIndex];\n if (!block) return;\n\n profiler.recordBlockCommit({\n actualDuration,\n baseDuration,\n blockChars: countChars(block.content),\n blockIndex,\n blockKey: offsetText ?? String(block.startOffset),\n phase,\n state: getBlockState(blockIndex),\n });\n },\n [blocks, getBlockState, profiler],\n );\n\n const content = (\n <div className={styles.animated}>\n {blocks.map((block, index) => {\n const state = getBlockState(index);\n if (state === 'queued') return null;\n const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);\n if (!animationMeta) return null;\n\n const plugins: Pluggable[] = animationMeta.settled\n ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN]\n : [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n {\n charDelay: animationMeta.charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n timelineElapsedMs: animationMeta.timelineElapsedMs,\n },\n ],\n ];\n\n const key = `${generatedId}-${block.startOffset}`;\n const blockNode = (\n <StreamdownBlock\n {...rest}\n components={components}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n\n if (!profiler) {\n return (\n <StreamdownBlock\n {...rest}\n components={components}\n key={key}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n }\n\n return (\n <Profiler\n id={`streamdown-block:${index}:${block.startOffset}`}\n key={key}\n onRender={handleBlockRender}\n >\n {blockNode}\n </Profiler>\n );\n })}\n </div>\n );\n\n if (!profiler) return content;\n\n return (\n <Profiler id={'streamdown-root'} onRender={handleRootRender}>\n {content}\n </Profiler>\n );\n});\n\nStreamdownRender.displayName = 'StreamdownRender';\n\nexport default StreamdownRender;\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ //#region src/Markdown/SyntaxMarkdown/streamAnimationMeta.ts
2
+ const isActiveBlock = (state) => {
3
+ return state === "animating" || state === "streaming";
4
+ };
5
+ const resolveBlockAnimationMeta = ({ blockCharCount, currentCharDelay, fadeDuration, previousCharDelay, state, timelineElapsedMs }) => {
6
+ const charDelay = isActiveBlock(state) ? currentCharDelay : previousCharDelay ?? currentCharDelay;
7
+ const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);
8
+ const settled = state === "revealed" && timelineElapsedMs >= latestCharStart + fadeDuration;
9
+ return {
10
+ charDelay,
11
+ settled,
12
+ timelineElapsedMs: settled ? latestCharStart + fadeDuration : timelineElapsedMs
13
+ };
14
+ };
15
+
16
+ //#endregion
17
+ export { resolveBlockAnimationMeta };
18
+ //# sourceMappingURL=streamAnimationMeta.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamAnimationMeta.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/streamAnimationMeta.ts"],"sourcesContent":["import { type BlockState } from './useStreamQueue';\n\nexport interface ResolveBlockAnimationMetaOptions {\n blockCharCount: number;\n currentCharDelay: number;\n fadeDuration: number;\n previousCharDelay?: number;\n state: BlockState;\n timelineElapsedMs: number;\n}\n\nexport interface BlockAnimationMeta {\n charDelay: number;\n settled: boolean;\n timelineElapsedMs: number;\n}\n\nconst isActiveBlock = (state: BlockState) => {\n return state === 'animating' || state === 'streaming';\n};\n\nexport const resolveBlockAnimationMeta = ({\n blockCharCount,\n currentCharDelay,\n fadeDuration,\n previousCharDelay,\n state,\n timelineElapsedMs,\n}: ResolveBlockAnimationMetaOptions): BlockAnimationMeta => {\n const charDelay = isActiveBlock(state)\n ? currentCharDelay\n : (previousCharDelay ?? currentCharDelay);\n const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);\n const settled = state === 'revealed' && timelineElapsedMs >= latestCharStart + fadeDuration;\n\n return {\n charDelay,\n settled,\n timelineElapsedMs: settled ? latestCharStart + fadeDuration : timelineElapsedMs,\n };\n};\n"],"mappings":";AAiBA,MAAM,iBAAiB,UAAsB;AAC3C,QAAO,UAAU,eAAe,UAAU;;AAG5C,MAAa,6BAA6B,EACxC,gBACA,kBACA,cACA,mBACA,OACA,wBAC0D;CAC1D,MAAM,YAAY,cAAc,MAAM,GAClC,mBACC,qBAAqB;CAC1B,MAAM,kBAAkB,KAAK,IAAI,IAAI,iBAAiB,KAAK,UAAU;CACrE,MAAM,UAAU,UAAU,cAAc,qBAAqB,kBAAkB;AAE/E,QAAO;EACL;EACA;EACA,mBAAmB,UAAU,kBAAkB,eAAe;EAC/D"}