77 For ,
88 Match ,
99 on ,
10+ onMount ,
1011 Show ,
1112 Switch ,
1213 useContext ,
@@ -1323,6 +1324,8 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
13231324 return props . message . time . completed - user . time . created
13241325 } )
13251326
1327+ const keybind = useKeybind ( )
1328+
13261329 return (
13271330 < >
13281331 < For each = { props . parts } >
@@ -1340,6 +1343,14 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
13401343 )
13411344 } }
13421345 </ For >
1346+ < Show when = { props . parts . some ( ( x ) => x . type === "tool" && x . tool === "task" ) } >
1347+ < box paddingTop = { 1 } paddingLeft = { 3 } >
1348+ < text fg = { theme . text } >
1349+ { keybind . print ( "session_child_first" ) }
1350+ < span style = { { fg : theme . textMuted } } > view subagents</ span >
1351+ </ text >
1352+ </ box >
1353+ </ Show >
13431354 < Show when = { props . message . error && props . message . error . name !== "MessageAbortedError" } >
13441355 < box
13451356 border = { [ "left" ] }
@@ -1609,6 +1620,7 @@ function InlineTool(props: {
16091620 iconColor ?: RGBA
16101621 complete : any
16111622 pending : string
1623+ spinner ?: boolean
16121624 children : JSX . Element
16131625 part : ToolPart
16141626} ) {
@@ -1665,11 +1677,18 @@ function InlineTool(props: {
16651677 }
16661678 } }
16671679 >
1668- < text paddingLeft = { 3 } fg = { fg ( ) } attributes = { denied ( ) ? TextAttributes . STRIKETHROUGH : undefined } >
1669- < Show fallback = { < > ~ { props . pending } </ > } when = { props . complete } >
1670- < span style = { { fg : props . iconColor } } > { props . icon } </ span > { props . children }
1671- </ Show >
1672- </ text >
1680+ < Switch >
1681+ < Match when = { props . spinner } >
1682+ < Spinner color = { fg ( ) } children = { props . children } />
1683+ </ Match >
1684+ < Match when = { true } >
1685+ < text paddingLeft = { 3 } fg = { fg ( ) } attributes = { denied ( ) ? TextAttributes . STRIKETHROUGH : undefined } >
1686+ < Show fallback = { < > ~ { props . pending } </ > } when = { props . complete } >
1687+ < span style = { { fg : props . iconColor } } > { props . icon } </ span > { props . children }
1688+ </ Show >
1689+ </ text >
1690+ </ Match >
1691+ </ Switch >
16731692 < Show when = { error ( ) && ! denied ( ) } >
16741693 < text fg = { theme . error } > { error ( ) } </ text >
16751694 </ Show >
@@ -1836,6 +1855,7 @@ function Glob(props: ToolProps<typeof GlobTool>) {
18361855
18371856function Read ( props : ToolProps < typeof ReadTool > ) {
18381857 const { theme } = useTheme ( )
1858+ const isRunning = createMemo ( ( ) => props . part . state . status === "running" )
18391859 const loaded = createMemo ( ( ) => {
18401860 if ( props . part . state . status !== "completed" ) return [ ]
18411861 if ( props . part . state . time . compacted ) return [ ]
@@ -1845,7 +1865,13 @@ function Read(props: ToolProps<typeof ReadTool>) {
18451865 } )
18461866 return (
18471867 < >
1848- < InlineTool icon = "→" pending = "Reading file..." complete = { props . input . filePath } part = { props . part } >
1868+ < InlineTool
1869+ icon = "→"
1870+ pending = "Reading file..."
1871+ complete = { props . input . filePath }
1872+ spinner = { isRunning ( ) }
1873+ part = { props . part }
1874+ >
18491875 Read { normalizePath ( props . input . filePath ! ) } { input ( props . input , [ "filePath" ] ) }
18501876 </ InlineTool >
18511877 < For each = { loaded ( ) } >
@@ -1921,62 +1947,60 @@ function Task(props: ToolProps<typeof TaskTool>) {
19211947 const local = useLocal ( )
19221948 const sync = useSync ( )
19231949
1950+ onMount ( ( ) => {
1951+ if ( props . metadata . sessionId && ! sync . data . message [ props . metadata . sessionId ] ?. length )
1952+ sync . session . sync ( props . metadata . sessionId )
1953+ } )
1954+
1955+ const messages = createMemo ( ( ) => sync . data . message [ props . metadata . sessionId ?? "" ] ?? [ ] )
1956+
19241957 const tools = createMemo ( ( ) => {
1925- const sessionID = props . metadata . sessionId
1926- const msgs = sync . data . message [ sessionID ?? "" ] ?? [ ]
1927- return msgs . flatMap ( ( msg ) =>
1958+ return messages ( ) . flatMap ( ( msg ) =>
19281959 ( sync . data . part [ msg . id ] ?? [ ] )
19291960 . filter ( ( part ) : part is ToolPart => part . type === "tool" )
19301961 . map ( ( part ) => ( { tool : part . tool , state : part . state } ) ) ,
19311962 )
19321963 } )
19331964
1934- const current = createMemo ( ( ) => tools ( ) . findLast ( ( x ) => x . state . status !== "pending" ) )
1965+ const current = createMemo ( ( ) => tools ( ) . findLast ( ( x ) => ( x . state as any ) . title ) )
19351966
19361967 const isRunning = createMemo ( ( ) => props . part . state . status === "running" )
19371968
1969+ const duration = createMemo ( ( ) => {
1970+ const first = messages ( ) . find ( ( x ) => x . role === "user" ) ?. time . created
1971+ const assistant = messages ( ) . findLast ( ( x ) => x . role === "assistant" ) ?. time . completed
1972+ if ( ! first || ! assistant ) return 0
1973+ return assistant - first
1974+ } )
1975+
19381976 return (
1939- < Switch >
1940- < Match when = { props . input . description || props . input . subagent_type } >
1941- < BlockTool
1942- title = { "# " + Locale . titlecase ( props . input . subagent_type ?? "unknown" ) + " Task" }
1943- onClick = {
1944- props . metadata . sessionId
1945- ? ( ) => navigate ( { type : "session" , sessionID : props . metadata . sessionId ! } )
1946- : undefined
1947- }
1948- part = { props . part }
1949- spinner = { isRunning ( ) }
1950- >
1951- < box >
1952- < text style = { { fg : theme . textMuted } } >
1953- { props . input . description } ({ tools ( ) . length } toolcalls)
1954- </ text >
1955- < Show when = { current ( ) } >
1956- { ( item ) => {
1957- const title = item ( ) . state . status === "completed" ? ( item ( ) . state as any ) . title : ""
1958- return (
1959- < text style = { { fg : item ( ) . state . status === "error" ? theme . error : theme . textMuted } } >
1960- └ { Locale . titlecase ( item ( ) . tool ) } { title }
1961- </ text >
1962- )
1963- } }
1964- </ Show >
1965- </ box >
1966- < Show when = { props . metadata . sessionId } >
1967- < text fg = { theme . text } >
1968- { keybind . print ( "session_child_first" ) }
1969- < span style = { { fg : theme . textMuted } } > view subagents</ span >
1970- </ text >
1971- </ Show >
1972- </ BlockTool >
1973- </ Match >
1974- < Match when = { true } >
1975- < InlineTool icon = "#" pending = "Delegating..." complete = { props . input . subagent_type } part = { props . part } >
1976- { props . input . subagent_type } Task { props . input . description }
1977- </ InlineTool >
1978- </ Match >
1979- </ Switch >
1977+ < InlineTool
1978+ icon = "≡"
1979+ spinner = { isRunning ( ) }
1980+ complete = { props . input . description }
1981+ pending = "Delegating..."
1982+ part = { props . part }
1983+ >
1984+ { props . input . description }
1985+ < Show when = { isRunning ( ) && tools ( ) . length > 0 } >
1986+ { " " }
1987+ · { tools ( ) . length } toolcalls
1988+ < Show fallback = { "\n└ Running..." } when = { current ( ) } >
1989+ { ( item ) => {
1990+ const title = createMemo ( ( ) => ( item ( ) . state as any ) . title )
1991+ return (
1992+ < >
1993+ { "\n" } └ { Locale . titlecase ( item ( ) . tool ) } { title ( ) }
1994+ </ >
1995+ )
1996+ } }
1997+ </ Show >
1998+ </ Show >
1999+ < Show when = { duration ( ) && props . part . state . status === "completed" } >
2000+ { "\n " }
2001+ { tools ( ) . length } toolcalls · { Locale . duration ( duration ( ) ) }
2002+ </ Show >
2003+ </ InlineTool >
19802004 )
19812005}
19822006
0 commit comments