untangle
parent
557a92a1f3
commit
11c76c6d9a
|
|
@ -1,12 +1,4 @@
|
|||
import {
|
||||
createSlot,
|
||||
createSolidSlotRegistry,
|
||||
render,
|
||||
useKeyboard,
|
||||
useRenderer,
|
||||
useTerminalDimensions,
|
||||
type SolidPlugin,
|
||||
} from "@opentui/solid"
|
||||
import { render, useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/solid"
|
||||
import { Clipboard } from "@tui/util/clipboard"
|
||||
import { Selection } from "@tui/util/selection"
|
||||
import { createCliRenderer, MouseButton, TextAttributes, type CliRendererConfig } from "@opentui/core"
|
||||
|
|
@ -49,9 +41,7 @@ import { writeHeapSnapshot } from "v8"
|
|||
import { PromptRefProvider, usePromptRef } from "./context/prompt"
|
||||
import { TuiConfigProvider } from "./context/tui-config"
|
||||
import { TuiConfig } from "@/config/tui"
|
||||
import type { TuiSlotContext, TuiSlotMap, TuiSlots } from "@opencode-ai/plugin/tui"
|
||||
|
||||
type TuiSlot = <K extends keyof TuiSlotMap>(props: { name: K } & TuiSlotMap[K]) => unknown
|
||||
import { TuiPlugin } from "./plugin"
|
||||
|
||||
async function getTerminalBackgroundColor(): Promise<"dark" | "light"> {
|
||||
// can't set raw mode if not a TTY
|
||||
|
|
@ -160,29 +150,7 @@ export function tui(input: {
|
|||
}
|
||||
|
||||
const renderer = await createCliRenderer(rendererConfig(input.config))
|
||||
const registry = createSolidSlotRegistry<TuiSlotMap, TuiSlotContext>(
|
||||
renderer,
|
||||
{},
|
||||
{
|
||||
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<TuiSlotMap, TuiSlotContext>(registry)
|
||||
const slot: TuiSlot = (props) => Slot(props)
|
||||
const slots: TuiSlots = {
|
||||
register(plugin) {
|
||||
console.error("[tui.slot] register", plugin.id)
|
||||
return registry.register(plugin as SolidPlugin<TuiSlotMap, TuiSlotContext>)
|
||||
},
|
||||
}
|
||||
const slots = TuiPlugin.slots(renderer)
|
||||
|
||||
await render(() => {
|
||||
return (
|
||||
|
|
@ -214,7 +182,7 @@ export function tui(input: {
|
|||
<FrecencyProvider>
|
||||
<PromptHistoryProvider>
|
||||
<PromptRefProvider>
|
||||
<App slot={slot} />
|
||||
<App />
|
||||
</PromptRefProvider>
|
||||
</PromptHistoryProvider>
|
||||
</FrecencyProvider>
|
||||
|
|
@ -238,7 +206,7 @@ export function tui(input: {
|
|||
})
|
||||
}
|
||||
|
||||
function App(props: { slot: TuiSlot }) {
|
||||
function App() {
|
||||
const route = useRoute()
|
||||
const dimensions = useTerminalDimensions()
|
||||
const renderer = useRenderer()
|
||||
|
|
@ -805,10 +773,10 @@ function App(props: { slot: TuiSlot }) {
|
|||
>
|
||||
<Switch>
|
||||
<Match when={route.data.type === "home"}>
|
||||
<Home slot={props.slot} />
|
||||
<Home />
|
||||
</Match>
|
||||
<Match when={route.data.type === "session"}>
|
||||
<Session slot={props.slot} />
|
||||
<Session />
|
||||
</Match>
|
||||
</Switch>
|
||||
</box>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,13 @@ import {
|
|||
type TuiPlugin as TuiPluginFn,
|
||||
type TuiPluginInput,
|
||||
type TuiPluginModule,
|
||||
type TuiSlotContext,
|
||||
type TuiSlotMap,
|
||||
type TuiSlotPlugin,
|
||||
type TuiSlots,
|
||||
} from "@opencode-ai/plugin/tui"
|
||||
import { createSlot, createSolidSlotRegistry, type SolidPlugin } from "@opentui/solid"
|
||||
import type { CliRenderer } from "@opentui/core"
|
||||
import type { JSX } from "solid-js"
|
||||
import "@opentui/solid/preload"
|
||||
|
||||
|
|
@ -14,9 +19,46 @@ import { BunProc } from "@/bun"
|
|||
import { Instance } from "@/project/instance"
|
||||
import { registerThemes } from "./context/theme"
|
||||
|
||||
type Slot = <K extends keyof TuiSlotMap>(props: { name: K } & TuiSlotMap[K]) => unknown
|
||||
|
||||
function empty<K extends keyof TuiSlotMap>(_props: { name: K } & TuiSlotMap[K]) {
|
||||
return null
|
||||
}
|
||||
|
||||
export namespace TuiPlugin {
|
||||
const log = Log.create({ service: "tui.plugin" })
|
||||
let loaded: Promise<void> | undefined
|
||||
let view: Slot = empty
|
||||
|
||||
export const Slot: Slot = (props) => view(props)
|
||||
|
||||
export function slots(renderer: CliRenderer): TuiSlots {
|
||||
const reg = createSolidSlotRegistry<TuiSlotMap, TuiSlotContext>(
|
||||
renderer,
|
||||
{},
|
||||
{
|
||||
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<TuiSlotMap, TuiSlotContext>(reg)
|
||||
view = (props) => slot(props)
|
||||
|
||||
return {
|
||||
register(plugin) {
|
||||
console.error("[tui.slot] register", plugin.id)
|
||||
return reg.register(plugin as SolidPlugin<TuiSlotMap, TuiSlotContext>)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function init(input: TuiPluginInput) {
|
||||
if (loaded) return loaded
|
||||
|
|
@ -32,7 +74,7 @@ export namespace TuiPlugin {
|
|||
return BunProc.install(pkg, version)
|
||||
}
|
||||
|
||||
function slot(entry: unknown) {
|
||||
function pick(entry: unknown) {
|
||||
if (!entry || typeof entry !== "object") return
|
||||
if ("id" in entry && typeof entry.id === "string" && "slots" in entry && typeof entry.slots === "object") {
|
||||
return entry as TuiSlotPlugin<JSX.Element>
|
||||
|
|
@ -88,7 +130,7 @@ export namespace TuiPlugin {
|
|||
registerThemes(pluginEntry.themes as Record<string, unknown>)
|
||||
}
|
||||
|
||||
const plugin = slot(pluginEntry)
|
||||
const plugin = pick(pluginEntry)
|
||||
if (plugin) {
|
||||
input.slots.register(plugin)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,14 +15,12 @@ import { Installation } from "@/installation"
|
|||
import { useKV } from "../context/kv"
|
||||
import { useCommandDialog } from "../component/dialog-command"
|
||||
import { useLocal } from "../context/local"
|
||||
import type { TuiSlotMap } from "@opencode-ai/plugin/tui"
|
||||
|
||||
type Slot = <K extends "home_hint" | "home_footer">(props: { name: K } & TuiSlotMap[K]) => unknown
|
||||
import { TuiPlugin } from "../plugin"
|
||||
|
||||
// TODO: what is the best way to do this?
|
||||
let once = false
|
||||
|
||||
export function Home(props: { slot: Slot }) {
|
||||
export function Home() {
|
||||
const sync = useSync()
|
||||
const kv = useKV()
|
||||
const { theme } = useTheme()
|
||||
|
|
@ -75,7 +73,7 @@ export function Home(props: { slot: Slot }) {
|
|||
</Switch>
|
||||
</text>
|
||||
</Show>
|
||||
{props.slot({ name: "home_hint" }) as never}
|
||||
{TuiPlugin.Slot({ name: "home_hint" }) as never}
|
||||
</box>
|
||||
)
|
||||
|
||||
|
|
@ -154,7 +152,7 @@ export function Home(props: { slot: Slot }) {
|
|||
</Show>
|
||||
</box>
|
||||
<box flexGrow={1} />
|
||||
{props.slot({ name: "home_footer" }) as never}
|
||||
{TuiPlugin.Slot({ name: "home_footer" }) as never}
|
||||
<box flexShrink={0}>
|
||||
<text fg={theme.textMuted}>{Installation.VERSION}</text>
|
||||
</box>
|
||||
|
|
|
|||
|
|
@ -80,12 +80,10 @@ import { DialogExportOptions } from "../../ui/dialog-export-options"
|
|||
import { formatTranscript } from "../../util/transcript"
|
||||
import { UI } from "@/cli/ui.ts"
|
||||
import { useTuiConfig } from "../../context/tui-config"
|
||||
import type { TuiSlotMap } from "@opencode-ai/plugin/tui"
|
||||
import { TuiPlugin } from "../../plugin"
|
||||
|
||||
addDefaultParsers(parsers.parsers)
|
||||
|
||||
type Slot = (props: { name: "session_footer"; session_id: TuiSlotMap["session_footer"]["session_id"] }) => unknown
|
||||
|
||||
class CustomSpeedScroll implements ScrollAcceleration {
|
||||
constructor(private speed: number) {}
|
||||
|
||||
|
|
@ -115,7 +113,7 @@ function use() {
|
|||
return ctx
|
||||
}
|
||||
|
||||
export function Session(props: { slot: Slot }) {
|
||||
export function Session() {
|
||||
const route = useRouteData("session")
|
||||
const { navigate } = useRoute()
|
||||
const sync = useSync()
|
||||
|
|
@ -1180,7 +1178,7 @@ export function Session(props: { slot: Slot }) {
|
|||
}}
|
||||
sessionID={route.sessionID}
|
||||
/>
|
||||
{props.slot({ name: "session_footer", session_id: route.sessionID }) as never}
|
||||
{TuiPlugin.Slot({ name: "session_footer", session_id: route.sessionID }) as never}
|
||||
</box>
|
||||
</Show>
|
||||
<Toast />
|
||||
|
|
|
|||
Loading…
Reference in New Issue