import { useMemo, useState } from "react"; import { useAtomValue, Result } from "@effect-atom/atom-react"; import { toolSchemaAtom } from "../api/atoms"; import { ScopeId, ToolId } from "@executor/sdk"; import { Button } from "./button"; import { Markdown } from "./markdown"; import { SchemaExplorer } from "./schema-explorer"; import { ExpandableCodeBlock } from "./expandable-code-block"; import { CardStack, CardStackHeader, CardStackContent } from "./card-stack"; import { CopyButton } from "./copy-button"; import { ChevronRight } from "lucide-react"; function EmptySection(props: { title: string; message: string }) { return ( {props.title}

{props.message}

); } // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- const friendlyName = (name: string): string => { const leaf = name.split(".").pop() ?? name; return leaf .replace(/([a-z])([A-Z])/g, "$1 $2") .replace(/[_.-]/g, " ") .replace(/\b\w/g, (c) => c.toUpperCase()); }; const breadcrumbParts = (name: string): string[] => name.split(".").map((p) => p .replace(/([a-z])([A-Z])/g, "$1 $2") .replace(/[_-]/g, " ") .replace(/\b\w/g, (c) => c.toUpperCase()), ); // --------------------------------------------------------------------------- // ToolDetail // --------------------------------------------------------------------------- export function ToolDetail(props: { toolId: string; toolName: string; toolDescription?: string; scopeId: ScopeId; }) { const toolContract = useAtomValue(toolSchemaAtom(props.scopeId, props.toolId as ToolId)); const [tab, setTab] = useState<"schema" | "typescript">("schema"); const data = useMemo(() => { if (!Result.isSuccess(toolContract)) return null; const v = toolContract.value; const definitions = Object.entries(v.typeScriptDefinitions ?? {}).map(([name, body]) => ({ name, code: body, })); return { inputSchema: v.inputSchema, outputSchema: v.outputSchema, inputTypeScript: v.inputTypeScript ? `type Input = ${v.inputTypeScript}` : null, outputTypeScript: v.outputTypeScript ? `type Output = ${v.outputTypeScript}` : null, definitions, }; }, [toolContract]); const crumbs = breadcrumbParts(props.toolName); const displayName = friendlyName(props.toolName); return (
{/* Header + tabs */}
{crumbs.length > 1 && (
{crumbs.slice(0, -1).map((part, i) => ( {i > 0 && } {part} ))}
)}

{displayName}

{props.toolDescription && (
{props.toolDescription}
)} {/* Tabs */}
{/* Content */}
{Result.match(toolContract, { onInitial: () =>
Loading…
, onFailure: () =>
Something went wrong
, onSuccess: () => tab === "schema" ? (
{data?.inputSchema ? ( ) : ( )} {data?.outputSchema ? ( ) : ( )}
) : (
{data?.inputTypeScript ? ( Input ) : ( )} {data?.outputTypeScript ? ( Output ) : ( )}
), })}
); } // --------------------------------------------------------------------------- // Empty state // --------------------------------------------------------------------------- export function ToolDetailEmpty(props: { hasTools: boolean }) { return (

{props.hasTools ? "Select a tool" : "No tools available"}

{props.hasTools && (

Choose from the list to see what it does.

)}
); }