@@ -1524,6 +1524,8 @@ const PART_MAPPING = {
15241524 reasoning : ReasoningPart ,
15251525}
15261526
1527+ const INLINE_TOOL_ICON_WIDTH = 2
1528+
15271529function ReasoningPart ( props : { last : boolean ; part : ReasoningPart ; message : AssistantMessage } ) {
15281530 const { theme } = useTheme ( )
15291531 const ctx = use ( )
@@ -1789,27 +1791,19 @@ function InlineTool(props: {
17891791 part : ToolPart
17901792 onClick ?: ( ) => void
17911793} ) {
1792- const [ margin , setMargin ] = createSignal ( 0 )
17931794 const { theme } = useTheme ( )
17941795 const ctx = use ( )
17951796 const sync = useSync ( )
17961797 const renderer = useRenderer ( )
17971798 const [ hover , setHover ] = createSignal ( false )
1799+ const [ errorExpanded , setErrorExpanded ] = createSignal ( false )
17981800
17991801 const permission = createMemo ( ( ) => {
18001802 const callID = sync . data . permission [ ctx . sessionID ] ?. at ( 0 ) ?. tool ?. callID
18011803 if ( ! callID ) return false
18021804 return callID === props . part . callID
18031805 } )
18041806
1805- const fg = createMemo ( ( ) => {
1806- if ( props . color ) return props . color
1807- if ( permission ( ) ) return theme . warning
1808- if ( hover ( ) && props . onClick ) return theme . text
1809- if ( props . complete ) return theme . textMuted
1810- return theme . text
1811- } )
1812-
18131807 const error = createMemo ( ( ) => ( props . part . state . status === "error" ? props . part . state . error : undefined ) )
18141808
18151809 const denied = createMemo (
@@ -1820,53 +1814,134 @@ function InlineTool(props: {
18201814 error ( ) ?. includes ( "user dismissed" ) ,
18211815 )
18221816
1817+ const failed = createMemo ( ( ) => Boolean ( error ( ) && ! denied ( ) ) )
1818+ const clickable = createMemo ( ( ) => Boolean ( props . onClick || failed ( ) ) )
1819+ const fg = createMemo ( ( ) => {
1820+ if ( props . color ) return props . color
1821+ if ( permission ( ) ) return theme . warning
1822+ if ( failed ( ) ) return theme . error
1823+ if ( hover ( ) && props . onClick ) return theme . text
1824+ if ( props . complete ) return theme . textMuted
1825+ return theme . text
1826+ } )
1827+
18231828 return (
1824- < box
1825- marginTop = { margin ( ) }
1826- paddingLeft = { 3 }
1827- onMouseOver = { ( ) => props . onClick && setHover ( true ) }
1829+ < InlineToolRow
1830+ icon = { props . icon }
1831+ iconColor = { props . iconColor }
1832+ color = { fg ( ) }
1833+ errorColor = { theme . error }
1834+ failed = { failed ( ) }
1835+ denied = { Boolean ( denied ( ) ) }
1836+ error = { error ( ) }
1837+ errorExpanded = { errorExpanded ( ) }
1838+ complete = { props . complete }
1839+ pending = { props . pending }
1840+ spinner = { props . spinner }
1841+ separateAfter = { ( id ) =>
1842+ sync . data . message [ ctx . sessionID ] ?. some ( ( message ) => message . role === "user" && message . id === id ) ?? false
1843+ }
1844+ onMouseOver = { ( ) => clickable ( ) && setHover ( true ) }
18281845 onMouseOut = { ( ) => setHover ( false ) }
18291846 onMouseUp = { ( ) => {
18301847 if ( renderer . getSelection ( ) ?. getSelectedText ( ) ) return
1848+ if ( failed ( ) ) {
1849+ setErrorExpanded ( ( value ) => ! value )
1850+ return
1851+ }
18311852 props . onClick ?.( )
18321853 } }
1854+ >
1855+ { props . children }
1856+ </ InlineToolRow >
1857+ )
1858+ }
1859+
1860+ export function InlineToolRow ( props : {
1861+ icon : string
1862+ iconColor ?: RGBA
1863+ color ?: RGBA
1864+ errorColor ?: RGBA
1865+ failed ?: boolean
1866+ denied ?: boolean
1867+ error ?: string
1868+ errorExpanded ?: boolean
1869+ complete : any
1870+ pending : string
1871+ spinner ?: boolean
1872+ children : JSX . Element
1873+ separateAfter ?: ( id : string | undefined ) => boolean
1874+ onMouseOver ?: ( ) => void
1875+ onMouseOut ?: ( ) => void
1876+ onMouseUp ?: ( ) => void
1877+ } ) {
1878+ const [ margin , setMargin ] = createSignal ( 0 )
1879+
1880+ return (
1881+ < box
1882+ marginTop = { margin ( ) }
1883+ paddingLeft = { 3 }
1884+ onMouseOver = { props . onMouseOver }
1885+ onMouseOut = { props . onMouseOut }
1886+ onMouseUp = { props . onMouseUp }
18331887 renderBefore = { function ( ) {
18341888 const el = this as BoxRenderable
18351889 const parent = el . parent
18361890 if ( ! parent ) {
18371891 return
18381892 }
1839- if ( el . height > 1 ) {
1840- setMargin ( 1 )
1841- return
1842- }
18431893 const children = parent . getChildren ( )
18441894 const index = children . indexOf ( el )
18451895 const previous = children [ index - 1 ]
1846- if ( ! previous ) {
1847- setMargin ( 0 )
1848- return
1849- }
1850- if ( previous . height > 1 || previous . id . startsWith ( "text-" ) ) {
1851- setMargin ( 1 )
1852- return
1853- }
1896+ setMargin (
1897+ previous ?. id . startsWith ( "text-" ) ||
1898+ previous ?. id . startsWith ( "tool-block-" ) ||
1899+ props . separateAfter ?.( previous ?. id )
1900+ ? 1
1901+ : 0 ,
1902+ )
18541903 } }
18551904 >
18561905 < Switch >
18571906 < Match when = { props . spinner } >
1858- < Spinner color = { fg ( ) } children = { props . children } />
1907+ < Spinner color = { props . color } children = { props . children } />
18591908 </ Match >
18601909 < Match when = { true } >
1861- < text paddingLeft = { 3 } fg = { fg ( ) } attributes = { denied ( ) ? TextAttributes . STRIKETHROUGH : undefined } >
1862- < Show fallback = { < > ~ { props . pending } </ > } when = { props . complete } >
1863- < span style = { { fg : props . iconColor } } > { props . icon } </ span > { props . children }
1864- </ Show >
1865- </ text >
1910+ < Show
1911+ fallback = {
1912+ < text
1913+ paddingLeft = { 3 }
1914+ fg = { props . color }
1915+ attributes = { props . denied ? TextAttributes . STRIKETHROUGH : undefined }
1916+ >
1917+ ~ { props . pending }
1918+ </ text >
1919+ }
1920+ when = { props . complete }
1921+ >
1922+ < box flexDirection = "row" >
1923+ < text
1924+ width = { INLINE_TOOL_ICON_WIDTH }
1925+ fg = { props . failed ? props . errorColor : ( props . iconColor ?? props . color ) }
1926+ attributes = { props . denied ? TextAttributes . STRIKETHROUGH : undefined }
1927+ >
1928+ { props . icon }
1929+ </ text >
1930+ < text
1931+ flexGrow = { 1 }
1932+ fg = { props . failed ? props . errorColor : props . color }
1933+ attributes = { props . denied ? TextAttributes . STRIKETHROUGH : undefined }
1934+ >
1935+ { props . children }
1936+ </ text >
1937+ </ box >
1938+ </ Show >
18661939 </ Match >
18671940 </ Switch >
1868- < Show when = { error ( ) && ! denied ( ) } >
1869- < text fg = { theme . error } > { error ( ) } </ text >
1941+ < Show when = { props . failed && props . errorExpanded } >
1942+ < box paddingLeft = { INLINE_TOOL_ICON_WIDTH } >
1943+ < text fg = { props . errorColor } > { props . error } </ text >
1944+ </ box >
18701945 </ Show >
18711946 </ box >
18721947 )
@@ -1885,6 +1960,7 @@ function BlockTool(props: {
18851960 const error = createMemo ( ( ) => ( props . part ?. state . status === "error" ? props . part . state . error : undefined ) )
18861961 return (
18871962 < box
1963+ id = { props . part ? "tool-block-" + props . part . id : undefined }
18881964 border = { [ "left" ] }
18891965 paddingTop = { 1 }
18901966 paddingBottom = { 1 }
0 commit comments