Note: このIssueは 2026-03-13 にレビュー結果(Stage 7: 影響範囲レビュー 2回目)を反映して更新されました。
詳細: dev-reports/issue/490/issue-review/
概要
ファイルパネルでHTMLファイルを選択した際に、ソースコード表示だけでなくブラウザレンダリング(プレビュー)表示を可能にする。
MarkdownEditorと同様に、ソースコード編集とプレビューの分割表示を提供する。
背景・課題
- Claude等のAIツールがHTMLファイルを生成するケースが増えている(ゲーム、データ可視化、レポート等)
- 現状、HTMLファイルはテキストとしてシンタックスハイライト表示されるのみで、レンダリング結果を確認するにはブラウザで別途開く必要がある
- 単一HTMLファイルで完結するインタラクティブコンテンツ(スペースインベーダー等のゲーム、Canvas描画)をCommandMate内で直接操作したい
- CommandMateのファイルパネル内で直接プレビュー・操作できれば、開発ワークフローが効率化される
提案する解決策
レンダリング方式
- iframe + sandbox属性を使用
- MARPスライドとの違い: MARPはサーバーサイドAPI(POST /marp-render)でMarkdownをHTMLに変換した結果をiframe srcDocに設定するが、HTMLプレビューではサーバーサイドAPIは不要。既存のファイル取得API(GET /files/...)が返すHTMLテキストをそのままiframe srcDocに設定するクライアントサイドのみのパターンで実装する
- sandbox属性のレベルをトグルで段階的に切り替え可能
- CSP設定の変更が必要:
next.config.js のCSPヘッダーに frame-src 'self' blob: を追加する必要がある。srcdoc 属性を使用したiframeはブラウザによっては blob: originとして扱われるため、明示的なCSP設定がないとブロックされる可能性がある(S3-001)
サンドボックスレベル(3段階トグル)
| レベル |
sandbox属性 |
スクリプト |
外部リソース |
ユースケース |
| Safe(デフォルト) |
sandbox="" |
ブロック |
ブロック |
静的HTMLプレビュー |
| Interactive |
sandbox="allow-scripts" |
許可 |
ブロック |
単一HTMLゲーム・Canvas等 |
| Full |
sandbox="allow-scripts allow-same-origin" |
許可 |
許可 |
外部CSS/画像参照あり |
- デフォルトは Safe(最も安全)
- ユーザーが明示的にレベルを上げることでスクリプト実行・外部リソース読み込みを許可
Fullレベルのセキュリティリスク
allow-scripts + allow-same-origin の組み合わせは、iframe内のスクリプトが親ページのDOMやCookieにアクセス可能になるため、sandbox属性を実質無効化する危険な組み合わせとして知られている(MDN Web Docs参照)。
対策:
- Fullレベル選択時に警告ダイアログを表示し、ユーザーに明示的なリスク承認を求める
- 初期実装ではSafe/InteractiveレベルはsrcdocでMVPを実装する。Fullレベルもまずsrcdocで実装し、セキュリティテストの結果に応じてblob: URLへのorigin分離方式への移行を検討する(S5-004)
- 将来的にFullレベルの廃止も視野に入れ、allow-scriptsのみの2段階構成への簡素化を検討する
表示モード
MarkdownEditorと同様の3モード:
- ソースのみ: HTMLソースコードをシンタックスハイライト表示(現状の動作)
- プレビューのみ: iframe内でHTMLをレンダリング表示
- 分割表示: 左にソース、右にプレビュー
モバイル版での表示モード
モバイル版(FileViewer)ではスペースの制約から分割表示は行わず、MarkdownEditorのmobileTab方式と同様にタブ切り替え(ソース/プレビュー)で実装する。FileViewer.tsxにはタブ切り替えUIの既存基盤がないため、新規実装が必要となる(S3-005)。iframeの高さはMARPプレビュー同様に isFullscreen の有無で切り替える。
HTMLファイル編集方針
HTMLファイルの編集UIは新規の HtmlPreview.tsx コンポーネントで実装し、既存の MarkdownEditor.tsx は再利用しない。MarkdownEditorはMarkdown固有のツールバーやプレビューロジックを含んでおり、HTMLプレビューとは要件が異なるため、別コンポーネントとして独立させる(S3-006)。
主要な変更点
- HTMLファイル判定ロジックの追加(
.html, .htm)
- FilePanelContent / FileViewer にHTML用レンダリング分岐を追加
- サンドボックスレベル切り替えトグルUI(Safe / Interactive / Full)
- HTMLファイルの編集対応(editable-extensionsへの追加)
- CSPヘッダーへの
frame-src ディレクティブ追加(next.config.js)
実装タスク
受入条件
影響範囲
変更対象ファイル
| ファイル |
変更内容 |
src/config/html-extensions.ts |
新規: HTML拡張子定義(HTML_EXTENSIONS, isHtmlExtension, HTML_MAX_SIZE_BYTES) |
src/config/editable-extensions.ts |
.html, .htm 追加 + EXTENSION_VALIDATORSにHTMLエントリ追加 |
src/types/models.ts |
isHtml フラグ追加(JSDocコメント付き - Issue #490)(S5-001) |
src/app/api/worktrees/[id]/files/[...path]/route.ts |
GETハンドラのNextResponse.jsonレスポンスオブジェクトに isHtml: isHtmlExtension(extension) を追加(S7-001) |
src/components/worktree/HtmlPreview.tsx |
新規: HTMLプレビューコンポーネント(Fullレベル警告含む) |
src/components/worktree/FilePanelContent.tsx |
HTML用表示分岐追加 |
src/components/worktree/FileViewer.tsx |
HTML用表示分岐追加(モバイル: タブ切り替えUI新規実装、codeViewData isHtml条件追加) |
next.config.js |
CSPヘッダーに frame-src 'self' blob: を追加(S3-001) |
tests/unit/config/editable-extensions.test.ts |
既存テスト更新: EDITABLE_EXTENSIONS長さ変更、HTML EXTENSION_VALIDATORS検証追加(S3-002) |
tests/unit/config/html-extensions.test.ts |
新規: HTML拡張子判定テスト |
tests/unit/components/html-preview.test.ts |
新規: HTMLプレビューコンポーネントテスト |
セキュリティ考慮
- iframe sandbox属性による段階的セキュリティ制御
- デフォルトは最も安全なレベル(Safe: sandbox="")
- スクリプト許可時も
allow-top-navigationは付与しない(親ページへの影響を防止)
- CSP frame-src設定:
next.config.js に frame-src 'self' blob: を追加し、srcdoc iframeの動作を許可する(S3-001)
- Fullレベル(allow-scripts + allow-same-origin)のリスク: sandbox属性が実質無効化される。選択時に警告ダイアログを表示し、blob: URLによるorigin分離を検討する
- DOMPurify方針: iframe srcDoc方式ではDOMPurifyによるサニタイズは行わず、sandbox属性による隔離で安全性を担保する。DOMPurifyはSafeモードでのフォールバック表示(iframeなしのインラインHTML表示)を将来検討する場合のオプションとして位置づける
- 初期実装のiframe方式: 全レベル(Safe/Interactive/Full)でsrcdoc方式を採用。Fullレベルはセキュリティテスト結果に応じてblob: URL方式への移行を検討する(S5-004)
- ファイルサイズ上限: 5MB(5MBを超えるHTMLファイルはソースコード表示のみ)
- EXTENSION_VALIDATORSによるHTMLファイル保存時のバリデーション(additionalValidation: 初期実装ではなし)(S5-003)
参考実装
- MARPスライドレンダリング:
src/app/api/worktrees/[id]/marp-render/route.ts(iframe srcDoc パターン。ただし本Issueではサーバーサイド変換APIは不要)
- MarkdownEditor:
src/components/worktree/MarkdownEditor.tsx(ソース/プレビュー分割パターン、モバイルタブ方式。ただし本IssueではMarkdownEditorを再利用せず、HtmlPreview.tsxとして独立実装する)
- SVG XSS防止:
src/config/image-extensions.ts(危険パターン検出)
- ファイルバリデーション:
src/config/editable-extensions.ts(EXTENSION_VALIDATORS参照実装)
レビュー履歴
| ステージ |
日付 |
概要 |
| Stage 1-2 |
2026-03-13 |
通常レビューの指摘事項反映(DOMPurify方針明記、Fullレベルリスク記載等) |
| Stage 3-4 |
2026-03-13 |
影響範囲レビューの指摘事項反映(CSP設定追加、既存テスト更新、FileViewer codeViewData対応、モバイルタブUI、MarkdownEditor非再利用方針) |
| Stage 5-6 |
2026-03-13 |
通常レビュー2回目の指摘事項反映(models.ts JSDocコメント規約、FileContentResponse型安全性テスト追加、EXTENSION_VALIDATORS additionalValidation方針明確化、srcdoc/blob: URL段階的方針) |
| Stage 7-8 |
2026-03-13 |
影響範囲レビュー2回目の指摘事項反映(GETハンドラのNextResponse.jsonレスポンスオブジェクトへのisHtml追加の具体的変更ポイント明記) |
概要
ファイルパネルでHTMLファイルを選択した際に、ソースコード表示だけでなくブラウザレンダリング(プレビュー)表示を可能にする。
MarkdownEditorと同様に、ソースコード編集とプレビューの分割表示を提供する。
背景・課題
提案する解決策
レンダリング方式
next.config.jsのCSPヘッダーにframe-src 'self' blob:を追加する必要がある。srcdoc属性を使用したiframeはブラウザによってはblob:originとして扱われるため、明示的なCSP設定がないとブロックされる可能性がある(S3-001)サンドボックスレベル(3段階トグル)
sandbox=""sandbox="allow-scripts"sandbox="allow-scripts allow-same-origin"Fullレベルのセキュリティリスク
allow-scripts+allow-same-originの組み合わせは、iframe内のスクリプトが親ページのDOMやCookieにアクセス可能になるため、sandbox属性を実質無効化する危険な組み合わせとして知られている(MDN Web Docs参照)。対策:
表示モード
MarkdownEditorと同様の3モード:
モバイル版での表示モード
モバイル版(FileViewer)ではスペースの制約から分割表示は行わず、MarkdownEditorのmobileTab方式と同様にタブ切り替え(ソース/プレビュー)で実装する。FileViewer.tsxにはタブ切り替えUIの既存基盤がないため、新規実装が必要となる(S3-005)。iframeの高さはMARPプレビュー同様に
isFullscreenの有無で切り替える。HTMLファイル編集方針
HTMLファイルの編集UIは新規の
HtmlPreview.tsxコンポーネントで実装し、既存のMarkdownEditor.tsxは再利用しない。MarkdownEditorはMarkdown固有のツールバーやプレビューロジックを含んでおり、HTMLプレビューとは要件が異なるため、別コンポーネントとして独立させる(S3-006)。主要な変更点
.html,.htm)frame-srcディレクティブ追加(next.config.js)実装タスク
src/config/html-extensions.ts新規作成HTML_EXTENSIONS配列(.html,.htm)isHtmlExtension()判定関数HTML_MAX_SIZE_BYTES定数(5MB)src/config/editable-extensions.tsに.html,.htmを追加EDITABLE_EXTENSIONS配列への追加EXTENSION_VALIDATORS配列にHTMLファイル用エントリを追加(maxFileSize: 5MB、additionalValidation: 初期実装ではundefined。sandbox属性による隔離で安全性を担保するため、保存時のXSSパターン検出は不要。将来的にXSS危険パターン検出を追加する可能性あり)(S5-003)EDITABLE_EXTENSIONSとEXTENSION_VALIDATORSは必ず同時に追加する(アトミックな変更)。片方のみ追加すると保存APIがUnsupported extensionエラーを返す(S3-004)src/types/models.tsにisHtml?: booleanフラグ追加(JSDocコメント付き:/** Whether the file is an HTML file (optional) - Issue #490 */。既存のisImage/isVideoと同じコメント規約に従う)(S5-001)src/app/api/worktrees/[id]/files/[...path]/route.tsのGETハンドラ変更isHtml: trueフラグを追加するisHtmlExtension()をimportして判定NextResponse.jsonのレスポンスオブジェクト(行290-296付近)にisHtml: isHtmlExtension(extension)プロパティを追加する(S7-001)src/components/worktree/HtmlPreview.tsx新規作成(iframe sandbox + サンドボックスレベルトグル)src/components/worktree/FilePanelContent.tsxにHTML用表示分岐追加(ソース/プレビュー/分割)src/components/worktree/FileViewer.tsxにHTML用表示分岐追加canCopy判定:isHtmlを除外条件に追加する必要はない(HTMLはコピー可能)が、プレビューモード時の動作を確認するcodeViewDataのuseMemo:isHtml条件を追加し、HTMLプレビュー表示時は不要なシンタックスハイライト処理をスキップする(S3-003)renderContent():isHtml分岐をisVideo/isImageの後、codeViewData参照の前に追加する(S3-003)next.config.jsのCSPヘッダーにframe-src 'self' blob:を追加(S3-001)tests/unit/config/editable-extensions.test.ts(S3-002)should only include .md for nowをshould include .md, .html, .htm extensionsに更新し、EDITABLE_EXTENSIONSの長さを3に変更EXTENSION_VALIDATORSのテストにHTMLエントリの検証を追加受入条件
.html/.htmファイルがプレビュー表示される影響範囲
変更対象ファイル
src/config/html-extensions.tssrc/config/editable-extensions.ts.html,.htm追加 + EXTENSION_VALIDATORSにHTMLエントリ追加src/types/models.tsisHtmlフラグ追加(JSDocコメント付き - Issue #490)(S5-001)src/app/api/worktrees/[id]/files/[...path]/route.tsisHtml: isHtmlExtension(extension)を追加(S7-001)src/components/worktree/HtmlPreview.tsxsrc/components/worktree/FilePanelContent.tsxsrc/components/worktree/FileViewer.tsxnext.config.jsframe-src 'self' blob:を追加(S3-001)tests/unit/config/editable-extensions.test.tstests/unit/config/html-extensions.test.tstests/unit/components/html-preview.test.tsセキュリティ考慮
allow-top-navigationは付与しない(親ページへの影響を防止)next.config.jsにframe-src 'self' blob:を追加し、srcdoc iframeの動作を許可する(S3-001)参考実装
src/app/api/worktrees/[id]/marp-render/route.ts(iframe srcDoc パターン。ただし本Issueではサーバーサイド変換APIは不要)src/components/worktree/MarkdownEditor.tsx(ソース/プレビュー分割パターン、モバイルタブ方式。ただし本IssueではMarkdownEditorを再利用せず、HtmlPreview.tsxとして独立実装する)src/config/image-extensions.ts(危険パターン検出)src/config/editable-extensions.ts(EXTENSION_VALIDATORS参照実装)レビュー履歴