castle-web-cli 0.4.1 → 0.4.2

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 (158) hide show
  1. package/dist/api.d.ts +53 -5
  2. package/dist/api.js +42 -15
  3. package/dist/config.d.ts +2 -0
  4. package/dist/config.js +25 -11
  5. package/dist/get-deck.d.ts +3 -0
  6. package/dist/get-deck.js +64 -0
  7. package/dist/ide-client.d.ts +1 -0
  8. package/dist/ide-client.js +537 -0
  9. package/dist/ide.d.ts +16 -0
  10. package/dist/ide.js +546 -0
  11. package/dist/index.js +36 -41
  12. package/dist/init.d.ts +3 -1
  13. package/dist/init.js +170 -24
  14. package/dist/localPaths.d.ts +6 -0
  15. package/dist/localPaths.js +33 -0
  16. package/dist/login.js +1 -1
  17. package/dist/preview.d.ts +3 -0
  18. package/dist/preview.js +53 -34
  19. package/dist/save-deck.d.ts +2 -0
  20. package/dist/{push.js → save-deck.js} +66 -5
  21. package/dist/serve.d.ts +2 -0
  22. package/dist/serve.js +290 -27
  23. package/kits/basic-2d/.prettierrc +8 -0
  24. package/kits/basic-2d/CLAUDE.md +131 -0
  25. package/kits/basic-2d/behaviors/Camera.jsx +43 -0
  26. package/kits/basic-2d/behaviors/Collider.jsx +71 -0
  27. package/kits/basic-2d/behaviors/Drawing.jsx +139 -0
  28. package/kits/basic-2d/behaviors/Layout.jsx +16 -0
  29. package/kits/basic-2d/drawings/floor.drawing +70 -0
  30. package/kits/basic-2d/editors/App.jsx +152 -0
  31. package/kits/basic-2d/editors/CodeEditor.jsx +112 -0
  32. package/kits/basic-2d/editors/DrawingEditor.jsx +222 -0
  33. package/kits/basic-2d/editors/FileBrowser.jsx +143 -0
  34. package/kits/basic-2d/editors/PlayOnly.jsx +21 -0
  35. package/kits/basic-2d/editors/SceneEditor.jsx +1012 -0
  36. package/kits/basic-2d/editors/behaviorRegistry.js +24 -0
  37. package/kits/basic-2d/editors/editorHistory.js +52 -0
  38. package/kits/basic-2d/engine/ScenePlayer.jsx +83 -0
  39. package/kits/basic-2d/engine/SceneUI.jsx +67 -0
  40. package/kits/basic-2d/engine/TouchControls.jsx +136 -0
  41. package/kits/basic-2d/engine/autoInspector.jsx +51 -0
  42. package/kits/basic-2d/engine/files.js +62 -0
  43. package/kits/basic-2d/engine/scene.js +420 -0
  44. package/kits/basic-2d/engine/ui.jsx +344 -0
  45. package/kits/basic-2d/engine/ui.module.css +928 -0
  46. package/kits/basic-2d/eslint.config.js +50 -0
  47. package/kits/basic-2d/index.html +11 -0
  48. package/kits/basic-2d/main.jsx +10 -0
  49. package/kits/basic-2d/package-lock.json +2706 -0
  50. package/kits/basic-2d/package.json +41 -0
  51. package/kits/basic-2d/scenes/main.scene +108 -0
  52. package/kits/basic-2d/vite.config.js +1 -0
  53. package/kits/basic-2d-frozen/.prettierrc +8 -0
  54. package/kits/basic-2d-frozen/CLAUDE.md +131 -0
  55. package/kits/basic-2d-frozen/behaviors/Camera.jsx +43 -0
  56. package/kits/basic-2d-frozen/behaviors/Collider.jsx +71 -0
  57. package/kits/basic-2d-frozen/behaviors/Drawing.jsx +139 -0
  58. package/kits/basic-2d-frozen/behaviors/Layout.jsx +16 -0
  59. package/kits/basic-2d-frozen/drawings/floor.drawing +70 -0
  60. package/kits/basic-2d-frozen/editors/App.jsx +152 -0
  61. package/kits/basic-2d-frozen/editors/CodeEditor.jsx +112 -0
  62. package/kits/basic-2d-frozen/editors/DrawingEditor.jsx +222 -0
  63. package/kits/basic-2d-frozen/editors/FileBrowser.jsx +143 -0
  64. package/kits/basic-2d-frozen/editors/PlayOnly.jsx +21 -0
  65. package/kits/basic-2d-frozen/editors/SceneEditor.jsx +1012 -0
  66. package/kits/basic-2d-frozen/editors/behaviorRegistry.js +24 -0
  67. package/kits/basic-2d-frozen/editors/editorHistory.js +52 -0
  68. package/kits/basic-2d-frozen/engine/ScenePlayer.jsx +83 -0
  69. package/kits/basic-2d-frozen/engine/SceneUI.jsx +67 -0
  70. package/kits/basic-2d-frozen/engine/TouchControls.jsx +136 -0
  71. package/kits/basic-2d-frozen/engine/autoInspector.jsx +51 -0
  72. package/kits/basic-2d-frozen/engine/files.js +62 -0
  73. package/kits/basic-2d-frozen/engine/scene.js +420 -0
  74. package/kits/basic-2d-frozen/engine/ui.jsx +344 -0
  75. package/kits/basic-2d-frozen/engine/ui.module.css +928 -0
  76. package/kits/basic-2d-frozen/eslint.config.js +50 -0
  77. package/kits/basic-2d-frozen/index.html +11 -0
  78. package/kits/basic-2d-frozen/main.jsx +10 -0
  79. package/kits/basic-2d-frozen/package-lock.json +2706 -0
  80. package/kits/basic-2d-frozen/package.json +41 -0
  81. package/kits/basic-2d-frozen/scenes/main.scene +108 -0
  82. package/kits/basic-2d-frozen/vite.config.js +1 -0
  83. package/kits/rpg-2d/.prettierrc +8 -0
  84. package/kits/rpg-2d/behaviors/Camera.tsx +52 -0
  85. package/kits/rpg-2d/behaviors/Collider.tsx +98 -0
  86. package/kits/rpg-2d/behaviors/Dialog.tsx +184 -0
  87. package/kits/rpg-2d/behaviors/Drawing.tsx +161 -0
  88. package/kits/rpg-2d/behaviors/Friend.tsx +45 -0
  89. package/kits/rpg-2d/behaviors/Layout.tsx +29 -0
  90. package/kits/rpg-2d/behaviors/PlayerController.tsx +255 -0
  91. package/kits/rpg-2d/behaviors/Portal.tsx +60 -0
  92. package/kits/rpg-2d/behaviors/QuestLog.tsx +90 -0
  93. package/kits/rpg-2d/behaviors/SaveMenu.tsx +123 -0
  94. package/kits/rpg-2d/behaviors/Tilemap.tsx +90 -0
  95. package/kits/rpg-2d/drawings/bld-home.drawing +8136 -0
  96. package/kits/rpg-2d/drawings/env-crate.drawing +509 -0
  97. package/kits/rpg-2d/drawings/env-fence.drawing +536 -0
  98. package/kits/rpg-2d/drawings/env-flower-bed.drawing +607 -0
  99. package/kits/rpg-2d/drawings/env-fountain.drawing +2622 -0
  100. package/kits/rpg-2d/drawings/env-hedge.drawing +601 -0
  101. package/kits/rpg-2d/drawings/env-house-blue.drawing +1 -0
  102. package/kits/rpg-2d/drawings/env-house-green.drawing +1 -0
  103. package/kits/rpg-2d/drawings/env-tree-oak.drawing +1540 -0
  104. package/kits/rpg-2d/drawings/env-tree-pine.drawing +1315 -0
  105. package/kits/rpg-2d/drawings/floor.drawing +70 -0
  106. package/kits/rpg-2d/drawings/fx-sparkle.drawing +926 -0
  107. package/kits/rpg-2d/drawings/npc-juno-idle-down.drawing +1099 -0
  108. package/kits/rpg-2d/drawings/npc-juno-walk-down.drawing +4177 -0
  109. package/kits/rpg-2d/drawings/npc-opal-idle-down.drawing +1099 -0
  110. package/kits/rpg-2d/drawings/npc-opal-walk-down.drawing +4177 -0
  111. package/kits/rpg-2d/drawings/player-idle-down.drawing +1070 -0
  112. package/kits/rpg-2d/drawings/player-idle-left.drawing +1070 -0
  113. package/kits/rpg-2d/drawings/player-idle-right.drawing +1070 -0
  114. package/kits/rpg-2d/drawings/player-idle-up.drawing +1070 -0
  115. package/kits/rpg-2d/drawings/player-walk-down.drawing +4148 -0
  116. package/kits/rpg-2d/drawings/player-walk-left.drawing +4148 -0
  117. package/kits/rpg-2d/drawings/player-walk-right.drawing +4148 -0
  118. package/kits/rpg-2d/drawings/player-walk-up.drawing +4148 -0
  119. package/kits/rpg-2d/editors/App.tsx +163 -0
  120. package/kits/rpg-2d/editors/CodeEditor.tsx +120 -0
  121. package/kits/rpg-2d/editors/DrawingEditor.tsx +278 -0
  122. package/kits/rpg-2d/editors/FileBrowser.tsx +191 -0
  123. package/kits/rpg-2d/editors/PlayOnly.tsx +26 -0
  124. package/kits/rpg-2d/editors/SceneEditor.tsx +1093 -0
  125. package/kits/rpg-2d/editors/behaviorRegistry.ts +33 -0
  126. package/kits/rpg-2d/editors/editorHistory.ts +75 -0
  127. package/kits/rpg-2d/editors/editorProps.ts +10 -0
  128. package/kits/rpg-2d/engine/ScenePlayer.tsx +130 -0
  129. package/kits/rpg-2d/engine/SceneUI.tsx +74 -0
  130. package/kits/rpg-2d/engine/TouchControls.tsx +157 -0
  131. package/kits/rpg-2d/engine/autoInspector.tsx +111 -0
  132. package/kits/rpg-2d/engine/drawing.ts +81 -0
  133. package/kits/rpg-2d/engine/files.ts +215 -0
  134. package/kits/rpg-2d/engine/scene.ts +484 -0
  135. package/kits/rpg-2d/engine/ui.module.css +928 -0
  136. package/kits/rpg-2d/engine/ui.tsx +483 -0
  137. package/kits/rpg-2d/eslint.config.js +46 -0
  138. package/kits/rpg-2d/index.html +11 -0
  139. package/kits/rpg-2d/main.tsx +14 -0
  140. package/kits/rpg-2d/package-lock.json +3149 -0
  141. package/kits/rpg-2d/package.json +46 -0
  142. package/kits/rpg-2d/scenes/main.scene +203 -0
  143. package/kits/rpg-2d/tsconfig.json +17 -0
  144. package/kits/rpg-2d/vite-env.d.ts +7 -0
  145. package/kits/rpg-2d/vite.config.js +1 -0
  146. package/package.json +27 -5
  147. package/AGENTS.md +0 -25
  148. package/dist/push.d.ts +0 -1
  149. package/src/api.ts +0 -160
  150. package/src/bundle.ts +0 -28
  151. package/src/config.ts +0 -36
  152. package/src/index.ts +0 -143
  153. package/src/init.ts +0 -71
  154. package/src/login.ts +0 -24
  155. package/src/preview.ts +0 -94
  156. package/src/push.ts +0 -118
  157. package/src/serve.ts +0 -134
  158. package/tsconfig.json +0 -13
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "rpg-2d",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "serve": "node ../../cli/dist/index.js serve . --open",
7
+ "restart": "node ../../cli/dist/index.js restart .",
8
+ "screenshot": "node ../../cli/dist/index.js screenshot .",
9
+ "check": "eslint . && jscpd && tsc --noEmit && node --input-type=module -e \"const { bundleProject } = await import('../../cli/dist/bundle.js'); await bundleProject('.');\""
10
+ },
11
+ "jscpd": {
12
+ "path": [
13
+ "engine",
14
+ "editors",
15
+ "behaviors"
16
+ ],
17
+ "threshold": 0,
18
+ "reporters": [
19
+ "consoleFull"
20
+ ]
21
+ },
22
+ "dependencies": {
23
+ "@codemirror/commands": "^6.10.3",
24
+ "@codemirror/lang-javascript": "^6.2.5",
25
+ "@codemirror/language": "^6.12.3",
26
+ "@codemirror/state": "^6.6.0",
27
+ "@codemirror/view": "^6.41.1",
28
+ "@fortawesome/free-solid-svg-icons": "^5.15.4",
29
+ "@lezer/highlight": "^1.2.3",
30
+ "castle-web-sdk": "file:../../sdk",
31
+ "codemirror": "^6.0.2",
32
+ "react": "^19.2.4",
33
+ "react-dom": "^19.2.4"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^25.6.0",
37
+ "@types/react": "^19.2.14",
38
+ "@types/react-dom": "^19.2.3",
39
+ "eslint": "^9.0.0",
40
+ "eslint-plugin-react-hooks": "^5.0.0",
41
+ "jscpd": "^4.0.5",
42
+ "prettier": "^3.8.3",
43
+ "typescript": "^6.0.3",
44
+ "typescript-eslint": "^8.0.0"
45
+ }
46
+ }
@@ -0,0 +1,203 @@
1
+ {
2
+ "name": "Overworld",
3
+ "background": "#2d5a3d",
4
+ "actors": [
5
+ {
6
+ "id": "player",
7
+ "components": {
8
+ "Layout": { "x": 460, "y": 620, "width": 64, "height": 64, "z": 20, "rotation": 0 },
9
+ "Drawing": { "file": "drawings/player-idle-down.drawing" },
10
+ "Collider": { "kind": "solid", "width": 40, "height": 24, "offsetX": 12, "offsetY": 36 },
11
+ "PlayerController": { "speed": 200, "spritePrefix": "drawings/player", "interactRange": 36 }
12
+ }
13
+ },
14
+ {
15
+ "id": "camera",
16
+ "components": {
17
+ "Layout": { "x": 0, "y": 0, "width": 1, "height": 1, "z": 0, "rotation": 0 },
18
+ "Camera": { "target": "player", "followX": true, "followY": true, "roomWidth": 1024, "roomHeight": 1024 }
19
+ }
20
+ },
21
+ {
22
+ "id": "manager",
23
+ "components": {
24
+ "Layout": { "x": 0, "y": 0, "width": 1, "height": 1, "z": 100, "rotation": 0 },
25
+ "QuestLog": {
26
+ "chapter": "Chapter 1",
27
+ "steps": [
28
+ { "id": "find-juno", "title": "Find Juno", "hint": "She's near the fountain." },
29
+ { "id": "find-opal", "title": "Talk to Opal", "hint": "Look between the cottages." }
30
+ ],
31
+ "completedIds": []
32
+ },
33
+ "SaveMenu": { "storageKey": "rpg-2d-starter/save" }
34
+ }
35
+ },
36
+
37
+ {
38
+ "id": "house_blue",
39
+ "components": {
40
+ "Layout": { "x": 80, "y": 80, "width": 240, "height": 208, "z": 10, "rotation": 0 },
41
+ "Drawing": { "file": "drawings/env-house-blue.drawing" },
42
+ "Collider": { "kind": "solid", "width": 216, "height": 104, "offsetX": 12, "offsetY": 96 }
43
+ }
44
+ },
45
+ {
46
+ "id": "house_green",
47
+ "components": {
48
+ "Layout": { "x": 700, "y": 100, "width": 240, "height": 208, "z": 10, "rotation": 0 },
49
+ "Drawing": { "file": "drawings/env-house-green.drawing" },
50
+ "Collider": { "kind": "solid", "width": 216, "height": 104, "offsetX": 12, "offsetY": 96 }
51
+ }
52
+ },
53
+ {
54
+ "id": "house_cottage",
55
+ "components": {
56
+ "Layout": { "x": 400, "y": 760, "width": 224, "height": 192, "z": 10, "rotation": 0 },
57
+ "Drawing": { "file": "drawings/bld-home.drawing" },
58
+ "Collider": { "kind": "solid", "width": 200, "height": 96, "offsetX": 12, "offsetY": 86 }
59
+ }
60
+ },
61
+
62
+ { "id": "fence_n_1", "components": {
63
+ "Layout": { "x": 0, "y": 380, "width": 96, "height": 48, "z": 12, "rotation": 0 },
64
+ "Drawing": { "file": "drawings/env-fence.drawing" },
65
+ "Collider": { "kind": "solid", "width": 96, "height": 24, "offsetX": 0, "offsetY": 12 }
66
+ }},
67
+ { "id": "fence_n_2", "components": {
68
+ "Layout": { "x": 96, "y": 380, "width": 96, "height": 48, "z": 12, "rotation": 0 },
69
+ "Drawing": { "file": "drawings/env-fence.drawing" },
70
+ "Collider": { "kind": "solid", "width": 96, "height": 24, "offsetX": 0, "offsetY": 12 }
71
+ }},
72
+ { "id": "fence_s_1", "components": {
73
+ "Layout": { "x": 700, "y": 700, "width": 96, "height": 48, "z": 12, "rotation": 0 },
74
+ "Drawing": { "file": "drawings/env-fence.drawing" },
75
+ "Collider": { "kind": "solid", "width": 96, "height": 24, "offsetX": 0, "offsetY": 12 }
76
+ }},
77
+ { "id": "fence_s_2", "components": {
78
+ "Layout": { "x": 796, "y": 700, "width": 96, "height": 48, "z": 12, "rotation": 0 },
79
+ "Drawing": { "file": "drawings/env-fence.drawing" },
80
+ "Collider": { "kind": "solid", "width": 96, "height": 24, "offsetX": 0, "offsetY": 12 }
81
+ }},
82
+ { "id": "fence_s_3", "components": {
83
+ "Layout": { "x": 892, "y": 700, "width": 96, "height": 48, "z": 12, "rotation": 0 },
84
+ "Drawing": { "file": "drawings/env-fence.drawing" },
85
+ "Collider": { "kind": "solid", "width": 96, "height": 24, "offsetX": 0, "offsetY": 12 }
86
+ }},
87
+
88
+ { "id": "tree_1", "components": {
89
+ "Layout": { "x": 40, "y": 540, "width": 112, "height": 168, "z": 15, "rotation": 0 },
90
+ "Drawing": { "file": "drawings/env-tree-oak.drawing" },
91
+ "Collider": { "kind": "solid", "width": 44, "height": 24, "offsetX": 34, "offsetY": 128 }
92
+ }},
93
+ { "id": "tree_2", "components": {
94
+ "Layout": { "x": 280, "y": 380, "width": 112, "height": 168, "z": 15, "rotation": 0 },
95
+ "Drawing": { "file": "drawings/env-tree-pine.drawing" },
96
+ "Collider": { "kind": "solid", "width": 44, "height": 24, "offsetX": 34, "offsetY": 128 }
97
+ }},
98
+ { "id": "tree_3", "components": {
99
+ "Layout": { "x": 880, "y": 380, "width": 112, "height": 168, "z": 15, "rotation": 0 },
100
+ "Drawing": { "file": "drawings/env-tree-oak.drawing" },
101
+ "Collider": { "kind": "solid", "width": 44, "height": 24, "offsetX": 34, "offsetY": 128 }
102
+ }},
103
+ { "id": "tree_4", "components": {
104
+ "Layout": { "x": 160, "y": 800, "width": 112, "height": 168, "z": 15, "rotation": 0 },
105
+ "Drawing": { "file": "drawings/env-tree-pine.drawing" },
106
+ "Collider": { "kind": "solid", "width": 44, "height": 24, "offsetX": 34, "offsetY": 128 }
107
+ }},
108
+ { "id": "tree_5", "components": {
109
+ "Layout": { "x": 760, "y": 840, "width": 112, "height": 168, "z": 15, "rotation": 0 },
110
+ "Drawing": { "file": "drawings/env-tree-oak.drawing" },
111
+ "Collider": { "kind": "solid", "width": 44, "height": 24, "offsetX": 34, "offsetY": 128 }
112
+ }},
113
+
114
+ { "id": "fountain", "components": {
115
+ "Layout": { "x": 470, "y": 450, "width": 128, "height": 128, "z": 14, "rotation": 0 },
116
+ "Drawing": { "file": "drawings/env-fountain.drawing" },
117
+ "Collider": { "kind": "solid", "width": 112, "height": 64, "offsetX": 8, "offsetY": 56 }
118
+ }},
119
+
120
+ { "id": "hedge_1", "components": {
121
+ "Layout": { "x": 360, "y": 320, "width": 96, "height": 48, "z": 13, "rotation": 0 },
122
+ "Drawing": { "file": "drawings/env-hedge.drawing" },
123
+ "Collider": { "kind": "solid", "width": 96, "height": 32, "offsetX": 0, "offsetY": 12 }
124
+ }},
125
+ { "id": "hedge_2", "components": {
126
+ "Layout": { "x": 600, "y": 320, "width": 96, "height": 48, "z": 13, "rotation": 0 },
127
+ "Drawing": { "file": "drawings/env-hedge.drawing" },
128
+ "Collider": { "kind": "solid", "width": 96, "height": 32, "offsetX": 0, "offsetY": 12 }
129
+ }},
130
+
131
+ { "id": "flowers_1", "components": {
132
+ "Layout": { "x": 200, "y": 480, "width": 64, "height": 64, "z": 13, "rotation": 0 },
133
+ "Drawing": { "file": "drawings/env-flower-bed.drawing" },
134
+ "Collider": { "kind": "solid", "width": 56, "height": 40, "offsetX": 4, "offsetY": 18 }
135
+ }},
136
+ { "id": "flowers_2", "components": {
137
+ "Layout": { "x": 640, "y": 620, "width": 64, "height": 64, "z": 13, "rotation": 0 },
138
+ "Drawing": { "file": "drawings/env-flower-bed.drawing" },
139
+ "Collider": { "kind": "solid", "width": 56, "height": 40, "offsetX": 4, "offsetY": 18 }
140
+ }},
141
+
142
+ { "id": "crate_1", "components": {
143
+ "Layout": { "x": 850, "y": 580, "width": 64, "height": 64, "z": 14, "rotation": 0 },
144
+ "Drawing": { "file": "drawings/env-crate.drawing" },
145
+ "Collider": { "kind": "solid", "width": 64, "height": 48, "offsetX": 0, "offsetY": 16 }
146
+ }},
147
+ { "id": "crate_2", "components": {
148
+ "Layout": { "x": 920, "y": 580, "width": 64, "height": 64, "z": 14, "rotation": 0 },
149
+ "Drawing": { "file": "drawings/env-crate.drawing" },
150
+ "Collider": { "kind": "solid", "width": 64, "height": 48, "offsetX": 0, "offsetY": 16 }
151
+ }},
152
+
153
+ { "id": "coin_1", "components": {
154
+ "Layout": { "x": 360, "y": 600, "width": 36, "height": 36, "z": 16, "rotation": 0 },
155
+ "Drawing": { "file": "drawings/fx-sparkle.drawing", "tint": "#ffd166ff" }
156
+ }},
157
+ { "id": "coin_2", "components": {
158
+ "Layout": { "x": 720, "y": 480, "width": 36, "height": 36, "z": 16, "rotation": 0 },
159
+ "Drawing": { "file": "drawings/fx-sparkle.drawing", "tint": "#ffd166ff" }
160
+ }},
161
+ { "id": "coin_3", "components": {
162
+ "Layout": { "x": 280, "y": 720, "width": 36, "height": 36, "z": 16, "rotation": 0 },
163
+ "Drawing": { "file": "drawings/fx-sparkle.drawing", "tint": "#ffd166ff" }
164
+ }},
165
+ { "id": "coin_4", "components": {
166
+ "Layout": { "x": 560, "y": 850, "width": 36, "height": 36, "z": 16, "rotation": 0 },
167
+ "Drawing": { "file": "drawings/fx-sparkle.drawing", "tint": "#ffd166ff" }
168
+ }},
169
+
170
+ {
171
+ "id": "npc_juno",
172
+ "components": {
173
+ "Layout": { "x": 380, "y": 540, "width": 64, "height": 64, "z": 20, "rotation": 0 },
174
+ "Drawing": { "file": "drawings/npc-juno-idle-down.drawing" },
175
+ "Friend": { "name": "Juno" },
176
+ "Dialog": {
177
+ "speaker": "Juno",
178
+ "lines": [
179
+ "Hey traveler! Welcome to the village.",
180
+ "There's been strange noises from the woods at night...",
181
+ "If you're brave enough, find Opal -- she knows the way."
182
+ ]
183
+ }
184
+ }
185
+ },
186
+ {
187
+ "id": "npc_opal",
188
+ "components": {
189
+ "Layout": { "x": 640, "y": 460, "width": 64, "height": 64, "z": 20, "rotation": 0 },
190
+ "Drawing": { "file": "drawings/npc-opal-idle-down.drawing" },
191
+ "Friend": { "name": "Opal" },
192
+ "Dialog": {
193
+ "speaker": "Opal",
194
+ "lines": [
195
+ "Hmph. You smell like the road.",
196
+ "The forest path is east of here. Don't go alone.",
197
+ "Take this -- it'll keep the dark off your boots."
198
+ ]
199
+ }
200
+ }
201
+ }
202
+ ]
203
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowImportingTsExtensions": true,
4
+ "allowSyntheticDefaultImports": true,
5
+ "isolatedModules": true,
6
+ "jsx": "react",
7
+ "lib": ["DOM", "DOM.Iterable", "ES2023"],
8
+ "module": "ESNext",
9
+ "moduleResolution": "Bundler",
10
+ "noEmit": true,
11
+ "skipLibCheck": true,
12
+ "strict": true,
13
+ "target": "ES2023",
14
+ "types": ["vite/client"]
15
+ },
16
+ "include": ["**/*.ts", "**/*.tsx"]
17
+ }
@@ -0,0 +1,7 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ declare module 'castle-web-sdk' {
4
+ export function setup(): void;
5
+ export function isEdit(): boolean;
6
+ export function writeFile(path: string, contents: string): Promise<{ ok: true; path: string }>;
7
+ }
@@ -0,0 +1 @@
1
+ export default {};
package/package.json CHANGED
@@ -1,15 +1,30 @@
1
1
  {
2
2
  "name": "castle-web-cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "castle-web": "./dist/index.js"
7
7
  },
8
8
  "scripts": {
9
- "build": "tsc",
10
- "dev": "tsc --watch"
9
+ "build": "tsc && rm -rf kits && cp -R ../kits kits",
10
+ "dev": "tsc --watch",
11
+ "check": "eslint . && jscpd && tsc --noEmit"
12
+ },
13
+ "jscpd": {
14
+ "path": [
15
+ "src"
16
+ ],
17
+ "threshold": 0,
18
+ "reporters": [
19
+ "consoleFull"
20
+ ]
11
21
  },
12
22
  "dependencies": {
23
+ "@lydell/node-pty": "^1.2.0-beta.12",
24
+ "@xterm/addon-fit": "^0.11.0",
25
+ "@xterm/addon-serialize": "^0.14.0",
26
+ "@xterm/headless": "^6.0.0",
27
+ "@xterm/xterm": "^6.0.0",
13
28
  "nanoid": "^5.1.7",
14
29
  "open": "^10.0.0",
15
30
  "vite": "^8.0.3",
@@ -19,6 +34,13 @@
19
34
  "devDependencies": {
20
35
  "@types/node": "^20.0.0",
21
36
  "@types/ws": "^8.18.1",
22
- "typescript": "^5.0.0"
23
- }
37
+ "eslint": "^9.0.0",
38
+ "jscpd": "^4.0.5",
39
+ "typescript": "^5.0.0",
40
+ "typescript-eslint": "^8.0.0"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "kits"
45
+ ]
24
46
  }
package/AGENTS.md DELETED
@@ -1,25 +0,0 @@
1
- # Castle Web CLI
2
-
3
- ## Commands
4
-
5
- - `npx castle-web-cli init <dir>` — scaffold a new project (index.html, game.js, package.json with castle-web-sdk)
6
- - `npx castle-web-cli serve [dir] [--port PORT] [--open]` — local dev server with Vite (default port 3737, WS on 3738). Writes `.castle/serve.json` with active ports. No auto-reload — use `restart` to reload.
7
- - `npx castle-web-cli restart [dir] [--port PORT]` — reload the game in the browser. Reads WS port from `.castle/serve.json` if no `--port` given.
8
- - `npx castle-web-cli screenshot [dir] [--out FILE] [--port PORT]` — capture a screenshot from the running game (default: screenshot.png). Reads WS port from `.castle/serve.json` if no `--port` given.
9
- - `npx castle-web-cli push [dir]` — bundle with Vite and publish to Castle. Creates a new deck if no castle.json exists. Automatically saves a preview image on first push if `serve` is running (reads WS port from `.castle/serve.json`).
10
- - `npx castle-web-cli save-preview-image [dir] [--port PORT] [--no-restart]` — restarts the deck, takes a screenshot, uploads it as the deck's preview image. Requires `serve` running and `castle.json` to exist (push first). Reads WS port from `.castle/serve.json` if no `--port` given.
11
- - `npx castle-web-cli login` — authenticate with Castle (saves token to ~/.castle/config.json)
12
-
13
- ## Project Structure
14
-
15
- ```
16
- my-game/
17
- index.html # entry point
18
- game.js # game code
19
- package.json # dependencies (castle-web-sdk)
20
- castle.json # deck/card IDs (created by first push)
21
- .castle/ # runtime state (logs, screenshots, serve port)
22
- serve.json # active serve ports (written by serve, cleaned up on exit)
23
- logs.txt # forwarded console.log/warn/error
24
- screenshots/ # captured screenshots
25
- ```
package/dist/push.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare function push(dir: string): Promise<void>;
package/src/api.ts DELETED
@@ -1,160 +0,0 @@
1
- import * as config from './config.js';
2
-
3
- const API_HOST = 'https://api.castle.xyz/graphql';
4
-
5
- async function graphql(query: string, variables?: Record<string, unknown>): Promise<any> {
6
- const token = config.getToken();
7
- const headers: Record<string, string> = {
8
- 'X-OS': 'cli',
9
- 'X-CLI-API-Version': '2',
10
- 'Content-Type': 'application/json',
11
- Accept: 'application/json',
12
- };
13
- if (token) headers['X-Auth-Token'] = token;
14
- const res = await fetch(API_HOST, {
15
- method: 'POST',
16
- headers,
17
- body: JSON.stringify(variables ? { query, variables } : { query }),
18
- signal: AbortSignal.timeout(10000),
19
- });
20
- return res.json();
21
- }
22
-
23
- function handleAPIError(data: any): void {
24
- if (data?.errors?.length) {
25
- const err: any = new Error(data.errors[0]?.message ?? 'GraphQL error');
26
- err.extensions = data.errors[0]?.extensions;
27
- throw err;
28
- }
29
- }
30
-
31
- export async function startCLILogin(): Promise<{ pollToken: string; url: string }> {
32
- const data = await graphql(`mutation { startCLILogin { pollToken url } }`);
33
- handleAPIError(data);
34
- return data.data.startCLILogin;
35
- }
36
-
37
- export async function pollForCLILogin(pollToken: string): Promise<any> {
38
- const data = await graphql(
39
- `query($pollToken: String!) {
40
- pollForCLILogin(pollToken: $pollToken) {
41
- userId username token isAnonymous
42
- }
43
- }`,
44
- { pollToken }
45
- );
46
- handleAPIError(data);
47
- return data.data?.pollForCLILogin ?? null;
48
- }
49
-
50
- export async function me(): Promise<any> {
51
- try {
52
- const data = await graphql(`query { me { userId username isAnonymous photo { url } photoFrame { frameUrl } } }`);
53
- return data.data?.me ?? null;
54
- } catch {
55
- return null;
56
- }
57
- }
58
-
59
- export async function myDecks(): Promise<any[]> {
60
- const data = await graphql(`query {
61
- me {
62
- decks {
63
- deckId
64
- title
65
- initialCard { cardId }
66
- }
67
- }
68
- }`);
69
- handleAPIError(data);
70
- return data.data?.me?.decks ?? [];
71
- }
72
-
73
- export async function updateCardAndDeckV2(
74
- deck: Record<string, unknown>,
75
- card: Record<string, unknown>,
76
- ): Promise<{ deckId: string; cardId: string }> {
77
- const data = await graphql(
78
- `mutation($deck: DeckInput!, $card: CardInput!) {
79
- updateCardAndDeckV2(deck: $deck, card: $card) {
80
- deck { deckId }
81
- card { cardId }
82
- }
83
- }`,
84
- { deck, card }
85
- );
86
- handleAPIError(data);
87
- return {
88
- deckId: data.data.updateCardAndDeckV2.deck.deckId,
89
- cardId: data.data.updateCardAndDeckV2.card.cardId,
90
- };
91
- }
92
-
93
- export async function createDeck(
94
- deck: Record<string, unknown>,
95
- card: Record<string, unknown>
96
- ): Promise<{ deckId: string; cardId: string }> {
97
- const data = await graphql(
98
- `mutation($deck: DeckInput!, $card: CardInput!) {
99
- createDeck(deck: $deck, card: $card) {
100
- deckId
101
- initialCard { cardId }
102
- }
103
- }`,
104
- { deck, card }
105
- );
106
- handleAPIError(data);
107
- return {
108
- deckId: data.data.createDeck.deckId,
109
- cardId: data.data.createDeck.initialCard.cardId,
110
- };
111
- }
112
-
113
- export async function createSceneDataUploadConfig(cardIds: string[]): Promise<any[]> {
114
- const data = await graphql(
115
- `mutation($cardIds: [ID!]!) {
116
- createSceneDataUploadConfig(cardIds: $cardIds) {
117
- cardId uploadId postUrl postFields
118
- }
119
- }`,
120
- { cardIds }
121
- );
122
- handleAPIError(data);
123
- return data.data.createSceneDataUploadConfig;
124
- }
125
-
126
- export async function uploadSceneData(
127
- cards: Array<{ cardId: string; uploadId: string }>
128
- ): Promise<any[]> {
129
- const data = await graphql(
130
- `mutation($cards: [CardSceneDataInput!]!) {
131
- uploadSceneData(cards: $cards) { cardId sceneDataUrl }
132
- }`,
133
- { cards }
134
- );
135
- handleAPIError(data);
136
- return data.data.uploadSceneData;
137
- }
138
-
139
- export async function uploadBase64(base64: string, filename: string): Promise<{ fileId: string; url: string }> {
140
- const data = await graphql(
141
- `mutation($data: String!, $filename: String, $mimetype: String) {
142
- uploadBase64(data: $data, filename: $filename, mimetype: $mimetype) {
143
- fileId url
144
- }
145
- }`,
146
- { data: base64, filename, mimetype: 'image/png' }
147
- );
148
- handleAPIError(data);
149
- return data.data.uploadBase64;
150
- }
151
-
152
- export async function updateCardCustomBackgroundImage(cardId: string, backgroundImageFileId: string): Promise<void> {
153
- const data = await graphql(
154
- `mutation($cardId: ID!, $backgroundImageFileId: ID) {
155
- updateCardCustomBackgroundImage(cardId: $cardId, backgroundImageFileId: $backgroundImageFileId)
156
- }`,
157
- { cardId, backgroundImageFileId }
158
- );
159
- handleAPIError(data);
160
- }
package/src/bundle.ts DELETED
@@ -1,28 +0,0 @@
1
- import * as path from 'path';
2
- import { build } from 'vite';
3
- import { viteSingleFile } from 'vite-plugin-singlefile';
4
-
5
- export async function bundleProject(dir: string): Promise<string> {
6
- const result = await build({
7
- root: dir,
8
- plugins: [viteSingleFile()],
9
- build: {
10
- write: false,
11
- rollupOptions: {
12
- input: path.join(dir, 'index.html'),
13
- },
14
- },
15
- logLevel: 'silent',
16
- });
17
-
18
- const output = Array.isArray(result) ? result[0] : result;
19
- if (!('output' in output)) throw new Error('Unexpected build result');
20
-
21
- for (const chunk of output.output) {
22
- if (chunk.type === 'asset' && chunk.fileName.endsWith('.html')) {
23
- return chunk.source as string;
24
- }
25
- }
26
-
27
- throw new Error('No HTML output found from build');
28
- }
package/src/config.ts DELETED
@@ -1,36 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import * as os from 'os';
4
-
5
- function getConfigDir() {
6
- return path.join(os.homedir(), '.castle');
7
- }
8
-
9
- function readConfigFile(filename: string): any {
10
- const configDir = getConfigDir();
11
- const configFilePath = path.join(configDir, filename);
12
- try {
13
- if (fs.existsSync(configFilePath)) {
14
- return JSON.parse(fs.readFileSync(configFilePath, 'utf-8'));
15
- }
16
- } catch {}
17
- return null;
18
- }
19
-
20
- export function getToken(): string | null {
21
- const config = readConfigFile('config.json');
22
- return config ? config.token : null;
23
- }
24
-
25
- export function setToken(token: string | null): void {
26
- const configDir = getConfigDir();
27
- fs.mkdirSync(configDir, { recursive: true });
28
- const existing = readConfigFile('config.json') ?? {};
29
- if (token === null) {
30
- delete existing.token;
31
- } else {
32
- existing.token = token;
33
- }
34
- const configFilePath = path.join(configDir, 'config.json');
35
- fs.writeFileSync(configFilePath, JSON.stringify(existing, null, 2), 'utf-8');
36
- }