| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- import { createKiloClient } from "@kilocode/sdk/v2"
- import { RGBA, type CliRenderer } from "@opentui/core"
- import { createPluginKeybind } from "../../src/cli/cmd/tui/context/plugin-keybinds"
- import type { HostPluginApi } from "../../src/cli/cmd/tui/plugin/slots"
- type Count = {
- event_add: number
- event_drop: number
- route_add: number
- route_drop: number
- command_add: number
- command_drop: number
- }
- function themeCurrent(): HostPluginApi["theme"]["current"] {
- const a = RGBA.fromInts(0, 120, 240)
- const b = RGBA.fromInts(120, 120, 120)
- const c = RGBA.fromInts(230, 230, 230)
- const d = RGBA.fromInts(120, 30, 30)
- const e = RGBA.fromInts(140, 100, 40)
- const f = RGBA.fromInts(20, 140, 80)
- const g = RGBA.fromInts(20, 80, 160)
- const h = RGBA.fromInts(40, 40, 40)
- const i = RGBA.fromInts(60, 60, 60)
- const j = RGBA.fromInts(80, 80, 80)
- return {
- primary: a,
- secondary: b,
- accent: a,
- error: d,
- warning: e,
- success: f,
- info: g,
- text: c,
- textMuted: b,
- selectedListItemText: h,
- background: h,
- backgroundPanel: h,
- backgroundElement: i,
- backgroundMenu: i,
- border: j,
- borderActive: c,
- borderSubtle: i,
- diffAdded: f,
- diffRemoved: d,
- diffContext: b,
- diffHunkHeader: b,
- diffHighlightAdded: f,
- diffHighlightRemoved: d,
- diffAddedBg: h,
- diffRemovedBg: h,
- diffContextBg: h,
- diffLineNumber: b,
- diffAddedLineNumberBg: h,
- diffRemovedLineNumberBg: h,
- markdownText: c,
- markdownHeading: c,
- markdownLink: a,
- markdownLinkText: g,
- markdownCode: f,
- markdownBlockQuote: e,
- markdownEmph: e,
- markdownStrong: c,
- markdownHorizontalRule: b,
- markdownListItem: a,
- markdownListEnumeration: g,
- markdownImage: a,
- markdownImageText: g,
- markdownCodeBlock: c,
- syntaxComment: b,
- syntaxKeyword: a,
- syntaxFunction: g,
- syntaxVariable: c,
- syntaxString: f,
- syntaxNumber: e,
- syntaxType: a,
- syntaxOperator: a,
- syntaxPunctuation: c,
- thinkingOpacity: 0.6,
- }
- }
- type Opts = {
- client?: HostPluginApi["client"] | (() => HostPluginApi["client"])
- renderer?: HostPluginApi["renderer"]
- count?: Count
- keybind?: Partial<HostPluginApi["keybind"]>
- tuiConfig?: HostPluginApi["tuiConfig"]
- app?: Partial<HostPluginApi["app"]>
- state?: {
- ready?: HostPluginApi["state"]["ready"]
- config?: HostPluginApi["state"]["config"]
- provider?: HostPluginApi["state"]["provider"]
- path?: HostPluginApi["state"]["path"]
- vcs?: HostPluginApi["state"]["vcs"]
- workspace?: Partial<HostPluginApi["state"]["workspace"]>
- session?: Partial<HostPluginApi["state"]["session"]>
- part?: HostPluginApi["state"]["part"]
- lsp?: HostPluginApi["state"]["lsp"]
- mcp?: HostPluginApi["state"]["mcp"]
- }
- theme?: {
- selected?: string
- has?: HostPluginApi["theme"]["has"]
- set?: HostPluginApi["theme"]["set"]
- install?: HostPluginApi["theme"]["install"]
- mode?: HostPluginApi["theme"]["mode"]
- ready?: boolean
- current?: HostPluginApi["theme"]["current"]
- }
- }
- export function createTuiPluginApi(opts: Opts = {}): HostPluginApi {
- const kv: Record<string, unknown> = {}
- const count = opts.count
- const ctrl = new AbortController()
- const own = createKiloClient({
- baseUrl: "http://localhost:4096",
- })
- const fallback = () => own
- const read =
- typeof opts.client === "function"
- ? opts.client
- : opts.client
- ? () => opts.client as HostPluginApi["client"]
- : fallback
- const client = () => read()
- let depth = 0
- let size: "medium" | "large" | "xlarge" = "medium"
- const has = opts.theme?.has ?? (() => false)
- let selected = opts.theme?.selected ?? "opencode"
- const key = {
- match: opts.keybind?.match ?? (() => false),
- print: opts.keybind?.print ?? ((name: string) => name),
- }
- const set =
- opts.theme?.set ??
- ((name: string) => {
- if (!has(name)) return false
- selected = name
- return true
- })
- const renderer: CliRenderer = opts.renderer ?? {
- ...Object.create(null),
- once(this: CliRenderer) {
- return this
- },
- }
- function kvGet(name: string): unknown
- function kvGet<Value>(name: string, fallback: Value): Value
- function kvGet(name: string, fallback?: unknown) {
- const value = kv[name]
- if (value === undefined) return fallback
- return value
- }
- return {
- app: {
- get version() {
- return opts.app?.version ?? "0.0.0-test"
- },
- },
- get client() {
- return client()
- },
- event: {
- on: () => {
- if (count) count.event_add += 1
- return () => {
- if (!count) return
- count.event_drop += 1
- }
- },
- },
- renderer,
- slots: {
- register: () => "fixture-slot",
- },
- plugins: {
- list: () => [],
- activate: async () => false,
- deactivate: async () => false,
- add: async () => false,
- install: async () => ({
- ok: false,
- message: "not implemented in fixture",
- }),
- },
- lifecycle: {
- signal: ctrl.signal,
- onDispose() {
- return () => {}
- },
- },
- command: {
- register: () => {
- if (count) count.command_add += 1
- return () => {
- if (!count) return
- count.command_drop += 1
- }
- },
- trigger: () => {},
- show: () => {},
- },
- route: {
- register: () => {
- if (count) count.route_add += 1
- return () => {
- if (!count) return
- count.route_drop += 1
- }
- },
- navigate: () => {},
- get current() {
- return { name: "home" }
- },
- },
- ui: {
- Dialog: () => null,
- DialogAlert: () => null,
- DialogConfirm: () => null,
- DialogPrompt: () => null,
- DialogSelect: () => null,
- Slot: () => null,
- Prompt: () => null,
- toast: () => {},
- dialog: {
- replace: () => {
- depth = 1
- },
- clear: () => {
- depth = 0
- size = "medium"
- },
- setSize: (next) => {
- size = next
- },
- get size() {
- return size
- },
- get depth() {
- return depth
- },
- get open() {
- return depth > 0
- },
- },
- },
- keybind: {
- ...key,
- create:
- opts.keybind?.create ??
- ((defaults, over) => {
- return createPluginKeybind(key, defaults, over)
- }),
- },
- tuiConfig: opts.tuiConfig ?? {},
- kv: {
- get: kvGet,
- set(name, value) {
- kv[name] = value
- },
- get ready() {
- return true
- },
- },
- state: {
- get ready() {
- return opts.state?.ready ?? true
- },
- get config() {
- return opts.state?.config ?? {}
- },
- get provider() {
- return opts.state?.provider ?? []
- },
- get path() {
- return opts.state?.path ?? { state: "", config: "", worktree: "", directory: "" }
- },
- get vcs() {
- return opts.state?.vcs
- },
- workspace: {
- list: opts.state?.workspace?.list ?? (() => []),
- get: opts.state?.workspace?.get ?? (() => undefined),
- },
- session: {
- count: opts.state?.session?.count ?? (() => 0),
- diff: opts.state?.session?.diff ?? (() => []),
- todo: opts.state?.session?.todo ?? (() => []),
- messages: opts.state?.session?.messages ?? (() => []),
- status: opts.state?.session?.status ?? (() => undefined),
- permission: opts.state?.session?.permission ?? (() => []),
- question: opts.state?.session?.question ?? (() => []),
- },
- part: opts.state?.part ?? (() => []),
- lsp: opts.state?.lsp ?? (() => []),
- mcp: opts.state?.mcp ?? (() => []),
- },
- theme: {
- get current() {
- return opts.theme?.current ?? themeCurrent()
- },
- get selected() {
- return selected
- },
- has(name) {
- return has(name)
- },
- set(name) {
- return set(name)
- },
- async install(file) {
- if (opts.theme?.install) return opts.theme.install(file)
- throw new Error("base theme.install should not run")
- },
- mode() {
- if (opts.theme?.mode) return opts.theme.mode()
- return "dark"
- },
- get ready() {
- return opts.theme?.ready ?? true
- },
- },
- }
- }
|