@@ -50,6 +50,7 @@ import { checksum } from "@opencode-ai/util/encode"
5050import { Tooltip } from "./tooltip"
5151import { IconButton } from "./icon-button"
5252import { TextShimmer } from "./text-shimmer"
53+ import { createAutoScroll } from "../hooks"
5354
5455interface Diagnostic {
5556 range : {
@@ -865,6 +866,27 @@ export interface ToolProps {
865866 locked ?: boolean
866867}
867868
869+ /**
870+ * A scrollable tool output container that auto-scrolls to the bottom
871+ * while the tool is running (tail -f effect). Respects user scroll —
872+ * if the user scrolls up, auto-scroll pauses until they scroll back down.
873+ */
874+ function ScrollableToolOutput ( props : { children : JSX . Element ; status ?: string } ) {
875+ const autoScroll = createAutoScroll ( {
876+ working : ( ) => props . status !== "completed" && props . status !== "error" ,
877+ } )
878+ return (
879+ < div
880+ ref = { autoScroll . scrollRef }
881+ onScroll = { autoScroll . handleScroll }
882+ data-component = "tool-output"
883+ data-scrollable
884+ >
885+ < div ref = { autoScroll . contentRef } > { props . children } </ div >
886+ </ div >
887+ )
888+ }
889+
868890export type ToolComponent = Component < ToolProps >
869891
870892const state : Record <
@@ -1205,9 +1227,9 @@ ToolRegistry.register({
12051227 >
12061228 < Show when = { props . output } >
12071229 { ( output ) => (
1208- < div data-component = "tool-output" data-scrollable >
1230+ < ScrollableToolOutput status = { props . status } >
12091231 < Markdown text = { output ( ) } />
1210- </ div >
1232+ </ ScrollableToolOutput >
12111233 ) }
12121234 </ Show >
12131235 </ BasicTool >
@@ -1231,9 +1253,9 @@ ToolRegistry.register({
12311253 >
12321254 < Show when = { props . output } >
12331255 { ( output ) => (
1234- < div data-component = "tool-output" data-scrollable >
1256+ < ScrollableToolOutput status = { props . status } >
12351257 < Markdown text = { output ( ) } />
1236- </ div >
1258+ </ ScrollableToolOutput >
12371259 ) }
12381260 </ Show >
12391261 </ BasicTool >
@@ -1260,9 +1282,9 @@ ToolRegistry.register({
12601282 >
12611283 < Show when = { props . output } >
12621284 { ( output ) => (
1263- < div data-component = "tool-output" data-scrollable >
1285+ < ScrollableToolOutput status = { props . status } >
12641286 < Markdown text = { output ( ) } />
1265- </ div >
1287+ </ ScrollableToolOutput >
12661288 ) }
12671289 </ Show >
12681290 </ BasicTool >
@@ -1412,6 +1434,9 @@ ToolRegistry.register({
14121434 return `$ ${ cmd } ${ out ? "\n\n" + out : "" } `
14131435 } )
14141436 const [ copied , setCopied ] = createSignal ( false )
1437+ const autoScroll = createAutoScroll ( {
1438+ working : ( ) => props . status !== "completed" && props . status !== "error" ,
1439+ } )
14151440
14161441 const handleCopy = async ( ) => {
14171442 const content = text ( )
@@ -1447,8 +1472,8 @@ ToolRegistry.register({
14471472 />
14481473 </ Tooltip >
14491474 </ div >
1450- < div data-slot = "bash-scroll" data-scrollable >
1451- < pre data-slot = "bash-pre" >
1475+ < div ref = { autoScroll . scrollRef } onScroll = { autoScroll . handleScroll } data-slot = "bash-scroll" data-scrollable >
1476+ < pre ref = { autoScroll . contentRef } data-slot = "bash-pre" >
14521477 < code > { text ( ) } </ code >
14531478 </ pre >
14541479 </ div >
0 commit comments