style(tui): tighten code panel gutters and expand tabs
Reduces gutter padding between line numbers and code, replaces tab characters with 4 spaces in code panels so Go and other tab-indented languages render at consistent width.
- commit
- e486f7757984f0a1783b87877bfd5f3e6f493b14
- author
- patriceckhart · · 2026-04-23 17:39 UTC
- parent
- ba3f328
- signature
- unsigned
1 file · +20 -7
M internal/tui/view.go +20 -7
| @@ -11,6 +11,13 @@ import ( | |||
| 11 | 11 | "github.com/patriceckhart/zot/internal/provider" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | + | // expandTabs replaces tab characters with 4 spaces so code from | |
| 15 | + | // tab-indented languages (Go, Makefiles, etc.) renders at a | |
| 16 | + | // consistent width instead of the terminal's default 8-column tabs. | |
| 17 | + | func expandTabs(s string) string { | |
| 18 | + | return strings.ReplaceAll(s, "\t", " ") | |
| 19 | + | } | |
| 20 | + | ||
| 14 | 21 | // pathFromToolArgs returns the "path" argument from a tool_call's | |
| 15 | 22 | // JSON arguments, or "" if the args aren't a JSON object or don't | |
| 16 | 23 | // include one. Used to pick a syntax language for rendering the | |
| @@ -777,7 +784,7 @@ func (v *View) renderDiffRow(line string, width, color int, lineNo int, mark byt | |||
| 777 | 784 | if len(line) == 0 { | |
| 778 | 785 | return "" | |
| 779 | 786 | } | |
| 780 | - | code := line[1:] // strip the leading marker; we re-emit it in colour | |
| 787 | + | code := expandTabs(line[1:]) // strip the leading marker; expand tabs | |
| 781 | 788 | ||
| 782 | 789 | // Syntax-highlight the code half when we know the language. Use | |
| 783 | 790 | // the same HighlightCode pipeline as renderNumberedFile so the | |
| @@ -805,11 +812,11 @@ func (v *View) renderDiffRow(line string, width, color int, lineNo int, mark byt | |||
| 805 | 812 | var gutterText string | |
| 806 | 813 | switch mark { | |
| 807 | 814 | case '+': | |
| 808 | - | gutterText = fmt.Sprintf("+%5d\t", lineNo) | |
| 815 | + | gutterText = fmt.Sprintf("+%3d ", lineNo) | |
| 809 | 816 | case '-': | |
| 810 | - | gutterText = fmt.Sprintf("-%5d\t", lineNo) | |
| 817 | + | gutterText = fmt.Sprintf("-%3d ", lineNo) | |
| 811 | 818 | default: | |
| 812 | - | gutterText = fmt.Sprintf(" %5d\t", lineNo) | |
| 819 | + | gutterText = fmt.Sprintf(" %3d ", lineNo) | |
| 813 | 820 | } | |
| 814 | 821 | var gutter string | |
| 815 | 822 | if mark == ' ' { | |
| @@ -943,8 +950,8 @@ func (v *View) renderNumberedFile(text, sourcePath string) []string { | |||
| 943 | 950 | codes = append(codes, l) | |
| 944 | 951 | continue | |
| 945 | 952 | } | |
| 946 | - | gutter := l[:idx+1] // keep the tab so alignment is preserved | |
| 947 | - | code := l[idx+1:] | |
| 953 | + | gutter := l[:idx] + " " // replace tab with single space | |
| 954 | + | code := expandTabs(l[idx+1:]) | |
| 948 | 955 | gutters = append(gutters, gutter) | |
| 949 | 956 | codes = append(codes, code) | |
| 950 | 957 | } | |
| @@ -1135,6 +1142,12 @@ func (v *View) renderRawFile(text, sourcePath string, startLine int) []string { | |||
| 1135 | 1142 | code := lines[:codeEnd] | |
| 1136 | 1143 | footer := lines[codeEnd:] | |
| 1137 | 1144 | ||
| 1145 | + | // Expand tabs to 4 spaces so Go / Makefile code renders | |
| 1146 | + | // at a consistent width. | |
| 1147 | + | for i, c := range code { | |
| 1148 | + | code[i] = expandTabs(c) | |
| 1149 | + | } | |
| 1150 | + | ||
| 1138 | 1151 | lang := LanguageFromPath(sourcePath) | |
| 1139 | 1152 | var highlighted []string | |
| 1140 | 1153 | if lang != "" { | |
| @@ -1154,7 +1167,7 @@ func (v *View) renderRawFile(text, sourcePath string, startLine int) []string { | |||
| 1154 | 1167 | ||
| 1155 | 1168 | out := make([]string, 0, len(lines)) | |
| 1156 | 1169 | for i, c := range highlighted { | |
| 1157 | - | gutter := fmt.Sprintf("%6d\t", startLine+i) | |
| 1170 | + | gutter := fmt.Sprintf("%4d ", startLine+i) | |
| 1158 | 1171 | out = append(out, " "+v.Theme.FG256(v.Theme.Muted, gutter)+c) | |
| 1159 | 1172 | } | |
| 1160 | 1173 | for _, f := range footer { | |
| @@ -11,6 +11,13 @@ import ( | |||||
| 11 | "github.com/patriceckhart/zot/internal/provider" | 11 | "github.com/patriceckhart/zot/internal/provider" | ||
| 12 | ) | 12 | ) | ||
| 13 | 13 | ||||
| 14 | + | // expandTabs replaces tab characters with 4 spaces so code from | |||
| 15 | + | // tab-indented languages (Go, Makefiles, etc.) renders at a | |||
| 16 | + | // consistent width instead of the terminal's default 8-column tabs. | |||
| 17 | + | func expandTabs(s string) string { | |||
| 18 | + | return strings.ReplaceAll(s, "\t", " ") | |||
| 19 | + | } | |||
| 20 | + | ||||
| 14 | // pathFromToolArgs returns the "path" argument from a tool_call's | 21 | // pathFromToolArgs returns the "path" argument from a tool_call's | ||
| 15 | // JSON arguments, or "" if the args aren't a JSON object or don't | 22 | // JSON arguments, or "" if the args aren't a JSON object or don't | ||
| 16 | // include one. Used to pick a syntax language for rendering the | 23 | // include one. Used to pick a syntax language for rendering the | ||
| @@ -777,7 +784,7 @@ func (v *View) renderDiffRow(line string, width, color int, lineNo int, mark byt | |||||
| 777 | if len(line) == 0 { | 784 | if len(line) == 0 { | ||
| 778 | return "" | 785 | return "" | ||
| 779 | } | 786 | } | ||
| 780 | - | code := line[1:] // strip the leading marker; we re-emit it in colour | 787 | + | code := expandTabs(line[1:]) // strip the leading marker; expand tabs |
| 781 | 788 | ||||
| 782 | // Syntax-highlight the code half when we know the language. Use | 789 | // Syntax-highlight the code half when we know the language. Use | ||
| 783 | // the same HighlightCode pipeline as renderNumberedFile so the | 790 | // the same HighlightCode pipeline as renderNumberedFile so the | ||
| @@ -805,11 +812,11 @@ func (v *View) renderDiffRow(line string, width, color int, lineNo int, mark byt | |||||
| 805 | var gutterText string | 812 | var gutterText string | ||
| 806 | switch mark { | 813 | switch mark { | ||
| 807 | case '+': | 814 | case '+': | ||
| 808 | - | gutterText = fmt.Sprintf("+%5d\t", lineNo) | 815 | + | gutterText = fmt.Sprintf("+%3d ", lineNo) |
| 809 | case '-': | 816 | case '-': | ||
| 810 | - | gutterText = fmt.Sprintf("-%5d\t", lineNo) | 817 | + | gutterText = fmt.Sprintf("-%3d ", lineNo) |
| 811 | default: | 818 | default: | ||
| 812 | - | gutterText = fmt.Sprintf(" %5d\t", lineNo) | 819 | + | gutterText = fmt.Sprintf(" %3d ", lineNo) |
| 813 | } | 820 | } | ||
| 814 | var gutter string | 821 | var gutter string | ||
| 815 | if mark == ' ' { | 822 | if mark == ' ' { | ||
| @@ -943,8 +950,8 @@ func (v *View) renderNumberedFile(text, sourcePath string) []string { | |||||
| 943 | codes = append(codes, l) | 950 | codes = append(codes, l) | ||
| 944 | continue | 951 | continue | ||
| 945 | } | 952 | } | ||
| 946 | - | gutter := l[:idx+1] // keep the tab so alignment is preserved | 953 | + | gutter := l[:idx] + " " // replace tab with single space |
| 947 | - | code := l[idx+1:] | 954 | + | code := expandTabs(l[idx+1:]) |
| 948 | gutters = append(gutters, gutter) | 955 | gutters = append(gutters, gutter) | ||
| 949 | codes = append(codes, code) | 956 | codes = append(codes, code) | ||
| 950 | } | 957 | } | ||
| @@ -1135,6 +1142,12 @@ func (v *View) renderRawFile(text, sourcePath string, startLine int) []string { | |||||
| 1135 | code := lines[:codeEnd] | 1142 | code := lines[:codeEnd] | ||
| 1136 | footer := lines[codeEnd:] | 1143 | footer := lines[codeEnd:] | ||
| 1137 | 1144 | ||||
| 1145 | + | // Expand tabs to 4 spaces so Go / Makefile code renders | |||
| 1146 | + | // at a consistent width. | |||
| 1147 | + | for i, c := range code { | |||
| 1148 | + | code[i] = expandTabs(c) | |||
| 1149 | + | } | |||
| 1150 | + | ||||
| 1138 | lang := LanguageFromPath(sourcePath) | 1151 | lang := LanguageFromPath(sourcePath) | ||
| 1139 | var highlighted []string | 1152 | var highlighted []string | ||
| 1140 | if lang != "" { | 1153 | if lang != "" { | ||
| @@ -1154,7 +1167,7 @@ func (v *View) renderRawFile(text, sourcePath string, startLine int) []string { | |||||
| 1154 | 1167 | ||||
| 1155 | out := make([]string, 0, len(lines)) | 1168 | out := make([]string, 0, len(lines)) | ||
| 1156 | for i, c := range highlighted { | 1169 | for i, c := range highlighted { | ||
| 1157 | - | gutter := fmt.Sprintf("%6d\t", startLine+i) | 1170 | + | gutter := fmt.Sprintf("%4d ", startLine+i) |
| 1158 | out = append(out, " "+v.Theme.FG256(v.Theme.Muted, gutter)+c) | 1171 | out = append(out, " "+v.Theme.FG256(v.Theme.Muted, gutter)+c) | ||
| 1159 | } | 1172 | } | ||
| 1160 | for _, f := range footer { | 1173 | for _, f := range footer { | ||