detached 56e2e7e

Add more fun spinner messages

Adds the 39 phrases requested in #215.

The five craftsman-style aphorisms go away in the same change. They read
differently from the action phrases around them, and the maintainer
agreed in that thread that removing them beats shipping the wider list
with the two styles mixed. Rephrasing and reintroducing them later is
still open.
commit
56e2e7ec57a1c4edf3bc4c9266b93b7c3b8cd5d5
author
Mike Smith · · 2026-09-28 11:46 UTC
committer
Mike Smith · · 2026-09-28 12:04 UTC
parent
2a19df1
signature
unsigned
2 files · +73 -5
M packages/tui/theme.go +39 -5
@@ -140,11 +140,45 @@ var defaultSpinnerMessages = []string{
140140 "arguing with a stack trace",
141141 "googling the answer (not really)",
142142 "rewriting history",
143- "every draft is a stone in the work",
144- "bringing order to the unhewn",
145- "finding the load-bearing measure",
146- "where clarity grows, work grows lighter",
147- "every correction serves the work",
143+ "quantizing confidence",
144+ "marinating the prompt",
145+ "improvising deterministically",
146+ "duct-taping inference",
147+ "shaking out bugs",
148+ "optimizing for vibes",
149+ "seasoning the output",
150+ "tuning the nonsense",
151+ "borrowing more RAM",
152+ "hydrating context",
153+ "feeding the decoder",
154+ "thrashing the cache",
155+ "starving the GPU",
156+ "decoding consequences",
157+ "tool-calling recklessly",
158+ "masking the panic",
159+ "escaping recursion",
160+ "hallucinating responsibly",
161+ "routing around doom",
162+ "tokenizing destiny",
163+ "indexing the void",
164+ "forking the timeline",
165+ "routing raccoons",
166+ "patching spacetime",
167+ "allocating spectral resources",
168+ "sampling forbidden geometry",
169+ "distilling bad decisions",
170+ "vectorizing intrusive thoughts",
171+ "sampling cursed logits",
172+ "ranking imaginary futures",
173+ "overflowing the window",
174+ "leasing neural horsepower",
175+ "vacuuming attention residue",
176+ "softmaxing reality",
177+ "truncating childhood memories",
178+ "spending probability mass",
179+ "paying the token toll",
180+ "renting datacenter wisdom",
181+ "downgrading gracefully",
148182 }
149183
150184 var Dark = Theme{
@@ -140,11 +140,45 @@ var defaultSpinnerMessages = []string{
140 "arguing with a stack trace",140 "arguing with a stack trace",
141 "googling the answer (not really)",141 "googling the answer (not really)",
142 "rewriting history",142 "rewriting history",
143- "every draft is a stone in the work",143+ "quantizing confidence",
144- "bringing order to the unhewn",144+ "marinating the prompt",
145- "finding the load-bearing measure",145+ "improvising deterministically",
146- "where clarity grows, work grows lighter",146+ "duct-taping inference",
147- "every correction serves the work",147+ "shaking out bugs",
148+ "optimizing for vibes",
149+ "seasoning the output",
150+ "tuning the nonsense",
151+ "borrowing more RAM",
152+ "hydrating context",
153+ "feeding the decoder",
154+ "thrashing the cache",
155+ "starving the GPU",
156+ "decoding consequences",
157+ "tool-calling recklessly",
158+ "masking the panic",
159+ "escaping recursion",
160+ "hallucinating responsibly",
161+ "routing around doom",
162+ "tokenizing destiny",
163+ "indexing the void",
164+ "forking the timeline",
165+ "routing raccoons",
166+ "patching spacetime",
167+ "allocating spectral resources",
168+ "sampling forbidden geometry",
169+ "distilling bad decisions",
170+ "vectorizing intrusive thoughts",
171+ "sampling cursed logits",
172+ "ranking imaginary futures",
173+ "overflowing the window",
174+ "leasing neural horsepower",
175+ "vacuuming attention residue",
176+ "softmaxing reality",
177+ "truncating childhood memories",
178+ "spending probability mass",
179+ "paying the token toll",
180+ "renting datacenter wisdom",
181+ "downgrading gracefully",
148 }182 }
149 183
150 var Dark = Theme{184 var Dark = Theme{
A packages/tui/theme_test.go +34 -0
@@ -0,0 +1,34 @@
1+package tui
2+
3+import (
4+ "strings"
5+ "testing"
6+ "unicode"
7+)
8+
9+// The spinner message is interpolated straight into the single-line
10+// status bar, so the default list has to stay well-formed: blank or
11+// untrimmed entries leave a ragged gap, control characters would break
12+// the line, and duplicates would quietly skew which quips show up.
13+func TestDefaultSpinnerMessagesAreUsable(t *testing.T) {
14+ if len(defaultSpinnerMessages) == 0 {
15+ t.Fatal("default spinner messages must not be empty")
16+ }
17+
18+ seen := make(map[string]int, len(defaultSpinnerMessages))
19+ for i, msg := range defaultSpinnerMessages {
20+ if strings.TrimSpace(msg) == "" {
21+ t.Fatalf("message %d is blank", i)
22+ }
23+ if msg != strings.TrimSpace(msg) {
24+ t.Fatalf("message %d = %q, want surrounding whitespace trimmed", i, msg)
25+ }
26+ if strings.ContainsFunc(msg, unicode.IsControl) {
27+ t.Fatalf("message %d = %q, want no control characters", i, msg)
28+ }
29+ if prev, dup := seen[msg]; dup {
30+ t.Fatalf("message %d = %q duplicates message %d", i, msg, prev)
31+ }
32+ seen[msg] = i
33+ }
34+}
@@ -0,0 +1,34 @@
1+package tui
2+
3+import (
4+ "strings"
5+ "testing"
6+ "unicode"
7+)
8+
9+// The spinner message is interpolated straight into the single-line
10+// status bar, so the default list has to stay well-formed: blank or
11+// untrimmed entries leave a ragged gap, control characters would break
12+// the line, and duplicates would quietly skew which quips show up.
13+func TestDefaultSpinnerMessagesAreUsable(t *testing.T) {
14+ if len(defaultSpinnerMessages) == 0 {
15+ t.Fatal("default spinner messages must not be empty")
16+ }
17+
18+ seen := make(map[string]int, len(defaultSpinnerMessages))
19+ for i, msg := range defaultSpinnerMessages {
20+ if strings.TrimSpace(msg) == "" {
21+ t.Fatalf("message %d is blank", i)
22+ }
23+ if msg != strings.TrimSpace(msg) {
24+ t.Fatalf("message %d = %q, want surrounding whitespace trimmed", i, msg)
25+ }
26+ if strings.ContainsFunc(msg, unicode.IsControl) {
27+ t.Fatalf("message %d = %q, want no control characters", i, msg)
28+ }
29+ if prev, dup := seen[msg]; dup {
30+ t.Fatalf("message %d = %q duplicates message %d", i, msg, prev)
31+ }
32+ seen[msg] = i
33+ }
34+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close