import type { Part, AssistantMessage, ReasoningPart, TextPart, ToolPart, Message } 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[] }) { const filteredParts = createMemo(() => { return props.parts?.filter((x) => { if (x.type === "reasoning") return false return x.type !== "tool" || x.tool !== "todoread" }) }) return (
{(part) => }
) } export function Part(props: { part: Part; message: Message; readonly?: boolean }) { const component = createMemo(() => PART_MAPPING[props.part.type as keyof typeof PART_MAPPING]) return ( ) } const PART_MAPPING = { text: TextPart, tool: ToolPart, reasoning: ReasoningPart, } function ReasoningPart(props: { part: ReasoningPart; message: Message }) { return ( ) } function TextPart(props: { part: TextPart; message: Message }) { return ( ) } function ToolPart(props: { part: ToolPart; message: Message; readonly?: boolean }) { 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 : {} 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 readonly?: boolean }) { const resolved = children(() => props.children) return (
{(trigger) => (
{trigger().title} {trigger().subtitle} {(arg) => ( {arg} )}
{trigger().action}
)}
{props.trigger as JSX.Element}
{resolved()}
// <> // {props.part.state.error.replace("Error: ", "")} // ) } function GenericTool(props: ToolProps) { return } type ToolProps = { input: Partial> metadata: Partial> tool: string output?: string readonly?: boolean } 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}
)}
) }, })