# 機能設計書 40-条件付き設定

## 概要

本ドキュメントは、Ghosttyターミナルエミュレータにおける条件付き設定（Conditional Configuration）機能の設計を定義する。

### 本機能の処理概要

条件付き設定機能は、OSのテーマ（ライト/ダーク）やプラットフォームなどの条件に基づいて、設定値を動的に切り替える。システムテーマの変更を検出し、設定を自動的に再適用することで、シームレスなテーマ切り替えを実現する。

**業務上の目的・背景**：ユーザーがOSのテーマ（ダークモード/ライトモード）に連動した設定を行えるようにする。これにより、日中と夜間で異なるテーマを自動切り替えするなど、柔軟な設定が可能になる。

**機能の利用シーン**：
- OSのダークモード/ライトモード切り替え時の自動テーマ変更
- プラットフォーム固有の設定（macOS/Linux固有設定）
- 環境に応じた設定の動的切り替え

**主要な処理内容**：
1. 条件状態（State）の管理（theme: light/dark, os）
2. 条件（Conditional）の評価（key, op, value）
3. 条件変更時の設定再適用（changeConditionalState）
4. テーマファイルの条件付き読み込み

**関連システム・外部連携**：
- OSテーマ検出（macOS: NSApp.effectiveAppearance, GTK: prefer-dark）
- 設定ファイルシステム
- テーマ管理

**権限による制御**：特に権限による制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | すべての画面の外観に影響 |

## 機能種別

設定管理 / 動的構成

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| theme | Theme | No | light:name,dark:name形式 | 構文チェック |

### State構造体

```zig
pub const State = struct {
    /// OSデスクトップ環境のテーマ
    theme: Theme = .light,

    /// ビルドターゲットのOS
    os: std.Target.Os.Tag = builtin.target.os.tag,

    pub const Theme = enum { light, dark };
};
```

### Conditional構造体

```zig
pub const Conditional = struct {
    key: Key,      // theme, os
    op: Op,        // eq（等しい）, ne（等しくない）
    value: []const u8,  // 比較値
};
```

### テーマ設定形式

```
# ライト/ダークモード連動テーマ
theme = light:Rose Pine Dawn,dark:Rose Pine

# 単一テーマ
theme = Dracula
```

### 入力データソース

- OSテーマ状態（システムから取得）
- 設定ファイル
- ランタイムテーマ変更イベント

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Config | struct | 条件適用済み設定 |
| bool | - | 条件マッチ結果 |

### 出力先

- 設定適用後の各サブシステム

## 処理フロー

### 処理シーケンス

```
1. 条件状態初期化
   └─ _conditional_state = State{}
       ├─ theme: OSテーマ検出
       └─ os: ビルドターゲット
2. 設定読み込み
   └─ loadIter
       ├─ 各設定行を解析
       └─ conditional_arg処理
           ├─ 条件評価（State.match）
           └─ マッチ時のみ設定適用
3. テーマ読み込み
   └─ loadTheme
       ├─ _conditional_state.themeで分岐
       │      ├─ .light → theme.light読み込み
       │      └─ .dark → theme.dark読み込み
       └─ replay_stepsを条件付きに変換
4. 条件変更時
   └─ changeConditionalState
       ├─ 差分検出（_conditional_set使用）
       ├─ 変更なし → null返却
       └─ 変更あり → 設定再構築
           ├─ cloneEmpty
           ├─ _conditional_state更新
           └─ replay再実行
```

### フローチャート

```mermaid
flowchart TD
    A[アプリ起動] --> B[OSテーマ検出]
    B --> C[State初期化]
    C --> D[設定ファイル読み込み]
    D --> E{theme設定あり?}
    E -->|Yes| F[light:xxx,dark:xxx解析]
    E -->|No| G[デフォルト設定]
    F --> H{_conditional_state.theme}
    H -->|light| I[lightテーマ読み込み]
    H -->|dark| J[darkテーマ読み込み]
    I --> K[replay_steps条件付き化]
    J --> K
    K --> L[設定適用]
    G --> L
    L --> M[ランタイム監視]
    M --> N{テーマ変更検出?}
    N -->|Yes| O[changeConditionalState]
    N -->|No| M
    O --> P{差分あり?}
    P -->|Yes| Q[設定再構築]
    P -->|No| R[スキップ]
    Q --> S[replay再実行]
    S --> L
    R --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | 条件キー | theme（light/dark）とos（macos/linux等）をサポート | 常時 |
| BR-40-02 | 条件演算子 | eq（等しい）とne（等しくない）をサポート | 常時 |
| BR-40-03 | 差分検出 | _conditional_setで使用キーを追跡、変更検出を最適化 | changeConditionalState時 |
| BR-40-04 | テーマ分岐 | theme = light:xxx,dark:xxx形式でテーマを分岐 | theme設定時 |
| BR-40-05 | replay条件化 | テーマ読み込み時、replay_stepsを条件付きに変換 | loadTheme時 |
| BR-40-06 | null返却 | 条件変更で設定に影響がない場合はnull | changeConditionalState時 |

### 計算ロジック

**条件マッチ評価（State.match）**:
```zig
pub fn match(self: State, cond: Conditional) bool {
    const value = @tagName(@field(self, @tagName(cond.key)));
    return switch (cond.op) {
        .eq => std.mem.eql(u8, value, cond.value),
        .ne => !std.mem.eql(u8, value, cond.value),
    };
}
```

## データベース操作仕様

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InvalidThemeFormat | theme = light:xxx,dark:xxxの形式不正 | エラーログ＋デフォルト使用 |
| - | ThemeNotFound | 指定テーマファイルが見つからない | 警告ログ＋スキップ |

### リトライ仕様

リトライは行わない。エラー時はデフォルト設定にフォールバック。

## トランザクション仕様

トランザクションは使用しない。

## パフォーマンス要件

- 条件評価: O(1)（enum比較）
- 条件変更検出: _conditional_setで使用キーのみチェック
- 設定再構築: replay_steps再実行（ファイル再読み込み不要）

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

- テーマファイルは設定ファイルと同じ権限で読み込み
- 条件値のインジェクション対策（enum限定）

## 備考

- OSテーマの自動追従をサポート
- ランタイムでの設定再読み込みに対応
- light/darkテーマは両方指定が必須

---

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

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

### 推奨読解順序

#### Step 1: 条件付き設定の基本構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | conditional.zig | `src/config/conditional.zig` | 条件付き設定の基本構造 |

**主要処理フロー**:
- **9-37行目**: State構造体（条件状態）
- **16行目**: Theme enum（light/dark）
- **18-36行目**: match関数（条件評価）
- **39-54行目**: Key enum（条件キー、Stateから自動生成）
- **56-74行目**: Conditional構造体（条件定義）
- **62行目**: Op enum（eq/ne演算子）

#### Step 2: Config構造体での条件管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Config.zig | `src/config/Config.zig` | 条件管理フィールド |

**主要処理フロー**:
- **3763-3765行目**: _conditional_state（条件状態保持）
- **3767-3769行目**: _conditional_set（使用キー追跡）
- **4254-4309行目**: changeConditionalState関数

#### Step 3: テーマの条件付き読み込みを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Config.zig | `src/config/Config.zig` | loadTheme関数 |

**主要処理フロー**:
- **4346-4440行目**: loadTheme関数
- **4350-4353行目**: _conditional_state.themeによる分岐
- **4390-4430行目**: replay_stepsの条件付き化

#### Step 4: テーマ設定の解析を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | theme.zig | `src/config/theme.zig` | テーマ解析・読み込み |

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

```
アプリ起動
    │
    ├─ OSテーマ検出
    │      ├─ [macOS] NSApp.effectiveAppearance
    │      └─ [GTK] GSettings prefer-dark
    │
    └─ Config初期化
           │
           ├─ _conditional_state = State{theme: detected}
           │
           └─ loadDefaultFiles
                  │
                  └─ finalize
                         │
                         └─ loadTheme
                                │
                                ├─ _conditional_state.theme判定
                                │      ├─ .light → theme.light
                                │      └─ .dark → theme.dark
                                │
                                └─ replay_steps条件付き化
                                       └─ conditional_arg変換

テーマ変更イベント
    │
    └─ changeConditionalState(new_state)
           │
           ├─ 差分検出
           │      └─ _conditional_set.contains(key)
           │             && old != new
           │
           ├─ [変更なし] → return null
           │
           └─ [変更あり]
                  ├─ cloneEmpty
                  ├─ _conditional_state = new
                  └─ Replay.iterator
                         └─ loadIter（再実行）
```

### データフロー図

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

OSテーマ状態 ────────────▶ State初期化 ──────────────────▶ _conditional_state
(light/dark)                   │
                          theme解析
                               │
                          loadTheme ────────────────────▶ テーマ設定適用
                               │
                          replay条件化
                               │
テーマ変更イベント ──────▶ changeConditionalState ────────▶ 新Config or null
                               │
                          差分検出
                          (_conditional_set)
                               │
                          [変更あり]
                               │
                          replay再実行 ──────────────────▶ 設定再適用
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| conditional.zig | `src/config/conditional.zig` | ソース | 条件付き設定の基本構造 |
| Config.zig | `src/config/Config.zig` | ソース | 条件管理・changeConditionalState |
| theme.zig | `src/config/theme.zig` | ソース | テーマ解析・読み込み |
| App.zig | `src/App.zig` | ソース | colorSchemeEvent（テーマ変更通知） |
| Surface.zig | `src/Surface.zig` | ソース | colorSchemeCallback |
| App.zig | `src/apprt/gtk/App.zig` | ソース | GTKテーマ検出 |
| AppDelegate.swift | `macos/Sources/App/macOS/AppDelegate.swift` | ソース | macOSテーマ検出 |
