# 機能設計書 50-プレビュースタイル

## 概要

本ドキュメントは、Decap CMSのプレビュースタイル機能の設計を記述する。プレビューペインに適用するカスタムCSSスタイル（CSS文字列またはスタイルシートURL）を登録する機能であり、レジストリ（`registry.js`）で管理される。

### 本機能の処理概要

**業務上の目的・背景**：CMSのプレビューペインでコンテンツを表示する際、実際のサイトと同じ見た目（フォント、レイアウト、色など）でプレビューしたいというニーズがある。プレビュースタイル機能により、CSSファイルのURLまたはCSS文字列を登録し、プレビューiframe内に適用できる。これにより、実際のサイトスタイルに近いプレビュー体験を提供する。

**機能の利用シーン**：CMS初期化時に`CMS.registerPreviewStyle('/styles.css')`でスタイルシートURLを登録するか、`CMS.registerPreviewStyle('body { color: red; }', { raw: true })`でCSS文字列を登録する。登録されたスタイルはプレビューiframeのhead要素に自動的に挿入される。

**主要な処理内容**：
1. `registerPreviewStyle(style, opts)`: スタイルをレジストリに登録（raw CSSまたはURL）
2. `getPreviewStyles()`: 登録された全スタイルを取得
3. EditorPreviewPaneでのiframe head要素へのスタイル挿入
4. raw CSS: `<style>`タグとして挿入
5. URL: `<link>`タグとして挿入

**関連システム・外部連携**：レジストリ（No.75）、プレビューテンプレート（No.49）

**権限による制御**：特になし。全ユーザーに適用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | エントリ編集画面 | 補助機能 | プレビューiframeへのスタイル適用 |

## 機能種別

UI表示 / プラグインシステム

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| style | string | Yes | CSS文字列またはスタイルシートURL | - |
| opts.raw | boolean | No | trueの場合、styleをCSS文字列として扱う。falseの場合はURLとして扱う | デフォルト: false（URL） |

### 入力データソース

- CMS API（`CMS.registerPreviewStyle()`）: スタイル登録

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| previewStyles | Array<{value: string, raw?: boolean}> | 登録されたスタイルの配列 |

### 出力先

- プレビューiframeのhead要素: `<style>`タグまたは`<link>`タグ

## 処理フロー

### 処理シーケンス

```
1. スタイル登録（初期化時）
   └─ CMS.registerPreviewStyle(style, opts)
   └─ registry.previewStyles.push({ ...opts, value: style })
2. スタイル取得（プレビュー表示時）
   └─ getPreviewStyles()
   └─ registry.previewStyles配列を返却
3. スタイル適用（EditorPreviewPane内）
   └─ getPreviewStyles().map() でstyleEl配列を構築
   └─ raw === true: <style>{style.value}</style>
   └─ raw !== true: <link href={style.value} type="text/css" rel="stylesheet" />
   └─ PreviewPaneFrame の head props に挿入
```

### フローチャート

```mermaid
flowchart TD
    A[CMS.registerPreviewStyle] --> B[registry.previewStyles.push]
    B --> C[EditorPreviewPane.render]
    C --> D[getPreviewStyles]
    D --> E{style.raw?}
    E -->|true| F[style タグ生成]
    E -->|false| G[link タグ生成]
    F --> H[styleEls配列]
    G --> H
    H --> I[PreviewPaneFrame head]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-50-01 | raw CSS | raw=trueの場合、valueをCSS文字列として`<style>`タグに挿入 | registerPreviewStyle呼び出し時 |
| BR-50-02 | URL参照 | raw=falseまたは未指定の場合、valueをURLとして`<link>`タグで参照 | registerPreviewStyle呼び出し時 |
| BR-50-03 | 複数スタイル | 複数回の登録が可能。配列にpushされるため登録順で適用される | - |
| BR-50-04 | iframe内適用 | スタイルはプレビューiframe内のhead要素にのみ適用。メインDOMには影響しない | PreviewPaneFrame |

### 計算ロジック

特筆すべき計算ロジックなし。

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

本機能はデータベースを操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | CSS読み込みエラー | URLで指定したスタイルシートが見つからない | ブラウザのネットワークエラーとして表示。プレビューは表示されるがスタイルなし |

### リトライ仕様

リトライは不要。

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

フロントエンドコンポーネントのため、トランザクション制御は不要。

## パフォーマンス要件

- 外部CSSファイルの読み込みはブラウザのキャッシュメカニズムに依存
- raw CSSはiframeの再描画ごとにインライン適用

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

- プレビュースタイルはiframe内にのみ適用されるため、メインCMS UIに影響を与えない
- 外部URLからのCSSファイル読み込みはCORS制約に従う

## 備考

- `registry.previewStyles`は`Array<{value: string, raw?: boolean}>`形式の配列
- スタイルは登録順でiframe headに挿入されるため、後から登録したスタイルが優先される（CSS詳細度が同じ場合）
- 登録解除（unregister）のAPIは提供されていない

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | registry.js | `packages/decap-cms-core/src/lib/registry.js` | registryオブジェクトのpreviewStylesプロパティ（26行目: previewStyles: []） |

**読解のコツ**: `registry.previewStyles`はプレーンな配列。各要素は`{ value: string, raw?: boolean }`のオブジェクト。`raw`がtrueならCSS文字列、falseまたは未定義ならURL。

#### Step 2: スタイル登録/取得API

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | registry.js | `packages/decap-cms-core/src/lib/registry.js` | registerPreviewStyle（74-76行目）、getPreviewStyles（77-79行目） |

**主要処理フロー**:
- **74-76行目**: `registerPreviewStyle(style, opts)` - `registry.previewStyles.push({ ...opts, value: style })`。optsを展開してvalueプロパティを追加
- **77-79行目**: `getPreviewStyles()` - `registry.previewStyles`配列をそのまま返却

#### Step 3: プレビューペインでのスタイル適用

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | EditorPreviewPane.js | `packages/decap-cms-core/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js` | renderメソッド内のスタイル構築処理（266-271行目） |

**主要処理フロー**:
- **266行目**: `const styleEls = getPreviewStyles().map((style, i) => { ... })` - 全登録スタイルをJSX要素に変換
- **267-268行目**: `if (style.raw)` - raw CSSの場合は`<style key={i}>{style.value}</style>`を生成
- **270行目**: URL CSSの場合は`<link key={i} href={style.value} type="text/css" rel="stylesheet" />`を生成
- **287行目**: `<PreviewPaneFrame id="preview-pane" head={styleEls} ...>` - iframe headにスタイル要素を挿入

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

```
CMS.registerPreviewStyle(style, opts)
    |
    +-- registry.previewStyles.push({ ...opts, value: style })

EditorPreviewPane.render()
    |
    +-- getPreviewStyles()
    |       +-- registry.previewStyles 返却
    |
    +-- styleEls = previewStyles.map()
    |       +-- [raw] <style>{value}</style>
    |       +-- [URL] <link href={value} />
    |
    +-- PreviewPaneFrame(head={styleEls})
            +-- iframe head にスタイル挿入
```

### データフロー図

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

CSS文字列/URL      ──▶  registerPreviewStyle        ──▶  registry.previewStyles
(CMS API)                push({ ...opts, value })          (配列に追加)

registry           ──▶  getPreviewStyles()          ──▶  styleEls
(previewStyles)          map → <style>/<link>              (JSX配列)

styleEls           ──▶  PreviewPaneFrame(head)      ──▶  iframe head
(JSX配列)                react-frame-component              (スタイル適用)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| registry.js | `packages/decap-cms-core/src/lib/registry.js` | ソース | スタイル登録/取得API（74-79行目） |
| EditorPreviewPane.js | `packages/decap-cms-core/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js` | ソース | プレビューペイン（スタイル適用: 266-271行目） |
