+
+
+ {meta.displayLanguage}
+ {isStreaming ? (
+
+
+ Live
+
+ ) : null}
+
+
+
+
{children}
+
+ {statusText}
+
+ {meta.displayLanguage}
+ {formatLineCount(meta.lineCount)}
+ {isStreaming ? "Live" : "Updated just now"}
+
+
+
+ );
+});
diff --git a/apps/web/src/components/ChatMarkdown.tsx b/apps/web/src/components/ChatMarkdown.tsx
index 60e39a17..69d41aaa 100644
--- a/apps/web/src/components/ChatMarkdown.tsx
+++ b/apps/web/src/components/ChatMarkdown.tsx
@@ -1,32 +1,28 @@
-import { CheckIcon, CopyIcon } from "lucide-react";
-import React, {
+import {
Children,
- Suspense,
+ useDeferredValue,
isValidElement,
- use,
- useCallback,
memo,
useEffect,
useMemo,
- useRef,
useState,
type ReactNode,
} from "react";
import type { Components } from "react-markdown";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
-import { CodeHighlightErrorBoundary } from "./CodeHighlightErrorBoundary";
+import { ChatCodePreviewCard } from "./ChatCodePreviewCard";
import { useAppSettings } from "../appSettings";
import { openFileReference } from "../fileOpen";
import { useFileViewNavigation } from "~/hooks/useFileViewNavigation";
import { resolveDiffThemeName, type DiffThemeName } from "../lib/diffRendering";
import {
- extractFenceLanguage,
getCachedHighlightedHtml,
getHighlighterPromise,
renderHighlightedCodeHtml,
setCachedHighlightedHtml,
} from "../lib/syntaxHighlighting";
+import { buildStreamingCodePreviewMeta } from "../lib/chatCodePreview";
import { useTheme } from "../hooks/useTheme";
import { resolveMarkdownFileLinkTarget } from "../markdown-links";
import { readNativeApi } from "../nativeApi";
@@ -73,94 +69,90 @@ function extractCodeBlock(
};
}
-function MarkdownCodeBlock({ code, children }: { code: string; children: ReactNode }) {
- const [copied, setCopied] = useState(false);
- const copiedTimerRef = useRef