1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 13:16:47 +00:00

feat(status): persist cat visibility preference

- Add loadCatHidden/saveCatHidden functions
- Save preference to ~/.config/mole/status_prefs
- Load preference on startup
This commit is contained in:
Tw93
2026-01-08 15:58:50 +08:00
parent b1bb91be1f
commit 180037c22b
7 changed files with 68 additions and 24 deletions

View File

@@ -9,15 +9,13 @@ run:
modules-download-mode: readonly modules-download-mode: readonly
linters: linters:
disable-all: true
enable: enable:
# Default linters # Default linters
- govet - govet
- staticcheck - staticcheck
- errcheck
- ineffassign - ineffassign
- unused - unused
# Additional useful linters
- errcheck
linters-settings: linters-settings:
govet: govet:

View File

@@ -74,6 +74,7 @@ mo purge --paths # Configure project scan directories
- **Safety**: Built with strict protections. See [Security Audit](SECURITY_AUDIT.md). Preview changes with `mo clean --dry-run`. - **Safety**: Built with strict protections. See [Security Audit](SECURITY_AUDIT.md). Preview changes with `mo clean --dry-run`.
- **Debug Mode**: Use `--debug` for detailed logs (e.g., `mo clean --debug`). Combine with `--dry-run` for comprehensive preview including risk levels and file details. - **Debug Mode**: Use `--debug` for detailed logs (e.g., `mo clean --debug`). Combine with `--dry-run` for comprehensive preview including risk levels and file details.
- **Navigation**: Supports arrow keys and Vim bindings (`h/j/k/l`). - **Navigation**: Supports arrow keys and Vim bindings (`h/j/k/l`).
- **Status Shortcuts**: In `mo status`, press `k` to toggle cat visibility and save preference, `q` to quit.
- **Configuration**: Run `mo touchid` for Touch ID sudo, `mo completion` for shell tab completion, `mo clean --whitelist` to manage protected paths. - **Configuration**: Run `mo touchid` for Touch ID sudo, `mo completion` for shell tab completion, `mo clean --whitelist` to manage protected paths.
## Features in Detail ## Features in Detail
@@ -185,7 +186,7 @@ Up ▮▯▯▯▯ 0.8 MB/s Chrome ▮▮▮▯▯ 2
Proxy HTTP · 192.168.1.100 Terminal ▮▯▯▯▯ 12.5% Proxy HTTP · 192.168.1.100 Terminal ▮▯▯▯▯ 12.5%
``` ```
Health score based on CPU, memory, disk, temperature, and I/O load. Color-coded by range. Press `k` to hide/show cat, `q` to quit. Health score based on CPU, memory, disk, temperature, and I/O load. Color-coded by range.
### Project Artifact Purge ### Project Artifact Purge

View File

@@ -1,8 +1,11 @@
// Package main provides the mo status command for real-time system monitoring.
package main package main
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"strings"
"time" "time"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
@@ -37,10 +40,50 @@ type model struct {
catHidden bool // true = hidden, false = visible catHidden bool // true = hidden, false = visible
} }
// getConfigPath returns the path to the status preferences file.
func getConfigPath() string {
home, err := os.UserHomeDir()
if err != nil {
return ""
}
return filepath.Join(home, ".config", "mole", "status_prefs")
}
// loadCatHidden loads the cat hidden preference from config file.
func loadCatHidden() bool {
path := getConfigPath()
if path == "" {
return false
}
data, err := os.ReadFile(path)
if err != nil {
return false
}
return strings.TrimSpace(string(data)) == "cat_hidden=true"
}
// saveCatHidden saves the cat hidden preference to config file.
func saveCatHidden(hidden bool) {
path := getConfigPath()
if path == "" {
return
}
// Ensure directory exists
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0755); err != nil {
return
}
value := "cat_hidden=false"
if hidden {
value = "cat_hidden=true"
}
_ = os.WriteFile(path, []byte(value+"\n"), 0644)
}
func newModel() model { func newModel() model {
return model{ return model{
collector: NewCollector(), collector: NewCollector(),
catHidden: false, catHidden: loadCatHidden(),
} }
} }
@@ -55,8 +98,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "q", "esc", "ctrl+c": case "q", "esc", "ctrl+c":
return m, tea.Quit return m, tea.Quit
case "k": case "k":
// Toggle cat visibility // Toggle cat visibility and persist preference
m.catHidden = !m.catHidden m.catHidden = !m.catHidden
saveCatHidden(m.catHidden)
return m, nil return m, nil
} }
case tea.WindowSizeMsg: case tea.WindowSizeMsg:

View File

@@ -286,9 +286,8 @@ func commandExists(name string) bool {
return false return false
} }
defer func() { defer func() {
if r := recover(); r != nil { //nolint:staticcheck // Treat LookPath panics as "missing".
// Treat LookPath panics as "missing". _ = recover()
}
}() }()
_, err := exec.LookPath(name) _, err := exec.LookPath(name)
return err == nil return err == nil

View File

@@ -32,7 +32,7 @@ func collectCPU() (CPUStatus, error) {
} }
// Two-call pattern for more reliable CPU usage. // Two-call pattern for more reliable CPU usage.
warmUpCpu() warmUpCPU()
time.Sleep(cpuSampleInterval) time.Sleep(cpuSampleInterval)
percents, err := cpu.Percent(0, true) percents, err := cpu.Percent(0, true)
var totalPercent float64 var totalPercent float64
@@ -256,6 +256,6 @@ func fallbackCPUUtilization(logical int) (float64, []float64, error) {
return avg, perCore, nil return avg, perCore, nil
} }
func warmUpCpu() { func warmUpCPU() {
cpu.Percent(0, true) //nolint:errcheck cpu.Percent(0, true) //nolint:errcheck
} }

View File

@@ -134,15 +134,16 @@ func calculateHealthScore(cpu CPUStatus, mem MemoryStatus, disks []DiskStatus, d
// Build message. // Build message.
var msg string var msg string
if score >= 90 { switch {
case score >= 90:
msg = "Excellent" msg = "Excellent"
} else if score >= 75 { case score >= 75:
msg = "Good" msg = "Good"
} else if score >= 60 { case score >= 60:
msg = "Fair" msg = "Fair"
} else if score >= 40 { case score >= 40:
msg = "Poor" msg = "Poor"
} else { default:
msg = "Critical" msg = "Critical"
} }

View File

@@ -187,15 +187,16 @@ func renderHeader(m MetricsSnapshot, errMsg string, animFrame int, termWidth int
} }
func getScoreStyle(score int) lipgloss.Style { func getScoreStyle(score int) lipgloss.Style {
if score >= 90 { switch {
case score >= 90:
return lipgloss.NewStyle().Foreground(lipgloss.Color("#87FF87")).Bold(true) return lipgloss.NewStyle().Foreground(lipgloss.Color("#87FF87")).Bold(true)
} else if score >= 75 { case score >= 75:
return lipgloss.NewStyle().Foreground(lipgloss.Color("#87D787")).Bold(true) return lipgloss.NewStyle().Foreground(lipgloss.Color("#87D787")).Bold(true)
} else if score >= 60 { case score >= 60:
return lipgloss.NewStyle().Foreground(lipgloss.Color("#FFD75F")).Bold(true) return lipgloss.NewStyle().Foreground(lipgloss.Color("#FFD75F")).Bold(true)
} else if score >= 40 { case score >= 40:
return lipgloss.NewStyle().Foreground(lipgloss.Color("#FFAF5F")).Bold(true) return lipgloss.NewStyle().Foreground(lipgloss.Color("#FFAF5F")).Bold(true)
} else { default:
return lipgloss.NewStyle().Foreground(lipgloss.Color("#FF6B6B")).Bold(true) return lipgloss.NewStyle().Foreground(lipgloss.Color("#FF6B6B")).Bold(true)
} }
} }
@@ -707,11 +708,11 @@ func humanBytesCompact(v uint64) string {
} }
} }
func shorten(s string, max int) string { func shorten(s string, maxLen int) string {
if len(s) <= max { if len(s) <= maxLen {
return s return s
} }
return s[:max-1] + "…" return s[:maxLen-1] + "…"
} }
func renderTwoColumns(cards []cardData, width int) string { func renderTwoColumns(cards []cardData, width int) string {