mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Deep compare chatrow props to update when message changes
This commit is contained in:
1
webview-ui/package-lock.json
generated
1
webview-ui/package-lock.json
generated
@@ -16,6 +16,7 @@
|
|||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@vscode/webview-ui-toolkit": "^1.4.0",
|
"@vscode/webview-ui-toolkit": "^1.4.0",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@vscode/webview-ui-toolkit": "^1.4.0",
|
"@vscode/webview-ui-toolkit": "^1.4.0",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
import { useCallback, useEffect, useState } from "react"
|
||||||
import { useEvent } from "react-use"
|
import { useEvent } from "react-use"
|
||||||
import { ExtensionMessage } from "../../src/shared/ExtensionMessage"
|
import { ExtensionMessage } from "../../src/shared/ExtensionMessage"
|
||||||
import { normalizeApiConfiguration } from "./components/ApiOptions"
|
|
||||||
import ChatView from "./components/ChatView"
|
import ChatView from "./components/ChatView"
|
||||||
import HistoryView from "./components/HistoryView"
|
import HistoryView from "./components/HistoryView"
|
||||||
import SettingsView from "./components/SettingsView"
|
import SettingsView from "./components/SettingsView"
|
||||||
@@ -10,7 +9,7 @@ import { ExtensionStateContextProvider, useExtensionState } from "./context/Exte
|
|||||||
import { vscode } from "./utils/vscode"
|
import { vscode } from "./utils/vscode"
|
||||||
|
|
||||||
const AppContent = () => {
|
const AppContent = () => {
|
||||||
const { didHydrateState, showWelcome, apiConfiguration, shouldShowAnnouncement } = useExtensionState()
|
const { didHydrateState, showWelcome, shouldShowAnnouncement } = useExtensionState()
|
||||||
const [showSettings, setShowSettings] = useState(false)
|
const [showSettings, setShowSettings] = useState(false)
|
||||||
const [showHistory, setShowHistory] = useState(false)
|
const [showHistory, setShowHistory] = useState(false)
|
||||||
const [showAnnouncement, setShowAnnouncement] = useState(false)
|
const [showAnnouncement, setShowAnnouncement] = useState(false)
|
||||||
@@ -39,10 +38,6 @@ const AppContent = () => {
|
|||||||
|
|
||||||
useEvent("message", handleMessage)
|
useEvent("message", handleMessage)
|
||||||
|
|
||||||
const { selectedModelInfo } = useMemo(() => {
|
|
||||||
return normalizeApiConfiguration(apiConfiguration)
|
|
||||||
}, [apiConfiguration])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shouldShowAnnouncement) {
|
if (shouldShowAnnouncement) {
|
||||||
setShowAnnouncement(true)
|
setShowAnnouncement(true)
|
||||||
@@ -70,8 +65,6 @@ const AppContent = () => {
|
|||||||
}}
|
}}
|
||||||
isHidden={showSettings || showHistory}
|
isHidden={showSettings || showHistory}
|
||||||
showAnnouncement={showAnnouncement}
|
showAnnouncement={showAnnouncement}
|
||||||
selectedModelSupportsImages={selectedModelInfo.supportsImages}
|
|
||||||
selectedModelSupportsPromptCache={selectedModelInfo.supportsPromptCache}
|
|
||||||
hideAnnouncement={() => {
|
hideAnnouncement={() => {
|
||||||
setShowAnnouncement(false)
|
setShowAnnouncement(false)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||||
import { ApiConfiguration } from "../../../src/shared/api"
|
|
||||||
import { memo } from "react"
|
import { memo } from "react"
|
||||||
// import VSCodeButtonLink from "./VSCodeButtonLink"
|
// import VSCodeButtonLink from "./VSCodeButtonLink"
|
||||||
// import { getOpenRouterAuthUrl } from "./ApiOptions"
|
// import { getOpenRouterAuthUrl } from "./ApiOptions"
|
||||||
@@ -8,13 +7,11 @@ import { memo } from "react"
|
|||||||
interface AnnouncementProps {
|
interface AnnouncementProps {
|
||||||
version: string
|
version: string
|
||||||
hideAnnouncement: () => void
|
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.
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import CodeAccordian from "./CodeAccordian"
|
|||||||
import CodeBlock from "./CodeBlock"
|
import CodeBlock from "./CodeBlock"
|
||||||
import Terminal from "./Terminal"
|
import Terminal from "./Terminal"
|
||||||
import Thumbnails from "./Thumbnails"
|
import Thumbnails from "./Thumbnails"
|
||||||
|
import deepEqual from "fast-deep-equal"
|
||||||
|
|
||||||
interface ChatRowProps {
|
interface ChatRowProps {
|
||||||
message: ClaudeMessage
|
message: ClaudeMessage
|
||||||
@@ -17,7 +18,8 @@ interface ChatRowProps {
|
|||||||
handleSendStdin: (text: string) => void
|
handleSendStdin: (text: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChatRow = memo((props: ChatRowProps) => {
|
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
|
// 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 (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -27,7 +29,10 @@ const ChatRow = memo((props: ChatRowProps) => {
|
|||||||
<ChatRowContent {...props} />
|
<ChatRowContent {...props} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
// memo does shallow comparison of props, so we need to do deep comparison of arrays/objects whose properties might change
|
||||||
|
deepEqual
|
||||||
|
)
|
||||||
|
|
||||||
export default ChatRow
|
export default ChatRow
|
||||||
|
|
||||||
|
|||||||
@@ -14,27 +14,19 @@ import ChatRow from "./ChatRow"
|
|||||||
import HistoryPreview from "./HistoryPreview"
|
import HistoryPreview from "./HistoryPreview"
|
||||||
import TaskHeader from "./TaskHeader"
|
import TaskHeader from "./TaskHeader"
|
||||||
import Thumbnails from "./Thumbnails"
|
import Thumbnails from "./Thumbnails"
|
||||||
|
import { normalizeApiConfiguration } from "./ApiOptions"
|
||||||
|
|
||||||
interface ChatViewProps {
|
interface ChatViewProps {
|
||||||
isHidden: boolean
|
isHidden: boolean
|
||||||
showAnnouncement: boolean
|
showAnnouncement: boolean
|
||||||
selectedModelSupportsImages: boolean
|
|
||||||
selectedModelSupportsPromptCache: boolean
|
|
||||||
hideAnnouncement: () => void
|
hideAnnouncement: () => void
|
||||||
showHistoryView: () => void
|
showHistoryView: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images
|
const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images
|
||||||
|
|
||||||
const ChatView = ({
|
const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryView }: ChatViewProps) => {
|
||||||
isHidden,
|
const { version, claudeMessages: messages, taskHistory, apiConfiguration } = useExtensionState()
|
||||||
showAnnouncement,
|
|
||||||
selectedModelSupportsImages,
|
|
||||||
selectedModelSupportsPromptCache,
|
|
||||||
hideAnnouncement,
|
|
||||||
showHistoryView,
|
|
||||||
}: ChatViewProps) => {
|
|
||||||
const { version, claudeMessages: messages, taskHistory, apiConfiguration, uriScheme } = useExtensionState()
|
|
||||||
|
|
||||||
//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined
|
//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)
|
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()
|
||||||
}, [startNewTask])
|
}, [startNewTask])
|
||||||
|
|
||||||
|
const { selectedModelInfo } = useMemo(() => {
|
||||||
|
return normalizeApiConfiguration(apiConfiguration)
|
||||||
|
}, [apiConfiguration])
|
||||||
|
|
||||||
const selectImages = useCallback(() => {
|
const selectImages = useCallback(() => {
|
||||||
vscode.postMessage({ type: "selectImages" })
|
vscode.postMessage({ type: "selectImages" })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const shouldDisableImages =
|
const shouldDisableImages =
|
||||||
!selectedModelSupportsImages || textAreaDisabled || selectedImages.length >= MAX_IMAGES_PER_MESSAGE
|
!selectedModelInfo.supportsImages || textAreaDisabled || selectedImages.length >= MAX_IMAGES_PER_MESSAGE
|
||||||
|
|
||||||
const handlePaste = useCallback(
|
const handlePaste = useCallback(
|
||||||
async (e: React.ClipboardEvent) => {
|
async (e: React.ClipboardEvent) => {
|
||||||
@@ -495,7 +491,7 @@ const ChatView = ({
|
|||||||
task={task}
|
task={task}
|
||||||
tokensIn={apiMetrics.totalTokensIn}
|
tokensIn={apiMetrics.totalTokensIn}
|
||||||
tokensOut={apiMetrics.totalTokensOut}
|
tokensOut={apiMetrics.totalTokensOut}
|
||||||
doesModelSupportPromptCache={selectedModelSupportsPromptCache}
|
doesModelSupportPromptCache={selectedModelInfo.supportsPromptCache}
|
||||||
cacheWrites={apiMetrics.totalCacheWrites}
|
cacheWrites={apiMetrics.totalCacheWrites}
|
||||||
cacheReads={apiMetrics.totalCacheReads}
|
cacheReads={apiMetrics.totalCacheReads}
|
||||||
totalCost={apiMetrics.totalCost}
|
totalCost={apiMetrics.totalCost}
|
||||||
@@ -503,14 +499,7 @@ const ChatView = ({
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{showAnnouncement && (
|
{showAnnouncement && <Announcement version={version} hideAnnouncement={hideAnnouncement} />}
|
||||||
<Announcement
|
|
||||||
version={version}
|
|
||||||
hideAnnouncement={hideAnnouncement}
|
|
||||||
apiConfiguration={apiConfiguration}
|
|
||||||
vscodeUriScheme={uriScheme}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div style={{ padding: "0 20px", flexGrow: taskHistory.length > 0 ? undefined : 1 }}>
|
<div style={{ padding: "0 20px", flexGrow: taskHistory.length > 0 ? undefined : 1 }}>
|
||||||
<h2>What can I do for you?</h2>
|
<h2>What can I do for you?</h2>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ const StyledPre = styled.pre<{ theme: any }>`
|
|||||||
.join("")}
|
.join("")}
|
||||||
`
|
`
|
||||||
|
|
||||||
const CodeBlock = memo(function CodeBlock({ source }: { source?: string }) {
|
const CodeBlock = memo(({ source }: { source?: string }) => {
|
||||||
const { theme } = useExtensionState()
|
const { theme } = useExtensionState()
|
||||||
const [reactContent, setMarkdownSource] = useRemark({
|
const [reactContent, setMarkdownSource] = useRemark({
|
||||||
remarkPlugins: [
|
remarkPlugins: [
|
||||||
|
|||||||
Reference in New Issue
Block a user