yoi/tickets/native-gui-mvp.md
2026-04-15 10:35:15 +09:00

92 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ネイティブ GUI クライアント MVP
## 背景
TUI は ratatui の `insert_before` を使った append-only モデルで動いており、ツール呼び出しのライブ更新(引数のストリーミングプレビュー、状態遷移の視覚化)のような「既に描いた領域の書き換え」が本質的に苦手である。`tickets/tui-tool-call-ui.md` で妥協的な拡張策inline viewport を可変化してアクティブフレームを保持は立てたが、terminal のスクロールバックモデルと live-updating な LLM UX の相性は根本的に悪く、どう組んでも制約と妥協がついて回る。
一方、GUI 側ならそもそも**全領域が毎フレーム再描画される retained-mode**なので、ツールフレームの live 更新・折り畳み・インタラクティブな介入といった操作が自然に書ける。TUI は軽量・ssh 親和のクライアントとして残し、**リッチな対話は GUI クライアントに切り出す**方針を取る。
## 方針
### アーキテクチャ
- **プロセス分離 + ソケット接続を維持**。GUI は独立バイナリとして動き、Pod はこれまで通り別プロセス。
- **通信は既存の `protocol` クレート**`Method` / `Event`をそのまま使う。GUI と TUI は同じ protocol を喋る。
- **Pod の spawn は GUI から直接行う**。manifest を選択 → `pod` バイナリを subprocess として起動 → その socket に接続、という流れ。daemon 層は導入しない。
- **MVP は単一 Pod**。複数 Pod の並列管理は本チケットの範囲外とし、GUI 側の protocol 抽象が固まってから別チケットで拡張する。
### GUI フレームワーク: GPUI
- Rust ネイティブ + async-aware で、`protocol` クレートを直接リンクできる。
- GPU 加速の retained-mode で、ツールフレームの live 更新が素直に書ける。
- virtualized list / text input / scrollable 履歴など、LLM チャット UI に必要な部品が一通り揃っている。
- 既知のリスク: プラットフォーム成熟度macOS > Linux > Windows、独立ライブラリとしての新しさ、Markdown レンダラ等のウィジェット生態系が Tauri/Iced より薄い。本 MVP は Linux only なのでプラットフォーム面のリスクは受容できる。
### プラットフォーム
- **Linux only**。macOS / Windows は MVP の範囲外。GPUI の Linux サポートが動作する前提で組む。
## MVP スコープ
### 含む
1. **Pod の spawn と接続**
- manifest ファイルを選ぶ UIファイルダイアログ or CLI 引数)
- `pod` バイナリを subprocess として起動し、その socket に接続
- 接続確立後は TUI と同じ protocol で対話
2. **現 TUI の機能相当**
- 入力フィールド + 送信
- ストリーミングテキストの表示(`TextDelta` → 追記)
- ターンヘッダ / ターン統計 / ステータスバー相当の情報表示
- セッション再開時の履歴復元(`Event::History`
- エラー表示
- cancel / graceful shutdown
3. **ツール呼び出しのフレーム更新 UI**
- `tickets/tui-tool-call-ui.md` で定義したライフサイクルPending → Streaming → Executing → Done/Errorをそのまま GPUI 側で実装
- TUI と違い inline viewport の制約が無いので、履歴スクロール内でも自由に再描画できる
- `ToolCallArgsDelta` を毎フレーム反映してライブプレビュー
- 完了済みフレームは履歴内に状態が焼き込まれた形で残る
4. **Pod の明示的 shutdown**
- GUI から shutdown 操作を行い、Pod subprocess を graceful に終了させる
- shutdown 完了後は GUI 自体も正常終了する
### 含まない
- 複数 Pod の並列表示・切替(別チケット)
- daemon 層の導入
- macOS / Windows サポート
- ツール結果のリッチレンダリングMarkdown 整形、シンタックスハイライト、diff 表示等)
- ツール実行への対話的介入permission ask/reply の UI 実装は `tickets/permission-extension-point.md` 側)
- protocol の拡張compact 通知・R-R パターン等は `tickets/protocol-design.md` 側で進行し、GUI は完了次第追従する)
- GUI 内での manifest 編集
- テーマカスタマイズ、キーバインドカスタマイズ
## 設計で決めること
- **GPUI のイベントループと Tokio ランタイムの統合**: GPUI 側の executor に Pod からの socket イベントをどう流し込むか
- **socket client の置き場所**: 現 `crates/tui/src/client.rs` と同等のクライアントを別 crate に切り出して共有するか、GUI crate 内に閉じて持つか
- **Pod subprocess のライフサイクル管理**: GUI プロセスが落ちたときの Pod 側の後処理orphan prevention、Pod が異常終了したときの GUI 側の復帰 UX
- **ツールフレームのデータモデル**: `OutputItem` 列に載せるか別コレクションで持つかTUI 側の設計議論と共通部分あり。共有可能なら abstract して流用)
- **履歴スクロールの挙動**: 下端追従chat 流儀)と手動スクロール時の追従停止
- **入力エリアの多行対応**: 単一行でよいか、複数行 + Ctrl+Enter 送信等
## 完了条件
- Linux 上で GUI バイナリが起動し、manifest を指定すると `pod` subprocess を起動して socket 接続する。
- 基本的なチャットuser 入力 → assistant 応答のストリーミング → ターン統計)が TUI と同等に動く。
- ツール呼び出しが 1 フレームとして表示され、`ToolCallArgsDelta` がライブでプレビューされ、完了時に視覚的に状態が変わる。
- セッション再開時に履歴(ツール呼び出し含む)が復元される。
- GUI から shutdown 操作で Pod を正常終了させられ、GUI 自体も正常終了する。
## 新規クレート構成(案)
- `crates/gui/` GPUI を使うバイナリ)
- socket client を共有する場合は `crates/client/` を新設し、TUI と GUI がそれぞれ依存する形に整理する選択肢もある
## 範囲外(再掲・重要なもの)
- 複数 Pod の並列管理・切替。単一 Pod に集中する。
- macOS / Windows。Linux only で完結させる。
- protocol の新規イベント追加。既存 protocol で足りる範囲に留める。
- TUI の廃止。TUI は軽量クライアントとして並行して残る。