diff options
| author | Dax <[email protected]> | 2026-02-01 22:51:55 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-01 22:51:55 -0500 |
| commit | 0dc80df6fd1f4c89cebf9ed20cd9d9291f996236 (patch) | |
| tree | d0d2a3124595512f1eb82ae173da5a998fc8338f | |
| parent | 8e985e0a75ca5f2cb859434fe82dee7ea81cb59f (diff) | |
| download | opencode-0dc80df6fd1f4c89cebf9ed20cd9d9291f996236.tar.gz opencode-0dc80df6fd1f4c89cebf9ed20cd9d9291f996236.zip | |
Add spinner animation for Task tool (#11725)
3 files changed, 47 insertions, 12 deletions
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 85c174c1d..775969bfc 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 ? ( - <Show when={kv.get("animations_enabled", true)} fallback={<text fg={theme.textMuted}>[⋯]</text>}> - <spinner frames={spinnerFrames} interval={80} color={theme.primary} /> - </Show> - ) : undefined, + gutter: isWorking ? <Spinner /> : 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 000000000..8dc545550 --- /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 ( + <Show when={kv.get("animations_enabled", true)} fallback={<text fg={color()}>⋯ {props.children}</text>}> + <box flexDirection="row" gap={1}> + <spinner frames={frames} interval={80} color={color()} /> + <Show when={props.children}> + <text fg={color()}>{props.children}</text> + </Show> + </box> + </Show> + ) +} 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 209469bad..8316d112c 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?.() }} > - <text paddingLeft={3} fg={theme.textMuted}> - {props.title} - </text> + <Show + when={props.spinner} + fallback={ + <text paddingLeft={3} fg={theme.textMuted}> + {props.title} + </text> + } + > + <Spinner color={theme.textMuted}>{props.title.replace(/^# /, "")}</Spinner> + </Show> {props.children} <Show when={error()}> <text fg={theme.error}>{error()}</text> @@ -1813,6 +1827,8 @@ function Task(props: ToolProps<typeof TaskTool>) { const current = createMemo(() => tools().findLast((x) => x.state.status !== "pending")) + const isRunning = createMemo(() => props.part.state.status === "running") + return ( <Switch> <Match when={props.input.description || props.input.subagent_type}> @@ -1824,6 +1840,7 @@ function Task(props: ToolProps<typeof TaskTool>) { : undefined } part={props.part} + spinner={isRunning()} > <box> <text style={{ fg: theme.textMuted }}> |
