@lobehub/ui 5.10.4 → 5.10.5

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.
@@ -21,47 +21,73 @@ const shimmer = keyframes`
21
21
  `;
22
22
  const useStyles = createStyles(({ css, cssVar, isDarkMode }) => ({
23
23
  loadingBackdrop: css`
24
+ pointer-events: none;
25
+
24
26
  position: absolute;
27
+ z-index: 1;
25
28
  inset: 0;
26
29
 
27
30
  /* Subtle moving sheen so it doesn't look frozen. */
28
- background:
29
- linear-gradient(
30
- 90deg,
31
- transparent 0%,
32
- ${isDarkMode ? "rgba(255, 255, 255, 0.04)" : "rgba(0, 0, 0, 0.04)"} 50%,
33
- transparent 100%
34
- ),
35
- ${isDarkMode ? "#1f1f1f" : "#fafafa"};
31
+ background: linear-gradient(
32
+ 90deg,
33
+ transparent 0%,
34
+ ${isDarkMode ? "rgba(255, 255, 255, 0.04)" : "rgba(0, 0, 0, 0.04)"} 50%,
35
+ transparent 100%
36
+ );
36
37
  background-repeat: no-repeat;
37
- background-size:
38
- 200% 100%,
39
- 100% 100%;
38
+ background-size: 200% 100%;
40
39
 
41
40
  animation: ${shimmer} 1.6s ${cssVar.motionEaseInOut} infinite;
42
41
  `,
43
- loadingLabel: css`
44
- font-size: 13px;
42
+ loadingBadge: css`
43
+ position: absolute;
44
+ z-index: 2;
45
+ inset-block-start: 12px;
46
+ inset-inline-start: 12px;
47
+
48
+ display: inline-flex;
49
+ gap: 8px;
50
+ align-items: center;
51
+
52
+ padding-block: 4px;
53
+ padding-inline: 6px 10px;
54
+ border-radius: 999px;
55
+
56
+ font-size: 12px;
45
57
  color: ${cssVar.colorTextDescription};
46
- `,
47
- loadingRoot: css`
48
- position: relative;
49
58
 
59
+ background: ${cssVar.colorBgContainer};
60
+ backdrop-filter: blur(8px);
61
+ box-shadow: 0 0 0 1px ${cssVar.colorBorderSecondary};
62
+ `,
63
+ loadingSource: css`
64
+ pointer-events: none;
50
65
  overflow: hidden;
51
- display: flex;
52
- flex-direction: column;
53
- gap: 16px;
54
- align-items: center;
55
- justify-content: center;
66
+ height: 100%;
67
+
68
+ /* Faded out so the iframe transition feels like content lighting up,
69
+ not like one document jump-cutting to another. */
70
+ opacity: 0.45;
71
+
72
+ /* SyntaxHighlighter sets its own background; flatten so the shimmer
73
+ overlay reads cleanly on top. */
74
+ & [data-code-type='highlighter'] {
75
+ background: transparent;
76
+ box-shadow: none;
77
+ }
78
+
79
+ /* Tail-follow is layout-only — we anchor the scrollable element to
80
+ its scrollHeight via the ref + effect; CSS just keeps the
81
+ overflow hidden. */
82
+ & pre,
83
+ & code {
84
+ background: transparent !important;
85
+ }
56
86
  `,
57
- loadingStack: css`
87
+ loadingRoot: css`
58
88
  position: relative;
59
- z-index: 1;
60
-
61
- display: flex;
62
- flex-direction: column;
63
- gap: 16px;
64
- align-items: center;
89
+ overflow: hidden;
90
+ background: ${isDarkMode ? "#1f1f1f" : "#fafafa"};
65
91
  `,
66
92
  toolbar: cx(actionsHoverCls, css`
67
93
  position: absolute;
@@ -194,6 +220,13 @@ const HtmlPreview = memo(({ actionIconSize, actionsRender, animated, bodyRender,
194
220
  useEffect(() => {
195
221
  contentRef.current = trimmedChildren;
196
222
  }, [trimmedChildren]);
223
+ const loadingSourceRef = useRef(null);
224
+ useEffect(() => {
225
+ if (isStable) return;
226
+ const node = loadingSourceRef.current;
227
+ if (!node) return;
228
+ node.scrollTop = node.scrollHeight;
229
+ }, [trimmedChildren, isStable]);
197
230
  const getCopyContent = useCallback(() => contentRef.current, []);
198
231
  const handleDownload = useCallback(() => {
199
232
  downloadHtml(contentRef.current, fileName || "preview.html");
@@ -239,14 +272,29 @@ const HtmlPreview = memo(({ actionIconSize, actionsRender, animated, bodyRender,
239
272
  const loadingBody = useMemo(() => /* @__PURE__ */ jsxs("div", {
240
273
  className: styles.loadingRoot,
241
274
  style: { height: defaultHeight ?? 400 },
242
- children: [/* @__PURE__ */ jsx("div", { className: styles.loadingBackdrop }), /* @__PURE__ */ jsxs("div", {
243
- className: styles.loadingStack,
244
- children: [/* @__PURE__ */ jsx(NeuralNetworkLoading, { size: 64 }), /* @__PURE__ */ jsx("span", {
245
- className: styles.loadingLabel,
246
- children: "Preparing preview…"
247
- })]
248
- })]
249
- }), [defaultHeight, styles]);
275
+ children: [
276
+ /* @__PURE__ */ jsx("div", {
277
+ className: styles.loadingSource,
278
+ ref: loadingSourceRef,
279
+ children: /* @__PURE__ */ jsx(SyntaxHighlighter, {
280
+ animated,
281
+ language: "html",
282
+ variant: "borderless",
283
+ children: trimmedChildren
284
+ })
285
+ }),
286
+ /* @__PURE__ */ jsx("div", { className: styles.loadingBackdrop }),
287
+ /* @__PURE__ */ jsxs("div", {
288
+ className: styles.loadingBadge,
289
+ children: [/* @__PURE__ */ jsx(NeuralNetworkLoading, { size: 16 }), /* @__PURE__ */ jsx("span", { children: "Preparing preview…" })]
290
+ })
291
+ ]
292
+ }), [
293
+ animated,
294
+ defaultHeight,
295
+ styles,
296
+ trimmedChildren
297
+ ]);
250
298
  const defaultBody = effectiveMode === "preview" ? isStable ? iframeBody : loadingBody : sourceBody;
251
299
  const body = useMemo(() => {
252
300
  if (!bodyRender) return defaultBody;
@@ -1 +1 @@
1
- {"version":3,"file":"HtmlPreview.mjs","names":["Flexbox"],"sources":["../../src/HtmlPreview/HtmlPreview.tsx"],"sourcesContent":["'use client';\n\nimport { createStyles, cx, keyframes } from 'antd-style';\nimport { Download, Expand } from 'lucide-react';\nimport { memo, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport CopyButton from '@/CopyButton';\nimport { Flexbox } from '@/Flex';\nimport { actionsHoverCls, variants } from '@/Highlighter/style';\nimport SyntaxHighlighter from '@/Highlighter/SyntaxHighlighter';\nimport NeuralNetworkLoading from '@/NeuralNetworkLoading';\nimport Segmented from '@/Segmented';\nimport { stopPropagation } from '@/utils/dom';\nimport { downloadBlob } from '@/utils/downloadBlob';\n\nimport { containsScript, DEFAULT_HEIGHT, isFullHtmlDocument, isHtmlContentClosed } from './const';\nimport HtmlPreviewIframe from './Iframe';\nimport type { HtmlPreviewMode, HtmlPreviewProps } from './type';\n\n// Sheen sweep direction: left → right.\n// `background-position` works inversely from \"where the image is drawn\":\n// at `200%` the over-sized gradient starts off to the left of the\n// container, at `-200%` it ends off to the right — so animating\n// 200% → -200% moves the visible bright spot from left to right.\nconst shimmer = keyframes`\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n`;\n\nconst useStyles = createStyles(({ css, cssVar, isDarkMode }) => ({\n loadingBackdrop: css`\n position: absolute;\n inset: 0;\n\n /* Subtle moving sheen so it doesn't look frozen. */\n background:\n linear-gradient(\n 90deg,\n transparent 0%,\n ${isDarkMode ? 'rgba(255, 255, 255, 0.04)' : 'rgba(0, 0, 0, 0.04)'} 50%,\n transparent 100%\n ),\n ${isDarkMode ? '#1f1f1f' : '#fafafa'};\n background-repeat: no-repeat;\n background-size:\n 200% 100%,\n 100% 100%;\n\n animation: ${shimmer} 1.6s ${cssVar.motionEaseInOut} infinite;\n `,\n loadingLabel: css`\n font-size: 13px;\n color: ${cssVar.colorTextDescription};\n `,\n loadingRoot: css`\n position: relative;\n\n overflow: hidden;\n display: flex;\n flex-direction: column;\n gap: 16px;\n align-items: center;\n justify-content: center;\n `,\n loadingStack: css`\n position: relative;\n z-index: 1;\n\n display: flex;\n flex-direction: column;\n gap: 16px;\n align-items: center;\n `,\n // Inline top-right toolbar. Tagged with `actionsHoverCls` so the Highlighter\n // container's `&:hover .${actionsHoverCls} { opacity: 1 }` rule flips it\n // in/out as the user moves over the preview — same UX as the regular code\n // block actions.\n toolbar: cx(\n actionsHoverCls,\n css`\n position: absolute;\n z-index: 2;\n inset-block-start: 8px;\n inset-inline-end: 8px;\n\n padding: 4px;\n border-radius: ${cssVar.borderRadiusLG};\n\n opacity: 0;\n background: ${cssVar.colorBgContainer};\n backdrop-filter: blur(8px);\n box-shadow: 0 0 0 1px ${cssVar.colorBorderSecondary};\n\n transition: opacity 0.2s ${cssVar.motionEaseOut};\n\n &:focus-within {\n opacity: 1;\n }\n `,\n ),\n}));\n\nconst themeBackground = (theme?: 'light' | 'dark') => {\n if (theme === 'dark') return '#1f1f1f';\n if (theme === 'light') return '#ffffff';\n return undefined;\n};\n\nconst downloadHtml = async (content: string, fileName: string) => {\n const blob = new Blob([content], { type: 'text/html;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n try {\n await downloadBlob(url, fileName);\n } finally {\n URL.revokeObjectURL(url);\n }\n};\n\nconst HtmlPreview = memo<HtmlPreviewProps>(\n ({\n actionIconSize,\n actionsRender,\n animated,\n bodyRender,\n children,\n className,\n classNames,\n copyable = true,\n defaultHeight,\n defaultMode = 'preview',\n downloadable = true,\n fileName,\n language = 'html',\n onExpand,\n sandbox,\n shadow,\n streamingMode = 'auto',\n style,\n styles: customStyles,\n theme,\n variant = 'filled',\n // `fullFeatured` / `showLanguage` / `defaultExpand` are accepted for API\n // compatibility with the rest of the Pre family but no longer drive a\n // separate header — the inline toolbar is always rendered.\n fullFeatured: _fullFeatured,\n showLanguage: _showLanguage,\n defaultExpand: _defaultExpand,\n ...rest\n }) => {\n const trimmedChildren = useMemo(() => (children || '').trim(), [children]);\n const isFragment = useMemo(() => !isFullHtmlDocument(trimmedChildren), [trimmedChildren]);\n\n // Per-session tracking. Reset on `animated` edge false → true.\n const [scriptLocked, setScriptLocked] = useState(false);\n const [headClosed, setHeadClosed] = useState(false);\n const [liveCommitted, setLiveCommitted] = useState(false);\n const prevAnimatedRef = useRef(animated);\n const lastCommitRef = useRef(0);\n const pendingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const latestContentRef = useRef(trimmedChildren);\n useEffect(() => {\n latestContentRef.current = trimmedChildren;\n }, [trimmedChildren]);\n useEffect(() => {\n if (animated && !prevAnimatedRef.current) {\n setScriptLocked(false);\n setHeadClosed(false);\n setLiveCommitted(false);\n lastCommitRef.current = 0;\n if (pendingTimerRef.current) {\n clearTimeout(pendingTimerRef.current);\n pendingTimerRef.current = null;\n }\n }\n prevAnimatedRef.current = animated;\n }, [animated]);\n\n // Sticky script detection: once a `<script>` appears in this session,\n // auto mode locks into defer.\n useEffect(() => {\n if (animated && !scriptLocked && containsScript(trimmedChildren)) {\n setScriptLocked(true);\n }\n }, [trimmedChildren, animated, scriptLocked]);\n\n // Track whether the head section has closed. Until it does, the iframe\n // would render either nothing or invalid HTML (style tag mid-stream).\n // After `</head>` (or `</style>` as a fallback for documents that skip\n // an explicit head close) we know the visual baseline is locked in.\n useEffect(() => {\n if (animated && !headClosed) {\n const lowered = trimmedChildren.toLowerCase();\n if (lowered.includes('</head>') || lowered.includes('</style>')) {\n setHeadClosed(true);\n }\n }\n }, [trimmedChildren, animated, headClosed]);\n\n // Two-phase streaming commit:\n // Phase 1 (head still streaming) — don't mount iframe at all. There's\n // nothing meaningful to render and styles aren't applied yet.\n // Phase 2 (head closed, body streaming) — commit at most once every\n // ~250ms. Important: this is a TRUE throttle, not a debounce. The\n // pending timer is held in a ref so it survives effect re-runs;\n // when it fires it commits the latest content (via another ref)\n // and clears itself, allowing a fresh schedule. If chunks come in\n // faster than the throttle window (which they do at ~50/sec) we\n // still get a commit every 250ms instead of waiting for streaming\n // to pause.\n // On streaming end → flush immediately, cancel any pending timer.\n const [throttledContent, setThrottledContent] = useState(trimmedChildren);\n useEffect(() => {\n if (!animated) {\n if (pendingTimerRef.current) {\n clearTimeout(pendingTimerRef.current);\n pendingTimerRef.current = null;\n }\n lastCommitRef.current = Date.now();\n setThrottledContent(trimmedChildren);\n return;\n }\n if (!headClosed) return;\n\n const throttleMs = 250;\n const now = Date.now();\n const elapsed = now - lastCommitRef.current;\n\n if (elapsed >= throttleMs) {\n if (pendingTimerRef.current) {\n clearTimeout(pendingTimerRef.current);\n pendingTimerRef.current = null;\n }\n lastCommitRef.current = now;\n setThrottledContent(trimmedChildren);\n return;\n }\n\n // Schedule a future commit, but only if one isn't already pending —\n // every chunk arrival re-runs this effect; we don't want to reset\n // the timer each time (that's debounce, and during continuous\n // streaming it would never fire).\n if (pendingTimerRef.current === null) {\n pendingTimerRef.current = setTimeout(() => {\n lastCommitRef.current = Date.now();\n pendingTimerRef.current = null;\n setThrottledContent(latestContentRef.current);\n }, throttleMs - elapsed);\n }\n }, [trimmedChildren, animated, headClosed]);\n useEffect(\n () => () => {\n if (pendingTimerRef.current) clearTimeout(pendingTimerRef.current);\n },\n [],\n );\n\n // Live-streaming commitment is sticky for the rest of the session. The\n // decision is made the moment the head seals:\n // • `live` → commit unconditionally\n // • `auto` → commit only if no `<script>` has appeared yet (script-\n // bearing docs go down the defer path to avoid running setup() on\n // partial source)\n // • `defer` → never commit; wait for `</html>`\n // Sticky-ness matters for `auto`: if a `<script>` arrives *after* the\n // head has already closed and we've started live-streaming, we keep\n // streaming rather than yanking the rendered content back into a\n // loading state mid-flight. The shell→static swap at end of streaming\n // re-runs the document cleanly anyway.\n useEffect(() => {\n if (!animated || liveCommitted || !headClosed) return;\n if (streamingMode === 'live' || (streamingMode === 'auto' && !scriptLocked)) {\n setLiveCommitted(true);\n }\n }, [animated, headClosed, liveCommitted, scriptLocked, streamingMode]);\n\n // Streaming gate. The iframe can mount in three situations:\n // 1. content is no longer animating\n // 2. `</html>` has arrived\n // 3. live streaming has been committed this session\n const isStable = !animated || isHtmlContentClosed(trimmedChildren) || liveCommitted;\n\n const [mode, setMode] = useState<HtmlPreviewMode>(defaultMode);\n\n // Fragments cannot meaningfully render in preview — force source view.\n // For streaming content that's not yet stable we keep the user's mode\n // choice and substitute a loading placeholder in the body instead, so\n // the toggle UI doesn't flip back and forth as content arrives.\n const effectiveMode: HtmlPreviewMode = isFragment ? 'source' : mode;\n\n const contentRef = useRef(trimmedChildren);\n useEffect(() => {\n contentRef.current = trimmedChildren;\n }, [trimmedChildren]);\n\n const getCopyContent = useCallback(() => contentRef.current, []);\n\n const handleDownload = useCallback(() => {\n void downloadHtml(contentRef.current, fileName || 'preview.html');\n }, [fileName]);\n\n const handleExpand = useCallback(() => {\n onExpand?.(contentRef.current);\n }, [onExpand]);\n\n const background = themeBackground(theme);\n\n const sourceBody = useMemo(\n () => (\n <SyntaxHighlighter\n animated={animated}\n className={classNames?.content}\n language={'html'}\n style={{ height: '100%', ...customStyles?.content }}\n variant={variant}\n >\n {trimmedChildren}\n </SyntaxHighlighter>\n ),\n [animated, classNames?.content, customStyles?.content, trimmedChildren, variant],\n );\n\n const { styles } = useStyles();\n\n const iframeBody = useMemo(\n () => (\n <HtmlPreviewIframe\n animated={animated}\n background={background}\n className={classNames?.iframe}\n content={throttledContent}\n defaultHeight={defaultHeight}\n sandbox={sandbox}\n style={customStyles?.iframe}\n />\n ),\n [\n background,\n classNames?.iframe,\n customStyles?.iframe,\n defaultHeight,\n sandbox,\n throttledContent,\n ],\n );\n\n // Shown when the user is on preview mode but the iframe isn't ready\n // yet (Phase 1 of streaming: head still arriving). Holds the eventual\n // iframe height to avoid layout shift on mount.\n const loadingBody = useMemo(\n () => (\n <div className={styles.loadingRoot} style={{ height: defaultHeight ?? DEFAULT_HEIGHT }}>\n <div className={styles.loadingBackdrop} />\n <div className={styles.loadingStack}>\n <NeuralNetworkLoading size={64} />\n <span className={styles.loadingLabel}>Preparing preview…</span>\n </div>\n </div>\n ),\n [defaultHeight, styles],\n );\n\n const previewBody = isStable ? iframeBody : loadingBody;\n\n const defaultBody = effectiveMode === 'preview' ? previewBody : sourceBody;\n\n const body = useMemo(() => {\n if (!bodyRender) return defaultBody;\n return bodyRender({\n content: trimmedChildren,\n mode: effectiveMode,\n originalNode: defaultBody,\n });\n }, [bodyRender, defaultBody, effectiveMode, trimmedChildren]);\n\n const segmentOptions = useMemo(\n () => [\n { label: 'Preview', value: 'preview' as const },\n { label: 'Source', value: 'source' as const },\n ],\n [],\n );\n\n const iconSize = actionIconSize || 'small';\n\n const originalActions: ReactNode = (\n <>\n {!isFragment && (\n <Segmented\n options={segmentOptions}\n size={'small'}\n value={effectiveMode}\n onChange={(v) => setMode(v as HtmlPreviewMode)}\n />\n )}\n {copyable && <CopyButton content={getCopyContent} size={iconSize} />}\n {downloadable && (\n <ActionIcon\n icon={Download}\n size={iconSize}\n title={'Download HTML'}\n onClick={handleDownload}\n />\n )}\n {onExpand && (\n <ActionIcon\n icon={Expand}\n size={iconSize}\n title={'Open full preview'}\n onClick={handleExpand}\n />\n )}\n </>\n );\n\n const actions = actionsRender\n ? actionsRender({\n actionIconSize: iconSize,\n content: trimmedChildren,\n getContent: getCopyContent,\n mode: effectiveMode,\n originalNode: originalActions,\n setMode,\n })\n : originalActions;\n\n return (\n <div\n className={cx(variants({ shadow, variant }), className)}\n data-code-type=\"html-preview\"\n data-html-preview-language={language}\n style={style}\n {...rest}\n >\n <Flexbox\n horizontal\n align={'center'}\n className={cx(styles.toolbar, classNames?.header)}\n flex={'none'}\n gap={4}\n style={customStyles?.header}\n onClick={stopPropagation}\n >\n {actions}\n </Flexbox>\n {body}\n </div>\n );\n },\n);\n\nHtmlPreview.displayName = 'HtmlPreview';\n\nexport default HtmlPreview;\n"],"mappings":"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"}
1
+ {"version":3,"file":"HtmlPreview.mjs","names":["Flexbox"],"sources":["../../src/HtmlPreview/HtmlPreview.tsx"],"sourcesContent":["'use client';\n\nimport { createStyles, cx, keyframes } from 'antd-style';\nimport { Download, Expand } from 'lucide-react';\nimport { memo, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport CopyButton from '@/CopyButton';\nimport { Flexbox } from '@/Flex';\nimport { actionsHoverCls, variants } from '@/Highlighter/style';\nimport SyntaxHighlighter from '@/Highlighter/SyntaxHighlighter';\nimport NeuralNetworkLoading from '@/NeuralNetworkLoading';\nimport Segmented from '@/Segmented';\nimport { stopPropagation } from '@/utils/dom';\nimport { downloadBlob } from '@/utils/downloadBlob';\n\nimport { containsScript, DEFAULT_HEIGHT, isFullHtmlDocument, isHtmlContentClosed } from './const';\nimport HtmlPreviewIframe from './Iframe';\nimport type { HtmlPreviewMode, HtmlPreviewProps } from './type';\n\n// Sheen sweep direction: left → right.\n// `background-position` works inversely from \"where the image is drawn\":\n// at `200%` the over-sized gradient starts off to the left of the\n// container, at `-200%` it ends off to the right — so animating\n// 200% → -200% moves the visible bright spot from left to right.\nconst shimmer = keyframes`\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n`;\n\nconst useStyles = createStyles(({ css, cssVar, isDarkMode }) => ({\n loadingBackdrop: css`\n pointer-events: none;\n\n position: absolute;\n z-index: 1;\n inset: 0;\n\n /* Subtle moving sheen so it doesn't look frozen. */\n background: linear-gradient(\n 90deg,\n transparent 0%,\n ${isDarkMode ? 'rgba(255, 255, 255, 0.04)' : 'rgba(0, 0, 0, 0.04)'} 50%,\n transparent 100%\n );\n background-repeat: no-repeat;\n background-size: 200% 100%;\n\n animation: ${shimmer} 1.6s ${cssVar.motionEaseInOut} infinite;\n `,\n loadingBadge: css`\n position: absolute;\n z-index: 2;\n inset-block-start: 12px;\n inset-inline-start: 12px;\n\n display: inline-flex;\n gap: 8px;\n align-items: center;\n\n padding-block: 4px;\n padding-inline: 6px 10px;\n border-radius: 999px;\n\n font-size: 12px;\n color: ${cssVar.colorTextDescription};\n\n background: ${cssVar.colorBgContainer};\n backdrop-filter: blur(8px);\n box-shadow: 0 0 0 1px ${cssVar.colorBorderSecondary};\n `,\n // The streaming source visible during Phase 1 — heavily faded so it\n // reads as \"this is preview-pending content\" rather than the finished\n // article. Auto-follows the tail so the user can see new tokens land\n // even on slow models.\n loadingSource: css`\n pointer-events: none;\n overflow: hidden;\n height: 100%;\n\n /* Faded out so the iframe transition feels like content lighting up,\n not like one document jump-cutting to another. */\n opacity: 0.45;\n\n /* SyntaxHighlighter sets its own background; flatten so the shimmer\n overlay reads cleanly on top. */\n & [data-code-type='highlighter'] {\n background: transparent;\n box-shadow: none;\n }\n\n /* Tail-follow is layout-only — we anchor the scrollable element to\n its scrollHeight via the ref + effect; CSS just keeps the\n overflow hidden. */\n & pre,\n & code {\n background: transparent !important;\n }\n `,\n loadingRoot: css`\n position: relative;\n overflow: hidden;\n background: ${isDarkMode ? '#1f1f1f' : '#fafafa'};\n `,\n // Inline top-right toolbar. Tagged with `actionsHoverCls` so the Highlighter\n // container's `&:hover .${actionsHoverCls} { opacity: 1 }` rule flips it\n // in/out as the user moves over the preview — same UX as the regular code\n // block actions.\n toolbar: cx(\n actionsHoverCls,\n css`\n position: absolute;\n z-index: 2;\n inset-block-start: 8px;\n inset-inline-end: 8px;\n\n padding: 4px;\n border-radius: ${cssVar.borderRadiusLG};\n\n opacity: 0;\n background: ${cssVar.colorBgContainer};\n backdrop-filter: blur(8px);\n box-shadow: 0 0 0 1px ${cssVar.colorBorderSecondary};\n\n transition: opacity 0.2s ${cssVar.motionEaseOut};\n\n &:focus-within {\n opacity: 1;\n }\n `,\n ),\n}));\n\nconst themeBackground = (theme?: 'light' | 'dark') => {\n if (theme === 'dark') return '#1f1f1f';\n if (theme === 'light') return '#ffffff';\n return undefined;\n};\n\nconst downloadHtml = async (content: string, fileName: string) => {\n const blob = new Blob([content], { type: 'text/html;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n try {\n await downloadBlob(url, fileName);\n } finally {\n URL.revokeObjectURL(url);\n }\n};\n\nconst HtmlPreview = memo<HtmlPreviewProps>(\n ({\n actionIconSize,\n actionsRender,\n animated,\n bodyRender,\n children,\n className,\n classNames,\n copyable = true,\n defaultHeight,\n defaultMode = 'preview',\n downloadable = true,\n fileName,\n language = 'html',\n onExpand,\n sandbox,\n shadow,\n streamingMode = 'auto',\n style,\n styles: customStyles,\n theme,\n variant = 'filled',\n // `fullFeatured` / `showLanguage` / `defaultExpand` are accepted for API\n // compatibility with the rest of the Pre family but no longer drive a\n // separate header — the inline toolbar is always rendered.\n fullFeatured: _fullFeatured,\n showLanguage: _showLanguage,\n defaultExpand: _defaultExpand,\n ...rest\n }) => {\n const trimmedChildren = useMemo(() => (children || '').trim(), [children]);\n const isFragment = useMemo(() => !isFullHtmlDocument(trimmedChildren), [trimmedChildren]);\n\n // Per-session tracking. Reset on `animated` edge false → true.\n const [scriptLocked, setScriptLocked] = useState(false);\n const [headClosed, setHeadClosed] = useState(false);\n const [liveCommitted, setLiveCommitted] = useState(false);\n const prevAnimatedRef = useRef(animated);\n const lastCommitRef = useRef(0);\n const pendingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const latestContentRef = useRef(trimmedChildren);\n useEffect(() => {\n latestContentRef.current = trimmedChildren;\n }, [trimmedChildren]);\n useEffect(() => {\n if (animated && !prevAnimatedRef.current) {\n setScriptLocked(false);\n setHeadClosed(false);\n setLiveCommitted(false);\n lastCommitRef.current = 0;\n if (pendingTimerRef.current) {\n clearTimeout(pendingTimerRef.current);\n pendingTimerRef.current = null;\n }\n }\n prevAnimatedRef.current = animated;\n }, [animated]);\n\n // Sticky script detection: once a `<script>` appears in this session,\n // auto mode locks into defer.\n useEffect(() => {\n if (animated && !scriptLocked && containsScript(trimmedChildren)) {\n setScriptLocked(true);\n }\n }, [trimmedChildren, animated, scriptLocked]);\n\n // Track whether the head section has closed. Until it does, the iframe\n // would render either nothing or invalid HTML (style tag mid-stream).\n // After `</head>` (or `</style>` as a fallback for documents that skip\n // an explicit head close) we know the visual baseline is locked in.\n useEffect(() => {\n if (animated && !headClosed) {\n const lowered = trimmedChildren.toLowerCase();\n if (lowered.includes('</head>') || lowered.includes('</style>')) {\n setHeadClosed(true);\n }\n }\n }, [trimmedChildren, animated, headClosed]);\n\n // Two-phase streaming commit:\n // Phase 1 (head still streaming) — don't mount iframe at all. There's\n // nothing meaningful to render and styles aren't applied yet.\n // Phase 2 (head closed, body streaming) — commit at most once every\n // ~250ms. Important: this is a TRUE throttle, not a debounce. The\n // pending timer is held in a ref so it survives effect re-runs;\n // when it fires it commits the latest content (via another ref)\n // and clears itself, allowing a fresh schedule. If chunks come in\n // faster than the throttle window (which they do at ~50/sec) we\n // still get a commit every 250ms instead of waiting for streaming\n // to pause.\n // On streaming end → flush immediately, cancel any pending timer.\n const [throttledContent, setThrottledContent] = useState(trimmedChildren);\n useEffect(() => {\n if (!animated) {\n if (pendingTimerRef.current) {\n clearTimeout(pendingTimerRef.current);\n pendingTimerRef.current = null;\n }\n lastCommitRef.current = Date.now();\n setThrottledContent(trimmedChildren);\n return;\n }\n if (!headClosed) return;\n\n const throttleMs = 250;\n const now = Date.now();\n const elapsed = now - lastCommitRef.current;\n\n if (elapsed >= throttleMs) {\n if (pendingTimerRef.current) {\n clearTimeout(pendingTimerRef.current);\n pendingTimerRef.current = null;\n }\n lastCommitRef.current = now;\n setThrottledContent(trimmedChildren);\n return;\n }\n\n // Schedule a future commit, but only if one isn't already pending —\n // every chunk arrival re-runs this effect; we don't want to reset\n // the timer each time (that's debounce, and during continuous\n // streaming it would never fire).\n if (pendingTimerRef.current === null) {\n pendingTimerRef.current = setTimeout(() => {\n lastCommitRef.current = Date.now();\n pendingTimerRef.current = null;\n setThrottledContent(latestContentRef.current);\n }, throttleMs - elapsed);\n }\n }, [trimmedChildren, animated, headClosed]);\n useEffect(\n () => () => {\n if (pendingTimerRef.current) clearTimeout(pendingTimerRef.current);\n },\n [],\n );\n\n // Live-streaming commitment is sticky for the rest of the session. The\n // decision is made the moment the head seals:\n // • `live` → commit unconditionally\n // • `auto` → commit only if no `<script>` has appeared yet (script-\n // bearing docs go down the defer path to avoid running setup() on\n // partial source)\n // • `defer` → never commit; wait for `</html>`\n // Sticky-ness matters for `auto`: if a `<script>` arrives *after* the\n // head has already closed and we've started live-streaming, we keep\n // streaming rather than yanking the rendered content back into a\n // loading state mid-flight. The shell→static swap at end of streaming\n // re-runs the document cleanly anyway.\n useEffect(() => {\n if (!animated || liveCommitted || !headClosed) return;\n if (streamingMode === 'live' || (streamingMode === 'auto' && !scriptLocked)) {\n setLiveCommitted(true);\n }\n }, [animated, headClosed, liveCommitted, scriptLocked, streamingMode]);\n\n // Streaming gate. The iframe can mount in three situations:\n // 1. content is no longer animating\n // 2. `</html>` has arrived\n // 3. live streaming has been committed this session\n const isStable = !animated || isHtmlContentClosed(trimmedChildren) || liveCommitted;\n\n const [mode, setMode] = useState<HtmlPreviewMode>(defaultMode);\n\n // Fragments cannot meaningfully render in preview — force source view.\n // For streaming content that's not yet stable we keep the user's mode\n // choice and substitute a loading placeholder in the body instead, so\n // the toggle UI doesn't flip back and forth as content arrives.\n const effectiveMode: HtmlPreviewMode = isFragment ? 'source' : mode;\n\n const contentRef = useRef(trimmedChildren);\n useEffect(() => {\n contentRef.current = trimmedChildren;\n }, [trimmedChildren]);\n\n // Tail-follow the streaming source visible during Phase 1 — anchor\n // the scroll position to the latest tokens so a slow model's output\n // doesn't sit pinned to the document head while the user waits.\n const loadingSourceRef = useRef<HTMLDivElement | null>(null);\n useEffect(() => {\n if (isStable) return;\n const node = loadingSourceRef.current;\n if (!node) return;\n node.scrollTop = node.scrollHeight;\n }, [trimmedChildren, isStable]);\n\n const getCopyContent = useCallback(() => contentRef.current, []);\n\n const handleDownload = useCallback(() => {\n void downloadHtml(contentRef.current, fileName || 'preview.html');\n }, [fileName]);\n\n const handleExpand = useCallback(() => {\n onExpand?.(contentRef.current);\n }, [onExpand]);\n\n const background = themeBackground(theme);\n\n const sourceBody = useMemo(\n () => (\n <SyntaxHighlighter\n animated={animated}\n className={classNames?.content}\n language={'html'}\n style={{ height: '100%', ...customStyles?.content }}\n variant={variant}\n >\n {trimmedChildren}\n </SyntaxHighlighter>\n ),\n [animated, classNames?.content, customStyles?.content, trimmedChildren, variant],\n );\n\n const { styles } = useStyles();\n\n const iframeBody = useMemo(\n () => (\n <HtmlPreviewIframe\n animated={animated}\n background={background}\n className={classNames?.iframe}\n content={throttledContent}\n defaultHeight={defaultHeight}\n sandbox={sandbox}\n style={customStyles?.iframe}\n />\n ),\n [\n background,\n classNames?.iframe,\n customStyles?.iframe,\n defaultHeight,\n sandbox,\n throttledContent,\n ],\n );\n\n // Shown when the user is on preview mode but the iframe isn't ready\n // yet (Phase 1 of streaming: head still arriving). Holds the eventual\n // iframe height to avoid layout shift on mount.\n //\n // Stream the raw source through `SyntaxHighlighter` at low opacity so\n // the user sees real progress on slow models (a 30-tps DeepSeek\n // pumping a ~5 KB head can otherwise sit on a static spinner for\n // 20+ seconds). A small \"Preparing preview…\" badge keeps the loading\n // state unambiguous. Tail-follow keeps the visible region anchored\n // to the latest tokens — see the `useEffect` below.\n const loadingBody = useMemo(\n () => (\n <div className={styles.loadingRoot} style={{ height: defaultHeight ?? DEFAULT_HEIGHT }}>\n <div className={styles.loadingSource} ref={loadingSourceRef}>\n <SyntaxHighlighter animated={animated} language={'html'} variant={'borderless'}>\n {trimmedChildren}\n </SyntaxHighlighter>\n </div>\n <div className={styles.loadingBackdrop} />\n <div className={styles.loadingBadge}>\n <NeuralNetworkLoading size={16} />\n <span>Preparing preview…</span>\n </div>\n </div>\n ),\n [animated, defaultHeight, styles, trimmedChildren],\n );\n\n const previewBody = isStable ? iframeBody : loadingBody;\n\n const defaultBody = effectiveMode === 'preview' ? previewBody : sourceBody;\n\n const body = useMemo(() => {\n if (!bodyRender) return defaultBody;\n return bodyRender({\n content: trimmedChildren,\n mode: effectiveMode,\n originalNode: defaultBody,\n });\n }, [bodyRender, defaultBody, effectiveMode, trimmedChildren]);\n\n const segmentOptions = useMemo(\n () => [\n { label: 'Preview', value: 'preview' as const },\n { label: 'Source', value: 'source' as const },\n ],\n [],\n );\n\n const iconSize = actionIconSize || 'small';\n\n const originalActions: ReactNode = (\n <>\n {!isFragment && (\n <Segmented\n options={segmentOptions}\n size={'small'}\n value={effectiveMode}\n onChange={(v) => setMode(v as HtmlPreviewMode)}\n />\n )}\n {copyable && <CopyButton content={getCopyContent} size={iconSize} />}\n {downloadable && (\n <ActionIcon\n icon={Download}\n size={iconSize}\n title={'Download HTML'}\n onClick={handleDownload}\n />\n )}\n {onExpand && (\n <ActionIcon\n icon={Expand}\n size={iconSize}\n title={'Open full preview'}\n onClick={handleExpand}\n />\n )}\n </>\n );\n\n const actions = actionsRender\n ? actionsRender({\n actionIconSize: iconSize,\n content: trimmedChildren,\n getContent: getCopyContent,\n mode: effectiveMode,\n originalNode: originalActions,\n setMode,\n })\n : originalActions;\n\n return (\n <div\n className={cx(variants({ shadow, variant }), className)}\n data-code-type=\"html-preview\"\n data-html-preview-language={language}\n style={style}\n {...rest}\n >\n <Flexbox\n horizontal\n align={'center'}\n className={cx(styles.toolbar, classNames?.header)}\n flex={'none'}\n gap={4}\n style={customStyles?.header}\n onClick={stopPropagation}\n >\n {actions}\n </Flexbox>\n {body}\n </div>\n );\n },\n);\n\nHtmlPreview.displayName = 'HtmlPreview';\n\nexport default HtmlPreview;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/ui",
3
- "version": "5.10.4",
3
+ "version": "5.10.5",
4
4
  "description": "Lobe UI is an open-source UI component library for building AIGC web apps",
5
5
  "keywords": [
6
6
  "lobehub",