Forked from OpenCode v1.17.4 with multi-agent system: - 5 agents: aircoding, scheduler, worker, architect, reviewer - Deterministic DAG scheduling engine (coordinator_tick) - Tool whitelists as hard enforcement - AirCoding validation plugin - V1 requirements: C4 docs, ADR, AGENTS.md, debug-log.md - Design documents in docs/
66 lines
2.1 KiB
TypeScript
66 lines
2.1 KiB
TypeScript
import type { TuiPluginApi, TuiSlotContext, TuiSlotMap, TuiSlotProps } from "@opencode-ai/plugin/tui"
|
|
import { createSlot, createSolidSlotRegistry, type JSX, type SolidPlugin } from "@opentui/solid"
|
|
import { createSignal } from "solid-js"
|
|
import { isRecord } from "../util/record"
|
|
|
|
type RuntimeSlotMap = TuiSlotMap<Record<string, object>>
|
|
type SlotView = <Name extends string>(props: TuiSlotProps<Name>) => JSX.Element | null
|
|
|
|
export type HostSlotPlugin<Slots extends Record<string, object> = {}> = SolidPlugin<TuiSlotMap<Slots>, TuiSlotContext>
|
|
export type HostPluginApi = TuiPluginApi
|
|
export type HostSlots = {
|
|
register: {
|
|
(plugin: HostSlotPlugin): () => void
|
|
<Slots extends Record<string, object>>(plugin: HostSlotPlugin<Slots>): () => void
|
|
}
|
|
dispose: () => void
|
|
}
|
|
|
|
function isHostSlotPlugin(value: unknown): value is HostSlotPlugin<Record<string, object>> {
|
|
if (!isRecord(value)) return false
|
|
if (typeof value.id !== "string") return false
|
|
return isRecord(value.slots)
|
|
}
|
|
|
|
export function createSlots() {
|
|
const empty: SlotView = () => null
|
|
const [view, setView] = createSignal<SlotView>(empty)
|
|
const Slot: SlotView = (props) => view()(props)
|
|
|
|
return {
|
|
Slot,
|
|
setup(api: HostPluginApi): HostSlots {
|
|
const registry = createSolidSlotRegistry<RuntimeSlotMap, TuiSlotContext>(
|
|
api.renderer,
|
|
{ theme: api.theme },
|
|
{
|
|
onPluginError(event) {
|
|
console.error("[tui.slot] plugin error", {
|
|
plugin: event.pluginId,
|
|
slot: event.slot,
|
|
phase: event.phase,
|
|
source: event.source,
|
|
message: event.error.message,
|
|
})
|
|
},
|
|
},
|
|
)
|
|
const slot = createSlot<RuntimeSlotMap, TuiSlotContext>(registry)
|
|
setView(() => (props: TuiSlotProps<string>) => slot(props))
|
|
|
|
return {
|
|
register(plugin: HostSlotPlugin) {
|
|
if (!isHostSlotPlugin(plugin)) return () => {}
|
|
return registry.register(plugin)
|
|
},
|
|
dispose() {
|
|
setView(() => empty)
|
|
},
|
|
}
|
|
},
|
|
clear() {
|
|
setView(() => empty)
|
|
},
|
|
}
|
|
}
|