@@ -107,23 +107,34 @@ export interface CodexActivityProps {
107107 generating ?: boolean ;
108108 fontSize ?: number ;
109109 durationLabel ?: string ;
110+ persistKey ?: string ;
110111 canResolveApproval ?: boolean ;
111112 onResolveApproval ?: ( args : {
112113 approvalId : string ;
113114 optionId ?: string ;
114115 } ) => Promise < void > | void ;
115116}
116117
118+ // Persist log visibility per chat message so Virtuoso remounts don’t reset it.
119+ const expandedState = new Map < string , boolean > ( ) ;
120+
117121export function CodexActivity ( {
118122 events,
119123 generating,
120124 fontSize,
121125 durationLabel,
126+ persistKey,
122127 canResolveApproval,
123128 onResolveApproval,
124129} : CodexActivityProps ) : React . ReactElement | null {
125130 const entries = useMemo ( ( ) => normalizeEvents ( events ?? [ ] ) , [ events ] ) ;
126- const [ expanded , setExpanded ] = useState < boolean > ( ! ! generating ) ;
131+ const [ expanded , setExpanded ] = useState < boolean > ( ( ) => {
132+ if ( persistKey ) {
133+ const persisted = expandedState . get ( persistKey ) ;
134+ if ( persisted != null ) return persisted ;
135+ }
136+ return ! ! generating ;
137+ } ) ;
127138 const [ hovered , setHovered ] = useState ( false ) ;
128139
129140 useEffect ( ( ) => {
@@ -132,6 +143,20 @@ export function CodexActivity({
132143 }
133144 } , [ generating ] ) ;
134145
146+ useEffect ( ( ) => {
147+ if ( ! persistKey ) return ;
148+ expandedState . set ( persistKey , expanded ) ;
149+ } , [ persistKey , expanded ] ) ;
150+
151+ useEffect ( ( ) => {
152+ if ( ! persistKey ) return ;
153+ const persisted = expandedState . get ( persistKey ) ;
154+ const next = persisted ?? ( generating ? true : expanded ) ;
155+ if ( next !== expanded ) {
156+ setExpanded ( next ) ;
157+ }
158+ } , [ persistKey , generating ] ) ; // eslint-disable-line react-hooks/exhaustive-deps
159+
135160 if ( ! entries . length ) return null ;
136161
137162 const baseFontSize = fontSize ?? 13 ;
0 commit comments