# 画面設計書 12-アップデートポップオーバー

## 概要

本ドキュメントは、Ghosttyアプリケーションのアップデートポップオーバーの設計仕様を定義する。アップデートポップオーバーは、Sparkleフレームワークを使用したアプリケーション更新プロセスの各段階（権限確認、チェック中、利用可能、ダウンロード中、展開中、インストール、エラー）に応じた適切なUIを表示するmacOS専用のポップオーバー画面である。

### 本画面の処理概要

アップデートポップオーバーは、アプリケーションの自動更新機能を視覚的にサポートし、ユーザーに更新プロセスの状態を通知する画面である。

**業務上の目的・背景**：ターミナルエミュレータの利用者が常に最新バージョンを使用できるよう、シームレスなアップデート体験を提供する。macOSアプリケーションの標準的な更新フローとして、Sparkleフレームワークを採用し、バックグラウンドでの自動更新チェック、手動チェック、ダウンロード、インストールの各段階でユーザーフレンドリーなUIを提供する。

**画面へのアクセス方法**：
1. メニューバーの「Check for Updates...」を選択
2. コマンドパレットからアップデート確認コマンドを実行
3. 自動更新チェックによる通知から表示
4. ターミナルウィンドウのアップデートバッジ/ピルをクリック

**主要な操作・処理内容**：
1. 自動更新許可の設定（初回起動時の権限リクエスト）
2. アップデート確認中の進捗表示とキャンセル
3. 新バージョン情報（バージョン番号、サイズ、リリース日）の表示
4. リリースノートへのリンク表示
5. 「Install and Relaunch」でダウンロード・インストール開始
6. 「Skip」で該当バージョンをスキップ
7. 「Later」で後で通知
8. ダウンロード進捗のパーセンテージ表示
9. 展開（Extracting）進捗の表示
10. 再起動要求と「Restart Now」/「Restart Later」の選択
11. エラー発生時のリトライまたは閉じる操作

**画面遷移**：
- 遷移元：メインターミナルウィンドウ（メニュー、コマンドパレット、バッジから）
- 遷移先：なし（ポップオーバー内で完結、インストール後はアプリ再起動）

**権限による表示制御**：権限による制御は存在しない。ただし、初回起動時にユーザーに自動更新の許可を求める。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 89 | アップデート確認 | 主機能 | アップデート確認、ダウンロード、インストール進行状況の表示 |

## 画面種別

ポップオーバー（状態遷移型）

## URL/ルーティング

macOSネイティブアプリケーションのため、URLルーティングは存在しない。UpdateViewModelの状態変更により表示内容が切り替わる。

## 入出力項目

| 項目名 | 入出力 | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| 自動更新許可応答 | 入力 | Bool | Yes | 自動更新を許可するかどうか |
| アップデート選択 | 入力 | Enum | Yes | Install/Skip/Later の選択 |
| 再起動選択 | 入力 | Enum | Yes | Restart Now/Restart Later の選択 |
| エラー対応選択 | 入力 | Enum | Yes | Retry/OK の選択 |

## 表示項目

### 権限リクエスト画面（PermissionRequestView）
| 項目名 | データ型 | ソース | 説明 |
|--------|----------|--------|------|
| タイトル | String | 固定値 | 「Enable automatic updates?」 |
| 説明文 | String | 固定値 | 「Ghostty can automatically check for updates in the background.」 |

### チェック中画面（CheckingView）
| 項目名 | データ型 | ソース | 説明 |
|--------|----------|--------|------|
| プログレスインジケータ | ProgressView | - | 回転インジケーター |
| メッセージ | String | 固定値 | 「Checking for updates...」 |

### アップデート利用可能画面（UpdateAvailableView）
| 項目名 | データ型 | ソース | 説明 |
|--------|----------|--------|------|
| タイトル | String | 固定値 | 「Update Available」 |
| バージョン | String | SUAppcastItem.displayVersionString | 新バージョン番号 |
| サイズ | String | SUAppcastItem.contentLength | ダウンロードサイズ（ByteCountFormatter使用） |
| リリース日 | String | SUAppcastItem.date | リリース日時 |
| リリースノートリンク | URL | ReleaseNotes.url | GitHub/ghostty.orgへのリンク |

### ダウンロード中画面（DownloadingView）
| 項目名 | データ型 | ソース | 説明 |
|--------|----------|--------|------|
| タイトル | String | 固定値 | 「Downloading Update」 |
| プログレスバー | Double | download.progress / download.expectedLength | 0.0〜1.0の進捗 |
| パーセンテージ | String | 計算値 | 「XX%」形式 |

### 展開中画面（ExtractingView）
| 項目名 | データ型 | ソース | 説明 |
|--------|----------|--------|------|
| タイトル | String | 固定値 | 「Preparing Update」 |
| プログレスバー | Double | extracting.progress | 0.0〜1.0の進捗 |
| パーセンテージ | String | 計算値 | 「XX%」形式 |

### インストール画面（InstallingView）
| 項目名 | データ型 | ソース | 説明 |
|--------|----------|--------|------|
| タイトル | String | 固定値 | 「Restart Required」 |
| 説明文 | String | 固定値 | 「The update is ready. Please restart the application to complete the installation.」 |

### 最新版画面（NotFoundView）
| 項目名 | データ型 | ソース | 説明 |
|--------|----------|--------|------|
| タイトル | String | 固定値 | 「No Updates Found」 |
| 説明文 | String | 固定値 | 「You're already running the latest version.」 |

### エラー画面（UpdateErrorView）
| 項目名 | データ型 | ソース | 説明 |
|--------|----------|--------|------|
| アイコン | SF Symbol | 固定値 | 「exclamationmark.triangle.fill」（オレンジ） |
| タイトル | String | 固定値 | 「Update Failed」 |
| エラー内容 | String | error.localizedDescription | エラー詳細 |

## イベント仕様

### 1-自動更新許可（Allow）

PermissionRequestViewの「Allow」ボタン押下で、SUUpdatePermissionResponse(automaticUpdateChecks: true)を返し、ポップオーバーを閉じる。

### 2-自動更新拒否（Not Now）

PermissionRequestViewの「Not Now」ボタン押下で、SUUpdatePermissionResponse(automaticUpdateChecks: false)を返し、ポップオーバーを閉じる。

### 3-チェックキャンセル

CheckingViewの「Cancel」ボタン押下で、checking.cancel()を呼び出し、ポップオーバーを閉じる。

### 4-インストール開始（Install and Relaunch）

UpdateAvailableViewの「Install and Relaunch」ボタン押下で、update.reply(.install)を呼び出し、ダウンロードを開始。

### 5-バージョンスキップ（Skip）

UpdateAvailableViewの「Skip」ボタン押下で、update.reply(.skip)を呼び出し、該当バージョンを今後表示しない。

### 6-後で通知（Later）

UpdateAvailableViewの「Later」ボタン押下で、update.reply(.dismiss)を呼び出し、ポップオーバーを閉じる。

### 7-ダウンロードキャンセル

DownloadingViewの「Cancel」ボタン押下で、download.cancel()を呼び出し、ポップオーバーを閉じる。

### 8-即座に再起動（Restart Now）

InstallingViewの「Restart Now」ボタン押下で、installing.retryTerminatingApplication()を呼び出し、アプリケーションを再起動。

### 9-後で再起動（Restart Later）

InstallingViewの「Restart Later」ボタン押下で、installing.dismiss()を呼び出し、ポップオーバーを閉じる。次回アプリ終了時に更新適用。

### 10-エラー確認（OK）

UpdateErrorViewの「OK」ボタン押下で、error.dismiss()を呼び出し、ポップオーバーを閉じる。

### 11-エラーリトライ（Retry）

UpdateErrorViewの「Retry」ボタン押下で、error.retry()を呼び出し、更新処理を再試行。

### 12-リリースノートリンク

UpdateAvailableViewのリリースノートリンククリックで、外部ブラウザでリリースノートページを開く。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| なし | - | - | データベース操作なし（Sparkle内部で設定を管理） |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-01 | 情報 | Enable automatic updates? | 初回起動時 |
| MSG-02 | 進捗 | Checking for updates... | チェック中 |
| MSG-03 | 情報 | Update Available | 新バージョン検出時 |
| MSG-04 | 進捗 | Downloading Update | ダウンロード中 |
| MSG-05 | 進捗 | Preparing Update | 展開中 |
| MSG-06 | 確認 | Restart Required | インストール準備完了時 |
| MSG-07 | 情報 | No Updates Found | 最新版使用中 |
| MSG-08 | エラー | Update Failed | エラー発生時 |

## 例外処理

| 例外状態 | 画面の振る舞い |
|---------|---------------|
| ネットワークエラー | UpdateErrorViewを表示、Retry/OKボタン |
| ダウンロード中断 | UpdateErrorViewを表示、Retry/OKボタン |
| 展開エラー | UpdateErrorViewを表示、Retry/OKボタン |
| インストール失敗 | UpdateErrorViewを表示、Retry/OKボタン |
| サーバー応答なし | UpdateErrorViewを表示、タイムアウトメッセージ |

## 備考

- macOS専用画面（Sparkleフレームワークを使用するため、GTK版には対応画面なし）
- ポップオーバー幅は300px固定（51行目の.frame(width: 300)）
- 各状態ビューは独立したSwiftUIビューとして実装
- リリースノートはセマンティックバージョン（x.y.z）とGitハッシュの両方をサポート
- セマンティックバージョンの場合はghostty.org/docs/install/release-notesへリンク
- Gitハッシュの場合はGitHubのコミット比較またはコミット詳細へリンク

---

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

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

### 推奨読解順序

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

UpdateViewModelとUpdateState enumが画面状態と表示データを管理する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | UpdateViewModel.swift | `macos/Sources/Features/Update/UpdateViewModel.swift` | UpdateState enum（177-372行目）の各ケースと付随データ構造 |

**読解のコツ**: UpdateStateの各ケース（idle, permissionRequest, checking, updateAvailable, downloading, extracting, installing, notFound, error）が画面状態に1:1対応する。各ケースのassociated valueが表示データを保持する。

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

UpdatePopoverViewがSwitch文で状態に応じたビューを表示する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | bodyのswitch文（17-49行目）で状態別ビューを分岐 |

**主要処理フロー**:
1. **8-10行目**: @ObservedObjectでUpdateViewModelを監視
2. **17-49行目**: model.stateに応じて適切なサブビューを表示
3. **51行目**: 固定幅300pxのVStack

#### Step 3: 各状態ビューを理解する

状態ごとに専用のサブビューが実装されている。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | PermissionRequestView（55-94行目）- 初回許可 |
| 3-2 | UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | CheckingView（96-121行目）- チェック中 |
| 3-3 | UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | UpdateAvailableView（123-218行目）- 利用可能 |
| 3-4 | UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | DownloadingView（220-256行目）- ダウンロード中 |
| 3-5 | UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | ExtractingView（258-275行目）- 展開中 |
| 3-6 | UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | InstallingView（277-314行目）- インストール |
| 3-7 | UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | NotFoundView（316-344行目）- 最新版 |
| 3-8 | UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | UpdateErrorView（346-387行目）- エラー |

**主要処理フロー**:
- **140-141行目**: appcastItem.displayVersionStringでバージョン表示
- **150行目**: ByteCountFormatterでサイズをフォーマット
- **231行目**: ダウンロード進捗を0-1の範囲で計算
- **267行目**: 展開進捗を表示

#### Step 4: リリースノートのURL生成を理解する

ReleaseNotes enumがバージョン形式に応じてURLを生成する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | UpdateViewModel.swift | `macos/Sources/Features/Update/UpdateViewModel.swift` | ReleaseNotes enum（285-348行目）のURL生成ロジック |

**主要処理フロー**:
- **294-300行目**: セマンティックバージョンの場合、ghostty.orgへリンク
- **303-314行目**: Gitハッシュの場合、GitHubへリンク

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

```
UpdatePopoverView
    │
    ├─ @ObservedObject UpdateViewModel
    │      └─ @Published state: UpdateState
    │
    └─ switch model.state
           │
           ├─ .permissionRequest ─▶ PermissionRequestView
           │                              └─ request.reply(SUUpdatePermissionResponse)
           │
           ├─ .checking ─▶ CheckingView
           │                    └─ checking.cancel()
           │
           ├─ .updateAvailable ─▶ UpdateAvailableView
           │                            ├─ update.reply(.install)
           │                            ├─ update.reply(.skip)
           │                            ├─ update.reply(.dismiss)
           │                            └─ Link → ReleaseNotes.url
           │
           ├─ .downloading ─▶ DownloadingView
           │                       └─ download.cancel()
           │
           ├─ .extracting ─▶ ExtractingView
           │
           ├─ .installing ─▶ InstallingView
           │                      ├─ installing.retryTerminatingApplication()
           │                      └─ installing.dismiss()
           │
           ├─ .notFound ─▶ NotFoundView
           │                    └─ notFound.acknowledgement()
           │
           └─ .error ─▶ UpdateErrorView
                              ├─ error.retry()
                              └─ error.dismiss()
```

### データフロー図

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

Sparkle                       UpdateViewModel                     UpdatePopoverView
SUUpdater          ───▶       - state更新           ───▶          - 状態別ビュー表示
- checkForUpdates             - text生成                          - ボタン表示
- downloadUpdate              - iconName生成                      - プログレス表示
- installUpdate               - badge生成

User Actions                  UpdateState                         Sparkle
- button tap       ───▶       - reply closure        ───▶         - ダウンロード実行
- link click                  - cancel closure                    - インストール実行
                              - retry closure                     - 再起動実行
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| UpdatePopoverView.swift | `macos/Sources/Features/Update/UpdatePopoverView.swift` | ソース | ポップオーバービュー（SwiftUI） |
| UpdateViewModel.swift | `macos/Sources/Features/Update/UpdateViewModel.swift` | ソース | 状態管理とデータ変換 |
| UpdateController.swift | `macos/Sources/Features/Update/UpdateController.swift` | ソース | ポップオーバー表示制御 |
| UpdateDelegate.swift | `macos/Sources/Features/Update/UpdateDelegate.swift` | ソース | Sparkleデリゲート |
| UpdateDriver.swift | `macos/Sources/Features/Update/UpdateDriver.swift` | ソース | Sparkleドライバー |
| UpdatePill.swift | `macos/Sources/Features/Update/UpdatePill.swift` | ソース | 通知ピルUI |
| UpdateBadge.swift | `macos/Sources/Features/Update/UpdateBadge.swift` | ソース | 通知バッジUI |
| UpdateSimulator.swift | `macos/Sources/Features/Update/UpdateSimulator.swift` | ソース | デバッグ用シミュレーター |
