Yuki Takei 5 месяцев назад
Родитель
Сommit
b85af1c19d

+ 0 - 126
.serena/memories/apps-app-modal-performance-optimization-v3-list.md

@@ -1,126 +0,0 @@
-# モーダル一覧 - V3動的ロード対象
-
-## V3進捗状況
-
-**実装完了**: 12/46モーダル (26%) (2025-10-16更新)
-
-### 中頻度モーダル (6/6 - 100%完了) ✅
-- ✅ PageAccessoriesModal (2025-10-15)
-- ✅ ShortcutsModal (2025-10-15)
-- ✅ PageRenameModal (2025-10-16) - ケースC
-- ✅ PageDuplicateModal (2025-10-16) - ケースC
-- ✅ DescendantsPageListModal (2025-10-16) - ケースC
-- ✅ PageDeleteModal (2025-10-16) - ケースA
-
-**時間**: 中頻度4モーダル完了に約20分
-- 3つケースC (最短経路): 各5分程度
-- 1つケースA: 約5分
-
-### 低頻度モーダル (6/38 - 16%完了) 🔄
-- ✅ DrawioModal (2025-10-16) - ケースC
-- ✅ HandsontableModal (2025-10-16) - ケースC + 複数ステータス対応
-- ✅ TemplateModal (2025-10-16) - ケースC + @growi/editor state
-- ✅ LinkEditModal (2025-10-16) - ケースC + @growi/editor state
-- ✅ TagEditModal (2025-10-16) - ケースC
-- ✅ ConflictDiffModal (2025-10-16) - ケースC
-
-**バグ修正 (2025-10-16)**:
-- LinkEditModal: 誤ったstate importパス修正 (`~/states` → `@growi/editor/dist/states`)
-- TemplateModal: 誤ったstate importパス修正 (`~/states` → `@growi/editor`)
-- HandsontableModal: 複数ステータス対応 (`isOpened || isOpendInEditor`)
-
----
-
-## V2完了モーダル (46個) - V3動的ロード候補
-
-### 高頻度使用 - 動的ロード非推奨 (2個)
-初期ロードを維持すべきモーダル:
-1. SearchModal.tsx - 検索機能 (頻繁に使用)
-2. PageCreateModal.tsx - ページ作成 (重要機能)
-
-### 中頻度使用 - 動的ロード完了✅ (6個)
-- ✅ PageAccessoriesModal.tsx
-- ✅ ShortcutsModal.tsx
-- ✅ PageDeleteModal.tsx
-- ✅ PageRenameModal.tsx
-- ✅ PageDuplicateModal.tsx
-- ✅ DescendantsPageListModal.tsx
-
-### 低頻度使用 - 動的ロード候補 (38個)
-
-**完了 (6個)** ✅:
-- ✅ LinkEditModal.tsx
-- ✅ TagEditModal.tsx
-- ✅ ConflictDiffModal.tsx
-- ✅ HandsontableModal.tsx
-- ✅ DrawioModal.tsx
-- ✅ TemplateModal.tsx
-
-**次の優先候補 (32個)** 🔜:
-- PagePresentationModal.tsx
-- PageBulkExportSelectModal.tsx
-- CreateTemplateModal.tsx
-- SearchOptionModal.tsx
-- ImageCropModal.tsx
-- DeleteCommentModal.tsx
-- AssociateModal.tsx
-- DisassociateModal.tsx
-- EmptyTrashModal.tsx
-- DeleteBookmarkFolderModal.tsx
-- GrantedGroupsInheritanceSelectModal.tsx
-- SelectUserGroupModal.tsx
-- UserGroupModal.tsx
-- DeleteAiAssistantModal.tsx
-- ShareScopeWarningModal.tsx
-- DeleteAttachmentModal.tsx
-- PrivateLegacyPagesMigrationModal.tsx
-- PluginDeleteModal.tsx
-- PutbackPageModal.jsx
-- DeleteSlackBotSettingsModal.tsx
-- AiAssistantManagementModal.tsx
-- PageSelectModal.tsx
-- その他 (約10個)
-
----
-
-## Container-Presentation構造 (V2成果)
-
-多くのモーダルは以下の構造:
-```
-Modal/
-  ├── Container (6-15行) - Modal wrapper
-  └── Substance (全ロジック) - 動的ロード対象
-```
-
-**V3での利点**: Substanceのみ動的ロード可能
-
----
-
-## 実装パターン
-
-### ケースC (最短経路) ⭐
-- 所要時間: 約5分/モーダル
-- Container有`<Modal>` + Substance分離済み
-- 作業: ディレクトリ化 + dynamic.tsx/index.ts追加 + named export化
-
-### ケースA (シンプル)
-- 所要時間: 約5-10分/モーダル
-- Container-Presentation分離なし
-- 作業: ディレクトリ化 + dynamic.tsx/index.ts追加 + named export化
-
----
-
-## 重要な注意事項
-
-### Cross-Package State Management
-一部のモーダル(特にエディター関連)は`@growi/editor`パッケージでstateを管理:
-- LinkEditModal: `@growi/editor/dist/states/modal/link-edit`
-- TemplateModal: `@growi/editor`
-- HandsontableModal (Editor用): `@growi/editor` (useHandsontableModalForEditorStatus)
-
-**注意**: `~/states`からインポートできると仮定しないこと!
-
-### 複数ステータス対応
-一部のモーダルは複数のステータスプロパティを持つ:
-- HandsontableModal: `isOpened || isOpendInEditor`
-- dynamic.tsxで両方をチェックする必要あり

+ 255 - 0
.serena/memories/apps-app-modal-performance-optimization-v3-progress.md

@@ -0,0 +1,255 @@
+# モーダルV3動的ロード最適化 - 進捗管理
+
+## 📊 進捗状況サマリー (2025-10-17更新)
+
+**実装完了**: 23/46モーダル (50%) 🎉
+
+**残り作業**: 
+- モーダル最適化: 2個(中優先度)
+- PageAlerts最適化: 4個
+- **合計目標**: 29/48 = **60%完了**(クラスコンポーネント2個、非モーダル、admin専用等を除く)
+
+---
+
+## 🔴 重要な学び: 正しい分類基準 (2025-10-17)
+
+### ❌ 誤った判断基準
+- "親ページがdynamic()でロードされている → 子モーダルの最適化不要"
+- **問題点**: 親が遅延ロードされていても、モーダルは親と一緒にダウンロードされる
+
+### ✅ 正しい判断基準
+1. **モーダル自身の利用頻度**(親ページの頻度ではない)
+2. **ファイルサイズ/複雑さ**(50行以上で効果的、100行以上で強く推奨)
+3. **レンダリングコスト**
+
+### ⚠️ 例外: 親ページ自体が低頻度の場合
+- **Me画面**: 個人設定画面、低頻度利用 → 配下のモーダルは最適化不要
+  - AssociateModal, DisassociateModal は除外
+- **Admin画面**: 管理画面、低頻度利用 → 配下のモーダルは最適化不要
+  - ImageCropModal, DeleteSlackBotSettingsModal, PluginDeleteModal は除外
+- **理由**: 親ページ自体がdynamic()かつ低頻度なら、子モーダルの最適化効果は限定的
+
+### 遅延ロードの階層構造
+
+```
+BasicLayout (常にレンダリング)
+  ├─ HotkeysManager (dynamic()) ← 遅延ロード
+  │    └─ ShowShortcutsModal ← ❌ 実体はモーダルではない(ホットキートリガーのみ)
+  │
+  ├─ SearchPage (dynamic()) ← 遅延ロード(中頻度)
+  │    └─ SearchOptionModal (静的import) ← ✅ 最適化対象(低頻度モーダル)
+  │
+  ├─ Me/PersonalSettings (dynamic()) ← 遅延ロード(低頻度)
+  │    ├─ AssociateModal ← ❌ 親自体が低頻度、最適化不要
+  │    └─ DisassociateModal ← ❌ 親自体が低頻度、最適化不要
+  │
+  └─ Admin/* (dynamic()) ← 遅延ロード(低頻度)
+       ├─ ImageCropModal ← ❌ 親自体が低頻度、最適化不要
+       ├─ DeleteSlackBotSettingsModal ← ❌ 親自体が低頻度、最適化不要
+       └─ PluginDeleteModal ← ❌ 親自体が低頻度、最適化不要
+```
+
+**結論**: 
+- 親がdynamic()でも、子モーダルは親と一緒にダウンロードされる → モーダル自身の頻度で判断
+- **ただし親自体が低頻度(Me画面、Admin画面など)なら、子の最適化は不要**
+
+---
+
+## ✅ 完了済みモーダル (23個)
+
+### 高頻度モーダル (0/2 - 意図的にスキップ) ⏭️
+- ⏭️ SearchModal (192行) - 検索機能、初期ロード維持
+- ⏭️ PageCreateModal (319行) - ページ作成、初期ロード維持
+
+### 中頻度モーダル (6/6 - 100%完了) ✅
+- ✅ PageAccessoriesModal (2025-10-15) - ケースB
+- ✅ ShortcutsModal (2025-10-15) - ケースC
+- ✅ PageRenameModal (2025-10-16) - ケースC
+- ✅ PageDuplicateModal (2025-10-16) - ケースC
+- ✅ DescendantsPageListModal (2025-10-16) - ケースC
+- ✅ PageDeleteModal (2025-10-16) - ケースA
+
+### 低頻度モーダル (17/38完了)
+
+**Session 1完了 (6個)** ✅:
+- ✅ DrawioModal (2025-10-16) - ケースC
+- ✅ HandsontableModal (2025-10-16) - ケースC + 複数ステータス対応
+- ✅ TemplateModal (2025-10-16) - ケースC + @growi/editor state
+- ✅ LinkEditModal (2025-10-16) - ケースC + @growi/editor state
+- ✅ TagEditModal (2025-10-16) - ケースC
+- ✅ ConflictDiffModal (2025-10-16) - ケースC
+
+**Session 2完了 (11個)** ✅:
+- ✅ DeleteBookmarkFolderModal (2025-10-17) - ケースC, BasicLayout
+- ✅ PutbackPageModal (2025-10-17) - ケースC, JSX→TSX変換
+- ✅ AiAssistantManagementModal (2025-10-17) - ケースC
+- ✅ PageSelectModal (2025-10-17) - ケースC
+- ✅ GrantedGroupsInheritanceSelectModal (2025-10-17) - ケースC
+- ✅ DeleteAttachmentModal (2025-10-17) - ケースC
+- ✅ PageBulkExportSelectModal (2025-10-17) - ケースC
+- ✅ PagePresentationModal (2025-10-17) - ケースC
+- ✅ EmptyTrashModal (2025-10-17) - ケースB
+- ✅ CreateTemplateModal (2025-10-17) - ケースB
+- ✅ DeleteCommentModal (2025-10-17) - ケースB
+
+---
+
+## 🔄 残りの最適化対象
+
+### 🔴 Session 3: 高優先度モーダル(1個)
+
+1. **SearchOptionModal** (92行) ⭐ 最優先
+   - **場所**: `features/search/client/components/SearchPage/`
+   - **呼び出し**: SearchPage (dynamic(), 中頻度) → SearchControl → SearchOptionModal
+   - **状態**: Props-based (`isOpen`)、`isFileterOptionModalShown`
+   - **ケース**: Case B
+   - **効果**: SearchPage利用時の不要なレンダリング防止
+
+**予想時間**: 約5-10分
+
+---
+
+### 🟡 Session 4: 中優先度モーダル(1個)
+
+2. **DeleteAiAssistantModal** (90行)
+   - **場所**: `features/openai/client/components/AiAssistant/Sidebar/`
+   - **呼び出し**: BasicLayout → AiAssistantSidebar (dynamic()) → DeleteAiAssistantModal
+   - **状態**: Props-based
+   - **ケース**: Case B(既にContainer-Presentation分離済み)
+   - **効果**: AiAssistantSidebar使用時の不要なレンダリング防止
+
+**予想時間**: 約5-10分
+
+---
+
+### 🔵 Session 5: PageAlerts最適化(4個)
+
+PageAlertsは`BasicLayout → PageView → PageAlerts`経由で全ページ常時レンダリング。
+既にNext.js `dynamic()`使用だが、getLayoutパターンでは初期ロードされる問題。
+
+1. **TrashPageAlert**
+   - **表示条件**: `useIsTrashPage()` hook
+   - **頻度**: ゴミ箱ページのみ(極めて低頻度)
+   - **最適化**: `useLazyLoader('trash-page-alert', ..., isTrashPage)`
+
+2. **FixPageGrantAlert** ⭐ 最重要
+   - **サイズ**: 412行(大規模)
+   - **特徴**: 内部にModalコンポーネント含む
+   - **表示条件**: 権限修正が必要な時(低頻度)
+   - **効果**: 大きなバンドル削減
+
+3. **PageRedirectedAlert**
+   - **表示条件**: `useRedirectFrom() != null`
+   - **頻度**: リダイレクト時のみ(低頻度)
+
+4. **FullTextSearchNotCoverAlert**
+   - **表示条件**: `markdownLength > elasticsearchMaxBodyLengthToIndex`
+   - **頻度**: 非常に長いページのみ(低頻度)
+
+**予想時間**: 約40-60分
+
+---
+
+## ⏭️ 最適化不要/スキップ(23個)
+
+### 非モーダルコンポーネント(1個)
+- ❌ **ShowShortcutsModal** (35行) - 実体はモーダルではなくホットキートリガーのみ
+
+### 親ページ低頻度 - Me画面(2個)
+- ⏸️ **AssociateModal** (142行) - Me画面(低頻度)内のモーダル
+- ⏸️ **DisassociateModal** (94行) - Me画面(低頻度)内のモーダル
+
+### 親ページ低頻度 - Admin画面(3個)
+- ⏸️ **ImageCropModal** (194行) - Admin/Customize(低頻度)内のモーダル
+- ⏸️ **DeleteSlackBotSettingsModal** (103行) - Admin/SlackIntegration(低頻度)内のモーダル
+- ⏸️ **PluginDeleteModal** (103行) - Admin/Plugins(低頻度)内のモーダル
+
+### 低優先スキップ(1個)
+- ⏸️ **PrivateLegacyPagesMigrationModal** (133行) - ユーザー指示によりスキップ
+
+### クラスコンポーネント(2個)
+- ❌ **UserInviteModal** (299行) - .jsx、対象外
+- ❌ **GridEditModal** (263行) - .jsx、対象外
+
+### 管理画面専用・低頻度(12個)
+
+管理画面自体が遅延ロードされており、使用頻度が極めて低いため最適化不要:
+
+- SelectCollectionsModal (222行) - ExportArchiveData
+- ImportCollectionConfigurationModal (228行) - ImportData
+- NotificationDeleteModal (53行) - Notification
+- DeleteAllShareLinksModal (61行) - Security
+- LdapAuthTestModal (72行) - Security
+- ConfirmBotChangeModal (58行) - SlackIntegration
+- UpdateParentConfirmModal (93行) - UserGroupDetail
+- UserGroupUserModal (110行) - UserGroupDetail
+- UserGroupDeleteModal (208行) - UserGroup
+- UserGroupModal (138行) - ExternalUserGroupManagement
+- PasswordResetModal (228行) - Users
+- ConfirmModal (74行) - App
+
+### 高頻度モーダル(2個)
+- ⏭️ **SearchModal** (192行) - 検索機能、初期ロード維持
+- ⏭️ **PageCreateModal** (319行) - ページ作成、初期ロード維持
+
+---
+
+## 📈 最適化進捗チャート
+
+```
+完了済み: ████████████████████████████████████████████████  23/46 (50%)
+残り:     ██                                                2/46 (4%)
+スキップ:  █████████                                         9/46 (20%)
+対象外:   ██                                                2/46 (4%)
+不要:     ██████████                                       10/46 (22%)
+```
+
+**次のマイルストーン**:
+- Session 3完了後: 24/46 (52%)
+- Session 4完了後: 25/46 (54%)
+- PageAlerts完了後: 25モーダル + 4 PageAlerts = **29/48 (60%)**
+
+---
+
+## 🎯 次のアクション
+
+### 即座に開始可能: Session 3
+
+**対象**:
+1. SearchOptionModal (92行) - 現在の分析対象、Case B
+
+**所要時間**: 約5-10分
+
+**完了後の効果**:
+- SearchPage利用時の不要レンダリング削減
+- 進捗: 23 → 24/46 (52%)
+
+**次のステップ**: Session 4(DeleteAiAssistantModal)
+
+---
+
+## 📝 再評価の記録 (2025-10-17)
+
+### 除外判断
+1. **ShowShortcutsModal**: 実体はモーダルコンポーネントではなく、ホットキートリガーのみ(36行、空のJSX返す)
+2. **AssociateModal, DisassociateModal**: Me画面(低頻度利用)内のモーダルのため、最適化効果限定的
+3. **ImageCropModal, DeleteSlackBotSettingsModal, PluginDeleteModal**: Admin画面(低頻度利用)内のモーダルのため、最適化効果限定的
+
+### 判断基準の明確化
+- **親ページ自体が低頻度**(Me画面、Admin画面など) → 子モーダルの最適化不要
+  - 例: Me/PersonalSettings(低頻度) → AssociateModal/DisassociateModal(最適化不要)
+  - 例: Admin/Customize(低頻度) → ImageCropModal(最適化不要)
+- **親ページが中頻度以上** → 子モーダルの頻度で判断
+  - 例: SearchPage(中頻度) → SearchOptionModal(低頻度、最適化**必要**)
+- **親ページがBasicLayout直下のdynamic()** → 親の頻度次第
+  - 例: AiAssistantSidebar(BasicLayoutから直接) → DeleteAiAssistantModal(最適化必要)
+
+---
+
+## 📝 セッション完了時の更新手順
+
+各セッション完了時に以下を更新:
+1. ✅ 完了済みリストに追加
+2. 🔄 残りリストから削除
+3. 📊 進捗チャート更新
+4. 🎯 次のアクション更新

+ 46 - 31
.serena/memories/apps-app-modal-performance-optimization-v3.md

@@ -202,7 +202,7 @@ import { ShortcutsModalLazyLoaded } from '~/client/components/ShortcutsModal';
 |--------|------|----------|
 | **ケースA** | Container-Presentation分離なし | 単一のコンポーネントのみ存在 |
 | **ケースB** | 分離済み、Container無`<Modal>` | `Substance`があるが、Containerに`<Modal>`なし |
-| **ケースC** | 分離済み、Container有`<Modal>` | Containerが`<Modal>`外枠を持つ |
+| **ケースC** | 分離済み、Container有`<Modal>` | Containerが`<Modal>`外枠を持つ ⭐最短経路 |
 
 ---
 
@@ -467,7 +467,7 @@ export const ShortcutsModalLazyLoaded = (): JSX.Element => {
     status?.isOpened ?? false,
   );
 
-  return ShortcutsModal ? <ShortcutsModal /> : <></>;
+  return ShortcutsModal ? <ShortcutsModal /> : <></>
 };
 ```
 
@@ -489,6 +489,47 @@ import { ShortcutsModalLazyLoaded } from '~/client/components/ShortcutsModal';
 
 ---
 
+## 最適化判断基準
+
+### ✅ 最適化すべきモーダル
+
+1. **モーダル自身の利用頻度が低い**(親ページの頻度ではない)
+2. **ファイルサイズが50行以上**(100行以上は強く推奨)
+3. **レンダリングコストが高い**
+
+### 最適化判断フローチャート
+
+```
+1. モーダルは常にレンダリングされるか?
+   YES → 次へ
+   NO → 最適化不要
+
+2. モーダル自身の利用頻度は?
+   高頻度 → スキップ(初期ロード維持)
+   中〜低頻度 → 次へ
+
+3. ファイルサイズは?
+   50行未満 → 効果小、要検討
+   50行以上 → V3最適化推奨
+   100行以上 → V3最適化強く推奨
+```
+
+### 重要な注意点
+
+**親の遅延ロード ≠ 子の遅延ロード**:
+```
+BasicLayout (常にレンダリング)
+  ├─ HotkeysManager (dynamic()) ← 遅延ロード
+  │    └─ ShowShortcutsModal (静的import) ← ❌ 遅延ロードされない!
+  │
+  ├─ SearchPage (dynamic()) ← 遅延ロード
+  │    └─ SearchOptionModal (静的import) ← ❌ 遅延ロードされない!
+```
+
+**結論**: 親がdynamic()でも、子モーダルは親と一緒にダウンロードされる
+
+---
+
 ## チェックリスト
 
 ### 実装確認項目
@@ -509,7 +550,7 @@ import { ShortcutsModalLazyLoaded } from '~/client/components/ShortcutsModal';
 - [ ] **Container-Presentation効果**: モーダル閉じている時、Substanceがレンダリングされない
 - [ ] TypeScriptエラーが発生しない
 
-### デグレチェック項目 🚨 NEW
+### デグレチェック項目 🚨
 - [ ] **モーダルが開くか**: トリガーボタンを押してモーダルが正しく開くことを確認
 - [ ] **State import パス**: `@growi/editor`パッケージのstateを使用していないか確認
   - LinkEditModal: `@growi/editor/dist/states/modal/link-edit`
@@ -522,7 +563,7 @@ import { ShortcutsModalLazyLoaded } from '~/client/components/ShortcutsModal';
 
 ---
 
-## デバッグガイド 🔧 NEW
+## デバッグガイド 🔧
 
 ### モーダルが開かない場合のチェックリスト
 
@@ -592,7 +633,7 @@ export const MyModal = () => { ... };
 - **Substance**: `isOpened && <Substance />`で条件付きレンダリング
 - この設計により、`<Modal isOpen={false}>`が正しくfadeout transitionを実行できる
 
-### Cross-Package State Management 🚨 NEW
+### Cross-Package State Management 🚨
 エディター関連のモーダルは`@growi/editor`パッケージでstateを管理している場合があります:
 - `~/states`からインポートできると仮定しないこと
 - モーダル本体のimport元を必ず確認すること
@@ -609,32 +650,6 @@ import { useLinkEditModalStatus } from '@growi/editor/dist/states/modal/link-edi
 
 ---
 
-## 他のモーダルへの適用優先度
-
-### 高優先度(低頻度使用モーダル) - 残り32個
-1. PagePresentationModal
-2. PageBulkExportSelectModal
-3. CreateTemplateModal
-4. SearchOptionModal
-5. ImageCropModal
-6. 管理者専用モーダル群
-
-### 中優先度(中頻度使用モーダル) - 完了✅
-- PageAccessoriesModal ✅
-- ShortcutsModal ✅
-- PageDuplicateModal ✅
-- PageRenameModal ✅
-- PageDeleteModal ✅
-- DescendantsPageListModal ✅
-
-### 低優先度(高頻度使用モーダル) - 動的ロード非推奨
-- PageCreateModal(使用頻度が非常に高いため保留)
-- SearchModal(使用頻度が非常に高いため保留)
-
-各モーダルで `importKey` を一意にし、適切な状態管理フックを使用することで同様の効果を得られる。
-
----
-
 ## 最短経路での指示テンプレート
 
 ### ケースA向け