@@ -13,6 +13,7 @@ use opencode_api::generated::V2Event;
1313use opencode_api:: transport:: { BoxFuture , Request , Response , Transport } ;
1414use opencode_local:: cli_config:: { Bounded , Debug , Info , Markdown , Scroll , Session , TurnTokens , Verbosity , Visibility } ;
1515use opencode_tui:: Deps ;
16+ use opentui:: widget:: Widget ;
1617use serde_json:: { Value , json} ;
1718use tokio:: sync:: Semaphore ;
1819
@@ -507,6 +508,61 @@ fn a_theme_switch_restyles_the_transcript_in_place() {
507508 assert_eq ! ( diff. map( |( x, y) | ( x, y, switched. cell( x, y) , expected. cell( x, y) ) ) , None , "{}" , switched. text( ) ) ;
508509}
509510
511+ /// P11 as a work counter: a theme switch builds no fence or diff again, so it parses none. The widgets one switch
512+ /// creates are as many with 0, 3 or 36 fences, and with 1 or 12 edit diffs. A kept code view restyles its highlight
513+ /// without a parse (OpenTUI's `option_changes_keep_builtin_fences_streaming` counts them).
514+ #[ test]
515+ fn a_theme_switch_builds_no_fence_or_diff_again ( ) {
516+ let fence = |language : & str , line : & str | format ! ( "```{language}\n {}```\n " , format!( "{line}\n " ) . repeat( 15 ) ) ;
517+ let reply = |fences : bool | {
518+ let code = [ fence ( "rust" , "let x = parse(\" a\" , 1); // b" ) , fence ( "python" , "total += f(1) # n" ) ] ;
519+ if fences { format ! ( "Reply.\n \n {}\n {}\n {}" , code[ 0 ] , code[ 1 ] , code[ 0 ] ) } else { "Reply." . to_owned ( ) }
520+ } ;
521+ let edit = |index : usize | {
522+ let lines: String =
523+ ( 0 ..15 ) . map ( |line| format ! ( "- let a{line} = {line};\n + let b{line} = {line};\n " ) ) . collect ( ) ;
524+ json ! ( [ {
525+ "type" : "tool" , "id" : format!( "call_{index}" ) , "name" : "edit" , "time" : { "created" : 1 , "completed" : 2 } ,
526+ "state" : {
527+ "status" : "completed" , "input" : { "path" : format!( "/project/src/m{index}.rs" ) } ,
528+ "content" : [ { "type" : "text" , "text" : "Edited" } ] ,
529+ "metadata" : { "files" : [ {
530+ "file" : format!( "src/m{index}.rs" ) , "filePath" : format!( "/project/src/m{index}.rs" ) , "type" : "update" ,
531+ "patch" : format!( "--- src/m{index}.rs\n +++ src/m{index}.rs\n @@ -1,16 +1,16 @@\n fn main() {{\n {lines}" ) ,
532+ "additions" : 15 , "deletions" : 15 ,
533+ } ] } ,
534+ } ,
535+ } ] )
536+ } ;
537+ let created = |replies : usize , content : & dyn Fn ( usize ) -> Value | {
538+ let messages = ( 0 ..replies as u64 )
539+ . flat_map ( |index| {
540+ let id = |suffix : & str | format ! ( "msg_{index:03}{suffix}" ) ;
541+ let at = 1000 + index * 10_000 ;
542+ [ user ( & id ( "a" ) , at, "Go" ) , assistant ( & id ( "b" ) , at + 100 , content ( index as usize ) , Some ( at + 5000 ) ) ]
543+ } )
544+ . collect ( ) ;
545+ let h = open ( 120 , 200 , "ses_w" , messages, support:: still ( ) ) ;
546+ h. wait_for ( "Reply." ) ;
547+ h. app . render ( ) ;
548+ let first = opentui:: widget:: View :: new ( & h. app ) . id ( ) ;
549+ assert ! ( h. tui. theme. borrow_mut( ) . set( "tokyonight" ) ) ;
550+ h. tui . theme_changed ( ) ;
551+ h. app . render ( ) ;
552+ opentui:: widget:: View :: new ( & h. app ) . id ( ) - first
553+ } ;
554+ let text = |fences : bool | move |_: usize | json ! ( [ { "type" : "text" , "text" : reply( fences) } ] ) ;
555+ let with_edit = |index : usize | {
556+ let mut content = edit ( index) ;
557+ content. as_array_mut ( ) . unwrap ( ) . insert ( 0 , json ! ( { "type" : "text" , "text" : "Reply." } ) ) ;
558+ content
559+ } ;
560+ let counts = [ created ( 12 , & text ( false ) ) , created ( 1 , & text ( true ) ) , created ( 12 , & text ( true ) ) ] ;
561+ assert_eq ! ( counts, [ counts[ 0 ] ; 3 ] , "widgets per switch at 0, 3 and 36 fences" ) ;
562+ let counts = [ created ( 1 , & with_edit) , created ( 12 , & with_edit) ] ;
563+ assert_eq ! ( counts, [ counts[ 0 ] ; 2 ] , "widgets per switch at 1 and 12 edit diffs" ) ;
564+ }
565+
510566/// `session.markdown` reaches the text already shown, as TS's `conceal` prop follows `markdownMode()`
511567/// (`message-parts.tsx:195`), and the fence keeps its widget.
512568#[ test]
0 commit comments