/** @jsxImportSource @opentui/solid */ import { useKeyboard, useTerminalDimensions } from "@opentui/solid" import { RGBA, VignetteEffect } from "@opentui/core" import type { TuiKeybindSet, TuiPlugin, TuiPluginApi, TuiPluginMeta, TuiPluginModule, TuiSlotPlugin, } from "@opencode-ai/plugin/tui" const tabs = ["overview", "counter", "help"] const bind = { modal: "ctrl+shift+m", screen: "ctrl+shift+o", home: "escape,ctrl+h", left: "left,h", right: "right,l", up: "up,k", down: "down,j", alert: "a", confirm: "c", prompt: "p", select: "s", modal_accept: "enter,return", modal_close: "escape", dialog_close: "escape", local: "x", local_push: "enter,return", local_close: "q,backspace", host: "z", } const pick = (value: unknown, fallback: string) => { if (typeof value !== "string") return fallback if (!value.trim()) return fallback return value } const num = (value: unknown, fallback: number) => { if (typeof value !== "number") return fallback return value } const rec = (value: unknown) => { if (!value || typeof value !== "object" || Array.isArray(value)) return return Object.fromEntries(Object.entries(value)) } type Cfg = { label: string route: string vignette: number keybinds: Record | undefined } type Route = { modal: string screen: string } type State = { tab: number count: number source: string note: string selected: string local: number } const cfg = (options: Record | undefined) => { return { label: pick(options?.label, "smoke"), route: pick(options?.route, "workspace-smoke"), vignette: Math.max(0, num(options?.vignette, 0.35)), keybinds: rec(options?.keybinds), } } const names = (input: Cfg) => { return { modal: `${input.route}.modal`, screen: `${input.route}.screen`, } } type Keys = TuiKeybindSet const ui = { panel: "#1d1d1d", border: "#4a4a4a", text: "#f0f0f0", muted: "#a5a5a5", accent: "#5f87ff", } type Color = RGBA | string const ink = (map: Record, name: string, fallback: string): Color => { const value = map[name] if (typeof value === "string") return value if (value instanceof RGBA) return value return fallback } const look = (map: Record) => { return { panel: ink(map, "backgroundPanel", ui.panel), border: ink(map, "border", ui.border), text: ink(map, "text", ui.text), muted: ink(map, "textMuted", ui.muted), accent: ink(map, "primary", ui.accent), selected: ink(map, "selectedListItemText", ui.text), } } const tone = (api: TuiPluginApi) => { return look(api.theme.current) } type Skin = { panel: Color border: Color text: Color muted: Color accent: Color selected: Color } const Btn = (props: { txt: string; run: () => void; skin: Skin; on?: boolean }) => { return ( { props.run() }} backgroundColor={props.on ? props.skin.accent : props.skin.border} paddingLeft={1} paddingRight={1} > {props.txt} ) } const parse = (params: Record | undefined) => { const tab = typeof params?.tab === "number" ? params.tab : 0 const count = typeof params?.count === "number" ? params.count : 0 const source = typeof params?.source === "string" ? params.source : "unknown" const note = typeof params?.note === "string" ? params.note : "" const selected = typeof params?.selected === "string" ? params.selected : "" const local = typeof params?.local === "number" ? params.local : 0 return { tab: Math.max(0, Math.min(tab, tabs.length - 1)), count, source, note, selected, local: Math.max(0, local), } } const current = (api: TuiPluginApi, route: Route) => { const value = api.route.current const ok = Object.values(route).includes(value.name) if (!ok) return parse(undefined) if (!("params" in value)) return parse(undefined) return parse(value.params) } const opts = [ { title: "Overview", value: 0, description: "Switch to overview tab", }, { title: "Counter", value: 1, description: "Switch to counter tab", }, { title: "Help", value: 2, description: "Switch to help tab", }, ] const host = (api: TuiPluginApi, input: Cfg, skin: Skin) => { api.ui.dialog.setSize("medium") api.ui.dialog.replace(() => ( {input.label} host overlay Using api.ui.dialog stack with built-in backdrop esc closes · depth {api.ui.dialog.depth} api.ui.dialog.clear()} skin={skin} on /> )) } const warn = (api: TuiPluginApi, route: Route, value: State) => { const DialogAlert = api.ui.DialogAlert api.ui.dialog.setSize("medium") api.ui.dialog.replace(() => ( api.route.navigate(route.screen, { ...value, source: "alert" })} /> )) } const check = (api: TuiPluginApi, route: Route, value: State) => { const DialogConfirm = api.ui.DialogConfirm api.ui.dialog.setSize("medium") api.ui.dialog.replace(() => ( api.route.navigate(route.screen, { ...value, count: value.count + 1, source: "confirm" })} onCancel={() => api.route.navigate(route.screen, { ...value, source: "confirm-cancel" })} /> )) } const entry = (api: TuiPluginApi, route: Route, value: State) => { const DialogPrompt = api.ui.DialogPrompt api.ui.dialog.setSize("medium") api.ui.dialog.replace(() => ( { api.ui.dialog.clear() api.route.navigate(route.screen, { ...value, note, source: "prompt" }) }} onCancel={() => { api.ui.dialog.clear() api.route.navigate(route.screen, value) }} /> )) } const picker = (api: TuiPluginApi, route: Route, value: State) => { const DialogSelect = api.ui.DialogSelect api.ui.dialog.setSize("medium") api.ui.dialog.replace(() => ( { api.ui.dialog.clear() api.route.navigate(route.screen, { ...value, tab: typeof item.value === "number" ? item.value : value.tab, selected: item.title, source: "select", }) }} /> )) } const Screen = (props: { api: TuiPluginApi input: Cfg route: Route keys: Keys meta: TuiPluginMeta params?: Record }) => { const dim = useTerminalDimensions() const value = parse(props.params) const skin = tone(props.api) const set = (local: number, base?: State) => { const next = base ?? current(props.api, props.route) props.api.route.navigate(props.route.screen, { ...next, local: Math.max(0, local), source: "local" }) } const push = (base?: State) => { const next = base ?? current(props.api, props.route) set(next.local + 1, next) } const open = () => { const next = current(props.api, props.route) if (next.local > 0) return set(1, next) } const pop = (base?: State) => { const next = base ?? current(props.api, props.route) const local = Math.max(0, next.local - 1) set(local, next) } const show = () => { setTimeout(() => { open() }, 0) } useKeyboard((evt) => { if (props.api.route.current.name !== props.route.screen) return const next = current(props.api, props.route) if (props.api.ui.dialog.open) { if (props.keys.match("dialog_close", evt)) { evt.preventDefault() evt.stopPropagation() props.api.ui.dialog.clear() return } return } if (next.local > 0) { if (evt.name === "escape" || props.keys.match("local_close", evt)) { evt.preventDefault() evt.stopPropagation() pop(next) return } if (props.keys.match("local_push", evt)) { evt.preventDefault() evt.stopPropagation() push(next) return } return } if (props.keys.match("home", evt)) { evt.preventDefault() evt.stopPropagation() props.api.route.navigate("home") return } if (props.keys.match("left", evt)) { evt.preventDefault() evt.stopPropagation() props.api.route.navigate(props.route.screen, { ...next, tab: (next.tab - 1 + tabs.length) % tabs.length }) return } if (props.keys.match("right", evt)) { evt.preventDefault() evt.stopPropagation() props.api.route.navigate(props.route.screen, { ...next, tab: (next.tab + 1) % tabs.length }) return } if (props.keys.match("up", evt)) { evt.preventDefault() evt.stopPropagation() props.api.route.navigate(props.route.screen, { ...next, count: next.count + 1 }) return } if (props.keys.match("down", evt)) { evt.preventDefault() evt.stopPropagation() props.api.route.navigate(props.route.screen, { ...next, count: next.count - 1 }) return } if (props.keys.match("modal", evt)) { evt.preventDefault() evt.stopPropagation() props.api.route.navigate(props.route.modal, next) return } if (props.keys.match("local", evt)) { evt.preventDefault() evt.stopPropagation() open() return } if (props.keys.match("host", evt)) { evt.preventDefault() evt.stopPropagation() host(props.api, props.input, skin) return } if (props.keys.match("alert", evt)) { evt.preventDefault() evt.stopPropagation() warn(props.api, props.route, next) return } if (props.keys.match("confirm", evt)) { evt.preventDefault() evt.stopPropagation() check(props.api, props.route, next) return } if (props.keys.match("prompt", evt)) { evt.preventDefault() evt.stopPropagation() entry(props.api, props.route, next) return } if (props.keys.match("select", evt)) { evt.preventDefault() evt.stopPropagation() picker(props.api, props.route, next) } }) return ( {props.input.label} screen plugin route {props.keys.print("home")} home {tabs.map((item, i) => { const on = value.tab === i return ( props.api.route.navigate(props.route.screen, { ...value, tab: i })} skin={skin} on={on} /> ) })} {value.tab === 0 ? ( Route: {props.route.screen} plugin state: {props.meta.state} first: {props.meta.state === "first" ? "yes" : "no"} · updated:{" "} {props.meta.state === "updated" ? "yes" : "no"} · loads: {props.meta.load_count} plugin source: {props.meta.source} source: {value.source} note: {value.note || "(none)"} selected: {value.selected || "(none)"} local stack depth: {value.local} host stack open: {props.api.ui.dialog.open ? "yes" : "no"} ) : null} {value.tab === 1 ? ( Counter: {value.count} {props.keys.print("up")} / {props.keys.print("down")} change value ) : null} {value.tab === 2 ? ( {props.keys.print("modal")} modal | {props.keys.print("alert")} alert | {props.keys.print("confirm")}{" "} confirm | {props.keys.print("prompt")} prompt | {props.keys.print("select")} select {props.keys.print("local")} local stack | {props.keys.print("host")} host stack local open: {props.keys.print("local_push")} push nested · esc or {props.keys.print("local_close")}{" "} close {props.keys.print("home")} returns home ) : null} props.api.route.navigate("home")} skin={skin} /> props.api.route.navigate(props.route.modal, value)} skin={skin} on /> host(props.api, props.input, skin)} skin={skin} /> warn(props.api, props.route, value)} skin={skin} /> check(props.api, props.route, value)} skin={skin} /> entry(props.api, props.route, value)} skin={skin} /> picker(props.api, props.route, value)} skin={skin} /> 0} width={dim().width} height={dim().height} alignItems="center" position="absolute" zIndex={3000} paddingTop={dim().height / 4} left={0} top={0} backgroundColor={RGBA.fromInts(0, 0, 0, 160)} onMouseUp={() => { pop() }} > { evt.stopPropagation() }} width={60} maxWidth={dim().width - 2} backgroundColor={skin.panel} border borderColor={skin.border} paddingTop={1} paddingBottom={1} paddingLeft={2} paddingRight={2} gap={1} flexDirection="column" > {props.input.label} local overlay Plugin-owned stack depth: {value.local} {props.keys.print("local_push")} push nested · {props.keys.print("local_close")} pop/close ) } const Modal = (props: { api: TuiPluginApi input: Cfg route: Route keys: Keys params?: Record }) => { const Dialog = props.api.ui.Dialog const value = parse(props.params) const skin = tone(props.api) useKeyboard((evt) => { if (props.api.route.current.name !== props.route.modal) return if (props.keys.match("modal_accept", evt)) { evt.preventDefault() evt.stopPropagation() props.api.route.navigate(props.route.screen, { ...value, source: "modal" }) return } if (props.keys.match("modal_close", evt)) { evt.preventDefault() evt.stopPropagation() props.api.route.navigate("home") } }) return ( props.api.route.navigate("home")}> {props.input.label} modal {props.keys.print("modal")} modal command {props.keys.print("screen")} screen command {props.keys.print("modal_accept")} opens screen · {props.keys.print("modal_close")} closes props.api.route.navigate(props.route.screen, { ...value, source: "modal" })} skin={skin} on /> props.api.route.navigate("home")} skin={skin} /> ) } const home = (input: Cfg): TuiSlotPlugin => ({ slots: { home_logo(ctx) { const map = ctx.theme.current const skin = look(map) const art = [ " $$\\", " $$ |", " $$$$$$$\\ $$$$$$\\$$$$\\ $$$$$$\\ $$ | $$\\ $$$$$$\\", "$$ _____|$$ _$$ _$$\\ $$ __$$\\ $$ | $$ |$$ __$$\\", "\\$$$$$$\\ $$ / $$ / $$ |$$ / $$ |$$$$$$ / $$$$$$$$ |", " \\____$$\\ $$ | $$ | $$ |$$ | $$ |$$ _$$< $$ ____|", "$$$$$$$ |$$ | $$ | $$ |\\$$$$$$ |$$ | \\$$\\ \\$$$$$$$\\", "\\_______/ \\__| \\__| \\__| \\______/ \\__| \\__| \\_______|", ] const fill = [ skin.accent, skin.muted, ink(map, "info", ui.accent), skin.text, ink(map, "success", ui.accent), ink(map, "warning", ui.accent), ink(map, "secondary", ui.accent), ink(map, "error", ui.accent), ] return ( {art.map((line, i) => ( {line} ))} ) }, home_bottom(ctx) { const skin = look(ctx.theme.current) const text = "extra content in the unified home bottom slot" return ( {input.label} {text} ) }, }, }) const block = (input: Cfg, order: number, title: string, text: string): TuiSlotPlugin => ({ order, slots: { sidebar_content(ctx, value) { const skin = look(ctx.theme.current) return ( {title} {text} {input.label} order {order} · session {value.session_id.slice(0, 8)} ) }, }, }) const slot = (input: Cfg): TuiSlotPlugin[] => [ home(input), block(input, 50, "Smoke above", "renders above internal sidebar blocks"), block(input, 250, "Smoke between", "renders between internal sidebar blocks"), block(input, 650, "Smoke below", "renders below internal sidebar blocks"), ] const reg = (api: TuiPluginApi, input: Cfg, keys: Keys) => { const route = names(input) api.command.register(() => [ { title: `${input.label} modal`, value: "plugin.smoke.modal", keybind: keys.get("modal"), category: "Plugin", slash: { name: "smoke", }, onSelect: () => { api.route.navigate(route.modal, { source: "command" }) }, }, { title: `${input.label} screen`, value: "plugin.smoke.screen", keybind: keys.get("screen"), category: "Plugin", slash: { name: "smoke-screen", }, onSelect: () => { api.route.navigate(route.screen, { source: "command", tab: 0, count: 0 }) }, }, { title: `${input.label} alert dialog`, value: "plugin.smoke.alert", category: "Plugin", slash: { name: "smoke-alert", }, onSelect: () => { warn(api, route, current(api, route)) }, }, { title: `${input.label} confirm dialog`, value: "plugin.smoke.confirm", category: "Plugin", slash: { name: "smoke-confirm", }, onSelect: () => { check(api, route, current(api, route)) }, }, { title: `${input.label} prompt dialog`, value: "plugin.smoke.prompt", category: "Plugin", slash: { name: "smoke-prompt", }, onSelect: () => { entry(api, route, current(api, route)) }, }, { title: `${input.label} select dialog`, value: "plugin.smoke.select", category: "Plugin", slash: { name: "smoke-select", }, onSelect: () => { picker(api, route, current(api, route)) }, }, { title: `${input.label} host overlay`, value: "plugin.smoke.host", category: "Plugin", slash: { name: "smoke-host", }, onSelect: () => { host(api, input, tone(api)) }, }, { title: `${input.label} go home`, value: "plugin.smoke.home", category: "Plugin", enabled: api.route.current.name !== "home", onSelect: () => { api.route.navigate("home") }, }, { title: `${input.label} toast`, value: "plugin.smoke.toast", category: "Plugin", onSelect: () => { api.ui.toast({ variant: "info", title: "Smoke", message: "Plugin toast works", duration: 2000, }) }, }, ]) } const tui: TuiPlugin = async (api, options, meta) => { if (options?.enabled === false) return await api.theme.install("./smoke-theme.json") api.theme.set("smoke-theme") const value = cfg(options ?? undefined) const route = names(value) const keys = api.keybind.create(bind, value.keybinds) const fx = new VignetteEffect(value.vignette) const post = fx.apply.bind(fx) api.renderer.addPostProcessFn(post) api.lifecycle.onDispose(() => { api.renderer.removePostProcessFn(post) }) api.route.register([ { name: route.screen, render: ({ params }) => , }, { name: route.modal, render: ({ params }) => , }, ]) reg(api, value, keys) for (const item of slot(value)) { api.slots.register(item) } } const plugin: TuiPluginModule & { id: string } = { id: "tui-smoke", tui, } export default plugin