Skip to content

Commit c3a9ec4

Browse files
authored
fix: restore subagent footer and fix style guide violations (anomalyco#19491)
1 parent 41b0d03 commit c3a9ec4

4 files changed

Lines changed: 115 additions & 4 deletions

File tree

packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ export function DialogModel(props: { providerID?: string }) {
138138
local.model.set({ providerID, modelID }, { recent: true })
139139
if (local.model.variant.list().length > 0) {
140140
dialog.replace(() => <DialogVariant />)
141-
} else {
142-
dialog.clear()
141+
return
143142
}
143+
dialog.clear()
144144
}
145145

146146
return (

packages/opencode/src/cli/cmd/tui/component/dialog-variant.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { createMemo } from "solid-js"
22
import { useLocal } from "@tui/context/local"
3-
import { useSync } from "@tui/context/sync"
43
import { DialogSelect } from "@tui/ui/dialog-select"
54
import { useDialog } from "@tui/ui/dialog"
65

packages/opencode/src/cli/cmd/tui/routes/session/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import { DialogTimeline } from "./dialog-timeline"
6161
import { DialogForkFromTimeline } from "./dialog-fork-from-timeline"
6262
import { DialogSessionRename } from "../../component/dialog-session-rename"
6363
import { Sidebar } from "./sidebar"
64+
import { SubagentFooter } from "./subagent-footer.tsx"
6465
import { Flag } from "@/flag/flag"
6566
import { LANGUAGE_EXTENSIONS } from "@/lsp/language"
6667
import parsers from "../../../../../../parsers-config.ts"
@@ -1054,7 +1055,6 @@ export function Session() {
10541055
flexGrow={1}
10551056
scrollAcceleration={scrollAcceleration()}
10561057
>
1057-
<box height={1} />
10581058
<For each={messages()}>
10591059
{(message, index) => (
10601060
<Switch>
@@ -1158,6 +1158,9 @@ export function Session() {
11581158
<Show when={permissions().length === 0 && questions().length > 0}>
11591159
<QuestionPrompt request={questions()[0]} />
11601160
</Show>
1161+
<Show when={session()?.parentID}>
1162+
<SubagentFooter />
1163+
</Show>
11611164
<Prompt
11621165
visible={!session()?.parentID && permissions().length === 0 && questions().length === 0}
11631166
ref={(r) => {
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { createMemo, createSignal, Show } from "solid-js"
2+
import { useRouteData } from "@tui/context/route"
3+
import { useSync } from "@tui/context/sync"
4+
import { useTheme } from "@tui/context/theme"
5+
import { SplitBorder } from "@tui/component/border"
6+
import type { AssistantMessage } from "@opencode-ai/sdk/v2"
7+
import { useCommandDialog } from "@tui/component/dialog-command"
8+
import { useKeybind } from "../../context/keybind"
9+
import { Locale } from "@/util/locale"
10+
import { useTerminalDimensions } from "@opentui/solid"
11+
12+
export function SubagentFooter() {
13+
const route = useRouteData("session")
14+
const sync = useSync()
15+
const messages = createMemo(() => sync.data.message[route.sessionID] ?? [])
16+
17+
const usage = createMemo(() => {
18+
const msg = messages()
19+
const last = msg.findLast((item): item is AssistantMessage => item.role === "assistant" && item.tokens.output > 0)
20+
if (!last) return
21+
22+
const tokens =
23+
last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
24+
if (tokens <= 0) return
25+
26+
const model = sync.data.provider.find((item) => item.id === last.providerID)?.models[last.modelID]
27+
const pct = model?.limit.context ? `${Math.round((tokens / model.limit.context) * 100)}%` : undefined
28+
const cost = msg.reduce((sum, item) => sum + (item.role === "assistant" ? item.cost : 0), 0)
29+
30+
const money = new Intl.NumberFormat("en-US", {
31+
style: "currency",
32+
currency: "USD",
33+
})
34+
35+
return {
36+
context: pct ? `${Locale.number(tokens)} (${pct})` : Locale.number(tokens),
37+
cost: cost > 0 ? money.format(cost) : undefined,
38+
}
39+
})
40+
41+
const { theme } = useTheme()
42+
const keybind = useKeybind()
43+
const command = useCommandDialog()
44+
const [hover, setHover] = createSignal<"parent" | "prev" | "next" | null>(null)
45+
const dimensions = useTerminalDimensions()
46+
47+
return (
48+
<box flexShrink={0}>
49+
<box
50+
paddingTop={1}
51+
paddingBottom={1}
52+
paddingLeft={2}
53+
paddingRight={1}
54+
{...SplitBorder}
55+
border={["left"]}
56+
borderColor={theme.border}
57+
flexShrink={0}
58+
backgroundColor={theme.backgroundPanel}
59+
>
60+
<box flexDirection="row" justifyContent="space-between" gap={1}>
61+
<box flexDirection="row" gap={2}>
62+
<text fg={theme.text}>
63+
<b>Subagent session</b>
64+
</text>
65+
<Show when={usage()}>
66+
{(item) => (
67+
<text fg={theme.textMuted} wrapMode="none">
68+
{[item().context, item().cost].filter(Boolean).join(" · ")}
69+
</text>
70+
)}
71+
</Show>
72+
</box>
73+
<box flexDirection="row" gap={2}>
74+
<box
75+
onMouseOver={() => setHover("parent")}
76+
onMouseOut={() => setHover(null)}
77+
onMouseUp={() => command.trigger("session.parent")}
78+
backgroundColor={hover() === "parent" ? theme.backgroundElement : theme.backgroundPanel}
79+
>
80+
<text fg={theme.text}>
81+
Parent <span style={{ fg: theme.textMuted }}>{keybind.print("session_parent")}</span>
82+
</text>
83+
</box>
84+
<box
85+
onMouseOver={() => setHover("prev")}
86+
onMouseOut={() => setHover(null)}
87+
onMouseUp={() => command.trigger("session.child.previous")}
88+
backgroundColor={hover() === "prev" ? theme.backgroundElement : theme.backgroundPanel}
89+
>
90+
<text fg={theme.text}>
91+
Prev <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span>
92+
</text>
93+
</box>
94+
<box
95+
onMouseOver={() => setHover("next")}
96+
onMouseOut={() => setHover(null)}
97+
onMouseUp={() => command.trigger("session.child.next")}
98+
backgroundColor={hover() === "next" ? theme.backgroundElement : theme.backgroundPanel}
99+
>
100+
<text fg={theme.text}>
101+
Next <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle")}</span>
102+
</text>
103+
</box>
104+
</box>
105+
</box>
106+
</box>
107+
</box>
108+
)
109+
}

0 commit comments

Comments
 (0)