1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
// display-infos prints a one-line status summary — battery, load, memory,
// Claude plan usage, and the date in three notations — for a status bar or a
// notification popup (see the display-infos-notify wrapper in the rolery
// machine config).
//
// It is a Go port of an embedded Python script. The port exists to drop the
// runtime dependencies that script carried: it shelled out to bc, date and
// free (parsing the last with the `jc` library) on every invocation, and lived
// as a string inside default.nix where it could be neither type-checked nor
// tested. Everything it used those tools for is a few lines of standard
// library here, and the sfttime rendering is inlined in sft.go.
package main
import (
"fmt"
"os"
"strings"
"time"
)
func main() {
line, err := statusLine(time.Now())
if err != nil {
fmt.Fprintf(os.Stderr, "display-infos: %v\n", err)
os.Exit(1)
}
fmt.Println(line)
}
// statusLine assembles the whole line. now is a parameter so tests can pin it.
func statusLine(now time.Time) (string, error) {
bat, err := readBattery()
if err != nil {
return "", err
}
load, err := readLoad5()
if err != nil {
return "", err
}
availGiB, err := readAvailableGiB()
if err != nil {
return "", err
}
// A missing or broken claude-usage service must not break the status line,
// so this error is advisory and the segment is simply omitted.
claude, _ := fetchClaudeUsage()
var b strings.Builder
fmt.Fprintf(&b, "BAT: %d%% ", bat.Percent)
if bat.Charging {
b.WriteString("⚡ ")
}
if bat.Remaining > 0 {
fmt.Fprintf(&b, "%s ", sftDuration(bat.Remaining.Seconds()))
}
fmt.Fprintf(&b, "{%s, %dG} | ", load, availGiB)
b.WriteString(claudeSegment(claude))
fmt.Fprintf(&b, "%s | %s | %s",
formatDate(now),
toSfttime(float64(now.UnixNano())/1e9, 3, false),
now.UTC().Format("15·04"))
return b.String(), nil
}
// claudeSegment renders the Claude usage part, including its trailing
// separator, or the empty string when there is nothing to say.
func claudeSegment(cu *claudeUsage) string {
switch {
case cu == nil:
return ""
case cu.NeedsLogin:
// Nobody is logged in, so any cached percentage is frozen at whatever
// it was when the session died and is actively misleading. Show the
// required action instead of a plausible-looking number.
return "Claude: ⚠login | "
default:
// ⚠ marks that the service could not reach claude.ai on its last
// refresh. Show how old the numbers are too, so a brief network blip
// is visibly different from a fortnight of silence.
stale := ""
if !cu.Reachable {
stale = "⚠"
if cu.Age >= 0 {
stale += humanizeAge(cu.Age)
}
}
return fmt.Sprintf("Claude: %d%% %s%s | ",
cu.Percent, sftDuration(cu.ResetIn.Seconds()), stale)
}
}
// weekdayAbbrev is an explicit English weekday table.
//
// Go's time package is locale-blind by design, so the abbreviation has to come
// from somewhere. Naming it here rather than inheriting LC_TIME also makes the
// output stable: the Python version rendered German ("Fr") purely because
// LC_TIME happened to be de_DE, and would have silently changed language with
// the environment.
var weekdayAbbrev = [...]string{
time.Sunday: "Sun",
time.Monday: "Mon",
time.Tuesday: "Tue",
time.Wednesday: "Wed",
time.Thursday: "Thu",
time.Friday: "Fri",
time.Saturday: "Sat",
}
// formatDate renders the civil date part: "25.09. KW39 Fri 11:56:42".
//
// KW is the ISO 8601 week number (Kalenderwoche), which is not the same as the
// week of the month or a simple day-of-year division: ISO weeks start on
// Monday and week 1 is the one containing the first Thursday, so early January
// can fall in week 52 or 53 of the previous year.
func formatDate(t time.Time) string {
_, week := t.ISOWeek()
return fmt.Sprintf("%02d.%02d. KW%02d %s %s",
t.Day(), int(t.Month()), week,
weekdayAbbrev[t.Weekday()],
t.Format("15:04:05"))
}
|