SLOPSHOPPER

clawd-mods

Clawd animates above the prompt by tool use and mirrors to a Cardputer over BLE — follow if you like : https://x.com/hawkieslab

bandguardcommandpromptnetworktimer
v0.1.1no licenseupdated 2026-10-15hawkymisc/claude-plugins/plugins/clawd-mods/mod
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · clawd-mods
› fix the failing auth test and add an audit log call ● clawd-mods: clawd-mods: status failed: bridge answered 0 ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /clawd-mods ⎿ clawd-mods: 使い方: /clawd-mods demo — プロンプト上の帯で全アニメを 1 つ 3 秒ずつ再生します Done! ▀▀▀▀▀▀▀▀▀▀ Cardputer: 未接続 (bridge停止) ▄▄▀▀▀▀▀▀▀▀▀▀▄▄ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀ ▀ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Band
Done! ▀▀▀▀▀▀▀▀▀▀ Cardputer: 未接続 (bridge停止) ▄▄▀▀▀▀▀▀▀▀▀▀▄▄ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀ ▀
README

claude-plugins

Hawkies' Lab の Claude Code プラグイン(Mods)マーケットプレイス。

follow if you like : https://x.com/hawkieslab

インストール

/plugin marketplace add hawkymisc/claude-plugins
/plugin install clawd-mods@hawkymisc-claude-plugins
/plugin install gh-board@hawkymisc-claude-plugins

プラグイン

名前説明
clawd-modsツール使用に応じた Clawd アニメをプロンプト上部に表示し、BLE で Cardputer ADV にミラーする(プラグイン本体は mod/、bridge/ と firmware/ は任意の周辺機器向け)
gh-boardセッション開始時とターン終了時に GitHub の PR / Issue 一覧を右ペインに表示する
Source 5 files
hooks/register.tsx 367 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register, Timer } from 'claude-code'
3
4import type { BleState, View } from '../types'
5import { RASTER_COLUMNS, RASTER_ROWS, animSvg, rasterCells, tickFor } from './draw'
6import { Tracker, animOf, captionOf, eventBody, sameView } from './logic'
7import { ANIMS } from './sprites'
8import type { AnimName } from './sprites'
9
10const PLUGIN = 'clawd-mods'
11const viewAtom = atom({ plugin: 'clawd-mods', key: 'view' } as const, { state: 'idle' } as View)
12const bleAtom = atom({ plugin: 'clawd-mods', key: 'ble' } as const, 'unknown' as BleState)
13const tickAtom = atom({ plugin: 'clawd-mods', key: 'tick' } as const, 0)
14// `/clawd-mods demo` 再生中のアニメ番号。null なら通常表示
15const demoAtom = atom({ plugin: 'clawd-mods', key: 'demo' } as const, null as number | null)
16
17const ERROR_MS = 1500
18const FLUSH_MS = 150
19const STATUS_MS = 5000
20const TERMINAL_TICK_MS = 100
21// bridge 再起動・10 分の stale 掃除 (SPEC §4) で消えないよう、現在の状態を定期再送する
22const HEARTBEAT_MS = 60_000
23const DEMO_STEP_MS = 3000
24const DEMO_NAMES = Object.keys(ANIMS) as AnimName[]
25const USAGE = '使い方: /clawd-mods demo — プロンプト上の帯で全アニメを 1 つ 3 秒ずつ再生します'
26
27type Ctx = {
28  tracker: Tracker
29  session: string
30  label: string
31  socketPath: string
32  shown: View
33  pending: View | null
34  isSending: boolean
35  lastError: string
36  isStarted: boolean
37  startScheduled: boolean
38  transient: Timer | undefined
39  terminalTimer: Timer | undefined
40  loops: Timer[]
41  demoTimer: Timer | undefined
42  isCommandRegistered: boolean
43}
44
45function newCtx(): Ctx {
46  return {
47    tracker: new Tracker(),
48    session: '',
49    label: '',
50    socketPath: '',
51    shown: { state: 'idle' },
52    pending: null,
53    isSending: false,
54    lastError: '',
55    isStarted: false,
56    startScheduled: false,
57    transient: undefined,
58    terminalTimer: undefined,
59    loops: [],
60    demoTimer: undefined,
61    isCommandRegistered: false,
62  }
63}
64
65function errText(e: unknown): string {
66  if (e instanceof Error) return e.message
67  if (typeof e === 'object' && e !== null) return JSON.stringify(e) // HookFailure 等
68  return String(e)
69}
70
71function logOnce($: EngineInterface, ctx: Ctx, text: string): void {
72  // 同じ失敗を毎回ログに積まない (bridge 停止中は FLUSH_MS 毎に失敗しうる)
73  if (text === ctx.lastError) return
74  ctx.lastError = text
75  $.ui.log(`${PLUGIN}: ${text}`)
76}
77
78async function publish($: EngineInterface, ctx: Ctx): Promise<void> {
79  const view = ctx.tracker.view()
80  if (sameView(view, ctx.shown)) return
81  ctx.shown = view
82  ctx.pending = view
83  await update($, viewAtom, () => view)
84}
85
86function later($: EngineInterface, ctx: Ctx, ms: number, fn: () => void): void {
87  ctx.transient?.cancel()
88  ctx.transient = $.clock.after(ms, () => {
89    fn()
90    void publish($, ctx)
91  })
92}
93
94async function post($: EngineInterface, ctx: Ctx, body: string): Promise<void> {
95  if (!ctx.socketPath) return
96  const res = await $.http.fetch('http://clawd/event', {
97    method: 'POST',
98    headers: { 'content-type': 'application/json' },
99    body,
100    socketPath: ctx.socketPath,
101  })
102  if (!res.ok) throw new Error(`bridge answered ${res.status}`)
103}
104
105async function flush($: EngineInterface, ctx: Ctx): Promise<void> {
106  if (ctx.isSending || ctx.pending === null) return
107  const view = ctx.pending
108  ctx.pending = null
109  ctx.isSending = true
110  try {
111    await post($, ctx, eventBody(ctx.session, ctx.label, view))
112    ctx.lastError = ''
113    // 停止表示のままにしない: 送れたなら bridge は生きている
114    if ((await read($, bleAtom)) === 'no-bridge') await pollStatus($, ctx)
115  } catch (e) {
116    // bridge 未起動は想定内 (D12): 帯の「未接続」で見せ、ログは同じ文面につき 1 回
117    logOnce($, ctx, `send failed: ${errText(e)}`)
118    await update($, bleAtom, () => 'no-bridge')
119  } finally {
120    ctx.isSending = false
121  }
122}
123
124async function pollStatus($: EngineInterface, ctx: Ctx): Promise<void> {
125  if (!ctx.socketPath) return
126  try {
127    const res = await $.http.fetch('http://clawd/status', { socketPath: ctx.socketPath })
128    if (!res.ok) throw new Error(`bridge answered ${res.status}`)
129    const ble = (JSON.parse(res.text) as { ble?: string }).ble
130    const next: BleState = ble === 'connected' ? 'connected' : ble === 'scanning' ? 'scanning' : 'disconnected'
131    await update($, bleAtom, () => next)
132  } catch (e) {
133    logOnce($, ctx, `status failed: ${errText(e)}`)
134    await update($, bleAtom, () => 'no-bridge')
135  }
136}
137
138function bleNoteOf(ble: BleState): string | undefined {
139  if (ble === 'connected' || ble === 'unknown') return undefined
140  return ble === 'no-bridge' ? 'Cardputer: 未接続 (bridge停止)' : 'Cardputer: 未接続'
141}
142
143function stopLoops(ctx: Ctx): void {
144  for (const t of ctx.loops) t.cancel()
145  ctx.loops = []
146}
147
148/**
149 * セッション情報を読み、送信ループを張る。session.start が来ない途中ロード
150 * (ホットリロード等) でも最初のフックで呼ばれる。再呼び出し時は張り直す。
151 */
152async function start($: EngineInterface, ctx: Ctx): Promise<void> {
153  ctx.isStarted = true
154  ctx.session = await $.session.id()
155  const cwd = await $.session.cwd()
156  ctx.label = cwd.split('/').filter(Boolean).pop() ?? 'claude'
157  const home = await $.env.get('HOME')
158  ctx.socketPath = home ? `${home}/.clawd-bridge/bridge.sock` : ''
159  stopLoops(ctx)
160  ctx.loops.push(
161    $.clock.every(FLUSH_MS, () => void flush($, ctx)),
162    $.clock.every(STATUS_MS, () => void pollStatus($, ctx)),
163    $.clock.every(HEARTBEAT_MS, () => {
164      ctx.pending ??= ctx.shown
165    }),
166  )
167  ctx.pending = ctx.shown
168  await pollStatus($, ctx)
169  if (!ctx.isCommandRegistered) {
170    try {
171      await $.command.register({ name: 'clawd-mods', description: 'Clawd: `demo` で全アニメを帯で再生' })
172      ctx.isCommandRegistered = true
173    } catch (err) {
174      // コマンドが無くても表示・送信は続ける
175      logHookFailure($, 'command.register', err)
176    }
177  }
178}
179
180/** 帯だけのローカル demo (bridge には送らない)。再実行で最初から。 */
181async function startDemo($: EngineInterface, ctx: Ctx): Promise<void> {
182  ctx.demoTimer?.cancel()
183  await update($, demoAtom, () => 0)
184  ctx.demoTimer = $.clock.every(DEMO_STEP_MS, () => {
185    void update($, demoAtom, i => (i === null || i + 1 >= DEMO_NAMES.length ? null : i + 1)).then(async () => {
186      if ((await read($, demoAtom)) === null) {
187        ctx.demoTimer?.cancel()
188        ctx.demoTimer = undefined
189      }
190    })
191  })
192}
193
194async function ensureStarted($: EngineInterface, ctx: Ctx): Promise<void> {
195  if (!ctx.isStarted) await start($, ctx)
196}
197
198function logHookFailure($: EngineInterface, hook: string, e: unknown): void {
199  $.ui.log(`${PLUGIN}: ${hook} hook failed: ${errText(e)}`)
200}
201
202function startTerminalTicks($: EngineInterface, ctx: Ctx): void {
203  // ターミナルだけフレームを自前で送る。Desktop は SVG の SMIL が回す。
204  ctx.terminalTimer ??= $.clock.every(TERMINAL_TICK_MS, () => void update($, tickAtom, t => t + 1))
205}
206
207export const register: Register = on => {
208  const ctx = newCtx()
209
210  // 方針 (D12): 表示用 Mod の失敗で Claude を止めない。自前の処理は try で囲んでログに残し、
211  // next(e) はちょうど 1 回だけ呼ぶ。.catch は想定外の throw の最後の受け皿 (ログ付き)。
212
213  on('session.start', async ($, e, next) => {
214    const result = await next(e)
215    try {
216      ctx.tracker.reset()
217      ctx.shown = { state: 'idle' }
218      await update($, viewAtom, () => ctx.shown)
219      await start($, ctx)
220    } catch (err) {
221      logHookFailure($, 'session.start', err)
222    }
223    return result
224  })
225
226  on('session.end', async ($, e, next) => {
227    // 送信待ち・ループを先に止め、end の後に古い状態が届いてセッションが蘇るのを防ぐ
228    stopLoops(ctx)
229    ctx.pending = null
230    try {
231      await post($, ctx, eventBody(ctx.session || e.sessionId, ctx.label, 'end'))
232    } catch (err) {
233      logOnce($, ctx, `end failed: ${errText(err)}`)
234    }
235    ctx.isStarted = false
236    return next(e)
237  })
238
239  on('prompt.submit', async ($, e, next) => {
240    try {
241      await ensureStarted($, ctx)
242      ctx.transient?.cancel()
243      ctx.tracker.submit()
244      await publish($, ctx)
245    } catch (err) {
246      logHookFailure($, 'prompt.submit', err)
247    }
248    return next(e)
249  }).catch(($, e, next) => {
250    logHookFailure($, 'prompt.submit (catch)', next.error)
251    return next(e)
252  })
253
254  on('tool.call', async ($, e, next) => {
255    // サブエージェント内の呼び出しは親の Agent 表示で代表する (D10)
256    if (e.agentId !== undefined || e.tool_use_id === undefined) return next(e)
257    const id = e.tool_use_id
258    try {
259      await ensureStarted($, ctx)
260      ctx.tracker.toolStart(id, e.tool)
261      await publish($, ctx)
262    } catch (err) {
263      logHookFailure($, 'tool.call (before)', err)
264    }
265    const result = await next(e)
266    try {
267      // ユーザーの拒否 (deny) は失敗ではないので error にしない。ツール自身のエラーだけ
268      const isError = result.isError === true
269      ctx.tracker.toolEnd(id, isError)
270      await publish($, ctx)
271      if (isError) later($, ctx, ERROR_MS, () => ctx.tracker.recover())
272    } catch (err) {
273      logHookFailure($, 'tool.call (after)', err)
274    }
275    return result
276  }).catch(($, e, next) => {
277    logHookFailure($, 'tool.call (catch)', next.error)
278    return next(e)
279  })
280
281  // waiting は実際に許可ダイアログが出る時だけ。tool.check の ask は auto mode では
282  // 分類器が自動判定してユーザーに聞かないので使わない
283  on('classic.PermissionRequest', async ($, e, next) => {
284    try {
285      if (e.agent_id === undefined) {
286        ctx.tracker.toolAskByName(e.tool_name)
287        await publish($, ctx)
288      }
289    } catch (err) {
290      logHookFailure($, 'classic.PermissionRequest', err)
291    }
292    return next(e)
293  }).catch(($, e, next) => {
294    logHookFailure($, 'classic.PermissionRequest (catch)', next.error)
295    return next(e)
296  })
297
298  on('turn.complete', async ($, e, next) => {
299    if (e.agentId !== undefined) return next(e) // サブエージェントのターン完了は対象外 (D10)
300    try {
301      // done は次のプロンプトまで維持する (残っている error 復帰タイマーも止める)
302      ctx.transient?.cancel()
303      ctx.tracker.complete()
304      await publish($, ctx)
305    } catch (err) {
306      logHookFailure($, 'turn.complete', err)
307    }
308    return next(e)
309  })
310
311  on('command.run', { command: 'clawd-mods' }, async ($, e) => {
312    if (e.args.trim() !== 'demo') return { text: USAGE }
313    await startDemo($, ctx)
314    return { text: `Clawd demo: ${DEMO_NAMES.length} 種を ${DEMO_STEP_MS / 1000} 秒ずつ再生します` }
315  })
316
317  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
318    if (e.props.hasSurvey) return next(e)
319    // 描画は純粋でなければならない (state 書き込み禁止) ので、途中ロード時の初期化はタイマーで外に出す
320    if (!ctx.isStarted && !ctx.startScheduled) {
321      ctx.startScheduled = true
322      $.clock.after(0, () => void ensureStarted($, ctx).catch(err => logHookFailure($, 'lazy start', err)))
323    }
324    const view = await read($, viewAtom)
325    const bleNote = bleNoteOf(await read($, bleAtom))
326    const demo = await read($, demoAtom)
327    const demoName = demo === null ? undefined : DEMO_NAMES[demo]
328    const anim = demoName ?? animOf(view)
329    const caption = demoName ? `demo ${(demo ?? 0) + 1}/${DEMO_NAMES.length} · ${demoName}` : captionOf(view)
330
331    if (e.surface === 'terminal') {
332      const { Box, Text, Raster } = $.ui.resolve(e)
333      startTerminalTicks($, ctx)
334      const tick = await read($, tickAtom)
335      const cells = rasterCells(anim, tickFor(anim, tick * TERMINAL_TICK_MS))
336      return (
337        <Box flexDirection="row" alignItems="center">
338          <Raster key="clawd" columns={RASTER_COLUMNS} rows={RASTER_ROWS} cells={cells} />
339          <Box flexDirection="column" marginLeft={1}>
340            <Text>{caption}</Text>
341            {bleNote ? <Text dimColor>{bleNote}</Text> : null}
342          </Box>
343        </Box>
344      )
345    }
346
347    if (e.surface === 'desktop') {
348      const { Box, Text, Svg } = $.ui.resolve(e)
349      return (
350        <Box flexDirection="row" alignItems="center">
351          <Svg source={animSvg(anim, 3)} alt={`Clawd: ${caption}`} width={48} height={36} isInteractive />
352          <Box flexDirection="column" marginLeft={1}>
353            <Text>{caption}</Text>
354            {bleNote ? <Text dimColor>{bleNote}</Text> : null}
355          </Box>
356        </Box>
357      )
358    }
359
360    return next(e)
361  }).catch(($, e, next) => {
362    // 描画に失敗したら engine 既定の帯に戻す (プロンプトを壊さない)
363    logHookFailure($, 'ui.render (catch)', next.error)
364    return next(e)
365  })
366}
367
hooks/draw.ts 101 lines
1import { ANIMS, PALETTE, SPRITE_H, SPRITE_W } from './sprites'
2import type { AnimName } from './sprites'
3
4function px(frame: number[][], x: number, y: number): number {
5  return frame[y]?.[x] ?? 0
6}
7
8/** 1 フレームを横方向のランにまとめた rect 列 (SVG サイズ節約)。 */
9function frameRects(frame: number[][], size: number): string {
10  let out = ''
11  for (let y = 0; y < SPRITE_H; y++) {
12    let x = 0
13    while (x < SPRITE_W) {
14      const c = px(frame, x, y)
15      if (c === 0) {
16        x++
17        continue
18      }
19      let end = x + 1
20      while (end < SPRITE_W && px(frame, end, y) === c) end++
21      out += `<rect x="${x * size}" y="${y * size}" width="${(end - x) * size}" height="${size}" fill="${PALETTE[c] ?? '#000'}"/>`
22      x = end
23    }
24  }
25  return out
26}
27
28/**
29 * アニメーションを SMIL の discrete 切替で 1 枚の SVG にする。
30 * Desktop の `Svg isInteractive` が自前で回すので再描画は要らない。
31 */
32export function animSvg(name: AnimName, size: number): string {
33  const { ms, frames } = ANIMS[name]
34  const n = frames.length
35  const dur = `${(ms * n) / 1000}s`
36  const keyTimes = Array.from({ length: n + 1 }, (_, i) => (i / n).toFixed(4)).join(';')
37  const groups = frames
38    .map((f, i) => {
39      const values = Array.from({ length: n + 1 }, (_, k) => (k === i ? 'visible' : 'hidden')).join(';')
40      const anim =
41        n > 1
42          ? `<animate attributeName="visibility" values="${values}" keyTimes="${keyTimes}" calcMode="discrete" dur="${dur}" repeatCount="indefinite"/>`
43          : ''
44      return `<g visibility="${i === 0 ? 'visible' : 'hidden'}">${anim}${frameRects(f, size)}</g>`
45    })
46    .join('')
47  const w = SPRITE_W * size
48  const h = SPRITE_H * size
49  return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}" shape-rendering="crispEdges">${groups}</svg>`
50}
51
52const DEFAULT = 0x01000000
53
54function rgb(c: number): number {
55  const hex = PALETTE[c]
56  return c === 0 || hex === undefined ? DEFAULT : parseInt(hex.slice(1), 16)
57}
58
59/** ターミナル用: `▀` 1 セルで縦 2 ドット。SPRITE_W 列 × SPRITE_H/2 行。 */
60export function rasterCells(name: AnimName, tick: number): string {
61  const { frames } = ANIMS[name]
62  const frame = frames[tick % frames.length] ?? []
63  const words = new Uint32Array(SPRITE_W * (SPRITE_H / 2) * 3)
64  let i = 0
65  for (let row = 0; row < SPRITE_H / 2; row++) {
66    for (let x = 0; x < SPRITE_W; x++) {
67      const top = px(frame, x, row * 2)
68      const bottom = px(frame, x, row * 2 + 1)
69      words[i++] = top === 0 && bottom === 0 ? 0x20 : top === 0 ? 0x2584 : 0x2580
70      words[i++] = top === 0 ? rgb(bottom) : rgb(top)
71      words[i++] = top === 0 ? DEFAULT : rgb(bottom)
72    }
73  }
74  return toBase64(new Uint8Array(words.buffer))
75}
76
77const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
78
79/** 標準パディング付き base64 (実行環境の toBase64 有無に依存しない)。 */
80export function toBase64(bytes: Uint8Array): string {
81  let out = ''
82  for (let i = 0; i < bytes.length; i += 3) {
83    const a = bytes[i] ?? 0
84    const b = bytes[i + 1]
85    const c = bytes[i + 2]
86    const n = (a << 16) | ((b ?? 0) << 8) | (c ?? 0)
87    out += B64[(n >> 18) & 63]! + B64[(n >> 12) & 63]!
88    out += b === undefined ? '=' : B64[(n >> 6) & 63]!
89    out += c === undefined ? '=' : B64[n & 63]!
90  }
91  return out
92}
93
94/** フレーム送り間隔: アニメの ms をターミナル tick (ms) で割った数。 */
95export function tickFor(name: AnimName, elapsedMs: number): number {
96  return Math.floor(elapsedMs / ANIMS[name].ms)
97}
98
99export const RASTER_COLUMNS = SPRITE_W
100export const RASTER_ROWS = SPRITE_H / 2
101
hooks/logic.ts 136 lines
1import type { Category, View } from '../types'
2import type { AnimName } from './sprites'
3
4export type Phase = 'idle' | 'thinking' | 'done' | 'error'
5
6const CATEGORY: Record<string, Category> = {
7  Read: 'read',
8  Grep: 'read',
9  Glob: 'read',
10  LS: 'read',
11  NotebookRead: 'read',
12  Edit: 'write',
13  MultiEdit: 'write',
14  Write: 'write',
15  NotebookEdit: 'write',
16  Bash: 'bash',
17  BashOutput: 'bash',
18  KillShell: 'bash',
19  WebFetch: 'web',
20  WebSearch: 'web',
21  Agent: 'agent',
22  Task: 'agent',
23}
24
25export function categoryOf(tool: string): Category {
26  return CATEGORY[tool] ?? 'other'
27}
28
29/** BLE に載せるツール名: MCP は末尾のツール名だけ、12 文字まで (SPEC §3)。 */
30export function shortToolName(tool: string): string {
31  const tail = tool.startsWith('mcp__') ? (tool.split('__').pop() ?? '') : tool
32  // bridge はスナップショット区切り (| ,) を含む名前を 400 で拒否するので、許可文字以外は _ に
33  const name = tail.replace(/[^A-Za-z0-9_.-]/g, '_').slice(0, 12)
34  return name || 'mcp'
35}
36
37type Call = { id: string; tool: string; isWaiting: boolean }
38
39/**
40 * メインループのツール呼び出しと turn の進行から、今見せる状態を決める。
41 * 優先度: waiting > error > 実行中ツール (最後に始まったもの) > phase。
42 */
43export class Tracker {
44  phase: Phase = 'idle'
45  private calls: Call[] = []
46
47  submit(): void {
48    this.phase = 'thinking'
49    this.calls = []
50  }
51
52  toolStart(id: string, tool: string): void {
53    this.calls.push({ id, tool, isWaiting: false })
54    if (this.phase !== 'error') this.phase = 'thinking'
55  }
56
57  /**
58   * 許可ダイアログ (classic.PermissionRequest) は tool_use_id を持たないので、
59   * 同名で実行中かつまだ待っていない最新の呼び出しを待ち状態にする。
60   */
61  toolAskByName(tool: string): void {
62    const call = [...this.calls].reverse().find(c => c.tool === tool && !c.isWaiting)
63    if (call) call.isWaiting = true
64  }
65
66  toolEnd(id: string, isError: boolean): void {
67    this.calls = this.calls.filter(c => c.id !== id)
68    if (isError) this.phase = 'error'
69    else if (this.phase !== 'error') this.phase = 'thinking'
70  }
71
72  /** error 表示の時間切れ。 */
73  recover(): void {
74    if (this.phase === 'error') this.phase = 'thinking'
75  }
76
77  complete(): void {
78    this.calls = []
79    this.phase = 'done'
80  }
81
82  reset(): void {
83    this.calls = []
84    this.phase = 'idle'
85  }
86
87  view(): View {
88    const waiting = this.calls.find(c => c.isWaiting)
89    if (waiting) return { state: 'waiting', tool: waiting.tool }
90    if (this.phase === 'error') return { state: 'error' }
91    const top = this.calls[this.calls.length - 1]
92    if (top) return { state: 'tool', category: categoryOf(top.tool), tool: top.tool }
93    return { state: this.phase }
94  }
95}
96
97export function animOf(view: View): AnimName {
98  return view.state === 'tool' ? (view.category ?? 'other') : view.state
99}
100
101const VERB: Record<AnimName, string> = {
102  idle: 'Idle',
103  thinking: 'Thinking…',
104  read: 'Reading',
105  write: 'Writing',
106  bash: 'Running',
107  web: 'Browsing',
108  agent: 'Delegating',
109  other: 'Working',
110  done: 'Done!',
111  error: 'Oops',
112  waiting: 'Waiting for you',
113}
114
115export function captionOf(view: View): string {
116  const verb = VERB[animOf(view)]
117  return view.tool ? `${verb} · ${view.tool}` : verb
118}
119
120export function sameView(a: View, b: View): boolean {
121  return a.state === b.state && a.category === b.category && a.tool === b.tool
122}
123
124/** bridge へ送る JSON (SPEC §3)。パス・コマンドは含めない (D7)。 */
125export function eventBody(session: string, label: string, view: View | 'end'): string {
126  if (view === 'end') return JSON.stringify({ v: 1, session, label, state: 'end' })
127  return JSON.stringify({
128    v: 1,
129    session,
130    label: label.slice(0, 12),
131    state: view.state,
132    ...(view.category ? { category: view.category } : {}),
133    ...(view.tool ? { tool: shortToolName(view.tool) } : {}),
134  })
135}
136
hooks/sprites.ts 8 lines
1// 生成ファイル: sprites/gen.py から生成。直接編集しない。
2export const SPRITE_W = 16
3export const SPRITE_H = 12
4export const PALETTE: readonly string[] = ["#000000", "#D97757", "#1F1F1F", "#FFFFFF", "#9A9A9A", "#5BA4E6", "#F2C94C", "#E5484D"]
5export type AnimName = 'idle' | 'thinking' | 'read' | 'write' | 'bash' | 'web' | 'agent' | 'other' | 'done' | 'error' | 'waiting'
6export type Anim = { ms: number; frames: number[][][] }
7export const ANIMS: Record<AnimName, Anim> = {"idle": {"ms": 600, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]}, "thinking": {"ms": 300, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]}, "read": {"ms": 450, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 1, 0, 0], [0, 0, 0, 3, 2, 2, 3, 4, 4, 3, 2, 2, 3, 0, 0, 0], [0, 0, 0, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 1, 0, 0], [0, 0, 0, 3, 2, 2, 3, 4, 4, 3, 2, 2, 3, 0, 0, 0], [0, 0, 0, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 1, 0, 0], [0, 0, 0, 3, 2, 2, 3, 4, 4, 3, 2, 2, 3, 0, 0, 0], [0, 0, 0, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 1, 0, 0], [0, 0, 0, 3, 2, 2, 3, 4, 4, 3, 2, 2, 3, 0, 0, 0], [0, 0, 0, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, 0], [0, 0, 1, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 1, 0, 0], [0, 0, 0, 3, 2, 2, 3, 4, 4, 3, 3, 3, 3, 0, 0, 0], [0, 0, 0, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 1, 0, 0], [0, 0, 0, 3, 3, 3, 3, 4, 4, 3, 2, 3, 2, 0, 0, 0], [0, 0, 0, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 1, 0, 0], [0, 0, 0, 3, 2, 3, 2, 4, 4, 3, 2, 3, 2, 0, 0, 0], [0, 0, 0, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 1, 0, 0], [0, 0, 0, 3, 2, 3, 2, 4, 4, 3, 2, 3, 2, 0, 0, 0], [0, 0, 0, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]}, "write": {"ms": 220, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 6, 0, 0, 0, 0], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 3, 6, 3, 3, 3, 3], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 5, 3, 3, 3, 3], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 6, 0, 0, 0], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 3, 3, 6, 3, 3, 3], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 5, 3, 3, 3], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 6, 0, 0], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 3, 3, 3, 6, 3, 3], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 2, 5, 3, 3], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 6, 0], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 3, 3, 3, 3, 6, 3], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 2, 3, 5, 3], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 6], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 3, 3, 3, 3, 3, 6], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 2, 3, 2, 5], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3]]]}, "bash": {"ms": 250, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 1, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 4], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 4], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]}, "web": {"ms": 260, "frames": [[[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [3, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0], [5, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0], [5, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 5]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 1, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 3], [0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0], [0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 5, 5]], [[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [3, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0], [0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 5, 0], [0, 5, 5, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 5, 5, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 1, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 3], [0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0], [5, 5, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0]]]}, "agent": {"ms": 300, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [4, 0, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 4], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]}, "other": {"ms": 300, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 4], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]}, "done": {"ms": 250, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 6, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 6, 0], [0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0], [6, 1, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 1, 6], [0, 0, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]}, "error": {"ms": 500, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0], [0, 6, 6, 6, 6, 2, 6, 6, 6, 6, 2, 6, 6, 6, 6, 0], [0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0], [0, 0, 0, 6, 0, 6, 0, 0, 0, 0, 6, 0, 6, 0, 0, 0], [0, 0, 0, 6, 0, 6, 0, 0, 0, 0, 6, 0, 6, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0], [0, 6, 6, 6, 6, 2, 6, 6, 6, 6, 2, 6, 6, 6, 6, 0], [0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0], [0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0], [0, 0, 0, 6, 0, 6, 0, 0, 0, 0, 6, 0, 6, 0, 0, 0], [0, 0, 0, 6, 0, 6, 0, 0, 0, 0, 6, 0, 6, 0, 0, 0]]]}, "waiting": {"ms": 300, "frames": [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 1, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]}}
8
types/index.d.ts 11 lines
1export type Category = 'read' | 'write' | 'bash' | 'web' | 'agent' | 'other'
2export type State = 'idle' | 'thinking' | 'tool' | 'done' | 'error' | 'waiting'
3export type View = { state: State; category?: Category; tool?: string }
4export type BleState = 'unknown' | 'connected' | 'scanning' | 'disconnected' | 'no-bridge'
5
6declare module 'claude-code' {
7  interface PluginState {
8    'clawd-mods': { view: View; ble: BleState; tick: number; demo: number | null }
9  }
10}
11