# 通知設計書 5-Theme switched

## 概要

本ドキュメントは、OpenCodeアプリケーションにおける「Theme switched（テーマ切り替え）」トースト通知の設計を記述します。アプリケーションのUIテーマが切り替わった際に、ユーザーに対してアプリ内トースト通知を表示する機能です。

### 本通知の処理概要

**業務上の目的・背景**：OpenCodeは複数のUIテーマをサポートしており、ユーザーはコマンドパレットやキーボードショートカットでテーマを切り替えることができます。テーマ変更はUI全体の見た目に影響するため、変更が適用されたことをユーザーに明確にフィードバックする必要があります。この通知により、ユーザーはテーマ変更が正常に完了したことを確認できます。

**通知の送信タイミング**：ユーザーがテーマを切り替えた時（cycleTheme関数実行時）に即座に送信されます。キーボードショートカット（mod+shift+t）またはコマンドパレットからのテーマ選択で発火します。

**通知の受信者**：テーマを切り替えたユーザー本人。設定による制御はなく、常に表示されます。

**通知内容の概要**：通知タイトル「Theme switched」と、切り替え先のテーマ名が説明として表示されます。

**期待されるアクション**：ユーザーは通知で新しいテーマ名を確認し、意図したテーマに切り替わったことを認識します。意図しないテーマの場合は再度テーマを切り替えることが想定されます。

## 通知種別

アプリ内トースト通知（一時的）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（ユーザーアクション直後） |
| 優先度 | 低 |
| リトライ | なし |

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

1. ユーザーがテーマ切り替えアクションを実行
2. theme.setTheme()でテーマを変更
3. 変更完了後、showToast()でトースト表示

## 通知テンプレート

### アプリ内トースト通知の場合

| 項目 | 内容 |
|-----|------|
| タイトル | `toast.theme.title`（"Theme switched"） |
| 説明 | テーマ名（nextTheme.name または themeId） |
| 永続性 | なし（自動消去） |
| アクション | なし |

### 本文テンプレート

```
タイトル: Theme switched
説明: {themeName}
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 添付ファイルは使用されません |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| themeName | テーマの表示名 | theme.themes()[themeId].name または themeId | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ユーザーアクション | キーボードショートカット（mod+shift+t） | 常に | テーマサイクル実行時 |
| ユーザーアクション | コマンドパレットからテーマ選択 | 常に | テーマ設定時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| なし | 抑止条件はありません |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[テーマ切り替えアクション] --> B[現在のテーマIndex取得]
    B --> C[次のテーマIndex計算]
    C --> D[theme.setTheme実行]
    D --> E[テーマ名取得]
    E --> F[showToast呼び出し]
    F --> G[トースト表示]
    G --> H[自動消去]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| theme.themes | 利用可能テーマ一覧 | ThemeContextから取得 |

### テーブル別参照項目詳細

#### theme.themes

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| themeId | テーマ識別子 | 現在のthemeId |
| name | テーマ表示名 | - |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | 通知自体は状態更新を行わない |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| テーマ名未定義 | theme.name が undefined | themeIdをフォールバックとして使用 |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし（24時間配信可能）

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

- テーマ名はシステム定義のため、セキュリティリスクは低い
- ユーザー入力を含まない

## 備考

- テーマはcycleTheme関数で direction=1（順方向）または direction=-1（逆方向）でサイクル可能
- コマンドパレットからの選択時はonHighlightでプレビュー表示、onSelectで確定
- テーマ情報はThemeContextで管理される

---

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

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

### 推奨読解順序

#### Step 1: トリガー処理を理解する

テーマ切り替え関数を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | layout.tsx | `packages/app/src/pages/layout.tsx` | cycleTheme関数（236-248行目）でテーマサイクルロジックを理解 |

**読解のコツ**: availableThemeEntries()でテーマ一覧を取得し、インデックス計算でサイクルしていることに注目。

**主要処理フロー**:
1. **237行目**: availableThemeEntries()で利用可能テーマ取得
2. **239行目**: 現在のテーマインデックス取得
3. **240行目**: 次のインデックス計算（モジュロ演算でループ）
4. **242行目**: theme.setTheme()でテーマ設定
5. **243-247行目**: showToast()でトースト表示

#### Step 2: コマンド登録を理解する

テーマ関連コマンドの登録を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | layout.tsx | `packages/app/src/pages/layout.tsx` | command.register内のtheme.cycle（1003-1009行目）を確認 |
| 2-2 | layout.tsx | `packages/app/src/pages/layout.tsx` | theme.set.{id}コマンド（1012-1023行目）を確認 |

**主要処理フロー**:
- **1007-1008行目**: keybind: "mod+shift+t"でサイクル実行
- **1017行目**: 個別テーマ選択時はtheme.commitPreview()で確定

#### Step 3: i18nキーを理解する

通知テキストの多言語対応を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | en.ts | `packages/app/src/i18n/en.ts` | toast.theme.title（397行目）を確認 |

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

```
キーボードショートカット (mod+shift+t)
    │
    └─ cycleTheme (layout.tsx:236)
           │
           ├─ availableThemeEntries() - テーマ一覧取得
           │
           ├─ theme.themeId() - 現在テーマ取得
           │
           ├─ theme.setTheme(nextThemeId) - テーマ設定
           │
           └─ showToast (toast.tsx:118)
                  ├─ title: "Theme switched"
                  └─ description: themeName

コマンドパレット選択
    │
    └─ theme.set.{id} コマンド
           │
           ├─ onHighlight: theme.previewTheme(id) - プレビュー
           │
           └─ onSelect: theme.commitPreview() - 確定
```

### データフロー図

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

ユーザーアクション          Layout - cycleTheme
mod+shift+t      ────▶    テーマIndex計算  ──────────────▶   トースト通知
                          theme.setTheme()                  UI更新
                               │
                               ▼
                          showToast({
                            title: "Theme switched",
                            description: themeName
                          })
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| layout.tsx | `packages/app/src/pages/layout.tsx` | ソース | cycleTheme関数・コマンド登録 |
| context.tsx | `packages/ui/src/theme/context.tsx` | ソース | ThemeContext・テーマ状態管理 |
| toast.tsx | `packages/ui/src/components/toast.tsx` | ソース | showToast関数 |
| en.ts | `packages/app/src/i18n/en.ts` | 設定 | 翻訳キー定義 |
