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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user