Open read files in editor instead of code accordian

This commit is contained in:
Saoud Rizwan
2024-09-11 16:38:33 -04:00
parent dd01fc7fc4
commit 0522a26fd9
7 changed files with 103 additions and 26 deletions

View File

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

View File

@@ -10,7 +10,7 @@ import fs from "fs/promises"
import { HistoryItem } from "../shared/HistoryItem" import { HistoryItem } from "../shared/HistoryItem"
import axios from "axios" import axios from "axios"
import { getTheme } from "../utils/getTheme" 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 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": case "openImage":
openImage(message.text!) openImage(message.text!)
break break
case "openFile":
openFile(message.text!)
break
// Add more switch case statements here as more webview message commands // Add more switch case statements here as more webview message commands
// are created within the webview context (i.e. inside media/main.js) // are created within the webview context (i.e. inside media/main.js)
} }

View File

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

50
src/utils/open-file.ts Normal file
View File

@@ -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!`)
}
}

View File

@@ -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}`)
}
}

View File

@@ -4,9 +4,10 @@ import React, { memo, useMemo } from "react"
import ReactMarkdown from "react-markdown" import ReactMarkdown from "react-markdown"
import { ClaudeMessage, ClaudeSayTool } from "../../../src/shared/ExtensionMessage" import { ClaudeMessage, ClaudeSayTool } from "../../../src/shared/ExtensionMessage"
import { COMMAND_OUTPUT_STRING } from "../../../src/shared/combineCommandSequences" 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 CodeBlock, { CODE_BLOCK_BG_COLOR } from "./CodeBlock"
import Thumbnails from "./Thumbnails" import Thumbnails from "./Thumbnails"
import { vscode } from "../utils/vscode"
interface ChatRowProps { interface ChatRowProps {
message: ClaudeMessage 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:"} {message.type === "ask" ? "Claude wants to read this file:" : "Claude read this file:"}
</span> </span>
</div> </div>
<CodeAccordian {/* <CodeAccordian
code={tool.content!} code={tool.content!}
path={tool.path!} path={tool.path!}
isExpanded={isExpanded} isExpanded={isExpanded}
onToggleExpand={onToggleExpand} 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": case "listFilesTopLevel":

View File

@@ -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. [^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. 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 CodeAccordian = ({ code, diff, language, path, isFeedback, isExpanded, onToggleExpand }: CodeAccordianProps) => {
const inferredLanguage = useMemo( const inferredLanguage = useMemo(