import { type ComponentProps, createMemo, Show, splitProps } from "solid-js" import { createStore } from "solid-js/store" import { Card, CardDescription } from "./card" import { Collapsible } from "./collapsible" import { Icon } from "./icon" import { IconButton } from "./icon-button" import { Tooltip } from "./tooltip" import { useI18n } from "../context/i18n" export interface ToolErrorCardProps extends Omit, "children" | "variant"> { tool: string error: string defaultOpen?: boolean subtitle?: string href?: string } export function ToolErrorCard(props: ToolErrorCardProps) { const i18n = useI18n() const [state, setState] = createStore({ open: props.defaultOpen ?? false, copied: false, }) const open = () => state.open const copied = () => state.copied const [split, rest] = splitProps(props, ["tool", "error", "defaultOpen", "subtitle", "href"]) const name = createMemo(() => { const map: Record = { read: "ui.tool.read", list: "ui.tool.list", glob: "ui.tool.glob", grep: "ui.tool.grep", task: "ui.tool.task", webfetch: "ui.tool.webfetch", websearch: "ui.tool.websearch", codesearch: "ui.tool.codesearch", bash: "ui.tool.shell", apply_patch: "ui.tool.patch", question: "ui.tool.questions", } const key = map[split.tool] if (!key) return split.tool if (!key.includes(".")) return key return i18n.t(key) }) const cleaned = createMemo(() => split.error.replace(/^Error:\s*/, "").trim()) const tail = createMemo(() => { const value = cleaned() const prefix = `${split.tool} ` if (value.startsWith(prefix)) return value.slice(prefix.length) return value }) const subtitle = createMemo(() => { if (split.subtitle) return split.subtitle const parts = tail().split(": ") if (parts.length <= 1) return i18n.t("ui.toolErrorCard.failed") const head = (parts[0] ?? "").trim() if (!head) return i18n.t("ui.toolErrorCard.failed") return head[0] ? head[0].toUpperCase() + head.slice(1) : i18n.t("ui.toolErrorCard.failed") }) const body = createMemo(() => { const parts = tail().split(": ") if (parts.length <= 1) return cleaned() return parts.slice(1).join(": ").trim() || cleaned() }) const copy = async () => { const text = cleaned() if (!text) return await navigator.clipboard.writeText(text) setState("copied", true) setTimeout(() => setState("copied", false), 2000) } return ( setState("open", value)} >
{name()} {subtitle()}} > e.stopPropagation()} > {subtitle()}
e.preventDefault()} onClick={(e) => { e.stopPropagation() copy() }} aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.toolErrorCard.copyError")} />
{(value) => {value()}}
) }