From 241a88a5d9856644d0932e742e53ae5b2ce26976 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Wed, 19 Aug 2026 10:33:44 -0400 Subject: [PATCH] fix(tui): open subagent panel from footer (#43325) --- .../tui/src/feature-plugins/prompt/footer.tsx | 39 ++++++++++++++----- .../feature-plugins/prompt-footer.test.tsx | 20 +++++++++- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/packages/tui/src/feature-plugins/prompt/footer.tsx b/packages/tui/src/feature-plugins/prompt/footer.tsx index 4b2d6020b2..8d818e8a95 100644 --- a/packages/tui/src/feature-plugins/prompt/footer.tsx +++ b/packages/tui/src/feature-plugins/prompt/footer.tsx @@ -1,5 +1,5 @@ import { Plugin } from "@opencode-ai/plugin/tui" -import { createMemo, Match, Show, Switch } from "solid-js" +import { createMemo, createSignal, Match, Show, Switch } from "solid-js" import { contextUsage, formatContextUsage } from "../../util/session" import { useTerminalDimensions } from "@opentui/solid" @@ -10,6 +10,7 @@ const money = new Intl.NumberFormat("en-US", { export function PromptFooter(props: { context: Plugin.Context; sessionID?: string; mode: "normal" | "shell" }) { const dimensions = useTerminalDimensions() + const [liveHovered, setLiveHovered] = createSignal(false) const subagents = createMemo(() => { if (!props.sessionID) return 0 const count = props.context.data.session @@ -47,16 +48,34 @@ export function PromptFooter(props: { context: Plugin.Context; sessionID?: strin 0}> - - - {(value) => {value()} } + + + setLiveHovered(true)} + onMouseOut={() => setLiveHovered(false)} + onMouseUp={() => props.context.keymap.dispatch("session.child.first")} + > + + + {(value) => {value()} } + + {(value) => <>{value()}} + · + {(value) => <>{value()}} + + - {(value) => {value()}} - · - {(value) => {value()}} - 0}> · - 0}>{status().join(" · ")} - + 0}> + + · + {status().join(" · ")} + + + = 44}> diff --git a/packages/tui/test/feature-plugins/prompt-footer.test.tsx b/packages/tui/test/feature-plugins/prompt-footer.test.tsx index dbb7f9da7e..eb9bb23358 100644 --- a/packages/tui/test/feature-plugins/prompt-footer.test.tsx +++ b/packages/tui/test/feature-plugins/prompt-footer.test.tsx @@ -1,18 +1,26 @@ /** @jsxImportSource @opentui/solid */ import { expect, test } from "bun:test" -import { RGBA } from "@opentui/core" +import { RGBA, TextRenderable } from "@opentui/core" import { testRender } from "@opentui/solid" import type { Context } from "@opencode-ai/plugin/tui/context" import { PromptFooter } from "../../src/feature-plugins/prompt/footer" test("prompt footer separates simultaneous subagent, shell, and usage status", async () => { const color = RGBA.fromInts(200, 200, 200) + const subdued = RGBA.fromInts(100, 100, 100) + const dispatched: string[] = [] const context = { location: { directory: "/workspace" }, - theme: { text: { default: color, subdued: color } }, + theme: { + text: { + default: color, + subdued, + }, + }, keymap: { shortcuts: (id: string) => id === "session.child.first" ? ["ctrl+j"] : id === "command.palette.show" ? ["ctrl+p"] : [], + dispatch: (id: string) => dispatched.push(id), }, data: { session: { @@ -39,6 +47,14 @@ test("prompt footer separates simultaneous subagent, shell, and usage status", a await app.renderOnce() expect(app.captureCharFrame()).toContain("ctrl+j 1 subagent · 1 shell · $1.00") expect(app.captureCharFrame()).toContain("ctrl+p commands") + + await app.mockMouse.moveTo(2, 0) + const live = app.renderer.root.getChildren()[0]?.getChildren()[0]?.getChildren()[0] + expect(live).toBeInstanceOf(TextRenderable) + expect((live as TextRenderable).fg.toInts()).toEqual(color.toInts()) + + await app.mockMouse.click(2, 0) + expect(dispatched).toEqual(["session.child.first"]) } finally { app.renderer.destroy() }