# 画面設計書 4-Index（メイン画面）

## 概要

本ドキュメントは、Template Explorer のメイン画面（index.html + src/index.ts）の画面設計書である。Vue テンプレートのコンパイル結果をリアルタイムに確認できる2パネルエディタ画面であり、左パネルにテンプレート入力、右パネルにコンパイル結果（JavaScript レンダリング関数）を表示する。Monaco Editor を使用し、SourceMap による双方向の同期ハイライト機能を備える。

### 本画面の処理概要

**業務上の目的・背景**：Vue.js のテンプレートコンパイラ（@vue/compiler-dom / @vue/compiler-ssr）が、テンプレート文字列からどのような JavaScript コード（レンダリング関数）を生成するかを視覚的に確認できるツールである。Vue のコンパイラ開発者やフレームワーク理解を深めたい開発者が、テンプレート構文と生成コードの対応関係をリアルタイムに把握することを目的とする。コンパイラオプションの変更による出力差異の確認や、コンパイルエラーの診断にも活用できる。

**画面へのアクセス方法**：Template Explorer の URL にブラウザでアクセスする。index.html がエントリーポイントとなり、Monaco Editor のローダーが完了した後に `init()` 関数（src/index.ts）が呼ばれてエディタが初期化される。URLハッシュまたは localStorage に保存された前回の状態が自動的に復元される。

**主要な操作・処理内容**：
1. 左パネル（Source Editor）での Vue テンプレートの入力・編集
2. 入力変更時の自動コンパイル（300ms デバウンス）とコンパイル結果の右パネル表示
3. コンパイルオプション（Options Panel）の変更による再コンパイル
4. Source Editor でのカーソル移動に連動した Output Editor のハイライト（SourceMap generatedPositionFor）
5. Output Editor でのカーソル移動に連動した Source Editor のハイライト（SourceMap originalPositionFor）
6. コンパイルエラー時のエディタマーカー表示とエラーメッセージ出力
7. 状態の localStorage / URLハッシュへの永続化

**画面遷移**：本画面はスタンドアロンのツール画面であり、他画面への遷移はない。Header 部分に GitHub コミットリンクと Netlify デプロイ履歴リンクがあり、いずれも外部サイトへの遷移となる。

**権限による表示制御**：権限による表示制御は存在しない。全機能が全ユーザーに開放されている。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 44 | テンプレート解析（Parser） | 主機能 | Vue テンプレートの字句解析・構文解析による AST 生成と結果表示 |
| 45 | コード生成（Codegen） | 主機能 | AST からレンダリング関数 JavaScript コードを生成し右パネルに表示 |
| 46 | v-for ディレクティブ変換 | 補助機能 | v-for テンプレートのコンパイル変換結果をリアルタイム確認 |
| 47 | v-if ディレクティブ変換 | 補助機能 | v-if/v-else-if/v-else の条件分岐コンパイル結果をリアルタイム確認 |
| 48 | v-model ディレクティブ変換 | 補助機能 | v-model の双方向バインディングコンパイル結果をリアルタイム確認 |
| 49 | v-on ディレクティブ変換 | 補助機能 | v-on イベントリスナーのコンパイル結果をリアルタイム確認 |
| 50 | v-bind ディレクティブ変換 | 補助機能 | v-bind 属性バインディングのコンパイル結果をリアルタイム確認 |
| 51 | v-slot ディレクティブ変換 | 補助機能 | v-slot スロットレンダリングのコンパイル結果をリアルタイム確認 |
| 52 | 式変換（transformExpression） | 補助機能 | テンプレート式のスコープ解析・識別子書き換え結果をリアルタイム確認 |
| 53 | 静的コンテンツキャッシング | 補助機能 | hoistStatic 有効時の静的ノードホイスティング最適化結果の確認 |
| 68 | generateCodeFrame() | 補助機能 | コンパイルエラー発生時のソースコード位置表示 |

## 画面種別

2パネルエディタ画面（テンプレート入力 + コンパイル結果表示）

## URL/ルーティング

- **URL**: Template Explorer のルート URL
- **ハッシュ**: `#{Base64エンコード済みJSON}` 形式で状態を保持
  - エンコード形式: `btoa(unescape(encodeURIComponent(JSON.stringify(state))))`
  - デコード形式: `decodeURIComponent(escape(atob(hash)))`
  - 保存される状態: `{ src: string, ssr: boolean, options: CompilerOptions }`

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 説明 | 初期値 |
|--------|----------|---------|------|--------|
| src | 入力 | string | テンプレートソースコード | `<div>Hello World</div>`（永続化状態がない場合） |
| ssr | 入力 | boolean | SSR モード | false（永続化状態から復元） |
| compilerOptions | 入力 | CompilerOptions | コンパイラオプション | defaultOptions（options.ts で定義） |
| code | 出力 | string | コンパイル結果の JavaScript コード | コンパイル関数の戻り値 |
| ast | 出力 | Object | コンパイルされた AST | コンソールに出力 |
| map | 出力 | SourceMap | ソースマップ | SourceMapConsumer に変換して使用 |
| errors | 出力 | CompilerError[] | コンパイルエラー一覧 | エディタマーカーとして表示 |

## 表示項目

| 項目名 | 表示領域 | データソース | 説明 |
|--------|---------|-------------|------|
| タイトル | ヘッダー左 | 固定文字列 | "Vue 3 Template Explorer"（h1要素） |
| コミットリンク | ヘッダー | __COMMIT__ | `@{コミットハッシュ}` 形式のGitHubリンク |
| History リンク | ヘッダー | 固定URL | Netlify デプロイ履歴へのリンク |
| Options Panel | ヘッダー下 | options.ts | コンパイルオプション設定UI（別画面設計書 No.5 参照） |
| Source Editor | 左パネル | src（入力テンプレート） | Monaco Editor（HTML言語モード、フォント14px） |
| Output Editor | 右パネル | code（コンパイル結果） | Monaco Editor（JavaScript言語モード、読み取り専用） |
| エラーマーカー | Source Editor 内 | errors | コンパイルエラーの位置をエディタマーカーで表示 |
| ハイライト | 両エディタ | SourceMap | カーソル位置に対応する相手側エディタの該当箇所をハイライト |

## イベント仕様

### 1-ページ読み込み時の初期化（init関数）

index.html が読み込まれると、Monaco Editor のローダースクリプトが実行され、`require(['vs/editor/editor.main'], init)` により init 関数が呼ばれる（index.html 23行目）。

init 関数（index.ts 41-267行目）の処理:
1. カスタムテーマ 'my-theme' の定義と適用（44-45行目）
2. URLハッシュまたは localStorage から前回状態の復元（47-72行目）
3. Source Editor の生成（148-157行目）
4. Output Editor の生成（159-167行目）
5. リサイズイベントリスナーの登録（170-173行目）
6. 入力変更時の reCompile コールバック登録（176行目）
7. Source → Output のカーソル連動ハイライト設定（184-216行目）
8. Output → Source のカーソル連動ハイライト設定（223-263行目）
9. Options Panel の初期化（265行目）
10. watchEffect による reCompile の自動実行登録（266行目）

### 2-テンプレート入力変更による再コンパイル

Source Editor の内容変更時に `debounce(reCompile, 300)` が呼ばれる（176行目）。

reCompile 関数（121-146行目）の処理:
1. Source Editor から現在のソースコードを取得（122行目）
2. 現在のオプションから変更されたもののみを optionsToSave に収集（125-133行目）
3. 状態を JSON 文字列化し localStorage に保存（135-140行目）
4. 状態を Base64 エンコードして URLハッシュに保存（141行目）
5. compileCode(src) を呼び出してコンパイル実行（142行目）
6. コンパイル結果を Output Editor に設定（143-145行目）

### 3-コンパイル処理（compileCode）

compileCode 関数（76-106行目）の処理:
1. コンソールをクリア（77行目）
2. SSR モードに応じて compile または ssrCompile を選択（80行目）
3. コンパイル実行（82-89行目）。オプション: sourceMap: true、onError でエラー収集
4. コンパイル時間をコンソールに出力（90行目）
5. エラーを Monaco Editor マーカーとして設定（91-95行目）
6. AST とオプションをコンソールに出力（96-97行目）
7. 成功時: lastSuccessfulCode と lastSuccessfulMap（SourceMapConsumer）を更新（98-100行目）
8. 失敗時: エラーメッセージをコメント形式で lastSuccessfulCode に設定（101-103行目）

### 4-Source カーソル移動によるOutputハイライト

Source Editor のカーソル位置変更イベント（184-216行目）で、100ms デバウンス後に処理:
1. Editor 側のデコレーションをクリア（186行目）
2. lastSuccessfulMap（SourceMapConsumer）から generatedPositionFor を使用して対応する Output 位置を算出（188-192行目）
3. Output Editor にインラインデコレーション（highlight クラス）を設定（194-206行目）
4. Output Editor の該当位置を画面中央にスクロール（207-210行目）

### 5-Output カーソル移動によるSourceハイライト

Output Editor のカーソル位置変更イベント（223-263行目）で、100ms デバウンス後に処理:
1. Output 側のデコレーションをクリア（225行目）
2. lastSuccessfulMap（SourceMapConsumer）から originalPositionFor を使用して対応する Source 位置を算出（227-230行目）
3. モック位置（line: 1, column: 0）を除外（234-237行目）
4. Source Editor に行全体のデコレーション（highlight クラス）を設定（243-256行目）
5. Source Editor の該当位置を画面中央にスクロール（257行目）

### 6-コンパイルオプション変更による再コンパイル

Options Panel のオプション変更は reactive オブジェクト（compilerOptions）と ref（ssrMode）を通じて検知される。watchEffect（266行目）が compilerOptions / ssrMode の依存を追跡し、変更時に reCompile を自動実行する。

### 7-ウィンドウリサイズ

window の resize イベント（170-173行目）で Source Editor と Output Editor の両方に `layout()` を呼び出し、エディタのサイズを再計算する。

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

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

本画面はクライアントサイドのみで動作するため、データベースへのアクセスは発生しない。

| 操作（イベント） | 対象ストレージ | 操作種別 | 概要 |
|----------------|--------------|---------|------|
| reCompile 実行時 | localStorage | SET | `state` キーに JSON 文字列を保存 |
| reCompile 実行時 | URLハッシュ | SET | Base64 エンコード済み状態をハッシュに保存 |
| 初期化時 | localStorage / URLハッシュ | GET | 前回状態の復元 |

### テーブル別更新項目詳細

#### localStorage

| 操作 | キー名 | 更新値 | 備考 |
|-----|--------|-------|------|
| SET | `state` | `JSON.stringify({ src, ssr, options })` | reCompile 実行のたびに更新（140行目） |
| GET | `state` | - | init 関数内で前回状態を復元（55行目） |

#### PersistedState 型

```typescript
interface PersistedState {
  src: string       // テンプレートソースコード
  ssr: boolean      // SSR モード
  options: CompilerOptions  // コンパイラオプション（デフォルトから変更された値のみ）
}
```

## メッセージ仕様

| メッセージ種別 | メッセージ内容 | 発生条件 | 表示方法 |
|--------------|--------------|---------|---------|
| 情報 | "Compiled in {N}ms." | コンパイル成功時 | console.log（90行目） |
| 情報 | "AST: {AST Object}" | コンパイル成功時 | console.log（96行目） |
| 情報 | "Options: {Options Object}" | コンパイル成功時 | console.log（97行目） |
| エラー | "/* ERROR: {message} (see console for more info) */" | コンパイル失敗時 | Output Editor に表示（102行目） |
| エラー | "Vue template compilation error: {message}" | コンパイルエラー時 | Source Editor のマーカー（117行目） |
| 警告 | "Persisted state in localStorage seems to be corrupted, please reload." | localStorage の状態復元失敗時 | console.warn（60行目） |

## 例外処理

| 例外条件 | 処理内容 | 発生箇所 |
|---------|---------|---------|
| URLハッシュ / localStorage の状態解析失敗 | console.warn 出力、localStorage クリア | index.ts 57-64行目 |
| コンパイル失敗（構文エラー等） | エラーメッセージを Output に表示、console.error | index.ts 101-103行目 |
| 永続化状態に関数が含まれる場合 | nodeTransforms を delete して関数を除外 | index.ts 69行目 |

## 備考

- Monaco Editor のバージョンは 0.20.0 を CDN（unpkg.com）から読み込んでいる
- エディタの共通設定: fontSize 14、minimap 無効、scrollBeyondLastLine 無効、renderWhitespace 'selection'
- Source Editor は wordWrap 'bounded' で折り返しあり、Output Editor は readOnly で読み取り専用
- カスタムテーマ 'my-theme' は 'vs-dark' をベースとしたダークテーマ（theme.ts で定義）
- コンパイル時間の計測には performance.now() を使用
- SourceMap の computeColumnSpans() を呼び出してカラムスパン情報を事前計算（100行目）
- debounce 関数はカスタム実装で、デフォルト遅延は300ms（index.ts 269-283行目）

---

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

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

### 推奨読解順序

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

永続化される状態の構造とコンパイラオプションの型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages-private/template-explorer/src/index.ts` | 26-30行目: PersistedState インターフェース（src, ssr, options） |
| 1-2 | options.ts | `packages-private/template-explorer/src/options.ts` | 7-27行目: defaultOptions オブジェクト（mode, filename, 各種フラグ, bindingMetadata） |
| 1-3 | options.ts | `packages-private/template-explorer/src/options.ts` | 29-31行目: compilerOptions リアクティブオブジェクト |
| 1-4 | options.ts | `packages-private/template-explorer/src/options.ts` | 5行目: ssrMode ref |

**読解のコツ**: `CompilerOptions` は `@vue/compiler-dom` パッケージの型であり、mode（'module' | 'function'）、prefixIdentifiers、hoistStatic、cacheHandlers 等のコンパイラ挙動を制御するオプションを含む。`BindingTypes` は変数バインディングの種類（SETUP_REF, SETUP_CONST 等）を定義する列挙型である。

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

index.html から Monaco Editor のローダーを経て init 関数が呼ばれる流れを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.html | `packages-private/template-explorer/index.html` | 1-24行目: HTML構造（#header, #source, #output の3つの div）と Monaco Editor CDN 読み込み |
| 2-2 | index.ts | `packages-private/template-explorer/src/index.ts` | 41行目: window.init に init 関数を設定 |

**主要処理フロー**:
1. **index.html 1-8行目**: CSSリンクとスタイルシートの読み込み
2. **index.html 9-11行目**: #header, #source, #output の3つの div 要素を配置
3. **index.html 13行目**: Monaco Editor のローダースクリプト読み込み
4. **index.html 14-19行目**: require.config で Monaco Editor のパス設定
5. **index.html 21行目**: コンパイル済み template-explorer.global.js の読み込み（init 関数がグローバルに登録される）
6. **index.html 22-24行目**: require で Monaco Editor 本体を読み込み後に init を呼び出し

#### Step 3: init 関数の初期化処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages-private/template-explorer/src/index.ts` | 44-45行目: カスタムテーマ定義と適用 |
| 3-2 | index.ts | `packages-private/template-explorer/src/index.ts` | 47-72行目: 永続化状態の復元ロジック（URLハッシュ → localStorage フォールバック） |
| 3-3 | index.ts | `packages-private/template-explorer/src/index.ts` | 148-167行目: Source Editor と Output Editor の生成 |
| 3-4 | index.ts | `packages-private/template-explorer/src/index.ts` | 265-266行目: Options Panel 初期化と watchEffect 登録 |

**主要処理フロー**:
- **50-53行目**: URLハッシュを Base64 デコードし、失敗時はそのまま使用
- **54-56行目**: デコード結果または localStorage の 'state' キーから JSON パース
- **66-72行目**: 復元した状態から ssrMode と compilerOptions を適用
- **148-153行目**: Source Editor を HTML 言語モードで生成、初期値は永続化状態の src または `<div>Hello World</div>`
- **159-164行目**: Output Editor を JavaScript 言語モードで読み取り専用として生成

#### Step 4: コンパイル処理とSourceMapハイライトを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.ts | `packages-private/template-explorer/src/index.ts` | 76-106行目: compileCode 関数のコンパイル処理 |
| 4-2 | index.ts | `packages-private/template-explorer/src/index.ts` | 121-146行目: reCompile 関数の永続化＆コンパイル処理 |
| 4-3 | index.ts | `packages-private/template-explorer/src/index.ts` | 184-216行目: Source → Output のハイライト連動 |
| 4-4 | index.ts | `packages-private/template-explorer/src/index.ts` | 223-263行目: Output → Source のハイライト連動 |

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

```
index.html
    |
    +-- Monaco Editor Loader (CDN)
    |       +-- require(['vs/editor/editor.main'], init)
    |
    +-- template-explorer.global.js (ビルド済み)
            |
            +-- window.init = () => { ... }
                    |
                    +-- monaco.editor.defineTheme('my-theme', theme)
                    +-- 永続化状態復元 (URLハッシュ / localStorage)
                    +-- monaco.editor.create(#source) --> Source Editor
                    +-- monaco.editor.create(#output) --> Output Editor
                    +-- editor.onDidChangeModelContent(debounce(reCompile))
                    |       |
                    |       +-- reCompile()
                    |               +-- compileCode(src)
                    |               |       +-- compile() / ssrCompile()
                    |               |       +-- SourceMapConsumer
                    |               +-- localStorage 保存
                    |               +-- URLハッシュ更新
                    |
                    +-- editor.onDidChangeCursorPosition(debounce(...))
                    |       +-- generatedPositionFor() --> Output ハイライト
                    |
                    +-- output.onDidChangeCursorPosition(debounce(...))
                    |       +-- originalPositionFor() --> Source ハイライト
                    |
                    +-- initOptions() --> Options Panel 初期化 (#header)
                    +-- watchEffect(reCompile) --> オプション変更時の自動再コンパイル
```

### データフロー図

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

URLハッシュ / localStorage ─> 状態復元 ────────────────────> Source Editor 初期値
                             (47-72行目)                      compilerOptions / ssrMode

テンプレートソース ──────────> debounce(300ms) ──────────────> reCompile()
(Source Editor 入力)          (176行目)                        |
                                                              +-- compileCode()
                                                              |       |
コンパイラオプション ────────> watchEffect ────────────────>   |       +-- compile/ssrCompile
(Options Panel)               (266行目)                        |       |       |
                                                              |       |       +-- code
                                                              |       |       +-- ast (console)
                                                              |       |       +-- map (SourceMap)
                                                              |       |       +-- errors (markers)
                                                              |       |
                                                              |       +-- Output Editor 更新
                                                              |
                                                              +-- localStorage 保存
                                                              +-- URLハッシュ更新

Source カーソル移動 ─────────> generatedPositionFor ───────> Output ハイライト
                             (SourceMapConsumer)

Output カーソル移動 ─────────> originalPositionFor ────────> Source ハイライト
                             (SourceMapConsumer)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.html | `packages-private/template-explorer/index.html` | テンプレート | エントリーポイント HTML。Monaco Editor CDN 読み込みと DOM 構造定義 |
| index.ts | `packages-private/template-explorer/src/index.ts` | ソース | メインロジック。エディタ初期化、コンパイル、SourceMap ハイライト、永続化 |
| options.ts | `packages-private/template-explorer/src/options.ts` | ソース | コンパイラオプション管理。reactive/ref による状態管理と Options Panel UI |
| theme.ts | `packages-private/template-explorer/src/theme.ts` | ソース | Monaco Editor のカスタムダークテーマ定義（vs-dark ベース） |
| style.css | `packages-private/template-explorer/style.css` | スタイル | 画面レイアウト用 CSS |
| @vue/compiler-dom | 外部パッケージ | 依存 | DOM 向けテンプレートコンパイラ（compile 関数） |
| @vue/compiler-ssr | 外部パッケージ | 依存 | SSR 向けテンプレートコンパイラ（ssrCompile 関数） |
| source-map-js | 外部パッケージ | 依存 | SourceMap の解析・位置変換（SourceMapConsumer） |
| @vue/runtime-dom | 外部パッケージ | 依存 | watchEffect による リアクティブ監視 |
