/** `browser` — drive a Chromium tab: open/close named tabs, run puppeteer scripts. */ import type { ReactNode } from "react"; import { Badge, CodeBlock, ResultImages, ResultText, type Tone } from "../parts"; import type { ToolRenderer, ToolRenderProps } from "../types"; import { detailsRecord, isRecord, num, shortenPath, str, truncate } from "../util"; interface BrowserDetails { action: string | null; name: string | null; url: string | null; browser: string | null; } function detailsOf(result: ToolRenderProps["result"]): BrowserDetails { const d = detailsRecord(result); return { action: d ? str(d.action) : null, name: d ? str(d.name) : null, url: d ? str(d.url) : null, browser: d ? str(d.browser) : null, }; } interface AppArg { path: string | null; cdpUrl: string | null; target: string | null; } function appOf(args: Record): AppArg | null { if (!isRecord(args.app)) return null; return { path: str(args.app.path), cdpUrl: str(args.app.cdp_url), target: str(args.app.target) }; } function actionTone(action: string): Tone | undefined { switch (action) { case "open": return "ok"; case "run": return "accent"; case "close": return "warn"; default: return undefined; } } /** Mirrors the TUI's `describeBrowser`: explicit app args win over reported mode. */ function describeBrowser(app: AppArg | null, details: BrowserDetails): string | null { if (app?.cdpUrl) return `connected ${app.cdpUrl}`; if (app?.path) return `spawned ${shortenPath(app.path)}`; return details.browser; } function Summary({ args, result }: ToolRenderProps): ReactNode { const details = detailsOf(result); const action = str(args.action) ?? details.action ?? "?"; const closeAll = action === "close" && (args.all === true || (str(args.name) === null && details.name === null)); const tab = details.name ?? str(args.name) ?? "main"; const url = details.url ?? str(args.url); return ( <> {action} {closeAll ? "all tabs" : tab} {args.kill === true && kill} {url && {truncate(shortenPath(url), 72)}} ); } function Body({ args, result }: ToolRenderProps): ReactNode { const details = detailsOf(result); const action = str(args.action) ?? details.action; const app = appOf(args); const tab = details.name ?? str(args.name); const url = details.url ?? str(args.url); const browserDesc = describeBrowser(app, details); const viewport = isRecord(args.viewport) ? args.viewport : null; const vpWidth = viewport ? num(viewport.width) : null; const vpHeight = viewport ? num(viewport.height) : null; const vpScale = viewport ? num(viewport.scale) : null; const code = str(args.code); return ( <> {tab !== null && tab {tab}} {url && {truncate(shortenPath(url), 120)}} {browserDesc && {browserDesc}} {app?.target && target {app.target}} {args.all === true && all} {args.kill === true && kill} {vpWidth !== null && vpHeight !== null && ( {vpWidth}×{vpHeight} {vpScale !== null ? `@${vpScale}x` : ""} )} {action === "run" && code !== null && } ); } export const browserRenderer: ToolRenderer = { Summary, Body };