# 画面設計書 2-セッション画面

## 概要

本ドキュメントは、OpenCodeデスクトップアプリケーションのセッション画面の設計仕様を定義する。セッション画面はAIとの対話を行う主要な作業画面である。

### 本画面の処理概要

セッション画面は、ユーザーがAIエージェントと対話し、コード生成・編集・レビューを行うための中心的なインターフェースである。メッセージタイムライン、ファイルタブ、レビューパネル、ターミナルパネルを統合した複合的なワークスペースを提供する。

**業務上の目的・背景**：開発者がAI支援を受けながらコーディング作業を行う際、対話履歴の確認、コード変更のレビュー、ターミナル操作を一つの画面で効率的に実行できる環境が必要である。この画面は、AI駆動開発ワークフローの中核を担う。

**画面へのアクセス方法**：
- ホーム画面からプロジェクトを選択後、自動的にセッション画面に遷移
- サイドバーのセッション一覧から既存セッションを選択
- URLに直接 `/:dir/session/:id` を指定してアクセス

**主要な操作・処理内容**：
1. AIエージェントへのメッセージ送信と応答の受信
2. メッセージタイムラインでの対話履歴の閲覧
3. ファイルタブによるコード表示・編集
4. セッションレビューパネルでの差分確認
5. ターミナルパネルでのコマンド実行結果表示
6. セッションフォーク、セッション共有
7. メッセージのリバート・アンドゥ

**画面遷移**：
- 遷移元：ホーム画面、ディレクトリレイアウト、サイドバーセッション一覧
- 遷移先：別のセッション画面、ホーム画面

**権限による表示制御**：特になし。ただし、権限確認ダイアログが表示される場合がある（ファイル操作、コマンド実行時）。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 21 | セッション作成 | 主機能 | 新規セッションの作成、NewSessionViewコンポーネントによるセッション開始 |
| 25 | メッセージ管理 | 主機能 | MessageTimelineによるメッセージの表示・管理、ユーザーメッセージと応答の処理 |
| 39 | ファイル変更追跡 | 補助機能 | SessionReviewTabによる変更差分の表示、スナップショットベースの変更追跡 |
| 40 | 差分計算 | 補助機能 | セッション差分（diffs）の計算と表示 |
| 47 | PTY管理 | 補助機能 | TerminalPanelコンポーネントによるターミナル表示 |
| 26 | 使用量追跡 | 補助機能 | セッション内のトークン使用量の追跡 |
| 56 | 国際化 | 補助機能 | 画面内テキストの多言語対応 |

## 画面種別

詳細 / 作業画面

## URL/ルーティング

- `/:dir/session` - 新規セッション画面
- `/:dir/session/:id` - 既存セッション画面

`:dir` はBase64エンコードされたディレクトリパス、`:id` はセッションIDを表す。

## 入出力項目

| 項目名 | 入力/出力 | 型 | 必須 | 説明 |
|--------|----------|------|------|------|
| メッセージ入力 | 入力 | string | ○ | AIに送信するメッセージテキスト |
| コンテキストファイル | 入力 | FileSelection[] | - | 参照するファイルと選択範囲 |
| コメント | 入力 | Comment[] | - | ファイルへのインラインコメント |
| エージェント種別 | 入力 | string | - | 使用するエージェント（build/plan） |
| モデル選択 | 入力 | string | - | 使用するAIモデル |
| ワークツリー選択 | 入力 | string | - | 新規セッション時のワークツリー |

## 表示項目

| 項目名 | 型 | 説明 |
|--------|------|------|
| セッションヘッダー | コンポーネント | セッション情報、タイトル |
| メッセージタイムライン | コンポーネント | ユーザーメッセージとAI応答の履歴 |
| ファイルタブ | コンポーネント | 開いているファイルのタブ一覧 |
| ファイルコンテンツ | コンポーネント | 選択中ファイルの内容表示 |
| セッションレビューパネル | コンポーネント | 変更差分の表示 |
| ターミナルパネル | コンポーネント | PTYターミナル出力 |
| コンポーザー（入力欄） | コンポーネント | メッセージ入力エリア |
| スクロール復帰ボタン | ボタン | 最新メッセージへスクロール |
| 差分件数バッジ | バッジ | レビュー対象ファイル数 |
| モバイルタブ | タブ | モバイル表示時のセッション/変更切り替え |

## イベント仕様

### 1-メッセージ送信

ユーザーがメッセージを入力して送信した際の処理。

処理フロー：
1. コンポーザーからメッセージテキストを取得
2. コンテキスト（選択ファイル、コメント）を収集
3. SDK経由でバックエンドにメッセージを送信
4. コメントをクリア（`comments.clear()`）
5. スクロールを再開（`resumeScroll()`）

### 2-レビューパネル操作

ファイル差分をレビューする際の処理。

処理フロー：
1. `view().reviewPanel.open()` でレビューパネルを開く
2. `reviewDiffs()` で差分データを取得
3. 差分スタイル（unified/split）を選択
4. 特定ファイルにフォーカス（`focusReviewDiff(path)`）
5. インラインコメントを追加（`addCommentToContext()`）

### 3-ターミナルパネル表示/非表示

ターミナルパネルの開閉操作。

処理フロー：
1. `view().terminal.opened()` で現在の状態を確認
2. キーボードショートカットまたはボタンでトグル
3. PTY出力がリアルタイムで表示される

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

メッセージ履歴を前後に移動する操作。

処理フロー：
1. `navigateMessageByOffset(offset)` で移動方向を指定
2. `visibleUserMessages()` から対象メッセージを特定
3. `scrollToMessage()` でスクロール実行
4. `autoScroll.pause()` で自動スクロールを一時停止

### 5-セッションフォーク

現在のセッションをフォークして新しいセッションを作成。

処理フロー：
1. フォークダイアログを表示
2. フォーク元メッセージを選択
3. 新規セッションが作成され、遷移

### 6-ファイルタブ操作

ファイルタブの開閉、選択操作。

処理フロー：
1. `tabs().open(tab)` でタブを開く
2. `tabs().setActive(tab)` でアクティブタブを設定
3. `file.load(path)` でファイル内容を読み込み
4. タブを閉じる際は `tabs().close(tab)`

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| メッセージ送信 | message | INSERT | ユーザーメッセージの保存 |
| AI応答受信 | message | INSERT | アシスタントメッセージの保存 |
| セッション作成 | session | INSERT | 新規セッションレコードの作成 |
| セッション更新 | session | UPDATE | タイトル、サマリーの更新 |
| 差分保存 | session_diff | INSERT/UPDATE | ファイル差分データの保存 |

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

#### message

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | UUID生成 | メッセージID |
| INSERT | sessionID | 現在のセッションID | セッション紐付け |
| INSERT | role | "user" / "assistant" | メッセージ種別 |
| INSERT | content | メッセージ本文 | - |
| INSERT | agent | 選択中のエージェント | build/plan |
| INSERT | model | 選択中のモデル | - |

#### session

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | UUID生成 | セッションID |
| INSERT | directory | プロジェクトディレクトリ | - |
| UPDATE | title | AI生成または手動設定 | - |
| UPDATE | summary.files | 変更ファイル数 | - |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|----------|
| session.review.empty | 情報 | 変更はありません | 差分がない場合 |
| session.review.noVcs | 警告 | バージョン管理されていません | VCSがない場合 |
| session.review.loadingChanges | 情報 | 変更を読み込み中... | 差分読み込み中 |
| session.review.noChanges | 情報 | このターンでの変更はありません | ターン差分がない場合 |
| ui.sessionReview.title | ラベル | セッション | 差分タブタイトル |
| ui.sessionReview.title.lastTurn | ラベル | 最後のターン | ターン差分タブタイトル |

## 例外処理

| 状況 | 処理内容 |
|------|---------|
| メッセージ送信失敗 | エラートーストを表示、再送信を促す |
| セッション同期失敗 | 再接続を試行、エラー表示 |
| ファイル読み込み失敗 | エラーメッセージをファイルタブに表示 |
| 権限拒否 | 操作をキャンセル、通知表示 |
| セッション履歴読み込み失敗 | 「読み込みに失敗しました」表示 |

## 備考

- デスクトップとモバイルでレイアウトが異なる（768px境界でレスポンシブ切り替え）
- レビューパネルとファイルツリーは排他的に表示される
- 自動スクロール機能により、新しいメッセージが追加されると自動的に最下部へスクロール
- ユーザーが手動スクロールした場合は自動スクロールが一時停止
- セッション差分は「セッション全体」と「最後のターン」の2種類を切り替え可能

---

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

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

### 推奨読解順序

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

セッションとメッセージのデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | client.ts | `packages/sdk/src/v2/client.ts` | Session, Message, UserMessage型の定義 |
| 1-2 | sync.tsx | `packages/app/src/context/sync.tsx` | セッション同期データの構造 |

**読解のコツ**: `sync.data.message[sessionID]` にメッセージ配列が格納される。

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

セッション画面のメインコンポーネントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | session.tsx | `packages/app/src/pages/session.tsx` | セッション画面のメインコンポーネント（約1127行） |

**主要処理フロー**:
1. **35-46行目**: 各種コンテキストのフック呼び出し
2. **48-55行目**: UIステートの初期化
3. **57行目**: コンポーザーステートの作成
4. **159-169行目**: セッションメッセージの取得とメモ化
5. **182-197行目**: ユーザーメッセージのフィルタリング
6. **772-775行目**: 自動スクロールの設定
7. **993-1125行目**: JSXレンダリング（レイアウト構造）

#### Step 3: サブコンポーネントを理解する

主要なサブコンポーネントの役割を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | message-timeline.tsx | `packages/app/src/pages/session/message-timeline.tsx` | メッセージタイムライン表示 |
| 3-2 | review-tab.tsx | `packages/app/src/pages/session/review-tab.tsx` | 差分レビューパネル |
| 3-3 | terminal-panel.tsx | `packages/app/src/pages/session/terminal-panel.tsx` | ターミナルパネル |
| 3-4 | composer.tsx | `packages/app/src/pages/session/composer.tsx` | メッセージ入力コンポーザー |
| 3-5 | session.tsx | `packages/app/src/components/session.tsx` | SessionHeader, NewSessionView |

#### Step 4: スクロール管理を理解する

自動スクロールとメッセージナビゲーションの仕組み。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | scroll-spy.ts | `packages/app/src/pages/session/scroll-spy.ts` | スクロール監視 |
| 4-2 | use-session-hash-scroll.ts | `packages/app/src/pages/session/use-session-hash-scroll.ts` | URLハッシュ連動スクロール |
| 4-3 | hooks.ts | `packages/ui/src/hooks.ts` | createAutoScroll |

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

```
Page (session.tsx)
    │
    ├─ useSync() ───────────────> SyncContext
    │      ├─ sync.session.get(id) (セッション情報)
    │      ├─ sync.data.message[id] (メッセージ一覧)
    │      └─ sync.data.session_diff[id] (差分データ)
    │
    ├─ useLayout() ─────────────> LayoutContext
    │      ├─ layout.tabs() (タブ管理)
    │      ├─ layout.view() (パネル状態)
    │      └─ layout.fileTree (ファイルツリー)
    │
    ├─ useFile() ───────────────> FileContext
    │      ├─ file.load(path) (ファイル読み込み)
    │      ├─ file.tab(path) (タブID取得)
    │      └─ file.get(path) (ファイル内容)
    │
    ├─ SessionHeader ───────────> セッションヘッダー表示
    │
    ├─ MessageTimeline ─────────> メッセージタイムライン
    │      └─ UserMessage / AssistantMessage
    │
    ├─ SessionReviewTab ────────> レビューパネル
    │      └─ DiffView (差分表示)
    │
    ├─ SessionSidePanel ────────> サイドパネル
    │      └─ FileTree / ReviewPanel
    │
    ├─ TerminalPanel ───────────> ターミナル
    │
    └─ SessionComposerRegion ───> 入力コンポーザー
           └─ Composer (メッセージ入力)
```

### データフロー図

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

メッセージ入力              SessionComposerRegion            メッセージ送信
(テキスト/コンテキスト) ──▶ composer.submit() ─────────────▶ SDK.message.create()
                                │
                                └─▶ comments.clear()
                                └─▶ resumeScroll()

セッションID               sync.session.sync(id)            メッセージ一覧
(URL params.id) ─────────▶ sync.data.message[id] ─────────▶ MessageTimeline
                                │
                                └─▶ visibleUserMessages()

差分データ                 sync.session.diff(id)            レビューパネル
(session_diff) ──────────▶ reviewDiffs() ─────────────────▶ SessionReviewTab
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| session.tsx | `packages/app/src/pages/session.tsx` | ソース | セッション画面メインコンポーネント |
| message-timeline.tsx | `packages/app/src/pages/session/message-timeline.tsx` | ソース | メッセージタイムライン |
| review-tab.tsx | `packages/app/src/pages/session/review-tab.tsx` | ソース | レビューパネル |
| terminal-panel.tsx | `packages/app/src/pages/session/terminal-panel.tsx` | ソース | ターミナルパネル |
| composer.tsx | `packages/app/src/pages/session/composer.tsx` | ソース | メッセージコンポーザー |
| session-side-panel.tsx | `packages/app/src/pages/session/session-side-panel.tsx` | ソース | サイドパネル |
| session-mobile-tabs.tsx | `packages/app/src/pages/session/session-mobile-tabs.tsx` | ソース | モバイルタブ |
| helpers.ts | `packages/app/src/pages/session/helpers.ts` | ソース | ヘルパー関数 |
| scroll-spy.ts | `packages/app/src/pages/session/scroll-spy.ts` | ソース | スクロール監視 |
| use-session-commands.ts | `packages/app/src/pages/session/use-session-commands.ts` | ソース | セッションコマンド |
| use-session-hash-scroll.ts | `packages/app/src/pages/session/use-session-hash-scroll.ts` | ソース | ハッシュスクロール |
| sync.tsx | `packages/app/src/context/sync.tsx` | ソース | 同期コンテキスト |
| file.tsx | `packages/app/src/context/file.tsx` | ソース | ファイルコンテキスト |
| comments.tsx | `packages/app/src/context/comments.tsx` | ソース | コメントコンテキスト |
| prompt.tsx | `packages/app/src/context/prompt.tsx` | ソース | プロンプトコンテキスト |
| local.tsx | `packages/app/src/context/local.tsx` | ソース | ローカル設定コンテキスト |
