@nanoforge-dev/schematics 2.2.1 → 2.3.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 (138) hide show
  1. package/CHANGELOG.md +474 -0
  2. package/README.md +12 -15
  3. package/dist/collection.json +8 -23
  4. package/dist/index.cjs +21 -6
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +19 -7
  7. package/dist/index.d.cts.map +1 -1
  8. package/dist/index.d.ts +19 -7
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +17 -4
  11. package/dist/index.js.map +1 -1
  12. package/dist/libs/component/component.factory.d.ts +1 -3
  13. package/dist/libs/component/component.factory.d.ts.map +1 -1
  14. package/dist/libs/component/component.factory.js +2 -1
  15. package/dist/libs/component/component.factory.js.map +1 -1
  16. package/dist/libs/project/files/js/.dockerignore +5 -0
  17. package/dist/libs/project/files/js/Dockerfile +36 -0
  18. package/dist/libs/project/files/js/README.md +53 -0
  19. package/dist/libs/project/files/js/assets/__dot__gitkeep +0 -0
  20. package/dist/libs/project/files/js/jsconfig.json +36 -0
  21. package/dist/libs/project/files/js/nanoforge.config.js +10 -0
  22. package/dist/libs/project/files/js/package.json.template +29 -0
  23. package/dist/libs/project/files/js/pnpm-workspace.yaml.template +3 -0
  24. package/dist/libs/project/files/js/src/components/drawable-circle-2d.component.js +404 -0
  25. package/dist/libs/project/files/js/src/components/position-2d.component.js +42 -0
  26. package/dist/libs/project/files/js/src/main.js +43 -0
  27. package/dist/libs/project/files/js/src/systems/draw-2d.system.js +33 -0
  28. package/dist/libs/project/files/js/src/systems/move-2d.system.js +34 -0
  29. package/dist/libs/project/files/js/src/systems/position-sync.system.js +35 -0
  30. package/dist/libs/project/files/ts/.dockerignore +5 -0
  31. package/dist/libs/project/files/ts/Dockerfile +36 -0
  32. package/dist/libs/project/files/ts/README.md +53 -0
  33. package/dist/libs/project/files/ts/assets/__dot__gitkeep +0 -0
  34. package/dist/libs/project/files/ts/nanoforge.config.ts +5 -0
  35. package/dist/libs/project/files/ts/package.json.template +29 -0
  36. package/dist/libs/project/files/ts/pnpm-workspace.yaml.template +3 -0
  37. package/dist/libs/project/files/ts/src/components/drawable-circle-2d.component.ts +367 -0
  38. package/dist/libs/project/files/ts/src/components/position-2d.component.ts +35 -0
  39. package/dist/libs/project/files/ts/src/main.ts +40 -0
  40. package/dist/libs/project/files/ts/src/systems/draw-2d.system.ts +25 -0
  41. package/dist/libs/project/files/ts/src/systems/move-2d.system.ts +26 -0
  42. package/dist/libs/project/files/ts/src/systems/position-sync.system.ts +27 -0
  43. package/dist/libs/project/files/ts/tsconfig.json +42 -0
  44. package/dist/libs/project/project.factory.d.ts +64 -0
  45. package/dist/libs/project/project.factory.d.ts.map +1 -0
  46. package/dist/libs/project/project.factory.js +127 -0
  47. package/dist/libs/project/project.factory.js.map +1 -0
  48. package/dist/libs/project/schema.json +83 -0
  49. package/dist/libs/system/system.factory.d.ts +1 -3
  50. package/dist/libs/system/system.factory.d.ts.map +1 -1
  51. package/dist/libs/system/system.factory.js +2 -1
  52. package/dist/libs/system/system.factory.js.map +1 -1
  53. package/dist/libs/workspace/files/js/.dockerignore +5 -0
  54. package/dist/libs/workspace/files/js/Dockerfile +36 -0
  55. package/dist/libs/workspace/files/js/README.md +51 -0
  56. package/dist/libs/workspace/files/js/__dot__gitignore +137 -0
  57. package/dist/libs/{application → workspace}/files/js/jsconfig.json +1 -2
  58. package/dist/libs/workspace/files/js/nanoforge.config.js +6 -0
  59. package/dist/libs/workspace/files/js/package.json.template +19 -0
  60. package/dist/libs/workspace/files/js/pnpm-workspace.yaml.template +6 -0
  61. package/dist/libs/workspace/files/ts/.dockerignore +5 -0
  62. package/dist/libs/workspace/files/ts/Dockerfile +36 -0
  63. package/dist/libs/workspace/files/ts/README.md +51 -0
  64. package/dist/libs/workspace/files/ts/__dot__gitignore +134 -0
  65. package/dist/libs/workspace/files/ts/nanoforge.config.ts +6 -0
  66. package/dist/libs/workspace/files/ts/package.json.template +20 -0
  67. package/dist/libs/workspace/files/ts/pnpm-workspace.yaml.template +6 -0
  68. package/dist/libs/{application → workspace}/files/ts/tsconfig.json +1 -2
  69. package/dist/libs/workspace/schema.json +45 -0
  70. package/dist/libs/workspace/workspace.factory.d.ts +42 -0
  71. package/dist/libs/workspace/workspace.factory.d.ts.map +1 -0
  72. package/dist/libs/workspace/workspace.factory.js +116 -0
  73. package/dist/libs/workspace/workspace.factory.js.map +1 -0
  74. package/package.json +23 -21
  75. package/dist/libs/application/application.factory.d.ts +0 -59
  76. package/dist/libs/application/application.factory.d.ts.map +0 -1
  77. package/dist/libs/application/application.factory.js +0 -57
  78. package/dist/libs/application/application.factory.js.map +0 -1
  79. package/dist/libs/application/files/js/.prettierignore +0 -8
  80. package/dist/libs/application/files/js/README.md +0 -166
  81. package/dist/libs/application/files/js/eslint.config.js +0 -11
  82. package/dist/libs/application/files/js/package.json.template +0 -48
  83. package/dist/libs/application/files/js/pnpm-workspace.yaml.template +0 -3
  84. package/dist/libs/application/files/js/prettier.config.js +0 -6
  85. package/dist/libs/application/files/ts/.prettierignore +0 -8
  86. package/dist/libs/application/files/ts/README.md +0 -166
  87. package/dist/libs/application/files/ts/eslint.config.js +0 -15
  88. package/dist/libs/application/files/ts/package.json.template +0 -52
  89. package/dist/libs/application/files/ts/pnpm-workspace.yaml.template +0 -3
  90. package/dist/libs/application/files/ts/prettier.config.js +0 -6
  91. package/dist/libs/application/schema.json +0 -70
  92. package/dist/libs/configuration/configuration.factory.d.ts +0 -31
  93. package/dist/libs/configuration/configuration.factory.d.ts.map +0 -1
  94. package/dist/libs/configuration/configuration.factory.js +0 -97
  95. package/dist/libs/configuration/configuration.factory.js.map +0 -1
  96. package/dist/libs/configuration/files/nanoforge.config.json +0 -1
  97. package/dist/libs/configuration/schema.json +0 -34
  98. package/dist/libs/docker/docker.factory.d.ts +0 -19
  99. package/dist/libs/docker/docker.factory.d.ts.map +0 -1
  100. package/dist/libs/docker/docker.factory.js +0 -20
  101. package/dist/libs/docker/docker.factory.js.map +0 -1
  102. package/dist/libs/docker/files/.dockerignore +0 -1
  103. package/dist/libs/docker/files/Dockerfile +0 -25
  104. package/dist/libs/docker/schema.json +0 -19
  105. package/dist/libs/part-base/files/js/__nanoforgeFolder__/client.save.json +0 -77
  106. package/dist/libs/part-base/files/js/__nanoforgeFolder__/server.save.json +0 -49
  107. package/dist/libs/part-base/files/js/__part__/components/example.component.js +0 -72
  108. package/dist/libs/part-base/files/js/__part__/init/after-init.js +0 -12
  109. package/dist/libs/part-base/files/js/__part__/init/after-registry-init.js +0 -14
  110. package/dist/libs/part-base/files/js/__part__/init/after-run.js +0 -12
  111. package/dist/libs/part-base/files/js/__part__/init/before-init.js +0 -13
  112. package/dist/libs/part-base/files/js/__part__/init/before-registry-init.js +0 -14
  113. package/dist/libs/part-base/files/js/__part__/init/before-run.js +0 -12
  114. package/dist/libs/part-base/files/js/__part__/systems/example.system.js +0 -44
  115. package/dist/libs/part-base/files/ts/__nanoforgeFolder__/client.save.json +0 -77
  116. package/dist/libs/part-base/files/ts/__nanoforgeFolder__/server.save.json +0 -49
  117. package/dist/libs/part-base/files/ts/__part__/components/example.component.ts +0 -55
  118. package/dist/libs/part-base/files/ts/__part__/init/after-init.ts +0 -5
  119. package/dist/libs/part-base/files/ts/__part__/init/after-registry-init.ts +0 -6
  120. package/dist/libs/part-base/files/ts/__part__/init/after-run.ts +0 -5
  121. package/dist/libs/part-base/files/ts/__part__/init/before-init.ts +0 -5
  122. package/dist/libs/part-base/files/ts/__part__/init/before-registry-init.ts +0 -6
  123. package/dist/libs/part-base/files/ts/__part__/init/before-run.ts +0 -5
  124. package/dist/libs/part-base/files/ts/__part__/systems/example.system.ts +0 -31
  125. package/dist/libs/part-base/part-base.factory.d.ts +0 -31
  126. package/dist/libs/part-base/part-base.factory.d.ts.map +0 -1
  127. package/dist/libs/part-base/part-base.factory.js +0 -33
  128. package/dist/libs/part-base/part-base.factory.js.map +0 -1
  129. package/dist/libs/part-base/schema.json +0 -35
  130. package/dist/libs/part-main/part-main.factory.d.ts +0 -39
  131. package/dist/libs/part-main/part-main.factory.d.ts.map +0 -1
  132. package/dist/libs/part-main/part-main.factory.js +0 -252
  133. package/dist/libs/part-main/part-main.factory.js.map +0 -1
  134. package/dist/libs/part-main/schema.json +0 -43
  135. /package/dist/libs/{application → project}/files/js/__dot__gitignore +0 -0
  136. /package/dist/libs/{application → project}/files/ts/__dot__gitignore +0 -0
  137. /package/dist/libs/{application → workspace}/files/js/__dot__env +0 -0
  138. /package/dist/libs/{application → workspace}/files/ts/__dot__env +0 -0
@@ -0,0 +1,404 @@
1
+ /**
2
+ * @typedef {import("@nanoforge-dev/common").NfFile} NfFile
3
+ * @typedef {import("@nanoforge-dev/ecs").EditorComponentManifest} EditorComponentManifest
4
+ * @typedef {import("@nanoforge-dev/graphics-2d").Layer} Layer
5
+ */
6
+
7
+ import { Circle } from "@nanoforge-dev/graphics-2d";
8
+
9
+ export class DrawableCircle2D {
10
+ name = this.constructor.name;
11
+ shape;
12
+
13
+ /** @type {NfFile | undefined} */
14
+ #fillPatternImage;
15
+
16
+ /**
17
+ * @param {number} [radius]
18
+ * @param {boolean} [visible]
19
+ * @param {string} [id]
20
+ * @param {number} [opacity]
21
+ * @param {number} [scaleX]
22
+ * @param {number} [scaleY]
23
+ * @param {number} [skewX]
24
+ * @param {number} [skewY]
25
+ * @param {number} [rotation]
26
+ * @param {number} [offsetX]
27
+ * @param {number} [offsetY]
28
+ * @param {string} [fillColor]
29
+ * @param {NfFile} [fillPatternImage]
30
+ * @param {number} [fillPatternX]
31
+ * @param {number} [fillPatternY]
32
+ * @param {number} [fillPatternOffsetX]
33
+ * @param {number} [fillPatternOffsetY]
34
+ * @param {number} [fillPatternScaleX]
35
+ * @param {number} [fillPatternScaleY]
36
+ * @param {number} [fillPatternRotation]
37
+ * @param {string} [fillPatternRepeat]
38
+ */
39
+ constructor(
40
+ radius,
41
+ visible,
42
+ id,
43
+ opacity,
44
+ scaleX,
45
+ scaleY,
46
+ skewX,
47
+ skewY,
48
+ rotation,
49
+ offsetX,
50
+ offsetY,
51
+ fillColor,
52
+ fillPatternImage,
53
+ fillPatternX,
54
+ fillPatternY,
55
+ fillPatternOffsetX,
56
+ fillPatternOffsetY,
57
+ fillPatternScaleX,
58
+ fillPatternScaleY,
59
+ fillPatternRotation,
60
+ fillPatternRepeat,
61
+ ) {
62
+ this.#fillPatternImage = fillPatternImage;
63
+
64
+ this.shape = new Circle({
65
+ radius,
66
+ visible,
67
+ id,
68
+ opacity,
69
+ scaleX,
70
+ scaleY,
71
+ skewX,
72
+ skewY,
73
+ rotation,
74
+ offsetX,
75
+ offsetY,
76
+ fill: fillColor,
77
+ fillPatternImage: fillPatternImage
78
+ ? Object.assign(new Image(), { src: fillPatternImage.path })
79
+ : undefined,
80
+ fillPatternX,
81
+ fillPatternY,
82
+ fillPatternOffsetX,
83
+ fillPatternOffsetY,
84
+ fillPatternScaleX,
85
+ fillPatternScaleY,
86
+ fillPatternRotation,
87
+ fillPatternRepeat,
88
+ });
89
+ }
90
+
91
+ get radius() {
92
+ return this.shape.radius();
93
+ }
94
+ set radius(v) {
95
+ this.shape.radius(v);
96
+ this.#redraw();
97
+ }
98
+ get visible() {
99
+ return this.shape.visible();
100
+ }
101
+ set visible(v) {
102
+ this.shape.visible(v);
103
+ this.#redraw();
104
+ }
105
+ get id() {
106
+ return this.shape.id();
107
+ }
108
+ set id(v) {
109
+ this.shape.id(v);
110
+ this.#redraw();
111
+ }
112
+ get opacity() {
113
+ return this.shape.opacity();
114
+ }
115
+ set opacity(v) {
116
+ this.shape.opacity(v);
117
+ this.#redraw();
118
+ }
119
+ get scaleX() {
120
+ return this.shape.scaleX();
121
+ }
122
+ set scaleX(v) {
123
+ this.shape.scaleX(v);
124
+ this.#redraw();
125
+ }
126
+ get scaleY() {
127
+ return this.shape.scaleY();
128
+ }
129
+ set scaleY(v) {
130
+ this.shape.scaleY(v);
131
+ this.#redraw();
132
+ }
133
+ get skewX() {
134
+ return this.shape.skewX();
135
+ }
136
+ set skewX(v) {
137
+ this.shape.skewX(v);
138
+ this.#redraw();
139
+ }
140
+ get skewY() {
141
+ return this.shape.skewY();
142
+ }
143
+ set skewY(v) {
144
+ this.shape.skewY(v);
145
+ this.#redraw();
146
+ }
147
+ get rotation() {
148
+ return this.shape.rotation();
149
+ }
150
+ set rotation(v) {
151
+ this.shape.rotation(v);
152
+ this.#redraw();
153
+ }
154
+ get offsetX() {
155
+ return this.shape.offsetX();
156
+ }
157
+ set offsetX(v) {
158
+ this.shape.offsetX(v);
159
+ this.#redraw();
160
+ }
161
+ get offsetY() {
162
+ return this.shape.offsetY();
163
+ }
164
+ set offsetY(v) {
165
+ this.shape.offsetY(v);
166
+ this.#redraw();
167
+ }
168
+ get fillColor() {
169
+ return this.shape.fill().toString();
170
+ }
171
+ set fillColor(v) {
172
+ this.shape.fill(v);
173
+ this.#redraw();
174
+ }
175
+ /** @returns {NfFile | undefined} */
176
+ get fillPatternImage() {
177
+ return this.#fillPatternImage;
178
+ }
179
+ /** @param {NfFile} v */
180
+ set fillPatternImage(v) {
181
+ this.#fillPatternImage = v;
182
+ this.shape.fillPatternImage(v ? Object.assign(new Image(), { src: v.path }) : undefined);
183
+ this.#redraw();
184
+ }
185
+ get fillPatternX() {
186
+ return this.shape.fillPatternX();
187
+ }
188
+ set fillPatternX(v) {
189
+ this.shape.fillPatternX(v);
190
+ this.#redraw();
191
+ }
192
+ get fillPatternY() {
193
+ return this.shape.fillPatternY();
194
+ }
195
+ set fillPatternY(v) {
196
+ this.shape.fillPatternY(v);
197
+ this.#redraw();
198
+ }
199
+ get fillPatternOffsetX() {
200
+ return this.shape.fillPatternOffsetX();
201
+ }
202
+ set fillPatternOffsetX(v) {
203
+ this.shape.fillPatternOffsetX(v);
204
+ this.#redraw();
205
+ }
206
+ get fillPatternOffsetY() {
207
+ return this.shape.fillPatternOffsetY();
208
+ }
209
+ set fillPatternOffsetY(v) {
210
+ this.shape.fillPatternOffsetY(v);
211
+ this.#redraw();
212
+ }
213
+ get fillPatternScaleX() {
214
+ return this.shape.fillPatternScaleX();
215
+ }
216
+ set fillPatternScaleX(v) {
217
+ this.shape.fillPatternScaleX(v);
218
+ this.#redraw();
219
+ }
220
+ get fillPatternScaleY() {
221
+ return this.shape.fillPatternScaleY();
222
+ }
223
+ set fillPatternScaleY(v) {
224
+ this.shape.fillPatternScaleY(v);
225
+ this.#redraw();
226
+ }
227
+ get fillPatternRotation() {
228
+ return this.shape.fillPatternRotation();
229
+ }
230
+ set fillPatternRotation(v) {
231
+ this.shape.fillPatternRotation(v);
232
+ this.#redraw();
233
+ }
234
+ get fillPatternRepeat() {
235
+ return this.shape.fillPatternRepeat();
236
+ }
237
+ set fillPatternRepeat(v) {
238
+ this.shape.fillPatternRepeat(v);
239
+ this.#redraw();
240
+ }
241
+
242
+ /** @param {Layer} layer */
243
+ addToLayer(layer) {
244
+ if (this.shape.getParent() !== layer) layer.add(this.shape);
245
+ }
246
+
247
+ #redraw() {
248
+ this.shape.getLayer()?.batchDraw();
249
+ }
250
+ }
251
+ // * Required to generate code
252
+ export default DrawableCircle2D.name;
253
+
254
+ // * Required for the editor to display the component and generate code
255
+ /** @type {EditorComponentManifest} */
256
+ export const EDITOR_COMPONENT_MANIFEST = {
257
+ name: "DrawableCircle2D",
258
+ description: "Graphically drawable circle 2D",
259
+ params: [
260
+ {
261
+ name: "radius",
262
+ type: "number",
263
+ description: "Radius of the circle in pixels",
264
+ default: 10,
265
+ optional: false,
266
+ },
267
+ {
268
+ name: "visible",
269
+ type: "boolean",
270
+ description: "Controls whether the node is rendered",
271
+ default: true,
272
+ optional: false,
273
+ },
274
+ {
275
+ name: "id",
276
+ type: "string",
277
+ description: "Unique identifier for the node, used with stage.findOne('#id')",
278
+ optional: true,
279
+ },
280
+ {
281
+ name: "opacity",
282
+ type: "number",
283
+ description: "Opacity from 0 (transparent) to 1 (fully opaque)",
284
+ default: 1,
285
+ optional: true,
286
+ },
287
+ {
288
+ name: "scaleX",
289
+ type: "number",
290
+ description: "Horizontal scale factor",
291
+ default: 1,
292
+ optional: true,
293
+ },
294
+ {
295
+ name: "scaleY",
296
+ type: "number",
297
+ description: "Vertical scale factor",
298
+ default: 1,
299
+ optional: true,
300
+ },
301
+ {
302
+ name: "skewX",
303
+ type: "number",
304
+ default: 0,
305
+ description: "Horizontal skew in radians, shears along the X axis",
306
+ optional: true,
307
+ },
308
+ {
309
+ name: "skewY",
310
+ type: "number",
311
+ default: 0,
312
+ description: "Vertical skew in radians, shears along the Y axis",
313
+ optional: true,
314
+ },
315
+ {
316
+ name: "rotation",
317
+ type: "number",
318
+ description:
319
+ "Rotation angle in degrees applied around the offset point (positive values rotate clockwise)",
320
+ default: 0,
321
+ optional: true,
322
+ },
323
+ {
324
+ name: "offsetX",
325
+ type: "number",
326
+ description: "Horizontal offset for the transform origin",
327
+ default: 0,
328
+ optional: true,
329
+ },
330
+ {
331
+ name: "offsetY",
332
+ type: "number",
333
+ description: "Vertical offset for the transform origin",
334
+ default: 0,
335
+ optional: true,
336
+ },
337
+ {
338
+ name: "fillColor",
339
+ type: "string",
340
+ description:
341
+ 'Solid fill color as a CSS color string (e.g. "red", "#ff0000", "rgba(255,0,0,0.5)"), or a pre-built CanvasGradient object',
342
+ default: "red",
343
+ example: "rgb(255,255,255)",
344
+ optional: true,
345
+ },
346
+ {
347
+ name: "fillPatternImage",
348
+ type: "asset",
349
+ description: "Image element to use as a tiling pattern fill",
350
+ optional: true,
351
+ },
352
+ {
353
+ name: "fillPatternX",
354
+ type: "number",
355
+ description:
356
+ "Horizontal offset of the pattern origin relative to the shape's top-left corner, in pixels",
357
+ optional: true,
358
+ },
359
+ {
360
+ name: "fillPatternY",
361
+ type: "number",
362
+ description:
363
+ "Vertical offset of the pattern origin relative to the shape's top-left corner, in pixels",
364
+ optional: true,
365
+ },
366
+ {
367
+ name: "fillPatternOffsetX",
368
+ type: "number",
369
+ description: "Horizontally shifts the pattern image within the tile before repeating",
370
+ optional: true,
371
+ },
372
+ {
373
+ name: "fillPatternOffsetY",
374
+ type: "number",
375
+ description: "Vertically shifts the pattern image within the tile before repeating",
376
+ optional: true,
377
+ },
378
+ {
379
+ name: "fillPatternScaleX",
380
+ type: "number",
381
+ description: "Horizontal scale factor for the pattern tile",
382
+ optional: true,
383
+ },
384
+ {
385
+ name: "fillPatternScaleY",
386
+ type: "number",
387
+ description: "Vertical scale factor for the pattern tile",
388
+ optional: true,
389
+ },
390
+ {
391
+ name: "fillPatternRotation",
392
+ type: "number",
393
+ description: "Rotation of the pattern tile in degrees",
394
+ optional: true,
395
+ },
396
+ {
397
+ name: "fillPatternRepeat",
398
+ type: "string",
399
+ description:
400
+ 'Repeat mode for the pattern. One of "repeat" (default), "repeat-x", "repeat-y", or "no-repeat"',
401
+ optional: true,
402
+ },
403
+ ],
404
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @typedef {import("@nanoforge-dev/ecs").EditorComponentManifest} EditorComponentManifest
3
+ */
4
+
5
+ export class Position2D {
6
+ name = this.constructor.name;
7
+
8
+ /**
9
+ * @param {number} x
10
+ * @param {number} y
11
+ */
12
+ constructor(x, y) {
13
+ this.x = x;
14
+ this.y = y;
15
+ }
16
+ }
17
+
18
+ // * Required to generate code
19
+ export default Position2D.name;
20
+
21
+ // * Required for the editor to display the component and generate code
22
+ /** @type {EditorComponentManifest} */
23
+ export const EDITOR_COMPONENT_MANIFEST = {
24
+ name: "Position2D",
25
+ description: "Position of an entity in a 2 dimensional space in pixel",
26
+ params: [
27
+ {
28
+ type: "number",
29
+ name: "x",
30
+ description: "Horizontal position in pixel",
31
+ example: 4.2,
32
+ default: 0,
33
+ },
34
+ {
35
+ type: "number",
36
+ name: "y",
37
+ description: "Vertical position in pixel",
38
+ example: 67,
39
+ default: 0,
40
+ },
41
+ ],
42
+ };
@@ -0,0 +1,43 @@
1
+ import { NanoforgeFactory } from "@nanoforge-dev/core";
2
+ import { EcsLibrary } from "@nanoforge-dev/ecs/<%= part %>";<% if (part === "client") { %>
3
+ import { Graphics2DLibrary } from "@nanoforge-dev/graphics-2d";
4
+ import { InputLibrary } from "@nanoforge-dev/input";<% } %>
5
+ import { <%= part === "client" ? "NetworkClientLibrary" : "NetworkServerLibrary" %> } from "@nanoforge-dev/network/<%= part %>";
6
+ <% if (part === "client") { %>
7
+ import { DrawableCircle2D } from "./components/drawable-circle-2d.component";<% } %>
8
+ import { Position2D } from "./components/position-2d.component";<% if (part === "client") { %>
9
+ import { draw2D } from "./systems/draw-2d.system";<% if (hasServer) { %>
10
+ import { positionSync } from "./systems/position-sync.system";<% } %><% } else { %>
11
+ import { move2D } from "./systems/move-2d.system";<% } %>
12
+
13
+ /**
14
+ * @param {import("@nanoforge-dev/common").<%= part === "client" ? "ClientRunOptions" : "ServerRunOptions" %>} options
15
+ * @returns {Promise<void>}
16
+ */
17
+ export const main = async (options) => {
18
+ const app = NanoforgeFactory.<%= part === "client" ? "createClient" : "createServer" %>({ tickRate: 60 });
19
+
20
+ const ecs = new EcsLibrary();
21
+ app.use(ecs);<% if (part === "client") { %>
22
+ app.use(new Graphics2DLibrary());
23
+ app.use(new InputLibrary());<% } %>
24
+ app.use(new <%= part === "client" ? "NetworkClientLibrary" : "NetworkServerLibrary" %>());
25
+
26
+ await app.init(options);
27
+
28
+ const registry = ecs.registry;
29
+ <% if (part === "client") { %>
30
+ const circle = registry.spawnEntity();
31
+ registry.addComponent(circle, new Position2D(500, 500));
32
+ registry.addComponent(circle, new DrawableCircle2D(75, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "red"));
33
+
34
+ registry.addSystem(draw2D);<% if (hasServer) { %>
35
+ registry.addSystem(positionSync);<% } %>
36
+ <% } else { %>
37
+ const entity = registry.spawnEntity();
38
+ registry.addComponent(entity, new Position2D(500, 500));
39
+
40
+ registry.addSystem(move2D);
41
+ <% } %>
42
+ await app.run();
43
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @typedef {import("@nanoforge-dev/common").Context} Context
3
+ * @typedef {import("@nanoforge-dev/ecs").EditorSystemManifest} EditorSystemManifest
4
+ * @typedef {import("@nanoforge-dev/ecs").Registry} Registry
5
+ */
6
+
7
+ import { DrawableCircle2D } from "../components/drawable-circle-2d.component";
8
+ import { Position2D } from "../components/position-2d.component";
9
+
10
+ /**
11
+ * @param {Registry} registry
12
+ * @param {Context} ctx
13
+ */
14
+ export function draw2D(registry, ctx) {
15
+ const graphic2d = ctx.graphics;
16
+
17
+ const entities = registry.getZipper([Position2D, DrawableCircle2D]);
18
+
19
+ entities.forEach(({ Position2D, DrawableCircle2D }) => {
20
+ DrawableCircle2D.addToLayer(graphic2d.baseLayer);
21
+ DrawableCircle2D.shape.setPosition(Position2D);
22
+ });
23
+ }
24
+ // * Required to generate code
25
+ export default draw2D.name;
26
+
27
+ // * Required for the editor to display the system and generate code
28
+ /** @type {EditorSystemManifest} */
29
+ export const EDITOR_SYSTEM_MANIFEST = {
30
+ name: "draw2D",
31
+ description: "Draw every drawable component",
32
+ dependencies: ["Position2D", "DrawableCircle2D"],
33
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @typedef {import("@nanoforge-dev/common").Context} Context
3
+ * @typedef {import("@nanoforge-dev/ecs").EditorSystemManifest} EditorSystemManifest
4
+ * @typedef {import("@nanoforge-dev/ecs").Registry} Registry
5
+ */
6
+
7
+ import { Position2D } from "../components/position-2d.component";
8
+
9
+ /**
10
+ * @param {Registry} registry
11
+ * @param {Context} ctx
12
+ */
13
+ export function move2D(registry, ctx) {
14
+ const network = ctx.network;
15
+ const entities = registry.getZipper([Position2D]);
16
+
17
+ entities.forEach(({ Position2D }) => {
18
+ Position2D.x += 1;
19
+
20
+ network.tcp.sendToEverybody(
21
+ new TextEncoder().encode(JSON.stringify({ x: Position2D.x, y: Position2D.y })),
22
+ );
23
+ });
24
+ }
25
+ // * Required to generate code
26
+ export default move2D.name;
27
+
28
+ // * Required for the editor to display the system and generate code
29
+ /** @type {EditorSystemManifest} */
30
+ export const EDITOR_SYSTEM_MANIFEST = {
31
+ name: "move2D",
32
+ description: "Moves the entity and broadcasts its position to every connected client",
33
+ dependencies: ["Position2D"],
34
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @typedef {import("@nanoforge-dev/common").Context} Context
3
+ * @typedef {import("@nanoforge-dev/ecs").EditorSystemManifest} EditorSystemManifest
4
+ * @typedef {import("@nanoforge-dev/ecs").Registry} Registry
5
+ */
6
+
7
+ import { Position2D } from "../components/position-2d.component";
8
+
9
+ /**
10
+ * @param {Registry} registry
11
+ * @param {Context} ctx
12
+ */
13
+ export function positionSync(registry, ctx) {
14
+ const network = ctx.network;
15
+ const packets = network.tcp
16
+ .getReceivedPackets()
17
+ .map((packet) => JSON.parse(new TextDecoder().decode(packet)));
18
+
19
+ const [entity] = registry.getZipper([Position2D]);
20
+
21
+ packets.forEach((packet) => {
22
+ entity.Position2D.x = packet.x;
23
+ entity.Position2D.y = packet.y;
24
+ });
25
+ }
26
+ // * Required to generate code
27
+ export default positionSync.name;
28
+
29
+ // * Required for the editor to display the system and generate code
30
+ /** @type {EditorSystemManifest} */
31
+ export const EDITOR_SYSTEM_MANIFEST = {
32
+ name: "positionSync",
33
+ description: "Overwrites the local entity's position with the latest value received from the server",
34
+ dependencies: ["Position2D"],
35
+ };
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ .nanoforge/
4
+ .git/
5
+ .env
@@ -0,0 +1,36 @@
1
+ <% if (packageManager === "bun") { %>FROM oven/bun:1.3 AS base
2
+ <% } else if (packageManager === "pnpm") { %>FROM ghcr.io/pnpm/pnpm:12 AS base
3
+ RUN pnpm runtime set node 26 -g
4
+ <% } else { %>FROM node:26-slim AS base
5
+ <% } %><% if (packageManager === "yarn") { %>
6
+ RUN npm install -g yarn
7
+ <% } %>
8
+ WORKDIR /app
9
+
10
+ FROM base AS deps
11
+
12
+ <% if (packageManager === "npm") { %>COPY package.json package-lock.json ./
13
+ RUN npm ci
14
+ <% } else if (packageManager === "yarn") { %>COPY package.json yarn.lock ./
15
+ RUN yarn install --frozen-lockfile
16
+ <% } else if (packageManager === "pnpm") { %>COPY package.json pnpm-lock.yaml ./
17
+ RUN pnpm install --frozen-lockfile
18
+ <% } else { %>COPY package.json bun.lock ./
19
+ RUN bun install --frozen-lockfile
20
+ <% } %>
21
+ FROM deps AS build
22
+ COPY . .
23
+ RUN <%= packageManager %> run build
24
+
25
+ <% if (packageManager === "bun") { %>FROM oven/bun:1.3-slim<% } else { %>FROM base<% } %>
26
+ WORKDIR /app
27
+
28
+ COPY --from=build /app/node_modules ./node_modules
29
+ COPY --from=build /app/dist ./dist
30
+ COPY --from=build /app/package.json ./
31
+ COPY --from=build /app/nanoforge.config.ts ./
32
+ <% if (part === "server") { %>
33
+ <% } %><% if (packageManager !== "pnpm") { %>
34
+ USER <% if (packageManager === "bun") { %>bun<% } else { %>node<% } %>
35
+ <% } %><% if (part === "client") { %>EXPOSE 3000
36
+ <% } %>CMD ["<%= packageManager %>", "run", "start"]
@@ -0,0 +1,53 @@
1
+ # <%= packageName %>
2
+
3
+ A [NanoForge](https://nanoforge.eu) <%= part %> app.
4
+ <% if (!workspace) { %>
5
+ ## Prerequisites
6
+
7
+ - Node.js and <%= packageManager %>
8
+ - The [NanoForge CLI](https://www.npmjs.com/package/@nanoforge-dev/cli): `npm install -g @nanoforge-dev/cli`
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ <%= packageManager %> install
14
+ ```
15
+ <% } %>
16
+ ## Development
17
+
18
+ ```bash
19
+ nf dev
20
+ ```
21
+
22
+ `nf dev` serves the game and watches your files, so changes are picked up as you work.
23
+
24
+ ## Build & start
25
+
26
+ ```bash
27
+ nf build
28
+ nf start
29
+ ```
30
+
31
+ `nf build` compiles this <%= part %> for production, and `nf start` serves the build.
32
+ <% if (editor) { %>
33
+ This app was generated with editor support. Launch it with:
34
+
35
+ ```bash
36
+ nf editor .
37
+ ```
38
+ <% } %><% if (docker) { %>
39
+ A `Dockerfile` is also included to containerize the built app for deployment.
40
+ <% } %>
41
+ ## CLI reference
42
+
43
+ | Command | Description |
44
+ | ------------- | ---------------------------------- |
45
+ | `nf dev` | Develop with hot reload |
46
+ | `nf build` | Build for production |
47
+ | `nf start` | Run a built app |<% if (editor) { %>
48
+ | `nf editor` | Launch the visual editor |<% } %>
49
+
50
+ ## More info
51
+
52
+ - [Documentation](https://docs.nanoforge.eu) - CLI commands, configuration, and guides
53
+ - [nanoforge.eu](https://nanoforge.eu) - learn more about NanoForge
@@ -0,0 +1,5 @@
1
+ import { defineConfig } from "@nanoforge-dev/config";
2
+
3
+ export default defineConfig({
4
+ type: "<%= part %>",
5
+ });