# 機能設計書 122-マークダウンペイン

## 概要

本ドキュメントは、Windows Terminalにおけるマークダウンペイン機能の設計を記述する。マークダウンペインは、Markdownファイルをペイン内で読み込み・表示・編集し、コードブロックの実行もサポートする実験的機能である。

### 本機能の処理概要

マークダウンペインは、指定されたファイルパスからMarkdownファイルを読み込み、ペイン内でレンダリングして表示する機能である。テキストファイルの場合はプレーンテキストとして表示し、.mdファイルの場合はMarkdownとしてパース・レンダリングする。さらに、コードブロック内のコマンドをターミナルに送信して実行する機能も備える。

**業務上の目的・背景**：開発者がターミナル作業中にMarkdownドキュメント（README、手順書、チートシートなど）を参照できるようにすることが目的である。また、コードブロックの実行機能により、ドキュメント内のコマンド例をワンクリックでターミナルに送信できるため、手順書の運用を効率化する。本機能はMarkdownパース機能の実験的検証も兼ねている。

**機能の利用シーン**：DevビルドおよびCanaryビルドにおいて、開発者がMarkdownドキュメントを参照しながらターミナル作業を行う場合に利用される。手順書のコマンドをそのまま実行したい場合にも有用である。Feature_MarkdownPaneフラグにより制御されるため、一般リリース版では無効化されている。

**主要な処理内容**：
1. ファイルパスの指定とMarkdownファイルの読み込み
2. ファイル拡張子に基づくレンダリング方式の切り替え（.md → Markdownレンダリング、その他 → プレーンテキスト）
3. Markdown文書のパースとXAML UIツリーへの変換（Microsoft.Terminal.UI.Markdown.Builder）
4. コードブロックへの実行ボタン表示と、コマンドのターミナルへの送信
5. 編集モードの切り替え（プレビュー表示 / テキスト編集）
6. ファイルの再読み込み

**関連システム・外部連携**：ターミナルペインとの連携があり、コードブロック実行時にはSendInputアクションを通じてアクティブなTermControlにコマンドを送信する。Microsoft.Terminal.UI.Markdown名前空間のMarkdownパーサーライブラリに依存する。

**権限による制御**：Feature_MarkdownPaneフィーチャーフラグにより、DevビルドおよびCanaryビルドでのみ有効化される。一般リリース版（Release/Preview）では常に無効化されている。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | MarkdownPaneContent（Markdownビューア） | 主画面 | Markdownファイルの読み込み・レンダリング・コードブロック実行 |

## 機能種別

UI表示 / ファイル読み込み / コマンド実行連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| initialPath | hstring | No | 初期表示するファイルパス。空文字列の場合はファイルを読み込まない | ファイルの存在確認（CreateFileWで検証） |

### 入力データソース

- ファイルシステム：指定パスのMarkdownファイル（til::io::read_file_as_utf8_string_if_existsで読み込み）
- UI入力：FilePathInputテキストボックスからのファイルパス入力
- ユーザー操作：Load/Edit/Closeボタンのタップイベント

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ペインUI | FrameworkElement（UserControl） | ファイルパス入力欄、操作ボタン、レンダリング済みMarkdown/テキスト |
| Title | hstring | 現在読み込んでいるファイルのパス |
| Icon | hstring | QuickNoteアイコン（\xe70b） |
| GetNewTerminalArgs | INewContentArgs | BaseContentArgs("x-markdown") |
| DispatchActionRequested | イベント | コードブロック実行時にSendInputアクションを発火 |

### 出力先

- TerminalPageのペインツリーに新しいペインとして追加される
- コードブロック実行時は、最後にアクティブだったTermControlにコマンドが送信される

## 処理フロー

### 処理シーケンス

```
1. TerminalPage::_MakePane でペインタイプ "x-markdown" を検出
   └─ Feature_MarkdownPane::IsEnabled()でフィーチャーフラグを確認
2. MarkdownPaneContentインスタンスを生成
   └─ コンストラクタでInitializeComponent()を呼び出しXAMLを初期化
3. ファイルパスが指定されている場合、_loadFile()を呼び出し
   └─ til::io::read_file_as_utf8_string_if_existsでファイル読み込み
4. _renderFileContents()でファイル拡張子に応じたレンダリング
   └─ .md: _loadMarkdown() → Microsoft.Terminal.UI.Markdown.Builder::Convert
   └─ その他: _loadText() → TextBlockにプレーンテキスト表示
5. コードブロックにPlayButton表示を設定
   └─ RequestRunCommandsイベントハンドラを登録
6. ユーザーがコードブロック実行ボタンを押下
   └─ _handleRunCommandRequest → SendInputアクション → DispatchActionRequested発火
```

### フローチャート

```mermaid
flowchart TD
    A["x-markdown" ペイン生成要求] --> B{Feature_MarkdownPane::IsEnabled?}
    B -->|No| C[何もしない]
    B -->|Yes| D[MarkdownPaneContent生成]
    D --> E[InitializeComponent XAML初期化]
    E --> F{ファイルパスが空?}
    F -->|Yes| G[何も表示しない]
    F -->|No| H[_loadFile でファイル読み込み]
    H --> I{拡張子が .md?}
    I -->|Yes| J[_loadMarkdown: Markdownレンダリング]
    I -->|No| K[_loadText: プレーンテキスト表示]
    J --> L[コードブロックにPlayButton設定]
    L --> M[RenderedMarkdownに追加]
    K --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-122-01 | フィーチャーフラグ制御 | Feature_MarkdownPaneが有効な場合のみペインを作成する | ペイン生成時 |
| BR-122-02 | 拡張子判定 | .mdファイルはMarkdownとしてレンダリング、それ以外はプレーンテキストとして表示 | _renderFileContents実行時 |
| BR-122-03 | タイトルはファイルパス | ペインのタイトルは読み込んだファイルのパスとする | 常時 |
| BR-122-04 | コードブロック実行 | コードブロック内のテキストをSendInputアクションとして最後にアクティブだったTermControlに送信 | PlayButton押下時 |
| BR-122-05 | ファイル存在確認 | Loadボタン押下時にCreateFileWでファイルの存在を確認し、存在しない場合は読み込まない | _loadTapped実行時 |
| BR-122-06 | 編集モード切替 | Editボタンでプレビュー表示とテキスト編集モードを切り替え、アイコンも連動して変更する | _editTapped実行時 |

### 計算ロジック

計算ロジックは存在しない。

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

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

データベース操作は存在しない。ファイルシステムからの読み取りのみ行う。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| なし | ファイル不存在 | 指定パスにファイルが存在しない場合 | _loadTappedでCreateFileWが失敗し、処理を中断（サイレント） |
| なし | 空パス | ファイルパスが空文字列の場合 | _loadFileで早期リターン |
| なし | フィーチャー無効 | Feature_MarkdownPaneが無効な場合 | ペイン生成をスキップ |

### リトライ仕様

リトライが必要な処理は存在しない。ファイルが見つからない場合は再度パスを入力してLoadボタンを押すことで再試行できる。

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

トランザクション処理は存在しない。

## パフォーマンス要件

Markdownのパースとレンダリングは同期的に行われる。大規模なMarkdownファイルの場合、UIスレッドでのレンダリング処理に時間がかかる可能性がある。

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

- ファイルシステムからの読み取りアクセスを行うため、アクセス権限の範囲内でのみ動作する
- コードブロック実行機能により、ドキュメント内のコマンドがターミナルに送信されるため、信頼できないMarkdownファイルを開いた場合に意図しないコマンドが実行されるリスクがある（ただしユーザーが明示的にPlayButtonを押す必要がある）

## 備考

- 本機能はDevビルドおよびCanaryビルド限定の実験的機能である
- features.xmlにおけるFeature IDは16495である
- ファイル読み込みはUTF-8エンコーディングで行われる（til::io::read_file_as_utf8_string_if_exists）
- 編集モードではFileContentsプロパティがTwoWayバインディングされている
- Markdownレンダリングには「Cascadia Code」フォントファミリが使用される（プレーンテキスト表示時）
- ダーク/ライト/ハイコントラストのテーマに対応した背景色を持つ

---

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

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

### 推奨読解順序

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

IDL定義とヘッダーファイルからクラスの全体像を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | MarkdownPaneContent.idl | `src/cascadia/TerminalApp/MarkdownPaneContent.idl` | WinRTインターフェース定義。IPaneContent実装、Editing/FileContentsプロパティ、DispatchActionRequestedイベント |
| 1-2 | MarkdownPaneContent.h | `src/cascadia/TerminalApp/MarkdownPaneContent.h` | C++クラス定義。til::propertyによるプロパティ定義、private メソッド群、_controlの弱参照 |
| 1-3 | BasicPaneEvents.h | `src/cascadia/TerminalApp/BasicPaneEvents.h` | ペインの標準イベント定義 |

**読解のコツ**: `til::property<T>` はWindows Terminal独自のプロパティヘルパーで、getter/setterを自動生成する。`til::typed_event` はイベントのヘルパー。`winrt::weak_ref` はWinRTの弱参照で、循環参照を防ぐために使用される。

#### Step 2: XAML UIレイアウトを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MarkdownPaneContent.xaml | `src/cascadia/TerminalApp/MarkdownPaneContent.xaml` | UIレイアウト定義。2行構成（ファイルパス入力+ボタン行、コンテンツ行） |

**主要処理フロー**:
1. **39-44行目**: Rootグリッドの行定義（Auto + *）
2. **46-84行目**: 上部の操作バー。FilePathInput TextBox、Loadボタン（_loadTapped）、Editボタン（_editTapped）、Closeボタン（_closeTapped）
3. **87-135行目**: コンテンツ領域。_editor（TextBox、編集モード用）と_scrollViewer（プレビュー表示用、RenderedMarkdown StackPanelを含む）
4. **15-37行目**: テーマリソース定義。Dark/Light/HighContrastの背景色・PlayButton色

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | _MakePaneでの "x-markdown" 分岐処理 |

**主要処理フロー**:
- **3693-3707行目**: "x-markdown" ペインタイプの処理。Feature_MarkdownPane::IsEnabled()確認後、MarkdownPaneContentを生成し、DispatchActionRequestedイベントを登録
- **3697行目**: MarkdownPaneContent(L"")で空パスで初期化
- **3703行目**: DispatchActionRequestedイベントで_actionDispatch->DoActionを呼び出す

#### Step 4: 実装の詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | MarkdownPaneContent.cpp | `src/cascadia/TerminalApp/MarkdownPaneContent.cpp` | 全実装。ファイル読み込み、レンダリング、コードブロック実行、編集モード切替 |

**主要処理フロー**:
- **24-34行目**: コンストラクタ。InitializeComponent()でXAML初期化、FilePathInputにパスを設定、_loadFile()を呼び出し
- **41-44行目**: _clearOldNotebook()。RenderedMarkdownの子要素をクリア
- **45-64行目**: _loadFile()。空パスチェック、TitleChangedイベント発火、ファイルをUTF-8で読み込み、Editing=falseに設定、_renderFileContents()を呼び出し
- **65-76行目**: _renderFileContents()。拡張子が.mdなら_loadMarkdown()、それ以外は_loadText()
- **77-86行目**: _loadText()。TextBlockを生成し、Cascadia Codeフォント・テキスト選択有効で表示
- **88-113行目**: _loadMarkdown()。Builder::Convertでパース、全コードブロックにPlayButtonVisibility(Visible)を設定し、RequestRunCommandsイベントを登録
- **115-128行目**: _loadTapped()。CreateFileWでファイル存在確認後、クリアして再読み込み
- **130-154行目**: _editTapped()。Editingフラグのトグル、ScrollViewerと_editorのVisibility切替、アイコン変更
- **161-176行目**: _handleRunCommandRequest()。コードブロックのテキストをSendInputアクションとしてDispatchActionRequestedで発火

#### Step 5: フィーチャーフラグを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | features.xml | `src/features.xml` | Feature_MarkdownPaneの定義。ID=16495、AlwaysDisabledだがDevとCanaryでは有効 |

**主要処理フロー**:
- **146-153行目**: Feature_MarkdownPaneの定義ブロック

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

```
TerminalPage::_MakePane (TerminalPage.cpp:3693)
    |
    +-- Feature_MarkdownPane::IsEnabled()  (features.xml生成コード)
    |
    +-- winrt::make_self<MarkdownPaneContent>(L"")
    |       |
    |       +-- MarkdownPaneContent::MarkdownPaneContent()  (MarkdownPaneContent.cpp:24)
    |               |
    |               +-- InitializeComponent()  (XAML初期化)
    |               +-- _loadFile()
    |                       |
    |                       +-- til::io::read_file_as_utf8_string_if_exists()
    |                       +-- _renderFileContents()
    |                               |
    |                               +-- _loadMarkdown()  (.md の場合)
    |                               |       |
    |                               |       +-- Markdown::Builder::Convert()
    |                               |       +-- CodeBlock::PlayButtonVisibility(Visible)
    |                               |       +-- CodeBlock::RequestRunCommands += _handleRunCommandRequest
    |                               |
    |                               +-- _loadText()  (その他の場合)
    |
    +-- DispatchActionRequested イベント登録
            |
            +-- _actionDispatch->DoAction()
                    |
                    +-- SendInput アクション (TermControl へ)
```

### データフロー図

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

ファイルパス            ───>  _loadFile()                    ───>  RenderedMarkdown
(FilePathInput)               |                                    (StackPanel)
                               +-- read_file_as_utf8_string
                               +-- _renderFileContents
                                   |
                                   +-- _loadMarkdown             ───> Markdown UI
                                   |   (Builder::Convert)              (RichTextBlock)
                                   |
                                   +-- _loadText                 ───> TextBlock
                                       (プレーンテキスト)

PlayButton押下          ───>  _handleRunCommandRequest      ───>  SendInputアクション
(CodeBlock)                     |                                    |
                                +-- DispatchActionRequested    ───>  TermControl.SendInput()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| MarkdownPaneContent.idl | `src/cascadia/TerminalApp/MarkdownPaneContent.idl` | ソース | WinRTインターフェース定義 |
| MarkdownPaneContent.h | `src/cascadia/TerminalApp/MarkdownPaneContent.h` | ソース | クラスヘッダー定義 |
| MarkdownPaneContent.cpp | `src/cascadia/TerminalApp/MarkdownPaneContent.cpp` | ソース | 実装（ファイル読み込み、レンダリング、コードブロック実行） |
| MarkdownPaneContent.xaml | `src/cascadia/TerminalApp/MarkdownPaneContent.xaml` | XAML | UIレイアウト定義 |
| BasicPaneEvents.h | `src/cascadia/TerminalApp/BasicPaneEvents.h` | ソース | ペインの標準イベント定義 |
| TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | ソース | "x-markdown" ペインの生成とイベント登録 |
| features.xml | `src/features.xml` | 設定 | Feature_MarkdownPaneフィーチャーフラグ定義 |
