プロンプト上の帯に色付きのストライプを表示し、ボタンを押すたびに色を切り替える。複数のターミナルやペインを見分けるための MOD

<img width="718" height="395" alt="Image" src="https://github.com/user-attachments/assets/dab554cf-74a4-4494-b5a9-654f768cd5de" />
プロンプトの上の帯に色付きのストライプを表示し、ボタンを押すたびに色を切り替える Claude Code MOD。色がある間は、会話の中の Claude の回答の左にも同じ色の縦線が付きます。
複数のターミナルやペインで Claude Code を開いているときに、どれがどれか一目で見分けるための道具です。
color ボタンを押すたびに、色が blue → green → yellow → magenta → cyan → red → none の順に切り替わります。/toggle-color 次の色へ/toggle-color blue その色に(blue, green, yellow, magenta, cyan, red)/toggle-color none 色を消すnone から始まります。ターミナル版 Claude Code のプロンプトで:
/plugin install toggle-color --marketplace stakiran/toggle-color
シェルから:
claude plugin install toggle-color --marketplace stakiran/toggle-color
アンインストールは /plugin メニューから。
このリポジトリを clone して:
claude --plugin-dir ./toggle-color
そのセッションだけ MOD が読み込まれます。
claude plugin validate .
claude plugin test .
MIT
hooks/register.js 110 lines1// 色の候補。先頭は「色なし」。押すたびにこの順で巡回する
2// ライト/ダークどちらのテーマでも目に刺さらないよう、彩度を少し落とした色
3const COLORS = [
4 { name: 'none', color: null },
5 { name: 'blue', color: '#4A7BD0' },
6 { name: 'green', color: '#3E9E5B' },
7 { name: 'yellow', color: '#C9A227' },
8 { name: 'magenta', color: '#B569C8' },
9 { name: 'cyan', color: '#3AA6B9' },
10 { name: 'red', color: '#D05A5A' },
11]
12
13// 現在の色。セッション内だけで持ち、終了すれば消える(あえて記憶しない)
14let index = 0
15
16function setIndex($, next) {
17 index = next
18 // 帯も会話の各行も描き直す
19 $.ui.invalidate('ui.render')
20}
21
22function cycle($) {
23 setIndex($, (index + 1) % COLORS.length)
24}
25
26export function register(on) {
27 on('session.start', async ($, e, next) => {
28 await $.command.register({
29 name: 'toggle-color',
30 description: '帯の色を切り替える。/toggle-color <色名> で直接指定、none で消す',
31 })
32 return next(e)
33 })
34
35 // /toggle-color → 次の色へ
36 // /toggle-color blue → その色に
37 // /toggle-color none → 色なしに
38 on('command.run', { command: 'toggle-color' }, async ($, e) => {
39 const arg = e.args.trim().toLowerCase()
40 if (arg === '') {
41 cycle($)
42 } else {
43 const found = COLORS.findIndex(c => c.name === arg)
44 if (found < 0) {
45 return { text: `不明な色: ${arg}。使える色: ${COLORS.map(c => c.name).join(', ')}` }
46 }
47 setIndex($, found)
48 }
49 return { text: `色: ${COLORS[index].name}` }
50 })
51
52 // プロンプト上の帯に、トグルボタンと色付きのストライプを描く
53 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
54 // 他の mod が帯に描いたもの(open-latest のボタンなど)は同じ行の左側に残す
55 const others = await next(e)
56 // アンケートが帯を使っている間は譲る
57 if (e.props.hasSurvey) return others
58
59 const { Box, Text, Button } = $.ui.resolve(e)
60 const { color } = COLORS[index]
61
62 // ラベルは固定の "color" だけにする(色名を入れると幅が変わって並びがずれる)
63 const button = Button({
64 key: 'toggle-color',
65 label: 'color',
66 plain: true,
67 dimColor: true,
68 onPress: () => cycle($),
69 })
70
71 // 他 mod の描画とボタンは縮ませない(ストライプが余白を食ってずれていた)
72 // 他 mod の描画との間は 2 桁あける(columnGap は入れ子の Box との間で効かなかった)
73 const children = [Box({ flexShrink: 0, marginLeft: others ? 2 : 0, children: [button] })]
74 if (others) children.unshift(Box({ flexShrink: 0, children: [others] }))
75 if (color) {
76 // ボタンの右側を帯の端まで塗りつぶす。残り幅いっぱいに伸び、はみ出す分は隠す
77 children.push(
78 Box({
79 flexGrow: 1,
80 flexShrink: 1,
81 overflow: 'hidden',
82 marginLeft: 1,
83 backgroundColor: color,
84 children: [Text({ backgroundColor: color, wrap: 'truncate-end', children: ' ' })],
85 }),
86 )
87 }
88
89 return Box({ flexDirection: 'row', children })
90 })
91
92 // 会話の中の Claude の回答ブロックの左に、色付きの縦線を付ける
93 // (ペインを切り替えるとき、画面は回答で終わっていることが多いので、回答側に色を付ける)
94 on('ui.render', { component: 'AssistantMessage' }, async ($, e, next) => {
95 const row = await next(e)
96 const { color } = COLORS[index]
97 if (!color || !row) return row
98
99 const { Box } = $.ui.resolve(e)
100 // 幅 1 桁の Box に背景色を付ける。行方向の Box の子は高さが揃うので、回答の全行にわたる
101 return Box({
102 flexDirection: 'row',
103 children: [
104 Box({ width: 1, flexShrink: 0, backgroundColor: color }),
105 Box({ flexGrow: 1, children: [row] }),
106 ],
107 })
108 })
109}
110