ターミナルレンダリング
ターミナル画面は cols × rows のセルグリッド です。正確さは VT から得られ、「見た目」はメトリクスとペイントから生まれます。同じエミュレーターでも見た目は異なります。
4つのレイヤー
| レイヤー | 役割 | 問題がある場合 |
|---|---|---|
| 1. VT | バイト → セルグリッド(カーソル、モード、スクロールバック、代替画面) | 文字化け / サイズ不正 / カーソル停止 |
| 2. メトリクス | フォントからのセル幅/高さ。PTYを cols/rows および セル px でリサイズ | 列のぐらつき、エッジのぼやけ |
| 3. ペイント | セルの描画:背景領域、グリフ、スプライト、コントラスト | 隙間、細い文字、TUI境界線の崩れ |
| 4. セッション(オプション) | 永続化/リモート:デーモンがVTを所有、クライアントがフレームをミラーリング | ウィンドウ終了とともにスクロールバックが消失 |
一言で言えば: VTで正確さを確保。フォント+メトリクス+セル単位の配置+幾何学的なボックス描画で見た目を制御。ダーティロウキャッシュで高速化。
1. VT(グリッドは正確であること)
- エージェントTUIには、手作りのANSIサブセットではなく、実際のエミュレーター(例:libghostty-vt)を推奨。
- 各セル:テキスト(書記素)、前景色/背景色、太字/斜体/淡色、下線/打消し線、幅フラグ。
- 対応:ワイド文字 / 絵文字の幅、代替画面、同期出力(モード2026)、次のPTY読み取り前に書き戻されるクエリ応答。
- UIはグリッドのスナップショットを読み取る。ペイントパスでバイトストリームを再パースしてはならない。
2. メトリクス
- 列幅 ≈
'M'のアドバンス(オプションでCellWidth乗数)。 - 行高 ≈ アセント + ディセント、それを × 約1.15–1.2倍し、デバイスピクセルにスナップ。
- 幅はフォントアドバンスに維持し、ボックス/背景のエッジをピクセルにスナップして隣接セル間にシームができないようにする(グリフがぐらつくようなセル幅の丸めは行わない)。
- リサイズ時は、
cols、rows、cell_width_px、cell_height_pxをPTYに送信する(プログラムはマウス/画像のためにピクセルサイズを使用する)。
3. ペイント(見た目が決まる場所)
既存のターミナルを改善する際の優先順位:
- 固定の等幅フォント+Nerdシンボルを同梱する(例:JetBrains Mono + Symbols Nerd Font)。システム標準の等幅フォント / Powerlineアイコンの欠如はエージェントTUIを台無しにする。
- ボックス / ブロック / 点字 / Powerlineはフォントグリフではなく幾何学的に描画する — フォントは行間に隙間を残す。全画面の境界線は結合しなければならない。
- グリッド上にグリフを配置する:同じスタイルの連続は強制幅シェーピング、またはセルごとの中央揃え。狭いセルにあふれるフォールバックグリフ(絵文字)は縮小する。
- 不足しているPUA / Nerdアイコンをシンボルフォントにルーティングする(またはセル内に輪郭適合させる)。
- オプションのポリッシュ:
minimum-contrast(スプライト / 意図的なグラフィックスをスキップ)、macOSフォント太字化、マルチスタイル下線、パディングカラー拡張。
ペイントパイプライン(フレームごと)
- ペインをターミナル背景色で塗りつぶす。
- 原点 = パディング + 垂直方向の余白/2。原点をデバイスピクセルにスナップする。
- 行の
Arc/IDが変更された場合(またはフォント/セルサイズが変更された場合 / 全体がダーティな場合)のみ、行の操作を再構築する。 - すべての背景スパン、次にスプライト、次にテキストラン、最後にカーソル / IME / オーバーレイをペイントする。
行操作
- 連続する同じ背景色のセルを1つのクワッドにマージする。
- 1つのスプライトセル → 幾何学的な描画(herdr / Ghosttyのスプライトアプローチを参照)。
- それ以外は同じスタイルのテキストランをシェーピングする。狭いセルではアドバンスを
cell_wに強制し、列を固定する。
VTのせいにしないこと
境界線に隙間がある、アイコンが豆腐になる、グリッドコンテンツが正しいのにテキストが細く/不均一に見える場合 — ペイント/フォントを修正し、エミュレーターを交換してはならない。
4. セッショントポロジー
インプロセス(シンプル): PTY → VT → スナップショット → UIペイント。
デーモン+クライアント(keroスタイル): デーモンのヘッドレスVT + PTY。Mirror が現在の画面のVTフレームをエンコード。クライアントのVTが Conn を使用してセルごとにペイント。スクロールバックはウィンドウクローズ / リモート接続後も存続する。
参考実装
| プロジェクト | スタック | 備考 |
|---|---|---|
| Ghostty | Zig + libghostty-vt | VT + スプライトセマンティクスの源泉 |
| herdr-app | GPUI + libghostty-vt | セルペイント、スプライト、コントラスト、バンドルフォント |
| kero | MyGo + libghostty-vt | ミラーフレーム、Ghosttyテーマ、GPU UI |
| kumu | GPUI-kit + libghostty-vt | 同様のペイントアイデア:スプライト + JetBrains + Nerd |
チェックリスト
ターミナルレンダリングを追加または修正する場合:
- VTがグリッドを所有。UIはスナップショットのみをペイントする
- セルメトリクスは等幅フェイスから取得。リサイズにはピクセルセルサイズを含める
- バンドル(または保証された)等幅フォント + Nerdシンボル
- ボックス / Powerline / 点字は図形として描画
- ダーティロウペイントキャッシュは行IDをキーとする
- カーソル、IME、選択はセル座標に配置される
- 既知のTUI(htop、Claude Code、statusline)をGhosttyと比較する
補足詳細
- スプライト / ジオメトリ範囲とコントラストルール: paint.md