# 画面設計書 4-レイアウト

## 概要

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

### 本画面の処理概要

レイアウト画面は、OpenCodeデスクトップアプリケーション全体のフレーム構造を提供するコンテナコンポーネントである。サイドバー、タイトルバー、メインコンテンツ領域を統合し、プロジェクト間のナビゲーションやセッション管理の基盤を提供する。

**業務上の目的・背景**：複数のプロジェクトとセッションを同時に管理する開発者のために、効率的なナビゲーションとワークスペース管理機能を提供する。サイドバーによるプロジェクト・セッション一覧の表示、ドラッグ＆ドロップによる並び替え、ワークスペース（Git worktree）管理など、高度な操作を可能にする。

**画面へのアクセス方法**：アプリケーション起動後、自動的にこのレイアウトが適用される。すべてのページ（ホーム画面、セッション画面等）はこのレイアウト内にレンダリングされる。

**主要な操作・処理内容**：
1. サイドバーの展開・折りたたみ制御
2. プロジェクト一覧の表示・選択・並び替え
3. セッション一覧の表示・選択・アーカイブ
4. ワークスペース（Git worktree）の作成・削除・リセット
5. テーマ・言語・カラースキームの切り替え
6. 設定ダイアログ・サーバー選択ダイアログの表示
7. アップデート通知の表示
8. キーボードショートカットによるナビゲーション

**画面遷移**：
- 遷移元：アプリケーション起動時に自動ロード
- 遷移先：子ルートとして各画面（ホーム、セッション等）をレンダリング

**権限による表示制御**：特になし。ただし、プラットフォーム機能（checkUpdate, update, restart等）の有無により一部機能が変化する。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 49 | プロジェクト検出 | 主機能 | プロジェクト一覧の表示・管理 |
| 21 | セッション作成 | 補助機能 | 新規セッション作成ボタン |
| 25 | メッセージ管理 | 補助機能 | セッションのプリフェッチ |
| 56 | 国際化 | 補助機能 | 画面内テキストの多言語対応 |
| 48 | 設定ファイル管理 | 補助機能 | 設定ダイアログの表示 |

## 画面種別

レイアウト / シェル

## URL/ルーティング

- `/:dir/*` - ディレクトリ指定時のレイアウト
- `/` - ルート（ホーム画面表示）

## 入出力項目

| 項目名 | 入力/出力 | 型 | 必須 | 説明 |
|--------|----------|------|------|------|
| params.dir | 入力 | string | - | Base64エンコードされたディレクトリパス |
| params.id | 入力 | string | - | セッションID |
| children | 入力 | JSX.Element | ○ | 子コンポーネント（ページ内容） |

## 表示項目

| 項目名 | 型 | 説明 |
|--------|------|------|
| タイトルバー | コンポーネント | アプリケーションタイトルバー |
| サイドバー | コンポーネント | プロジェクト・セッション一覧 |
| プロジェクト一覧 | リスト | 開いているプロジェクトのリスト |
| セッション一覧 | リスト | 選択中プロジェクトのセッション |
| ワークスペース一覧 | リスト | Git worktreeによるワークスペース |
| 新規セッションボタン | ボタン | 新しいセッションを作成 |
| 新規ワークスペースボタン | ボタン | 新しいワークスペースを作成 |
| 設定ボタン | ボタン | 設定ダイアログを開く |
| ヘルプボタン | ボタン | フィードバックリンクを開く |
| プロジェクト追加ボタン | ボタン | 新規プロジェクトを追加 |
| メインコンテンツ | コンポーネント | 子ルートのレンダリング領域 |
| トースト領域 | コンポーネント | 通知メッセージ表示領域 |

## イベント仕様

### 1-サイドバートグル

ユーザーがサイドバーの展開・折りたたみを切り替える際の処理。

処理フロー：
1. `layout.sidebar.toggle()` を呼び出し
2. サイドバーの幅が変更される（展開時244px以上、折りたたみ時64px）
3. CSS変数 `--dialog-left-margin` が更新される

### 2-プロジェクト選択

ユーザーがサイドバーのプロジェクトをクリックした際の処理。

処理フロー：
1. `navigateToProject(directory)` を呼び出し
2. `server.projects.touch(root)` で最終アクセス時刻を更新
3. 最後に開いていたセッションがあれば `navigate()` でそのセッションに遷移
4. なければプロジェクトのセッション画面に遷移

### 3-プロジェクト並び替え

ユーザーがドラッグ＆ドロップでプロジェクトを並び替える際の処理。

処理フロー：
1. `handleDragStart()` でドラッグ開始、`store.activeProject` を設定
2. `handleDragOver()` で並び替え位置を計算
3. `layout.projects.move(draggable.id, toIndex)` でプロジェクト順序を更新
4. `handleDragEnd()` でドラッグ終了

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

キーボードショートカットでセッション間を移動する際の処理。

処理フロー：
1. `navigateSessionByOffset(offset)` を呼び出し（+1で次、-1で前）
2. `currentSessions()` から対象セッションを取得
3. `prefetchSession()` で前後のセッションをプリフェッチ
4. `navigate()` で画面遷移
5. `scrollToSession()` でサイドバーをスクロール

### 5-ワークスペース作成

新規ワークスペースを作成する際の処理。

処理フロー：
1. `createWorkspace(project)` を呼び出し
2. `globalSDK.client.worktree.create()` でワークスペース作成
3. `setWorkspaceName()` でワークスペース名を設定
4. `setBusy(directory, true)` でローディング状態に
5. ワークスペース作成完了後、`navigate()` で新ワークスペースに遷移

### 6-ワークスペース削除

ワークスペースを削除する際の処理。

処理フロー：
1. `deleteWorkspace(root, directory)` を呼び出し
2. `setBusy(directory, true)` でローディング状態に
3. `globalSDK.client.worktree.remove()` でワークスペース削除
4. `globalSync.set("project", ...)` でプロジェクト情報更新
5. `layout.projects.close(directory)` でサイドバーから削除
6. 現在表示中なら親プロジェクトに遷移

### 7-ワークスペースリセット

ワークスペースをリセットする際の処理。

処理フロー：
1. `resetWorkspace(root, directory)` を呼び出し
2. `clearWorkspaceTerminals()` でターミナルをクリア
3. `globalSDK.client.instance.dispose()` でインスタンス破棄
4. `globalSDK.client.worktree.reset()` でワークスペースリセット
5. 関連セッションをアーカイブ
6. トーストで完了通知

### 8-テーマ切り替え

テーマを切り替える際の処理。

処理フロー：
1. `cycleTheme(direction)` を呼び出し
2. `theme.setTheme(nextThemeId)` でテーマを設定
3. `showToast()` で変更を通知

### 9-言語切り替え

言語を切り替える際の処理。

処理フロー：
1. `cycleLanguage(direction)` または `setLocale(locale)` を呼び出し
2. `language.setLocale(next)` で言語を設定
3. `showToast()` で変更を通知

### 10-アップデート確認

アップデートを確認・インストールする際の処理。

処理フロー：
1. `platform.checkUpdate()` でアップデート確認
2. 利用可能な場合、トーストで通知
3. ユーザーが「インストール＆再起動」を選択
4. `platform.update()` でアップデート実行
5. `platform.restart()` で再起動

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| プロジェクト選択 | project | UPDATE | 最終アクセス時刻を更新 |
| セッションアーカイブ | session | UPDATE | アーカイブ時刻を設定 |
| ワークスペース作成 | project | UPDATE | sandboxes配列に追加 |
| ワークスペース削除 | project | UPDATE | sandboxes配列から削除 |

### テーブル別更新項目詳細

#### project

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | sandboxes | ワークスペースパスの配列 | ワークスペース作成・削除時 |

#### session

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | time.archived | 現在のタイムスタンプ | アーカイブ時 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|----------|
| command.sidebar.toggle | アクション | サイドバーを切り替え | コマンドパレット |
| command.project.open | アクション | プロジェクトを開く | コマンドパレット・ボタン |
| command.provider.connect | アクション | プロバイダーを接続 | コマンドパレット |
| command.server.switch | アクション | サーバーを切り替え | コマンドパレット |
| command.settings.open | アクション | 設定を開く | コマンドパレット・ボタン |
| command.session.previous | アクション | 前のセッション | キーボードショートカット |
| command.session.next | アクション | 次のセッション | キーボードショートカット |
| command.session.archive | アクション | セッションをアーカイブ | コマンドパレット |
| workspace.new | アクション | 新規ワークスペース | ボタン |
| workspace.delete.title | ダイアログ | ワークスペースを削除 | 削除ダイアログ |
| workspace.reset.title | ダイアログ | ワークスペースをリセット | リセットダイアログ |
| toast.update.title | 情報 | アップデートが利用可能です | アップデート検出時 |
| toast.theme.title | 情報 | テーマを変更しました | テーマ変更時 |
| toast.language.title | 情報 | 言語を変更しました | 言語変更時 |
| notification.permission.title | 通知 | 権限リクエスト | 権限要求時 |
| notification.question.title | 通知 | 質問があります | エージェント質問時 |

## 例外処理

| 状況 | 処理内容 |
|------|---------|
| ワークスペース作成失敗 | トーストでエラーメッセージを表示 |
| ワークスペース削除失敗 | トーストでエラーメッセージを表示 |
| ワークスペースリセット失敗 | トーストでエラーメッセージを表示、ローディング解除 |
| アップデート確認失敗 | サイレントに失敗（次回ポーリングで再試行） |

## 備考

- サイドバーの幅は244px〜ウィンドウ幅の30%+64pxで調整可能
- サイドバー折りたたみ時はホバーで展開表示（aim機能による予測ホバー）
- セッションのプリフェッチは最大10件/ディレクトリ、LRUで管理
- アップデートポーリングは10分間隔
- モバイル表示（1280px未満）ではスライドアウトサイドバーを使用
- プロジェクト・ワークスペースはドラッグ＆ドロップで並び替え可能
- ワークスペース機能はGitリポジトリのみで有効

---

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

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

### 推奨読解順序

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

レイアウトで管理する状態とプロジェクト型を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | layout.tsx | `packages/app/src/pages/layout.tsx` | persistedストア定義（81-92行目） |
| 1-2 | layout.tsx | `packages/app/src/context/layout.tsx` | LocalProject型、LayoutContextの定義 |

**読解のコツ**: `persisted`で永続化されるデータ（lastProjectSession, workspaceOrder等）と、createStoreで管理される一時状態（busyWorkspaces, hoverSession等）を区別する。

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

レイアウトコンポーネントの全体構造を確認する。

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

**主要処理フロー**:
1. **81-92行目**: 永続化ストアの初期化（lastProjectSession, workspaceOrder等）
2. **98-123行目**: 各種コンテキストのフック呼び出し
3. **125-132行目**: 一時状態のストア初期化
4. **281-332行目**: アップデートポーリング（useUpdatePolling）
5. **334-440行目**: SDK通知トースト（useSDKNotificationToasts）
6. **894-1063行目**: コマンド登録（command.register）
7. **1926-2063行目**: JSXレンダリング

#### Step 3: サイドバー関連処理を理解する

サイドバーの展開・折りたたみ、プロジェクト・セッション表示を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | layout.tsx | `packages/app/src/pages/layout.tsx` | aim（予測ホバー）設定（160-169行目） |
| 3-2 | layout.tsx | `packages/app/src/pages/layout.tsx` | SidebarPanel関数コンポーネント（1695-1924行目） |
| 3-3 | sidebar-shell.tsx | `packages/app/src/pages/layout/sidebar-shell.tsx` | SidebarContentコンポーネント |
| 3-4 | sidebar-project.tsx | `packages/app/src/pages/layout/sidebar-project.tsx` | SortableProjectコンポーネント |
| 3-5 | sidebar-workspace.tsx | `packages/app/src/pages/layout/sidebar-workspace.tsx` | SortableWorkspaceコンポーネント |

#### Step 4: ワークスペース操作を理解する

Git worktreeによるワークスペース管理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | layout.tsx | `packages/app/src/pages/layout.tsx` | createWorkspace関数（1595-1634行目） |
| 4-2 | layout.tsx | `packages/app/src/pages/layout.tsx` | deleteWorkspace関数（1205-1241行目） |
| 4-3 | layout.tsx | `packages/app/src/pages/layout.tsx` | resetWorkspace関数（1243-1319行目） |
| 4-4 | layout.tsx | `packages/app/src/pages/layout.tsx` | DialogDeleteWorkspace（1321-1373行目） |
| 4-5 | layout.tsx | `packages/app/src/pages/layout.tsx` | DialogResetWorkspace（1375-1449行目） |

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

```
Layout (layout.tsx)
    │
    ├─ persisted() ────────────────> Persist (永続化ストア)
    │      └─ store (lastProjectSession, workspaceOrder, etc.)
    │
    ├─ useGlobalSync() ────────────> GlobalSyncContext
    │      ├─ globalSync.data.project (プロジェクト一覧)
    │      └─ globalSync.child() (ワークスペース別データ)
    │
    ├─ useLayout() ────────────────> LayoutContext
    │      ├─ layout.sidebar (サイドバー制御)
    │      ├─ layout.projects (プロジェクト管理)
    │      └─ layout.mobileSidebar (モバイル用)
    │
    ├─ usePlatform() ──────────────> PlatformContext
    │      ├─ platform.checkUpdate() (アップデート確認)
    │      ├─ platform.update() (アップデート実行)
    │      ├─ platform.restart() (再起動)
    │      └─ platform.notify() (通知表示)
    │
    ├─ useGlobalSDK() ─────────────> GlobalSDKContext
    │      ├─ globalSDK.client.worktree (ワークスペース操作)
    │      ├─ globalSDK.client.session (セッション操作)
    │      └─ globalSDK.event.listen() (イベント監視)
    │
    ├─ useCommand() ───────────────> CommandContext
    │      └─ command.register() (コマンド登録)
    │
    ├─ useTheme() ─────────────────> ThemeContext
    │      └─ theme.setTheme() (テーマ変更)
    │
    ├─ useLanguage() ──────────────> LanguageContext
    │      └─ language.setLocale() (言語変更)
    │
    ├─ DragDropProvider ───────────> solid-dnd
    │      └─ プロジェクト・ワークスペース並び替え
    │
    └─ SidebarContent ─────────────> サイドバーUI
           ├─ SortableProject (プロジェクト表示)
           └─ SortableWorkspace (ワークスペース表示)
```

### データフロー図

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

プロジェクト一覧              currentProject()                サイドバー表示
(layout.projects) ──────────▶ currentSessions() ────────────▶ SidebarPanel
                                    │
                                    ├─▶ prefetchSession() ───▶ メッセージキャッシュ
                                    │
                                    └─▶ navigateToSession() ─▶ URL遷移

ドラッグイベント              handleDragOver()                並び替え結果
(DragDropProvider) ─────────▶ layout.projects.move() ───────▶ プロジェクト順序

ワークスペース操作            globalSDK.client.worktree       ワークスペース更新
(ボタンクリック) ───────────▶ create/remove/reset ───────────▶ globalSync更新

アップデートポーリング        platform.checkUpdate()          トースト通知
(10分間隔) ─────────────────▶ showToast() ───────────────────▶ ユーザー通知
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| layout.tsx | `packages/app/src/pages/layout.tsx` | ソース | メインレイアウトコンポーネント（2065行） |
| sidebar-shell.tsx | `packages/app/src/pages/layout/sidebar-shell.tsx` | ソース | サイドバーシェルコンポーネント |
| sidebar-project.tsx | `packages/app/src/pages/layout/sidebar-project.tsx` | ソース | プロジェクト表示コンポーネント |
| sidebar-workspace.tsx | `packages/app/src/pages/layout/sidebar-workspace.tsx` | ソース | ワークスペース表示コンポーネント |
| helpers.ts | `packages/app/src/pages/layout/helpers.ts` | ソース | ヘルパー関数（sortedRootSessions等） |
| deep-links.ts | `packages/app/src/pages/layout/deep-links.ts` | ソース | ディープリンク処理 |
| inline-editor.ts | `packages/app/src/pages/layout/inline-editor.ts` | ソース | インライン編集コントローラー |
| layout.tsx | `packages/app/src/context/layout.tsx` | ソース | レイアウトコンテキスト |
| global-sync.tsx | `packages/app/src/context/global-sync.tsx` | ソース | グローバル同期コンテキスト |
| global-sdk.tsx | `packages/app/src/context/global-sdk.tsx` | ソース | SDK接続コンテキスト |
| platform.tsx | `packages/app/src/context/platform.tsx` | ソース | プラットフォームコンテキスト |
| command.tsx | `packages/app/src/context/command.tsx` | ソース | コマンドパレットコンテキスト |
| notification.tsx | `packages/app/src/context/notification.tsx` | ソース | 通知管理コンテキスト |
| permission.tsx | `packages/app/src/context/permission.tsx` | ソース | 権限管理コンテキスト |
| titlebar.tsx | `packages/app/src/components/titlebar.tsx` | ソース | タイトルバーコンポーネント |
| dialog-select-provider.tsx | `packages/app/src/components/dialog-select-provider.tsx` | ソース | プロバイダー選択ダイアログ |
| dialog-select-server.tsx | `packages/app/src/components/dialog-select-server.tsx` | ソース | サーバー選択ダイアログ |
| dialog-settings.tsx | `packages/app/src/components/dialog-settings.tsx` | ソース | 設定ダイアログ |
| dialog-edit-project.tsx | `packages/app/src/components/dialog-edit-project.tsx` | ソース | プロジェクト編集ダイアログ |
| resize-handle.tsx | `packages/ui/src/resize-handle.tsx` | ソース | リサイズハンドル |
| toast.tsx | `packages/ui/src/toast.tsx` | ソース | トースト通知 |
| dropdown-menu.tsx | `packages/ui/src/dropdown-menu.tsx` | ソース | ドロップダウンメニュー |
