@@ -16,7 +16,6 @@ use crate::key_hint::KeyBinding;
1616use crate :: render:: highlight:: highlight_bash_to_lines;
1717use crate :: render:: renderable:: ColumnRenderable ;
1818use crate :: render:: renderable:: Renderable ;
19- use crate :: text_formatting:: truncate_text;
2019use codex_core:: protocol:: FileChange ;
2120use codex_core:: protocol:: Op ;
2221use codex_core:: protocol:: ReviewDecision ;
@@ -105,9 +104,9 @@ impl ApprovalOverlay {
105104 ) ,
106105 } ;
107106
108- let header = Box :: new ( ColumnRenderable :: new ( [
109- Box :: new ( Line :: from ( title. bold ( ) ) ) ,
110- Box :: new ( Line :: from ( "" ) ) ,
107+ let header = Box :: new ( ColumnRenderable :: with ( [
108+ Line :: from ( title. bold ( ) ) . into ( ) ,
109+ Line :: from ( "" ) . into ( ) ,
111110 header,
112111 ] ) ) ;
113112
@@ -160,11 +159,8 @@ impl ApprovalOverlay {
160159 }
161160
162161 fn handle_exec_decision ( & self , id : & str , command : & [ String ] , decision : ReviewDecision ) {
163- if let Some ( lines) = build_exec_history_lines ( command. to_vec ( ) , decision) {
164- self . app_event_tx . send ( AppEvent :: InsertHistoryCell ( Box :: new (
165- history_cell:: new_user_approval_decision ( lines) ,
166- ) ) ) ;
167- }
162+ let cell = history_cell:: new_approval_decision_cell ( command. to_vec ( ) , decision) ;
163+ self . app_event_tx . send ( AppEvent :: InsertHistoryCell ( cell) ) ;
168164 self . app_event_tx . send ( AppEvent :: CodexOp ( Op :: ExecApproval {
169165 id : id. to_string ( ) ,
170166 decision,
@@ -327,7 +323,7 @@ impl From<ApprovalRequest> for ApprovalRequestState {
327323 header. push ( DiffSummary :: new ( changes, cwd) . into ( ) ) ;
328324 Self {
329325 variant : ApprovalVariant :: ApplyPatch { id } ,
330- header : Box :: new ( ColumnRenderable :: new ( header) ) ,
326+ header : Box :: new ( ColumnRenderable :: with ( header) ) ,
331327 }
332328 }
333329 }
@@ -396,91 +392,11 @@ fn patch_options() -> Vec<ApprovalOption> {
396392 ]
397393}
398394
399- fn build_exec_history_lines (
400- command : Vec < String > ,
401- decision : ReviewDecision ,
402- ) -> Option < Vec < Line < ' static > > > {
403- use ReviewDecision :: * ;
404-
405- let ( symbol, summary) : ( Span < ' static > , Vec < Span < ' static > > ) = match decision {
406- Approved => {
407- let snippet = Span :: from ( exec_snippet ( & command) ) . dim ( ) ;
408- (
409- "✔ " . green ( ) ,
410- vec ! [
411- "You " . into( ) ,
412- "approved" . bold( ) ,
413- " codex to run " . into( ) ,
414- snippet,
415- " this time" . bold( ) ,
416- ] ,
417- )
418- }
419- ApprovedForSession => {
420- let snippet = Span :: from ( exec_snippet ( & command) ) . dim ( ) ;
421- (
422- "✔ " . green ( ) ,
423- vec ! [
424- "You " . into( ) ,
425- "approved" . bold( ) ,
426- " codex to run " . into( ) ,
427- snippet,
428- " every time this session" . bold( ) ,
429- ] ,
430- )
431- }
432- Denied => {
433- let snippet = Span :: from ( exec_snippet ( & command) ) . dim ( ) ;
434- (
435- "✗ " . red ( ) ,
436- vec ! [
437- "You " . into( ) ,
438- "did not approve" . bold( ) ,
439- " codex to run " . into( ) ,
440- snippet,
441- ] ,
442- )
443- }
444- Abort => {
445- let snippet = Span :: from ( exec_snippet ( & command) ) . dim ( ) ;
446- (
447- "✗ " . red ( ) ,
448- vec ! [
449- "You " . into( ) ,
450- "canceled" . bold( ) ,
451- " the request to run " . into( ) ,
452- snippet,
453- ] ,
454- )
455- }
456- } ;
457-
458- let mut lines = Vec :: new ( ) ;
459- let mut spans = Vec :: new ( ) ;
460- spans. push ( symbol) ;
461- spans. extend ( summary) ;
462- lines. push ( Line :: from ( spans) ) ;
463- Some ( lines)
464- }
465-
466- fn truncate_exec_snippet ( full_cmd : & str ) -> String {
467- let mut snippet = match full_cmd. split_once ( '\n' ) {
468- Some ( ( first, _) ) => format ! ( "{first} ..." ) ,
469- None => full_cmd. to_string ( ) ,
470- } ;
471- snippet = truncate_text ( & snippet, 80 ) ;
472- snippet
473- }
474-
475- fn exec_snippet ( command : & [ String ] ) -> String {
476- let full_cmd = strip_bash_lc_and_escape ( command) ;
477- truncate_exec_snippet ( & full_cmd)
478- }
479-
480395#[ cfg( test) ]
481396mod tests {
482397 use super :: * ;
483398 use crate :: app_event:: AppEvent ;
399+ use pretty_assertions:: assert_eq;
484400 use tokio:: sync:: mpsc:: unbounded_channel;
485401
486402 fn make_exec_request ( ) -> ApprovalRequest {
@@ -550,6 +466,34 @@ mod tests {
550466 ) ;
551467 }
552468
469+ #[ test]
470+ fn exec_history_cell_wraps_with_two_space_indent ( ) {
471+ let command = vec ! [
472+ "/bin/zsh" . into( ) ,
473+ "-lc" . into( ) ,
474+ "git add tui/src/render/mod.rs tui/src/render/renderable.rs" . into( ) ,
475+ ] ;
476+ let cell = history_cell:: new_approval_decision_cell ( command, ReviewDecision :: Approved ) ;
477+ let lines = cell. display_lines ( 28 ) ;
478+ let rendered: Vec < String > = lines
479+ . iter ( )
480+ . map ( |line| {
481+ line. spans
482+ . iter ( )
483+ . map ( |span| span. content . as_ref ( ) )
484+ . collect :: < String > ( )
485+ } )
486+ . collect ( ) ;
487+ let expected = vec ! [
488+ "✔ You approved codex to" . to_string( ) ,
489+ " run /bin/zsh -lc 'git add" . to_string( ) ,
490+ " tui/src/render/mod.rs tui/" . to_string( ) ,
491+ " src/render/renderable.rs'" . to_string( ) ,
492+ " this time" . to_string( ) ,
493+ ] ;
494+ assert_eq ! ( rendered, expected) ;
495+ }
496+
553497 #[ test]
554498 fn enter_sets_last_selected_index_without_dismissing ( ) {
555499 let ( tx_raw, mut rx) = unbounded_channel :: < AppEvent > ( ) ;
0 commit comments