mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 20:19:45 +00:00
feat: show scanning progress as percentage in disk analyzer
- Implemented progress percentage display (e.g., `(45%)`) in `cmd/analyze` to show scanning status based on cached total files. - Kept the UI clean by avoiding a full progress bar. - fix: formatting improvements in `bin/touchid.sh`.
This commit is contained in:
@@ -30,6 +30,7 @@ func snapshotFromModel(m model) historyEntry {
|
||||
Entries: cloneDirEntries(m.entries),
|
||||
LargeFiles: cloneFileEntries(m.largeFiles),
|
||||
TotalSize: m.totalSize,
|
||||
TotalFiles: m.totalFiles,
|
||||
Selected: m.selected,
|
||||
EntryOffset: m.offset,
|
||||
LargeSelected: m.largeSelected,
|
||||
@@ -250,6 +251,7 @@ func saveCacheToDisk(path string, result scanResult) error {
|
||||
Entries: result.Entries,
|
||||
LargeFiles: result.LargeFiles,
|
||||
TotalSize: result.TotalSize,
|
||||
TotalFiles: result.TotalFiles,
|
||||
ModTime: info.ModTime(),
|
||||
ScanTime: time.Now(),
|
||||
}
|
||||
@@ -264,6 +266,29 @@ func saveCacheToDisk(path string, result scanResult) error {
|
||||
return encoder.Encode(entry)
|
||||
}
|
||||
|
||||
// peekCacheTotalFiles attempts to read the total file count from cache,
|
||||
// ignoring expiration. Used for initial scan progress estimates.
|
||||
func peekCacheTotalFiles(path string) (int64, error) {
|
||||
cachePath, err := getCachePath(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
file, err := os.Open(cachePath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer file.Close() //nolint:errcheck
|
||||
|
||||
var entry cacheEntry
|
||||
decoder := gob.NewDecoder(file)
|
||||
if err := decoder.Decode(&entry); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return entry.TotalFiles, nil
|
||||
}
|
||||
|
||||
func invalidateCache(path string) {
|
||||
cachePath, err := getCachePath(path)
|
||||
if err == nil {
|
||||
|
||||
@@ -35,12 +35,14 @@ type scanResult struct {
|
||||
Entries []dirEntry
|
||||
LargeFiles []fileEntry
|
||||
TotalSize int64
|
||||
TotalFiles int64
|
||||
}
|
||||
|
||||
type cacheEntry struct {
|
||||
Entries []dirEntry
|
||||
LargeFiles []fileEntry
|
||||
TotalSize int64
|
||||
TotalFiles int64
|
||||
ModTime time.Time
|
||||
ScanTime time.Time
|
||||
}
|
||||
@@ -50,6 +52,7 @@ type historyEntry struct {
|
||||
Entries []dirEntry
|
||||
LargeFiles []fileEntry
|
||||
TotalSize int64
|
||||
TotalFiles int64
|
||||
Selected int
|
||||
EntryOffset int
|
||||
LargeSelected int
|
||||
@@ -114,6 +117,8 @@ type model struct {
|
||||
height int // Terminal height
|
||||
multiSelected map[string]bool // Track multi-selected items by path (safer than index)
|
||||
largeMultiSelected map[string]bool // Track multi-selected large files by path (safer than index)
|
||||
totalFiles int64 // Total files found in current/last scan
|
||||
lastTotalFiles int64 // Total files from previous scan (for progress bar)
|
||||
}
|
||||
|
||||
func (m model) inOverviewMode() bool {
|
||||
@@ -195,6 +200,13 @@ func newModel(path string, isOverview bool) model {
|
||||
}
|
||||
}
|
||||
|
||||
// Try to peek last total files for progress bar, even if cache is stale
|
||||
if !isOverview {
|
||||
if total, err := peekCacheTotalFiles(path); err == nil && total > 0 {
|
||||
m.lastTotalFiles = total
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -355,6 +367,7 @@ func (m model) scanCmd(path string) tea.Cmd {
|
||||
Entries: cached.Entries,
|
||||
LargeFiles: cached.LargeFiles,
|
||||
TotalSize: cached.TotalSize,
|
||||
TotalFiles: 0, // Cache doesn't store file count currently, minor UI limitation
|
||||
}
|
||||
return scanResultMsg{result: result, err: nil}
|
||||
}
|
||||
@@ -441,6 +454,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.entries = filteredEntries
|
||||
m.largeFiles = msg.result.LargeFiles
|
||||
m.totalSize = msg.result.TotalSize
|
||||
m.totalFiles = msg.result.TotalFiles
|
||||
m.status = fmt.Sprintf("Scanned %s", humanizeBytes(m.totalSize))
|
||||
m.clampEntrySelection()
|
||||
m.clampLargeSelection()
|
||||
@@ -685,6 +699,9 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
invalidateCache(m.path)
|
||||
m.status = "Refreshing..."
|
||||
m.scanning = true
|
||||
if m.totalFiles > 0 {
|
||||
m.lastTotalFiles = m.totalFiles
|
||||
}
|
||||
atomic.StoreInt64(m.filesScanned, 0)
|
||||
atomic.StoreInt64(m.dirsScanned, 0)
|
||||
atomic.StoreInt64(m.bytesScanned, 0)
|
||||
@@ -968,6 +985,7 @@ func (m model) enterSelectedDir() (tea.Model, tea.Cmd) {
|
||||
m.entries = cloneDirEntries(cached.Entries)
|
||||
m.largeFiles = cloneFileEntries(cached.LargeFiles)
|
||||
m.totalSize = cached.TotalSize
|
||||
m.totalFiles = cached.TotalFiles
|
||||
m.selected = cached.Selected
|
||||
m.offset = cached.EntryOffset
|
||||
m.largeSelected = cached.LargeSelected
|
||||
@@ -978,6 +996,10 @@ func (m model) enterSelectedDir() (tea.Model, tea.Cmd) {
|
||||
m.scanning = false
|
||||
return m, nil
|
||||
}
|
||||
m.lastTotalFiles = 0
|
||||
if total, err := peekCacheTotalFiles(m.path); err == nil && total > 0 {
|
||||
m.lastTotalFiles = total
|
||||
}
|
||||
return m, tea.Batch(m.scanCmd(m.path), tickCmd())
|
||||
}
|
||||
m.status = fmt.Sprintf("File: %s (%s)", selected.Name, humanizeBytes(selected.Size))
|
||||
|
||||
@@ -251,6 +251,7 @@ func scanPathConcurrent(root string, filesScanned, dirsScanned, bytesScanned *in
|
||||
Entries: entries,
|
||||
LargeFiles: largeFiles,
|
||||
TotalSize: total,
|
||||
TotalFiles: atomic.LoadInt64(filesScanned),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -75,10 +75,25 @@ func (m model) View() string {
|
||||
if m.scanning {
|
||||
filesScanned, dirsScanned, bytesScanned := m.getScanProgress()
|
||||
|
||||
fmt.Fprintf(&b, "%s%s%s%s Scanning: %s%s files%s, %s%s dirs%s, %s%s%s\n",
|
||||
progressPrefix := ""
|
||||
if m.lastTotalFiles > 0 {
|
||||
percent := float64(filesScanned) / float64(m.lastTotalFiles) * 100
|
||||
// Cap at 100% generally
|
||||
if percent > 100 {
|
||||
percent = 100
|
||||
}
|
||||
// While strictly scanning, cap at 99% to avoid "100% but still working" confusion
|
||||
if m.scanning && percent >= 100 {
|
||||
percent = 99
|
||||
}
|
||||
progressPrefix = fmt.Sprintf(" %s(%.0f%%)%s", colorCyan, percent, colorReset)
|
||||
}
|
||||
|
||||
fmt.Fprintf(&b, "%s%s%s%s Scanning%s: %s%s files%s, %s%s dirs%s, %s%s%s\n",
|
||||
colorCyan, colorBold,
|
||||
spinnerFrames[m.spinner],
|
||||
colorReset,
|
||||
progressPrefix,
|
||||
colorYellow, formatNumber(filesScanned), colorReset,
|
||||
colorYellow, formatNumber(dirsScanned), colorReset,
|
||||
colorGreen, humanizeBytes(bytesScanned), colorReset)
|
||||
|
||||
Reference in New Issue
Block a user