11import type { EnvironmentId , ThreadId , TurnItemId } from "@t3tools/contracts" ;
22import { TERMINAL_PALETTES , terminalOutputSpans } from "@t3tools/shared/terminalOutput" ;
3- import { useLayoutEffect , useMemo , useRef } from "react" ;
3+ import { useLayoutEffect , useRef } from "react" ;
44
55import { useTheme } from "../../hooks/useTheme" ;
66import { useServerConfigs } from "../../state/entities" ;
@@ -22,6 +22,13 @@ export function useCommandOutputStreaming(environmentId: EnvironmentId): boolean
2222 * shows its final output. Mount it only when `useCommandOutputStreaming` holds.
2323 * It subscribes only while mounted, so collapsed rows and other threads receive nothing.
2424 */
25+ // Parsing ANSI output is linear in its length, and the compiler would redo it on
26+ // every render because the layout effect runs after it.
27+ function useTerminalOutputSpans ( text : string , resolvedTheme : keyof typeof TERMINAL_PALETTES ) {
28+ "use memo" ;
29+ return terminalOutputSpans ( text , TERMINAL_PALETTES [ resolvedTheme ] ) ;
30+ }
31+
2532export function CommandOutputPanel ( props : {
2633 readonly environmentId : EnvironmentId ;
2734 readonly threadId : ThreadId ;
@@ -38,10 +45,7 @@ export function CommandOutputPanel(props: {
3845 const followRef = useRef ( true ) ;
3946 const text = data ?. output . text ?? "" ;
4047 const { resolvedTheme } = useTheme ( ) ;
41- const spans = useMemo (
42- ( ) => terminalOutputSpans ( text , TERMINAL_PALETTES [ resolvedTheme ] ) ,
43- [ text , resolvedTheme ] ,
44- ) ;
48+ const spans = useTerminalOutputSpans ( text , resolvedTheme ) ;
4549
4650 // This panel only re-renders when its output changes, so keep the end in view after each.
4751 useLayoutEffect ( ( ) => {
0 commit comments