1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 17:24:45 +00:00

feat: Enhance clean, optimize, analyze, and status commands, and update security audit documentation.

This commit is contained in:
Tw93
2025-12-31 16:23:31 +08:00
parent 8ac59da0e2
commit 9aa569cbb6
53 changed files with 538 additions and 1659 deletions

View File

@@ -33,7 +33,7 @@ const (
iconProcs = "❊"
)
// Check if it's Christmas season (Dec 10-31)
// isChristmasSeason reports Dec 10-31.
func isChristmasSeason() bool {
now := time.Now()
month := now.Month()
@@ -41,7 +41,7 @@ func isChristmasSeason() bool {
return month == time.December && day >= 10 && day <= 31
}
// Mole body frames (legs animate)
// Mole body frames.
var moleBody = [][]string{
{
` /\_/\`,
@@ -69,7 +69,7 @@ var moleBody = [][]string{
},
}
// Mole body frames with Christmas hat
// Mole body frames with Christmas hat.
var moleBodyWithHat = [][]string{
{
` *`,
@@ -105,7 +105,7 @@ var moleBodyWithHat = [][]string{
},
}
// Generate frames with horizontal movement
// getMoleFrame renders the animated mole.
func getMoleFrame(animFrame int, termWidth int) string {
var body []string
var bodyIdx int
@@ -119,15 +119,12 @@ func getMoleFrame(animFrame int, termWidth int) string {
body = moleBody[bodyIdx]
}
// Calculate mole width (approximate)
moleWidth := 15
// Move across terminal width
maxPos := termWidth - moleWidth
if maxPos < 0 {
maxPos = 0
}
// Move position: 0 -> maxPos -> 0
cycleLength := maxPos * 2
if cycleLength == 0 {
cycleLength = 1
@@ -141,7 +138,6 @@ func getMoleFrame(animFrame int, termWidth int) string {
var lines []string
if isChristmas {
// Render with red hat on first 3 lines
for i, line := range body {
if i < 3 {
lines = append(lines, padding+hatStyle.Render(line))
@@ -165,27 +161,24 @@ type cardData struct {
}
func renderHeader(m MetricsSnapshot, errMsg string, animFrame int, termWidth int) string {
// Title
title := titleStyle.Render("Mole Status")
// Health Score
scoreStyle := getScoreStyle(m.HealthScore)
scoreText := subtleStyle.Render("Health ") + scoreStyle.Render(fmt.Sprintf("● %d", m.HealthScore))
// Hardware info - compact for single line
// Hardware info for a single line.
infoParts := []string{}
if m.Hardware.Model != "" {
infoParts = append(infoParts, primaryStyle.Render(m.Hardware.Model))
}
if m.Hardware.CPUModel != "" {
cpuInfo := m.Hardware.CPUModel
// Add GPU core count if available (compact format)
// Append GPU core count when available.
if len(m.GPU) > 0 && m.GPU[0].CoreCount > 0 {
cpuInfo += fmt.Sprintf(" (%dGPU)", m.GPU[0].CoreCount)
}
infoParts = append(infoParts, cpuInfo)
}
// Combine RAM and Disk to save space
var specs []string
if m.Hardware.TotalRAM != "" {
specs = append(specs, m.Hardware.TotalRAM)
@@ -200,10 +193,8 @@ func renderHeader(m MetricsSnapshot, errMsg string, animFrame int, termWidth int
infoParts = append(infoParts, m.Hardware.OSVersion)
}
// Single line compact header
headerLine := title + " " + scoreText + " " + subtleStyle.Render(strings.Join(infoParts, " · "))
// Running mole animation
mole := getMoleFrame(animFrame, termWidth)
if errMsg != "" {
@@ -214,19 +205,14 @@ func renderHeader(m MetricsSnapshot, errMsg string, animFrame int, termWidth int
func getScoreStyle(score int) lipgloss.Style {
if score >= 90 {
// Excellent - Bright Green
return lipgloss.NewStyle().Foreground(lipgloss.Color("#87FF87")).Bold(true)
} else if score >= 75 {
// Good - Green
return lipgloss.NewStyle().Foreground(lipgloss.Color("#87D787")).Bold(true)
} else if score >= 60 {
// Fair - Yellow
return lipgloss.NewStyle().Foreground(lipgloss.Color("#FFD75F")).Bold(true)
} else if score >= 40 {
// Poor - Orange
return lipgloss.NewStyle().Foreground(lipgloss.Color("#FFAF5F")).Bold(true)
} else {
// Critical - Red
return lipgloss.NewStyle().Foreground(lipgloss.Color("#FF6B6B")).Bold(true)
}
}
@@ -240,7 +226,6 @@ func buildCards(m MetricsSnapshot, _ int) []cardData {
renderProcessCard(m.TopProcesses),
renderNetworkCard(m.Network, m.Proxy),
}
// Only show sensors if we have valid temperature readings
if hasSensorData(m.Sensors) {
cards = append(cards, renderSensorsCard(m.Sensors))
}
@@ -334,7 +319,7 @@ func renderMemoryCard(mem MemoryStatus) cardData {
} else {
lines = append(lines, fmt.Sprintf("Swap %s", subtleStyle.Render("not in use")))
}
// Memory pressure
// Memory pressure status.
if mem.Pressure != "" {
pressureStyle := okStyle
pressureText := "Status " + mem.Pressure
@@ -405,7 +390,6 @@ func formatDiskLine(label string, d DiskStatus) string {
}
func ioBar(rate float64) string {
// Scale: 0-50 MB/s maps to 0-5 blocks
filled := int(rate / 10.0)
if filled > 5 {
filled = 5
@@ -441,7 +425,7 @@ func renderProcessCard(procs []ProcessInfo) cardData {
}
func miniBar(percent float64) string {
filled := int(percent / 20) // 5 chars max for 100%
filled := int(percent / 20)
if filled > 5 {
filled = 5
}
@@ -471,7 +455,7 @@ func renderNetworkCard(netStats []NetworkStatus, proxy ProxyStatus) cardData {
txBar := netBar(totalTx)
lines = append(lines, fmt.Sprintf("Down %s %s", rxBar, formatRate(totalRx)))
lines = append(lines, fmt.Sprintf("Up %s %s", txBar, formatRate(totalTx)))
// Show proxy and IP in one line
// Show proxy and IP on one line.
var infoParts []string
if proxy.Enabled {
infoParts = append(infoParts, "Proxy "+proxy.Type)
@@ -487,7 +471,6 @@ func renderNetworkCard(netStats []NetworkStatus, proxy ProxyStatus) cardData {
}
func netBar(rate float64) string {
// Scale: 0-10 MB/s maps to 0-5 blocks
filled := int(rate / 2.0)
if filled > 5 {
filled = 5
@@ -511,8 +494,6 @@ func renderBatteryCard(batts []BatteryStatus, thermal ThermalStatus) cardData {
lines = append(lines, subtleStyle.Render("No battery"))
} else {
b := batts[0]
// Line 1: label + bar + percentage (consistent with other cards)
// Only show red when battery is critically low
statusLower := strings.ToLower(b.Status)
percentText := fmt.Sprintf("%5.1f%%", b.Percent)
if b.Percent < 20 && statusLower != "charging" && statusLower != "charged" {
@@ -520,7 +501,6 @@ func renderBatteryCard(batts []BatteryStatus, thermal ThermalStatus) cardData {
}
lines = append(lines, fmt.Sprintf("Level %s %s", batteryProgressBar(b.Percent), percentText))
// Line 2: status with power info
statusIcon := ""
statusStyle := subtleStyle
if statusLower == "charging" || statusLower == "charged" {
@@ -529,7 +509,6 @@ func renderBatteryCard(batts []BatteryStatus, thermal ThermalStatus) cardData {
} else if b.Percent < 20 {
statusStyle = dangerStyle
}
// Capitalize first letter
statusText := b.Status
if len(statusText) > 0 {
statusText = strings.ToUpper(statusText[:1]) + strings.ToLower(statusText[1:])
@@ -537,21 +516,18 @@ func renderBatteryCard(batts []BatteryStatus, thermal ThermalStatus) cardData {
if b.TimeLeft != "" {
statusText += " · " + b.TimeLeft
}
// Add power information
// Add power info.
if statusLower == "charging" || statusLower == "charged" {
// AC powered - show system power consumption
if thermal.SystemPower > 0 {
statusText += fmt.Sprintf(" · %.0fW", thermal.SystemPower)
} else if thermal.AdapterPower > 0 {
statusText += fmt.Sprintf(" · %.0fW Adapter", thermal.AdapterPower)
}
} else if thermal.BatteryPower > 0 {
// Battery powered - show discharge rate
statusText += fmt.Sprintf(" · %.0fW", thermal.BatteryPower)
}
lines = append(lines, statusStyle.Render(statusText+statusIcon))
// Line 3: Health + cycles + temp
healthParts := []string{}
if b.Health != "" {
healthParts = append(healthParts, b.Health)
@@ -560,7 +536,6 @@ func renderBatteryCard(batts []BatteryStatus, thermal ThermalStatus) cardData {
healthParts = append(healthParts, fmt.Sprintf("%d cycles", b.CycleCount))
}
// Add temperature if available
if thermal.CPUTemp > 0 {
tempStyle := subtleStyle
if thermal.CPUTemp > 80 {
@@ -571,7 +546,6 @@ func renderBatteryCard(batts []BatteryStatus, thermal ThermalStatus) cardData {
healthParts = append(healthParts, tempStyle.Render(fmt.Sprintf("%.0f°C", thermal.CPUTemp)))
}
// Add fan speed if available
if thermal.FanSpeed > 0 {
healthParts = append(healthParts, fmt.Sprintf("%d RPM", thermal.FanSpeed))
}
@@ -607,7 +581,6 @@ func renderCard(data cardData, width int, height int) string {
header := titleStyle.Render(titleText) + " " + lineStyle.Render(strings.Repeat("╌", lineLen))
content := header + "\n" + strings.Join(data.lines, "\n")
// Pad to target height
lines := strings.Split(content, "\n")
for len(lines) < height {
lines = append(lines, "")
@@ -780,7 +753,6 @@ func renderTwoColumns(cards []cardData, width int) string {
}
}
// Add empty lines between rows for separation
var spacedRows []string
for i, r := range rows {
if i > 0 {