import type { Part, AssistantMessage, ReasoningPart, TextPart, ToolPart } from "@opencode-ai/sdk" import { children, Component, createMemo, For, Match, Show, Switch, type JSX } from "solid-js" import { Dynamic } from "solid-js/web" import { Markdown } from "./markdown" import { Checkbox, Collapsible, Diff, Icon, IconProps } from "@opencode-ai/ui" import { getDirectory, getFilename } from "@/utils" import type { Tool } from "opencode/tool/tool" import type { ReadTool } from "opencode/tool/read" import type { ListTool } from "opencode/tool/ls" import type { GlobTool } from "opencode/tool/glob" import type { GrepTool } from "opencode/tool/grep" import type { WebFetchTool } from "opencode/tool/webfetch" import type { TaskTool } from "opencode/tool/task" import type { BashTool } from "opencode/tool/bash" import type { EditTool } from "opencode/tool/edit" import type { WriteTool } from "opencode/tool/write" import type { TodoWriteTool } from "opencode/tool/todo" import { DiffChanges } from "./diff-changes" export function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; lastToolOnly?: boolean }) { const filteredParts = createMemo(() => { let tool = false return props.parts?.filter((x) => { if (x.type === "tool" && props.lastToolOnly && tool) return false if (x.type === "tool") tool = true return x.type !== "tool" || x.tool !== "todoread" }) }) return (
{(part) => { const component = createMemo(() => PART_MAPPING[part.type as keyof typeof PART_MAPPING]) return ( ) }}
) } const PART_MAPPING = { text: TextPart, tool: ToolPart, reasoning: ReasoningPart, } function ReasoningPart(props: { part: ReasoningPart; message: AssistantMessage }) { return null // return ( // //
{props.part.text}
//
// ) } function TextPart(props: { part: TextPart; message: AssistantMessage }) { return ( ) } function ToolPart(props: { part: ToolPart; message: AssistantMessage }) { // const sync = useSync() const component = createMemo(() => { const render = ToolRegistry.render(props.part.tool) ?? GenericTool const metadata = props.part.state.status === "pending" ? {} : (props.part.state.metadata ?? {}) const input = props.part.state.status === "completed" ? props.part.state.input : {} // const permissions = sync.data.permission[props.message.sessionID] ?? [] // const permissionIndex = permissions.findIndex((x) => x.callID === props.part.callID) // const permission = permissions[permissionIndex] return ( ) }) return {component()} } type TriggerTitle = { title: string titleClass?: string subtitle?: string subtitleClass?: string args?: string[] argsClass?: string action?: JSX.Element } const isTriggerTitle = (val: any): val is TriggerTitle => { return typeof val === "object" && val !== null && "title" in val && !(val instanceof Node) } function BasicTool(props: { icon: IconProps["name"]; trigger: TriggerTitle | JSX.Element; children?: JSX.Element }) { const resolved = children(() => props.children) return (
{(trigger) => (
{trigger().title} {trigger().subtitle} {(arg) => ( {arg} )}
{trigger().action}
)}
{props.trigger as JSX.Element}
{props.children}
// <> // {props.part.state.error.replace("Error: ", "")} // ) } function GenericTool(props: ToolProps) { return } type ToolProps = { input: Partial> metadata: Partial> // permission: Record tool: string output?: string } const ToolRegistry = (() => { const state: Record< string, { name: string render?: Component> } > = {} function register(input: { name: string; render?: Component> }) { state[input.name] = input return input } return { register, render(name: string) { return state[name]?.render }, } })() ToolRegistry.register({ name: "read", render(props) { return ( ) }, }) ToolRegistry.register({ name: "list", render(props) { return (
{props.output}
) }, }) ToolRegistry.register({ name: "glob", render(props) { return ( {props.output} ) }, }) ToolRegistry.register({ name: "grep", render(props) { const args = [] if (props.input.pattern) args.push("pattern=" + props.input.pattern) if (props.input.include) args.push("include=" + props.input.include) return (
{props.output}
) }, }) ToolRegistry.register({ name: "webfetch", render(props) { return ( ), }} >
{props.output}
) }, }) ToolRegistry.register({ name: "task", render(props) { return (
{props.output}
) }, }) ToolRegistry.register({ name: "bash", render(props) { return (
{props.output}
) }, }) ToolRegistry.register({ name: "edit", render(props) { return (
Edit
{getDirectory(props.input.filePath!)} {getFilename(props.input.filePath ?? "")}
} >
) }, }) ToolRegistry.register({ name: "write", render(props) { return (
Write
{getDirectory(props.input.filePath!)} {getFilename(props.input.filePath ?? "")}
{/* */}
} >
{props.output}
) }, }) ToolRegistry.register({ name: "todowrite", render(props) { return ( t.status === "completed").length}/${props.input.todos?.length}`, }} >
{(todo) => (
{todo.content}
)}
) }, })