Clawd sits above the prompt and keeps typing: fast while Claude works, slow while idle

hooks/register.tsx 38 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4const frame = atom({ plugin: 'clawd-buddy', key: 'frame' } as const, 0)
5
6const HANDS = [' ▘▘ ▝▝', ' ▝▝ ▘▘']
7const DOTS = ['●○○', '○●○', '○○●', '○●○']
8
9// One frame of Clawd: idle types at a third of the working speed.
10export function sprite(tick: number, working: boolean) {
11 const t = working ? tick : Math.floor(tick / 3)
12 return { hands: HANDS[t % HANDS.length], dots: DOTS[t % DOTS.length], caption: working ? 'typing' : 'vibing' }
13}
14
15export const register: Register = on => {
16 on('session.start', ($, e, next) => {
17 $.clock.every(450, () => void update($, frame, f => (f + 1) % 1200))
18 return next(e)
19 })
20
21 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
22 if (e.props.hasSurvey || e.props.maxRows < 3) return next(e)
23 const { Box, Text } = $.ui.resolve(e)
24 const s = sprite(await read($, frame), e.props.isWorking)
25 return (
26 <Box flexDirection="column">
27 <Text color="claude"> ▐▛███▜▌</Text>
28 <Text>
29 <Text color="claude">▝▜█████▛▘ </Text>
30 <Text color="#A99BF5">{s.dots}</Text>
31 <Text dimColor> clawd is {s.caption}</Text>
32 </Text>
33 <Text color="claude">{s.hands}</Text>
34 </Box>
35 )
36 })
37}
38types/index.d.ts 8 lines1export type Frame = number
2
3declare module 'claude-code' {
4 interface PluginState {
5 'clawd-buddy': { frame: Frame }
6 }
7}
8