mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-24 06:33:01 -04:00
fix(tui): open subagent panel from footer
This commit is contained in:
@@ -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,43 @@ export function PromptFooter(props: { context: Plugin.Context; sessionID?: strin
|
||||
<Match when={props.mode === "normal"}>
|
||||
<Switch>
|
||||
<Match when={live() || status().length > 0}>
|
||||
<text fg={props.context.theme.text.subdued} wrapMode="none" truncate flexShrink={1}>
|
||||
<Show when={live() && shortcut("session.child.first")}>
|
||||
{(value) => <span style={{ fg: props.context.theme.text.default }}>{value()} </span>}
|
||||
<box flexDirection="row" flexShrink={1} minWidth={0}>
|
||||
<Show when={live()}>
|
||||
<box
|
||||
flexDirection="row"
|
||||
flexShrink={0}
|
||||
backgroundColor={
|
||||
liveHovered()
|
||||
? props.context.theme.background.action.primary.focused
|
||||
: props.context.theme.background.action.primary.default
|
||||
}
|
||||
onMouseOver={() => setLiveHovered(true)}
|
||||
onMouseOut={() => setLiveHovered(false)}
|
||||
onMouseDown={() => setLiveHovered(true)}
|
||||
onMouseUp={() => props.context.keymap.dispatch("session.child.first")}
|
||||
>
|
||||
<text
|
||||
fg={
|
||||
liveHovered()
|
||||
? props.context.theme.text.action.primary.focused
|
||||
: props.context.theme.text.action.primary.default
|
||||
}
|
||||
wrapMode="none"
|
||||
>
|
||||
<Show when={shortcut("session.child.first")}>{(value) => <>{value()} </>}</Show>
|
||||
<Show when={subagents()}>{(value) => <>{value()}</>}</Show>
|
||||
<Show when={subagents() && shells()}> · </Show>
|
||||
<Show when={shells()}>{(value) => <>{value()}</>}</Show>
|
||||
</text>
|
||||
</box>
|
||||
</Show>
|
||||
<Show when={subagents()}>{(value) => <span>{value()}</span>}</Show>
|
||||
<Show when={subagents() && shells()}> · </Show>
|
||||
<Show when={shells()}>{(value) => <span>{value()}</span>}</Show>
|
||||
<Show when={live() && status().length > 0}> · </Show>
|
||||
<Show when={status().length > 0}>{status().join(" · ")}</Show>
|
||||
</text>
|
||||
<Show when={status().length > 0}>
|
||||
<text fg={props.context.theme.text.subdued} wrapMode="none" truncate flexShrink={1}>
|
||||
<Show when={live()}> · </Show>
|
||||
{status().join(" · ")}
|
||||
</text>
|
||||
</Show>
|
||||
</box>
|
||||
</Match>
|
||||
<Match when={dimensions().width >= 44}>
|
||||
<text fg={props.context.theme.text.default} flexShrink={0}>
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
/** @jsxImportSource @opentui/solid */
|
||||
import { expect, test } from "bun:test"
|
||||
import { RGBA } from "@opentui/core"
|
||||
import { BoxRenderable, RGBA } 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 focused = RGBA.fromInts(40, 40, 40)
|
||||
const dispatched: string[] = []
|
||||
const context = {
|
||||
location: { directory: "/workspace" },
|
||||
theme: { text: { default: color, subdued: color } },
|
||||
theme: {
|
||||
text: {
|
||||
default: color,
|
||||
subdued: color,
|
||||
action: { primary: { default: color, focused: color } },
|
||||
},
|
||||
background: { action: { primary: { default: RGBA.fromInts(0, 0, 0, 0), focused } } },
|
||||
},
|
||||
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 +49,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]
|
||||
expect(live).toBeInstanceOf(BoxRenderable)
|
||||
expect((live as BoxRenderable).backgroundColor.toInts()).toEqual(focused.toInts())
|
||||
|
||||
await app.mockMouse.click(2, 0)
|
||||
expect(dispatched).toEqual(["session.child.first"])
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user