# 画面設計書 3-スプリットビュー

## 概要

本ドキュメントは、Ghosttyターミナルエミュレータのスプリットビュー機能の設計について記載する。スプリットビューは、ターミナルウィンドウを水平または垂直に分割して複数のターミナルを同時に表示する機能である。

### 本画面の処理概要

スプリットビューは、1つのウィンドウ内で複数のターミナルペインを管理し、効率的なマルチタスク作業を実現する。

**業務上の目的・背景**：開発者やシステム管理者が、ログ監視、複数サーバーへの同時接続、コード編集と実行の並行作業など、複数のターミナルセッションを同時に確認・操作する必要がある場面に対応する。タブとは異なり、複数のペインを同時に視認できるため、情報の比較や並行作業に適している。

**画面へのアクセス方法**：
- Cmd+D（右に分割）、Cmd+Shift+D（下に分割）
- メニューバーから「View > Split Right/Left/Up/Down」を選択
- コマンドパレットからスプリットコマンドを実行

**主要な操作・処理内容**：
1. 水平方向（左右）へのスプリット作成
2. 垂直方向（上下）へのスプリット作成
3. ドラッグによるスプリットサイズの調整
4. スプリット間のフォーカス移動（方向キー、Tab）
5. スプリットのズーム（一時的フルサイズ表示）
6. スプリットの均等化
7. スプリットの閉じる操作
8. ドラッグ&ドロップによるスプリットの再配置

**画面遷移**：
- スプリット作成 → 現在のペインが分割され新規ペインが追加
- スプリット閉じる → 親ペインに戻る（最後の1つの場合はウィンドウ閉じる確認）
- スプリットズーム → 選択したペインがフルサイズ表示（トグル）

**権限による表示制御**：特になし。すべてのユーザーが同一の機能を利用可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | 端末エミュレーション | 主機能 | 分割された各ペインでの端末エミュレーション |
| 29 | スプリット(分割) | 主機能 | ウィンドウの水平・垂直分割と複数端末の表示 |
| 30 | スプリットズーム | 補助機能 | 分割ペインの一時的フルサイズ表示 |
| 3 | スクリーン管理 | 主機能 | 各分割ペインのスクリーン管理 |

## 画面種別

スプリット管理画面（ペイン分割表示）

## URL/ルーティング

N/A（ネイティブデスクトップアプリケーション）

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 必須 | 説明 |
|--------|----------|----------|------|------|
| スプリット方向 | 入力 | SplitViewDirection | はい | horizontal/vertical |
| スプリット比率 | 入力/出力 | CGFloat | はい | 0.0〜1.0の分割比率 |
| ディバイダー色 | 設定 | Color | - | 分割線の色 |
| リサイズ増分 | 設定 | NSSize | - | リサイズ時の最小増分 |

## 表示項目

| 項目名 | 表示位置 | データ型 | 説明 |
|--------|----------|----------|------|
| 左/上ペイン | 左または上 | View | 分割の左側または上側のターミナル |
| 右/下ペイン | 右または下 | View | 分割の右側または下側のターミナル |
| ディバイダー | 中央 | View | ドラッグ可能な分割線 |
| ドロップゾーンオーバーレイ | 条件付き | View | ドラッグ&ドロップ時の挿入位置表示 |

## イベント仕様

### 1-スプリット作成

キーボードショートカットまたはメニューからスプリット作成が要求されると、以下の処理が実行される：

1. `ghostty.split(surface:direction:)`が呼び出される
2. 通知`ghosttyNewSplit`が発行される
3. `BaseTerminalController.newSplit`が新しいSurfaceViewを作成
4. SplitTreeに新しいノードが挿入される
5. 新しいペインにフォーカスが移動

### 2-ディバイダードラッグ

ディバイダーがドラッグされると、以下の処理が実行される：

1. `DragGesture.onChanged`がドラッグ位置を取得
2. 新しいスプリット比率が計算される（最小値minSizeを考慮）
3. `split`バインディングが更新される
4. `performSplitAction(.resize)`が呼び出される
5. SplitTreeのノード比率が更新される

### 3-ディバイダーダブルタップ

ディバイダーがダブルタップされると、`onEqualize`コールバックが呼び出され、すべてのスプリットが均等化される。

### 4-フォーカス移動

方向キーまたはTab/Shift+Tabでスプリット間のフォーカスが移動する：

1. `splitMoveFocus(direction:)`が呼び出される
2. SplitTree.focusTargetで次のフォーカス先を決定
3. Spatial表現を使用して方向に基づいた移動を計算
4. 対象サーフェスにフォーカスを移動

### 5-スプリットズーム

Cmd+Shift+Enterでスプリットズームがトグルされる：

1. `ghostty.splitToggleZoom(surface:)`が呼び出される
2. SplitTreeのzoomedプロパティが更新される
3. ズーム中は選択されたノードのみが表示される
4. 再度トグルで元のレイアウトに戻る

### 6-ドラッグ&ドロップによる再配置

サーフェスをドラッグして別のペインにドロップすると、スプリットの位置が変更される：

1. ドラッグ開始でUTType.ghosttySurfaceIdが設定される
2. DropDelegateでドロップ位置を検出
3. TerminalSplitDropZone（top/bottom/left/right）を計算
4. 元の位置から削除し、新しい位置に挿入

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

N/A（本アプリケーションはデータベースを使用しない）

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG001 | 確認 | "Close Terminal? The terminal still has a running process." | プロセス実行中にスプリット閉じる時 |

## 例外処理

| 例外状況 | 処理内容 |
|----------|----------|
| スプリット作成失敗 | エラーログを出力し、操作をスキップ |
| 無効なドロップ操作 | 元の位置に戻るアニメーション |
| 最後のペインを閉じる | ウィンドウの閉じる確認を表示 |

## 備考

- SplitTreeは不変（immutable）データ構造として実装
- ノードは.leafまたは.splitの再帰的構造
- Spatial表現により2D空間での位置関係を計算可能
- Undo/Redo機能でスプリット操作を取り消し可能
- splitPreserveZoom設定でズーム状態をナビゲーション時に保持可能

---

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

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

### 推奨読解順序

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

スプリットの木構造を理解することが最も重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SplitTree.swift | `macos/Sources/Features/Splits/SplitTree.swift` | 木構造の定義（5-89行目）、Node enum、Direction、Path |
| 1-2 | SplitTree.swift | `macos/Sources/Features/Splits/SplitTree.swift` | Spatial表現（44-65行目）、空間的な位置計算 |

**読解のコツ**: SplitTreeは値型（struct）で不変。操作は新しいツリーを返す関数として実装。Nodeは`.leaf(view)`と`.split(Split)`の再帰的定義。

#### Step 2: ビュー層を理解する

SwiftUIによるスプリット表示の実装を理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SplitView.swift | `macos/Sources/Features/Splits/SplitView.swift` | SplitView構造体（8-69行目）、body変数 |
| 2-2 | SplitView.Divider.swift | `macos/Sources/Features/Splits/SplitView.Divider.swift` | ディバイダーの実装、ポインタースタイル |

**主要処理フロー**:
1. **38-68行目**: body - GeometryReaderで領域計算
2. **90-103行目**: dragGesture - ドラッグによるリサイズ
3. **106-122行目**: leftRect/rightRect計算

#### Step 3: ツリー表示を理解する

TerminalSplitTreeViewでのツリー再帰表示を理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TerminalSplitTreeView.swift | `macos/Sources/Features/Splits/TerminalSplitTreeView.swift` | ツリー全体の表示（28-45行目） |
| 3-2 | TerminalSplitTreeView.swift | `macos/Sources/Features/Splits/TerminalSplitTreeView.swift` | サブツリーの再帰表示（47-87行目） |

**主要処理フロー**:
- **54-86行目**: switch nodeで.leaf/.splitを分岐して再帰表示

#### Step 4: ドラッグ&ドロップを理解する

ペインの再配置機能を理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TerminalSplitTreeView.swift | `macos/Sources/Features/Splits/TerminalSplitTreeView.swift` | TerminalSplitLeaf（89-131行目）、DropDelegate（138-191行目） |
| 4-2 | TerminalSplitTreeView.swift | `macos/Sources/Features/Splits/TerminalSplitTreeView.swift` | TerminalSplitDropZone（194-257行目） |

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

```
SplitTree<Ghostty.SurfaceView>
    │
    ├─ root: Node?
    │      ├─ .leaf(view: Ghostty.SurfaceView)
    │      └─ .split(Split)
    │             ├─ direction: Direction (.horizontal/.vertical)
    │             ├─ ratio: Double (0.0-1.0)
    │             ├─ left: Node
    │             └─ right: Node
    │
    └─ zoomed: Node? (ズーム中のノード)

TerminalSplitTreeView
    │
    └─ TerminalSplitSubtreeView (再帰)
           ├─ .leaf → TerminalSplitLeaf
           │              └─ Ghostty.InspectableSurface
           │                     └─ DropDelegate
           │
           └─ .split → SplitView
                  ├─ left: TerminalSplitSubtreeView
                  ├─ right: TerminalSplitSubtreeView
                  └─ Divider
```

### データフロー図

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

スプリット作成要求 ───────▶ BaseTerminalController ────▶ SplitTree更新
                              │
                              ▼
                        newSplit()
                              │
                              ▼
                        SplitTree.inserting()
                              │
                              ▼
                        新しいSplitTree

ディバイダードラッグ ─────▶ DragGesture ─────────────────▶ split比率更新
                              │
                              ▼
                        performSplitAction(.resize)
                              │
                              ▼
                        Node.resizing()

ドラッグ&ドロップ ────────▶ DropDelegate ─────────────────▶ ツリー再構成
                              │
                              ▼
                        splitDidDrop()
                              │
                              ▼
                        SplitTree.removing() + inserting()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SplitTree.swift | `macos/Sources/Features/Splits/SplitTree.swift` | ソース | スプリット構造のデータモデル |
| SplitView.swift | `macos/Sources/Features/Splits/SplitView.swift` | ソース | 2ペイン分割のSwiftUIビュー |
| SplitView.Divider.swift | `macos/Sources/Features/Splits/SplitView.Divider.swift` | ソース | ディバイダーの実装 |
| TerminalSplitTreeView.swift | `macos/Sources/Features/Splits/TerminalSplitTreeView.swift` | ソース | ツリー全体の表示、ドロップ処理 |
| BaseTerminalController.swift | `macos/Sources/Features/Terminal/BaseTerminalController.swift` | ソース | スプリット操作のコントローラー |
