@@ -935,6 +935,58 @@ impl Renderer {
935935
936936 // ── Public render ─────────────────────────────────────────────────────────
937937
938+ /// Emit underline decorations for a single cell.
939+ fn emit_underline (
940+ & mut self ,
941+ x : f32 ,
942+ y : f32 ,
943+ cw : f32 ,
944+ ch : f32 ,
945+ style : crate :: terminal:: UnderlineStyle ,
946+ color : [ f32 ; 4 ] ,
947+ ) {
948+ use crate :: terminal:: UnderlineStyle :: * ;
949+ let uy = y + ch - 2. ;
950+ match style {
951+ None => { }
952+ Straight => {
953+ push_rect ( & mut self . fg_rects , x, uy, cw, 1. , color) ;
954+ }
955+ Double => {
956+ push_rect ( & mut self . fg_rects , x, uy - 2. , cw, 1. , color) ;
957+ push_rect ( & mut self . fg_rects , x, uy, cw, 1. , color) ;
958+ }
959+ Curly => {
960+ // Sawtooth approximation: alternating 1-px-high segments.
961+ let seg = ( cw / 4. ) . max ( 1. ) ;
962+ let n = ( cw / seg) as usize ;
963+ for i in 0 ..n {
964+ let sx = x + i as f32 * seg;
965+ let sy = if i % 2 == 0 { uy - 1. } else { uy + 1. } ;
966+ push_rect ( & mut self . fg_rects , sx, sy, seg, 1. , color) ;
967+ }
968+ }
969+ Dotted => {
970+ let dot = 2_f32 ;
971+ let gap = 2_f32 ;
972+ let mut sx = x;
973+ while sx + dot <= x + cw {
974+ push_rect ( & mut self . fg_rects , sx, uy, dot, 1. , color) ;
975+ sx += dot + gap;
976+ }
977+ }
978+ Dashed => {
979+ let dash = ( cw * 0.45 ) . max ( 2. ) ;
980+ let gap = ( cw * 0.1 ) . max ( 1. ) ;
981+ let mut sx = x;
982+ while sx + dash <= x + cw {
983+ push_rect ( & mut self . fg_rects , sx, uy, dash, 1. , color) ;
984+ sx += dash + gap;
985+ }
986+ }
987+ }
988+ }
989+
938990 /// Render all `panes` into `view`. `dividers` are solid rectangles
939991 /// (x, y, w, h) drawn between panes — typically 2 px wide/tall separator lines.
940992 pub fn render (
@@ -1028,6 +1080,19 @@ impl Renderer {
10281080 }
10291081 }
10301082 }
1083+
1084+ // ── Per-cell underlines (SGR style + OSC 8 links) ─────────
1085+ let ul_style = cell. attrs . underline_style ;
1086+ if ul_style != crate :: terminal:: UnderlineStyle :: None {
1087+ let ul_color = cell. attrs . underline_color
1088+ . map ( c2f)
1089+ . unwrap_or ( c2f ( fg_color) ) ;
1090+ self . emit_underline ( px, py, cw, ch, ul_style, ul_color) ;
1091+ } else if cell. link_id != 0 {
1092+ // OSC 8 hyperlink — use same blue as URL underlines.
1093+ let lc = rgb_f ( 0x58 , 0x9a , 0xdd ) ;
1094+ push_rect ( & mut self . fg_rects , px, py + ch - 2. , cw, 1. , lc) ;
1095+ }
10311096 }
10321097 }
10331098
@@ -1063,7 +1128,20 @@ impl Renderer {
10631128 {
10641129 let px = ox + pane. state . cursor_col as f32 * cw;
10651130 let py = oy + pane. state . cursor_row as f32 * ch;
1066- push_rect ( & mut self . fg_rects , px, py, 2. , ch, c2f ( CURSOR_COLOR ) ) ;
1131+ match pane. state . cursor_shape {
1132+ 1 | 2 => {
1133+ // Block cursor: fill entire cell
1134+ push_rect ( & mut self . fg_rects , px, py, cw, ch, c2f ( CURSOR_COLOR ) ) ;
1135+ }
1136+ 3 | 4 => {
1137+ // Underline cursor: thin bar at cell bottom
1138+ push_rect ( & mut self . fg_rects , px, py + ch - 2. , cw, 2. , c2f ( CURSOR_COLOR ) ) ;
1139+ }
1140+ _ => {
1141+ // Bar cursor (default, 0, 5, 6): thin vertical bar at left
1142+ push_rect ( & mut self . fg_rects , px, py, 2. , ch, c2f ( CURSOR_COLOR ) ) ;
1143+ }
1144+ }
10671145 }
10681146
10691147 // ── Scrollbar ─────────────────────────────────────────────────────────
0 commit comments