diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index 9ff67732fa..6b8c07dcaa 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -16,6 +16,7 @@ "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vscode/webview-ui-toolkit": "^1.4.0", + "fast-deep-equal": "^3.1.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^9.0.1", diff --git a/webview-ui/package.json b/webview-ui/package.json index 6d78fe6a61..624b22d8df 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -11,6 +11,7 @@ "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vscode/webview-ui-toolkit": "^1.4.0", + "fast-deep-equal": "^3.1.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^9.0.1", diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index f2cbfb1326..ce85cb53f2 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -1,7 +1,6 @@ -import { useCallback, useEffect, useMemo, useState } from "react" +import { useCallback, useEffect, useState } from "react" import { useEvent } from "react-use" import { ExtensionMessage } from "../../src/shared/ExtensionMessage" -import { normalizeApiConfiguration } from "./components/ApiOptions" import ChatView from "./components/ChatView" import HistoryView from "./components/HistoryView" import SettingsView from "./components/SettingsView" @@ -10,7 +9,7 @@ import { ExtensionStateContextProvider, useExtensionState } from "./context/Exte import { vscode } from "./utils/vscode" const AppContent = () => { - const { didHydrateState, showWelcome, apiConfiguration, shouldShowAnnouncement } = useExtensionState() + const { didHydrateState, showWelcome, shouldShowAnnouncement } = useExtensionState() const [showSettings, setShowSettings] = useState(false) const [showHistory, setShowHistory] = useState(false) const [showAnnouncement, setShowAnnouncement] = useState(false) @@ -39,10 +38,6 @@ const AppContent = () => { useEvent("message", handleMessage) - const { selectedModelInfo } = useMemo(() => { - return normalizeApiConfiguration(apiConfiguration) - }, [apiConfiguration]) - useEffect(() => { if (shouldShowAnnouncement) { setShowAnnouncement(true) @@ -70,8 +65,6 @@ const AppContent = () => { }} isHidden={showSettings || showHistory} showAnnouncement={showAnnouncement} - selectedModelSupportsImages={selectedModelInfo.supportsImages} - selectedModelSupportsPromptCache={selectedModelInfo.supportsPromptCache} hideAnnouncement={() => { setShowAnnouncement(false) }} diff --git a/webview-ui/src/components/Announcement.tsx b/webview-ui/src/components/Announcement.tsx index ac91d728a0..c454ac8ce7 100644 --- a/webview-ui/src/components/Announcement.tsx +++ b/webview-ui/src/components/Announcement.tsx @@ -1,5 +1,4 @@ import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { ApiConfiguration } from "../../../src/shared/api" import { memo } from "react" // import VSCodeButtonLink from "./VSCodeButtonLink" // import { getOpenRouterAuthUrl } from "./ApiOptions" @@ -8,13 +7,11 @@ import { memo } from "react" interface AnnouncementProps { version: string hideAnnouncement: () => void - apiConfiguration?: ApiConfiguration - vscodeUriScheme?: string } /* You must update the latestAnnouncementId in ClaudeDevProvider for new announcements to show to users. This new id will be compared with whats in state for the 'last announcement shown', and if it's different then the announcement will render. As soon as an announcement is shown, the id will be updated in state. This ensures that announcements are not shown more than once, even if the user doesn't close it themselves. */ -const Announcement = ({ version, hideAnnouncement, apiConfiguration, vscodeUriScheme }: AnnouncementProps) => { +const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => { return (
void } -const ChatRow = memo((props: ChatRowProps) => { - // we cannot return null as virtuoso does not support it, so we use a separate visibleMessages array to filter out messages that should not be rendered - return ( -
- -
- ) -}) +const ChatRow = memo( + (props: ChatRowProps) => { + // we cannot return null as virtuoso does not support it, so we use a separate visibleMessages array to filter out messages that should not be rendered + return ( +
+ +
+ ) + }, + // memo does shallow comparison of props, so we need to do deep comparison of arrays/objects whose properties might change + deepEqual +) export default ChatRow diff --git a/webview-ui/src/components/ChatView.tsx b/webview-ui/src/components/ChatView.tsx index c1548bbf59..2280440255 100644 --- a/webview-ui/src/components/ChatView.tsx +++ b/webview-ui/src/components/ChatView.tsx @@ -14,27 +14,19 @@ import ChatRow from "./ChatRow" import HistoryPreview from "./HistoryPreview" import TaskHeader from "./TaskHeader" import Thumbnails from "./Thumbnails" +import { normalizeApiConfiguration } from "./ApiOptions" interface ChatViewProps { isHidden: boolean showAnnouncement: boolean - selectedModelSupportsImages: boolean - selectedModelSupportsPromptCache: boolean hideAnnouncement: () => void showHistoryView: () => void } const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images -const ChatView = ({ - isHidden, - showAnnouncement, - selectedModelSupportsImages, - selectedModelSupportsPromptCache, - hideAnnouncement, - showHistoryView, -}: ChatViewProps) => { - const { version, claudeMessages: messages, taskHistory, apiConfiguration, uriScheme } = useExtensionState() +const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryView }: ChatViewProps) => { + const { version, claudeMessages: messages, taskHistory, apiConfiguration } = useExtensionState() //const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined const task = messages.length > 0 ? messages[0] : undefined // leaving this less safe version here since if the first message is not a task, then the extension is in a bad state and needs to be debugged (see ClaudeDev.abort) @@ -313,12 +305,16 @@ const ChatView = ({ startNewTask() }, [startNewTask]) + const { selectedModelInfo } = useMemo(() => { + return normalizeApiConfiguration(apiConfiguration) + }, [apiConfiguration]) + const selectImages = useCallback(() => { vscode.postMessage({ type: "selectImages" }) }, []) const shouldDisableImages = - !selectedModelSupportsImages || textAreaDisabled || selectedImages.length >= MAX_IMAGES_PER_MESSAGE + !selectedModelInfo.supportsImages || textAreaDisabled || selectedImages.length >= MAX_IMAGES_PER_MESSAGE const handlePaste = useCallback( async (e: React.ClipboardEvent) => { @@ -495,7 +491,7 @@ const ChatView = ({ task={task} tokensIn={apiMetrics.totalTokensIn} tokensOut={apiMetrics.totalTokensOut} - doesModelSupportPromptCache={selectedModelSupportsPromptCache} + doesModelSupportPromptCache={selectedModelInfo.supportsPromptCache} cacheWrites={apiMetrics.totalCacheWrites} cacheReads={apiMetrics.totalCacheReads} totalCost={apiMetrics.totalCost} @@ -503,14 +499,7 @@ const ChatView = ({ /> ) : ( <> - {showAnnouncement && ( - - )} + {showAnnouncement && }
0 ? undefined : 1 }}>

What can I do for you?

diff --git a/webview-ui/src/components/CodeBlock.tsx b/webview-ui/src/components/CodeBlock.tsx index ff68469fad..2bae7b6093 100644 --- a/webview-ui/src/components/CodeBlock.tsx +++ b/webview-ui/src/components/CodeBlock.tsx @@ -80,7 +80,7 @@ const StyledPre = styled.pre<{ theme: any }>` .join("")} ` -const CodeBlock = memo(function CodeBlock({ source }: { source?: string }) { +const CodeBlock = memo(({ source }: { source?: string }) => { const { theme } = useExtensionState() const [reactContent, setMarkdownSource] = useRemark({ remarkPlugins: [