# 通知設計書 15-キーテーブル通知

## 概要

本ドキュメントは、Ghosttyターミナルエミュレーターにおける「キーテーブル通知」機能の設計を記載するものである。キーテーブルがアクティブ化または非アクティブ化された際にUIに通知を送信し、現在アクティブなキーテーブル名を視覚的に示す機能について詳述する。

### 本通知の処理概要

**業務上の目的・背景**：キーテーブル通知は、モーダルなキーバインディングセット（キーテーブル）の状態をユーザーに伝達するために実装されている。キーテーブルは名前付きのキーバインディングセットであり、アクティブ化されると通常のキーバインディングを一時的にオーバーライドする。例えば、「resize」モードや「copy」モードなど、特定のタスクに最適化されたキーバインディングセットを切り替えて使用できる。ユーザーが現在どのキーテーブルがアクティブであるかを認識することで、期待通りのキー操作を行えるようになる。

**通知の送信タイミング**：キーテーブルの状態が変化したタイミングでトリガーされる。具体的には以下の3つのケースがある：
1. activate：キーテーブルがアクティブ化された時
2. deactivate：最上位のキーテーブルが非アクティブ化された時
3. deactivate_all：全てのキーテーブルが非アクティブ化された時

**通知の受信者**：当該ターミナルサーフェスを管理するアプリケーションランタイム（macOS/GTK等）が受信者となる。受信したランタイムは、キーテーブルオーバーレイの表示・更新・非表示を行い、ユーザーに現在のキーテーブル状態を視覚的にフィードバックする。

**通知内容の概要**：通知にはKeyTable unionが含まれ、「activate」（キーテーブル名付き）、「deactivate」、または「deactivate_all」の状態を示す。activateの場合は、アクティブ化されたキーテーブル名が含まれる。

**期待されるアクション**：ユーザーはオーバーレイに表示されたキーテーブル名を確認し、現在のモードに応じたキー操作を行う。deactivate_key_tableアクションまたはキーテーブル定義のトリガーにより、キーテーブルを非アクティブ化できる。onceフラグが設定されたキーテーブルは、最初のキー入力で自動的に非アクティブ化される。

## 通知種別

アプリ内通知（UIオーバーレイ表示）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（バインディングアクション実行内で直接呼び出し） |
| 優先度 | 高（ユーザー入力への即時フィードバック） |
| リトライ | 無し |

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

通知は、キーテーブル操作が行われたサーフェスに対応するアプリケーションランタイムに送信される。performAction関数を通じてkey_tableアクションとして処理される。

## 通知テンプレート

### オーバーレイ表示の場合

| 項目 | 内容 |
|-----|------|
| 種別 | キーテーブルオーバーレイ |
| 状態 | activate / deactivate / deactivate_all |
| 表示内容 | アクティブなキーテーブル名のスタック |

### 本文テンプレート

```
[単一キーテーブルがアクティブの場合]
resize

[複数キーテーブルがスタックされている場合]
resize > navigate

[deactivate/deactivate_allの場合]
（オーバーレイ非表示、またはスタック更新）
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 添付ファイルはサポートしない |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| tag | 操作種別（activate/deactivate/deactivate_all） | KeyTable.Tag enum | Yes |
| name | キーテーブル名 | []const u8（activateの場合） | activateの場合Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| バインディング | activate_key_tableアクション | キーテーブルが存在する場合 | キーテーブルアクティブ化通知 |
| バインディング | activate_key_table_onceアクション | キーテーブルが存在する場合 | 一回限りのキーテーブルアクティブ化通知 |
| バインディング | deactivate_key_tableアクション | キーテーブルがアクティブな場合 | キーテーブル非アクティブ化通知 |
| バインディング | deactivate_all_key_tablesアクション | キーテーブルがアクティブな場合 | 全キーテーブル非アクティブ化通知 |
| 自動 | onceフラグ付きキーテーブルでのキー入力 | 最上位のonceテーブルでキーマッチ | 自動非アクティブ化通知 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| キーテーブルが存在しない | 指定された名前のキーテーブルが設定に存在しない場合 |
| 最大スタック深度到達 | table_stackが8に達している場合、activate通知は発行されない |
| キーテーブル非アクティブ時のdeactivate | table_stackが空の場合はdeactivate通知を発行しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[バインディングアクション実行] --> B{アクション種別}
    B -->|activate_key_table| C{キーテーブル存在?}
    B -->|deactivate_key_table| D{スタック非空?}
    B -->|deactivate_all_key_tables| E[全テーブル非アクティブ化]
    C -->|Yes| F{スタック深度 < 8?}
    C -->|No| G[警告ログ出力]
    F -->|Yes| H[テーブルをスタックに追加]
    F -->|No| I[警告ログ出力]
    H --> J[key_table activate通知]
    D -->|Yes| K[最上位テーブルをpop]
    D -->|No| L[処理終了]
    K --> M[key_table deactivate通知]
    E --> N[スタックをクリア]
    N --> O[key_table deactivate_all通知]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| config.keybind.tables | キーテーブル定義の参照 | 設定から読み込み |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| keyboard.table_stack | push/pop | アクティブなキーテーブルのスタック管理 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| キーテーブル不存在 | 指定名のキーテーブルがconfigに存在しない | 情報ログ出力、処理終了 |
| スタックオーバーフロー | table_stack.len >= max_active_key_tables (8) | 情報ログ出力、activate無視 |
| performAction失敗 | アプリランタイムエラー | 警告ログ出力、処理続行 |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（アクションベース） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（アクション実行時に即座に処理）

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

- キーテーブル名は設定ファイルで定義されるため、悪意のある名前が含まれる可能性は低い
- 画面共有やスクリーンキャプチャ時にオーバーレイ内容が表示される

## 備考

- 最大同時アクティブキーテーブル数は8（max_active_key_tables定数）
- キーテーブルはスタック構造で管理され、後入れ先出し（LIFO）で非アクティブ化される
- onceフラグ付きキーテーブルは、最初のキーバインディングマッチ後に自動的にpopされる
- KeyTable unionはC ABI互換の構造体（C）を持ち、libghosttyとの相互運用性を確保
- macOSではKeyStateIndicator SwiftUIコンポーネントでオーバーレイを表示
- GTKではKeyStateOverlay Zigクラスでオーバーレイを表示

---

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

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

### 推奨読解順序

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

キーテーブル通知のデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | action.zig | `src/apprt/action.zig` | KeyTable union定義（行788-830）を確認 |
| 1-2 | Surface.zig | `src/Surface.zig` | table_stackの定義（行272-278）を確認 |

**読解のコツ**: KeyTable unionは`activate`、`deactivate`、`deactivate_all`の3つのバリアントを持ち、activateのみが名前情報を含む。

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

キーテーブル通知が発生する処理の入口を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Surface.zig | `src/Surface.zig` | performBindingAction関数内のキーテーブル処理（行5785-5865） |

**主要処理フロー**:
1. **行5785-5833**: activate_key_table/activate_key_table_onceの処理
2. **行5835-5859**: deactivate_key_tableの処理
3. **行5862-5864**: deactivate_all_key_tablesの処理

#### Step 3: キーテーブル解決を理解する

キー入力時のテーブルスタック参照を追跡する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Surface.zig | `src/Surface.zig` | onceフラグ処理（行2859-2863） |

**主要処理フロー**:
- **行2859-2863**: onceフラグ付きテーブルの自動deactivate

#### Step 4: 全テーブル非アクティブ化を理解する

deactivateAllKeyTables関数の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Surface.zig | `src/Surface.zig` | deactivateAllKeyTables関数（行3032-3040付近） |

**主要処理フロー**:
- **行3032-3038**: key_table deactivate_all通知の送信

#### Step 5: UI実装を理解する

各プラットフォームでのオーバーレイ表示を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | SurfaceView.swift | `macos/Sources/Ghostty/Surface View/SurfaceView.swift` | KeyStateIndicator（行132-135） |
| 5-2 | key_state_overlay.zig | `src/apprt/gtk/class/key_state_overlay.zig` | tablesプロパティ（行30-48） |

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

```
バインディングアクション
    |
    +-- Surface.performBindingAction() [Surface.zig]
           |
           +-- activate_key_table
           |      |
           |      +-- config.keybind.tables.getPtr() (テーブル検索)
           |      +-- table_stack.append() (スタック追加)
           |      +-- performAction(.key_table, .activate)
           |
           +-- deactivate_key_table
           |      |
           |      +-- table_stack.pop() (スタックから削除)
           |      +-- performAction(.key_table, .deactivate)
           |
           +-- deactivate_all_key_tables
                  |
                  +-- deactivateAllKeyTables()
                         |
                         +-- table_stack.clear() (スタッククリア)
                         +-- performAction(.key_table, .deactivate_all)
```

### データフロー図

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

バインディング      --->  アクション判定     --->   操作種別
アクション                                         (activate/deactivate)
                              |
                              v
                         テーブル検索
                         (config.keybind.tables)
                              |
                              v
                         スタック操作
                         (keyboard.table_stack)
                              |
                              v
                         performAction
                         (key_table)
                              |
                              +--> activate通知     --->   オーバーレイ表示
                              |                           (テーブル名追加)
                              |
                              +--> deactivate通知   --->   オーバーレイ更新
                              |                           (最上位テーブル削除)
                              |
                              +--> deactivate_all通知 --> オーバーレイ非表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| action.zig | `src/apprt/action.zig` | ソース | KeyTable型とkey_tableアクション定義 |
| Surface.zig | `src/Surface.zig` | ソース | キーテーブル管理とスタック操作 |
| Config.zig | `src/config/Config.zig` | ソース | キーテーブル設定の定義 |
| Binding.zig | `src/input/Binding.zig` | ソース | キーバインディング定義とテーブル参照 |
| SurfaceView.swift | `macos/Sources/Ghostty/Surface View/SurfaceView.swift` | ソース | macOS UIとKeyStateIndicator |
| key_state_overlay.zig | `src/apprt/gtk/class/key_state_overlay.zig` | ソース | GTKオーバーレイ実装 |
