@@ -20,29 +20,40 @@ const (
2020 StateError
2121)
2222
23+ // SortMode represents different sorting options
24+ type SortMode int
25+
26+ const (
27+ SortByDirty SortMode = iota
28+ SortByName
29+ SortByBranch
30+ SortByLastCommit
31+ )
32+
2333// Model is the Bubbletea model for the TUI
2434type Model struct {
25- cfg * config.Config
26- table table.Model
27- repos []model.Repo
28- state State
29- err error
30- statusMsg string
31- width int
32- height int
35+ cfg * config.Config
36+ table table.Model
37+ repos []model.Repo
38+ sortedRepos []model.Repo // Sorted copy for display
39+ state State
40+ err error
41+ statusMsg string
42+ width int
43+ height int
44+ sortMode SortMode
3345}
3446
3547// NewModel creates a new TUI model
3648func NewModel (cfg * config.Config ) Model {
3749 columns := []table.Column {
38- {Title : "⬤" , Width : 2 }, // Status indicator
39- {Title : "Repository" , Width : 20 },
40- {Title : "Path" , Width : 30 },
41- {Title : "Branch" , Width : 15 },
50+ {Title : "Status" , Width : 6 },
51+ {Title : "Repository" , Width : 18 },
52+ {Title : "Branch" , Width : 14 },
4253 {Title : "Staged" , Width : 6 },
4354 {Title : "Modified" , Width : 8 },
4455 {Title : "Untracked" , Width : 9 },
45- {Title : "Last Commit" , Width : 16 },
56+ {Title : "Last Commit" , Width : 14 },
4657 }
4758
4859 t := table .New (
@@ -52,30 +63,33 @@ func NewModel(cfg *config.Config) Model {
5263 table .WithHeight (12 ),
5364 )
5465
55- // Apply modern table styles
66+ // Apply modern table styles with strong highlighting
5667 s := table .DefaultStyles ()
5768 s .Header = s .Header .
58- BorderStyle (lipgloss .ThickBorder ()).
69+ BorderStyle (lipgloss .NormalBorder ()).
5970 BorderForeground (lipgloss .Color ("#7C3AED" )).
6071 BorderBottom (true ).
6172 Bold (true ).
62- Foreground (lipgloss .Color ("#F9FAFB" )).
63- Background (lipgloss .Color ("#374151" ))
64-
65- s .Selected = s .Selected .
6673 Foreground (lipgloss .Color ("#FFFFFF" )).
6774 Background (lipgloss .Color ("#7C3AED" )).
75+ Padding (0 , 1 )
76+
77+ // Strong row highlighting
78+ s .Selected = s .Selected .
79+ Foreground (lipgloss .Color ("#000000" )).
80+ Background (lipgloss .Color ("#A78BFA" )).
6881 Bold (true )
6982
7083 s .Cell = s .Cell .
71- Foreground ( lipgloss . Color ( "#F9FAFB" ) )
84+ Padding ( 0 , 1 )
7285
7386 t .SetStyles (s )
7487
7588 return Model {
76- cfg : cfg ,
77- table : t ,
78- state : StateLoading ,
89+ cfg : cfg ,
90+ table : t ,
91+ state : StateLoading ,
92+ sortMode : SortByDirty ,
7993 }
8094}
8195
@@ -84,55 +98,96 @@ func (m Model) Init() tea.Cmd {
8498 return scanReposCmd (m .cfg )
8599}
86100
101+ // GetSelectedRepo returns the currently selected repo
102+ func (m Model ) GetSelectedRepo () * model.Repo {
103+ if m .state != StateReady || len (m .sortedRepos ) == 0 {
104+ return nil
105+ }
106+
107+ cursor := m .table .Cursor ()
108+ if cursor >= 0 && cursor < len (m .sortedRepos ) {
109+ return & m .sortedRepos [cursor ]
110+ }
111+ return nil
112+ }
113+
114+ // sortRepos sorts repos based on current sort mode
115+ func (m * Model ) sortRepos () {
116+ m .sortedRepos = make ([]model.Repo , len (m .repos ))
117+ copy (m .sortedRepos , m .repos )
118+
119+ switch m .sortMode {
120+ case SortByDirty :
121+ sort .Slice (m .sortedRepos , func (i , j int ) bool {
122+ if m .sortedRepos [i ].Status .IsDirty != m .sortedRepos [j ].Status .IsDirty {
123+ return m .sortedRepos [i ].Status .IsDirty
124+ }
125+ return m .sortedRepos [i ].Name < m .sortedRepos [j ].Name
126+ })
127+ case SortByName :
128+ sort .Slice (m .sortedRepos , func (i , j int ) bool {
129+ return m .sortedRepos [i ].Name < m .sortedRepos [j ].Name
130+ })
131+ case SortByBranch :
132+ sort .Slice (m .sortedRepos , func (i , j int ) bool {
133+ return m .sortedRepos [i ].Status .Branch < m .sortedRepos [j ].Status .Branch
134+ })
135+ case SortByLastCommit :
136+ sort .Slice (m .sortedRepos , func (i , j int ) bool {
137+ return m .sortedRepos [i ].Status .LastCommit .After (m .sortedRepos [j ].Status .LastCommit )
138+ })
139+ }
140+ }
141+
142+ // updateTable refreshes the table with current sorted repos
143+ func (m * Model ) updateTable () {
144+ m .sortRepos ()
145+ m .table .SetRows (reposToRows (m .sortedRepos ))
146+ }
147+
148+ // GetSortModeName returns the display name of current sort mode
149+ func (m Model ) GetSortModeName () string {
150+ switch m .sortMode {
151+ case SortByDirty :
152+ return "Dirty First"
153+ case SortByName :
154+ return "Name"
155+ case SortByBranch :
156+ return "Branch"
157+ case SortByLastCommit :
158+ return "Recent"
159+ }
160+ return "Unknown"
161+ }
162+
87163// reposToRows converts repos to table rows with status indicators
88164func reposToRows (repos []model.Repo ) []table.Row {
89- // Sort by dirty first, then by name
90- sorted := make ([]model.Repo , len (repos ))
91- copy (sorted , repos )
92- sort .Slice (sorted , func (i , j int ) bool {
93- // Dirty repos first
94- if sorted [i ].Status .IsDirty != sorted [j ].Status .IsDirty {
95- return sorted [i ].Status .IsDirty
96- }
97- // Then by name
98- return sorted [i ].Name < sorted [j ].Name
99- })
100-
101- rows := make ([]table.Row , 0 , len (sorted ))
102- for _ , r := range sorted {
165+ rows := make ([]table.Row , 0 , len (repos ))
166+ for _ , r := range repos {
103167 lastCommit := "N/A"
104168 if ! r .Status .LastCommit .IsZero () {
105169 lastCommit = r .Status .LastCommit .Format ("Jan 02 15:04" )
106170 }
107171
108- // Status indicator
109- indicator := "○" // Clean
172+ // Status indicator with text
173+ status := "✓ Clean"
110174 if r .Status .IsDirty {
111- indicator = "●" // Dirty
175+ status = "● Dirty"
112176 }
113177
114178 rows = append (rows , table.Row {
115- indicator ,
116- truncateString (r .Name , 20 ),
117- truncatePath (r .Path , 30 ),
118- truncateString (r .Status .Branch , 15 ),
119- colorNumber (r .Status .Staged , "#10B981" ), // Green
120- colorNumber (r .Status .Unstaged , "#F59E0B" ), // Amber
121- colorNumber (r .Status .Untracked , "#9CA3AF" ), // Gray
179+ status ,
180+ truncateString (r .Name , 18 ),
181+ truncateString (r .Status .Branch , 14 ),
182+ formatNumber (r .Status .Staged ),
183+ formatNumber (r .Status .Unstaged ),
184+ formatNumber (r .Status .Untracked ),
122185 lastCommit ,
123186 })
124187 }
125188 return rows
126189}
127190
128- // truncatePath shortens a path to fit in the given width
129- func truncatePath (path string , maxLen int ) string {
130- if len (path ) <= maxLen {
131- return path
132- }
133- return "…" + path [len (path )- maxLen + 1 :]
134- }
135-
136191// truncateString shortens a string with ellipsis
137192func truncateString (s string , maxLen int ) string {
138193 if len (s ) <= maxLen {
@@ -141,8 +196,8 @@ func truncateString(s string, maxLen int) string {
141196 return s [:maxLen - 1 ] + "…"
142197}
143198
144- // colorNumber returns a string representation of a number
145- func colorNumber (n int , _ string ) string {
199+ // formatNumber formats a number for display
200+ func formatNumber (n int ) string {
146201 if n == 0 {
147202 return "—"
148203 }
0 commit comments