Mod API リファレンス

描画(ui.render)

出典: Claude Code 2.1.294 が書き出す型定義 Early access:リリースごとに変更される可能性あり ← 私家版 Claude Mods まとめ

ui.render のフックは、コンポーネントのインスタンスを1つずつ受け取ります。

e のフィールド説明
e.componentコンポーネントの種類。マッチャーで絞り込むときに使う。
e.surface表示環境(terminal / desktop / vscode / mobile。ターミナル、デスクトップアプリ、VS Code、モバイルアプリのいずれか)。どこに表示するかを示すだけの値で、信頼できる情報ではないため、ポリシーの判断には使わない。
e.requestIdインスタンスの識別子。ツールの行やダイアログでは tool_use_id、メッセージでは message id、スピナーでは agent id、Pane では開いたときの id になる。
e.propsコンポーネントの props(プレーンなデータ)。next({ ...e, props }) で書き換えられる。
e.viewport描画領域の大きさ { columns, rows, isFullscreen? }。表示環境がまだ大きさを測定していない場合は存在しない。幅が変わるとすべての描画箇所が再描画されるが、高さだけが変わった場合は再描画されない。

フックの戻り値は次の3通りです。描画ツリーを返す、next({ ...e, props }) で Claude Code 本体の描画に渡す props を変える、next(e) で Claude Code 本体の描画をそのまま使う。Pane と AbovePrompt では、e.props.bodyColumns の幅に合わせて描画します(Pane を横に並べて表示しているときは、viewport より狭くなります)。

描画箇所(RenderComponent)

component描画される場所
Pane$.ui.open({ id }) で開いた枠。id ごとに1つのインスタンスがあり、複数開くとタブで切り替えられる。プラグインが自分で開ける唯一のコンポーネントである。
AbovePrompt入力欄のすぐ上の帯(アンケートが表示される場所)。Claude Code 本体が横に [-]、下に n more を表示する。e.props.hasSurvey が true のときは、next(e) を返して Claude Code 本体の表示を優先する。
PromptHint入力欄の下に表示される薄い色のヒント(? for shortcuts など)。
SessionMode入力欄のフッター右側に表示されるモード名。
UserMessageユーザー側のすべての行。プロンプト、バックグラウンドタスクの通知、他のエージェントからのメッセージを含み、e.props.origin.kind で区別する。e.props.isExpanded(ctrl+o で展開した状態)のときは next(e) を返す。テキストを書き換えても変わるのは表示だけで、モデルが読んだ内容は変わらない。
AssistantMessageアシスタントの回答のブロック1つ。
ToolUseツール呼び出しの行(Bash(ls -la) とその結果)。書き換えても変わるのは表示だけである。
ToolResult単独で表示されるツールの行の下にある結果のブロック。
ToolGroup複数のツール呼び出しをまとめて件数を表示する行(Read 3 files…)。
ToolProgress実行中のツールの下に表示される進捗の行。
CommandOutputスラッシュコマンドの出力の行。{ component: 'CommandOutput', props: { command: 'mine' } } のように指定すると、自分のコマンドの出力を独自のツリーで描画できる。
Spinnerターンの実行中に表示されるアニメーションの行(Sauteing... (12s, 300 tokens))。
TurnDurationターンの終了時に表示される行(Baked for 3s)。
InfoNoticeロゴの下に表示される薄い色の状態表示。
AskUserQuestionAskUserQuestion ツールのダイアログ。

許可ダイアログは、ユーザーの回答がそのまま操作の許可になるため、Claude Code 本体しか描画できません。情報を追加したい場合は $.ui.notice を使います。

要素と表示環境

要素はグローバル変数としては提供されません。const { Box, Text, Button } = $.ui.resolve(e) のように、表示環境の要素表から取り出して使います。e.surface で条件分岐すると、要素表の型も絞り込まれます。

要素terminaldesktopvscodemobile
Box Text Button Link Code Markdown○○○○
Input Select○○○×
Svg×○○○
Client○○××
Raster Image○×××

表示環境が対応していない要素や、受け付けない prop を含むツリーは描画されず、代わりに Claude Code 本来の描画が使われます。このときデバッグログに ui.render (<Component>): a hook returned a tree that does not validate と理由が出力されます。

要素の props

要素主な props
BoxInk の Box の props のうち、許可されたもの: flexDirection flexGrow flexShrink flexWrap alignItems alignSelf justifyContent gap columnGap rowGap width height minWidth minHeight margin* padding* borderStyle borderColor borderDimColor backgroundColor overflow display。さらに、key(ホバー判定の範囲になる)、hover(ポインタを重ねたときに上書きするスタイル)、position: "absolute" と top left right bottom(セル単位で指定し、周囲の配置を変えずに上に重ねて描画する)を指定できる。
Textcolor backgroundColor dimColor bold italic underline strikethrough inverse wrap(wrap / end / middle / truncate*)、hover。色にテーマのキー(success error warning claude diffAdded など)を指定すると、ユーザーのテーマに合わせた色になる。
Buttonkey(既定はラベル)、label、onPress、hotkey(数字1文字か英小文字1文字)、action(Claude Code 本体のキーバインド名。例: app:cycleDiffBase)、plain、dimColor、variant(primary / secondary)、role: "dismiss"、autoFocus、hover。ターミナルでは [ label ] の形で表示される。
Inputkey、label、placeholder、value、submitLabel、autoFocus、onInput(入力が変わるたびに呼ばれる)、onSubmit(Enter で呼ばれる)。
Selectkey、label、options(1つ以上。値は重複不可)、value、autoFocus、onSelect。
Linkhref(2048 文字まで)、label(子要素がないときに表示される)。
Codesource、language、path(拡張子から言語を推定する)、startLine(行番号を表示する)、format: "diff"(unified diff として色分けする)、wrap。
Markdowntext、dimColor、key、onLinkPress(リンクのクリックを ui.press で受け取り、e.link に href が入る)、pressableLinks。https: / http: / file: 以外のリンクは通常のテキストとして表示される。
Svgsource(131,072 文字まで)、alt(必須)、width、height、isInteractive(スクリプトを無効にした sandbox 内で描画し、ホバー、SMIL アニメーション、title のツールチップを有効にする)。
Rasterkey、columns(1〜512)、rows(1〜256)、cells(行優先で、セルごとに [codePoint, fg, bg] を little-endian の u32 で並べ、base64 にしたもの)。スパークラインやヒートマップは、セルごとに Box を並べずにこの要素1つで描画する。
Imagekey、source({ png } / { rgba, width, height } / { file, format } / { shm, format, width, height })、columns、rows(1〜255)、alt(必須)。kitty graphics protocol に対応した端末(kitty、Ghostty)では画像が表示され、それ以外の端末では alt が表示される。
Clientkey、module(表示環境側で動くモジュールのパス。文字列リテラルで書く必要がある)、props(JSON)、width、height、flexGrow。モジュールが surface.post(data) を呼ぶと、ui.message イベントが発生する。

Box の borderStyle に指定できるのは single double round bold singleDouble doubleSingle classic arrow dashed quote です。それ以外の値を指定すると、ツリーは拒否されずに枠なしで描画されます。

操作とフォーカス

  • Button、Input、Select のハンドラはプラグイン側で実行され、押下や入力は ui.press、ui.input、ui.select イベントとして届く。ハンドラの中では、描画時に変数に取り込んだ値ではなく、その時点の値を読んでから書き込む(update を使う)。
  • キー入力はフォーカスのある要素にだけ届き、Esc を押すと入力欄にフォーカスが戻る。帯は ctrl+x tab またはクリックでキー入力を受け付けるようになる。
  • hotkey は、その描画箇所がキー入力を受け付けている間だけ有効になる。action を指定した Button は、表示されている間であれば、入力欄にフォーカスがあってもユーザーのキー割り当てで押せる。
  • focus、closeOnEscape、holdToasts を指定して開いた Pane はダイアログとして動作する(Tab と矢印キーでボタン間を移動し、Esc で閉じ、表示中はトーストを保留する)。開いたままにしておく Pane には holdToasts を指定しない。
  • rows を指定すると、Pane は入力欄の上に、内容に必要な高さで開く。
// 帯の例: 直前のターンの所要時間とツール呼び出し回数を入力欄の上に表示する
on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
  const turn = await read($, last)
  if (e.props.hasSurvey || turn === null) return next(e)
  const { Box, Button, Text } = $.ui.resolve(e)
  return (
    <Box>
      <Text dimColor>前のターン: {turn.seconds}秒 / ツール {turn.tools} 回 </Text>
      <Button key="hide" label="非表示" onPress={() => update($, isHidden, () => true)} />
    </Box>
  )
})

絞り込みの条件に一致する行がありません。