Skip to content

Commit beef752

Browse files
committed
refactor: move duplicate values to constants.go
Signed-off-by: Ayush <mail@ayuch.dev>
1 parent e209532 commit beef752

File tree

8 files changed

+142
-54
lines changed

8 files changed

+142
-54
lines changed

internal/tui/constants.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package tui
2+
3+
import "time"
4+
5+
const (
6+
// --- Layout Ratios ---
7+
// leftPanelWidthRatio defines the percentage of the total width for the left column.
8+
// rightPanelWidthRatio is "1 - leftPanelWidthRatio".
9+
leftPanelWidthRatio = 0.4
10+
// helpViewWidthRatio defines the width of the help view relative to the total width.
11+
helpViewWidthRatio = 0.5
12+
// helpViewHeightRatio defines the height of the help view relative to the total height.
13+
helpViewHeightRatio = 0.75
14+
// expandedPanelHeightRatio defines the height of a focused panel relative to the total height.
15+
expandedPanelHeightRatio = 0.4
16+
17+
// --- Layout Dimensions ---
18+
// collapsedPanelHeight is the fixed height for a panel when it's not in focus.
19+
collapsedPanelHeight = 3
20+
// borderWidth is the horizontal space taken by left and right borders.
21+
borderWidth = 2
22+
// titleBarHeight is the vertical space taken by top and bottom borders with titles.
23+
titleBarHeight = 2
24+
// statusPanelHeight is the fixed height for the status panel.
25+
statusPanelHeight = 3
26+
27+
// --- Help View Styling ---
28+
// helpTitleMargin is the left margin for the title in the help view.
29+
helpTitleMargin = 9
30+
// helpKeyWidth is the fixed width for the keybinding column in the help view.
31+
helpKeyWidth = 12
32+
// helpDescMargin is the right margin for the keybinding column in the help view.
33+
helpDescMargin = 1
34+
35+
// --- Characters & Symbols ---
36+
scrollThumbChar = "▐"
37+
graphNodeChar = "○"
38+
dirExpandedIcon = "▼ "
39+
repoRootNodeName = "."
40+
gitRenameDelimiter = " -> "
41+
initialContentLoading = "Loading..."
42+
43+
// --- File Watcher ---
44+
// fileWatcherPollInterval is the debounce interval for repository file system events.
45+
fileWatcherPollInterval = 500 * time.Millisecond
46+
47+
// --- Git Status Parsing ---
48+
// porcelainStatusPrefixLength is the length of the status prefix in `git status --porcelain`.
49+
porcelainStatusPrefixLength = 3
50+
)
51+
52+
// --- Border Characters ---
53+
const (
54+
borderTop = "─"
55+
borderBottom = "─"
56+
borderLeft = "│"
57+
borderRight = "│"
58+
borderTopLeft = "╭"
59+
borderTopRight = "╮"
60+
borderBottomLeft = "╰"
61+
borderBottomRight = "╯"
62+
)
63+
64+
// --- Tree Characters ---
65+
const (
66+
treeConnector = ""
67+
treeConnectorLast = ""
68+
treePrefix = " "
69+
treePrefixLast = " "
70+
)

internal/tui/filetree.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,23 @@ type Node struct {
1818

1919
// BuildTree parses the output of `git status --porcelain` to construct a file tree.
2020
func BuildTree(gitStatus string) *Node {
21-
root := &Node{name: "."}
21+
root := &Node{name: repoRootNodeName}
2222

2323
lines := strings.Split(gitStatus, "\n")
2424
if len(lines) == 1 && lines[0] == "" {
2525
return root
2626
}
2727

2828
for _, line := range lines {
29-
if len(line) < 3 {
29+
if len(line) < porcelainStatusPrefixLength {
3030
continue
3131
}
3232
status := line[:2]
33-
path := strings.TrimSpace(line[3:])
33+
path := strings.TrimSpace(line[porcelainStatusPrefixLength:])
3434
isRenamed := false
3535

3636
if status[0] == 'R' || status[0] == 'C' {
37-
parts := strings.Split(path, " -> ")
37+
parts := strings.Split(path, gitRenameDelimiter)
3838
if len(parts) == 2 {
3939
path = parts[1]
4040
isRenamed = true
@@ -111,7 +111,7 @@ func (n *Node) compact() {
111111
}
112112

113113
// Do not compact the root node itself.
114-
if n.name == "." {
114+
if n.name == repoRootNodeName {
115115
return
116116
}
117117

@@ -131,7 +131,7 @@ func (n *Node) renderRecursive(prefix string, theme Theme) []string {
131131
newPrefix := prefix + theme.Tree.Prefix
132132

133133
if len(child.children) > 0 { // It's a directory
134-
displayName := "▼ " + child.name
134+
displayName := dirExpandedIcon + child.name
135135
lines = append(lines, fmt.Sprintf("%s\t\t%s", prefix, displayName))
136136
lines = append(lines, child.renderRecursive(newPrefix, theme)...)
137137
} else { // It's a file.

internal/tui/model.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func initialModel() Model {
3232
themeNames := ThemeNames()
3333
gc := git.NewGitCommands()
3434
repoName, branchName, _ := gc.GetRepoInfo()
35-
initialContent := "Loading..."
35+
initialContent := initialContentLoading
3636

3737
// Create a slice to hold all UI panels.
3838
panels := make([]panel, totalPanels)

internal/tui/model_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,23 +99,23 @@ func TestModel_ConditionalScrollbar(t *testing.T) {
9999
t.Run("Scrollbar is hidden when StashPanel is not focused", func(t *testing.T) {
100100
tm.focusedPanel = MainPanel
101101
rendered := tm.renderPanel("Stash", 30, tm.panelHeights[StashPanel], StashPanel)
102-
if strings.Contains(rendered, scrollThumb) {
102+
if strings.Contains(rendered, scrollThumbChar) {
103103
t.Error("Scrollbar thumb should be hidden but was found")
104104
}
105105
})
106106

107107
t.Run("Scrollbar is visible when StashPanel is focused", func(t *testing.T) {
108108
tm.focusedPanel = StashPanel
109109
rendered := tm.renderPanel("Stash", 30, tm.panelHeights[StashPanel], StashPanel)
110-
if !strings.Contains(rendered, scrollThumb) {
110+
if !strings.Contains(rendered, scrollThumbChar) {
111111
t.Error("Scrollbar thumb should be visible but was not found")
112112
}
113113
})
114114

115115
t.Run("Normal panel scrollbar is always visible if scrollable", func(t *testing.T) {
116116
tm.focusedPanel = MainPanel // Focus is NOT on CommitsPanel
117117
rendered := tm.renderPanel("Commits", 30, tm.panelHeights[CommitsPanel], CommitsPanel)
118-
if !strings.Contains(rendered, scrollThumb) {
118+
if !strings.Contains(rendered, scrollThumbChar) {
119119
t.Error("Scrollbar thumb should be visible but was not found")
120120
}
121121
})

internal/tui/theme.go

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package tui
22

3-
import "github.com/charmbracelet/lipgloss"
3+
import (
4+
"sort"
5+
6+
"github.com/charmbracelet/lipgloss"
7+
)
48

59
// Palette defines a set of colors for a theme.
610
type Palette struct {
@@ -105,6 +109,7 @@ type Theme struct {
105109
CommitMerge lipgloss.Style
106110
GraphEdge lipgloss.Style
107111
GraphNode lipgloss.Style
112+
GraphColors []lipgloss.Style
108113
StashName lipgloss.Style
109114
StashMessage lipgloss.Style
110115
ActiveBorder BorderStyle
@@ -130,11 +135,6 @@ type TreeStyle struct {
130135
Connector, ConnectorLast, Prefix, PrefixLast string
131136
}
132137

133-
const (
134-
scrollThumb = "▐"
135-
graphNode = "○"
136-
)
137-
138138
// Themes holds all the available themes, generated from palettes.
139139
var Themes = map[string]Theme{}
140140

@@ -166,23 +166,35 @@ func NewThemeFromPalette(p Palette) Theme {
166166
CommitMerge: lipgloss.NewStyle().Foreground(lipgloss.Color(p.Magenta)),
167167
GraphEdge: lipgloss.NewStyle().Foreground(lipgloss.Color(p.BrightBlack)),
168168
GraphNode: lipgloss.NewStyle().Foreground(lipgloss.Color(p.Green)),
169-
StashName: lipgloss.NewStyle().Foreground(lipgloss.Color(p.Yellow)),
170-
StashMessage: lipgloss.NewStyle().Foreground(lipgloss.Color(p.Fg)),
169+
GraphColors: []lipgloss.Style{
170+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.Green)),
171+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.Yellow)),
172+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.Blue)),
173+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.Magenta)),
174+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.Cyan)),
175+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.BrightGreen)),
176+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.BrightYellow)),
177+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.BrightBlue)),
178+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.BrightMagenta)),
179+
lipgloss.NewStyle().Foreground(lipgloss.Color(p.BrightCyan)),
180+
},
181+
StashName: lipgloss.NewStyle().Foreground(lipgloss.Color(p.Yellow)),
182+
StashMessage: lipgloss.NewStyle().Foreground(lipgloss.Color(p.Fg)),
171183
ActiveBorder: BorderStyle{
172-
Top: "─", Bottom: "─", Left: "│", Right: "│",
173-
TopLeft: "╭", TopRight: "╮", BottomLeft: "╰", BottomRight: "╯",
184+
Top: borderTop, Bottom: borderBottom, Left: borderLeft, Right: borderRight,
185+
TopLeft: borderTopLeft, TopRight: borderTopRight, BottomLeft: borderBottomLeft, BottomRight: borderBottomRight,
174186
Style: lipgloss.NewStyle().Foreground(lipgloss.Color(p.BrightCyan)),
175187
},
176188
InactiveBorder: BorderStyle{
177-
Top: "─", Bottom: "─", Left: "│", Right: "│",
178-
TopLeft: "╭", TopRight: "╮", BottomLeft: "╰", BottomRight: "╯",
189+
Top: borderTop, Bottom: borderBottom, Left: borderLeft, Right: borderRight,
190+
TopLeft: borderTopLeft, TopRight: borderTopRight, BottomLeft: borderBottomLeft, BottomRight: borderBottomRight,
179191
Style: lipgloss.NewStyle().Foreground(lipgloss.Color(p.BrightBlack)),
180192
},
181193
Tree: TreeStyle{
182-
Connector: "",
183-
ConnectorLast: "",
184-
Prefix: " ",
185-
PrefixLast: " ",
194+
Connector: treeConnector,
195+
ConnectorLast: treeConnectorLast,
196+
Prefix: treePrefix,
197+
PrefixLast: treePrefixLast,
186198
},
187199
}
188200
}
@@ -193,5 +205,6 @@ func ThemeNames() []string {
193205
for name := range Palettes {
194206
names = append(names, name)
195207
}
208+
sort.Strings(names)
196209
return names
197210
}

internal/tui/tui.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func (a *App) watchGitDir() {
7373
}
7474
}
7575

76-
ticker := time.NewTicker(500 * time.Millisecond)
76+
ticker := time.NewTicker(fileWatcherPollInterval)
7777
defer ticker.Stop()
7878
var needsUpdate bool
7979

internal/tui/update.go

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func (m Model) fetchPanelContent(panel Panel) tea.Cmd {
194194
}
195195
}
196196
case MainPanel, SecondaryPanel:
197-
content = "Loading..."
197+
content = initialContentLoading
198198
}
199199

200200
if err != nil {
@@ -209,8 +209,8 @@ func (m Model) handleWindowSizeMsg(msg tea.WindowSizeMsg) (Model, tea.Cmd) {
209209
m.width = msg.Width
210210
m.height = msg.Height
211211
m.help.Width = msg.Width
212-
m.helpViewport.Width = int(float64(m.width) * 0.5)
213-
m.helpViewport.Height = int(float64(m.height) * 0.75)
212+
m.helpViewport.Width = int(float64(m.width) * helpViewWidthRatio)
213+
m.helpViewport.Height = int(float64(m.height) * helpViewHeightRatio)
214214

215215
m = m.recalculateLayout()
216216
return m, nil
@@ -376,26 +376,25 @@ func (m Model) recalculateLayout() Model {
376376

377377
contentHeight := m.height - 1 // Account for help bar
378378
m.panelHeights = make([]int, totalPanels)
379-
expandedHeight := int(float64(contentHeight) * 0.4)
380-
collapsedHeight := 3
379+
expandedHeight := int(float64(contentHeight) * expandedPanelHeightRatio)
381380

382381
// Right Column Layout
383382
if m.focusedPanel == SecondaryPanel {
384383
m.panelHeights[SecondaryPanel] = expandedHeight
385384
m.panelHeights[MainPanel] = contentHeight - expandedHeight
386385
} else {
387-
m.panelHeights[SecondaryPanel] = collapsedHeight
388-
m.panelHeights[MainPanel] = contentHeight - collapsedHeight
386+
m.panelHeights[SecondaryPanel] = collapsedPanelHeight
387+
m.panelHeights[MainPanel] = contentHeight - collapsedPanelHeight
389388
}
390389

391390
// Left Column Layout
392-
m.panelHeights[StatusPanel] = 3
391+
m.panelHeights[StatusPanel] = statusPanelHeight
393392
remainingHeight := contentHeight - m.panelHeights[StatusPanel]
394393

395394
if m.focusedPanel == StashPanel {
396395
m.panelHeights[StashPanel] = expandedHeight
397396
} else {
398-
m.panelHeights[StashPanel] = collapsedHeight
397+
m.panelHeights[StashPanel] = collapsedPanelHeight
399398
}
400399

401400
flexiblePanels := []Panel{FilesPanel, BranchesPanel, CommitsPanel}
@@ -437,18 +436,15 @@ func (m Model) recalculateLayout() Model {
437436

438437
// updateViewportSizes applies the calculated dimensions from the model to the viewports.
439438
func (m Model) updateViewportSizes() Model {
440-
horizontalBorderWidth := 2
441-
titleBarHeight := 2
442-
443-
rightSectionWidth := m.width - int(float64(m.width)*0.3)
444-
rightContentWidth := rightSectionWidth - horizontalBorderWidth
439+
leftSectionWidth := int(float64(m.width) * leftPanelWidthRatio)
440+
rightSectionWidth := m.width - leftSectionWidth
441+
rightContentWidth := rightSectionWidth - borderWidth
445442
m.panels[MainPanel].viewport.Width = rightContentWidth
446443
m.panels[MainPanel].viewport.Height = m.panelHeights[MainPanel] - titleBarHeight
447444
m.panels[SecondaryPanel].viewport.Width = rightContentWidth
448445
m.panels[SecondaryPanel].viewport.Height = m.panelHeights[SecondaryPanel] - titleBarHeight
449446

450-
leftSectionWidth := int(float64(m.width) * 0.3)
451-
leftContentWidth := leftSectionWidth - horizontalBorderWidth
447+
leftContentWidth := leftSectionWidth - borderWidth
452448
leftPanels := []Panel{StatusPanel, FilesPanel, BranchesPanel, CommitsPanel, StashPanel}
453449
for _, panel := range leftPanels {
454450
m.panels[panel].viewport.Width = leftContentWidth

0 commit comments

Comments
 (0)