# 通知設計書 54-TUI Toast

## 概要

本ドキュメントは、OpenCodeのTUI（Terminal User Interface）環境におけるトースト通知機能の設計について記載する。

### 本通知の処理概要

本通知は、OpenCodeのターミナルUI（CLI版）において、ユーザーに対して一時的なフィードバックメッセージを表示するトースト通知機能である。info、success、warning、errorの4種類のバリアントを持ち、操作結果やシステム状態をユーザーに通知する。

**業務上の目的・背景**：TUI環境ではデスクトップアプリケーションのようなネイティブダイアログやポップアップを使用できないため、ターミナル内で視覚的にフィードバックを提供する手段としてトースト通知が必要である。セッション操作、クリップボードコピー、エラー発生時など、様々な場面でユーザーに即時的なフィードバックを提供し、操作の成功/失敗を明確に伝える。

**通知の送信タイミング**：以下のような様々なイベントで送信される：
- セッション操作（共有、共有解除、フォーク失敗等）
- クリップボード操作（コピー成功/失敗）
- ファイル操作（エクスポート成功/失敗）
- システムイベント（アップデート通知、MCP認証要求等）
- エラー発生時（セッションエラー、操作失敗等）

**通知の受信者**：OpenCode TUIを使用中のユーザー本人。トーストはターミナルウィンドウの右上隅に表示される。

**通知内容の概要**：トーストには任意のタイトル（オプション）とメッセージ（必須）が表示され、variantに応じてテーマカラーが変わる（info=青/シアン、success=緑、warning=黄、error=赤）。指定した期間（デフォルト5000ms）後に自動的に消える。

**期待されるアクション**：ユーザーはトースト通知を視認し、操作結果やシステム状態を把握する。エラーの場合は適切な対処（再試行、設定変更等）を行う。トーストは自動的に消えるため、明示的なアクションは不要。

## 通知種別

TUI Toast（ターミナル内トースト通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（即時表示、自動消去） |
| 優先度 | 中（既存のトーストは新しいトーストで置き換え） |
| リトライ | なし |

### 送信先決定ロジック

- 現在アクティブなTUIセッションに対して表示
- REST API（`/tui/show-toast`）経由でも送信可能
- Busイベント（`TuiEvent.ToastShow`）経由でも送信可能

## 通知テンプレート

### トースト通知の構造

| 項目 | 内容 |
|-----|------|
| 表示位置 | 画面右上（top: 2, right: 2） |
| 最大幅 | 60文字またはターミナル幅-6のいずれか小さい方 |
| パディング | 左右: 2, 上下: 1 |
| ボーダー | 左右にvariantカラーのボーダー |
| 背景色 | テーマのbackgroundPanel |

### 本文テンプレート

```
[タイトル（太字、オプション）]
メッセージ（テキスト折り返し有効）
```

### バリアント一覧

| バリアント | 用途 | ボーダー色 |
|-----------|------|-----------|
| info | 情報通知 | テーマのinfo色（通常シアン/青） |
| success | 成功通知 | テーマのsuccess色（通常緑） |
| warning | 警告通知 | テーマのwarning色（通常黄） |
| error | エラー通知 | テーマのerror色（通常赤） |

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| title | トーストのタイトル | 呼び出し元から指定 | No |
| message | トーストのメッセージ | 呼び出し元から指定 | Yes |
| variant | トーストの種別（info/success/warning/error） | 呼び出し元から指定 | Yes |
| duration | 表示期間（ミリ秒） | 呼び出し元から指定、デフォルト5000 | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | クリップボードコピー | コピー成功 | 「Copied to clipboard」を表示 |
| 画面操作 | セッション共有 | 共有成功 | 「Share URL copied to clipboard!」を表示 |
| 画面操作 | セッション共有解除 | 解除成功 | 「Session unshared successfully」を表示 |
| システムイベント | セッションエラー | エラー発生 | エラーメッセージを表示 |
| システムイベント | アップデート利用可能 | 新バージョン検出 | 「Update Available」を表示 |
| システムイベント | MCP認証要求 | 認証必要 | 「MCP Authentication Required」を表示 |
| API経由 | `/tui/show-toast`呼び出し | リクエスト受信 | 指定されたメッセージを表示 |
| Busイベント | `TuiEvent.ToastShow`パブリッシュ | イベント受信 | 指定されたメッセージを表示 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| TUI非アクティブ | TUIが起動していない場合は表示不可 |
| メッセージ未指定 | message が空の場合はバリデーションエラー |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[トリガー発生] --> B{発生元}
    B -->|直接呼び出し| C[toast.show呼び出し]
    B -->|API| D[/tui/show-toast エンドポイント]
    B -->|Busイベント| E[TuiEvent.ToastShow]
    D --> F[Bus.publish]
    E --> F
    F --> G[sdk.event.onでリッスン]
    G --> C
    C --> H[ToastOptions をパース]
    H --> I[currentToast を更新]
    I --> J[タイマー設定]
    J --> K[duration後に currentToast = null]
    I --> L[Toastコンポーネント描画]
    K --> M[トースト非表示]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（本機能はデータベースを使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| バリデーションエラー | message未指定、variant不正 | Zodによるパースエラー（表示せず） |
| タイムアウトクリア失敗 | タイマー処理の問題 | 新しいトーストで上書き |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 同時表示数 | 1件（新しいトーストが既存を置き換え） |
| 1分あたり上限 | なし |

### 配信時間帯

制限なし（TUIが起動している限り常時表示可能）

## セキュリティ考慮事項

- トーストの内容はZodスキーマでバリデーションされる
- HTML/スクリプトインジェクションのリスクなし（プレーンテキストのみ）
- API経由のトースト表示は同一プロセス内からのみ可能（外部からのアクセス不可）

## 備考

- 本機能はCLI版OpenCode（TUI）専用であり、Web版やデスクトップ版では別の通知メカニズムを使用
- トーストは`@opentui/solid`フレームワークのSolid.jsコンポーネントとして実装
- テーマに応じてボーダー色が動的に変化する
- `toast.error()`ヘルパーメソッドでErrorオブジェクトを直接渡すことも可能

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

トースト通知のデータ構造（ToastOptions）を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.ts | `packages/opencode/src/cli/cmd/tui/event.ts` | `TuiEvent.ToastShow`のZodスキーマ定義。title（オプション）、message（必須）、variant（enum）、duration（デフォルト5000ms）の構造を理解 |

**読解のコツ**: `BusEvent.define`はイベントタイプと、そのプロパティを定義するZodスキーマを受け取る。`z.enum(["info", "success", "warning", "error"])`でvariantの選択肢が固定されている。

#### Step 2: エントリーポイントを理解する

トースト表示の起点となるコンテキストとAPIを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | toast.tsx | `packages/opencode/src/cli/cmd/tui/ui/toast.tsx` | `ToastProvider`と`useToast`フック。`toast.show()`メソッドの実装 |
| 2-2 | app.tsx | `packages/opencode/src/cli/cmd/tui/app.tsx` | TUIアプリのルートコンポーネント。`ToastProvider`のマウント位置と`TuiEvent.ToastShow`のリスナー設定 |

**主要処理フロー（toast.tsx）**:
1. **50-53行目**: `init`関数で`createStore`を使用してcurrentToastの状態を管理
2. **55行目**: タイムアウトハンドルを保持
3. **57-66行目**: `toast.show()`メソッドでオプションをパース、currentToastを更新、タイマー設定
4. **67-77行目**: `toast.error()`ヘルパーでErrorオブジェクトを処理

**主要処理フロー（app.tsx）**:
1. **139行目**: `<ToastProvider>`がアプリ全体をラップ
2. **208行目**: `const toast = useToast()`でトーストコンテキストを取得
3. **675-682行目**: `sdk.event.on(TuiEvent.ToastShow.type, ...)`でBusイベントをリッスン

#### Step 3: UIコンポーネントを理解する

トーストの視覚的なレンダリング処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | toast.tsx | `packages/opencode/src/cli/cmd/tui/ui/toast.tsx` | `Toast`コンポーネント（12-48行目）。レイアウト、スタイリング、条件付きレンダリング |

**主要処理フロー**:
- **17-18行目**: `<Show when={toast.currentToast}>`で条件付きレンダリング
- **20-34行目**: boxコンポーネントで位置・サイズ・スタイル設定
- **32行目**: `borderColor={theme[current().variant]}`でvariantに応じた色を設定
- **36-39行目**: タイトルの条件付き表示（太字）
- **41-43行目**: メッセージの表示（ワードラップ有効）

#### Step 4: REST APIを理解する

外部からトーストを表示するAPIエンドポイントを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | tui.ts | `packages/opencode/src/server/routes/tui.ts` | `/tui/show-toast`エンドポイント（290-312行目）。バリデーションとBus.publishの流れ |

**主要処理フロー**:
- **290-296行目**: `describeRoute`でOpenAPIドキュメント定義
- **307行目**: `validator("json", TuiEvent.ToastShow.properties)`でリクエストボディをバリデーション
- **309行目**: `Bus.publish(TuiEvent.ToastShow, c.req.valid("json"))`でBusイベントを発行

### プログラム呼び出し階層図

```
[直接呼び出し]
toast.show({ title, message, variant, duration })
    │
    └─ setStore("currentToast", ...)
           │
           └─ Toast コンポーネント再レンダリング

[API経由]
POST /tui/show-toast
    │
    └─ validator("json", TuiEvent.ToastShow.properties)
           │
           └─ Bus.publish(TuiEvent.ToastShow, ...)
                  │
                  └─ sdk.event.on(TuiEvent.ToastShow.type, ...)
                         │
                         └─ toast.show({ ... })
                                │
                                └─ Toast コンポーネント再レンダリング

[Bus経由]
Bus.publish(TuiEvent.ToastShow, { ... })
    │
    └─ GlobalBus.emit("event", ...)
           │
           └─ sdk.event.on(TuiEvent.ToastShow.type, ...)
                  │
                  └─ toast.show({ ... })
```

### データフロー図

```
[入力]                     [処理]                           [出力]

ToastOptions     ───▶  TuiEvent.ToastShow.properties.parse()
(title, message,              │
 variant, duration)           ▼
                       setStore("currentToast", ...)
                              │
                              ▼
                       setTimeout(duration)
                              │
                              ├─ [表示期間中]
                              │         │
                              │         ▼
useTheme() ───────────▶  Toastコンポーネント  ───▶  ターミナル右上に
                       (variant → theme[variant])       トースト表示
                              │
                              ├─ [timeout後]
                              │         │
                              │         ▼
                              │  setStore("currentToast", null)
                              │         │
                              │         ▼
                              └─ トースト非表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.ts | `packages/opencode/src/cli/cmd/tui/event.ts` | ソース | TuiEvent.ToastShowイベント定義 |
| toast.tsx | `packages/opencode/src/cli/cmd/tui/ui/toast.tsx` | ソース | Toastコンポーネントとコンテキスト |
| app.tsx | `packages/opencode/src/cli/cmd/tui/app.tsx` | ソース | TUIルートアプリ、イベントリスナー設定 |
| tui.ts | `packages/opencode/src/server/routes/tui.ts` | ソース | REST API エンドポイント |
| bus-event.ts | `packages/opencode/src/bus/bus-event.ts` | ソース | BusEvent.define関数定義 |
| index.ts (bus) | `packages/opencode/src/bus/index.ts` | ソース | Bus.publish/subscribe関数 |
| theme.tsx | `packages/opencode/src/cli/cmd/tui/context/theme.tsx` | ソース | テーマ定義（info/success/warning/error色） |
| session/index.tsx | `packages/opencode/src/cli/cmd/tui/routes/session/index.tsx` | ソース | toast.show呼び出し例多数 |
| index.ts (mcp) | `packages/opencode/src/mcp/index.ts` | ソース | MCP認証時のtoast通知例 |
