From 92770a6cd5512be601cf79cbffbf4c13d577653c Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 1 Feb 2026 22:36:57 -0500 Subject: [PATCH] Add spinner animation for Task tool while pending --- .../cmd/tui/component/dialog-session-list.tsx | 10 ++------ .../src/cli/cmd/tui/component/spinner.tsx | 24 ++++++++++++++++++ .../src/cli/cmd/tui/routes/session/index.tsx | 25 ++++++++++++++++--- 3 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 packages/opencode/src/cli/cmd/tui/component/spinner.tsx diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index 85c174c1dcb9..775969bfcb38 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -10,7 +10,7 @@ import { useSDK } from "../context/sdk" import { DialogSessionRename } from "./dialog-session-rename" import { useKV } from "../context/kv" import { createDebouncedSignal } from "../util/signal" -import "opentui-spinner/solid" +import { Spinner } from "./spinner" export function DialogSessionList() { const dialog = useDialog() @@ -32,8 +32,6 @@ export function DialogSessionList() { const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined)) - const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] - const sessions = createMemo(() => searchResults() ?? sync.data.session) const options = createMemo(() => { @@ -56,11 +54,7 @@ export function DialogSessionList() { value: x.id, category, footer: Locale.time(x.time.updated), - gutter: isWorking ? ( - [⋯]}> - - - ) : undefined, + gutter: isWorking ? : undefined, } }) }) diff --git a/packages/opencode/src/cli/cmd/tui/component/spinner.tsx b/packages/opencode/src/cli/cmd/tui/component/spinner.tsx new file mode 100644 index 000000000000..8dc54555043b --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/component/spinner.tsx @@ -0,0 +1,24 @@ +import { Show } from "solid-js" +import { useTheme } from "../context/theme" +import { useKV } from "../context/kv" +import type { JSX } from "@opentui/solid" +import type { RGBA } from "@opentui/core" +import "opentui-spinner/solid" + +const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] + +export function Spinner(props: { children?: JSX.Element; color?: RGBA }) { + const { theme } = useTheme() + const kv = useKV() + const color = () => props.color ?? theme.textMuted + return ( + ⋯ {props.children}}> + + + + {props.children} + + + + ) +} 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 209469bad823..8316d112c9db 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -16,6 +16,7 @@ import path from "path" import { useRoute, useRouteData } from "@tui/context/route" import { useSync } from "@tui/context/sync" import { SplitBorder } from "@tui/component/border" +import { Spinner } from "@tui/component/spinner" import { useTheme } from "@tui/context/theme" import { BoxRenderable, @@ -1559,7 +1560,13 @@ function InlineTool(props: { ) } -function BlockTool(props: { title: string; children: JSX.Element; onClick?: () => void; part?: ToolPart }) { +function BlockTool(props: { + title: string + children: JSX.Element + onClick?: () => void + part?: ToolPart + spinner?: boolean +}) { const { theme } = useTheme() const renderer = useRenderer() const [hover, setHover] = createSignal(false) @@ -1582,9 +1589,16 @@ function BlockTool(props: { title: string; children: JSX.Element; onClick?: () = props.onClick?.() }} > - - {props.title} - + + {props.title} + + } + > + {props.title.replace(/^# /, "")} + {props.children} {error()} @@ -1813,6 +1827,8 @@ function Task(props: ToolProps) { const current = createMemo(() => tools().findLast((x) => x.state.status !== "pending")) + const isRunning = createMemo(() => props.part.state.status === "running") + return ( @@ -1824,6 +1840,7 @@ function Task(props: ToolProps) { : undefined } part={props.part} + spinner={isRunning()} >