diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx index 3b6b5ef21827..6918400c1513 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx @@ -4,6 +4,7 @@ import { useTheme } from "../context/theme" import { useDialog } from "@tui/ui/dialog" import { useSync } from "@tui/context/sync" import { For, Match, Switch, Show, createMemo } from "solid-js" +import { useTerminalDimensions } from "@opentui/solid" export type DialogStatusProps = {} @@ -11,6 +12,7 @@ export function DialogStatus() { const sync = useSync() const { theme } = useTheme() const dialog = useDialog() + const dimensions = useTerminalDimensions() const enabledFormatters = createMemo(() => sync.data.formatter.filter((f) => f.enabled)) @@ -40,8 +42,8 @@ export function DialogStatus() { }) return ( - - + + Status @@ -49,119 +51,131 @@ export function DialogStatus() { esc - 0} fallback={No MCP Servers}> - - {Object.keys(sync.data.mcp).length} MCP Servers - - {([key, item]) => ( - - + 0} fallback={No MCP Servers}> + + {Object.keys(sync.data.mcp).length} MCP Servers + + {([key, item]) => ( + + + )[item.status], + }} + > + • + + + {key}{" "} + + + Connected + {(val) => val().error} + Disabled in configuration + + Needs authentication (run: opencode mcp auth {key}) + + + {(val) => (val() as { error: string }).error} + + + + + + )} + + + + {sync.data.lsp.length > 0 && ( + + {sync.data.lsp.length} LSP Servers + + {(item) => ( + + - )[item.status], - }} - > - • - - - {key}{" "} - - - Connected - {(val) => val().error} - Disabled in configuration - - Needs authentication (run: opencode mcp auth {key}) - - - {(val) => (val() as { error: string }).error} - - - - - - )} - - - - {sync.data.lsp.length > 0 && ( - - {sync.data.lsp.length} LSP Servers - - {(item) => ( - - - • - - - {item.id} {item.root} - - - )} - - - )} - 0} fallback={No Formatters}> - - {enabledFormatters().length} Formatters - - {(item) => ( - - - • - - - {item.name} - - - )} - - - - 0} fallback={No Plugins}> - - {plugins().length} Plugins - - {(item) => ( - - - • - - - {item.name} - {item.version && @{item.version}} - - - )} - - - + error: theme.error, + }[item.status], + }} + > + • + + + {item.id} {item.root} + + + )} + + + )} + 0} fallback={No Formatters}> + + {enabledFormatters().length} Formatters + + {(item) => ( + + + • + + + {item.name} + + + )} + + + + 0} fallback={No Plugins}> + + {plugins().length} Plugins + + {(item) => ( + + + • + + + {item.name} + {item.version && @{item.version}} + + + )} + + + + ) } diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index 42ac5fbe080a..7b9249071641 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -3,6 +3,7 @@ import { createMemo, For, Show, Switch, Match } from "solid-js" import { createStore } from "solid-js/store" import { useTheme } from "../../context/theme" import { Locale } from "@/util/locale" +import { fileURLToPath } from "bun" import path from "path" import type { AssistantMessage } from "@opencode-ai/sdk/v2" import { Global } from "@/global" @@ -25,6 +26,8 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) { diff: true, todo: true, lsp: true, + formatters: true, + plugins: true, }) // Sort MCP servers alphabetically for consistent display order @@ -40,6 +43,34 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) { ).length, ) + + const enabledFormatters = createMemo(() => sync.data.formatter.filter((f) => f.enabled)) + + const plugins = createMemo(() => { + const list = sync.data.config.plugin ?? [] + const result = list.map((value) => { + if (value.startsWith("file://")) { + const fileURLPath = fileURLToPath(value) + const parts = fileURLPath.split("/") + const filename = parts.pop() || fileURLPath + if (!filename.includes(".")) return { name: filename } + const basename = filename.split(".")[0] + if (basename === "index") { + const dirname = parts.pop() + const name = dirname || basename + return { name } + } + return { name: basename } + } + const index = value.lastIndexOf("@") + if (index <= 0) return { name: value, version: "latest" } + const name = value.substring(0, index) + const version = value.substring(index + 1) + return { name, version } + }) + return result.toSorted((a, b) => a.name.localeCompare(b.name)) + }) + const cost = createMemo(() => { const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0) return new Intl.NumberFormat("en-US", { @@ -210,6 +241,78 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) { + + 0}> + + enabledFormatters().length > 2 && setExpanded("formatters", !expanded.formatters)} + > + 2}> + {expanded.formatters ? "▼" : "▶"} + + + Formatters + + + + + {(item) => ( + + + • + + + {item.name} + + + )} + + + + + 0}> + + plugins().length > 2 && setExpanded("plugins", !expanded.plugins)} + > + 2}> + {expanded.plugins ? "▼" : "▶"} + + + Plugins + + + + + {(item) => ( + + + • + + + {item.name} + {item.version && @{item.version}} + + + )} + + + + 0 && todo().some((t) => t.status !== "completed")}>