From 113a7b5996fff82de337abd7106cafcf73f3f5dc Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 8 Oct 2025 19:38:17 -0400 Subject: [PATCH] tui: improve sidebar visibility logic and session title display - Fix sidebar visibility calculation to prevent layout issues - Add session title and share URL to sidebar header - Remove empty text parts from assistant messages - Adjust user message spacing for first message - Add OpenAI codex auth plugin to configuration --- opencode.json | 1 + .../src/cli/cmd/tui/routes/session/index.tsx | 28 ++-- .../cli/cmd/tui/routes/session/sidebar.tsx | 157 ++++++++++-------- 3 files changed, 101 insertions(+), 85 deletions(-) diff --git a/opencode.json b/opencode.json index 4b774c647..04f5def8f 100644 --- a/opencode.json +++ b/opencode.json @@ -1,5 +1,6 @@ { "$schema": "https://opencode.ai/config.json", + "plugin": ["opencode-openai-codex-auth"], "mcp": { "weather": { "type": "local", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 360e249b3..11bd179f1 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -46,6 +46,7 @@ export function Session() { const dimensions = useTerminalDimensions() const [sidebar, setSidebar] = createSignal<"show" | "hide" | "auto">("auto") const wide = createMemo(() => dimensions().width > 120) + const sidebarVisible = createMemo(() => sidebar() === "show" || (sidebar() === "auto" && wide())) createEffect(() => sync.session.sync(route.sessionID)) @@ -234,7 +235,7 @@ export function Session() { category: "Session", onSelect: (dialog) => { setSidebar((prev) => { - if (prev === "auto") return wide() ? "hide" : "show" + if (prev === "auto") return sidebarVisible() ? "hide" : "show" if (prev === "show") return "hide" return "show" }) @@ -284,16 +285,16 @@ export function Session() { return ( - + -
+ +
+ (scroll = r)} scrollbarOptions={{ visible: false }} stickyScroll={true} stickyStart="bottom" - paddingTop={1} - paddingBottom={1} flexGrow={1} > @@ -365,6 +366,7 @@ export function Session() { dialog.replace(() => ) } @@ -395,7 +397,7 @@ export function Session() { - + @@ -412,7 +414,7 @@ const MIME_BADGE: Record = { "application/x-directory": "dir", } -function UserMessage(props: { message: UserMessage; parts: Part[]; onMouseUp: () => void }) { +function UserMessage(props: { message: UserMessage; parts: Part[]; onMouseUp: () => void; index: number }) { const text = createMemo(() => props.parts.flatMap((x) => (x.type === "text" && !x.synthetic ? [x] : []))[0]) const files = createMemo(() => props.parts.flatMap((x) => (x.type === "file" ? [x] : []))) const sync = useSync() @@ -433,7 +435,7 @@ function UserMessage(props: { message: UserMessage; parts: Part[]; onMouseUp: () paddingTop={1} paddingBottom={1} paddingLeft={2} - marginTop={1} + marginTop={props.index === 0 ? 0 : 1} backgroundColor={hover() ? Theme.backgroundElement : Theme.backgroundPanel} customBorderChars={SplitBorder.customBorderChars} borderColor={Theme.secondary} @@ -549,9 +551,11 @@ function ReasoningPart(props: { part: ReasoningPart; message: AssistantMessage } function TextPart(props: { part: TextPart; message: AssistantMessage }) { return ( - - {props.part.text.trim()} - + + + {props.part.text.trim()} + + ) } @@ -596,7 +600,6 @@ function ToolPart(props: { part: ToolPart; message: AssistantMessage }) { const el = this as BoxRenderable const parent = el.parent if (!parent) { - setMargin(0) return } if (el.height > 1) { @@ -608,6 +611,7 @@ function ToolPart(props: { part: ToolPart; message: AssistantMessage }) { const previous = children[index - 1] if (!previous) { setMargin(0) + return } if (previous.height > 1 || previous.id.startsWith("text-")) { setMargin(1) 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 0f5975de2..62887dd0f 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -8,6 +8,7 @@ import type { AssistantMessage } from "@opencode-ai/sdk" export function Sidebar(props: { sessionID: string }) { const sync = useSync() const sdk = useSDK() + const session = createMemo(() => sync.session.get(props.sessionID)!) const todo = createMemo(() => sync.data.todo[props.sessionID] ?? []) const messages = createMemo(() => sync.data.message[props.sessionID] ?? []) const files = createMemo(() => { @@ -51,97 +52,107 @@ export function Sidebar(props: { sessionID: string }) { }) return ( - - - - Context - - {context()?.tokens ?? 0} tokens - {context()?.percentage ?? 0}% used - {cost()} spent - - - - MCP - - - {([key, item]) => ( - - - • - - - {key}{" "} - - - Connected - {(val) => {val().error}} - Disabled in configuration - - - - - )} - - - 0}> + + - LSP + {session().title} - - {(item) => ( + + {session().share!.url} + + + + + Context + + {context()?.tokens ?? 0} tokens + {context()?.percentage ?? 0}% used + {cost()} spent + + + + MCP + + + {([key, item]) => ( - - {item.id} {item.root} + + {key}{" "} + + + Connected + {(val) => {val().error}} + Disabled in configuration + + )} - - 0}> - - - Modified Files - - {(file) => {Locale.truncateMiddle(file, 40)}} - - - 0}> - - - Todo - - - {(todo) => ( - - [{todo.status === "completed" ? "✓" : " "}] {todo.content} - - )} - - - - + 0}> + + + LSP + + + {(item) => ( + + + • + + + {item.id} {item.root} + + + )} + + + + 0}> + + + Modified Files + + {(file) => {Locale.truncateMiddle(file, 40)}} + + + 0}> + + + Todo + + + {(todo) => ( + + [{todo.status === "completed" ? "✓" : " "}] {todo.content} + + )} + + + + + ) }