# 機能設計書 30-スキーマ表示

## 概要

本ドキュメントは、Liam DBシステムにおけるスキーマ表示機能の設計を記述するものである。本機能は、プロジェクトのデータベーススキーマ情報をER図として視覚的に表示し、Webブラウザでのインタラクティブな閲覧を可能にする。

### 本機能の処理概要

スキーマ表示機能は、スキーマファイルの解析からER図レンダリングまでを一貫して行う機能である：
1. **スキーマファイル取得**：GitHubリポジトリまたはURLからスキーマファイルを取得
2. **スキーマパース**：@liam-hq/schemaを使用してスキーマ情報を抽出
3. **ER図レンダリング**：@xyflow/react（React Flow）を使用してインタラクティブなER図を表示

**業務上の目的・背景**：Liam DBはデータベーススキーマの可視化を核とするサービスである。スキーマ表示機能により、データベース設計者や開発者はスキーマの全体構造を直感的に把握し、テーブル間のリレーションシップを視覚的に理解できる。

**機能の利用シーン**：
- プロジェクトのスキーマファイルをER図として閲覧
- 公開URLからスキーマファイルを読み込んでER図を表示
- テーブル間のリレーションシップを視覚的に確認
- テーブル詳細（カラム、制約、インデックス等）を確認

**主要な処理内容**：
1. スキーマファイル取得（GitHub API / URL fetch）
2. フォーマット自動検出（detectFormat）
3. スキーマパース（parse）
4. スキーマからノード・エッジ変換（convertSchemaToNodes）
5. ELKアルゴリズムによる自動レイアウト（computeAutoLayout）
6. React Flowによるインタラクティブ表示（ERDRenderer / ERDContent）

**関連システム・外部連携**：@xyflow/react、elkjs、@liam-hq/schema、GitHub API

**権限による制御**：公開スキーマは認証不要。プロジェクトのスキーマはプロジェクトメンバーのみアクセス可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 3 | ERD表示画面（公開URL） | 主画面 | URLからスキーマを読み込みER図表示 |
| 9 | プロジェクト詳細画面 | 主画面 | プロジェクトのスキーマをER図表示 |
| 14 | デザインセッション詳細画面 | 関連画面 | 編集中スキーマのER図表示 |

## 機能種別

表示機能 / ビジュアライゼーション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| slug | string[] | Yes（URL表示時） | URLパスセグメント（github.com/owner/repo/...） | - |
| format | SupportedFormat | No | スキーマフォーマット指定 | supportedFormatSchemaで検証 |
| projectId | string | Yes（プロジェクト表示時） | プロジェクト識別子 | - |
| branchOrCommit | string | Yes（プロジェクト表示時） | ブランチ名またはコミットSHA | - |
| schemaFilePath | string | Yes（プロジェクト表示時） | スキーマファイルパス | - |

### 入力データソース

- URLパスパラメータ（public ERD表示）
- Supabaseデータベース（プロジェクト情報、スキーマファイルパス）
- GitHub API（スキーマファイル内容）
- Cookie（サイドバー状態、パネルサイズ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| nodes | Node[] | React Flowのノード配列（テーブルノード） |
| edges | Edge[] | React Flowのエッジ配列（リレーションシップ） |
| schema | Schema | パースされたスキーマオブジェクト |
| errorObjects | ErrorObject[] | パースエラー情報 |

### 出力先

- React Flowキャンバス（ER図）
- サイドバー（テーブル一覧）
- テーブル詳細ドロワー

## 処理フロー

### 処理シーケンス

```
1. スキーマファイル取得
   ├─ [URL表示] handleSingleFileUrl / handleGitHubFolderUrl
   │     └─ fetch() / fetchSchemaFromGitHubFolder()
   └─ [プロジェクト表示] getERDEditorContent()
         └─ getFileContent() via GitHub API
2. フォーマット検出
   └─ detectFormat(url / filename)
3. スキーマパース
   └─ parse(content, format)
         └─ @liam-hq/schema/parser
4. ノード・エッジ変換
   └─ convertSchemaToNodes({ schema, showMode })
         ├─ constraintsToRelationships()
         ├─ テーブルノード生成
         └─ リレーションシップエッジ生成
5. 自動レイアウト
   └─ computeAutoLayout(nodes, edges)
         └─ getElkLayout({ nodes, edges })
               └─ elkjs (ELK algorithm)
6. レンダリング
   └─ ERDRenderer
         ├─ LeftPane（テーブル一覧）
         ├─ ERDContent（React Flow）
         ├─ Toolbar（操作ツールバー）
         └─ TableDetailDrawer（詳細ドロワー）
```

### フローチャート

```mermaid
flowchart TD
    A[スキーマファイル取得] --> B{フォーマット検出}
    B --> C[parse]
    C --> D{パース成功?}
    D -->|Yes| E[convertSchemaToNodes]
    D -->|No| F[エラー表示]
    E --> G[computeAutoLayout]
    G --> H[ERDRenderer]
    H --> I[React Flow キャンバス]
    H --> J[LeftPane テーブル一覧]
    H --> K[Toolbar]
    H --> L[TableDetailDrawer]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-30-01 | サイドバー状態保持 | サイドバーの開閉状態をCookieで保持 | Cookie 'sidebar:state' |
| BR-30-02 | パネルサイズ保持 | パネルのサイズ比率をCookieで保持 | Cookie 'panels:layout' |
| BR-30-03 | デフォルトパネルサイズ | パネルサイズ未設定時は[20, 80] | Cookie未設定時 |
| BR-30-04 | 表示モード | TABLE_NAME / KEY_ONLY / ALL_FIELDS | ユーザー選択 |
| BR-30-05 | ハイライト表示 | 選択・ホバー時に関連ノードをハイライト | テーブル選択時 |

### 計算ロジック

- Cookie有効期間: `maxAge = 60 * 60 * 24 * 7` (7日間)
- ズーム範囲: `MIN_ZOOM` 〜 `MAX_ZOOM`
- ELKレイアウトオプション: `elk.algorithm = 'layered'`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| プロジェクトスキーマ取得 | projects | SELECT | プロジェクト情報とリポジトリマッピング取得 |
| スキーマファイルパス取得 | schema_file_paths | SELECT | スキーマファイルパスとフォーマット取得 |

### テーブル別操作詳細

#### projects（SELECT）
- **取得条件**: `id = projectId`
- **JOINテーブル**: `project_repository_mappings`, `github_repositories`
- **取得カラム**: `*`, リポジトリ情報（name, owner, github_installation_identifier）

#### schema_file_paths（SELECT）
- **取得条件**: `project_id = projectId`, `path = schemaFilePath`
- **取得カラム**: `path`, `format`

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NetworkError | ネットワークエラー | URL取得失敗 | エラーメッセージ表示 |
| FileNotFound | ファイルエラー | スキーマファイル未存在 | エラーメッセージ表示 |
| FormatError | フォーマットエラー | フォーマット検出失敗 | format パラメータ指定を促す |
| ParseError | パースエラー | スキーマ解析失敗 | エラーメッセージ表示、Sentry通知 |

### リトライ仕様

- URL fetch: リトライなし（cache: 'no-store'）
- GitHub API: リトライなし

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

本機能は読み取り専用のため、トランザクション制御は行わない。

## パフォーマンス要件

- ELKレイアウト: 非同期処理（async/await）
- スキーマパース: prism.wasmによる高速パース
- React Flow: ズーム・パン時の再レンダリング最適化
- ノード表示: 非関連テーブルはグループノードでまとめて配置

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

- GitHub URL: raw.githubusercontent.comのみ許可（SSRF対策）
- ファイルサイズ制限: MAX_FILE_SIZE_BYTES = 5MB
- 最大再帰深度: MAX_RECURSION_DEPTH = 5（フォルダ探索時）
- 最大ファイル数: MAX_TOTAL_FILES = 100

## 備考

スキーマ表示機能は2つのコンテキストで使用される：
1. **公開URL表示**（`/erd/p/[...slug]`）: 認証不要、URLからスキーマを読み込み
2. **プロジェクト表示**（SchemaPage）: 認証必要、プロジェクトのスキーマを表示

表示モード：
- `TABLE_NAME`: テーブル名のみ表示
- `KEY_ONLY`: プライマリキー・外部キーのみ表示
- `ALL_FIELDS`: 全カラム表示

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | page.tsx | `frontend/apps/app/app/erd/p/[...slug]/page.tsx` | 公開URLからのスキーマ表示 |
| 1-2 | erdViewer.tsx | `frontend/apps/app/app/erd/p/[...slug]/erdViewer.tsx` | ERDViewerコンポーネント |
| 1-3 | SchemaPage.tsx | `frontend/apps/app/components/SchemaPage/SchemaPage.tsx` | プロジェクトスキーマ表示 |
| 1-4 | ErdEditor.tsx | `frontend/apps/app/components/SchemaPage/components/ERDEditor/ErdEditor.tsx` | プロジェクト用ERDエディタ |

**主要処理フロー**:
- **224-303行目（page.tsx）**: Page関数でURL解析、スキーマ取得、フォーマット検出、パース
- **246行目**: setPrismWasmUrl設定
- **268行目**: parse(content, format)でスキーマパース
- **279-293行目**: Cookie読み取り（サイドバー状態、パネルサイズ）
- **47-160行目（SchemaPage.tsx）**: getERDEditorContentでGitHubからスキーマ取得

#### Step 2: スキーマ取得処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | githubUrlHandler.ts | `frontend/apps/app/app/erd/p/[...slug]/utils/githubUrlHandler.ts` | GitHubフォルダURL処理 |

**主要処理フロー**:
- **38-44行目**: isGitHubFolderUrl判定
- **112-188行目**: parseGitHubFolderUrl（ブランチ/パス解析）
- **309-339行目**: collectSchemaFilesFromFolder（再帰的ファイル収集）
- **385-424行目**: fetchSchemaFromGitHubFolder（ファイル取得・結合）
- **11-18行目**: SECURITY_LIMITS定義（セキュリティ制限）

#### Step 3: ERDRendererを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ErdRenderer.tsx | `frontend/packages/erd-core/src/features/erd/components/ERDRenderer/ErdRenderer.tsx` | メインレンダラー |
| 3-2 | ErdRendererProvider.tsx | `frontend/packages/erd-core/src/providers/ErdRendererProvider.tsx` | コンテキストプロバイダー |

**主要処理フロー**:
- **55-194行目（ErdRenderer.tsx）**: ERDRendererコンポーネント
- **67行目**: useSchemaOrThrow()でスキーマ取得
- **78-81行目**: convertSchemaToNodes({ schema, showMode })
- **119-192行目**: ResizablePanelGroupでレイアウト構成
- **130行目**: ReactFlowProvider
- **13-29行目（ErdRendererProvider.tsx）**: NuqsAdapter、UserEditingProvider、SchemaProvider

#### Step 4: ノード・エッジ変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | convertSchemaToNodes.ts | `frontend/packages/erd-core/src/features/erd/utils/convertSchemaToNodes.ts` | スキーマからノード変換 |
| 4-2 | highlightNodesAndEdges.ts | `frontend/packages/erd-core/src/features/erd/utils/highlightNodesAndEdges.ts` | ハイライト処理 |

**主要処理フロー**:
- **13-103行目（convertSchemaToNodes.ts）**: convertSchemaToNodes関数
- **20-21行目**: テーブルとリレーションシップ抽出
- **44-66行目**: テーブルノード生成
- **83-100行目**: エッジ生成（sourceHandle、targetHandle）
- **84-139行目（highlightNodesAndEdges.ts）**: highlightNodesAndEdges関数

#### Step 5: 自動レイアウトを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | computeAutoLayout.ts | `frontend/packages/erd-core/src/features/erd/utils/computeAutoLayout/computeAutoLayout.ts` | 自動レイアウト |
| 5-2 | getElkLayout.ts | `frontend/packages/erd-core/src/features/erd/utils/computeAutoLayout/getElkLayout.ts` | ELKアルゴリズム |

**主要処理フロー**:
- **4-30行目（computeAutoLayout.ts）**: computeAutoLayout関数
- **5-13行目**: hiddenノードとvisibleノードの分離
- **21-24行目**: getElkLayoutでレイアウト計算
- **9-19行目（getElkLayout.ts）**: ELKレイアウトオプション
- **26-44行目**: getElkLayout関数

#### Step 6: ERDContentを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | ErdContent.tsx | `frontend/packages/erd-core/src/features/erd/components/ERDContent/ErdContent.tsx` | React Flowコンテンツ |
| 6-2 | TableNode.tsx | `frontend/packages/erd-core/src/features/erd/components/ERDContent/components/TableNode/TableNode.tsx` | テーブルノード |
| 6-3 | LeftPane.tsx | `frontend/packages/erd-core/src/features/erd/components/ERDRenderer/LeftPane/LeftPane.tsx` | サイドバー |

**主要処理フロー**:
- **46-184行目（ErdContent.tsx）**: ERDContentInner
- **51-61行目**: useNodesState、useEdgesState
- **69-75行目**: useInitialAutoLayout、useQueryParamsChanged
- **101-113行目**: handleMouseEnterNode（ハイライト）
- **152-181行目**: ReactFlowコンポーネント
- **19-59行目（TableNode.tsx）**: TableNodeコンポーネント
- **40-43行目**: showModeによる表示切り替え
- **30-183行目（LeftPane.tsx）**: LeftPaneコンポーネント
- **77-90行目**: テーブルノード一覧取得とソート

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

```
[公開URL表示]
Page (/erd/p/[...slug])
    │
    ├─ handleSingleFileUrl / handleGitHubFolderUrl
    │      └─ fetch / fetchSchemaFromGitHubFolder
    │             ├─ parseGitHubFolderUrl
    │             ├─ collectSchemaFilesFromFolder
    │             └─ downloadAndCombineFiles
    │
    ├─ detectFormat(url)
    │
    ├─ parse(content, format)
    │      └─ @liam-hq/schema/parser
    │
    └─ ERDViewer
           └─ VersionProvider
                  └─ ErdRendererProvider
                         └─ ERDRenderer (下記参照)

[プロジェクト表示]
SchemaPage
    │
    ├─ getERDEditorContent
    │      ├─ supabase.from('projects').select()
    │      ├─ supabase.from('schema_file_paths').select()
    │      ├─ getFileContent (GitHub API)
    │      └─ parse(content, format)
    │
    └─ ERDEditor
           └─ VersionProvider
                  └─ ErdRendererProvider
                         └─ ERDRenderer (下記参照)

[ERDRenderer共通]
ERDRenderer
    │
    ├─ useSchemaOrThrow()
    │
    ├─ convertSchemaToNodes({ schema, showMode })
    │      ├─ constraintsToRelationships()
    │      ├─ tableNodes生成
    │      └─ edges生成
    │
    ├─ SidebarProvider
    │
    ├─ ToastProvider / CommandPaletteProvider
    │
    ├─ ReactFlowProvider
    │      │
    │      ├─ ResizablePanelGroup
    │      │      ├─ ResizablePanel (LeftPane)
    │      │      │      └─ TableNameMenuButton[]
    │      │      │
    │      │      └─ ResizablePanel (main)
    │      │             ├─ ERDContent
    │      │             │      ├─ useInitialAutoLayout
    │      │             │      │      └─ computeAutoLayout
    │      │             │      │             └─ getElkLayout
    │      │             │      │
    │      │             │      └─ ReactFlow
    │      │             │             ├─ TableNode
    │      │             │             └─ RelationshipEdge
    │      │             │
    │      │             ├─ TableDetailDrawer
    │      │             │
    │      │             └─ Toolbar
    │      │
    │      └─ CommandPalette
```

### データフロー図

```
[スキーマ取得フロー]
URL / GitHub ──────────▶ fetch / getFileContent
       │                        │
       │                        ▼
       │                   content (string)
       │                        │
       │                        ▼
       │                   detectFormat
       │                        │
       │                        ▼
       │                   SupportedFormat
       │                        │
       └────────────────────────▼
                           parse(content, format)
                                │
                                ▼
                           Schema {
                             tables: {},
                             enums: {},
                             extensions: {}
                           }

[ノード変換フロー]
Schema ────────────────▶ convertSchemaToNodes
       │                        │
       │                        ├─ constraintsToRelationships
       │                        │
       │                        ├─ tableNodes[]
       │                        │
       │                        └─ edges[]
       │                               │
       │                               ▼
       │                        computeAutoLayout
       │                               │
       │                               ├─ getElkLayout
       │                               │      └─ elkjs
       │                               │
       │                               └─ positioned nodes[]
       │                                      │
       └───────────────────────────────────────▼
                                        ReactFlow
                                              │
                                        ┌─────┴─────┐
                                        ▼           ▼
                                   TableNode   RelationshipEdge

[ハイライトフロー]
Node Click / Hover ─────▶ highlightNodesAndEdges
       │                        │
       │                        ├─ edgeMap構築
       │                        │
       │                        ├─ activeHighlightNode / highlightNode
       │                        │
       │                        └─ highlightEdge
       │                               │
       └───────────────────────────────▼
                                  setNodes / setEdges
                                        │
                                        ▼
                                  React Flow再描画
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| page.tsx | `frontend/apps/app/app/erd/p/[...slug]/page.tsx` | ページ | 公開URLエントリーポイント |
| erdViewer.tsx | `frontend/apps/app/app/erd/p/[...slug]/erdViewer.tsx` | コンポーネント | ERDViewer |
| githubUrlHandler.ts | `frontend/apps/app/app/erd/p/[...slug]/utils/githubUrlHandler.ts` | ユーティリティ | GitHubフォルダURL処理 |
| SchemaPage.tsx | `frontend/apps/app/components/SchemaPage/SchemaPage.tsx` | ページ | プロジェクトスキーマ表示 |
| ErdEditor.tsx | `frontend/apps/app/components/SchemaPage/components/ERDEditor/ErdEditor.tsx` | コンポーネント | プロジェクトERDエディタ |
| ErdRenderer.tsx | `frontend/packages/erd-core/src/features/erd/components/ERDRenderer/ErdRenderer.tsx` | コンポーネント | ERDレンダラー |
| ErdRendererProvider.tsx | `frontend/packages/erd-core/src/providers/ErdRendererProvider.tsx` | プロバイダー | コンテキストプロバイダー |
| ErdContent.tsx | `frontend/packages/erd-core/src/features/erd/components/ERDContent/ErdContent.tsx` | コンポーネント | React Flowコンテンツ |
| TableNode.tsx | `frontend/packages/erd-core/src/features/erd/components/ERDContent/components/TableNode/TableNode.tsx` | コンポーネント | テーブルノード |
| LeftPane.tsx | `frontend/packages/erd-core/src/features/erd/components/ERDRenderer/LeftPane/LeftPane.tsx` | コンポーネント | サイドバー |
| Toolbar.tsx | `frontend/packages/erd-core/src/features/erd/components/ERDRenderer/Toolbar/Toolbar.tsx` | コンポーネント | ツールバー |
| convertSchemaToNodes.ts | `frontend/packages/erd-core/src/features/erd/utils/convertSchemaToNodes.ts` | ユーティリティ | スキーマ→ノード変換 |
| highlightNodesAndEdges.ts | `frontend/packages/erd-core/src/features/erd/utils/highlightNodesAndEdges.ts` | ユーティリティ | ハイライト処理 |
| computeAutoLayout.ts | `frontend/packages/erd-core/src/features/erd/utils/computeAutoLayout/computeAutoLayout.ts` | ユーティリティ | 自動レイアウト |
| getElkLayout.ts | `frontend/packages/erd-core/src/features/erd/utils/computeAutoLayout/getElkLayout.ts` | ユーティリティ | ELKレイアウト |
