@@ -10,17 +10,36 @@ import { getBashToolSearchText } from "../search-text"
1010function RunningBashOutput ( props : {
1111 content : Accessor < string >
1212 scrollHelpers ?: ToolScrollHelpers
13+ onContentRendered ?: ( ) => void
1314} ) {
1415 let preRef : HTMLPreElement | undefined
16+ let pendingRenderNotificationFrame : number | null = null
1517 const updater = createStableAnsiStreamUpdater ( )
1618
19+ const notifyContentRendered = ( ) => {
20+ if ( ! props . onContentRendered || typeof requestAnimationFrame !== "function" ) return
21+ if ( pendingRenderNotificationFrame !== null ) {
22+ cancelAnimationFrame ( pendingRenderNotificationFrame )
23+ }
24+ pendingRenderNotificationFrame = requestAnimationFrame ( ( ) => {
25+ pendingRenderNotificationFrame = null
26+ props . onContentRendered ?.( )
27+ } )
28+ }
29+
1730 createEffect ( ( ) => {
1831 const element = preRef
1932 if ( ! element ) return
2033 updater . update ( element , props . content ( ) )
34+ props . scrollHelpers ?. restoreAfterRender ( )
35+ notifyContentRendered ( )
2136 } )
2237
2338 onCleanup ( ( ) => {
39+ if ( pendingRenderNotificationFrame !== null ) {
40+ cancelAnimationFrame ( pendingRenderNotificationFrame )
41+ pendingRenderNotificationFrame = null
42+ }
2443 preRef = undefined
2544 updater . reset ( )
2645 } )
@@ -37,10 +56,49 @@ function RunningBashOutput(props: {
3756 )
3857}
3958
59+ function FinalAnsiOutput ( props : {
60+ html : string
61+ scrollHelpers ?: ToolScrollHelpers
62+ onContentRendered ?: ( ) => void
63+ } ) {
64+ let pendingRenderNotificationFrame : number | null = null
65+
66+ const notifyContentRendered = ( ) => {
67+ if ( ! props . onContentRendered || typeof requestAnimationFrame !== "function" ) return
68+ if ( pendingRenderNotificationFrame !== null ) {
69+ cancelAnimationFrame ( pendingRenderNotificationFrame )
70+ }
71+ pendingRenderNotificationFrame = requestAnimationFrame ( ( ) => {
72+ pendingRenderNotificationFrame = null
73+ props . onContentRendered ?.( )
74+ } )
75+ }
76+
77+ createEffect ( ( ) => {
78+ props . html
79+ props . scrollHelpers ?. restoreAfterRender ( )
80+ notifyContentRendered ( )
81+ } )
82+
83+ onCleanup ( ( ) => {
84+ if ( pendingRenderNotificationFrame !== null ) {
85+ cancelAnimationFrame ( pendingRenderNotificationFrame )
86+ pendingRenderNotificationFrame = null
87+ }
88+ } )
89+
90+ return (
91+ < div class = "message-text tool-call-markdown" ref = { props . scrollHelpers ?. registerContainer } >
92+ < pre class = "tool-call-content tool-call-ansi" dir = "auto" innerHTML = { props . html } />
93+ </ div >
94+ )
95+ }
96+
4097function BashToolBody ( props : {
4198 toolState : Accessor < ToolState | undefined >
4299 renderMarkdown : ( options : { content : string } ) => ReturnType < ToolRenderer [ "renderBody" ] >
43100 scrollHelpers ?: ToolScrollHelpers
101+ onContentRendered ?: ( ) => void
44102} ) {
45103 const state = createMemo ( ( ) => props . toolState ( ) )
46104
@@ -91,14 +149,12 @@ function BashToolBody(props: {
91149 fallback = {
92150 < Show when = { finalAnsiHtml ( ) } fallback = { finalMarkdown ( ) ? props . renderMarkdown ( { content : finalMarkdown ( ) ! as string } ) : null } >
93151 { ( html ) => (
94- < div class = "message-text tool-call-markdown" ref = { props . scrollHelpers ?. registerContainer } >
95- < pre class = "tool-call-content tool-call-ansi" dir = "auto" innerHTML = { html ( ) } />
96- </ div >
152+ < FinalAnsiOutput html = { html ( ) } scrollHelpers = { props . scrollHelpers } onContentRendered = { props . onContentRendered } />
97153 ) }
98154 </ Show >
99155 }
100156 >
101- < RunningBashOutput content = { joinedContent } scrollHelpers = { props . scrollHelpers } />
157+ < RunningBashOutput content = { joinedContent } scrollHelpers = { props . scrollHelpers } onContentRendered = { props . onContentRendered } />
102158 </ Show >
103159 </ Show >
104160 )
@@ -124,7 +180,7 @@ export const bashRenderer: ToolRenderer = {
124180 const timeoutLabel = `${ timeout } ms`
125181 return `${ baseTitle } · ${ tGlobal ( "toolCall.renderer.bash.title.timeout" , { timeout : timeoutLabel } ) } `
126182 } ,
127- renderBody ( { toolState, renderMarkdown, scrollHelpers } ) {
128- return < BashToolBody toolState = { toolState } renderMarkdown = { renderMarkdown as any } scrollHelpers = { scrollHelpers } />
183+ renderBody ( { toolState, renderMarkdown, scrollHelpers, onContentRendered } ) {
184+ return < BashToolBody toolState = { toolState } renderMarkdown = { renderMarkdown as any } scrollHelpers = { scrollHelpers } onContentRendered = { onContentRendered } />
129185 } ,
130186}
0 commit comments