detached ba458a0

fix(ui): improve layout with global width constants and centered forms

- Add global MaxFormWidth (80) and MaxContentWidth (120) constants in styles
- Center form containers on screen while keeping content left-aligned using lipgloss.Place
- Apply theme colors to welcome logo (cyan) and version text (dimmed)
- Update form builder to use MaxFormWidth constant instead of hardcoded value
- Fix logo and form truncation issues by properly constraining container widths
commit
ba458a0fcb2f4e1a7eb7fec9b2e049d5aa2ed377
author
bnema · · 2025-10-28 03:32 UTC
parent
2574ff7
signature
unsigned
4 files · +35 -10
M internal/ui/components/forms.go +3 -2
@@ -79,8 +79,9 @@ func (fb *FormBuilder) Confirm(title, affirmative, negative string, value *bool)
7979 // CreateForm creates a form with accessibility settings and custom theme
8080 func (fb *FormBuilder) CreateForm(groups ...*huh.Group) *huh.Form {
8181 formWidth := fb.width
82- if formWidth > 76 {
83- formWidth = 76
82+ // Cap to MaxFormWidth from styles
83+ if formWidth > styles.MaxFormWidth {
84+ formWidth = styles.MaxFormWidth
8485 }
8586 if formWidth < 40 {
8687 formWidth = 40
@@ -79,8 +79,9 @@ func (fb *FormBuilder) Confirm(title, affirmative, negative string, value *bool)
79 // CreateForm creates a form with accessibility settings and custom theme79 // CreateForm creates a form with accessibility settings and custom theme
80 func (fb *FormBuilder) CreateForm(groups ...*huh.Group) *huh.Form {80 func (fb *FormBuilder) CreateForm(groups ...*huh.Group) *huh.Form {
81 formWidth := fb.width81 formWidth := fb.width
82- if formWidth > 76 {82+ // Cap to MaxFormWidth from styles
83- formWidth = 7683+ if formWidth > styles.MaxFormWidth {
84+ formWidth = styles.MaxFormWidth
84 }85 }
85 if formWidth < 40 {86 if formWidth < 40 {
86 formWidth = 4087 formWidth = 40
M internal/ui/styles/containers.go +14 -3
@@ -2,17 +2,28 @@ package styles
22
33 import "github.com/charmbracelet/lipgloss"
44
5+// Layout constants
6+const (
7+ MaxFormWidth = 80 // Maximum width for form containers
8+ MaxContentWidth = 120 // Maximum width for content (welcome, complete screens)
9+)
10+
11+// Alignment aliases for clarity
12+const (
13+ LeftAlign = lipgloss.Left
14+ CenterAlign = lipgloss.Center
15+)
16+
517 // Container styles for consistent layout
618 var (
719 // CenteredContainerStyle - Used for welcome, complete, error screens
820 CenteredContainerStyle = lipgloss.NewStyle().
9- MaxWidth(80).
21+ MaxWidth(MaxContentWidth).
1022 Padding(0, 2).
1123 AlignHorizontal(lipgloss.Center)
1224
13- // FormContainerStyle - Used for all forms
25+ // FormContainerStyle - Used for all forms (content left-aligned)
1426 FormContainerStyle = lipgloss.NewStyle().
15- MaxWidth(80).
1627 Padding(0, 2).
1728 AlignHorizontal(lipgloss.Left)
1829 )
@@ -2,17 +2,28 @@ package styles
2 2
3 import "github.com/charmbracelet/lipgloss"3 import "github.com/charmbracelet/lipgloss"
4 4
5+// Layout constants
6+const (
7+ MaxFormWidth = 80 // Maximum width for form containers
8+ MaxContentWidth = 120 // Maximum width for content (welcome, complete screens)
9+)
10+
11+// Alignment aliases for clarity
12+const (
13+ LeftAlign = lipgloss.Left
14+ CenterAlign = lipgloss.Center
15+)
16+
5 // Container styles for consistent layout17 // Container styles for consistent layout
6 var (18 var (
7 // CenteredContainerStyle - Used for welcome, complete, error screens19 // CenteredContainerStyle - Used for welcome, complete, error screens
8 CenteredContainerStyle = lipgloss.NewStyle().20 CenteredContainerStyle = lipgloss.NewStyle().
9- MaxWidth(80).21+ MaxWidth(MaxContentWidth).
10 Padding(0, 2).22 Padding(0, 2).
11 AlignHorizontal(lipgloss.Center)23 AlignHorizontal(lipgloss.Center)
12 24
13- // FormContainerStyle - Used for all forms25+ // FormContainerStyle - Used for all forms (content left-aligned)
14 FormContainerStyle = lipgloss.NewStyle().26 FormContainerStyle = lipgloss.NewStyle().
15- MaxWidth(80).
16 Padding(0, 2).27 Padding(0, 2).
17 AlignHorizontal(lipgloss.Left)28 AlignHorizontal(lipgloss.Left)
18 )29 )
M internal/ui/views/forms.go +12 -2
@@ -3,9 +3,10 @@ package views
33 import (
44 "github.com/bnema/archup/internal/ui/model"
55 "github.com/bnema/archup/internal/ui/styles"
6+ "github.com/charmbracelet/lipgloss"
67 )
78
8-// RenderForm renders any form view (left-aligned with padding)
9+// RenderForm renders any form view (container centered, content left-aligned)
910 func RenderForm(m model.UI) string {
1011 if m.CurrentForm() == nil {
1112 return "Loading form..."
@@ -13,6 +14,15 @@ func RenderForm(m model.UI) string {
1314
1415 header := CenterText(m.RenderPhaseHeader(), m.Width())
1516 form := m.CurrentForm().View()
17+ content := styles.FormContainerStyle.Render(header + "\n\n" + form)
1618
17- return styles.FormContainerStyle.Render(header + "\n\n" + form)
19+ // Center the container on screen, but keep form content left-aligned
20+ return lipgloss.Place(
21+ m.Width(),
22+ m.Height(),
23+ lipgloss.Center,
24+ lipgloss.Top,
25+ content,
26+ lipgloss.WithWhitespaceChars(" "),
27+ )
1828 }
@@ -3,9 +3,10 @@ package views
3 import (3 import (
4 "github.com/bnema/archup/internal/ui/model"4 "github.com/bnema/archup/internal/ui/model"
5 "github.com/bnema/archup/internal/ui/styles"5 "github.com/bnema/archup/internal/ui/styles"
6+ "github.com/charmbracelet/lipgloss"
6 )7 )
7 8
8-// RenderForm renders any form view (left-aligned with padding)9+// RenderForm renders any form view (container centered, content left-aligned)
9 func RenderForm(m model.UI) string {10 func RenderForm(m model.UI) string {
10 if m.CurrentForm() == nil {11 if m.CurrentForm() == nil {
11 return "Loading form..."12 return "Loading form..."
@@ -13,6 +14,15 @@ func RenderForm(m model.UI) string {
13 14
14 header := CenterText(m.RenderPhaseHeader(), m.Width())15 header := CenterText(m.RenderPhaseHeader(), m.Width())
15 form := m.CurrentForm().View()16 form := m.CurrentForm().View()
17+ content := styles.FormContainerStyle.Render(header + "\n\n" + form)
16 18
17- return styles.FormContainerStyle.Render(header + "\n\n" + form)19+ // Center the container on screen, but keep form content left-aligned
20+ return lipgloss.Place(
21+ m.Width(),
22+ m.Height(),
23+ lipgloss.Center,
24+ lipgloss.Top,
25+ content,
26+ lipgloss.WithWhitespaceChars(" "),
27+ )
18 }28 }
M internal/ui/views/helpers.go +6 -3
@@ -4,6 +4,7 @@ import (
44 "fmt"
55 "strings"
66
7+ "github.com/bnema/archup/internal/ui/styles"
78 "github.com/charmbracelet/lipgloss"
89 )
910
@@ -51,7 +52,7 @@ func (l *Logo) RenderCentered(termWidth int) string {
5152 return l.Render()
5253 }
5354
54-// Render renders the logo
55+// Render renders the logo with theme colors
5556 func (l *Logo) Render() string {
5657 lines := strings.Split(logoASCII, "\n")
5758 var rendered strings.Builder
@@ -62,12 +63,14 @@ func (l *Logo) Render() string {
6263 continue
6364 }
6465
65- rendered.WriteString(line)
66+ // Apply bright cyan color to the logo
67+ rendered.WriteString(styles.LogoCyanStyle.Render(line))
6668 rendered.WriteString("\n")
6769 }
6870
6971 rendered.WriteString("\n")
70- rendered.WriteString(fmt.Sprintf(" %s\n", l.version))
72+ // Apply dimmed style to version
73+ rendered.WriteString(styles.HelpStyle.Render(fmt.Sprintf(" %s\n", l.version)))
7174
7275 return rendered.String()
7376 }
@@ -4,6 +4,7 @@ import (
4 "fmt"4 "fmt"
5 "strings"5 "strings"
6 6
7+ "github.com/bnema/archup/internal/ui/styles"
7 "github.com/charmbracelet/lipgloss"8 "github.com/charmbracelet/lipgloss"
8 )9 )
9 10
@@ -51,7 +52,7 @@ func (l *Logo) RenderCentered(termWidth int) string {
51 return l.Render()52 return l.Render()
52 }53 }
53 54
54-// Render renders the logo55+// Render renders the logo with theme colors
55 func (l *Logo) Render() string {56 func (l *Logo) Render() string {
56 lines := strings.Split(logoASCII, "\n")57 lines := strings.Split(logoASCII, "\n")
57 var rendered strings.Builder58 var rendered strings.Builder
@@ -62,12 +63,14 @@ func (l *Logo) Render() string {
62 continue63 continue
63 }64 }
64 65
65- rendered.WriteString(line)66+ // Apply bright cyan color to the logo
67+ rendered.WriteString(styles.LogoCyanStyle.Render(line))
66 rendered.WriteString("\n")68 rendered.WriteString("\n")
67 }69 }
68 70
69 rendered.WriteString("\n")71 rendered.WriteString("\n")
70- rendered.WriteString(fmt.Sprintf(" %s\n", l.version))72+ // Apply dimmed style to version
73+ rendered.WriteString(styles.HelpStyle.Render(fmt.Sprintf(" %s\n", l.version)))
71 74
72 return rendered.String()75 return rendered.String()
73 }76 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 0

    ↑↓ moveenter openesc close