summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOpeOginni <[email protected]>2025-11-05 18:33:30 +0100
committerGitHub <[email protected]>2025-11-05 11:33:30 -0600
commit69a499f80786b2656121cee4469f39df2e6c40e7 (patch)
treeaec00083c25ae0afd780332add4d32ff9f08c707
parent37e564139fc1b46a66f916280a3baf3e3e399bf2 (diff)
downloadopencode-69a499f80786b2656121cee4469f39df2e6c40e7.tar.gz
opencode-69a499f80786b2656121cee4469f39df2e6c40e7.zip
fix(tui): restructure Sidebar component to be scrollable (#3946)
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx242
1 files changed, 122 insertions, 120 deletions
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 c63297db2..2f1451a5f 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx
@@ -40,136 +40,138 @@ export function Sidebar(props: { sessionID: string }) {
return (
<Show when={session()}>
- <box flexShrink={0} gap={1} width={40}>
- <box>
- <text fg={theme.text}>
- <b>{session().title}</b>
- </text>
- <Show when={session().share?.url}>
- <text fg={theme.textMuted}>{session().share!.url}</text>
- </Show>
- </box>
- <box>
- <text fg={theme.text}>
- <b>Context</b>
- </text>
- <text fg={theme.textMuted}>{context()?.tokens ?? 0} tokens</text>
- <text fg={theme.textMuted}>{context()?.percentage ?? 0}% used</text>
- <text fg={theme.textMuted}>{cost()} spent</text>
- </box>
- <Show when={Object.keys(sync.data.mcp).length > 0}>
+ <scrollbox width={40}>
+ <box flexShrink={0} gap={1} paddingRight={1}>
<box>
<text fg={theme.text}>
- <b>MCP</b>
+ <b>{session().title}</b>
</text>
- <For each={Object.entries(sync.data.mcp)}>
- {([key, item]) => (
- <box flexDirection="row" gap={1}>
- <text
- flexShrink={0}
- style={{
- fg: {
- connected: theme.success,
- failed: theme.error,
- disabled: theme.textMuted,
- }[item.status],
- }}
- >
- •
- </text>
- <text fg={theme.text} wrapMode="word">
- {key}{" "}
- <span style={{ fg: theme.textMuted }}>
- <Switch>
- <Match when={item.status === "connected"}>Connected</Match>
- <Match when={item.status === "failed" && item}>
- {(val) => <i>{val().error}</i>}
- </Match>
- <Match when={item.status === "disabled"}>Disabled in configuration</Match>
- </Switch>
- </span>
- </text>
- </box>
- )}
- </For>
+ <Show when={session().share?.url}>
+ <text fg={theme.textMuted}>{session().share!.url}</text>
+ </Show>
</box>
- </Show>
- <Show when={sync.data.lsp.length > 0}>
<box>
<text fg={theme.text}>
- <b>LSP</b>
+ <b>Context</b>
</text>
- <For each={sync.data.lsp}>
- {(item) => (
- <box flexDirection="row" gap={1}>
- <text
- flexShrink={0}
- style={{
- fg: {
- connected: theme.success,
- error: theme.error,
- }[item.status],
- }}
- >
- •
- </text>
- <text fg={theme.textMuted}>
- {item.id} {item.root}
- </text>
- </box>
- )}
- </For>
+ <text fg={theme.textMuted}>{context()?.tokens ?? 0} tokens</text>
+ <text fg={theme.textMuted}>{context()?.percentage ?? 0}% used</text>
+ <text fg={theme.textMuted}>{cost()} spent</text>
</box>
- </Show>
- <Show when={session().summary?.diffs}>
- <box>
- <text fg={theme.text}>
- <b>Modified Files</b>
- </text>
- <For each={session().summary?.diffs || []}>
- {(item) => {
- const file = createMemo(() => {
- const splits = item.file.split(path.sep).filter(Boolean)
- const last = splits.at(-1)!
- const rest = splits.slice(0, -1).join(path.sep)
- return Locale.truncateMiddle(rest, 30 - last.length) + "/" + last
- })
- return (
- <box flexDirection="row" gap={1} justifyContent="space-between">
- <text fg={theme.textMuted} wrapMode="char">
- {file()}
+ <Show when={Object.keys(sync.data.mcp).length > 0}>
+ <box>
+ <text fg={theme.text}>
+ <b>MCP</b>
+ </text>
+ <For each={Object.entries(sync.data.mcp)}>
+ {([key, item]) => (
+ <box flexDirection="row" gap={1}>
+ <text
+ flexShrink={0}
+ style={{
+ fg: {
+ connected: theme.success,
+ failed: theme.error,
+ disabled: theme.textMuted,
+ }[item.status],
+ }}
+ >
+ •
+ </text>
+ <text fg={theme.text} wrapMode="word">
+ {key}{" "}
+ <span style={{ fg: theme.textMuted }}>
+ <Switch>
+ <Match when={item.status === "connected"}>Connected</Match>
+ <Match when={item.status === "failed" && item}>
+ {(val) => <i>{val().error}</i>}
+ </Match>
+ <Match when={item.status === "disabled"}>Disabled in configuration</Match>
+ </Switch>
+ </span>
</text>
- <box flexDirection="row" gap={1} flexShrink={0}>
- <Show when={item.additions}>
- <text fg={theme.diffAdded}>+{item.additions}</text>
- </Show>
- <Show when={item.deletions}>
- <text fg={theme.diffRemoved}>-{item.deletions}</text>
- </Show>
- </box>
</box>
- )
- }}
- </For>
- </box>
- </Show>
- <Show when={todo().length > 0}>
- <box>
- <text fg={theme.text}>
- <b>Todo</b>
- </text>
- <For each={todo()}>
- {(todo) => (
- <text
- style={{ fg: todo.status === "in_progress" ? theme.success : theme.textMuted }}
- >
- [{todo.status === "completed" ? "✓" : " "}] {todo.content}
- </text>
- )}
- </For>
- </box>
- </Show>
- </box>
+ )}
+ </For>
+ </box>
+ </Show>
+ <Show when={sync.data.lsp.length > 0}>
+ <box>
+ <text fg={theme.text}>
+ <b>LSP</b>
+ </text>
+ <For each={sync.data.lsp}>
+ {(item) => (
+ <box flexDirection="row" gap={1}>
+ <text
+ flexShrink={0}
+ style={{
+ fg: {
+ connected: theme.success,
+ error: theme.error,
+ }[item.status],
+ }}
+ >
+ •
+ </text>
+ <text fg={theme.textMuted}>
+ {item.id} {item.root}
+ </text>
+ </box>
+ )}
+ </For>
+ </box>
+ </Show>
+ <Show when={session().summary?.diffs}>
+ <box>
+ <text fg={theme.text}>
+ <b>Modified Files</b>
+ </text>
+ <For each={session().summary?.diffs || []}>
+ {(item) => {
+ const file = createMemo(() => {
+ const splits = item.file.split(path.sep).filter(Boolean)
+ const last = splits.at(-1)!
+ const rest = splits.slice(0, -1).join(path.sep)
+ return Locale.truncateMiddle(rest, 30 - last.length) + "/" + last
+ })
+ return (
+ <box flexDirection="row" gap={1} justifyContent="space-between">
+ <text fg={theme.textMuted} wrapMode="char">
+ {file()}
+ </text>
+ <box flexDirection="row" gap={1} flexShrink={0}>
+ <Show when={item.additions}>
+ <text fg={theme.diffAdded}>+{item.additions}</text>
+ </Show>
+ <Show when={item.deletions}>
+ <text fg={theme.diffRemoved}>-{item.deletions}</text>
+ </Show>
+ </box>
+ </box>
+ )
+ }}
+ </For>
+ </box>
+ </Show>
+ <Show when={todo().length > 0}>
+ <box>
+ <text fg={theme.text}>
+ <b>Todo</b>
+ </text>
+ <For each={todo()}>
+ {(todo) => (
+ <text
+ style={{ fg: todo.status === "in_progress" ? theme.success : theme.textMuted }}
+ >
+ [{todo.status === "completed" ? "✓" : " "}] {todo.content}
+ </text>
+ )}
+ </For>
+ </box>
+ </Show>
+ </box>
+ </scrollbox>
</Show>
)
}