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(/^[/\\:*?"<>|]+/, "")