fix(tui): align slash popup descriptions across built-in + extension commands
The slash-command popup padded every command name to 10 chars. Any command longer than 10 (/reload-ext at 11, /clear-notes at 12) skipped the padding entirely, so its description started further right than the others, breaking column alignment between the built-in section and the extension section. Fix: compute the widest name across the whole match list (both groups) and pad every row to that width. Minimum 10 so short lists don't look cramped. Both sections now share one description column x-position.
- commit
- 00b3d9dc76278e1964bd068a1ffda3ff390dac70
- author
- patriceckhart · · 2026-04-19 17:34 UTC
- parent
- 1303e18
- signature
- unsigned
- refs
- tag: v0.0.46
1 file · +15 -2
M internal/agent/modes/slash_suggest.go +15 -2
| @@ -316,6 +316,19 @@ func (s *slashSuggester) Render(input string, th tui.Theme, width int) []string | |||
| 316 | 316 | } | |
| 317 | 317 | } | |
| 318 | 318 | } | |
| 319 | + | // Compute the widest command name across the whole match list | |
| 320 | + | // (built-ins + extension-contributed) so every row's description | |
| 321 | + | // starts at the same x-position. A minimum keeps short lists | |
| 322 | + | // from collapsing the descriptions into the name column. | |
| 323 | + | nameWidth := 10 | |
| 324 | + | for _, c := range m { | |
| 325 | + | if c.Header { | |
| 326 | + | continue | |
| 327 | + | } | |
| 328 | + | if n := len(c.Name); n > nameWidth { | |
| 329 | + | nameWidth = n | |
| 330 | + | } | |
| 331 | + | } | |
| 319 | 332 | var lines []string | |
| 320 | 333 | for i, c := range m { | |
| 321 | 334 | if c.Header { | |
| @@ -332,8 +345,8 @@ func (s *slashSuggester) Render(input string, th tui.Theme, width int) []string | |||
| 332 | 345 | continue | |
| 333 | 346 | } | |
| 334 | 347 | name := c.Name | |
| 335 | - | if len(name) < 10 { | |
| 336 | - | name = name + strings.Repeat(" ", 10-len(name)) | |
| 348 | + | if len(name) < nameWidth { | |
| 349 | + | name = name + strings.Repeat(" ", nameWidth-len(name)) | |
| 337 | 350 | } | |
| 338 | 351 | plain := " " + name + " " + c.Desc | |
| 339 | 352 | if i == s.cursor { | |
| @@ -316,6 +316,19 @@ func (s *slashSuggester) Render(input string, th tui.Theme, width int) []string | |||||
| 316 | } | 316 | } | ||
| 317 | } | 317 | } | ||
| 318 | } | 318 | } | ||
| 319 | + | // Compute the widest command name across the whole match list | |||
| 320 | + | // (built-ins + extension-contributed) so every row's description | |||
| 321 | + | // starts at the same x-position. A minimum keeps short lists | |||
| 322 | + | // from collapsing the descriptions into the name column. | |||
| 323 | + | nameWidth := 10 | |||
| 324 | + | for _, c := range m { | |||
| 325 | + | if c.Header { | |||
| 326 | + | continue | |||
| 327 | + | } | |||
| 328 | + | if n := len(c.Name); n > nameWidth { | |||
| 329 | + | nameWidth = n | |||
| 330 | + | } | |||
| 331 | + | } | |||
| 319 | var lines []string | 332 | var lines []string | ||
| 320 | for i, c := range m { | 333 | for i, c := range m { | ||
| 321 | if c.Header { | 334 | if c.Header { | ||
| @@ -332,8 +345,8 @@ func (s *slashSuggester) Render(input string, th tui.Theme, width int) []string | |||||
| 332 | continue | 345 | continue | ||
| 333 | } | 346 | } | ||
| 334 | name := c.Name | 347 | name := c.Name | ||
| 335 | - | if len(name) < 10 { | 348 | + | if len(name) < nameWidth { |
| 336 | - | name = name + strings.Repeat(" ", 10-len(name)) | 349 | + | name = name + strings.Repeat(" ", nameWidth-len(name)) |
| 337 | } | 350 | } | ||
| 338 | plain := " " + name + " " + c.Desc | 351 | plain := " " + name + " " + c.Desc | ||
| 339 | if i == s.cursor { | 352 | if i == s.cursor { | ||