# 画面設計書 5-ディレクトリレイアウト

## 概要

本ドキュメントは、OpenCodeデスクトップアプリケーションのディレクトリレイアウト画面の設計仕様を定義する。

### 本画面の処理概要

ディレクトリレイアウトは、特定のプロジェクトディレクトリに関連するコンテキストプロバイダーを提供するラッパーコンポーネントである。SDK接続、データ同期、ローカルストレージなど、ディレクトリ固有の機能を子コンポーネントに提供する。

**業務上の目的・背景**：各プロジェクトディレクトリは独自のSDK接続、データ同期状態、権限管理を必要とする。このコンポーネントは、URLからディレクトリパスを抽出し、関連するすべてのコンテキストプロバイダーをネストして子コンポーネントに提供することで、ディレクトリ固有の操作を可能にする。

**画面へのアクセス方法**：`/:dir/*`パターンのURLにアクセスすると自動的にこのレイアウトが適用される。ユーザーがプロジェクトを選択すると、Base64エンコードされたディレクトリパスがURLに含まれ、このコンポーネントがアクティブになる。

**主要な操作・処理内容**：
1. URLパラメータからディレクトリパスをデコード
2. SDKプロバイダーの初期化
3. データ同期プロバイダーの初期化
4. 権限応答・質問応答のハンドラ設定
5. 無効なURLの検出とリダイレクト

**画面遷移**：
- 遷移元：ホーム画面（プロジェクト選択後）
- 遷移先：無効なURL時はホーム画面（`/`）にリダイレクト

**権限による表示制御**：特になし。ディレクトリパスが有効である必要がある。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | 権限管理 | 主機能 | 権限応答ハンドラの設定（onPermissionRespond） |
| 25 | メッセージ管理 | 主機能 | データ同期プロバイダーによるメッセージ管理 |
| 56 | 国際化 | 補助機能 | エラーメッセージの多言語対応 |

## 画面種別

レイアウト / コンテキストプロバイダー

## URL/ルーティング

`/:dir/*` - Base64エンコードされたディレクトリパスを含むすべてのルート

## 入出力項目

| 項目名 | 入力/出力 | 型 | 必須 | 説明 |
|--------|----------|------|------|------|
| params.dir | 入力 | string | ○ | Base64エンコードされたディレクトリパス |
| children | 入力 | JSX.Element | ○ | 子コンポーネント |

## 表示項目

| 項目名 | 型 | 説明 |
|--------|------|------|
| 子コンポーネント | JSX.Element | 各種プロバイダーでラップされた子コンポーネント |

## イベント仕様

### 1-ディレクトリデコード

URLパラメータからディレクトリパスをデコードする処理。

処理フロー：
1. `params.dir` を取得
2. `decode64(params.dir)` でBase64デコード
3. デコード結果を `directory` メモとして保持

### 2-無効URL検出

無効なディレクトリパスを検出してリダイレクトする処理。

処理フロー：
1. `params.dir` が存在し、`directory()` が空（デコード失敗）の場合
2. `store.invalid` と比較して重複チェック
3. `showToast()` でエラーメッセージを表示
4. `navigate("/", { replace: true })` でホーム画面にリダイレクト

### 3-権限応答

ユーザーが権限リクエストに応答する際の処理。

処理フロー：
1. DataProviderの `onPermissionRespond` コールバックが呼び出される
2. `sdk.client.permission.respond(input)` で権限応答を送信
3. 応答タイプは `"once"` / `"always"` / `"reject"`

### 4-質問応答

ユーザーがエージェントの質問に回答する際の処理。

処理フロー：
1. DataProviderの `onQuestionReply` コールバックが呼び出される
2. `sdk.client.question.reply(input)` で回答を送信
3. または `onQuestionReject` で質問を拒否

### 5-セッションナビゲーション

ダイアログ等からセッションに遷移する際の処理。

処理フロー：
1. DataProviderの `onNavigateToSession` コールバックが呼び出される
2. `navigate(\`/${params.dir}/session/${sessionID}\`)` で遷移

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 権限応答 | permission | UPDATE | 権限応答結果を記録 |
| 質問応答 | question | UPDATE | 質問応答結果を記録 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|----------|
| common.requestFailed | エラー | リクエストに失敗しました | 無効なURL時 |
| directory.error.invalidUrl | エラー | 無効なURLです | 無効なURL時 |

## 例外処理

| 状況 | 処理内容 |
|------|---------|
| 無効なBase64エンコード | トーストでエラー表示後、ホーム画面にリダイレクト |
| ディレクトリパスが空 | トーストでエラー表示後、ホーム画面にリダイレクト |

## 備考

- このコンポーネントは直接UIをレンダリングせず、コンテキストプロバイダーのネストのみを行う
- ディレクトリパスは安全のためBase64エンコードされてURLに含まれる
- SDKProvider、SyncProvider、DataProvider、LocalProviderの4つのプロバイダーがネストされる
- 無効なURLへのアクセスは1回のみトースト表示し、重複表示を防止する

---

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

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

### 推奨読解順序

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

入力パラメータとデコード処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | directory-layout.tsx | `packages/app/src/pages/directory-layout.tsx` | useParamsによるパラメータ取得（40行目） |
| 1-2 | base64.ts | `packages/app/src/utils/base64.ts` | decode64関数の実装 |

**読解のコツ**: `params.dir` はBase64エンコードされた文字列で、`decode64()` でデコードすると元のディレクトリパスが得られる。

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

メインのLayoutコンポーネントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | directory-layout.tsx | `packages/app/src/pages/directory-layout.tsx` | Layoutコンポーネント（39-69行目） |

**主要処理フロー**:
1. **40-42行目**: useParams, useNavigate, useLanguageの取得
2. **43行目**: 無効URL検出用のストア初期化
3. **44-46行目**: ディレクトリパスのメモ化
4. **48-59行目**: 無効URL検出とリダイレクト
5. **60-68行目**: プロバイダーのネストとレンダリング

#### Step 3: コンテキストプロバイダーを理解する

各プロバイダーの役割を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sdk.tsx | `packages/app/src/context/sdk.tsx` | SDKProviderの実装 |
| 3-2 | sync.tsx | `packages/app/src/context/sync.tsx` | SyncProviderの実装 |
| 3-3 | local.tsx | `packages/app/src/context/local.tsx` | LocalProviderの実装 |
| 3-4 | context.tsx | `packages/ui/src/context.tsx` | DataProviderの実装 |

#### Step 4: DirectoryDataProviderを理解する

権限応答・質問応答のハンドラを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | directory-layout.tsx | `packages/app/src/pages/directory-layout.tsx` | DirectoryDataProvider（14-37行目） |

**主要処理フロー**:
- **24-28行目**: onPermissionRespond - 権限応答ハンドラ
- **29行目**: onQuestionReply - 質問回答ハンドラ
- **30行目**: onQuestionReject - 質問拒否ハンドラ
- **31行目**: onNavigateToSession - セッション遷移ハンドラ
- **32行目**: onSessionHref - セッションURLの生成

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

```
Layout (directory-layout.tsx)
    │
    ├─ useParams() ─────────────────> Router
    │      └─ params.dir (Base64ディレクトリ)
    │
    ├─ decode64() ──────────────────> base64.ts
    │      └─ directory (デコード済みパス)
    │
    ├─ createEffect() ──────────────> 無効URL検出
    │      ├─ showToast() (エラー表示)
    │      └─ navigate("/") (リダイレクト)
    │
    └─ プロバイダーネスト
           │
           ├─ SDKProvider ─────────> sdk.tsx
           │      └─ SDK接続管理
           │
           ├─ SyncProvider ────────> sync.tsx
           │      └─ データ同期管理
           │
           ├─ DirectoryDataProvider
           │      │
           │      ├─ DataProvider ──> context.tsx
           │      │      ├─ onPermissionRespond
           │      │      ├─ onQuestionReply
           │      │      └─ onNavigateToSession
           │      │
           │      └─ LocalProvider ─> local.tsx
           │             └─ ローカルストレージ
           │
           └─ props.children ──────> 子コンポーネント
```

### データフロー図

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

URL params.dir            decode64()                    directoryメモ
(Base64文字列) ─────────▶ (デコード) ─────────────────▶ (パス文字列)
                              │
                              ├─▶ 成功: プロバイダー初期化
                              │         │
                              │         ├─▶ SDKProvider
                              │         │
                              │         ├─▶ SyncProvider
                              │         │
                              │         └─▶ DataProvider
                              │
                              └─▶ 失敗: showToast()
                                        │
                                        └─▶ navigate("/")

権限リクエスト            onPermissionRespond()         権限応答
(ダイアログ) ────────────▶ sdk.client.permission.respond ▶ サーバー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| directory-layout.tsx | `packages/app/src/pages/directory-layout.tsx` | ソース | ディレクトリレイアウトコンポーネント（70行） |
| sdk.tsx | `packages/app/src/context/sdk.tsx` | ソース | SDKプロバイダー |
| sync.tsx | `packages/app/src/context/sync.tsx` | ソース | データ同期プロバイダー |
| local.tsx | `packages/app/src/context/local.tsx` | ソース | ローカルストレージプロバイダー |
| context.tsx | `packages/ui/src/context.tsx` | ソース | DataProviderコンポーネント |
| base64.ts | `packages/app/src/utils/base64.ts` | ソース | Base64エンコード・デコード関数 |
| language.tsx | `packages/app/src/context/language.tsx` | ソース | 国際化コンテキスト |
| toast.tsx | `packages/ui/src/toast.tsx` | ソース | トースト通知 |
