mirror of
https://github.com/anotherhadi/settuings.git
synced 2026-10-05 16:18:24 +02:00
140 lines
3.5 KiB
Go
140 lines
3.5 KiB
Go
package power
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"charm.land/lipgloss/v2"
|
|
ilovetui "github.com/anotherhadi/ilovetui"
|
|
"github.com/anotherhadi/settuings/internal/power"
|
|
)
|
|
|
|
func (m *Model) handleBattery(msg batteryMsg) {
|
|
m.batteryLoading = false
|
|
m.batteryErr = msg.err
|
|
if msg.err != nil {
|
|
return
|
|
}
|
|
m.batteries = msg.batteries
|
|
m.ac = msg.ac
|
|
}
|
|
|
|
func (m Model) renderBattery(width int) string {
|
|
title := lipgloss.NewStyle().Bold(true).Render("Battery")
|
|
|
|
switch {
|
|
case m.batteryLoading:
|
|
return lipgloss.JoinVertical(lipgloss.Left, title, "", ilovetui.S.Faint.Render("Reading battery status…"))
|
|
case m.batteryErr != nil:
|
|
return lipgloss.JoinVertical(lipgloss.Left, title, "", errorStyle().Render(m.batteryErr.Error()))
|
|
}
|
|
|
|
lines := []string{title, ""}
|
|
if len(m.batteries) == 0 {
|
|
lines = append(lines, subtleStyle().Render("No battery detected — this looks like a desktop."))
|
|
}
|
|
for i, b := range m.batteries {
|
|
if i > 0 {
|
|
lines = append(lines, "")
|
|
}
|
|
lines = append(lines, renderBatteryInfo(b, width)...)
|
|
}
|
|
|
|
lines = append(lines, "", subtleStyle().Render("Power source: ")+acLabel(m.ac))
|
|
|
|
return lipgloss.JoinVertical(lipgloss.Left, lines...)
|
|
}
|
|
|
|
func renderBatteryInfo(b power.Battery, width int) []string {
|
|
rows := []string{
|
|
subtleStyle().Render("Name: ") + b.Name,
|
|
subtleStyle().Render("Status: ") + statusLabel(b.Status),
|
|
subtleStyle().Render("Charge: ") + renderBatteryBar(b.Capacity, width),
|
|
}
|
|
if remaining := timeRemainingLabel(b); remaining != "" {
|
|
rows = append(rows, subtleStyle().Render("Time left: ")+remaining)
|
|
}
|
|
if b.Health >= 0 {
|
|
rows = append(rows, subtleStyle().Render("Health: ")+fmt.Sprintf("%d%%", b.Health))
|
|
}
|
|
if b.CycleCount >= 0 {
|
|
rows = append(rows, subtleStyle().Render("Cycle count: ")+fmt.Sprintf("%d", b.CycleCount))
|
|
}
|
|
if b.Technology != "" {
|
|
rows = append(rows, subtleStyle().Render("Technology: ")+b.Technology)
|
|
}
|
|
if model := strings.TrimSpace(b.Manufacturer + " " + b.Model); model != "" {
|
|
rows = append(rows, subtleStyle().Render("Model: ")+model)
|
|
}
|
|
return rows
|
|
}
|
|
|
|
func timeRemainingLabel(b power.Battery) string {
|
|
switch {
|
|
case b.TimeToEmpty != "":
|
|
return b.TimeToEmpty + " until empty"
|
|
case b.TimeToFull != "":
|
|
return b.TimeToFull + " until full"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func statusLabel(status string) string {
|
|
switch status {
|
|
case "Charging":
|
|
return successStyle().Render("⚡ Charging")
|
|
case "Full":
|
|
return successStyle().Render(status)
|
|
case "Discharging":
|
|
return status
|
|
default:
|
|
return mutedStyle().Render(status)
|
|
}
|
|
}
|
|
|
|
func acLabel(ac power.ACStatus) string {
|
|
switch {
|
|
case !ac.Present:
|
|
return mutedStyle().Render("unknown")
|
|
case ac.Online:
|
|
return successStyle().Render("connected")
|
|
default:
|
|
return mutedStyle().Render("disconnected")
|
|
}
|
|
}
|
|
|
|
// renderBatteryBar draws a static block-bar gauge for the charge level,
|
|
// colored by how low it is, same spirit as the audio page's volume bar.
|
|
func renderBatteryBar(pct, width int) string {
|
|
if pct < 0 {
|
|
return subtleStyle().Render("unknown")
|
|
}
|
|
|
|
barWidth := width / 3
|
|
switch {
|
|
case barWidth < 10:
|
|
barWidth = 10
|
|
case barWidth > 30:
|
|
barWidth = 30
|
|
}
|
|
|
|
filled := pct * barWidth / 100
|
|
switch {
|
|
case filled > barWidth:
|
|
filled = barWidth
|
|
case filled < 0:
|
|
filled = 0
|
|
}
|
|
bar := strings.Repeat("█", filled) + strings.Repeat("░", barWidth-filled)
|
|
|
|
color := ilovetui.S.Success
|
|
switch {
|
|
case pct <= 15:
|
|
color = ilovetui.S.Error
|
|
case pct <= 35:
|
|
color = ilovetui.S.Warning
|
|
}
|
|
gauge := lipgloss.NewStyle().Foreground(color).Render("[" + bar + "]")
|
|
return fmt.Sprintf("%s %3d%%", gauge, pct)
|
|
}
|