@@ -1320,20 +1320,25 @@ func (m Model) formatKeysForHelp(action keymap.Action) string {
13201320 return strings .Join (display , " / " )
13211321}
13221322
1323- // helpOverlay returns a bordered help popup with keybinding sections.
1323+ // helpOverlay returns a bordered help popup with keybinding sections arranged in two columns .
13241324// sections and key bindings are rendered dynamically from the keymap.
13251325func (m Model ) helpOverlay () string {
13261326 sections := m .keymap .HelpSections ()
13271327
1328- var buf strings.Builder
1329- for i , sec := range sections {
1330- if i > 0 {
1331- buf .WriteString ("\n " )
1332- }
1333- buf .WriteString (sec .Name )
1334- buf .WriteString ("\n " )
1328+ // render each section into a block of lines
1329+ type sectionBlock struct {
1330+ lines []string
1331+ }
1332+ var blocks []sectionBlock
1333+
1334+ reset := "\033 [0m"
1335+ headerColor := m .ansiFg (m .styles .colors .Accent )
1336+ keyColor := m .ansiFg (m .styles .colors .Annotation )
1337+
1338+ for _ , sec := range sections {
1339+ var block sectionBlock
1340+ block .lines = append (block .lines , headerColor + sec .Name + reset )
13351341
1336- // compute max key width for column alignment
13371342 type helpLine struct { keys , desc string }
13381343 lines := make ([]helpLine , 0 , len (sec .Entries ))
13391344 maxW := 0
@@ -1346,25 +1351,93 @@ func (m Model) helpOverlay() string {
13461351 }
13471352 for _ , l := range lines {
13481353 pad := max (maxW - runewidth .StringWidth (l .keys ), 0 )
1349- fmt .Fprintf (& buf , " %s%s %s\n " , l .keys , strings .Repeat (" " , pad ), l .desc )
1354+ block .lines = append (block .lines , fmt .Sprintf (" %s%s%s%s %s" ,
1355+ keyColor , l .keys , reset , strings .Repeat (" " , pad ), l .desc ))
13501356 }
1357+ blocks = append (blocks , block )
13511358
1352- // add Markdown TOC note after Pane section
1359+ // add Markdown TOC section after Pane
13531360 if sec .Name == "Pane" {
1354- buf .WriteString ("\n " )
1355- m .writeTOCHelpSection (& buf )
1361+ var tocBuf strings.Builder
1362+ m .writeTOCHelpSection (& tocBuf )
1363+ tocLines := strings .Split (strings .TrimRight (tocBuf .String (), "\n " ), "\n " )
1364+ blocks = append (blocks , sectionBlock {lines : tocLines })
1365+ }
1366+ }
1367+
1368+ // count total lines (with blank line separators between sections)
1369+ totalLines := 0
1370+ for _ , b := range blocks {
1371+ totalLines += len (b .lines ) + 1 // +1 for separator
1372+ }
1373+
1374+ // assign sections to left/right columns, keeping sections intact
1375+ var leftBlocks , rightBlocks []sectionBlock
1376+ leftLines := 0
1377+ half := totalLines / 2
1378+ for _ , b := range blocks {
1379+ blockSize := len (b .lines ) + 1
1380+ if leftLines < half {
1381+ leftBlocks = append (leftBlocks , b )
1382+ leftLines += blockSize
1383+ } else {
1384+ rightBlocks = append (rightBlocks , b )
1385+ }
1386+ }
1387+
1388+ // render column from blocks
1389+ renderColumn := func (colBlocks []sectionBlock ) []string {
1390+ var result []string
1391+ for i , b := range colBlocks {
1392+ if i > 0 {
1393+ result = append (result , "" )
1394+ }
1395+ result = append (result , b .lines ... )
1396+ }
1397+ return result
1398+ }
1399+
1400+ left := renderColumn (leftBlocks )
1401+ right := renderColumn (rightBlocks )
1402+
1403+ // find max visible width of left column for padding (ANSI-aware)
1404+ leftWidth := 0
1405+ for _ , line := range left {
1406+ if w := lipgloss .Width (line ); w > leftWidth {
1407+ leftWidth = w
13561408 }
13571409 }
13581410
1359- help := strings .TrimRight (buf .String (), "\n " )
1411+ gap := 4
1412+ // join columns side by side
1413+ maxRows := max (len (left ), len (right ))
1414+ var buf strings.Builder
1415+ for i := range maxRows {
1416+ l := ""
1417+ if i < len (left ) {
1418+ l = left [i ]
1419+ }
1420+ // pad left column to fixed width (ANSI-aware width)
1421+ pad := max (leftWidth - lipgloss .Width (l ), 0 )
1422+ buf .WriteString (l )
1423+ buf .WriteString (strings .Repeat (" " , pad ))
1424+
1425+ if i < len (right ) {
1426+ buf .WriteString (strings .Repeat (" " , gap ))
1427+ buf .WriteString (right [i ])
1428+ }
1429+ if i < maxRows - 1 {
1430+ buf .WriteString ("\n " )
1431+ }
1432+ }
13601433
13611434 border := lipgloss .NormalBorder ()
13621435 boxStyle := lipgloss .NewStyle ().
13631436 Border (border ).
13641437 BorderForeground (lipgloss .Color (m .styles .colors .Accent )).
13651438 Padding (1 , 2 )
13661439
1367- return boxStyle .Render (help )
1440+ return boxStyle .Render (buf . String () )
13681441}
13691442
13701443// writeTOCHelpSection writes the Markdown TOC contextual help section.
@@ -1401,10 +1474,14 @@ func (m Model) writeTOCHelpSection(buf *strings.Builder) {
14011474 }
14021475 }
14031476
1404- buf .WriteString ("Markdown TOC (single-file full-context mode)\n " )
1477+ reset := "\033 [0m"
1478+ headerColor := m .ansiFg (m .styles .colors .Accent )
1479+ keyColor := m .ansiFg (m .styles .colors .Annotation )
1480+
1481+ buf .WriteString (headerColor + "Markdown TOC (single-file full-context mode)" + reset + "\n " )
14051482 for _ , l := range lines {
14061483 pad := max (maxW - runewidth .StringWidth (l .keys ), 0 )
1407- fmt .Fprintf (buf , " %s%s %s\n " , l .keys , strings .Repeat (" " , pad ), l .desc )
1484+ fmt .Fprintf (buf , " %s%s%s%s %s\n " , keyColor , l .keys , reset , strings .Repeat (" " , pad ), l .desc )
14081485 }
14091486}
14101487
0 commit comments