# 画面設計書 2-パッド編集画面

## 概要

本ドキュメントは、Etherpad Liteのパッド編集画面の設計内容を記述するものである。この画面は、リアルタイム共同編集を行うためのメインエディター画面であり、Etherpadの中核となる機能を提供する。テキスト編集、書式設定、チャット、ユーザー管理など、コラボレーションに必要な機能が集約されている。

### 本画面の処理概要

**業務上の目的・背景**：本画面は、複数ユーザーが同時に同一ドキュメントを編集できるリアルタイム共同編集環境を提供する。会議メモ、ブレインストーミング、共同執筆、議事録作成など、チームコラボレーションの様々なシナリオで活用される。各ユーザーの編集内容はWebSocket経由でリアルタイムに同期され、編集履歴はリビジョンとして保存される。

**画面へのアクセス方法**：トップページからパッド名を入力して遷移、または直接 `/p/{パッド名}` のURLにアクセスする。既存パッドの場合は内容がロードされ、新規パッドの場合は空のエディターが表示される。

**主要な操作・処理内容**：
1. テキストエディターでの文章入力・編集（リアルタイム同期）
2. ツールバーによる書式設定（太字、斜体、下線、リスト等）
3. チャット機能による参加者間コミュニケーション
4. ユーザー名・カラーの設定
5. インポート/エクスポート機能（txt, html, etherpad, doc, pdf, odt形式）
6. 共有リンクの生成と埋め込みコードの取得
7. タイムスライダーへの遷移（編集履歴確認）
8. パッドの削除
9. 表示設定（フォント、言語、テーマ等）

**画面遷移**：
- 遷移元: トップページ（/）、管理画面のパッド管理画面、直接URL、共有リンク
- 遷移先: タイムスライダー画面（/p/:pad/timeslider）、トップページ（/）

**権限による表示制御**：読み取り専用（readOnly）モードでは編集機能が無効化される。パッドへのアクセス権限がない場合は「Permission Denied」メッセージが表示される。設定により書式設定ツールバーの表示/非表示が制御される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 56 | パッド変更同期 | 主機能 | テキスト編集内容をリアルタイムで全クライアントに同期 |
| 57 | ユーザー参加処理 | 主機能 | CLIENT_READYメッセージを処理してユーザーをパッドに参加させる |
| 58 | 変更セット適用 | 主機能 | USER_CHANGESメッセージを処理してパッドに変更を適用 |
| 59 | カーソル位置同期 | 主機能 | ユーザーのカーソル位置をリアルタイムで他のユーザーと同期 |
| 60 | ユーザー情報更新 | 主機能 | ユーザー名や色などの情報変更を同期 |
| 34 | チャット履歴取得 | 補助機能 | チャットウィンドウにチャット履歴を表示 |
| 36 | チャットメッセージ追加 | 補助機能 | チャット入力欄からメッセージを送信 |
| 51 | ファイルインポート | 補助機能 | インポートポップアップでファイルを選択してパッドにインポート |
| 52 | テキストエクスポート | 補助機能 | エクスポートポップアップからtxt形式でダウンロード |
| 53 | HTMLエクスポート | 補助機能 | エクスポートポップアップからhtml形式でダウンロード |
| 54 | Etherpadエクスポート | 補助機能 | エクスポートポップアップからetherpad形式でダウンロード |
| 55 | ドキュメント変換エクスポート | 補助機能 | エクスポートポップアップからdoc/pdf/odt形式でダウンロード |
| 8 | パッド削除 | 補助機能 | 設定ポップアップ内のDelete padボタンでパッドを削除 |
| 75 | 国際化対応 | 補助機能 | 言語設定で表示言語を切り替え |
| 22 | リビジョン数取得 | API連携 | パッド情報の取得時にリビジョン数を取得 |
| 25 | アクティブユーザー数取得 | API連携 | 接続ユーザー数の表示 |
| 26 | アクティブユーザー一覧取得 | API連携 | ユーザーパネルにアクティブユーザー一覧を表示 |

## 画面種別

編集 / コラボレーション

## URL/ルーティング

- **URL**: `/p/:pad`
- **HTTPメソッド**: GET
- **ルーティング定義**: `src/node/hooks/express/specialpages.ts` の `expressCreateServer` フック内で定義
- **パラメータ**: `:pad` - パッド名（URLエンコード）

## 入出力項目

| 項目名 | 項目ID | 入力/出力 | データ型 | 必須 | 最大長 | 説明 |
|--------|--------|----------|---------|------|--------|------|
| テキストコンテンツ | - | 入力/出力 | テキスト | - | 無制限 | エディターの本文 |
| ユーザー名 | myusernameedit | 入力 | テキスト | - | - | ユーザーの表示名 |
| ユーザーカラー | myswatch | 入力 | カラー | - | - | ユーザーを識別する色 |
| チャットメッセージ | chatinput | 入力 | テキスト | - | 999文字 | チャットへの投稿内容 |
| インポートファイル | importfileinput | 入力 | ファイル | - | - | インポートするファイル |

## 表示項目

| 項目名 | 項目ID | 説明 |
|--------|--------|------|
| ツールバー | editbar | 書式設定ボタン群 |
| エディターコンテナ | editorcontainer | テキスト編集領域（iframe内） |
| 設定ポップアップ | settings | フォント、言語、表示設定 |
| インポート/エクスポートポップアップ | import_export | ファイル入出力機能 |
| 接続状態ポップアップ | connectivity | 接続断時のモーダル表示 |
| 共有ポップアップ | embed | 共有リンクと埋め込みコード |
| ユーザーパネル | users | 参加ユーザー一覧とカラー設定 |
| チャットボックス | chatbox | チャット履歴と入力欄 |
| チャットアイコン | chaticon | チャット表示切替とメッセージカウント |
| ローディング表示 | loading | パッド読み込み中の表示 |
| 権限拒否メッセージ | permissionDenied | アクセス権限がない場合の表示 |

## イベント仕様

### 1-テキスト編集

ユーザーがエディター内でテキストを入力・編集すると、変更内容がChangesetとして生成される。このChangesetはWebSocket経由でサーバーに送信され（USER_CHANGESメッセージ）、サーバーで検証・適用後、他の全クライアントに配信される。Operational Transformationにより同時編集の競合が解決される。

### 2-ユーザー参加（CLIENT_READY）

パッドにアクセスすると、クライアントはCLIENT_READYメッセージをサーバーに送信する。サーバーはパッドの現在状態（テキスト、リビジョン番号、参加ユーザー情報等）をクライアントに返送し、エディターが初期化される。

### 3-チャットメッセージ送信

チャット入力欄にメッセージを入力しEnterキーまたは送信ボタンを押下すると、CHAT_MESSAGEがサーバーに送信される。サーバーはメッセージを保存し、同じパッドに接続している全クライアントにブロードキャストする。

### 4-ファイルインポート

インポートポップアップでファイルを選択し「Import Now」ボタンを押下すると、ファイルがサーバーにアップロードされる。サーバーはファイル形式に応じて内容をパース（AbiwordまたはLibreOffice使用）し、パッドの内容を置換する。

### 5-ファイルエクスポート

エクスポートポップアップで形式（txt, html, etherpad, doc, pdf, odt）を選択すると、新しいタブでエクスポートURLが開かれ、ファイルがダウンロードされる。

### 6-パッド削除

設定ポップアップ内の「Delete pad」ボタンを押下すると、確認後にパッドがサーバーから削除される。削除後、接続中の全ユーザーに削除通知が送信され、セッションが切断される。

### 7-共有リンク生成

共有ポップアップを開くと、現在のパッドURL、読み取り専用URL、埋め込み用HTMLコードが表示される。「Read only」チェックボックスで読み取り専用URLへの切り替えが可能。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| テキスト編集 | pad:{padId}:revs:{revNum} | INSERT | 新しいリビジョンを保存 |
| テキスト編集 | pad:{padId} | UPDATE | headリビジョン番号を更新、atext更新 |
| チャット送信 | pad:{padId}:chat:{chatNum} | INSERT | チャットメッセージを保存 |
| ユーザー名変更 | globalAuthor:{authorId} | UPDATE | 著者名を更新 |
| パッド削除 | pad:{padId}* | DELETE | パッド関連の全データを削除 |

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

#### pad:{padId}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | atext | 新しいテキスト状態 | 編集反映 |
| UPDATE | pool | 更新された属性プール | 書式情報含む |
| UPDATE | head | インクリメントされたリビジョン番号 | 編集反映 |

#### pad:{padId}:revs:{revNum}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | changeset | 変更セット文字列 | OT形式 |
| INSERT | meta | メタ情報（著者、タイムスタンプ） | 編集追跡用 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| pad.loading | 情報 | Loading... | パッド読み込み中 |
| pad.permissionDenied | エラー | You do not have permission to access this pad | アクセス権限なし |
| pad.modals.connected | 情報 | Connected | 接続成功時 |
| pad.modals.reconnecting | 情報 | Reconnecting... | 再接続中 |
| pad.modals.disconnected | エラー | Disconnected | 接続断 |
| pad.modals.deleted | 情報 | This pad has been deleted | パッド削除時 |
| pad.modals.userdup | 警告 | User appears to be open in another window | 重複接続検知 |
| pad.modals.unauth | エラー | Unauthorized | 認証エラー |
| pad.modals.rateLimited | エラー | Rate limited | 編集速度制限 |
| pad.importExport.importSuccessful | 成功 | Import successful | インポート成功 |
| pad.chat | ラベル | Chat | チャットラベル |
| pad.settings.padSettings | タイトル | Pad Settings | 設定ダイアログ |

## 例外処理

| 例外状態 | 対応処理 | 表示内容 |
|---------|---------|---------|
| パッドへのアクセス権限なし | エディター非表示 | 「You do not have permission to access this pad」 |
| WebSocket接続断 | 再接続試行 | 再接続中モーダル表示 |
| 接続ループ検知 | 接続停止 | loopingモーダル表示 |
| レート制限超過 | 編集一時停止 | rateLimitedモーダル表示 |
| パッド破損 | 編集不可 | corruptPadモーダル表示 |
| パッド削除 | セッション切断 | deletedモーダル表示 |
| インポート失敗 | エラー表示 | インポートエラーメッセージ |
| 変更セット不正 | 再接続要求 | badChangesetモーダル表示 |

## 備考

- エディターはiframe内で動作し、aceエディターベースの実装
- Changesetは独自のOperational Transformation形式で、差分のみを転送
- チャットメッセージは最大999文字
- プラグインによりツールバーやエディター機能の拡張が可能
- スキン（skinName）とスキンバリアント（skinVariants）により外観カスタマイズ可能
- 書式設定はAttributePoolで管理され、著者ごとの色分け表示に使用

---

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

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

### 推奨読解順序

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

パッド編集画面の中核はChangeset（変更セット）とAttributePool（属性プール）である。これらのデータ構造を理解することが、リアルタイム同期の仕組みを理解する鍵となる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Changeset.ts | `src/static/js/Changeset.ts` | Changeset形式の構造、pack/unpack、compose、follow関数 |
| 1-2 | AttributePool.ts | `src/static/js/AttributePool.ts` | 書式属性の管理構造 |
| 1-3 | SocketIOMessage.ts | `src/static/js/types/SocketIOMessage.ts` | WebSocketメッセージの型定義 |

**読解のコツ**: Changesetは`Z:base>delta$operation`形式の文字列で表現される。`Z:`はヘッダー、`base`は元テキスト長、`delta`は変更量、`$`以降が操作内容。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | specialpages.ts | `src/node/hooks/express/specialpages.ts` | `/p/:pad` ルートハンドラー（339-356行目） |
| 2-2 | pad.html | `src/templates/pad.html` | 画面テンプレート全体構造 |
| 2-3 | padBootstrap.js | `src/templates/padBootstrap.js` | クライアントJSエントリーポイント |

**主要処理フロー**:
1. **339行目**: `/p/:pad` ルートハンドラー定義
2. **341行目**: 読み取り専用判定
3. **343-346行目**: ツールバー初期化フック呼び出し
4. **348-355行目**: テンプレートレンダリング

#### Step 3: サーバーサイドメッセージハンドラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | WebSocketメッセージ処理の中核 |
| 3-2 | SocketIORouter.ts | `src/node/handler/SocketIORouter.ts` | Socket.IOルーティング |

**主要処理フロー**:
- **171-176行目**: handleConnect - 新規接続時の初期化
- **196-200行目**: handleDisconnect - 切断時の後処理
- **sessioninfos オブジェクト**: 接続セッション情報の管理

#### Step 4: クライアントサイドの同期処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | collab_client.js | `src/static/js/collab_client.js` | クライアント側のOT処理 |
| 4-2 | ace.js | `src/static/js/ace.js` | エディターのDOM操作 |

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

```
[ブラウザ] GET /p/:pad
    │
    ├─ express.Router (specialpages.ts)
    │      │
    │      └─ eejs.require('templates/pad.html')
    │              │
    │              ├─ toolbar 設定
    │              └─ settings (公開設定のみ)
    │
    └─ [クライアント] padBootstrap.js
           │
           ├─ WebSocket接続確立
           │      │
           │      └─ CLIENT_READY送信
           │              │
           │              └─ [サーバー] PadMessageHandler
           │                     │
           │                     ├─ パッド読み込み (PadManager)
           │                     ├─ 著者ID取得 (AuthorManager)
           │                     └─ CLIENT_VARS応答
           │
           └─ エディター初期化 (ace.js)
                  │
                  ├─ テキスト編集
                  │      │
                  │      └─ Changeset生成 → USER_CHANGES送信
                  │              │
                  │              └─ [サーバー] handleUserChanges
                  │                     │
                  │                     ├─ Changeset検証
                  │                     ├─ パッド更新
                  │                     └─ 全クライアントへブロードキャスト
                  │
                  └─ 受信 (NEW_CHANGES)
                         │
                         └─ ローカル適用 (OT)
```

### データフロー図

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

テキスト入力 ───────▶ Changeset生成 ────────────▶ WebSocket送信
                        (collab_client)              (USER_CHANGES)
                              │
                              ▼
                     [サーバー処理]
                              │
                     Changeset検証
                              │
                     OT適用 (follow)
                              │
                     パッド更新 (DB)
                              │
                              ▼
                     ブロードキャスト ─────────▶ 他クライアント
                        (NEW_CHANGES)              (ローカル適用)

チャット入力 ─────▶ CHAT_MESSAGE ──────────────▶ 全クライアント配信
                                                    (チャット表示更新)

ファイル選択 ─────▶ HTTP POST ─────────────────▶ パッド内容置換
                   (/p/:pad/import)               (サーバー変換)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| pad.html | `src/templates/pad.html` | テンプレート | 画面のHTMLテンプレート |
| specialpages.ts | `src/node/hooks/express/specialpages.ts` | ソース | ルーティング定義 |
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | WebSocketメッセージ処理 |
| Changeset.ts | `src/static/js/Changeset.ts` | ソース | 変更セット処理 |
| collab_client.js | `src/static/js/collab_client.js` | ソース | クライアント同期処理 |
| ace.js | `src/static/js/ace.js` | ソース | エディター実装 |
| pad.css | `src/static/css/pad.css` | スタイル | パッド編集画面CSS |
| ImportHandler.ts | `src/node/handler/ImportHandler.ts` | ソース | ファイルインポート処理 |
| ExportHandler.ts | `src/node/handler/ExportHandler.ts` | ソース | ファイルエクスポート処理 |
| ChatMessage.ts | `src/static/js/ChatMessage.ts` | ソース | チャットメッセージ構造 |
| toolbar.ts | `src/node/utils/toolbar.ts` | ソース | ツールバー設定 |
