Add CodeBlock component

This commit is contained in:
Saoud Rizwan
2024-07-09 20:17:07 -04:00
parent 6f5b0565e0
commit 97faff3ba5
6 changed files with 571 additions and 53 deletions

View File

@@ -2,6 +2,9 @@ import React, { useState } from "react"
import { ClaudeMessage, ClaudeAsk, ClaudeSay, ClaudeSayTool } from "@shared/ExtensionMessage"
import { VSCodeButton, VSCodeProgressRing, VSCodeBadge } from "@vscode/webview-ui-toolkit/react"
import { COMMAND_OUTPUT_STRING } from "../utilities/combineCommandSequences"
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
import { dark } from "react-syntax-highlighter/dist/esm/styles/prism"
import CodeBlock from "./CodeBlock"
interface ChatRowProps {
message: ClaudeMessage
@@ -45,13 +48,6 @@ const ChatRow: React.FC<ChatRowProps> = ({ message }) => {
style={{ color: successColor, marginBottom: "-1.5px" }}></span>,
<span style={{ color: successColor, fontWeight: "bold" }}>Task Completed</span>,
]
case "tool":
return [
<span
className="codicon codicon-tools"
style={{ color: normalColor, marginBottom: "-1.5px" }}></span>,
<span style={{ color: normalColor, fontWeight: "bold" }}>Tool</span>,
]
case "api_req_started":
return [
cost ? (
@@ -123,47 +119,51 @@ const ChatRow: React.FC<ChatRowProps> = ({ message }) => {
tool: "editedExistingFile",
path: "/path/to/file",
}
const toolIcon = (name: string) => (
<span
className={`codicon codicon-${name}`}
style={{ color: "var(--vscode-foreground)", marginBottom: "-1.5px" }}></span>
)
switch (tool.tool) {
case "editedExistingFile":
return (
<>
<div style={headerStyle}>
{icon}
Edited File
{toolIcon("edit")}
Edited file...
</div>
<p>Path: {tool.path!}</p>
<p>{tool.diff!}</p>
<CodeBlock diff={tool.diff!} path={tool.path!} />
</>
)
case "newFileCreated":
return (
<>
<div style={headerStyle}>
{icon}
Created New File
{toolIcon("new-file")}
Created new file...
</div>
<p>Path: {tool.path!}</p>
<p>{tool.content!}</p>
<CodeBlock code={tool.content!} path={tool.path!} />
</>
)
case "readFile":
return (
<>
<div style={headerStyle}>
{icon}
Read File
{toolIcon("file-code")}
Read file...
</div>
<p>Path: {tool.path!}</p>
<CodeBlock code={tool.content!} path={tool.path!} />
</>
)
case "listFiles":
return (
<>
<div style={headerStyle}>
{icon}
Viewed Directory
{toolIcon("folder-opened")}
Viewed contents of directory...
</div>
<p>Path: {tool.path!}</p>
<CodeBlock code={tool.content!} path={tool.path!} language="shell-session" />
</>
)
}
@@ -244,14 +244,24 @@ const ChatRow: React.FC<ChatRowProps> = ({ message }) => {
{title}
</div>
<div style={contentStyle}>
<p style={contentStyle}>Claude Dev wants to execute the following terminal command. Would you like to proceed?</p>
<p style={contentStyle}>{command}</p>
<p style={contentStyle}>
Claude Dev wants to execute the following terminal command. Would you like to
proceed?
</p>
<div style={{ marginTop: "10px" }}>
<CodeBlock code={command} language="shell-session" />
</div>
{output && (
<>
<p style={{ ...contentStyle, fontWeight: "bold" }}>
<p style={{ ...contentStyle, margin: "10px 0 10px 0" }}>
{COMMAND_OUTPUT_STRING}
</p>
<p style={contentStyle}>{output}</p>
<CodeBlock
code={output}
language="shell-session"
path="src/components/WelcomeView.tsx/src/components/WelcomeView.tsx"
/>
</>
)}
</div>
@@ -300,7 +310,9 @@ const ChatRow: React.FC<ChatRowProps> = ({ message }) => {
}}>
{renderContent()}
{isExpanded && message.say === "api_req_started" && (
<p style={{ marginTop: "10px" }}>{JSON.stringify(JSON.parse(message.text || "{}").request)}</p>
<div style={{ marginTop: "10px" }}>
<CodeBlock code={JSON.stringify(JSON.parse(message.text || "{}").request)} language="json" />
</div>
)}
</div>
)

View File

@@ -0,0 +1,143 @@
import React, { useState } from "react"
import SyntaxHighlighter from "react-syntax-highlighter"
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
/*
const vscodeSyntaxStyle: React.CSSProperties = {
backgroundColor: "var(--vscode-editor-background)",
color: "var(--vscode-editor-foreground)",
fontFamily: "var(--vscode-editor-font-family)",
fontSize: "var(--vscode-editor-font-size)",
lineHeight: "var(--vscode-editor-line-height)",
textAlign: "left",
whiteSpace: "pre",
wordSpacing: "normal",
wordBreak: "normal",
wordWrap: "normal",
tabSize: 4,
hyphens: "none",
padding: "1em",
margin: "0.5em 0",
overflow: "auto",
borderRadius: "6px",
}
const tokenStyles = {
comment: { color: "var(--vscode-editor-foreground)" },
prolog: { color: "var(--vscode-editor-foreground)" },
doctype: { color: "var(--vscode-editor-foreground)" },
cdata: { color: "var(--vscode-editor-foreground)" },
punctuation: { color: "var(--vscode-editor-foreground)" },
property: { color: "var(--vscode-symbolIcon-propertyForeground)" },
tag: { color: "var(--vscode-symbolIcon-colorForeground)" },
boolean: { color: "var(--vscode-symbolIcon-booleanForeground)" },
number: { color: "var(--vscode-symbolIcon-numberForeground)" },
constant: { color: "var(--vscode-symbolIcon-constantForeground)" },
symbol: { color: "var(--vscode-symbolIcon-colorForeground)" },
selector: { color: "var(--vscode-symbolIcon-colorForeground)" },
"attr-name": { color: "var(--vscode-symbolIcon-propertyForeground)" },
string: { color: "var(--vscode-symbolIcon-stringForeground)" },
char: { color: "var(--vscode-symbolIcon-stringForeground)" },
builtin: { color: "var(--vscode-symbolIcon-keywordForeground)" },
inserted: { color: "var(--vscode-gitDecoration-addedResourceForeground)" },
operator: { color: "var(--vscode-symbolIcon-operatorForeground)" },
entity: { color: "var(--vscode-symbolIcon-snippetForeground)", cursor: "help" },
url: { color: "var(--vscode-textLink-foreground)" },
variable: { color: "var(--vscode-symbolIcon-variableForeground)" },
atrule: { color: "var(--vscode-symbolIcon-keywordForeground)" },
"attr-value": { color: "var(--vscode-symbolIcon-stringForeground)" },
keyword: { color: "var(--vscode-symbolIcon-keywordForeground)" },
function: { color: "var(--vscode-symbolIcon-functionForeground)" },
regex: { color: "var(--vscode-symbolIcon-regexForeground)" },
important: { color: "var(--vscode-editorWarning-foreground)", fontWeight: "bold" },
bold: { fontWeight: "bold" },
italic: { fontStyle: "italic" },
deleted: { color: "var(--vscode-gitDecoration-deletedResourceForeground)" },
}
*/
interface CodeBlockProps {
code?: string
diff?: string
language?: string | undefined
path?: string
}
const CodeBlock = ({ code, diff, language, path }: CodeBlockProps) => {
const [isExpanded, setIsExpanded] = useState(false)
const backgroundColor = oneDark['pre[class*="language-"]'].background as string
return (
<div
style={{
borderRadius: "3px",
backgroundColor: backgroundColor,
overflow: "hidden", // This ensures the inner scrollable area doesn't overflow the rounded corners
}}>
{path && (
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "6px 10px",
}}>
<span
style={{
color: "var(--vscode-descriptionForeground)",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
marginRight: "8px",
// trick to get ellipsis at beginning of string
direction: "rtl",
textAlign: "left",
}}>
{path}
</span>
<VSCodeButton appearance="icon" aria-label="Toggle Code" onClick={() => setIsExpanded(!isExpanded)}>
<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
</VSCodeButton>
</div>
)}
{(!path || isExpanded) && (
<div
className="code-block-scrollable"
style={{
overflowX: "auto",
overflowY: "hidden",
maxWidth: "100%",
}}>
<SyntaxHighlighter
wrapLines={false}
language={language}
style={oneDark}
customStyle={{
margin: 0,
padding: "6px 10px",
borderRadius: 0,
}}
lineProps={
diff != null
? (lineNumber) => {
const line = diff?.split("\n")?.[lineNumber - 1]
let style: React.CSSProperties = { display: "block", width: "100%" }
if (line && line[0] === "+") {
style.backgroundColor = "var(--vscode-diffEditor-insertedTextBackground)"
} else if (line && line[0] === "-") {
style.backgroundColor = "var(--vscode-diffEditor-removedTextBackground)"
}
return { style }
}
: undefined
}>
{code ?? diff ?? ""}
</SyntaxHighlighter>
</div>
)}
</div>
)
}
export default CodeBlock