@@ -708,7 +708,12 @@ impl<R: SessionNameResolver> Formatter<R> {
708708 _ => ANSI_DIM ,
709709 } ;
710710 let prio = self . paint ( & prio_token, prio_color) ;
711- let arrow = self . paint ( "↳" , ANSI_DIM ) ;
711+ // `╰` shares the Box Drawing block with the continuation `│`, so
712+ // the two glyphs render with identical cell width and font
713+ // metrics. Plain arrows like `↳` (Arrows block) drift visually in
714+ // some fonts/terminals even when their reported display width is
715+ // the same.
716+ let arrow = self . paint ( "╰" , ANSI_DIM ) ;
712717 let pad = " " . repeat ( body_col) ;
713718 format ! ( "{pad}{arrow} {prio} {rule_name}" )
714719 }
@@ -1784,7 +1789,7 @@ mod tests {
17841789 fn matched_allow_event_renders_with_circled_bullet ( ) {
17851790 // A rule with no deny/ask tag fires for the event. The verdict is
17861791 // matched-allow: ◉ green bullet, full block (bold tool name +
1787- // continuation lines + ↳ rule detail line).
1792+ // continuation lines + ╰ rule detail line).
17881793 let mut f = Formatter :: new (
17891794 opts_no_color ( ) ,
17901795 "/home/u" . to_string ( ) ,
@@ -1863,7 +1868,7 @@ mod tests {
18631868 let out = f. process_line ( & make_seen ( 3 , "s" , "/home/u" , "Bash" , "command" , "x" ) ) ;
18641869 let joined = out. join ( "\n " ) ;
18651870 assert ! ( joined. matches( "⊘" ) . count( ) >= 1 ) ;
1866- let detail_count = joined. matches ( "↳ " ) . count ( ) ;
1871+ let detail_count = joined. matches ( "╰ " ) . count ( ) ;
18671872 assert_eq ! ( detail_count, 2 , "two deny rules → two detail lines" ) ;
18681873 assert_eq ! ( f. counters( ) . deny, 1 ) ;
18691874 }
@@ -2204,13 +2209,42 @@ mod tests {
22042209 . expect ( "event line missing" ) ;
22052210 let detail_line = out
22062211 . iter ( )
2207- . find ( |l| l. contains ( "↳ " ) )
2212+ . find ( |l| l. contains ( "╰ " ) )
22082213 . expect ( "detail line missing" ) ;
22092214 let event_tool_col = char_col ( event_line, 'R' ) . expect ( "R not found in event" ) ;
2210- let detail_arrow_col = char_col ( detail_line, '↳ ' ) . expect ( "↳ not found in detail" ) ;
2215+ let detail_arrow_col = char_col ( detail_line, '╰ ' ) . expect ( "╰ not found in detail" ) ;
22112216 assert_eq ! (
22122217 event_tool_col, detail_arrow_col,
2213- "tool name and ↳ must share a column.\n event: {event_line}\n detail: {detail_line}"
2218+ "tool name and ╰ must share a column.\n event: {event_line}\n detail: {detail_line}"
2219+ ) ;
2220+ }
2221+
2222+ #[ test]
2223+ fn continuation_and_detail_share_column ( ) {
2224+ // `│` (continuation) and `╰` (detail) live in the same Box
2225+ // Drawing block so their visual cell metrics line up. Numerical
2226+ // equality is the structural invariant: both must sit at body_col.
2227+ let mut f = Formatter :: new (
2228+ opts_no_color ( ) ,
2229+ "/home/u" . to_string ( ) ,
2230+ StubResolver ( HashMap :: new ( ) ) ,
2231+ ) ;
2232+ let _ = f. process_line ( & make_deny ( 7 , "Deny dangerous" , "blocked" , "Critical" ) ) ;
2233+ let seen = make_seen ( 7 , "abc" , "/home/u/proj" , "Bash" , "command" , "rm -rf /" ) ;
2234+ let out = f. process_line ( & seen) ;
2235+ let cont_line = out
2236+ . iter ( )
2237+ . find ( |l| l. contains ( "│" ) )
2238+ . expect ( "continuation line missing" ) ;
2239+ let detail_line = out
2240+ . iter ( )
2241+ . find ( |l| l. contains ( "╰" ) )
2242+ . expect ( "detail line missing" ) ;
2243+ let cont_col = char_col ( cont_line, '│' ) . expect ( "│ not found in continuation" ) ;
2244+ let detail_col = char_col ( detail_line, '╰' ) . expect ( "╰ not found in detail" ) ;
2245+ assert_eq ! (
2246+ cont_col, detail_col,
2247+ "│ and ╰ must share a column.\n cont: {cont_line}\n detail: {detail_line}"
22142248 ) ;
22152249 }
22162250
0 commit comments