Răsfoiți Sursa

fix(tui): restructure Sidebar component to be scrollable (#3946)

OpeOginni 3 luni în urmă
părinte
comite
69a499f807
1 a modificat fișierele cu 122 adăugiri și 120 ștergeri
  1. 122 120
      packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx

+ 122 - 120
packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx

@@ -40,136 +40,138 @@ export function Sidebar(props: { sessionID: string }) {
 
 
   return (
   return (
     <Show when={session()}>
     <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>
           <box>
             <text fg={theme.text}>
             <text fg={theme.text}>
-              <b>MCP</b>
+              <b>{session().title}</b>
             </text>
             </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>
           </box>
-        </Show>
-        <Show when={sync.data.lsp.length > 0}>
           <box>
           <box>
             <text fg={theme.text}>
             <text fg={theme.text}>
-              <b>LSP</b>
+              <b>Context</b>
             </text>
             </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>
           </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>
                     </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>
                   </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>
     </Show>
   )
   )
 }
 }