detached 2ff82fa

Merge pull request #222 from m1k3s0/feature/more-spinner-messages

Add more fun spinner messages
commit
2ff82faa07ac0b4a91880014bad8e60562430f2c
author
Patric Eckhart · · 2026-09-28 19:47 UTC
committer
GitHub · · 2026-09-28 19:47 UTC
merge of
885ddb0 + 66f54fd · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
refs
tag: v0.4.6tag: v0.4.7
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