tui: align /help descriptions across slash commands and keys
each section had its own label column width (10 vs 14) so the descriptions formed two staircases at different x-positions. compute one shared width from the longest label across BOTH lists (with a 14-cell minimum so future entries don't compress the column visually) and pad every label to it. now every description starts at the same column.
- commit
- 154420e9383f4fb42075e71dae1128c16e72860e
- author
- patriceckhart · · 2026-04-19 11:14 UTC
- parent
- c4251d8
- signature
- unsigned
- refs
- tag: v0.0.19
1 file · +28 -10
M internal/agent/modes/help.go +28 -10
| @@ -29,34 +29,52 @@ var helpKeyRows = [][2]string{ | |||
| 29 | 29 | // frameHeader/frameRule helpers so the rules match every other block | |
| 30 | 30 | // in the tui (tool results, code fences, dialogs) — full terminal width | |
| 31 | 31 | // in the muted colour. | |
| 32 | + | // | |
| 33 | + | // Slash commands and keybindings share the same label column width so | |
| 34 | + | // every description starts at the same x-position, regardless of which | |
| 35 | + | // section it lives in. The width is computed from the longest label | |
| 36 | + | // across BOTH lists, with a minimum of 14 cells so changes to either | |
| 37 | + | // list don't compress the column visually. | |
| 32 | 38 | func renderHelpBlock(th tui.Theme, width int) []string { | |
| 33 | 39 | if width < 20 { | |
| 34 | 40 | width = 20 | |
| 35 | 41 | } | |
| 42 | + | ||
| 43 | + | labelWidth := 14 | |
| 44 | + | for _, c := range slashCatalog { | |
| 45 | + | if n := len(c.Name); n > labelWidth { | |
| 46 | + | labelWidth = n | |
| 47 | + | } | |
| 48 | + | } | |
| 49 | + | for _, k := range helpKeyRows { | |
| 50 | + | if n := len(k[0]); n > labelWidth { | |
| 51 | + | labelWidth = n | |
| 52 | + | } | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | pad := func(s string) string { | |
| 56 | + | if len(s) >= labelWidth { | |
| 57 | + | return s | |
| 58 | + | } | |
| 59 | + | return s + strings.Repeat(" ", labelWidth-len(s)) | |
| 60 | + | } | |
| 61 | + | ||
| 36 | 62 | var out []string | |
| 37 | 63 | out = append(out, frameHeader(th, "zot help", width)) | |
| 38 | 64 | ||
| 39 | 65 | // commands section | |
| 40 | 66 | out = append(out, tui.Bold("slash commands:")) | |
| 41 | 67 | for _, c := range slashCatalog { | |
| 42 | - | name := c.Name | |
| 43 | - | if len(name) < 10 { | |
| 44 | - | name = name + strings.Repeat(" ", 10-len(name)) | |
| 45 | - | } | |
| 46 | 68 | out = append(out, fmt.Sprintf(" %s %s", | |
| 47 | - | th.FG256(th.Accent, name), | |
| 69 | + | th.FG256(th.Accent, pad(c.Name)), | |
| 48 | 70 | th.FG256(th.Muted, c.Desc))) | |
| 49 | 71 | } | |
| 50 | 72 | ||
| 51 | 73 | // keys section | |
| 52 | 74 | out = append(out, "", tui.Bold("keys:")) | |
| 53 | 75 | for _, k := range helpKeyRows { | |
| 54 | - | name := k[0] | |
| 55 | - | if len(name) < 14 { | |
| 56 | - | name = name + strings.Repeat(" ", 14-len(name)) | |
| 57 | - | } | |
| 58 | 76 | out = append(out, fmt.Sprintf(" %s %s", | |
| 59 | - | th.FG256(th.Accent, name), | |
| 77 | + | th.FG256(th.Accent, pad(k[0])), | |
| 60 | 78 | th.FG256(th.Muted, k[1]))) | |
| 61 | 79 | } | |
| 62 | 80 | ||
| @@ -29,34 +29,52 @@ var helpKeyRows = [][2]string{ | |||||
| 29 | // frameHeader/frameRule helpers so the rules match every other block | 29 | // frameHeader/frameRule helpers so the rules match every other block | ||
| 30 | // in the tui (tool results, code fences, dialogs) — full terminal width | 30 | // in the tui (tool results, code fences, dialogs) — full terminal width | ||
| 31 | // in the muted colour. | 31 | // in the muted colour. | ||
| 32 | + | // | |||
| 33 | + | // Slash commands and keybindings share the same label column width so | |||
| 34 | + | // every description starts at the same x-position, regardless of which | |||
| 35 | + | // section it lives in. The width is computed from the longest label | |||
| 36 | + | // across BOTH lists, with a minimum of 14 cells so changes to either | |||
| 37 | + | // list don't compress the column visually. | |||
| 32 | func renderHelpBlock(th tui.Theme, width int) []string { | 38 | func renderHelpBlock(th tui.Theme, width int) []string { | ||
| 33 | if width < 20 { | 39 | if width < 20 { | ||
| 34 | width = 20 | 40 | width = 20 | ||
| 35 | } | 41 | } | ||
| 42 | + | ||||
| 43 | + | labelWidth := 14 | |||
| 44 | + | for _, c := range slashCatalog { | |||
| 45 | + | if n := len(c.Name); n > labelWidth { | |||
| 46 | + | labelWidth = n | |||
| 47 | + | } | |||
| 48 | + | } | |||
| 49 | + | for _, k := range helpKeyRows { | |||
| 50 | + | if n := len(k[0]); n > labelWidth { | |||
| 51 | + | labelWidth = n | |||
| 52 | + | } | |||
| 53 | + | } | |||
| 54 | + | ||||
| 55 | + | pad := func(s string) string { | |||
| 56 | + | if len(s) >= labelWidth { | |||
| 57 | + | return s | |||
| 58 | + | } | |||
| 59 | + | return s + strings.Repeat(" ", labelWidth-len(s)) | |||
| 60 | + | } | |||
| 61 | + | ||||
| 36 | var out []string | 62 | var out []string | ||
| 37 | out = append(out, frameHeader(th, "zot help", width)) | 63 | out = append(out, frameHeader(th, "zot help", width)) | ||
| 38 | 64 | ||||
| 39 | // commands section | 65 | // commands section | ||
| 40 | out = append(out, tui.Bold("slash commands:")) | 66 | out = append(out, tui.Bold("slash commands:")) | ||
| 41 | for _, c := range slashCatalog { | 67 | for _, c := range slashCatalog { | ||
| 42 | - | name := c.Name | |||
| 43 | - | if len(name) < 10 { | |||
| 44 | - | name = name + strings.Repeat(" ", 10-len(name)) | |||
| 45 | - | } | |||
| 46 | out = append(out, fmt.Sprintf(" %s %s", | 68 | out = append(out, fmt.Sprintf(" %s %s", | ||
| 47 | - | th.FG256(th.Accent, name), | 69 | + | th.FG256(th.Accent, pad(c.Name)), |
| 48 | th.FG256(th.Muted, c.Desc))) | 70 | th.FG256(th.Muted, c.Desc))) | ||
| 49 | } | 71 | } | ||
| 50 | 72 | ||||
| 51 | // keys section | 73 | // keys section | ||
| 52 | out = append(out, "", tui.Bold("keys:")) | 74 | out = append(out, "", tui.Bold("keys:")) | ||
| 53 | for _, k := range helpKeyRows { | 75 | for _, k := range helpKeyRows { | ||
| 54 | - | name := k[0] | |||
| 55 | - | if len(name) < 14 { | |||
| 56 | - | name = name + strings.Repeat(" ", 14-len(name)) | |||
| 57 | - | } | |||
| 58 | out = append(out, fmt.Sprintf(" %s %s", | 76 | out = append(out, fmt.Sprintf(" %s %s", | ||
| 59 | - | th.FG256(th.Accent, name), | 77 | + | th.FG256(th.Accent, pad(k[0])), |
| 60 | th.FG256(th.Muted, k[1]))) | 78 | th.FG256(th.Muted, k[1]))) | ||
| 61 | } | 79 | } | ||
| 62 | 80 | ||||