diff --git a/src/core/tools/applyDiffTool.ts b/src/core/tools/applyDiffTool.ts index 0fc9d9796c..9fbe1e1de1 100644 --- a/src/core/tools/applyDiffTool.ts +++ b/src/core/tools/applyDiffTool.ts @@ -31,6 +31,7 @@ export async function applyDiffTool( const sharedMessageProps: ClineSayTool = { tool: "appliedDiff", path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)), + diff: diffContent, } try { @@ -46,8 +47,10 @@ export async function applyDiffTool( return } - const partialMessage = JSON.stringify(sharedMessageProps) - await cline.ask("tool", partialMessage, block.partial, toolProgressStatus).catch(() => {}) + await cline + .ask("tool", JSON.stringify(sharedMessageProps), block.partial, toolProgressStatus) + .catch(() => {}) + return } else { if (!relPath) { diff --git a/src/core/tools/insertContentTool.ts b/src/core/tools/insertContentTool.ts index 8c15f96a8b..bcb217326a 100644 --- a/src/core/tools/insertContentTool.ts +++ b/src/core/tools/insertContentTool.ts @@ -26,13 +26,13 @@ export async function insertContentTool( const sharedMessageProps: ClineSayTool = { tool: "insertContent", path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)), + diff: content, lineNumber: line ? parseInt(line, 10) : undefined, } try { if (block.partial) { - const partialMessage = JSON.stringify(sharedMessageProps) - await cline.ask("tool", partialMessage, block.partial).catch(() => {}) + await cline.ask("tool", JSON.stringify(sharedMessageProps), block.partial).catch(() => {}) return } @@ -145,14 +145,15 @@ export async function insertContentTool( return } - const userFeedbackDiff = JSON.stringify({ - tool: "insertContent", - path: getReadablePath(cline.cwd, relPath), - lineNumber: lineNumber, - diff: userEdits, - } satisfies ClineSayTool) - - await cline.say("user_feedback_diff", userFeedbackDiff) + await cline.say( + "user_feedback_diff", + JSON.stringify({ + tool: "insertContent", + path: getReadablePath(cline.cwd, relPath), + diff: userEdits, + lineNumber: lineNumber, + } satisfies ClineSayTool), + ) pushToolResult( `The user made the following updates to your content:\n\n${userEdits}\n\n` + diff --git a/src/core/tools/searchAndReplaceTool.ts b/src/core/tools/searchAndReplaceTool.ts index 48910b9a2b..417e2046df 100644 --- a/src/core/tools/searchAndReplaceTool.ts +++ b/src/core/tools/searchAndReplaceTool.ts @@ -226,13 +226,14 @@ export async function searchAndReplaceTool( return } - const userFeedbackDiff = JSON.stringify({ - tool: "appliedDiff", - path: getReadablePath(cline.cwd, relPath), - diff: userEdits, - } satisfies ClineSayTool) - - await cline.say("user_feedback_diff", userFeedbackDiff) + await cline.say( + "user_feedback_diff", + JSON.stringify({ + tool: "appliedDiff", + path: getReadablePath(cline.cwd, relPath), + diff: userEdits, + } satisfies ClineSayTool), + ) // Format and send response with user's updates const resultMessage = [ diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index d2f22040c6..2c37f95b74 100644 --- a/src/core/tools/writeToFileTool.ts +++ b/src/core/tools/writeToFileTool.ts @@ -72,6 +72,7 @@ export async function writeToFileTool( const sharedMessageProps: ClineSayTool = { tool: fileExists ? "editedExistingFile" : "newFileCreated", path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)), + content: newContent, isOutsideWorkspace, } diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 046c2c9d53..8912a5d80e 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -12,10 +12,12 @@ import { useCopyToClipboard } from "@src/utils/clipboard" import { useExtensionState } from "@src/context/ExtensionStateContext" import { findMatchingResourceOrTemplate } from "@src/utils/mcp" import { vscode } from "@src/utils/vscode" +import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric" import { Button } from "@src/components/ui" -import CodeAccordian, { removeLeadingNonAlphanumeric } from "../common/CodeAccordian" -import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock" +import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock" +import CodeAccordian from "../common/CodeAccordian" +import CodeBlock from "../common/CodeBlock" import MarkdownBlock from "../common/MarkdownBlock" import { ReasoningBlock } from "./ReasoningBlock" import Thumbnails from "../common/Thumbnails" @@ -287,10 +289,11 @@ export const ChatRowContent = ({ @@ -312,10 +315,11 @@ export const ChatRowContent = ({ @@ -333,10 +337,10 @@ export const ChatRowContent = ({ @@ -350,9 +354,9 @@ export const ChatRowContent = ({ {t("chat:fileOperations.wantsToCreate")} @@ -371,47 +375,21 @@ export const ChatRowContent = ({ : t("chat:fileOperations.didRead")} -
-
{ - vscode.postMessage({ type: "openFile", text: tool.content }) - }}> + + vscode.postMessage({ type: "openFile", text: tool.content })}> {tool.path?.startsWith(".") && .} - + {removeLeadingNonAlphanumeric(tool.path ?? "") + "\u200E"} {tool.reason}
-
-
+ style={{ fontSize: 13.5, margin: "1px 0" }} + /> + + ) case "fetchInstructions": @@ -422,8 +400,8 @@ export const ChatRowContent = ({ {t("chat:instructions.wantsToFetch")} @@ -441,8 +419,8 @@ export const ChatRowContent = ({ @@ -510,8 +488,8 @@ export const ChatRowContent = ({ (message.text) return ( -
+
void diff --git a/webview-ui/src/components/common/CodeAccordian.tsx b/webview-ui/src/components/common/CodeAccordian.tsx index e1ea77826e..4b92fe416e 100644 --- a/webview-ui/src/components/common/CodeAccordian.tsx +++ b/webview-ui/src/components/common/CodeAccordian.tsx @@ -1,109 +1,59 @@ import { memo, useMemo } from "react" -import { getLanguageFromPath } from "@src/utils/getLanguageFromPath" -import CodeBlock, { CODE_BLOCK_BG_COLOR } from "./CodeBlock" -import { ToolProgressStatus } from "@roo/shared/ExtensionMessage" import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" +import { type ToolProgressStatus } from "@roo/shared/ExtensionMessage" +import { getLanguageFromPath } from "@src/utils/getLanguageFromPath" +import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric" + +import { ToolUseBlock, ToolUseBlockHeader } from "./ToolUseBlock" +import CodeBlock from "./CodeBlock" + interface CodeAccordianProps { + path?: string code?: string - diff?: string language?: string | undefined - path?: string - isFeedback?: boolean - isConsoleLogs?: boolean + progressStatus?: ToolProgressStatus + isLoading?: boolean isExpanded: boolean + isFeedback?: boolean onToggleExpand: () => void - isLoading?: boolean - progressStatus?: ToolProgressStatus -} - -/* -We need to remove certain leading characters from the path in order for our leading ellipses trick to work. -However, we want to preserve all language characters (including CJK, Cyrillic, etc.) and only remove specific -punctuation that might interfere with the ellipsis display. -*/ -export const removeLeadingNonAlphanumeric = (path: string): string => { - // Only remove specific punctuation characters that might interfere with ellipsis display - // Keep all language characters (including CJK, Cyrillic, etc.) and numbers - return path.replace(/^[/\\:*?"<>|]+/, "") } const CodeAccordian = ({ - code, - diff, - language, path, - isFeedback, - isConsoleLogs, + code = "", + language, + progressStatus, + isLoading, isExpanded, + isFeedback, onToggleExpand, - isLoading, - progressStatus, }: CodeAccordianProps) => { - const inferredLanguage = useMemo( - () => code && (language ?? (path ? getLanguageFromPath(path) : undefined)), - [path, language, code], - ) + const inferredLanguage = useMemo(() => language ?? (path ? getLanguageFromPath(path) : undefined), [path, language]) + const source = useMemo(() => code.trim(), [code]) + const hasHeader = Boolean(path || isFeedback) return ( -
- {(path || isFeedback || isConsoleLogs) && ( -
+ + {hasHeader && ( + {isLoading && } - {isFeedback || isConsoleLogs ? ( -
- - + {isFeedback ? ( +
+ + {isFeedback ? "User Edits" : "Console Logs"}
) : ( <> {path?.startsWith(".") && .} - + {removeLeadingNonAlphanumeric(path ?? "") + "\u200E"} )} -
+
{progressStatus && progressStatus.text && ( <> {progressStatus.icon && } @@ -113,24 +63,17 @@ const CodeAccordian = ({ )} -
+ )} - {(!(path || isFeedback || isConsoleLogs) || isExpanded) && ( -
- + {(!hasHeader || isExpanded) && ( +
+
)} -
+ ) } -// memo does shallow comparison of props, so if you need it to re-render when a nested object changes, you need to pass a custom comparison function +// Memo does shallow comparison of props, so if you need it to re-render when a +// nested object changes, you need to pass a custom comparison function. export default memo(CodeAccordian) diff --git a/webview-ui/src/components/common/ToolUseBlock.tsx b/webview-ui/src/components/common/ToolUseBlock.tsx new file mode 100644 index 0000000000..6fb2b3a521 --- /dev/null +++ b/webview-ui/src/components/common/ToolUseBlock.tsx @@ -0,0 +1,17 @@ +import { cn } from "@/lib/utils" + +import { CODE_BLOCK_BG_COLOR } from "./CodeBlock" + +export const ToolUseBlock = ({ className, ...props }: React.HTMLAttributes) => ( +
+) + +export const ToolUseBlockHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+) diff --git a/webview-ui/src/utils/removeLeadingNonAlphanumeric.ts b/webview-ui/src/utils/removeLeadingNonAlphanumeric.ts new file mode 100644 index 0000000000..869ca293b0 --- /dev/null +++ b/webview-ui/src/utils/removeLeadingNonAlphanumeric.ts @@ -0,0 +1,10 @@ +// We need to remove certain leading characters from the path in order for our +// leading ellipses trick to work. +// However, we want to preserve all language characters (including CJK, +// Cyrillic, etc.) and only remove specific punctuation that might interfere +// with the ellipsis display. +// +// Only remove specific punctuation characters that might interfere with +// ellipsis display. Keep all language characters (including CJK, Cyrillic +// etc.) and numbers. +export const removeLeadingNonAlphanumeric = (path: string): string => path.replace(/^[/\\:*?"<>|]+/, "")