Skip to content

Commit 99f58fa

Browse files
committed
feat: add spinner, improve errors, optimize scan performance
Priority 1 Polish Features: - Add animated Braille spinner during repo scanning - Improve error messages with actionable suggestions - Include config file path in error suggestions Performance Optimization: - Add smart default ignore patterns for system directories - Always skip Library, .cache, .npm, .vscode, etc. - Fixes slow scan when home directory is configured Files changed: - internal/tui/model.go: Add spinner component - internal/tui/update.go: Handle spinner tick messages - internal/tui/view.go: Show spinner and better error UI - internal/scan/scan.go: Add smart ignore patterns
1 parent c240f2a commit 99f58fa

File tree

4 files changed

+63
-6
lines changed

4 files changed

+63
-6
lines changed

internal/scan/scan.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,39 @@ import (
1313
"github.com/Bharath-code/git-scope/internal/model"
1414
)
1515

16+
// smartIgnorePatterns are always-ignored directories for performance
17+
// These are system/tool directories that should never contain user repos
18+
var smartIgnorePatterns = []string{
19+
// macOS/Linux system directories
20+
"Library", ".Trash", ".cache", ".local",
21+
// Package managers & runtimes
22+
".npm", ".yarn", ".pnpm", ".bun", ".cargo", ".rustup", ".go",
23+
".venv", ".pyenv", ".rbenv", ".nvm", ".sdkman",
24+
// IDE extensions (contain third-party repos, not your code)
25+
".vscode", ".vscode-server", ".cursor", ".zed", ".idea", ".atom",
26+
// Shell & tools configs
27+
".oh-my-zsh", ".tmux", ".vim", ".emacs.d", ".gemini",
28+
// Docker/Cloud
29+
".docker", ".kube", ".ssh", ".gnupg",
30+
// Cloud sync (slow and likely duplicates)
31+
"Google Drive", "OneDrive", "Dropbox", "iCloud",
32+
}
33+
1634
// ScanRoots recursively scans the given root directories for git repositories
1735
// It skips directories matching the ignore patterns
1836
func ScanRoots(roots, ignore []string) ([]model.Repo, error) {
19-
ignoreSet := make(map[string]struct{}, len(ignore))
37+
// Build ignore set from user config + smart defaults
38+
ignoreSet := make(map[string]struct{}, len(ignore)+len(smartIgnorePatterns))
39+
40+
// Add user-defined ignores
2041
for _, pattern := range ignore {
2142
ignoreSet[pattern] = struct{}{}
2243
}
44+
45+
// Add smart defaults (always apply for performance)
46+
for _, pattern := range smartIgnorePatterns {
47+
ignoreSet[pattern] = struct{}{}
48+
}
2349

2450
var mu sync.Mutex
2551
var repos []model.Repo

internal/tui/model.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"sort"
66
"strings"
77

8+
"github.com/charmbracelet/bubbles/spinner"
89
"github.com/charmbracelet/bubbles/table"
910
"github.com/charmbracelet/bubbles/textinput"
1011
tea "github.com/charmbracelet/bubbletea"
@@ -48,6 +49,7 @@ type Model struct {
4849
cfg *config.Config
4950
table table.Model
5051
textInput textinput.Model
52+
spinner spinner.Model
5153
repos []model.Repo
5254
filteredRepos []model.Repo // After filter applied
5355
sortedRepos []model.Repo // After sort applied
@@ -113,10 +115,16 @@ func NewModel(cfg *config.Config) Model {
113115
ti.CharLimit = 50
114116
ti.Width = 30
115117

118+
// Create spinner with Braille pattern
119+
sp := spinner.New()
120+
sp.Spinner = spinner.Dot
121+
sp.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("#7C3AED"))
122+
116123
return Model{
117124
cfg: cfg,
118125
table: t,
119126
textInput: ti,
127+
spinner: sp,
120128
state: StateLoading,
121129
sortMode: SortByDirty,
122130
filterMode: FilterAll,
@@ -125,7 +133,7 @@ func NewModel(cfg *config.Config) Model {
125133

126134
// Init initializes the model
127135
func (m Model) Init() tea.Cmd {
128-
return scanReposCmd(m.cfg)
136+
return tea.Batch(m.spinner.Tick, scanReposCmd(m.cfg))
129137
}
130138

131139
// GetSelectedRepo returns the currently selected repo

internal/tui/update.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os/exec"
66

7+
"github.com/charmbracelet/bubbles/spinner"
78
"github.com/charmbracelet/bubbles/textinput"
89
tea "github.com/charmbracelet/bubbletea"
910
"github.com/Bharath-code/git-scope/internal/model"
@@ -21,6 +22,13 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
2122
m.height = msg.Height
2223
m.resizeTable()
2324

25+
case spinner.TickMsg:
26+
// Update spinner during loading
27+
if m.state == StateLoading {
28+
m.spinner, cmd = m.spinner.Update(msg)
29+
cmds = append(cmds, cmd)
30+
}
31+
2432
case scanCompleteMsg:
2533
m.repos = msg.repos
2634
m.state = StateReady

internal/tui/view.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ func (m Model) renderLoading() string {
3333

3434
b.WriteString(compactLogo())
3535
b.WriteString(" ")
36-
b.WriteString(loadingStyle.Render("⏳ Scanning repositories..."))
36+
b.WriteString(m.spinner.View())
37+
b.WriteString(" ")
38+
b.WriteString(loadingStyle.Render("Scanning repositories..."))
3739
b.WriteString("\n\n")
3840

3941
b.WriteString(subtitleStyle.Render("Searching for git repos in:"))
@@ -56,7 +58,7 @@ func (m Model) renderError() string {
5658
b.WriteString(compactLogo())
5759
b.WriteString(" ")
5860
b.WriteString(errorTitleStyle.Render("✗ Error"))
59-
b.WriteString("\n")
61+
b.WriteString("\n\n")
6062

6163
errContent := ""
6264
if m.err != nil {
@@ -67,9 +69,22 @@ func (m Model) renderError() string {
6769
b.WriteString(errorBoxStyle.Render(errContent))
6870
b.WriteString("\n\n")
6971

70-
b.WriteString(helpItem("q", "quit"))
71-
b.WriteString(" • ")
72+
// Actionable suggestions
73+
b.WriteString(subtitleStyle.Render("💡 Suggestions:"))
74+
b.WriteString("\n")
75+
b.WriteString(pathBulletStyle.Render(" → "))
76+
b.WriteString(pathStyle.Render("Check your config at ~/.config/git-scope/config.yml"))
77+
b.WriteString("\n")
78+
b.WriteString(pathBulletStyle.Render(" → "))
79+
b.WriteString(pathStyle.Render("Run 'git-scope init' to reconfigure"))
80+
b.WriteString("\n")
81+
b.WriteString(pathBulletStyle.Render(" → "))
82+
b.WriteString(pathStyle.Render("Make sure git is installed and in PATH"))
83+
b.WriteString("\n\n")
84+
7285
b.WriteString(helpItem("r", "retry"))
86+
b.WriteString(" • ")
87+
b.WriteString(helpItem("q", "quit"))
7388

7489
return b.String()
7590
}

0 commit comments

Comments
 (0)