next2d-development-mcp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +344 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +16 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/prompts/index.d.ts +3 -0
  7. package/dist/prompts/index.d.ts.map +1 -0
  8. package/dist/prompts/index.js +211 -0
  9. package/dist/prompts/index.js.map +1 -0
  10. package/dist/prompts/prompts.test.d.ts +2 -0
  11. package/dist/prompts/prompts.test.d.ts.map +1 -0
  12. package/dist/prompts/prompts.test.js +23 -0
  13. package/dist/prompts/prompts.test.js.map +1 -0
  14. package/dist/references/develop-specs.md +1576 -0
  15. package/dist/references/framework-specs.md +1687 -0
  16. package/dist/references/player-specs.md +3292 -0
  17. package/dist/resources/index.d.ts +3 -0
  18. package/dist/resources/index.d.ts.map +1 -0
  19. package/dist/resources/index.js +185 -0
  20. package/dist/resources/index.js.map +1 -0
  21. package/dist/resources/resources.test.d.ts +2 -0
  22. package/dist/resources/resources.test.d.ts.map +1 -0
  23. package/dist/resources/resources.test.js +32 -0
  24. package/dist/resources/resources.test.js.map +1 -0
  25. package/dist/templates/animation.d.ts +6 -0
  26. package/dist/templates/animation.d.ts.map +1 -0
  27. package/dist/templates/animation.js +65 -0
  28. package/dist/templates/animation.js.map +1 -0
  29. package/dist/templates/animation.test.d.ts +2 -0
  30. package/dist/templates/animation.test.d.ts.map +1 -0
  31. package/dist/templates/animation.test.js +30 -0
  32. package/dist/templates/animation.test.js.map +1 -0
  33. package/dist/templates/domainService.d.ts +9 -0
  34. package/dist/templates/domainService.d.ts.map +1 -0
  35. package/dist/templates/domainService.js +56 -0
  36. package/dist/templates/domainService.js.map +1 -0
  37. package/dist/templates/domainService.test.d.ts +2 -0
  38. package/dist/templates/domainService.test.d.ts.map +1 -0
  39. package/dist/templates/domainService.test.js +33 -0
  40. package/dist/templates/domainService.test.js.map +1 -0
  41. package/dist/templates/interfaceFile.d.ts +5 -0
  42. package/dist/templates/interfaceFile.d.ts.map +1 -0
  43. package/dist/templates/interfaceFile.js +16 -0
  44. package/dist/templates/interfaceFile.js.map +1 -0
  45. package/dist/templates/interfaceFile.test.d.ts +2 -0
  46. package/dist/templates/interfaceFile.test.d.ts.map +1 -0
  47. package/dist/templates/interfaceFile.test.js +30 -0
  48. package/dist/templates/interfaceFile.test.js.map +1 -0
  49. package/dist/templates/loading.d.ts +5 -0
  50. package/dist/templates/loading.d.ts.map +1 -0
  51. package/dist/templates/loading.js +62 -0
  52. package/dist/templates/loading.js.map +1 -0
  53. package/dist/templates/loading.test.d.ts +2 -0
  54. package/dist/templates/loading.test.d.ts.map +1 -0
  55. package/dist/templates/loading.test.js +31 -0
  56. package/dist/templates/loading.test.js.map +1 -0
  57. package/dist/templates/repository.d.ts +2 -0
  58. package/dist/templates/repository.d.ts.map +1 -0
  59. package/dist/templates/repository.js +42 -0
  60. package/dist/templates/repository.js.map +1 -0
  61. package/dist/templates/repository.test.d.ts +2 -0
  62. package/dist/templates/repository.test.d.ts.map +1 -0
  63. package/dist/templates/repository.test.js +44 -0
  64. package/dist/templates/repository.test.js.map +1 -0
  65. package/dist/templates/uiComponent.d.ts +4 -0
  66. package/dist/templates/uiComponent.d.ts.map +1 -0
  67. package/dist/templates/uiComponent.js +108 -0
  68. package/dist/templates/uiComponent.js.map +1 -0
  69. package/dist/templates/uiComponent.test.d.ts +2 -0
  70. package/dist/templates/uiComponent.test.d.ts.map +1 -0
  71. package/dist/templates/uiComponent.test.js +76 -0
  72. package/dist/templates/uiComponent.test.js.map +1 -0
  73. package/dist/templates/usecase.d.ts +2 -0
  74. package/dist/templates/usecase.d.ts.map +1 -0
  75. package/dist/templates/usecase.js +31 -0
  76. package/dist/templates/usecase.js.map +1 -0
  77. package/dist/templates/usecase.test.d.ts +2 -0
  78. package/dist/templates/usecase.test.d.ts.map +1 -0
  79. package/dist/templates/usecase.test.js +23 -0
  80. package/dist/templates/usecase.test.js.map +1 -0
  81. package/dist/templates/view.d.ts +3 -0
  82. package/dist/templates/view.d.ts.map +1 -0
  83. package/dist/templates/view.js +112 -0
  84. package/dist/templates/view.js.map +1 -0
  85. package/dist/templates/view.test.d.ts +2 -0
  86. package/dist/templates/view.test.d.ts.map +1 -0
  87. package/dist/templates/view.test.js +71 -0
  88. package/dist/templates/view.test.js.map +1 -0
  89. package/dist/tools/addRoute.d.ts +3 -0
  90. package/dist/tools/addRoute.d.ts.map +1 -0
  91. package/dist/tools/addRoute.js +101 -0
  92. package/dist/tools/addRoute.js.map +1 -0
  93. package/dist/tools/createAnimation.d.ts +3 -0
  94. package/dist/tools/createAnimation.d.ts.map +1 -0
  95. package/dist/tools/createAnimation.js +53 -0
  96. package/dist/tools/createAnimation.js.map +1 -0
  97. package/dist/tools/createDomainService.d.ts +3 -0
  98. package/dist/tools/createDomainService.d.ts.map +1 -0
  99. package/dist/tools/createDomainService.js +82 -0
  100. package/dist/tools/createDomainService.js.map +1 -0
  101. package/dist/tools/createInterface.d.ts +3 -0
  102. package/dist/tools/createInterface.d.ts.map +1 -0
  103. package/dist/tools/createInterface.js +59 -0
  104. package/dist/tools/createInterface.js.map +1 -0
  105. package/dist/tools/createLoading.d.ts +3 -0
  106. package/dist/tools/createLoading.d.ts.map +1 -0
  107. package/dist/tools/createLoading.js +52 -0
  108. package/dist/tools/createLoading.js.map +1 -0
  109. package/dist/tools/createRepository.d.ts +3 -0
  110. package/dist/tools/createRepository.d.ts.map +1 -0
  111. package/dist/tools/createRepository.js +55 -0
  112. package/dist/tools/createRepository.js.map +1 -0
  113. package/dist/tools/createUiComponent.d.ts +3 -0
  114. package/dist/tools/createUiComponent.d.ts.map +1 -0
  115. package/dist/tools/createUiComponent.js +80 -0
  116. package/dist/tools/createUiComponent.js.map +1 -0
  117. package/dist/tools/createUseCase.d.ts +3 -0
  118. package/dist/tools/createUseCase.d.ts.map +1 -0
  119. package/dist/tools/createUseCase.js +52 -0
  120. package/dist/tools/createUseCase.js.map +1 -0
  121. package/dist/tools/createView.d.ts +3 -0
  122. package/dist/tools/createView.d.ts.map +1 -0
  123. package/dist/tools/createView.js +59 -0
  124. package/dist/tools/createView.js.map +1 -0
  125. package/dist/tools/index.d.ts +5 -0
  126. package/dist/tools/index.d.ts.map +1 -0
  127. package/dist/tools/index.js +25 -0
  128. package/dist/tools/index.js.map +1 -0
  129. package/dist/tools/tools.test.d.ts +2 -0
  130. package/dist/tools/tools.test.d.ts.map +1 -0
  131. package/dist/tools/tools.test.js +58 -0
  132. package/dist/tools/tools.test.js.map +1 -0
  133. package/dist/tools/validateArchitecture.d.ts +3 -0
  134. package/dist/tools/validateArchitecture.d.ts.map +1 -0
  135. package/dist/tools/validateArchitecture.js +134 -0
  136. package/dist/tools/validateArchitecture.js.map +1 -0
  137. package/dist/utils.d.ts +10 -0
  138. package/dist/utils.d.ts.map +1 -0
  139. package/dist/utils.js +18 -0
  140. package/dist/utils.js.map +1 -0
  141. package/dist/utils.test.d.ts +2 -0
  142. package/dist/utils.test.d.ts.map +1 -0
  143. package/dist/utils.test.js +34 -0
  144. package/dist/utils.test.js.map +1 -0
  145. package/package.json +48 -0
@@ -0,0 +1,1576 @@
1
+ # Next2D Framework TypeScript Template - Development Specs
2
+
3
+ ## Table of Contents
4
+
5
+ 1. [Overview](#next2d-framework-typescript-template---overview)
6
+ 2. [CLI Commands Reference](#cli-commands-reference)
7
+ 3. [Configuration Files](#configuration-files)
8
+ 4. [Interface Definitions](#interface-definitions)
9
+ 5. [Model Layer (Application / Domain / Infrastructure)](#model-layer-application--domain--infrastructure)
10
+ 6. [UI Layer (Components / Animation / Content)](#ui-layer-components--animation--content)
11
+ 7. [View / ViewModel (MVVM Pattern)](#view--viewmodel-mvvm-pattern)
12
+
13
+ ---
14
+
15
+ # Next2D Framework TypeScript Template - Overview
16
+
17
+ ## Project Summary
18
+
19
+ Next2D Frameworkを使用したTypeScriptプロジェクトテンプレート。MVVM + Clean Architecture + Atomic Designを採用。
20
+
21
+ - **レンダリングエンジン**: Next2D Player
22
+ - **フレームワーク**: Next2D Framework
23
+ - **言語**: TypeScript
24
+ - **ビルドツール**: Vite
25
+ - **テスト**: Vitest
26
+ - **パッケージマネージャ**: npm
27
+
28
+ ## Requirements
29
+
30
+ - Node.js 22.x以上
31
+ - npm 10.x以上
32
+ - iOS: Xcode 14以上 (iOS/Androidビルド時のみ)
33
+ - Android: Android Studio, JDK 21以上 (iOS/Androidビルド時のみ)
34
+
35
+ ## Architecture
36
+
37
+ **MVVM + Clean Architecture + Atomic Design** の5層構成:
38
+
39
+ ```
40
+ View Layer (view/, ui/)
41
+ └─ depends on ─→ Interface Layer (interface/)
42
+
43
+ Application Layer (model/application/)
44
+ ├─ depends on ─→ Interface Layer
45
+ ├─ depends on ─→ Domain Layer (model/domain/)
46
+ └─ calls ──────→ Infrastructure Layer (model/infrastructure/)
47
+ ```
48
+
49
+ ### Layer Dependencies (依存関係の方向)
50
+
51
+ - **View層** → Interface経由でApplication層を使用
52
+ - **Application層** → Interface経由でDomain層・Infrastructure層を使用
53
+ - **Domain層** → 何にも依存しない(最も安定、純粋なビジネスロジック)
54
+ - **Infrastructure層** → Interface層を実装
55
+
56
+ ### Key Design Patterns
57
+
58
+ 1. **MVVM**: View(表示) / ViewModel(橋渡し) / Model(ビジネスロジック+データアクセス)
59
+ 2. **UseCase Pattern**: ユーザーアクションごとに専用のUseCaseクラスを作成
60
+ 3. **Dependency Inversion**: 具象クラスではなくインターフェースに依存
61
+ 4. **Repository Pattern**: データアクセスを抽象化
62
+ 5. **Atomic Design**: Atom → Molecule → Organism → Template → Page
63
+
64
+ ## Directory Structure
65
+
66
+ ```
67
+ src/
68
+ ├── config/ # 設定ファイル (stage.json, config.json, routing.json)
69
+ ├── interface/ # TypeScriptインターフェース定義
70
+ ├── model/
71
+ │ ├── application/ # UseCase (ビジネスロジック実装)
72
+ │ │ └── {screen}/usecase/
73
+ │ ├── domain/ # コアビジネスロジック
74
+ │ │ └── {feature}/service/
75
+ │ └── infrastructure/ # Repository (データアクセス)
76
+ │ └── repository/
77
+ ├── ui/
78
+ │ ├── animation/ # アニメーション定義
79
+ │ │ └── {screen}/
80
+ │ ├── component/
81
+ │ │ ├── atom/ # 最小コンポーネント (Button, Text等)
82
+ │ │ ├── molecule/ # 複合コンポーネント
83
+ │ │ ├── organism/ # 複数Moleculeの組み合わせ (拡張用)
84
+ │ │ ├── page/ # ページコンポーネント
85
+ │ │ │ └── {screen}/
86
+ │ │ └── template/ # ページテンプレート (拡張用)
87
+ │ └── content/ # Animation Tool生成コンテンツ
88
+ ├── view/ # View & ViewModel
89
+ │ └── {screen}/
90
+ │ ├── {Screen}View.ts
91
+ │ └── {Screen}ViewModel.ts
92
+ └── assets/ # 静的ファイル (画像, JSON)
93
+
94
+ @types/ # グローバル型定義 (.d.ts)
95
+ electron/ # Electron設定 (デスクトップビルド用)
96
+ file/ # Animation Tool n2dファイル
97
+ mock/ # 開発用モックデータ (API, Content, 画像)
98
+ ```
99
+
100
+ ## Best Practices (全体共通)
101
+
102
+ 1. **インターフェース優先**: 常に具象クラスではなくインターフェースに依存
103
+ 2. **単一責任の原則**: 各クラスは1つの責務のみを持つ
104
+ 3. **型安全性**: `any`型を避け、明示的な型定義を使用
105
+ 4. **テスタブル**: 各層を独立してテスト可能にする
106
+ 5. **JSDoc**: 処理内容を日英両方で明記
107
+ 6. **executeメソッド**: UseCaseのエントリーポイントを統一
108
+ 7. **エラーハンドリング**: Infrastructure層で適切に処理
109
+
110
+ ---
111
+
112
+ # CLI Commands Reference
113
+
114
+ ## Setup
115
+
116
+ ```bash
117
+ npm install # 依存パッケージのインストール
118
+ ```
119
+
120
+ ## Development
121
+
122
+ ```bash
123
+ npm start # 開発サーバー起動 (http://localhost:5173)
124
+ npm run generate # routing.jsonからView/ViewModelクラスを自動生成
125
+ ```
126
+
127
+ ## Testing
128
+
129
+ ```bash
130
+ npm test # 全テスト実行 (Vitest)
131
+ npm test -- --watch # ウォッチモード
132
+ npm test -- --coverage # カバレッジレポート
133
+ ```
134
+
135
+ ## Build
136
+
137
+ | Command | Platform | Output |
138
+ |---------|----------|--------|
139
+ | `npm run build:web -- --env prd` | Web (HTML) | `dist/web/prd/` |
140
+ | `npm run build:steam:windows -- --env prd` | Windows (Steam) | `dist/steam/windows/` |
141
+ | `npm run build:steam:macos -- --env prd` | macOS (Steam) | `dist/steam/macos/` |
142
+ | `npm run build:steam:linux -- --env prd` | Linux (Steam) | `dist/steam/linux/` |
143
+ | `npm run build:ios -- --env prd` | iOS | Xcode project |
144
+ | `npm run build:android -- --env prd` | Android | Android Studio project |
145
+
146
+ ## Platform Emulators
147
+
148
+ ```bash
149
+ npm run preview:windows -- --env prd # Windows
150
+ npm run preview:macos -- --env prd # macOS
151
+ npm run preview:linux -- --env prd # Linux
152
+ npm run preview:ios -- --env prd # iOS
153
+ npm run preview:android -- --env prd # Android
154
+ ```
155
+
156
+ `--env` オプション: `local`, `dev`, `stg`, `prd`
157
+
158
+ ## Environment Configuration
159
+
160
+ 環境ごとの設定は`src/config/config.json`で管理。`--env`で指定した環境名の設定値と`all`の設定値がマージされる。
161
+
162
+ ---
163
+
164
+ # Configuration Files
165
+
166
+ 設定ファイルは `src/config/` ディレクトリに配置。
167
+
168
+ ## stage.json
169
+
170
+ 表示領域(Stage)の設定。
171
+
172
+ | Property | Type | Default | Description |
173
+ |----------|------|---------|-------------|
174
+ | `width` | number | 240 | 表示領域の幅 |
175
+ | `height` | number | 240 | 表示領域の高さ |
176
+ | `fps` | number | 60 | 描画回数/秒 (1-60) |
177
+ | `options` | object | null | オプション設定 |
178
+
179
+ ### Stage Options
180
+
181
+ | Property | Type | Default | Description |
182
+ |----------|------|---------|-------------|
183
+ | `options.fullScreen` | boolean | false | 画面全体に描画 |
184
+ | `options.tagId` | string | null | 描画先のエレメントID |
185
+ | `options.bgColor` | string | "transparent" | 背景色 (16進数) |
186
+
187
+ ### Example
188
+
189
+ ```json
190
+ {
191
+ "width": 240,
192
+ "height": 240,
193
+ "fps": 60,
194
+ "options": {
195
+ "fullScreen": true
196
+ }
197
+ }
198
+ ```
199
+
200
+ ---
201
+
202
+ ## config.json
203
+
204
+ 環境別の設定ファイル。`local`, `dev`, `stg`, `prd`, `all` に分離。
205
+
206
+ ### Structure
207
+
208
+ ```json
209
+ {
210
+ "local": { "api": { "endPoint": "/" }, "content": { "endPoint": "/" } },
211
+ "dev": { "api": { "endPoint": "/" }, "content": { "endPoint": "/" } },
212
+ "stg": { "api": { "endPoint": "/" }, "content": { "endPoint": "/" } },
213
+ "prd": { "api": { "endPoint": "https://..." }, "content": { "endPoint": "https://..." } },
214
+ "all": { /* 全環境共通 */ }
215
+ }
216
+ ```
217
+
218
+ ### `all` Properties (全環境共通)
219
+
220
+ | Property | Type | Default | Description |
221
+ |----------|------|---------|-------------|
222
+ | `defaultTop` | string | "top" | ページトップのView名 |
223
+ | `spa` | boolean | true | SPA (URLでシーン制御) |
224
+ | `loading.callback` | string | "Loading" | ローディング画面のコールバッククラス。start/end関数が呼ばれる |
225
+ | `gotoView.callback` | string/array | ["callback.Background"] | 画面遷移完了後のコールバッククラス。execute関数がasync/awaitで呼ばれる |
226
+
227
+ ### `platform` Property
228
+
229
+ ビルド時の`--platform`値がセットされる。値: `macos`, `windows`, `linux`, `ios`, `android`, `web`
230
+
231
+ ### Config Access in Code
232
+
233
+ ```typescript
234
+ import { config } from "@/config/Config";
235
+
236
+ const endpoint = config.api.endPoint;
237
+ const stageWidth = config.stage.width;
238
+ ```
239
+
240
+ ---
241
+
242
+ ## routing.json
243
+
244
+ ルーティング設定。トッププロパティは英数字・スラッシュ。スラッシュをキーにCamelCaseでViewクラスにアクセス。
245
+
246
+ ### Routing Example
247
+
248
+ ```json
249
+ {
250
+ "quest/list": {
251
+ "requests": []
252
+ }
253
+ }
254
+ ```
255
+
256
+ → `https://example.com/quest/list` でアクセス可能。`QuestListView`クラスがセットされる。
257
+
258
+ ### Cluster Pattern (共通リクエストの再利用)
259
+
260
+ `@`プレフィックスで共通リクエスト群を定義し、他のルートから参照:
261
+
262
+ ```json
263
+ {
264
+ "@sample": {
265
+ "requests": [
266
+ {
267
+ "type": "content",
268
+ "path": "{{ content.endPoint }}content/sample.json",
269
+ "name": "MainContent",
270
+ "cache": true
271
+ }
272
+ ]
273
+ },
274
+ "top": {
275
+ "requests": [
276
+ { "type": "cluster", "path": "@sample" },
277
+ { "type": "json", "path": "{{ api.endPoint }}api/top.json", "name": "TopText" }
278
+ ]
279
+ }
280
+ }
281
+ ```
282
+
283
+ ### Second Level Properties
284
+
285
+ | Property | Type | Default | Description |
286
+ |----------|------|---------|-------------|
287
+ | `private` | boolean | false | true時、URLアクセスするとTopViewが読み込まれる |
288
+ | `requests` | array | null | Viewバインド前に実行するリクエスト群 |
289
+
290
+ ### Request Properties
291
+
292
+ | Property | Type | Default | Description |
293
+ |----------|------|---------|-------------|
294
+ | `type` | string | "content" | `json`, `content`, `custom`, `cluster` |
295
+ | `path` | string | "" | `{{***}}`でconfig変数を参照可能。`@`プレフィックスでcluster参照 |
296
+ | `name` | string | "" | responseのキー名。`app.getResponse().get("key")` |
297
+ | `cache` | boolean | false | キャッシュ有効。`app.getCache().get("key")` |
298
+ | `callback` | string/array | null | リクエスト完了後のコールバッククラス。execute関数が呼ばれる |
299
+ | `class` | string | "" | custom type時のリクエスト実行クラス |
300
+ | `access` | string | "public" | custom type時の関数アクセス (`public`/`static`) |
301
+ | `method` | string | "" | custom type時の関数名 |
302
+
303
+ ### Request Types
304
+
305
+ - **`json`**: URLからJSONを取得
306
+ - **`content`**: Animation Toolコンテンツを取得
307
+ - **`custom`**: 指定クラスのメソッドを実行
308
+ - **`cluster`**: `@`プレフィックスの共通リクエスト群を参照
309
+
310
+ ### Data Access
311
+
312
+ ```typescript
313
+ // responseデータ (画面遷移で初期化される)
314
+ const data = app.getResponse().get("HomeText");
315
+
316
+ // cacheデータ (画面遷移しても保持される)
317
+ const cached = app.getCache().get("MainContent");
318
+ ```
319
+
320
+ ---
321
+
322
+ ## Static Files
323
+
324
+ ### mock/ Directory
325
+
326
+ ローカル開発用モックデータ。`http://localhost:5173/***`でアクセス可能。`routing.json`のパスと重複しないよう注意。
327
+
328
+ ```
329
+ mock/
330
+ ├── api/ # APIモック (JSON)
331
+ ├── content/ # Animation Toolコンテンツモック
332
+ └── img/ # 画像モック
333
+ ```
334
+
335
+ ### file/ Directory
336
+
337
+ Animation Toolで作成した`.n2d`ファイルを格納。バージョン管理可能。
338
+
339
+ ### assets/ Directory
340
+
341
+ ビルド時にバンドルに含める静的アセット。
342
+
343
+ ```typescript
344
+ // 画像インポート
345
+ import logoImage from "@/assets/logo.png?inline";
346
+
347
+ // JSONインポート
348
+ import animation from "@/assets/animation.json";
349
+ ```
350
+
351
+ | 項目 | assets | mock |
352
+ |------|--------|------|
353
+ | 用途 | バンドルに含める | 開発サーバーで配信 |
354
+ | アクセス | importで取得 | URL経由でfetch |
355
+ | ビルド | バンドルに含まれる | 含まれない |
356
+
357
+ ---
358
+
359
+ ## @types/ Directory
360
+
361
+ グローバルな型定義ファイル (.d.ts)。`Window`インターフェースの拡張等。アプリケーション固有のインターフェースは`src/interface/`に配置。
362
+
363
+ ---
364
+
365
+ # Interface Definitions
366
+
367
+ TypeScriptインターフェース定義。Clean Architecture原則に従い、各層の依存関係を抽象化。
368
+
369
+ ## Rules
370
+
371
+ - 命名規則: `I` プレフィックスを使用 (例: `IDraggable`, `ITextField`)
372
+ - 必要最小限のプロパティのみ定義
373
+ - `any`型を禁止、常に明示的な型を使用
374
+ - JSDocコメントを追加
375
+
376
+ ## Interface Categories
377
+
378
+ ### 1. UI関連 (コンポーネントの振る舞い)
379
+
380
+ ```typescript
381
+ // IDraggable.ts - ドラッグ可能なオブジェクト
382
+ export interface IDraggable {
383
+ startDrag(): void;
384
+ stopDrag(): void;
385
+ }
386
+ // 使用: HomeBtnMolecule, HomeContent
387
+
388
+ // ITextField.ts - テキストフィールドの基本プロパティ
389
+ export interface ITextField {
390
+ width: number;
391
+ x: number;
392
+ }
393
+ // 使用: TextAtom, CenterTextFieldUseCase
394
+
395
+ // ITextFieldProps.ts - テキストフィールドの詳細プロパティ設定
396
+ // 使用: TextAtomのコンストラクタ
397
+
398
+ // ITextFieldType.ts - テキストフィールドタイプ
399
+ // ITextFieldAutoSize.ts - テキストフィールドオートサイズ
400
+ // ITextFormatAlign.ts - テキストフォーマットアライン
401
+ // ITextFormatObject.ts - テキストフォーマットスタイル設定
402
+ ```
403
+
404
+ ### 2. データ転送オブジェクト (DTO)
405
+
406
+ ```typescript
407
+ // IHomeTextResponse.ts - APIレスポンス型
408
+ export interface IHomeTextResponse {
409
+ word: string;
410
+ }
411
+ // 使用: HomeTextRepository.get()の戻り値型
412
+ ```
413
+
414
+ ### 3. 画面遷移関連
415
+
416
+ ```typescript
417
+ // IViewName.ts - 利用可能な画面名 (Union型)
418
+ export type ViewName = "top" | "home";
419
+ // 使用: NavigateToViewUseCase
420
+ // 新画面追加時はこの型にも追加が必要
421
+ ```
422
+
423
+ ### 4. 設定関連
424
+
425
+ - `IConfig.ts` - アプリケーション全体設定
426
+ - `IStage.ts` - ステージ設定 (`stage.json`の型)
427
+ - `IRouting.ts` - ルーティング設定
428
+ - `IGotoView.ts` - 画面遷移オプション
429
+ - `IRequest.ts` / `IRequestType.ts` - HTTPリクエスト設定
430
+ - `IOptions.ts` - オプション設定
431
+
432
+ ## Interface Template
433
+
434
+ ```typescript
435
+ /**
436
+ * @description [インターフェースの説明]
437
+ * [Interface description]
438
+ *
439
+ * @interface
440
+ */
441
+ export interface IYourInterface
442
+ {
443
+ /**
444
+ * @description [プロパティの説明]
445
+ * [Property description]
446
+ *
447
+ * @type {type}
448
+ */
449
+ propertyName: type;
450
+
451
+ /**
452
+ * @description [メソッドの説明]
453
+ * [Method description]
454
+ *
455
+ * @param {ParamType} paramName
456
+ * @return {ReturnType}
457
+ * @method
458
+ */
459
+ methodName(paramName: ParamType): ReturnType;
460
+ }
461
+ ```
462
+
463
+ ## Best Practices
464
+
465
+ ```typescript
466
+ // OK: 必要最小限
467
+ export interface ITextField {
468
+ width: number;
469
+ x: number;
470
+ }
471
+
472
+ // NG: 不要なプロパティ
473
+ export interface ITextField {
474
+ width: number;
475
+ height: number; // 使用しない
476
+ x: number;
477
+ y: number; // 使用しない
478
+ }
479
+
480
+ // OK: 型の再利用
481
+ export interface IPosition { x: number; y: number; }
482
+ export interface ITextField extends IPosition { width: number; }
483
+
484
+ // OK: 明示的型
485
+ export interface IHomeTextResponse { word: string; }
486
+
487
+ // NG: any型
488
+ export interface IHomeTextResponse { word: any; }
489
+ ```
490
+
491
+ ## Adding New Interface Steps
492
+
493
+ 1. 目的を明確にする(どの層の依存を抽象化するか)
494
+ 2. `I`プレフィックスの命名規則に従う
495
+ 3. 必要最小限のプロパティ/メソッドのみ定義
496
+ 4. JSDocコメントを追加
497
+ 5. 使用箇所を明記
498
+
499
+ ---
500
+
501
+ # Model Layer (Application / Domain / Infrastructure)
502
+
503
+ Model層はビジネスロジックとデータアクセスを担当。Clean Architectureに基づき3層で構成。
504
+
505
+ ## Directory Structure
506
+
507
+ ```
508
+ model/
509
+ ├── application/ # UseCase (ビジネスロジック)
510
+ │ └── {screen}/
511
+ │ └── usecase/
512
+ │ └── {Action}UseCase.ts
513
+ ├── domain/ # コアビジネスロジック
514
+ │ └── {feature}/
515
+ │ ├── {Feature}.ts
516
+ │ └── service/
517
+ │ └── {Feature}{Action}Service.ts
518
+ └── infrastructure/ # Repository (データアクセス)
519
+ └── repository/
520
+ └── {Resource}Repository.ts
521
+ ```
522
+
523
+ ## Layer Dependencies
524
+
525
+ ```
526
+ Application → Domain (uses)
527
+ Application → Infrastructure (calls)
528
+ Domain → 依存なし (最も安定)
529
+ ```
530
+
531
+ ---
532
+
533
+ ## Application Layer (UseCase)
534
+
535
+ ### Rules
536
+
537
+ - 1つのユーザーアクションに対して1つのUseCaseクラスを作成
538
+ - エントリーポイントは `execute` メソッドに統一
539
+ - インターフェースに依存し、具象クラスに依存しない
540
+ - 画面ごとにディレクトリを作成: `application/{screen}/usecase/`
541
+
542
+ ### UseCase Template
543
+
544
+ ```typescript
545
+ import type { IYourInterface } from "@/interface/IYourInterface";
546
+
547
+ /**
548
+ * @description [UseCaseの説明]
549
+ * [UseCase description]
550
+ *
551
+ * @class
552
+ */
553
+ export class YourUseCase
554
+ {
555
+ /**
556
+ * @description [処理の説明]
557
+ * [Process description]
558
+ *
559
+ * @param {IYourInterface} param
560
+ * @return {void}
561
+ * @method
562
+ * @public
563
+ */
564
+ execute (param: IYourInterface): void
565
+ {
566
+ // ビジネスロジックを実装
567
+ }
568
+ }
569
+ ```
570
+
571
+ ### UseCase with Repository
572
+
573
+ ```typescript
574
+ import { YourRepository } from "@/model/infrastructure/repository/YourRepository";
575
+ import type { IYourResponse } from "@/interface/IYourResponse";
576
+
577
+ export class FetchDataUseCase
578
+ {
579
+ async execute (): Promise<IYourResponse>
580
+ {
581
+ try {
582
+ const data = await YourRepository.get();
583
+ // ビジネスロジック: データの加工・検証
584
+ return data;
585
+ } catch (error) {
586
+ console.error('Failed to fetch data:', error);
587
+ throw error;
588
+ }
589
+ }
590
+ }
591
+ ```
592
+
593
+ ### UseCase Composition (複数UseCaseの組み合わせ)
594
+
595
+ ```typescript
596
+ export class InitializeScreenUseCase
597
+ {
598
+ private readonly fetchUseCase: FetchDataUseCase;
599
+ private readonly centerUseCase: CenterTextFieldUseCase;
600
+
601
+ constructor ()
602
+ {
603
+ this.fetchUseCase = new FetchDataUseCase();
604
+ this.centerUseCase = new CenterTextFieldUseCase();
605
+ }
606
+
607
+ async execute (textField: ITextField): Promise<void>
608
+ {
609
+ const data = await this.fetchUseCase.execute();
610
+ this.centerUseCase.execute(textField, stageWidth);
611
+ }
612
+ }
613
+ ```
614
+
615
+ ### UseCase Anti-Patterns
616
+
617
+ ```typescript
618
+ // NG: 複数の責務
619
+ export class DragUseCase {
620
+ start(target: IDraggable): void { ... }
621
+ stop(target: IDraggable): void { ... }
622
+ validate(target: IDraggable): boolean { ... }
623
+ }
624
+
625
+ // OK: 単一の責務
626
+ export class StartDragUseCase {
627
+ execute(target: IDraggable): void { target.startDrag(); }
628
+ }
629
+ export class StopDragUseCase {
630
+ execute(target: IDraggable): void { target.stopDrag(); }
631
+ }
632
+
633
+ // NG: 具象クラスに依存
634
+ execute(target: HomeBtnMolecule): void { ... }
635
+
636
+ // OK: インターフェースに依存
637
+ execute(target: IDraggable): void { ... }
638
+ ```
639
+
640
+ ### UseCase Test Template
641
+
642
+ ```typescript
643
+ import { StartDragUseCase } from "./StartDragUseCase";
644
+ import type { IDraggable } from "@/interface/IDraggable";
645
+
646
+ describe('StartDragUseCase', () => {
647
+ test('should call startDrag on target', () => {
648
+ const mockDraggable: IDraggable = {
649
+ startDrag: vi.fn(),
650
+ stopDrag: vi.fn()
651
+ };
652
+
653
+ const useCase = new StartDragUseCase();
654
+ useCase.execute(mockDraggable);
655
+
656
+ expect(mockDraggable.startDrag).toHaveBeenCalled();
657
+ });
658
+ });
659
+ ```
660
+
661
+ ---
662
+
663
+ ## Domain Layer
664
+
665
+ ### Rules
666
+
667
+ - アプリケーションのコアビジネスルールを実装
668
+ - 可能な限りフレームワーク非依存(※Next2D描画機能の使用は許容)
669
+ - 純粋関数を心がけ、副作用を最小化
670
+ - 可能な限り不変オブジェクトを使用
671
+
672
+ ### Domain Service (Functional Style)
673
+
674
+ ```typescript
675
+ /**
676
+ * @description [サービスの説明]
677
+ * [Service description]
678
+ *
679
+ * @param {ParamType} param
680
+ * @return {ReturnType}
681
+ */
682
+ export const execute = (param: ParamType): ReturnType =>
683
+ {
684
+ // ビジネスルールの実装
685
+ return result;
686
+ };
687
+ ```
688
+
689
+ ### Domain Class (Class-based Style)
690
+
691
+ ```typescript
692
+ import { Shape, stage } from "@next2d/display";
693
+ import { Event } from "@next2d/events";
694
+
695
+ /**
696
+ * @description [ドメインクラスの説明]
697
+ * [Domain class description]
698
+ *
699
+ * @class
700
+ */
701
+ export class YourDomainClass
702
+ {
703
+ public readonly shape: Shape;
704
+
705
+ constructor ()
706
+ {
707
+ this.shape = new Shape();
708
+ }
709
+
710
+ execute (): void
711
+ {
712
+ // コアビジネスロジック
713
+ }
714
+ }
715
+ ```
716
+
717
+ ### Domain Callback Pattern
718
+
719
+ `config.json`の`gotoView.callback`で設定されたクラスは、画面遷移完了後に`execute()`が呼び出される。
720
+
721
+ ```typescript
722
+ // config.json: "gotoView": { "callback": ["domain.callback.Background"] }
723
+ // → model/domain/callback/Background.ts の execute() が呼ばれる
724
+
725
+ export class Background
726
+ {
727
+ execute (): void
728
+ {
729
+ const context = app.getContext();
730
+ const view = context.view;
731
+ if (!view) return;
732
+ view.addChildAt(this.shape, 0);
733
+ }
734
+ }
735
+ ```
736
+
737
+ ### Domain Directory Extensions (将来の拡張)
738
+
739
+ ```
740
+ domain/
741
+ ├── callback/ # コールバック処理
742
+ ├── service/ # ドメインサービス
743
+ ├── entity/ # エンティティ (ID持ち)
744
+ └── value-object/ # 値オブジェクト
745
+ ```
746
+
747
+ ---
748
+
749
+ ## Infrastructure Layer (Repository)
750
+
751
+ ### Rules
752
+
753
+ - 外部システムとの連携(API、DB等)を担当
754
+ - `any`型を避け、明示的な型定義を使用
755
+ - すべての外部アクセスでtry-catchを実装
756
+ - エンドポイントは`config`から取得(ハードコーディング禁止)
757
+ - シンプルな場合は静的メソッド、状態を持つ場合はインスタンスメソッド
758
+
759
+ ### Repository Template
760
+
761
+ ```typescript
762
+ import type { IYourResponse } from "@/interface/IYourResponse";
763
+ import { config } from "@/config/Config";
764
+
765
+ /**
766
+ * @description [Repositoryの説明]
767
+ * [Repository description]
768
+ *
769
+ * @class
770
+ */
771
+ export class YourRepository
772
+ {
773
+ /**
774
+ * @description [処理の説明]
775
+ * [Process description]
776
+ *
777
+ * @param {string} id
778
+ * @return {Promise<IYourResponse>}
779
+ * @static
780
+ * @throws {Error} [エラーの説明]
781
+ */
782
+ static async get (id: string): Promise<IYourResponse>
783
+ {
784
+ try {
785
+ const response = await fetch(
786
+ `${config.api.endPoint}api/your-endpoint/${id}`
787
+ );
788
+
789
+ if (!response.ok) {
790
+ throw new Error(
791
+ `HTTP error! status: ${response.status}`
792
+ );
793
+ }
794
+
795
+ return await response.json() as IYourResponse;
796
+
797
+ } catch (error) {
798
+ console.error('Failed to fetch data:', error);
799
+ throw error;
800
+ }
801
+ }
802
+ }
803
+ ```
804
+
805
+ ### Repository with Cache
806
+
807
+ ```typescript
808
+ export class CachedRepository
809
+ {
810
+ private static cache: Map<string, { data: Data; timestamp: number }> = new Map();
811
+ private static readonly CACHE_TTL = 60000;
812
+
813
+ static async get (id: string): Promise<Data>
814
+ {
815
+ const cached = this.cache.get(id);
816
+ const now = Date.now();
817
+
818
+ if (cached && (now - cached.timestamp) < this.CACHE_TTL) {
819
+ return cached.data;
820
+ }
821
+
822
+ const response = await fetch(`${config.api.endPoint}api/${id}`);
823
+ const data = await response.json();
824
+ this.cache.set(id, { data, timestamp: now });
825
+
826
+ return data;
827
+ }
828
+ }
829
+ ```
830
+
831
+ ### Repository Anti-Patterns
832
+
833
+ ```typescript
834
+ // NG: any型
835
+ static async get(): Promise<any> { ... }
836
+
837
+ // OK: 明示的型定義
838
+ static async get(): Promise<IHomeTextResponse> { ... }
839
+
840
+ // NG: エラーハンドリングなし
841
+ static async get(): Promise<Data> {
842
+ const response = await fetch(...);
843
+ return await response.json();
844
+ }
845
+
846
+ // NG: ハードコーディング
847
+ const response = await fetch('https://example.com/api/data.json');
848
+
849
+ // OK: configから取得
850
+ const response = await fetch(`${config.api.endPoint}api/data.json`);
851
+ ```
852
+
853
+ ### routing.json Custom Request Pattern
854
+
855
+ `routing.json`でRepositoryを直接呼び出すことも可能:
856
+
857
+ ```json
858
+ {
859
+ "home": {
860
+ "requests": [
861
+ {
862
+ "type": "custom",
863
+ "class": "infrastructure.repository.HomeTextRepository",
864
+ "access": "static",
865
+ "method": "get",
866
+ "name": "HomeText",
867
+ "cache": true
868
+ }
869
+ ]
870
+ }
871
+ }
872
+ ```
873
+
874
+ 取得したデータは `app.getResponse().get("HomeText")` でアクセス可能。
875
+
876
+ ---
877
+
878
+ # UI Layer (Components / Animation / Content)
879
+
880
+ ## Directory Structure
881
+
882
+ ```
883
+ ui/
884
+ ├── animation/ # アニメーション定義
885
+ │ └── {screen}/
886
+ │ └── {Component}{Action}Animation.ts
887
+ ├── component/
888
+ │ ├── atom/ # 最小単位 (Button, Text等)
889
+ │ ├── molecule/ # Atomの組み合わせ
890
+ │ ├── organism/ # 複数Moleculeの組み合わせ (拡張用)
891
+ │ ├── page/ # ページコンポーネント
892
+ │ │ └── {screen}/
893
+ │ └── template/ # ページテンプレート (拡張用)
894
+ └── content/ # Animation Tool生成コンテンツ
895
+ ```
896
+
897
+ ## Rules (共通)
898
+
899
+ - 各コンポーネントは単一の責務のみ
900
+ - ビジネスロジックやデータアクセスに直接依存しない
901
+ - データはViewModelから引数で受け取る
902
+ - インターフェースを実装して抽象化する
903
+
904
+ ---
905
+
906
+ ## DisplayObject 配置の基本方針(中心基準点パターン)
907
+
908
+ Next2D の座標系は画面左上 (0, 0) が基準点。Shape や Sprite をそのまま配置すると、スケールや回転アニメーション時に座標がずれる。
909
+ **基本方針として、子要素を親 Sprite に中心配置する。**
910
+
911
+ ### パターン: Shape を Sprite に中心配置
912
+
913
+ ```typescript
914
+ const sprite = new Sprite();
915
+ const shape = new Shape();
916
+
917
+ // 画像の場合、Retina対応でスケールを設定
918
+ shape.scaleX = shape.scaleY = 0.5;
919
+
920
+ // スケール設定後に中心配置
921
+ shape.x = -shape.width / 2;
922
+ shape.y = -shape.height / 2;
923
+
924
+ sprite.addChild(shape);
925
+ ```
926
+
927
+ ### パターン: Sprite を Sprite に中心配置
928
+
929
+ ```typescript
930
+ const parent = new Sprite();
931
+ const child = new Sprite();
932
+
933
+ // 子要素のサイズ確定後に中心配置
934
+ child.x = -child.width / 2;
935
+ child.y = -child.height / 2;
936
+
937
+ parent.addChild(child);
938
+ ```
939
+
940
+ ### なぜ中心配置が必要か
941
+
942
+ - スケール・回転は DisplayObject の (0, 0) を基点に実行される
943
+ - 中心配置しないと、回転・拡縮時に意図しない位置ずれが発生する
944
+ - 中心配置により、親 Sprite の (x, y) がそのまま表示上の中心座標になる
945
+
946
+ **注意:** すべてのケースで必須ではないが、アニメーション対象の要素には基本的にこのパターンを適用する。
947
+
948
+ ---
949
+
950
+ ## Atomic Design Hierarchy
951
+
952
+ ### Atom (原子) - 最小単位
953
+
954
+ 最も基本的なUI要素。これ以上分割できない。
955
+
956
+ ```typescript
957
+ // ButtonAtom: ボタンの基本機能 (enable/disable による連続押下防止機能付き)
958
+ import { Sprite } from "@next2d/display";
959
+
960
+ export class ButtonAtom extends Sprite
961
+ {
962
+ constructor ()
963
+ {
964
+ super();
965
+ this.buttonMode = true;
966
+ }
967
+
968
+ /**
969
+ * @description ボタンを有効化する
970
+ * Enable button
971
+ *
972
+ * @return {void}
973
+ * @method
974
+ * @public
975
+ */
976
+ enable (): void
977
+ {
978
+ this.mouseEnabled = true;
979
+ this.mouseChildren = true;
980
+ }
981
+
982
+ /**
983
+ * @description ボタンを無効化する
984
+ * Disable button
985
+ *
986
+ * @return {void}
987
+ * @method
988
+ * @public
989
+ */
990
+ disable (): void
991
+ {
992
+ this.mouseEnabled = false;
993
+ this.mouseChildren = false;
994
+ }
995
+ }
996
+ ```
997
+
998
+ ```typescript
999
+ // TextAtom: テキスト表示の基本機能
1000
+ import { TextField } from "@next2d/text";
1001
+ import type { ITextField } from "@/interface/ITextField";
1002
+ import type { ITextFormatObject } from "@/interface/ITextFormatObject";
1003
+
1004
+ export class TextAtom extends TextField implements ITextField
1005
+ {
1006
+ constructor (
1007
+ text: string = "",
1008
+ props: any | null = null,
1009
+ format_object: ITextFormatObject | null = null
1010
+ ) {
1011
+ super();
1012
+ // プロパティ設定、フォーマット設定
1013
+ }
1014
+ }
1015
+ ```
1016
+
1017
+ ### ボタン連続押下防止パターン
1018
+
1019
+ ボタン押下後に処理が完了するまで連続押下を防止したい場合に使えるパターン。`ButtonAtom` の `disable()` / `enable()` を利用して `mouseEnabled` と `mouseChildren` を制御する。
1020
+
1021
+ 連続押下を防止するかどうかはユースケースに応じて判断する。画面遷移やAPI通信など多重実行を避けたい処理では有効。
1022
+
1023
+ #### View でのイベント登録パターン
1024
+
1025
+ ```typescript
1026
+ // View: ボタン押下時にViewModelのハンドラを呼び出す
1027
+ async initialize (): Promise<void>
1028
+ {
1029
+ const btn = new YourBtnMolecule();
1030
+ btn.addEventListener(PointerEvent.POINTER_DOWN, (event) => {
1031
+ this.vm.handleButtonTap(event);
1032
+ });
1033
+ this.addChild(btn);
1034
+ }
1035
+ ```
1036
+
1037
+ #### ViewModel での連続押下防止パターン
1038
+
1039
+ ```typescript
1040
+ // ViewModel: disable → 処理 → enable で連続押下を防止
1041
+ handleButtonTap (event: PointerEvent): void
1042
+ {
1043
+ // ボタンを即座に無効化して連続押下を防止
1044
+ const button = event.currentTarget as unknown as ButtonAtom;
1045
+ button.disable();
1046
+
1047
+ // 処理実行 (画面遷移、API呼び出し、アニメーション等)
1048
+ this.someUseCase.execute();
1049
+
1050
+ // 処理完了後にボタンを再有効化 (画面遷移の場合は不要)
1051
+ button.enable();
1052
+ }
1053
+ ```
1054
+
1055
+ #### 非同期処理での連続押下防止パターン
1056
+
1057
+ ```typescript
1058
+ // ViewModel: 非同期処理の完了を待ってから再有効化
1059
+ async handleButtonTap (event: PointerEvent): Promise<void>
1060
+ {
1061
+ const button = event.currentTarget as unknown as ButtonAtom;
1062
+ button.disable();
1063
+
1064
+ try {
1065
+ await this.fetchDataUseCase.execute();
1066
+ } finally {
1067
+ // エラー時も必ず再有効化
1068
+ button.enable();
1069
+ }
1070
+ }
1071
+ ```
1072
+
1073
+ #### アニメーション完了後に再有効化するパターン
1074
+
1075
+ ```typescript
1076
+ // ViewModel: アニメーション完了コールバックで再有効化
1077
+ handleButtonTap (event: PointerEvent): void
1078
+ {
1079
+ const button = event.currentTarget as unknown as ButtonAtom;
1080
+ button.disable();
1081
+
1082
+ new SomeAnimation(button, () => {
1083
+ // アニメーション完了後に再有効化
1084
+ button.enable();
1085
+ }).start();
1086
+ }
1087
+ ```
1088
+
1089
+ #### 連続押下を許可するケース
1090
+
1091
+ 以下のケースでは `disable()` / `enable()` を使わず、連続押下を許可する:
1092
+
1093
+ - **インクリメント/デクリメントボタン**: 数量の増減など、連打を前提とした操作
1094
+ - **連射系ゲーム操作**: 連続タップがゲームメカニクスの一部である場合
1095
+ - **トグルボタン**: ON/OFF を素早く切り替える必要がある場合
1096
+
1097
+ ### Molecule (分子) - Atomの組み合わせ
1098
+
1099
+ 複数のAtomを組み合わせた、特定の用途向けコンポーネント。
1100
+
1101
+ ```typescript
1102
+ import { ButtonAtom } from "../atom/ButtonAtom";
1103
+ import { HomeContent } from "@/ui/content/HomeContent";
1104
+ import type { IDraggable } from "@/interface/IDraggable";
1105
+
1106
+ export class HomeBtnMolecule extends ButtonAtom implements IDraggable
1107
+ {
1108
+ private readonly homeContent: HomeContent;
1109
+
1110
+ constructor ()
1111
+ {
1112
+ super();
1113
+ this.homeContent = new HomeContent();
1114
+ this.addChild(this.homeContent);
1115
+ }
1116
+
1117
+ // IDraggableメソッド(startDrag/stopDrag)はMovieClipContentの親クラスから継承
1118
+ }
1119
+ ```
1120
+
1121
+ ```typescript
1122
+ import { ButtonAtom } from "../atom/ButtonAtom";
1123
+ import { TextAtom } from "../atom/TextAtom";
1124
+
1125
+ export class TopBtnMolecule extends ButtonAtom
1126
+ {
1127
+ constructor (text: string) // ViewModelからテキストを受け取る
1128
+ {
1129
+ super();
1130
+ const textField = new TextAtom(text, { autoSize: "center" });
1131
+ this.addChild(textField);
1132
+ }
1133
+
1134
+ playEntrance (callback: () => void): void
1135
+ {
1136
+ // アニメーション再生
1137
+ }
1138
+ }
1139
+ ```
1140
+
1141
+ ### Organism (有機体) - 拡張用
1142
+
1143
+ 複数のMoleculeを組み合わせた大きな機能単位。必要に応じて実装。
1144
+
1145
+ ### Page (ページ)
1146
+
1147
+ 画面全体を構成するコンポーネント。ViewからPageを配置し、PageがMolecule/Atomを組み合わせて画面構築。
1148
+
1149
+ ### Template (テンプレート) - 拡張用
1150
+
1151
+ ページのレイアウト構造を定義。必要に応じて実装。
1152
+
1153
+ ## Component Creation Templates
1154
+
1155
+ ### New Atom
1156
+
1157
+ ```typescript
1158
+ import { Sprite } from "@next2d/display";
1159
+
1160
+ export class YourAtom extends Sprite
1161
+ {
1162
+ constructor (props: any = null)
1163
+ {
1164
+ super();
1165
+ if (props) {
1166
+ Object.assign(this, props);
1167
+ }
1168
+ }
1169
+ }
1170
+ ```
1171
+
1172
+ ### New Molecule
1173
+
1174
+ ```typescript
1175
+ import { ButtonAtom } from "../atom/ButtonAtom";
1176
+ import { TextAtom } from "../atom/TextAtom";
1177
+
1178
+ export class YourMolecule extends ButtonAtom
1179
+ {
1180
+ constructor ()
1181
+ {
1182
+ super();
1183
+ const text = new TextAtom("Click me");
1184
+ this.addChild(text);
1185
+ }
1186
+ }
1187
+ ```
1188
+
1189
+ ## Anti-Patterns
1190
+
1191
+ ```typescript
1192
+ // NG: コンポーネント内でデータ取得
1193
+ export class BadAtom extends TextField {
1194
+ async fetchDataFromAPI() { ... } // NG: データ取得は別層の責務
1195
+ }
1196
+
1197
+ // NG: 直接APIアクセス
1198
+ constructor() {
1199
+ const data = await Repository.get(); // NG
1200
+ }
1201
+
1202
+ // OK: ViewModelからデータを受け取る
1203
+ constructor(text: string) {
1204
+ this.textField = new TextAtom(text); // OK
1205
+ }
1206
+ ```
1207
+
1208
+ ---
1209
+
1210
+ ## Animation
1211
+
1212
+ アニメーションロジックをコンポーネントから分離し、再利用性と保守性を向上。
1213
+
1214
+ ### Naming Convention
1215
+
1216
+ `{Component}{Action}Animation.ts` (例: `TopBtnShowAnimation.ts`)
1217
+
1218
+ ### Animation Types
1219
+
1220
+ - **Show Animation**: 画面表示時のアニメーション
1221
+ - **Exit Animation**: 画面遷移時のアニメーション
1222
+ - **Interaction Animation**: ユーザー操作に対するアニメーション
1223
+
1224
+ ### Animation Class Template
1225
+
1226
+ ```typescript
1227
+ import type { Sprite } from "@next2d/display";
1228
+ import { Tween, Easing, type Job } from "@next2d/ui";
1229
+ import { Event } from "@next2d/events";
1230
+
1231
+ /**
1232
+ * @description [アニメーションの説明]
1233
+ * [Animation description]
1234
+ *
1235
+ * @class
1236
+ * @public
1237
+ */
1238
+ export class YourAnimation
1239
+ {
1240
+ private readonly _job: Job;
1241
+
1242
+ /**
1243
+ * @param {Sprite} sprite - アニメーション対象
1244
+ * @param {() => void} callback - 完了時コールバック
1245
+ * @constructor
1246
+ * @public
1247
+ */
1248
+ constructor (
1249
+ sprite: Sprite,
1250
+ callback?: () => void
1251
+ ) {
1252
+ // 初期状態設定
1253
+ sprite.alpha = 0;
1254
+
1255
+ // Tween設定: (対象, 開始値, 終了値, 秒数, 遅延秒数, イージング)
1256
+ this._job = Tween.add(sprite,
1257
+ { "alpha": 0 },
1258
+ { "alpha": 1 },
1259
+ 0.5, 1, Easing.outQuad
1260
+ );
1261
+
1262
+ if (callback) {
1263
+ this._job.addEventListener(Event.COMPLETE, callback);
1264
+ }
1265
+ }
1266
+
1267
+ /**
1268
+ * @description アニメーション開始
1269
+ * Start animation
1270
+ *
1271
+ * @method
1272
+ * @public
1273
+ */
1274
+ start (): void
1275
+ {
1276
+ this._job.start();
1277
+ }
1278
+ }
1279
+ ```
1280
+
1281
+ ### Component-Animation Coordination
1282
+
1283
+ ```typescript
1284
+ // component/molecule/TopBtnMolecule.ts
1285
+ import { TopBtnShowAnimation } from "@/ui/animation/top/TopBtnShowAnimation";
1286
+
1287
+ export class TopBtnMolecule extends ButtonAtom {
1288
+ playShow(callback: () => void): void {
1289
+ new TopBtnShowAnimation(this, callback).start();
1290
+ }
1291
+ }
1292
+ ```
1293
+
1294
+ ---
1295
+
1296
+ ## Content (Animation Tool)
1297
+
1298
+ Animation Toolで作成されたコンテンツをTypeScriptクラスとしてラップ。
1299
+
1300
+ ### Content Template
1301
+
1302
+ ```typescript
1303
+ import { MovieClipContent } from "@next2d/framework";
1304
+
1305
+ /**
1306
+ * @description [コンテンツの説明]
1307
+ * [Content description]
1308
+ *
1309
+ * @class
1310
+ * @extends {MovieClipContent}
1311
+ */
1312
+ export class YourContent extends MovieClipContent
1313
+ {
1314
+ /**
1315
+ * @description Animation Toolのシンボル名を返す
1316
+ * Returns the Animation Tool symbol name
1317
+ *
1318
+ * @return {string}
1319
+ * @readonly
1320
+ */
1321
+ get namespace (): string
1322
+ {
1323
+ return "YourSymbolName"; // Animation Toolで設定した名前と一致させる
1324
+ }
1325
+ }
1326
+ ```
1327
+
1328
+ ### Content with Interface
1329
+
1330
+ ```typescript
1331
+ import { MovieClipContent } from "@next2d/framework";
1332
+ import type { IDraggable } from "@/interface/IDraggable";
1333
+
1334
+ export class HomeContent extends MovieClipContent implements IDraggable
1335
+ {
1336
+ get namespace (): string
1337
+ {
1338
+ return "HomeContent";
1339
+ }
1340
+
1341
+ // IDraggableメソッド(startDrag/stopDrag)は
1342
+ // MovieClipContentの親クラス(MovieClip)から継承
1343
+ }
1344
+ ```
1345
+
1346
+ ### Content Creation Steps
1347
+
1348
+ 1. Animation Toolでシンボルを作成
1349
+ 2. `.n2d`ファイルを`file/`ディレクトリに配置
1350
+ 3. Contentクラスを作成 (`namespace`はシンボル名と一致させる)
1351
+ 4. Molecule等のコンポーネントで使用
1352
+
1353
+ ### Content Rules
1354
+
1355
+ - クラス名とシンボル名を一致させる
1356
+ - アニメーションの制御のみを担当
1357
+ - 必要な機能はインターフェースで定義
1358
+
1359
+ ---
1360
+
1361
+ # View / ViewModel (MVVM Pattern)
1362
+
1363
+ ## Rules
1364
+
1365
+ - 1画面にView + ViewModelをワンセット作成
1366
+ - ディレクトリ名はキャメルケースの最初のブロック (例: `questList` → `view/quest/`)
1367
+ - Viewは表示構造のみ担当、ビジネスロジックはViewModelに委譲
1368
+ - イベントは必ずViewModelに委譲(View内で完結させない)
1369
+ - ViewModelはインターフェースに依存し、具象クラスに依存しない
1370
+
1371
+ ## Lifecycle (実行順序)
1372
+
1373
+ ```
1374
+ 1. ViewModel インスタンス生成
1375
+ 2. ViewModel.initialize() ← ViewModelが先
1376
+ 3. View インスタンス生成 (ViewModelを注入)
1377
+ 4. View.initialize() ← UIコンポーネントの構築
1378
+ 5. View.onEnter() ← 画面表示時の処理
1379
+ (ユーザー操作)
1380
+ 6. View.onExit() ← 画面非表示時の処理
1381
+ ```
1382
+
1383
+ ### View Lifecycle Methods
1384
+
1385
+ | Method | Timing | Purpose | Do | Don't |
1386
+ |--------|--------|---------|-----|-------|
1387
+ | `initialize()` | View生成直後、表示前 | UIコンポーネントの生成・配置・イベントリスナー登録 | addChild, addEventListener | API呼び出し、重い処理 |
1388
+ | `onEnter()` | initialize完了後、画面表示直前 | 入場アニメーション、データ取得、タイマー開始 | アニメーション再生、fetchInitialData | UIコンポーネント生成 |
1389
+ | `onExit()` | 別画面遷移前 | アニメーション停止、タイマークリア、リソース解放 | clearInterval, 状態リセット | 新リソース作成 |
1390
+
1391
+ ### ViewModel Lifecycle Methods
1392
+
1393
+ | Method | Timing | Purpose | View参照 |
1394
+ |--------|--------|---------|---------|
1395
+ | `constructor()` | インスタンス生成時 | UseCaseの生成 | 不可 |
1396
+ | `initialize()` | Viewの`initialize()`より前 | 初期データ取得、状態初期化 | 不可 |
1397
+ | イベントハンドラ | ユーザー操作時 | ビジネスロジック実行 | 可能 |
1398
+
1399
+ ## View Class Template
1400
+
1401
+ ```typescript
1402
+ import type { {Screen}ViewModel } from "./{Screen}ViewModel";
1403
+ import { View } from "@next2d/framework";
1404
+
1405
+ /**
1406
+ * @class
1407
+ * @extends {View}
1408
+ */
1409
+ export class {Screen}View extends View
1410
+ {
1411
+ /**
1412
+ * @param {{Screen}ViewModel} vm
1413
+ * @constructor
1414
+ * @public
1415
+ */
1416
+ constructor (
1417
+ private readonly vm: {Screen}ViewModel
1418
+ ) {
1419
+ super();
1420
+ }
1421
+
1422
+ /**
1423
+ * @description 画面の初期化 - UIコンポーネントの構築
1424
+ * Initialize - Build UI components
1425
+ *
1426
+ * @return {Promise<void>}
1427
+ * @method
1428
+ * @override
1429
+ * @public
1430
+ */
1431
+ async initialize (): Promise<void>
1432
+ {
1433
+ // UIコンポーネントの作成と配置
1434
+ // イベントリスナーの登録 (ViewModelのメソッドに接続)
1435
+ }
1436
+
1437
+ /**
1438
+ * @description 画面表示時の処理
1439
+ * On screen shown
1440
+ *
1441
+ * @return {Promise<void>}
1442
+ * @method
1443
+ * @override
1444
+ * @public
1445
+ */
1446
+ async onEnter (): Promise<void>
1447
+ {
1448
+ // 入場アニメーション、データ取得
1449
+ }
1450
+
1451
+ /**
1452
+ * @description 画面非表示時の処理
1453
+ * On screen hidden
1454
+ *
1455
+ * @return {Promise<void>}
1456
+ * @method
1457
+ * @override
1458
+ * @public
1459
+ */
1460
+ async onExit (): Promise<void>
1461
+ {
1462
+ // タイマークリア、リソース解放
1463
+ }
1464
+ }
1465
+ ```
1466
+
1467
+ ## ViewModel Class Template
1468
+
1469
+ ```typescript
1470
+ import { ViewModel } from "@next2d/framework";
1471
+ import { YourUseCase } from "@/model/application/{screen}/usecase/YourUseCase";
1472
+
1473
+ /**
1474
+ * @class
1475
+ * @extends {ViewModel}
1476
+ */
1477
+ export class {Screen}ViewModel extends ViewModel
1478
+ {
1479
+ private readonly yourUseCase: YourUseCase;
1480
+
1481
+ constructor ()
1482
+ {
1483
+ super();
1484
+ this.yourUseCase = new YourUseCase();
1485
+ }
1486
+
1487
+ /**
1488
+ * @description ViewModelの初期化 (Viewのinitialize()より前に呼ばれる)
1489
+ * Initialize ViewModel (called before View's initialize())
1490
+ *
1491
+ * @return {Promise<void>}
1492
+ * @method
1493
+ * @override
1494
+ * @public
1495
+ */
1496
+ async initialize (): Promise<void>
1497
+ {
1498
+ // 初期データ取得、状態初期化
1499
+ // ※ この時点ではViewは未生成のためUI操作不可
1500
+ }
1501
+
1502
+ /**
1503
+ * @description イベントハンドラ
1504
+ * Event handler
1505
+ *
1506
+ * @param {PointerEvent} event
1507
+ * @return {void}
1508
+ * @method
1509
+ * @public
1510
+ */
1511
+ yourEventHandler (event: PointerEvent): void
1512
+ {
1513
+ // インターフェースを通じてターゲットを取得
1514
+ const target = event.currentTarget as unknown as IYourInterface;
1515
+ this.yourUseCase.execute(target);
1516
+ }
1517
+ }
1518
+ ```
1519
+
1520
+ ## View-ViewModel Coordination Pattern
1521
+
1522
+ ViewModelの`initialize()`で事前取得したデータをViewで使用するパターン:
1523
+
1524
+ ```typescript
1525
+ // ViewModel: 事前にデータ取得
1526
+ async initialize(): Promise<void> {
1527
+ const data = await HomeTextRepository.get();
1528
+ this.homeText = data.word;
1529
+ }
1530
+
1531
+ getHomeText(): string {
1532
+ return this.homeText;
1533
+ }
1534
+
1535
+ // View: ViewModelから取得済みデータを使用
1536
+ async initialize(): Promise<void> {
1537
+ // vm.initialize()は既に完了している
1538
+ const text = this.vm.getHomeText();
1539
+ const textField = new TextAtom(text);
1540
+ this.addChild(textField);
1541
+ }
1542
+ ```
1543
+
1544
+ ## Code Generation
1545
+
1546
+ ```bash
1547
+ npm run generate
1548
+ ```
1549
+
1550
+ `routing.json`のトッププロパティ値を分解し、`view`ディレクトリ直下に対象ディレクトリがなければ作成。View/ViewModelが存在しない場合のみ新規クラスを生成。
1551
+
1552
+ ## Anti-Patterns
1553
+
1554
+ ```typescript
1555
+ // NG: Viewでビジネスロジック
1556
+ class BadView extends View {
1557
+ async initialize() {
1558
+ btn.addEventListener(PointerEvent.POINTER_DOWN, async () => {
1559
+ const data = await Repository.get(); // NG
1560
+ this.processData(data); // NG
1561
+ });
1562
+ }
1563
+ }
1564
+
1565
+ // NG: ViewModelで具象クラスに依存
1566
+ homeContentPointerDownEvent(event: PointerEvent): void {
1567
+ const target = event.currentTarget as HomeBtnMolecule; // NG
1568
+ target.startDrag();
1569
+ }
1570
+
1571
+ // OK: ViewModelでインターフェースに依存
1572
+ homeContentPointerDownEvent(event: PointerEvent): void {
1573
+ const target = event.currentTarget as unknown as IDraggable; // OK
1574
+ this.startDragUseCase.execute(target);
1575
+ }
1576
+ ```