# 機能設計書 101-選択範囲書き出し

## 概要

本ドキュメントは、Ghosttyターミナルエミュレータにおける「選択範囲書き出し」機能(write_selection_file)の設計を記載する。ユーザーがターミナル上で選択したテキストを一時ファイルに保存し、クリップボードへのコピー、ターミナルへの貼り付け、またはOSのデフォルトアプリケーションで開く操作を提供する。

### 本機能の処理概要

選択範囲書き出し機能は、ターミナル画面上でユーザーがマウスまたはキーボードで選択したテキスト領域を一時ファイルとして書き出す機能である。

**業務上の目的・背景**：ターミナル作業において、出力結果の一部を外部ツールやエディタで利用したい場面がある。選択したテキストをファイルとして書き出すことで、他のアプリケーションとのデータ連携や、ログの部分的な保存、コード片のエディタでの編集といったワークフローを実現する。特にクリップボードにはファイルパスを格納することで、シェルスクリプトやコマンドライン操作との親和性が高い。

**機能の利用シーン**：
- ターミナル出力の一部を選択し、外部エディタで編集したい場合
- エラーログの特定部分をファイルとして保存し、共有したい場合
- 選択したコマンド出力をパイプ処理に利用したい場合
- HTMLやVT形式でスタイル付きの出力を保存したい場合

**主要な処理内容**：
1. ユーザーによるテキスト選択の取得(terminal.Selection)
2. 一時ディレクトリの作成(TempDir)
3. 選択テキストのフォーマット変換(ScreenFormatter)
4. 一時ファイルへの書き込み
5. 指定されたアクション(copy/paste/open)の実行

**関連システム・外部連携**：
- システムクリップボード(copyアクション時)
- PTY(pasteアクション時)
- OSのデフォルトエディタ/ビューア(openアクション時: macOSではopen、Linuxではxdg-open)

**権限による制御**：
- 読み取り専用モード(readonly)でも本機能は利用可能(PTYへの書き込みを伴うpaste以外)
- ファイル書き込み権限は一時ディレクトリに限定(mode 0o600)

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | メインターミナルウィンドウ | 主画面 | テキスト選択操作と書き出しアクションの実行 |

## 機能種別

ファイル出力 / データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| action | WriteScreen.Action | Yes | 書き出し後のアクション(copy/paste/open) | enumの有効値のみ |
| emit | WriteScreen.Format | Yes | 出力フォーマット(plain/vt/html) | enumの有効値のみ |

### 入力データソース

- **選択範囲**: terminal.Selection - ユーザーが画面上で選択した領域(ターミナル内部状態から取得)
- **カラーパレット**: terminal.colors - HTML形式出力時の色情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ファイルパス | []const u8 | 一時ファイルの絶対パス |
| ファイル内容 | テキスト | 選択されたテキスト(plain/vt/html形式) |
| ファイル拡張子 | .txt/.html | フォーマットに応じた拡張子 |

### 出力先

| アクション | 出力先 |
|-----------|--------|
| copy | システムクリップボード(ファイルパスを格納) |
| paste | PTY(ファイルパスを送信) |
| open | OSデフォルトエディタ(ファイルを開く) |

## 処理フロー

### 処理シーケンス

```
1. アクショントリガー
   └─ write_selection_fileアクションが発火

2. 選択範囲の確認
   └─ terminal.screens.active.selectionがnullの場合は早期リターン

3. 一時ディレクトリ作成
   └─ internal_os.TempDir.init()で一時ディレクトリを生成

4. ファイル名決定
   └─ "selection.txt" または "selection.html"(フォーマットによる)

5. ファイル作成・書き込み
   └─ mode 0o600でファイルを作成
   └─ ScreenFormatterを使用してテキストをフォーマット
   └─ バッファリングしてファイルに書き込み

6. アクション実行
   └─ copy: クリップボードにファイルパスを設定
   └─ paste: PTYにファイルパスを送信
   └─ open: OSのopen/xdg-openでファイルを開く
```

### フローチャート

```mermaid
flowchart TD
    A[write_selection_file発火] --> B{選択範囲あり?}
    B -->|No| C[処理終了]
    B -->|Yes| D[一時ディレクトリ作成]
    D --> E[ファイル名決定]
    E --> F[ファイル作成]
    F --> G[mutex取得]
    G --> H[選択範囲取得]
    H --> I[ScreenFormatter初期化]
    I --> J[テキストフォーマット]
    J --> K[ファイル書き込み]
    K --> L[mutex解放]
    L --> M{アクション}
    M -->|copy| N[クリップボードに設定]
    M -->|paste| O[PTYに送信]
    M -->|open| P[OSで開く]
    N --> Q[完了]
    O --> Q
    P --> Q
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-101 | 選択必須 | 選択範囲がない場合は何も行わない | 常時 |
| BR-102 | フォーマット形式 | plain/vtは.txt、htmlは.html拡張子 | 常時 |
| BR-103 | ファイルパーミッション | 一時ファイルは0o600で作成 | Unix/Linux環境 |

### 計算ロジック

なし

## データベース操作仕様

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

該当なし(データベースは使用しない)

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 選択なし | 選択範囲が存在しない | 早期リターン(エラーとせず正常終了) |
| FileError | ファイル作成失敗 | 一時ディレクトリへの書き込み権限がない | エラーをスローし上位で処理 |
| AllocError | メモリ確保失敗 | バッファ確保時のメモリ不足 | エラーをスローし上位で処理 |

### リトライ仕様

リトライは行わない。エラー発生時はユーザーに通知して処理を終了する。

## トランザクション仕様

- renderer_state.mutexによる排他制御を使用
- 選択範囲の取得とフォーマット処理はmutex保護下で実行
- ファイル書き込み後のアクション実行はmutex解放後に行う

## パフォーマンス要件

- 大量のテキスト選択時でもバッファリングにより効率的に書き込む(4096バイトバッファ)
- mutex保持時間を最小化するため、ファイルI/Oはバッファ経由

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

- 一時ファイルはmode 0o600で作成(所有者のみ読み書き可能)
- 一時ディレクトリはランダム名を使用(Base64エンコード、16バイト乱数)
- ファイルパスがクリップボードまたはPTYに露出する点に注意

## 備考

- write_scrollback_fileおよびwrite_screen_fileと共通のwriteScreenFile関数を使用
- TempDirはdeinit時に自動削除されるが、本機能ではファイル使用後も残る設計

---

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

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

### 推奨読解順序

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

まず、選択範囲とファイル書き出しのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Binding.zig | `src/input/Binding.zig` | WriteScreen構造体の定義(Action/Format enum) |
| 1-2 | Selection.zig | `src/terminal/Selection.zig` | 選択範囲を表すSelection構造体 |

**読解のコツ**: WriteScreenはaction(copy/paste/open)とemit(plain/vt/html)の2つのフィールドを持つ構造体。キーバインド設定では"write_selection_file:open"のように指定する。

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

処理の起点となるアクションハンドラを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Surface.zig | `src/Surface.zig` | performBindingAction関数内のwrite_selection_fileケース |

**主要処理フロー**:
1. **5571-5574行目**: write_selection_fileアクションの処理。writeScreenFile関数を.selectionロケーションで呼び出す

#### Step 3: ファイル書き出し処理を理解する

writeScreenFile関数の処理フローを追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Surface.zig | `src/Surface.zig` | writeScreenFile関数(5962-6088行目) |
| 3-2 | TempDir.zig | `src/os/TempDir.zig` | 一時ディレクトリの生成と管理 |
| 3-3 | formatter.zig | `src/terminal/formatter.zig` | ScreenFormatterによるテキストフォーマット |

**主要処理フロー**:
- **5968行目**: TempDir.init()で一時ディレクトリ作成
- **5985-5992行目**: ファイル作成(mode 0o600)
- **6001-6002行目**: renderer_state.mutexでロック取得
- **6033行目**: selection = self.io.terminal.screens.active.selectionで選択範囲取得
- **6036-6040行目**: 選択範囲がない場合は早期リターン
- **6043-6059行目**: ScreenFormatterで選択テキストをフォーマット
- **6067-6087行目**: アクションに応じた処理(copy/paste/open)

#### Step 4: アクション別の処理を理解する

各アクションの実行処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Surface.zig | `src/Surface.zig` | アクション別処理(6067-6087行目) |

**主要処理フロー**:
- **6068-6075行目**: copyアクション - setClipboardでファイルパスをクリップボードに設定
- **6076-6082行目**: openアクション - openUrl関数でOSのエディタを起動
- **6083-6086行目**: pasteアクション - queueIoでPTYにファイルパスを送信

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

```
Surface.performBindingAction
    │
    └─ .write_selection_file
           │
           └─ writeScreenFile(.selection, WriteScreen)
                  │
                  ├─ TempDir.init()
                  │
                  ├─ file.createFile()
                  │
                  ├─ [mutex lock]
                  │      │
                  │      ├─ screens.active.selection 取得
                  │      │
                  │      └─ ScreenFormatter.format()
                  │
                  └─ [アクション実行]
                         ├─ copy: rt_surface.setClipboard()
                         ├─ paste: queueIo(write_req)
                         └─ open: openUrl()
```

### データフロー図

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

terminal.Selection  ───▶ ScreenFormatter.format()  ───▶ 一時ファイル(.txt/.html)
                                │
                                │
カラーパレット     ───▶        │                    ───▶ クリップボード(copy)
                                │                         または
                                └──────────────────────▶ PTY(paste)
                                                          または
                                                          OSエディタ(open)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Surface.zig | `src/Surface.zig` | ソース | メイン処理。writeScreenFile関数を含む |
| Binding.zig | `src/input/Binding.zig` | ソース | WriteScreen構造体、Action enum定義 |
| Selection.zig | `src/terminal/Selection.zig` | ソース | 選択範囲のデータ構造 |
| TempDir.zig | `src/os/TempDir.zig` | ソース | 一時ディレクトリ管理 |
| formatter.zig | `src/terminal/formatter.zig` | ソース | テキストフォーマッタ(ScreenFormatter) |
| Screen.zig | `src/terminal/Screen.zig` | ソース | 画面バッファ管理、selection保持 |
