# 画面設計書 20-CouldNotOpenUriDialog（URI エラー）

## 概要

本ドキュメントは、Windows Terminal の CouldNotOpenUriDialog（URI エラー）画面の設計仕様を記述する。CouldNotOpenUriDialog は、ハイパーリンクのURIを開けなかった場合にエラー理由を表示するダイアログである。

### 本画面の処理概要

CouldNotOpenUriDialog は、ターミナル出力内のハイパーリンクをクリックした際に、そのURIを開くことができなかった場合にエラーの理由と対象URIを表示するダイアログである。無効なURIやサポートされていないスキームの場合に表示される。

**業務上の目的・背景**：ターミナル出力にはOSC 8エスケープシーケンスによるハイパーリンクが含まれることがある。これらのリンクをクリックした際に、URIが無効であったりサポートされていないスキームである場合、ユーザーに理由を明示する必要がある。セキュリティ上の理由からサポート対象のURIスキームが制限されているため、その説明も兼ねている。

**画面へのアクセス方法**：TerminalPage の `_OpenHyperlinkHandler` が呼び出された際に、URIの検証に失敗した場合またはサポートされていないスキームである場合に表示される（TerminalPage.cpp 行3055-3074）。ユーザーが直接開く操作はない。

**主要な操作・処理内容**：
1. エラー理由の表示：URI を開けなかった理由（無効なURI / サポートされていないスキーム）を表示する
2. 対象URIの表示：開こうとしたURIを等幅フォント（Cascadia Mono）で表示する
3. 確認（Primary ボタン）：「Cancel」ボタンでダイアログを閉じる

**画面遷移**：TerminalPage のハイパーリンクオープン操作からダイアログが自動表示される。Primary ボタンで閉じて TerminalPage に戻る。

**権限による表示制御**：特になし。全ユーザーに対して表示される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 116 | 作業ディレクトリを開く | 補助機能 | URIを開けなかった場合のエラー表示 |

## 画面種別

ダイアログ（ContentDialog - インライン定義）

## URL/ルーティング

該当なし（TerminalPage.xaml 内にインライン定義される ContentDialog）

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|-----|------|------|
| reason | 入力 | hstring | Yes | URI を開けなかった理由 |
| uri | 入力 | hstring | Yes | 開こうとした URI 文字列 |

## 表示項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| CouldNotOpenUriReason | Run（TextBlock内） | エラー理由テキスト |
| UnopenedUri | Run（TextBlock内） | 開けなかったURI（Cascadia Mono フォント） |
| PrimaryButtonText | ボタンラベル | "Cancel" |

## イベント仕様

### 1-ハイパーリンクオープンエラー（_OpenHyperlinkHandler）

TerminalPage::_OpenHyperlinkHandler（TerminalPage.cpp 行3055-3074）でハイパーリンクの URI を検証する：
- URI のパースに成功し、`_IsUriSupported` で検証に合格した場合：`ShellExecute` で URI を開く
- URI のスキームがサポートされていない場合：`_ShowCouldNotOpenDialog(RS_(L"UnsupportedSchemeText"), eventArgs.Uri())` でダイアログ表示（行3066）
- URI のパースに例外が発生した場合：`_ShowCouldNotOpenDialog(RS_(L"InvalidUriText"), eventArgs.Uri())` でダイアログ表示（行3072）

### 2-ダイアログ表示（_ShowCouldNotOpenDialog）

`_ShowCouldNotOpenDialog`（TerminalPage.cpp 行3081-3095）で以下の処理が行われる：
1. `FindName(L"CouldNotOpenUriDialog")` でダイアログを遅延読み込み（行3086）
2. `CouldNotOpenUriReason().Text(reason)` でエラー理由を設定（行3089）
3. `UnopenedUri().Text(uri)` で対象URIを設定（行3090）
4. `presenter.ShowDialog(unopenedUriDialog)` でダイアログを表示（行3093）

### 3-サポート対象URIスキームの判定（_IsUriSupported）

`_IsUriSupported`（TerminalPage.cpp 行3103-3138）で以下のスキームがサポートされる：
- **http / https**: 常にサポート
- **file**: ホスト名なし、"wsl$"、"wsl.localhost" の場合のみサポート
- **その他**: OSC 8 で明示的に指定されたスキームはサポート（ユーザーが意図的に設定したと判断）

### 4-Primary ボタン（Cancel）

ユーザーが「Cancel」ボタンをクリックするとダイアログが閉じる。

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

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

該当なし。本画面はデータベースを操作しない。

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|----------|---------------|
| UnsupportedSchemeText | エラー理由 | サポートされていないスキーム | "This link type is currently not supported:" |
| InvalidUriText | エラー理由 | 無効なURI | "This link is invalid:" |
| CouldNotOpenUriDialog.PrimaryButtonText | ボタンラベル | 常時 | "Cancel" |

## 例外処理

- URI パース例外：try-catch で `LOG_CAUGHT_EXCEPTION()` によりログ出力後、InvalidUriText 理由でダイアログを表示する（TerminalPage.cpp 行3069-3073）
- DialogPresenter が存在しない場合：`_dialogPresenter.get()` の null チェックにより、ダイアログ表示がスキップされる（行3083）

## 備考

- DefaultButton は Primary に設定されている（TerminalPage.xaml 行151）
- x:Load="False" により遅延読み込みされる（TerminalPage.xaml 行150）
- エラー理由とURIは別の Run 要素として表示され、改行（LineBreak）で区切られる（TerminalPage.xaml 行157）
- URI は Cascadia Mono フォントで表示される（TerminalPage.xaml 行159）
- テキストは選択可能（IsTextSelectionEnabled="True"）でコンテキストメニューからコピー可能
- PrimaryButtonText が "Cancel" であり、肯定的なアクションは提供されない（情報表示のみ）
- GH#10188 に基づき、WSL パス（wsl$ / wsl.localhost）は file スキームとしてサポートされている

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | TerminalPage.xaml | `src/cascadia/TerminalApp/TerminalPage.xaml` | CouldNotOpenUriDialog の XAML 定義（行147-161）。TextBlock 内に CouldNotOpenUriReason（Run）+ LineBreak + UnopenedUri（Run, Cascadia Mono）の構成を確認 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | `_OpenHyperlinkHandler`（行3055-3074）でURIの検証フロー。`_IsUriSupported`（行3103-3138）でサポート対象スキームの判定ロジックを確認 |
| 2-2 | TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | `_ShowCouldNotOpenDialog`（行3081-3095）でダイアログの動的テキスト設定と表示。FindName による遅延読み込み、reason と uri の設定を確認 |

**主要処理フロー**:
1. **行3055**: `_OpenHyperlinkHandler` がハイパーリンククリックから呼び出される
2. **行3059**: `winrt::Windows::Foundation::Uri` で URI をパース
3. **行3060**: `_IsUriSupported` でスキーム検証
4. **行3062**: サポート対象 → `ShellExecute` で開く
5. **行3066**: 非サポート → `_ShowCouldNotOpenDialog("UnsupportedSchemeText", uri)`
6. **行3072**: パース例外 → `_ShowCouldNotOpenDialog("InvalidUriText", uri)`

#### Step 3: URI サポート判定の詳細

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | `_IsUriSupported`（行3103-3138）の判定ロジック。http/https は常にサポート、file スキームはホスト名条件付き、その他のスキームは OSC 8 で明示的に指定されたものとして信頼 |

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

```
TermControl::OpenHyperlink イベント
    |
    +-- TerminalPage::_OpenHyperlinkHandler (TerminalPage.cpp:3055)
            |
            +-- Windows::Foundation::Uri パース
            |       |
            |       +-- [成功] _IsUriSupported (行3103)
            |       |       |
            |       |       +-- [サポート] ShellExecute (URI を開く)
            |       |       +-- [非サポート] _ShowCouldNotOpenDialog
            |       |                       ("UnsupportedSchemeText", uri)
            |       |
            |       +-- [例外] _ShowCouldNotOpenDialog
            |                   ("InvalidUriText", uri)
            |
            +-- _ShowCouldNotOpenDialog (行3081)
                    |
                    +-- FindName("CouldNotOpenUriDialog")
                    +-- CouldNotOpenUriReason().Text(reason)
                    +-- UnopenedUri().Text(uri)
                    +-- DialogPresenter::ShowDialog
```

### データフロー図

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

TermControl                TerminalPage                        CouldNotOpenUriDialog
  OpenHyperlink        --> _OpenHyperlinkHandler          --> ダイアログ表示
    URI                --> Uri パース                         |
                            |                                  +-- CouldNotOpenUriReason
                            +-- _IsUriSupported               |    (エラー理由テキスト)
                            |   (スキーム検証)                 |
                            |                                  +-- UnopenedUri
                            +-- [エラー時]                         (対象URI, 等幅フォント)
                                _ShowCouldNotOpenDialog
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| TerminalPage.xaml | `src/cascadia/TerminalApp/TerminalPage.xaml` | ソース（XAML） | CouldNotOpenUriDialog のインライン定義（行147-161） |
| TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | ソース（実装） | _OpenHyperlinkHandler / _IsUriSupported / _ShowCouldNotOpenDialog の実装 |
| Resources.resw | `src/cascadia/TerminalApp/Resources/en-US/Resources.resw` | リソース | エラー理由・ボタンテキストの文字列リソース（行666-673） |
