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 } const ChatRow: React.FC = ({ message }) => { const [isExpanded, setIsExpanded] = useState(false) const cost = message.text != null && message.say === "api_req_started" ? JSON.parse(message.text).cost : undefined const getIconAndTitle = (type: ClaudeAsk | ClaudeSay | undefined): [JSX.Element | null, JSX.Element | null] => { const normalColor = "var(--vscode-foreground)" const errorColor = "var(--vscode-errorForeground)" const successColor = "var(--vscode-testing-iconPassed)" switch (type) { case "request_limit_reached": return [ , Max Requests Reached, ] case "error": return [ , Error, ] case "command": return [ , Command, ] case "completion_result": return [ , Task Completed, ] case "api_req_started": return [ cost ? ( ) : (
), {cost ? "API Request Complete" : "Making API Request..."} , ] default: return [null, null] } } const renderContent = () => { const [icon, title] = getIconAndTitle(message.type === "ask" ? message.ask : message.say) const headerStyle: React.CSSProperties = { display: "flex", alignItems: "center", gap: "10px", marginBottom: "10px", } const contentStyle: React.CSSProperties = { margin: 0, whiteSpace: "pre-line", } switch (message.type) { case "say": switch (message.say) { case "api_req_started": return (
{icon} {title} {cost && ${Number(cost).toFixed(4)}}
setIsExpanded(!isExpanded)}>
) case "api_req_finished": return null // Hide this message type case "tool": //const tool = JSON.parse(message.text || "{}") as ClaudeSayTool const tool: ClaudeSayTool = { tool: "editedExistingFile", path: "/path/to/file", } const toolIcon = (name: string) => ( ) switch (tool.tool) { case "editedExistingFile": return ( <>
{toolIcon("edit")} Edited file...
) case "newFileCreated": return ( <>
{toolIcon("new-file")} Created new file...
) case "readFile": return ( <>
{toolIcon("file-code")} Read file...
) case "listFiles": return ( <>
{toolIcon("folder-opened")} Viewed contents of directory...
) } break case "text": return

{message.text}

case "error": return ( <> {title && (
{icon} {title}
)}

{message.text}

) case "completion_result": return ( <>
{icon} {title}

{message.text}

) default: return ( <> {title && (
{icon} {title}
)}

{message.text}

) } break case "ask": switch (message.ask) { case "request_limit_reached": return ( <>
{icon} {title}

{message.text}

) case "command": const splitMessage = (text: string) => { const outputIndex = text.indexOf(COMMAND_OUTPUT_STRING) if (outputIndex === -1) { return { command: text, output: "" } } return { command: text.slice(0, outputIndex).trim(), output: text.slice(outputIndex + COMMAND_OUTPUT_STRING.length).trim(), } } const { command, output } = splitMessage(message.text || "") return ( <>
{icon} {title}

Claude Dev wants to execute the following terminal command. Would you like to proceed?

{output && ( <>

{COMMAND_OUTPUT_STRING}

)}
) case "completion_result": if (message.text) { return (
{icon} {title}

{message.text}

) } else { return null // Don't render anything when we get a completion_result ask without text } default: return ( <> {title && (
{icon} {title}
)}

{message.text}

) } } } if (message.say === "api_req_finished") { return null // Don't render anything for this message type } return (
{renderContent()} {isExpanded && message.say === "api_req_started" && (
)}
) } export default ChatRow