mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-31 02:14:58 +00:00
fix(vscode): reduce webview streaming re-render churn (#1994)
Chat components subscribed to the entire chat store, so every streaming delta re-rendered every assistant message. Subscribe to narrow store slices and memoize ChatMessage so settled messages no longer re-render on each delta. Cap getImageDataUri at 10MB, matching the media picker, so oversized local images are not inlined into the webview DOM.
This commit is contained in:
parent
6dd4fd3368
commit
beeb964393
4 changed files with 16 additions and 9 deletions
|
|
@ -165,6 +165,10 @@ const getImageDataUri: Handler<FilePathParams, string | null> = async ({ filePat
|
|||
const mime = IMAGE_MIME_TYPES[path.extname(resolved.relativePath).toLowerCase()];
|
||||
if (!mime) return null;
|
||||
try {
|
||||
// Same cap as the media picker: an oversized image inlined as a data URI
|
||||
// would live in the webview DOM (and its decoded bitmap) forever.
|
||||
const stat = await vscode.workspace.fs.stat(resolved.uri);
|
||||
if (stat.size > 10 * 1024 * 1024) return null;
|
||||
const bytes = await vscode.workspace.fs.readFile(resolved.uri);
|
||||
return `data:${mime};base64,${Buffer.from(bytes).toString("base64")}`;
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ function ScrollButton() {
|
|||
}
|
||||
|
||||
function MessageList() {
|
||||
const { messages, isStreaming } = useChatStore();
|
||||
const messages = useChatStore((s) => s.messages);
|
||||
const isStreaming = useChatStore((s) => s.isStreaming);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -43,9 +44,9 @@ function MessageList() {
|
|||
}
|
||||
|
||||
export function ChatArea() {
|
||||
const { messages } = useChatStore();
|
||||
const messageCount = useChatStore((s) => s.messages.length);
|
||||
|
||||
if (messages.length === 0) {
|
||||
if (messageCount === 0) {
|
||||
return (
|
||||
<div className="h-full flex items-center justify-center relative">
|
||||
<WelcomeScreen />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, Fragment } from "react";
|
||||
import { useState, Fragment, memo } from "react";
|
||||
import { IconLoader3, IconGitFork } from "@tabler/icons-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Content } from "@/lib/content";
|
||||
|
|
@ -146,7 +146,9 @@ interface ForkButtonProps {
|
|||
function ForkButton({ turnIndex, className }: ForkButtonProps) {
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
const [isForking, setIsForking] = useState(false);
|
||||
const { sessionId, isStreaming, loadSession } = useChatStore();
|
||||
const sessionId = useChatStore((s) => s.sessionId);
|
||||
const isStreaming = useChatStore((s) => s.isStreaming);
|
||||
const loadSession = useChatStore((s) => s.loadSession);
|
||||
|
||||
const handleFork = () => {
|
||||
if (!sessionId || turnIndex < 0) return;
|
||||
|
|
@ -234,7 +236,7 @@ function UserMessage({ message }: { message: ChatMessageType }) {
|
|||
|
||||
function AssistantMessage({ message, turnIndex, isStreaming }: { message: ChatMessageType; turnIndex?: number; isStreaming?: boolean }) {
|
||||
const [previewMedia, setPreviewMedia] = useState<string | null>(null);
|
||||
const { isCompacting } = useChatStore();
|
||||
const isCompacting = useChatStore((s) => s.isCompacting);
|
||||
|
||||
const steps = message.steps || [];
|
||||
const hasSteps = steps.length > 0;
|
||||
|
|
@ -330,9 +332,9 @@ function hasMessageContent(message: ChatMessageType): boolean {
|
|||
return message.steps?.some((s) => s.items.length > 0) ?? false;
|
||||
}
|
||||
|
||||
export function ChatMessage({ message, turnIndex, isStreaming }: ChatMessageProps) {
|
||||
export const ChatMessage = memo(function ChatMessage({ message, turnIndex, isStreaming }: ChatMessageProps) {
|
||||
if (message.role === "user") {
|
||||
return <UserMessage message={message} />;
|
||||
}
|
||||
return <AssistantMessage message={message} turnIndex={turnIndex} isStreaming={isStreaming} />;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { IconLoader2 } from "@tabler/icons-react";
|
|||
import { useChatStore } from "@/stores";
|
||||
|
||||
export function CompactionCard() {
|
||||
const { isCompacting } = useChatStore();
|
||||
const isCompacting = useChatStore((s) => s.isCompacting);
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-muted/20 overflow-hidden">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue