Optimize WebUI streaming and long history rendering

Batch stream deltas, window long transcripts, lazy-load syntax highlighting, and refine activity/composer interactions.

Add title refresh retries plus tests for streaming, windowing, code blocks, and live activity behavior.
This commit is contained in:
Xubin Ren
2026-05-17 17:04:57 +08:00
parent 175b58e259
commit e5be4dac7a
30 changed files with 1551 additions and 282 deletions
+52 -38
View File
@@ -1,12 +1,8 @@
import { useCallback, useEffect, useState } from "react";
import { Suspense, lazy, useCallback, useState } from "react";
import { Check, Copy } from "lucide-react";
import { useTranslation } from "react-i18next";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import {
oneDark,
oneLight,
} from "react-syntax-highlighter/dist/esm/styles/prism";
import { useThemeValue } from "@/hooks/useTheme";
import { cn } from "@/lib/utils";
interface CodeBlockProps {
@@ -15,30 +11,59 @@ interface CodeBlockProps {
className?: string;
}
/** Read dark mode straight from the DOM — stays in sync with Tailwind's `dark:`. */
function useIsDark() {
const [isDark, setIsDark] = useState(() =>
typeof document !== "undefined"
? document.documentElement.classList.contains("dark")
: true,
interface HighlightedCodeProps {
language?: string;
code: string;
isDark: boolean;
}
const LazyHighlightedCode = lazy(async () => {
const [
{ default: SyntaxHighlighter },
{ default: oneDark },
{ default: oneLight },
] = await Promise.all([
import("react-syntax-highlighter/dist/esm/prism-async-light"),
import("react-syntax-highlighter/dist/esm/styles/prism/one-dark"),
import("react-syntax-highlighter/dist/esm/styles/prism/one-light"),
]);
return {
default({ language, code, isDark }: HighlightedCodeProps) {
return (
<SyntaxHighlighter
language={language}
style={isDark ? oneDark : oneLight}
customStyle={{
margin: 0,
padding: "1rem",
fontSize: "0.875rem",
lineHeight: 1.6,
}}
PreTag="pre"
wrapLongLines
>
{code}
</SyntaxHighlighter>
);
},
};
});
function PlainCodeFallback({ code }: { code: string }) {
return (
<pre
className="m-0 overflow-x-auto whitespace-pre-wrap p-4 font-mono text-sm leading-[1.6]"
>
<code>{code}</code>
</pre>
);
useEffect(() => {
const el = document.documentElement;
const observer = new MutationObserver(() => {
setIsDark(el.classList.contains("dark"));
});
observer.observe(el, { attributeFilter: ["class"] });
return () => observer.disconnect();
}, []);
return isDark;
}
export function CodeBlock({ language, code, className }: CodeBlockProps) {
const { t } = useTranslation();
const [copied, setCopied] = useState(false);
const isDark = useIsDark();
const isDark = useThemeValue() === "dark";
const onCopy = useCallback(() => {
if (!navigator.clipboard) return;
@@ -86,20 +111,9 @@ export function CodeBlock({ language, code, className }: CodeBlockProps) {
<span>{copied ? t("code.copied") : t("code.copy")}</span>
</button>
</div>
<SyntaxHighlighter
language={language}
style={isDark ? oneDark : oneLight}
customStyle={{
margin: 0,
padding: "1rem",
fontSize: "0.875rem",
lineHeight: 1.6,
}}
PreTag="pre"
wrapLongLines
>
{code}
</SyntaxHighlighter>
<Suspense fallback={<PlainCodeFallback code={code} />}>
<LazyHighlightedCode language={language} code={code} isDark={isDark} />
</Suspense>
</div>
);
}