Themed spinner packs: moon, braille or coffee verbs and glyphs that follow each phase of a turn.

Themed spinner packs: moon, braille or coffee verbs and glyphs that follow each phase of a turn.
The pack swaps the spinner's word for a themed verb with a themed glyph beside it. Both step as the turn moves from requesting to thinking, tool use and responding. With the moon pack, one turn in the terminal reads:
✻ Orbiting ◐…
✻ Waxing ◓… (4s · ↓ 310 tokens)
✻ Eclipsing ◒… (12s · ↓ 1.2k tokens)
✻ Waning ◐… (19s · ↓ 2.4k tokens)
The glyph in front, the ellipsis and everything in brackets are Claude Code's own. The mod only rewrites the word.
| Phase | moon | braille | coffee |
|---|---|---|---|
| requesting | Orbiting ◐ | Computing ⠋ | Grinding ∴ |
| thinking | Waxing ◓ | Computing ⠹ | Steeping ∵ |
| tool input | Eclipsing ◑ | Threading ⠼ | Brewing ∷ |
| tool use | Eclipsing ◒ | Threading ⠦ | Brewing ⁂ |
| responding | Waning ◐ | Weaving ⠇ | Pouring ∴ |
In the desktop app, a row that names its step (Creating notes.md ◒…) keeps those words and gains the glyph. Only the generic Working takes the pack's verb.
| Option | Type | Default | What it does |
|---|---|---|---|
pack | string | moon | The pack: moon, braille, coffee, or classic to leave the spinner untouched. |
Set it with /config, or in settings under pluginConfigs["spinner-pack@mod-showroom"].options.
/plugin install spinner-pack --marketplace KROU4/claude-code-mod-showroomclaude plugin install spinner-pack --marketplace KROU4/claude-code-mod-showroomKROU4/claude-code-mod-showroom → Add pluginMIT. See LICENSE.
hooks/register.ts 55 lines1import type { Register, RenderPropsOf } from 'claude-code'
2
3type Phase = RenderPropsOf['Spinner']['mode']
4type Pack = Record<Phase, readonly [verb: string, glyph: string]>
5
6// The Spinner's props carry no glyph: the engine animates its own in front of
7// the word. A pack's glyph therefore joins the verb in `word`, ahead of the
8// engine's ellipsis, and steps with it each time the turn changes phase.
9const PACKS: Record<string, Pack | undefined> = {
10 moon: {
11 requesting: ['Orbiting', '◐'],
12 thinking: ['Waxing', '◓'],
13 'tool-input': ['Eclipsing', '◑'],
14 'tool-use': ['Eclipsing', '◒'],
15 responding: ['Waning', '◐'],
16 },
17 braille: {
18 requesting: ['Computing', '⠋'],
19 thinking: ['Computing', '⠹'],
20 'tool-input': ['Threading', '⠼'],
21 'tool-use': ['Threading', '⠦'],
22 responding: ['Weaving', '⠇'],
23 },
24 coffee: {
25 requesting: ['Grinding', '∴'],
26 thinking: ['Steeping', '∵'],
27 'tool-input': ['Brewing', '∷'],
28 'tool-use': ['Brewing', '⁂'],
29 responding: ['Pouring', '∴'],
30 },
31}
32
33export const register: Register = (on, options) => {
34 const pack = PACKS[String(options.pack)]
35 if (pack === undefined) {
36 return // `classic`: the engine's spinner as it is
37 }
38
39 on('ui.render', { component: 'Spinner' }, ($, e, next) => {
40 // A later build may raise a phase this pack does not know yet.
41 const entry: Pack[Phase] | undefined = pack[e.props.mode]
42 if (entry === undefined || e.props.message !== null) {
43 return next(e)
44 }
45
46 const [verb, glyph] = entry
47 // On the desktop the word names the step being taken (`Creating
48 // notes.md`), worth keeping; only its stand-in `Working` gives way.
49 const isStep = e.surface === 'desktop' && e.props.word !== 'Working'
50 const word = isStep ? e.props.word : verb
51
52 return next({ ...e, props: { ...e.props, word: `${word} ${glyph}` } })
53 })
54}
55