import type { OpenRequest, PanelInputs, PanelKind, Slot } from "./layout.ts"; /** * Where a panel's UI renders. `chrome` panels draw on the shell's top-level origin from data the * shell parses (file trees, terminal bytes, transcripts). `sandbox` panels render guest UI, so they * must be a sandbox origin's iframe (D3, D10). The playground renders both in-page for now. */ export type PanelOrigin = "chrome" | "sandbox"; export interface KindSpec { readonly title: (input: PanelInputs[K]) => string; /** Identity of the view: opening a panel whose key is already open activates that panel. */ readonly key: (input: PanelInputs[K]) => string; readonly slot: Slot; readonly origin: PanelOrigin; /** What each of the input's fields holds, checked when a stored layout is parsed. */ readonly fields: FieldTypes; } type FieldTypes = { readonly [F in keyof I]-?: null extends I[F] ? "string | null" : "string" }; export type Kinds = { readonly [K in PanelKind]: KindSpec }; const basename = (path: string) => path.slice(path.lastIndexOf("/") + 1) || path; export const kinds: Kinds = { explorer: { title: () => "Explorer", key: (input) => input.root, slot: "primary", origin: "chrome", fields: { root: "string" } }, editor: { title: (input) => basename(input.path), key: (input) => input.path, slot: "main", origin: "sandbox", fields: { path: "string" } }, preview: { title: (input) => `Preview ${basename(input.path)}`, key: (input) => input.path, slot: "main", origin: "sandbox", fields: { path: "string" } }, terminal: { title: () => "Terminal", key: (input) => input.session, slot: "panel", origin: "chrome", fields: { cwd: "string", session: "string" } }, agent: { title: () => "Agent", key: (input) => input.conversation, slot: "secondary", origin: "chrome", fields: { conversation: "string" } }, processes: { title: () => "Processes", key: (input) => input.sandbox, slot: "panel", origin: "chrome", fields: { sandbox: "string" } }, // One panel per server: another path on the same origin is the same page navigated. The unbound // preview's key is empty, which no origin is. browser: { title: (input) => (input.url === null ? "Preview" : (URL.parse(input.url)?.host ?? input.url)), key: (input) => (input.url === null ? "" : (URL.parse(input.url)?.origin ?? input.url)), slot: "main", origin: "sandbox", fields: { url: "string | null" }, }, }; export function isKind(value: unknown): value is PanelKind { return typeof value === "string" && Object.hasOwn(kinds, value); } export function spec(kind: K): KindSpec { return kinds[kind]; } export function keyOf(request: OpenRequest): string { return (spec(request.kind).key as (input: OpenRequest["input"]) => string)(request.input); } export function titleOf(request: OpenRequest): string { return (spec(request.kind).title as (input: OpenRequest["input"]) => string)(request.input); }