Просмотр исходного кода

Open read files in editor instead of code accordian

Saoud Rizwan 1 год назад
Родитель
Сommit
0522a26fd9

+ 1 - 1
src/ClaudeDev.ts

@@ -1138,7 +1138,7 @@ export class ClaudeDev {
 			const message = JSON.stringify({
 				tool: "readFile",
 				path: this.getReadablePath(relPath),
-				content,
+				content: absolutePath,
 			} as ClaudeSayTool)
 			if (this.alwaysAllowReadOnly) {
 				await this.say("tool", message)

+ 4 - 1
src/providers/ClaudeDevProvider.ts

@@ -10,7 +10,7 @@ import fs from "fs/promises"
 import { HistoryItem } from "../shared/HistoryItem"
 import axios from "axios"
 import { getTheme } from "../utils/getTheme"
-import { openImage } from "../utils/open-image"
+import { openFile, openImage } from "../utils/open-file"
 
 /*
 https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@@ -402,6 +402,9 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 					case "openImage":
 						openImage(message.text!)
 						break
+					case "openFile":
+						openFile(message.text!)
+						break
 					// Add more switch case statements here as more webview message commands
 					// are created within the webview context (i.e. inside media/main.js)
 				}

+ 1 - 0
src/shared/WebviewMessage.ts

@@ -18,6 +18,7 @@ export interface WebviewMessage {
 		| "resetState"
 		| "requestOllamaModels"
 		| "openImage"
+		| "openFile"
 	text?: string
 	askResponse?: ClaudeAskResponse
 	apiConfiguration?: ApiConfiguration

+ 50 - 0
src/utils/open-file.ts

@@ -0,0 +1,50 @@
+import * as path from "path"
+import * as os from "os"
+import * as vscode from "vscode"
+
+export async function openImage(dataUri: string) {
+	const matches = dataUri.match(/^data:image\/([a-zA-Z]+);base64,(.+)$/)
+	if (!matches) {
+		vscode.window.showErrorMessage("Invalid data URI format")
+		return
+	}
+	const [, format, base64Data] = matches
+	const imageBuffer = Buffer.from(base64Data, "base64")
+	const tempFilePath = path.join(os.tmpdir(), `temp_image_${Date.now()}.${format}`)
+	try {
+		await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFilePath), imageBuffer)
+		await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(tempFilePath))
+	} catch (error) {
+		vscode.window.showErrorMessage(`Error opening image: ${error}`)
+	}
+}
+
+export async function openFile(absolutePath: string) {
+	try {
+		const uri = vscode.Uri.file(absolutePath)
+
+		// Check if the document is already open in a tab group that's not in the active editor's column. If it is, then close it (if not dirty) so that we don't duplicate tabs
+		try {
+			for (const group of vscode.window.tabGroups.all) {
+				const existingTab = group.tabs.find(
+					(tab) => tab.input instanceof vscode.TabInputText && tab.input.uri.fsPath === uri.fsPath
+				)
+				if (existingTab) {
+					const activeColumn = vscode.window.activeTextEditor?.viewColumn
+					const tabColumn = vscode.window.tabGroups.all.find((group) =>
+						group.tabs.includes(existingTab)
+					)?.viewColumn
+					if (activeColumn && activeColumn !== tabColumn && !existingTab.isDirty) {
+						await vscode.window.tabGroups.close(existingTab)
+					}
+					break
+				}
+			}
+		} catch {} // not essential, sometimes tab operations fail
+
+		const document = await vscode.workspace.openTextDocument(uri)
+		await vscode.window.showTextDocument(document, { preview: false })
+	} catch (error) {
+		vscode.window.showErrorMessage(`Could not open file!`)
+	}
+}

+ 0 - 20
src/utils/open-image.ts

@@ -1,20 +0,0 @@
-import * as path from "path"
-import * as os from "os"
-import * as vscode from "vscode"
-
-export async function openImage(dataUri: string) {
-	const matches = dataUri.match(/^data:image\/([a-zA-Z]+);base64,(.+)$/)
-	if (!matches) {
-		vscode.window.showErrorMessage("Invalid data URI format")
-		return
-	}
-	const [, format, base64Data] = matches
-	const imageBuffer = Buffer.from(base64Data, "base64")
-	const tempFilePath = path.join(os.tmpdir(), `temp_image_${Date.now()}.${format}`)
-	try {
-		await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFilePath), imageBuffer)
-		await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(tempFilePath))
-	} catch (error) {
-		vscode.window.showErrorMessage(`Error opening image: ${error}`)
-	}
-}

+ 46 - 3
webview-ui/src/components/ChatRow.tsx

@@ -4,9 +4,10 @@ import React, { memo, useMemo } from "react"
 import ReactMarkdown from "react-markdown"
 import { ClaudeMessage, ClaudeSayTool } from "../../../src/shared/ExtensionMessage"
 import { COMMAND_OUTPUT_STRING } from "../../../src/shared/combineCommandSequences"
-import CodeAccordian from "./CodeAccordian"
+import CodeAccordian, { removeLeadingNonAlphanumeric } from "./CodeAccordian"
 import CodeBlock, { CODE_BLOCK_BG_COLOR } from "./CodeBlock"
 import Thumbnails from "./Thumbnails"
+import { vscode } from "../utils/vscode"
 
 interface ChatRowProps {
 	message: ClaudeMessage
@@ -190,12 +191,54 @@ const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifiedMessa
 								{message.type === "ask" ? "Claude wants to read this file:" : "Claude read this file:"}
 							</span>
 						</div>
-						<CodeAccordian
+						{/* <CodeAccordian
 							code={tool.content!}
 							path={tool.path!}
 							isExpanded={isExpanded}
 							onToggleExpand={onToggleExpand}
-						/>
+						/> */}
+						<div
+							style={{
+								borderRadius: 3,
+								backgroundColor: CODE_BLOCK_BG_COLOR,
+								overflow: "hidden",
+								border: "1px solid var(--vscode-editorGroup-border)",
+							}}>
+							<div
+								style={{
+									color: "var(--vscode-descriptionForeground)",
+									display: "flex",
+									justifyContent: "space-between",
+									alignItems: "center",
+									padding: "6px 10px",
+									cursor: "pointer",
+									userSelect: "none",
+									WebkitUserSelect: "none",
+									MozUserSelect: "none",
+									msUserSelect: "none",
+								}}
+								onClick={() => {
+									vscode.postMessage({ type: "openFile", text: tool.content })
+								}}>
+								<div style={{ display: "flex", alignItems: "center" }}>
+									<span
+										style={{
+											whiteSpace: "nowrap",
+											overflow: "hidden",
+											textOverflow: "ellipsis",
+											marginRight: "8px",
+											fontSize: "11px",
+											direction: "rtl",
+											textAlign: "left",
+										}}>
+										{removeLeadingNonAlphanumeric(tool.path ?? "") + "\u200E"}
+									</span>
+								</div>
+								<span
+									className={`codicon codicon-link-external`}
+									style={{ fontSize: 13, margin: "1.5px 0" }}></span>
+							</div>
+						</div>
 					</>
 				)
 			case "listFilesTopLevel":

+ 1 - 1
webview-ui/src/components/CodeAccordian.tsx

@@ -18,7 +18,7 @@ We need to remove leading non-alphanumeric characters from the path in order for
 [^a-zA-Z0-9]+: Matches one or more characters that are not alphanumeric.
 The replace method removes these matched characters, effectively trimming the string up to the first alphanumeric character.
 */
-const removeLeadingNonAlphanumeric = (path: string): string => path.replace(/^[^a-zA-Z0-9]+/, "")
+export const removeLeadingNonAlphanumeric = (path: string): string => path.replace(/^[^a-zA-Z0-9]+/, "")
 
 const CodeAccordian = ({ code, diff, language, path, isFeedback, isExpanded, onToggleExpand }: CodeAccordianProps) => {
 	const inferredLanguage = useMemo(