@@ -3,16 +3,15 @@ import { Icon } from "@opencode/ui/icon"
33import { Keybind } from "@opencode/ui/keybind"
44import { Slash } from "@opencode/ui/slash"
55import { SessionProgressIndicatorV2 } from "@opencode/session-ui/v2/session-progress-indicator-v2"
6- import { createEffect , createSignal , Match , onCleanup , Show , Switch } from "solid-js"
6+ import { createEffect , createSignal , For , Match , onCleanup , Show , Switch } from "solid-js"
77import { useLanguage } from "@/runtime/i18n/language"
88import { formatKeybind } from "@/shell/commands/command"
99import { formatElapsed } from "./activity-chips"
1010
1111export type SubagentStatus = "running" | "succeeded" | "failed" | "interrupted"
1212
13- // A subagent session takes no prompts, so its composer slot shows what the run is: the composer's geometry with the
14- // agent, model and variant where the composer's chips sit, the run's state where the placeholder sits, and the way back
15- // where the send button sits.
13+ // A subagent session takes no prompts, so its composer slot is one quiet status line on the timeline's own surface:
14+ // the run's state and duration, the agent, model and variant, and the way back.
1615export function SubagentBar ( props : {
1716 agent ?: string
1817 model ?: { provider ?: string ; name : string ; variant ?: string }
@@ -24,6 +23,13 @@ export function SubagentBar(props: {
2423 const language = useLanguage ( )
2524 const [ now , setNow ] = createSignal ( Date . now ( ) )
2625
26+ // Only what is known shows, with one separator between neighbours.
27+ const parts = ( ) => [
28+ ...( props . status ? ( [ "status" , "elapsed" ] as const ) : [ ] ) ,
29+ ...( props . agent ? ( [ "agent" ] as const ) : [ ] ) ,
30+ ...( props . model ? ( [ "model" ] as const ) : [ ] ) ,
31+ ]
32+
2733 createEffect ( ( ) => {
2834 if ( props . status !== "running" ) return
2935
@@ -33,24 +39,13 @@ export function SubagentBar(props: {
3339 } )
3440
3541 return (
42+ // The timeline scrolls on underneath, so the line keeps its surface and fades the text passing into it.
3643 < div
3744 data-component = "subagent-bar"
38- data-dock-border-underlay = "true"
39- class = "w-full overflow-clip rounded-xl bg-v2-background-bg-base text-[13px] font-[440] leading-5 tabular-nums [[data-color-scheme=dark]_&]:bg-v2-background-bg-layer-01"
45+ class = "relative flex h-8 w-full min-w-0 items-center gap-2 bg-v2-background-bg-base ps-3 pe-1.5 text-[12px] font-[440] leading-4 tracking-[-0.04px] whitespace-nowrap tabular-nums text-v2-text-text-faint before:pointer-events-none before:absolute before:inset-x-0 before:bottom-full before:h-4 before:bg-[linear-gradient(to_top,var(--v2-background-bg-base),transparent)]"
4046 >
41- < div class = "min-h-[60px] px-4 pt-4 pb-2 text-v2-text-text-faint" >
42- < Show when = { props . status } >
43- { ( status ) => (
44- < >
45- { language . t ( `session.child.status.${ status ( ) } ` ) }
46- { " · " }
47- { formatElapsed ( ( props . ended ?? now ( ) ) - props . started , language . t ) }
48- </ >
49- ) }
50- </ Show >
51- </ div >
52- < div class = "flex h-11 items-center px-2" >
53- < span class = "flex size-7 shrink-0 items-center justify-center text-v2-icon-icon-muted" >
47+ < Show when = { props . status } >
48+ < span class = "flex size-4 shrink-0 items-center justify-center text-v2-icon-icon-faint" >
5449 < Switch >
5550 < Match when = { props . status === "running" } >
5651 < SessionProgressIndicatorV2 />
@@ -66,35 +61,56 @@ export function SubagentBar(props: {
6661 </ Match >
6762 </ Switch >
6863 </ span >
69- < div class = "ms-1 me-3 flex min-w-0 flex-1 items-center gap-1 whitespace-nowrap text-v2-text-text-muted" >
70- < Show when = { props . agent } >
71- { ( agent ) => < span class = "shrink-0 px-[11px] capitalize" > { agent ( ) } </ span > }
72- </ Show >
73- < Show when = { props . model } >
74- { ( model ) => (
75- < >
76- < span data-slot = "subagent-model" class = "flex min-w-0 px-[11px]" >
77- < Show when = { model ( ) . provider } >
78- < span class = "shrink-0 text-v2-text-text-faint" > { model ( ) . provider } </ span >
79- < Slash />
80- < span class = "sr-only" > { " / " } </ span >
81- </ Show >
82- < span class = "truncate" > { model ( ) . name } </ span >
83- </ span >
84- < Show when = { model ( ) . variant !== "default" && model ( ) . variant } >
85- < span class = "shrink-0 px-[11px] capitalize" > { model ( ) . variant } </ span >
86- </ Show >
87- </ >
88- ) }
89- </ Show >
90- </ div >
91- < Button type = "button" variant = "ghost-faint" size = "small" class = "gap-1.5 px-1.5" onClick = { props . onBack } >
92- { language . t ( "session.child.backToParent" ) }
93- < span class = "hidden sm:block" >
94- < Keybind keys = { [ formatKeybind ( "esc" , language . t ) ] } variant = "neutral" />
95- </ span >
96- </ Button >
64+ </ Show >
65+ < div class = "flex min-w-0 flex-1 items-center gap-1.5" >
66+ < For each = { parts ( ) } >
67+ { ( part , index ) => (
68+ < >
69+ < Show when = { index ( ) > 0 } >
70+ < span aria-hidden = "true" > ·</ span >
71+ </ Show >
72+ < Switch >
73+ < Match when = { part === "status" && props . status } >
74+ { ( status ) => < span class = "shrink-0" > { language . t ( `session.child.status.${ status ( ) } ` ) } </ span > }
75+ </ Match >
76+ < Match when = { part === "elapsed" } >
77+ < span class = "shrink-0" > { formatElapsed ( ( props . ended ?? now ( ) ) - props . started , language . t ) } </ span >
78+ </ Match >
79+ < Match when = { part === "agent" && props . agent } >
80+ { ( agent ) => < span class = "shrink-0 capitalize" > { agent ( ) } </ span > }
81+ </ Match >
82+ < Match when = { part === "model" && props . model } >
83+ { ( model ) => (
84+ < span data-slot = "subagent-model" class = "flex min-w-0" >
85+ < Show when = { model ( ) . provider } >
86+ < span class = "shrink-0" > { model ( ) . provider } </ span >
87+ < Slash gap = { 4 } />
88+ < span class = "sr-only" > { " / " } </ span >
89+ </ Show >
90+ < span class = "truncate" > { model ( ) . name } </ span >
91+ { /* Like the message metadata: the variant follows the name, dimmer, with no separator. */ }
92+ < Show when = { model ( ) . variant !== "default" && model ( ) . variant } >
93+ { ( variant ) => (
94+ < >
95+ < span class = "sr-only" > { " " } </ span >
96+ < span class = "ms-1 shrink-0 capitalize opacity-[0.66]" > { variant ( ) } </ span >
97+ </ >
98+ ) }
99+ </ Show >
100+ </ span >
101+ ) }
102+ </ Match >
103+ </ Switch >
104+ </ >
105+ ) }
106+ </ For >
97107 </ div >
108+ < Button type = "button" variant = "ghost-faint" size = "small" class = "shrink-0 gap-1.5 px-1.5" onClick = { props . onBack } >
109+ { language . t ( "session.child.backToParent" ) }
110+ < span class = "hidden sm:block" >
111+ < Keybind keys = { [ formatKeybind ( "esc" , language . t ) ] } variant = "neutral" />
112+ </ span >
113+ </ Button >
98114 </ div >
99115 )
100116}
0 commit comments