@underpostnet/cyberia 3.2.80

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 (821) hide show
  1. package/.dockerignore +14 -0
  2. package/.env.example +144 -0
  3. package/.github/workflows/cyberia-client.cd.yml +40 -0
  4. package/.github/workflows/cyberia-server.cd.yml +40 -0
  5. package/.github/workflows/docker-image.cyberia-client.ci.yml +71 -0
  6. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +70 -0
  7. package/.github/workflows/docker-image.cyberia-server.ci.yml +71 -0
  8. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +71 -0
  9. package/.github/workflows/docker-image.engine-cyberia.ci.yml +53 -0
  10. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +53 -0
  11. package/.github/workflows/engine-cyberia.cd.yml +157 -0
  12. package/.github/workflows/engine-cyberia.ci.yml +125 -0
  13. package/.github/workflows/ghpkg.ci.yml +160 -0
  14. package/.github/workflows/gitlab.ci.yml +20 -0
  15. package/.github/workflows/hardhat.ci.yml +82 -0
  16. package/.github/workflows/npmpkg.ci.yml +126 -0
  17. package/.github/workflows/publish.ci.yml +87 -0
  18. package/.github/workflows/publish.cyberia.ci.yml +79 -0
  19. package/.github/workflows/pwa-microservices-template-page.cd.yml +65 -0
  20. package/.github/workflows/pwa-microservices-template-test.ci.yml +33 -0
  21. package/.github/workflows/release.cd.yml +46 -0
  22. package/.nycrc +9 -0
  23. package/.prettierignore +12 -0
  24. package/.prettierrc +9 -0
  25. package/.vscode/extensions.json +10 -0
  26. package/.vscode/settings.json +26 -0
  27. package/CHANGELOG.md +2516 -0
  28. package/CLI-HELP.md +1099 -0
  29. package/Dockerfile +161 -0
  30. package/Dockerfile.dev +161 -0
  31. package/Dockerfile.test +165 -0
  32. package/LICENSE +21 -0
  33. package/README.md +259 -0
  34. package/baremetal/commission-workflows.json +211 -0
  35. package/baremetal/packer-workflows.json +24 -0
  36. package/bin/build.js +261 -0
  37. package/bin/build.template.js +33 -0
  38. package/bin/cyberia.js +5627 -0
  39. package/bin/deploy.js +1547 -0
  40. package/bin/index.js +5627 -0
  41. package/bump.config.js +27 -0
  42. package/compose.env +144 -0
  43. package/conf.js +952 -0
  44. package/deployment.yaml +418 -0
  45. package/docker-compose.yml +434 -0
  46. package/examples/static-page/README.md +150 -0
  47. package/examples/static-page/ssr-components/CustomPage.js +554 -0
  48. package/examples/static-page/static-config-example.json +57 -0
  49. package/hardhat/.env.example +31 -0
  50. package/hardhat/contracts/ObjectLayerToken.sol +391 -0
  51. package/hardhat/deployments/.gitkeep +0 -0
  52. package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
  53. package/hardhat/hardhat.config.js +136 -0
  54. package/hardhat/ignition/modules/.gitkeep +0 -0
  55. package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
  56. package/hardhat/networks/besu-object-layer.network.json +138 -0
  57. package/hardhat/package-lock.json +2871 -0
  58. package/hardhat/package.json +43 -0
  59. package/hardhat/scripts/deployObjectLayerToken.js +98 -0
  60. package/hardhat/test/ObjectLayerToken.js +696 -0
  61. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  62. package/hardhat/types/ethers-contracts/common.ts +92 -0
  63. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  64. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  65. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  66. package/hardhat/types/ethers-contracts/index.ts +6 -0
  67. package/ipfs/configure-ipfs.sh +13 -0
  68. package/jsconfig.json +9 -0
  69. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +48 -0
  70. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +48 -0
  71. package/manifests/deployment/adminer/deployment.yaml +32 -0
  72. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  73. package/manifests/deployment/adminer/service.yaml +13 -0
  74. package/manifests/deployment/dd-cyberia-development/deployment.yaml +418 -0
  75. package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
  76. package/manifests/deployment/dd-cyberia-development/proxy.yaml +189 -0
  77. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +164 -0
  78. package/manifests/deployment/dd-default-development/deployment.yaml +163 -0
  79. package/manifests/deployment/dd-default-development/proxy.yaml +46 -0
  80. package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
  81. package/manifests/deployment/fastapi/backend-service.yml +19 -0
  82. package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
  83. package/manifests/deployment/fastapi/frontend-service.yml +15 -0
  84. package/manifests/deployment/fastapi/initial_data.sh +8 -0
  85. package/manifests/deployment/kafka/deployment.yaml +67 -0
  86. package/manifests/deployment/mongo-express/deployment.yaml +62 -0
  87. package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
  88. package/manifests/deployment/playwright/deployment.yaml +52 -0
  89. package/manifests/deployment/spark/spark-pi-py.yaml +20 -0
  90. package/manifests/deployment/tensorflow/tf-gpu-test.yaml +63 -0
  91. package/manifests/envoy-service-nodeport.yaml +22 -0
  92. package/manifests/grafana/deployment.yaml +64 -0
  93. package/manifests/grafana/kustomization.yaml +7 -0
  94. package/manifests/grafana/pvc.yaml +12 -0
  95. package/manifests/grafana/service.yaml +14 -0
  96. package/manifests/grafana/storage-class.yaml +9 -0
  97. package/manifests/ipfs/configmap.yaml +64 -0
  98. package/manifests/ipfs/headless-service.yaml +35 -0
  99. package/manifests/ipfs/kustomization.yaml +8 -0
  100. package/manifests/ipfs/statefulset.yaml +149 -0
  101. package/manifests/ipfs/storage-class.yaml +9 -0
  102. package/manifests/kind-config-dev.yaml +20 -0
  103. package/manifests/kind-config.yaml +12 -0
  104. package/manifests/kubeadm-calico-config.yaml +14 -0
  105. package/manifests/letsencrypt-prod.yaml +14 -0
  106. package/manifests/lxd/lxd-admin-profile.yaml +26 -0
  107. package/manifests/lxd/lxd-preseed.yaml +30 -0
  108. package/manifests/mariadb/kustomization.yaml +9 -0
  109. package/manifests/mariadb/pv.yaml +12 -0
  110. package/manifests/mariadb/pvc.yaml +10 -0
  111. package/manifests/mariadb/service.yaml +10 -0
  112. package/manifests/mariadb/statefulset.yaml +56 -0
  113. package/manifests/mariadb/storage-class.yaml +10 -0
  114. package/manifests/mongodb/headless-service.yaml +10 -0
  115. package/manifests/mongodb/kustomization.yaml +11 -0
  116. package/manifests/mongodb/pv-pvc.yaml +59 -0
  117. package/manifests/mongodb/statefulset.yaml +113 -0
  118. package/manifests/mongodb/storage-class.yaml +9 -0
  119. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  120. package/manifests/mongodb-4.4/kustomization.yaml +9 -0
  121. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  122. package/manifests/mongodb-4.4/pv-pvc.yaml +19 -0
  123. package/manifests/mongodb-4.4/service-deployment.yaml +61 -0
  124. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  125. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  126. package/manifests/mysql/kustomization.yaml +7 -0
  127. package/manifests/mysql/pv-pvc.yaml +27 -0
  128. package/manifests/mysql/statefulset.yaml +55 -0
  129. package/manifests/postgresql/configmap.yaml +9 -0
  130. package/manifests/postgresql/kustomization.yaml +10 -0
  131. package/manifests/postgresql/pv.yaml +15 -0
  132. package/manifests/postgresql/pvc.yaml +13 -0
  133. package/manifests/postgresql/service.yaml +10 -0
  134. package/manifests/postgresql/statefulset.yaml +37 -0
  135. package/manifests/prometheus/deployment.yaml +82 -0
  136. package/manifests/pv-pvc-dd.yaml +34 -0
  137. package/manifests/valkey/kustomization.yaml +7 -0
  138. package/manifests/valkey/service.yaml +10 -0
  139. package/manifests/valkey/statefulset.yaml +37 -0
  140. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  141. package/mongodb/entrypoint.sh +76 -0
  142. package/nginx.conf +128 -0
  143. package/nodemon.json +6 -0
  144. package/package.json +101 -0
  145. package/packer/images/Rocky9Amd64/Makefile +62 -0
  146. package/packer/images/Rocky9Amd64/QUICKSTART.md +113 -0
  147. package/packer/images/Rocky9Amd64/README.md +122 -0
  148. package/packer/images/Rocky9Amd64/http/rocky9.ks.pkrtpl.hcl +114 -0
  149. package/packer/images/Rocky9Amd64/rocky9.pkr.hcl +164 -0
  150. package/packer/images/Rocky9Arm64/Makefile +69 -0
  151. package/packer/images/Rocky9Arm64/README.md +122 -0
  152. package/packer/images/Rocky9Arm64/http/rocky9.ks.pkrtpl.hcl +114 -0
  153. package/packer/images/Rocky9Arm64/rocky9.pkr.hcl +171 -0
  154. package/packer/scripts/fuse-nbd +64 -0
  155. package/packer/scripts/fuse-tar-root +63 -0
  156. package/proxy.yaml +189 -0
  157. package/pv-pvc.yaml +164 -0
  158. package/scripts/device-scan.sh +65 -0
  159. package/scripts/disk-clean.sh +182 -0
  160. package/scripts/disk-devices.sh +13 -0
  161. package/scripts/gen-fqdns.sh +14 -0
  162. package/scripts/gpu-diag.sh +19 -0
  163. package/scripts/ip-info.sh +118 -0
  164. package/scripts/ipxe-setup.sh +200 -0
  165. package/scripts/k3s-node-setup.sh +124 -0
  166. package/scripts/kubeadm-node-setup.sh +317 -0
  167. package/scripts/link-local-underpost-cli.sh +6 -0
  168. package/scripts/lxd-vm-setup.sh +208 -0
  169. package/scripts/maas-nat-firewalld.sh +145 -0
  170. package/scripts/maas-setup.sh +135 -0
  171. package/scripts/maas-upload-boot-resource.sh +183 -0
  172. package/scripts/nat-iptables.sh +111 -0
  173. package/scripts/nvim.sh +91 -0
  174. package/scripts/packer-init-vars-file.sh +40 -0
  175. package/scripts/packer-setup.sh +289 -0
  176. package/scripts/ports-ls.sh +33 -0
  177. package/scripts/quick-tftp.sh +19 -0
  178. package/scripts/rhel-grpc-setup.sh +56 -0
  179. package/scripts/rocky-kickstart.sh +986 -0
  180. package/scripts/rocky-pwa.sh +200 -0
  181. package/scripts/rocky-setup.sh +106 -0
  182. package/scripts/rpmfusion-ffmpeg-setup.sh +32 -0
  183. package/scripts/ssl.sh +170 -0
  184. package/scripts/test-monitor.sh +248 -0
  185. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +30 -0
  186. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +155 -0
  187. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +39 -0
  188. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +382 -0
  189. package/src/api/core/core.controller.js +8 -0
  190. package/src/api/core/core.model.js +11 -0
  191. package/src/api/core/core.router.js +23 -0
  192. package/src/api/core/core.service.js +30 -0
  193. package/src/api/crypto/crypto.controller.js +10 -0
  194. package/src/api/crypto/crypto.model.js +23 -0
  195. package/src/api/crypto/crypto.router.js +23 -0
  196. package/src/api/crypto/crypto.service.js +64 -0
  197. package/src/api/cyberia-action/cyberia-action.controller.js +8 -0
  198. package/src/api/cyberia-action/cyberia-action.model.js +79 -0
  199. package/src/api/cyberia-action/cyberia-action.router.js +21 -0
  200. package/src/api/cyberia-action/cyberia-action.service.js +58 -0
  201. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +27 -0
  202. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +100 -0
  203. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +70 -0
  204. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
  205. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +8 -0
  206. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  207. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +20 -0
  208. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +57 -0
  209. package/src/api/cyberia-entity/cyberia-entity.controller.js +6 -0
  210. package/src/api/cyberia-entity/cyberia-entity.model.js +25 -0
  211. package/src/api/cyberia-entity/cyberia-entity.router.js +17 -0
  212. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  213. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +6 -0
  214. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
  215. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -0
  216. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
  217. package/src/api/cyberia-instance/cyberia-fallback-world.js +337 -0
  218. package/src/api/cyberia-instance/cyberia-instance-boot.service.js +57 -0
  219. package/src/api/cyberia-instance/cyberia-instance-map.service.js +583 -0
  220. package/src/api/cyberia-instance/cyberia-instance.controller.js +42 -0
  221. package/src/api/cyberia-instance/cyberia-instance.model.js +117 -0
  222. package/src/api/cyberia-instance/cyberia-instance.router.js +75 -0
  223. package/src/api/cyberia-instance/cyberia-instance.service.js +190 -0
  224. package/src/api/cyberia-instance/cyberia-portal-connector.js +260 -0
  225. package/src/api/cyberia-instance/cyberia-world-generator.js +618 -0
  226. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +6 -0
  227. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +200 -0
  228. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +17 -0
  229. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +46 -0
  230. package/src/api/cyberia-map/cyberia-map.controller.js +6 -0
  231. package/src/api/cyberia-map/cyberia-map.model.js +33 -0
  232. package/src/api/cyberia-map/cyberia-map.router.js +19 -0
  233. package/src/api/cyberia-map/cyberia-map.service.js +76 -0
  234. package/src/api/cyberia-quest/cyberia-quest.controller.js +9 -0
  235. package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
  236. package/src/api/cyberia-quest/cyberia-quest.router.js +26 -0
  237. package/src/api/cyberia-quest/cyberia-quest.service.js +97 -0
  238. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +6 -0
  239. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +49 -0
  240. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +21 -0
  241. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
  242. package/src/api/cyberia-saga/cyberia-saga.controller.js +6 -0
  243. package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
  244. package/src/api/cyberia-saga/cyberia-saga.router.js +17 -0
  245. package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
  246. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +1076 -0
  247. package/src/api/cyberia-skill/cyberia-skill.controller.js +6 -0
  248. package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
  249. package/src/api/cyberia-skill/cyberia-skill.router.js +17 -0
  250. package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
  251. package/src/api/default/default.controller.js +6 -0
  252. package/src/api/default/default.model.js +20 -0
  253. package/src/api/default/default.router.js +21 -0
  254. package/src/api/default/default.service.js +42 -0
  255. package/src/api/document/document.controller.js +9 -0
  256. package/src/api/document/document.model.js +95 -0
  257. package/src/api/document/document.router.js +32 -0
  258. package/src/api/document/document.service.js +633 -0
  259. package/src/api/file/file.controller.js +15 -0
  260. package/src/api/file/file.model.js +127 -0
  261. package/src/api/file/file.ref.json +39 -0
  262. package/src/api/file/file.router.js +36 -0
  263. package/src/api/file/file.service.js +499 -0
  264. package/src/api/instance/instance.controller.js +8 -0
  265. package/src/api/instance/instance.model.js +34 -0
  266. package/src/api/instance/instance.router.js +27 -0
  267. package/src/api/instance/instance.service.js +75 -0
  268. package/src/api/ipfs/ipfs.controller.js +8 -0
  269. package/src/api/ipfs/ipfs.model.js +77 -0
  270. package/src/api/ipfs/ipfs.router.js +26 -0
  271. package/src/api/ipfs/ipfs.service.js +145 -0
  272. package/src/api/object-layer/object-layer.controller.js +18 -0
  273. package/src/api/object-layer/object-layer.model.js +200 -0
  274. package/src/api/object-layer/object-layer.router.js +729 -0
  275. package/src/api/object-layer/object-layer.service.js +894 -0
  276. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +6 -0
  277. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +93 -0
  278. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +17 -0
  279. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +46 -0
  280. package/src/api/test/test.controller.js +14 -0
  281. package/src/api/test/test.model.js +14 -0
  282. package/src/api/test/test.router.js +23 -0
  283. package/src/api/test/test.service.js +35 -0
  284. package/src/api/types.js +24 -0
  285. package/src/api/user/guest.service.js +100 -0
  286. package/src/api/user/postman_collection.json +216 -0
  287. package/src/api/user/user.build.js +16 -0
  288. package/src/api/user/user.controller.js +35 -0
  289. package/src/api/user/user.model.js +142 -0
  290. package/src/api/user/user.router.js +367 -0
  291. package/src/api/user/user.service.js +582 -0
  292. package/src/api.js +23 -0
  293. package/src/cli/baremetal.js +3947 -0
  294. package/src/cli/cloud-init.js +673 -0
  295. package/src/cli/cluster.js +1395 -0
  296. package/src/cli/db.js +1533 -0
  297. package/src/cli/deploy.js +1542 -0
  298. package/src/cli/docker-compose.js +802 -0
  299. package/src/cli/env.js +177 -0
  300. package/src/cli/fs.js +380 -0
  301. package/src/cli/image.js +389 -0
  302. package/src/cli/index.js +1059 -0
  303. package/src/cli/ipfs.js +182 -0
  304. package/src/cli/kickstart.js +271 -0
  305. package/src/cli/kubectl.js +214 -0
  306. package/src/cli/lxd.js +1232 -0
  307. package/src/cli/monitor.js +659 -0
  308. package/src/cli/release.js +595 -0
  309. package/src/cli/repository.js +1969 -0
  310. package/src/cli/run.js +3326 -0
  311. package/src/cli/secrets.js +151 -0
  312. package/src/cli/ssh.js +975 -0
  313. package/src/cli/static.js +653 -0
  314. package/src/cli/system.js +332 -0
  315. package/src/cli/test.js +176 -0
  316. package/src/client/Cryptokoyn.index.js +30 -0
  317. package/src/client/CyberiaPortal.index.js +31 -0
  318. package/src/client/Default.index.js +77 -0
  319. package/src/client/Itemledger.index.js +35 -0
  320. package/src/client/Underpost.index.js +31 -0
  321. package/src/client/components/core/404.js +20 -0
  322. package/src/client/components/core/500.js +20 -0
  323. package/src/client/components/core/Account.js +630 -0
  324. package/src/client/components/core/AgGrid.js +238 -0
  325. package/src/client/components/core/Alert.js +76 -0
  326. package/src/client/components/core/AppStore.js +69 -0
  327. package/src/client/components/core/Auth.js +353 -0
  328. package/src/client/components/core/Badge.js +26 -0
  329. package/src/client/components/core/Blockchain.js +41 -0
  330. package/src/client/components/core/Blog.js +9 -0
  331. package/src/client/components/core/BtnIcon.js +109 -0
  332. package/src/client/components/core/CalendarCore.js +437 -0
  333. package/src/client/components/core/Chat.js +62 -0
  334. package/src/client/components/core/ClientEvents.js +163 -0
  335. package/src/client/components/core/ColorPaletteElement.js +309 -0
  336. package/src/client/components/core/CommonJs.js +1014 -0
  337. package/src/client/components/core/Content.js +349 -0
  338. package/src/client/components/core/Css.js +1134 -0
  339. package/src/client/components/core/CssCore.js +881 -0
  340. package/src/client/components/core/D3Chart.js +44 -0
  341. package/src/client/components/core/Docs.js +478 -0
  342. package/src/client/components/core/DropDown.js +321 -0
  343. package/src/client/components/core/EventBus.js +96 -0
  344. package/src/client/components/core/EventsUI.js +130 -0
  345. package/src/client/components/core/FileExplorer.js +1406 -0
  346. package/src/client/components/core/FullScreen.js +201 -0
  347. package/src/client/components/core/Input.js +537 -0
  348. package/src/client/components/core/Keyboard.js +80 -0
  349. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  350. package/src/client/components/core/LoadingAnimation.js +152 -0
  351. package/src/client/components/core/LogIn.js +212 -0
  352. package/src/client/components/core/LogOut.js +72 -0
  353. package/src/client/components/core/Logger.js +22 -0
  354. package/src/client/components/core/Modal.js +2956 -0
  355. package/src/client/components/core/NotificationManager.js +80 -0
  356. package/src/client/components/core/Pagination.js +252 -0
  357. package/src/client/components/core/Panel.js +1308 -0
  358. package/src/client/components/core/PanelForm.js +915 -0
  359. package/src/client/components/core/Polyhedron.js +947 -0
  360. package/src/client/components/core/PublicProfile.js +895 -0
  361. package/src/client/components/core/Recover.js +211 -0
  362. package/src/client/components/core/Responsive.js +146 -0
  363. package/src/client/components/core/RichText.js +33 -0
  364. package/src/client/components/core/Router.js +523 -0
  365. package/src/client/components/core/Scroll.js +76 -0
  366. package/src/client/components/core/SearchBox.js +1007 -0
  367. package/src/client/components/core/SignUp.js +153 -0
  368. package/src/client/components/core/SocketIo.js +163 -0
  369. package/src/client/components/core/SocketIoHandler.js +75 -0
  370. package/src/client/components/core/Stream.js +161 -0
  371. package/src/client/components/core/ToggleSwitch.js +89 -0
  372. package/src/client/components/core/ToolTip.js +78 -0
  373. package/src/client/components/core/Translate.js +694 -0
  374. package/src/client/components/core/Validator.js +140 -0
  375. package/src/client/components/core/VanillaJs.js +485 -0
  376. package/src/client/components/core/Wallet.js +95 -0
  377. package/src/client/components/core/WebComponent.js +44 -0
  378. package/src/client/components/core/Webhook.js +58 -0
  379. package/src/client/components/core/Worker.js +313 -0
  380. package/src/client/components/core/windowGetDimensions.js +269 -0
  381. package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +352 -0
  382. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  383. package/src/client/components/cryptokoyn/CssCryptokoyn.js +212 -0
  384. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +16 -0
  385. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +12 -0
  386. package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
  387. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +16 -0
  388. package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +11 -0
  389. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +6 -0
  390. package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
  391. package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
  392. package/src/client/components/cyberia/InstanceEngineCyberia.js +1088 -0
  393. package/src/client/components/cyberia/InstanceSelectionView.js +900 -0
  394. package/src/client/components/cyberia/MapEngineCyberia.js +1976 -0
  395. package/src/client/components/cyberia/ObjectLayerEngine.js +1540 -0
  396. package/src/client/components/cyberia/ObjectLayerEngineModal.js +2420 -0
  397. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +1522 -0
  398. package/src/client/components/cyberia/SharedDefaultsCyberia.js +528 -0
  399. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +772 -0
  400. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  401. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +254 -0
  402. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +21 -0
  403. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +16 -0
  404. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +518 -0
  405. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +72 -0
  406. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +16 -0
  407. package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +13 -0
  408. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +6 -0
  409. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +48 -0
  410. package/src/client/components/default/AppShellDefault.js +765 -0
  411. package/src/client/components/default/AppStoreDefault.js +5 -0
  412. package/src/client/components/default/CommonDefault.js +29 -0
  413. package/src/client/components/default/CssDefault.js +27 -0
  414. package/src/client/components/default/LogInDefault.js +16 -0
  415. package/src/client/components/default/LogOutDefault.js +12 -0
  416. package/src/client/components/default/RouterDefault.js +47 -0
  417. package/src/client/components/default/SettingsDefault.js +16 -0
  418. package/src/client/components/default/SignUpDefault.js +11 -0
  419. package/src/client/components/default/SocketIoDefault.js +6 -0
  420. package/src/client/components/default/TranslateDefault.js +7 -0
  421. package/src/client/components/itemledger/AppShellItemledger.js +353 -0
  422. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  423. package/src/client/components/itemledger/CssItemledger.js +212 -0
  424. package/src/client/components/itemledger/LogInItemledger.js +17 -0
  425. package/src/client/components/itemledger/LogOutItemledger.js +12 -0
  426. package/src/client/components/itemledger/RouterItemledger.js +38 -0
  427. package/src/client/components/itemledger/SettingsItemledger.js +16 -0
  428. package/src/client/components/itemledger/SignUpItemledger.js +11 -0
  429. package/src/client/components/itemledger/SocketIoItemledger.js +6 -0
  430. package/src/client/components/itemledger/TranslateItemledger.js +7 -0
  431. package/src/client/components/underpost/AppShellUnderpost.js +691 -0
  432. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  433. package/src/client/components/underpost/CssUnderpost.js +281 -0
  434. package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +879 -0
  435. package/src/client/components/underpost/DocumentSearchProvider.js +448 -0
  436. package/src/client/components/underpost/LabGalleryUnderpost.js +79 -0
  437. package/src/client/components/underpost/LogInUnderpost.js +25 -0
  438. package/src/client/components/underpost/LogOutUnderpost.js +17 -0
  439. package/src/client/components/underpost/RouterUnderpost.js +45 -0
  440. package/src/client/components/underpost/SettingsUnderpost.js +16 -0
  441. package/src/client/components/underpost/SignUpUnderpost.js +11 -0
  442. package/src/client/components/underpost/SocketIoUnderpost.js +6 -0
  443. package/src/client/components/underpost/TranslateUnderpost.js +10 -0
  444. package/src/client/public/cryptokoyn/assets/banner/koyn-social.png +0 -0
  445. package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
  446. package/src/client/public/cryptokoyn/assets/logo/cryptokoyn.png +0 -0
  447. package/src/client/public/cryptokoyn/assets/splash/android-chrome-144x144.png +0 -0
  448. package/src/client/public/cryptokoyn/assets/splash/android-chrome-192x192.png +0 -0
  449. package/src/client/public/cryptokoyn/assets/splash/android-chrome-256x256.png +0 -0
  450. package/src/client/public/cryptokoyn/assets/splash/android-chrome-36x36.png +0 -0
  451. package/src/client/public/cryptokoyn/assets/splash/android-chrome-48x48.png +0 -0
  452. package/src/client/public/cryptokoyn/assets/splash/android-chrome-72x72.png +0 -0
  453. package/src/client/public/cryptokoyn/assets/splash/android-chrome-96x96.png +0 -0
  454. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114-precomposed.png +0 -0
  455. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114.png +0 -0
  456. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120-precomposed.png +0 -0
  457. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120.png +0 -0
  458. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144-precomposed.png +0 -0
  459. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144.png +0 -0
  460. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152-precomposed.png +0 -0
  461. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152.png +0 -0
  462. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180-precomposed.png +0 -0
  463. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180.png +0 -0
  464. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57-precomposed.png +0 -0
  465. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57.png +0 -0
  466. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60-precomposed.png +0 -0
  467. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60.png +0 -0
  468. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72-precomposed.png +0 -0
  469. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72.png +0 -0
  470. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76-precomposed.png +0 -0
  471. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76.png +0 -0
  472. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-precomposed.png +0 -0
  473. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon.png +0 -0
  474. package/src/client/public/cryptokoyn/assets/splash/favicon-16x16.png +0 -0
  475. package/src/client/public/cryptokoyn/assets/splash/favicon-32x32.png +0 -0
  476. package/src/client/public/cryptokoyn/assets/splash/mstile-144x144.png +0 -0
  477. package/src/client/public/cryptokoyn/assets/splash/mstile-150x150.png +0 -0
  478. package/src/client/public/cryptokoyn/assets/splash/mstile-310x150.png +0 -0
  479. package/src/client/public/cryptokoyn/assets/splash/mstile-310x310.png +0 -0
  480. package/src/client/public/cryptokoyn/assets/splash/mstile-70x70.png +0 -0
  481. package/src/client/public/cryptokoyn/assets/splash/safari-pinned-tab.svg +42 -0
  482. package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
  483. package/src/client/public/cryptokoyn/favicon.ico +0 -0
  484. package/src/client/public/cryptokoyn/microdata.json +85 -0
  485. package/src/client/public/cryptokoyn/site.webmanifest +57 -0
  486. package/src/client/public/cryptokoyn/sitemap +148 -0
  487. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +307 -0
  488. package/src/client/public/cyberia-docs/ARCHITECTURE.md +309 -0
  489. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +204 -0
  490. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +341 -0
  491. package/src/client/public/cyberia-docs/CYBERIA-LORE.md +84 -0
  492. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
  493. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +294 -0
  494. package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
  495. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +242 -0
  496. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
  497. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +309 -0
  498. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +228 -0
  499. package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
  500. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
  501. package/src/client/public/cyberia-docs/WHITE-PAPER.md +740 -0
  502. package/src/client/public/default/android-chrome-144x144.png +0 -0
  503. package/src/client/public/default/android-chrome-192x192.png +0 -0
  504. package/src/client/public/default/android-chrome-256x256.png +0 -0
  505. package/src/client/public/default/android-chrome-36x36.png +0 -0
  506. package/src/client/public/default/android-chrome-384x384.png +0 -0
  507. package/src/client/public/default/android-chrome-48x48.png +0 -0
  508. package/src/client/public/default/android-chrome-512x512.png +0 -0
  509. package/src/client/public/default/android-chrome-72x72.png +0 -0
  510. package/src/client/public/default/android-chrome-96x96.png +0 -0
  511. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  512. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  513. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  514. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  515. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  516. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  517. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  518. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  519. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  520. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  521. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  522. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  523. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  524. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  525. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  526. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  527. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  528. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  529. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  530. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  531. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  532. package/src/client/public/default/apple-touch-icon.png +0 -0
  533. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  534. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  535. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  536. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  537. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  538. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  539. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  540. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  541. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  542. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  543. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  544. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  545. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  546. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  547. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  548. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  549. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  550. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  551. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  552. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  553. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  554. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  555. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  556. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  557. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  558. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  559. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  560. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  561. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  562. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  563. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  564. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  565. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  566. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  567. package/src/client/public/default/assets/background/dark.jpg +0 -0
  568. package/src/client/public/default/assets/background/dark.svg +557 -0
  569. package/src/client/public/default/assets/background/white.jpg +0 -0
  570. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  571. package/src/client/public/default/assets/background/white0.jpg +0 -0
  572. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  573. package/src/client/public/default/assets/logo/underpost.gif +0 -0
  574. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  575. package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
  576. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  577. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  578. package/src/client/public/default/browserconfig.xml +12 -0
  579. package/src/client/public/default/favicon-16x16.png +0 -0
  580. package/src/client/public/default/favicon-32x32.png +0 -0
  581. package/src/client/public/default/favicon-48x48.png +0 -0
  582. package/src/client/public/default/favicon.ico +0 -0
  583. package/src/client/public/default/manifest.webmanifest +69 -0
  584. package/src/client/public/default/mstile-144x144.png +0 -0
  585. package/src/client/public/default/mstile-150x150.png +0 -0
  586. package/src/client/public/default/mstile-310x150.png +0 -0
  587. package/src/client/public/default/mstile-310x310.png +0 -0
  588. package/src/client/public/default/mstile-70x70.png +0 -0
  589. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  590. package/src/client/public/default/site.webmanifest +69 -0
  591. package/src/client/public/default/sitemap +148 -0
  592. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  593. package/src/client/public/default/yandex-browser-manifest.json +9 -0
  594. package/src/client/public/doc/favicon.ico +0 -0
  595. package/src/client/public/doc/sitemap +148 -0
  596. package/src/client/public/itemledger/android-chrome-144x144.png +0 -0
  597. package/src/client/public/itemledger/android-chrome-192x192.png +0 -0
  598. package/src/client/public/itemledger/android-chrome-256x256.png +0 -0
  599. package/src/client/public/itemledger/android-chrome-36x36.png +0 -0
  600. package/src/client/public/itemledger/android-chrome-384x384.png +0 -0
  601. package/src/client/public/itemledger/android-chrome-48x48.png +0 -0
  602. package/src/client/public/itemledger/android-chrome-512x512.png +0 -0
  603. package/src/client/public/itemledger/android-chrome-72x72.png +0 -0
  604. package/src/client/public/itemledger/android-chrome-96x96.png +0 -0
  605. package/src/client/public/itemledger/apple-touch-icon-1024x1024.png +0 -0
  606. package/src/client/public/itemledger/apple-touch-icon-114x114.png +0 -0
  607. package/src/client/public/itemledger/apple-touch-icon-120x120.png +0 -0
  608. package/src/client/public/itemledger/apple-touch-icon-144x144.png +0 -0
  609. package/src/client/public/itemledger/apple-touch-icon-152x152.png +0 -0
  610. package/src/client/public/itemledger/apple-touch-icon-167x167.png +0 -0
  611. package/src/client/public/itemledger/apple-touch-icon-180x180.png +0 -0
  612. package/src/client/public/itemledger/apple-touch-icon-57x57.png +0 -0
  613. package/src/client/public/itemledger/apple-touch-icon-60x60.png +0 -0
  614. package/src/client/public/itemledger/apple-touch-icon-72x72.png +0 -0
  615. package/src/client/public/itemledger/apple-touch-icon-76x76.png +0 -0
  616. package/src/client/public/itemledger/apple-touch-icon-precomposed.png +0 -0
  617. package/src/client/public/itemledger/apple-touch-icon.png +0 -0
  618. package/src/client/public/itemledger/apple-touch-startup-image-1125x2436.png +0 -0
  619. package/src/client/public/itemledger/apple-touch-startup-image-1136x640.png +0 -0
  620. package/src/client/public/itemledger/apple-touch-startup-image-1170x2532.png +0 -0
  621. package/src/client/public/itemledger/apple-touch-startup-image-1179x2556.png +0 -0
  622. package/src/client/public/itemledger/apple-touch-startup-image-1242x2208.png +0 -0
  623. package/src/client/public/itemledger/apple-touch-startup-image-1242x2688.png +0 -0
  624. package/src/client/public/itemledger/apple-touch-startup-image-1284x2778.png +0 -0
  625. package/src/client/public/itemledger/apple-touch-startup-image-1290x2796.png +0 -0
  626. package/src/client/public/itemledger/apple-touch-startup-image-1334x750.png +0 -0
  627. package/src/client/public/itemledger/apple-touch-startup-image-1488x2266.png +0 -0
  628. package/src/client/public/itemledger/apple-touch-startup-image-1536x2048.png +0 -0
  629. package/src/client/public/itemledger/apple-touch-startup-image-1620x2160.png +0 -0
  630. package/src/client/public/itemledger/apple-touch-startup-image-1640x2160.png +0 -0
  631. package/src/client/public/itemledger/apple-touch-startup-image-1668x2224.png +0 -0
  632. package/src/client/public/itemledger/apple-touch-startup-image-1668x2388.png +0 -0
  633. package/src/client/public/itemledger/apple-touch-startup-image-1792x828.png +0 -0
  634. package/src/client/public/itemledger/apple-touch-startup-image-2048x1536.png +0 -0
  635. package/src/client/public/itemledger/apple-touch-startup-image-2048x2732.png +0 -0
  636. package/src/client/public/itemledger/apple-touch-startup-image-2160x1620.png +0 -0
  637. package/src/client/public/itemledger/apple-touch-startup-image-2160x1640.png +0 -0
  638. package/src/client/public/itemledger/apple-touch-startup-image-2208x1242.png +0 -0
  639. package/src/client/public/itemledger/apple-touch-startup-image-2224x1668.png +0 -0
  640. package/src/client/public/itemledger/apple-touch-startup-image-2266x1488.png +0 -0
  641. package/src/client/public/itemledger/apple-touch-startup-image-2388x1668.png +0 -0
  642. package/src/client/public/itemledger/apple-touch-startup-image-2436x1125.png +0 -0
  643. package/src/client/public/itemledger/apple-touch-startup-image-2532x1170.png +0 -0
  644. package/src/client/public/itemledger/apple-touch-startup-image-2556x1179.png +0 -0
  645. package/src/client/public/itemledger/apple-touch-startup-image-2688x1242.png +0 -0
  646. package/src/client/public/itemledger/apple-touch-startup-image-2732x2048.png +0 -0
  647. package/src/client/public/itemledger/apple-touch-startup-image-2778x1284.png +0 -0
  648. package/src/client/public/itemledger/apple-touch-startup-image-2796x1290.png +0 -0
  649. package/src/client/public/itemledger/apple-touch-startup-image-640x1136.png +0 -0
  650. package/src/client/public/itemledger/apple-touch-startup-image-750x1334.png +0 -0
  651. package/src/client/public/itemledger/apple-touch-startup-image-828x1792.png +0 -0
  652. package/src/client/public/itemledger/assets/logo/base-icon.png +0 -0
  653. package/src/client/public/itemledger/browserconfig.xml +12 -0
  654. package/src/client/public/itemledger/favicon-16x16.png +0 -0
  655. package/src/client/public/itemledger/favicon-32x32.png +0 -0
  656. package/src/client/public/itemledger/favicon-48x48.png +0 -0
  657. package/src/client/public/itemledger/favicon.ico +0 -0
  658. package/src/client/public/itemledger/manifest.webmanifest +69 -0
  659. package/src/client/public/itemledger/microdata.json +71 -0
  660. package/src/client/public/itemledger/mstile-144x144.png +0 -0
  661. package/src/client/public/itemledger/mstile-150x150.png +0 -0
  662. package/src/client/public/itemledger/mstile-310x150.png +0 -0
  663. package/src/client/public/itemledger/mstile-310x310.png +0 -0
  664. package/src/client/public/itemledger/mstile-70x70.png +0 -0
  665. package/src/client/public/itemledger/sitemap +148 -0
  666. package/src/client/public/itemledger/yandex-browser-50x50.png +0 -0
  667. package/src/client/public/itemledger/yandex-browser-manifest.json +9 -0
  668. package/src/client/public/test/favicon.ico +0 -0
  669. package/src/client/public/test/sitemap +148 -0
  670. package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +137 -0
  671. package/src/client/services/core/core.service.js +309 -0
  672. package/src/client/services/crypto/crypto.service.js +68 -0
  673. package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
  674. package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
  675. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +99 -0
  676. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  677. package/src/client/services/cyberia-entity/cyberia-entity.service.js +99 -0
  678. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
  679. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  680. package/src/client/services/cyberia-instance/cyberia-instance.service.js +137 -0
  681. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +99 -0
  682. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  683. package/src/client/services/cyberia-map/cyberia-map.service.js +120 -0
  684. package/src/client/services/cyberia-quest/cyberia-quest.service.js +99 -0
  685. package/src/client/services/cyberia-quest-progress/cyberia-quest-progress.service.js +99 -0
  686. package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
  687. package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
  688. package/src/client/services/default/default.management.js +1015 -0
  689. package/src/client/services/default/default.service.js +99 -0
  690. package/src/client/services/document/document.service.js +134 -0
  691. package/src/client/services/file/file.service.js +94 -0
  692. package/src/client/services/instance/instance.management.js +78 -0
  693. package/src/client/services/instance/instance.service.js +107 -0
  694. package/src/client/services/ipfs/ipfs.service.js +118 -0
  695. package/src/client/services/object-layer/object-layer.management.js +414 -0
  696. package/src/client/services/object-layer/object-layer.service.js +208 -0
  697. package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +99 -0
  698. package/src/client/services/test/test.service.js +68 -0
  699. package/src/client/services/user/guest.service.js +86 -0
  700. package/src/client/services/user/user.management.js +51 -0
  701. package/src/client/services/user/user.service.js +106 -0
  702. package/src/client/ssr/RootDocument.js +237 -0
  703. package/src/client/ssr/body/404.js +77 -0
  704. package/src/client/ssr/body/500.js +76 -0
  705. package/src/client/ssr/body/CacheControl.js +117 -0
  706. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +97 -0
  707. package/src/client/ssr/body/DefaultSplashScreen.js +97 -0
  708. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  709. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +90 -0
  710. package/src/client/ssr/head/CryptokoynScripts.js +4 -0
  711. package/src/client/ssr/head/Css.js +241 -0
  712. package/src/client/ssr/head/CyberiaPortalScripts.js +6 -0
  713. package/src/client/ssr/head/DefaultScripts.js +6 -0
  714. package/src/client/ssr/head/ItemledgerScripts.js +4 -0
  715. package/src/client/ssr/head/Microdata.js +11 -0
  716. package/src/client/ssr/head/Production.js +1 -0
  717. package/src/client/ssr/head/Pwa.js +146 -0
  718. package/src/client/ssr/head/PwaItemledger.js +197 -0
  719. package/src/client/ssr/head/Seo.js +15 -0
  720. package/src/client/ssr/head/UnderpostScripts.js +6 -0
  721. package/src/client/ssr/mailer/DefaultRecoverEmail.js +20 -0
  722. package/src/client/ssr/mailer/DefaultVerifyEmail.js +16 -0
  723. package/src/client/ssr/views/Cyberia404.js +328 -0
  724. package/src/client/ssr/views/CyberiaServerMetrics.js +1070 -0
  725. package/src/client/ssr/views/Maintenance.js +64 -0
  726. package/src/client/ssr/views/NoNetworkConnection.js +68 -0
  727. package/src/client/ssr/views/Test.js +199 -0
  728. package/src/client/sw/core.sw.js +274 -0
  729. package/src/client-builder/client-build-docs.js +493 -0
  730. package/src/client-builder/client-build-live.js +100 -0
  731. package/src/client-builder/client-build.js +1092 -0
  732. package/src/client-builder/client-dev-server.js +88 -0
  733. package/src/client-builder/client-formatted.js +170 -0
  734. package/src/client-builder/client-icons.js +108 -0
  735. package/src/client-builder/ssr.js +124 -0
  736. package/src/client.build.js +17 -0
  737. package/src/client.dev.js +21 -0
  738. package/src/db/DataBaseProvider.js +213 -0
  739. package/src/db/mariadb/MariaDB.js +72 -0
  740. package/src/db/mongo/MongoBootstrap.js +657 -0
  741. package/src/db/mongo/MongooseDB.js +199 -0
  742. package/src/grpc/cyberia/grpc-server.js +162 -0
  743. package/src/index.js +357 -0
  744. package/src/mailer/EmailRender.js +116 -0
  745. package/src/mailer/MailerProvider.js +213 -0
  746. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +413 -0
  747. package/src/projects/cyberia/besu-genesis-generator.js +1630 -0
  748. package/src/projects/cyberia/catalog-cyberia.js +90 -0
  749. package/src/projects/cyberia/gemini-client.js +175 -0
  750. package/src/projects/cyberia/generate-saga.js +2137 -0
  751. package/src/projects/cyberia/hot-reload-trigger.js +163 -0
  752. package/src/projects/cyberia/instance-data.js +719 -0
  753. package/src/projects/cyberia/ipfs-client.js +599 -0
  754. package/src/projects/cyberia/map-preview-generator.js +304 -0
  755. package/src/projects/cyberia/object-layer.js +978 -0
  756. package/src/projects/cyberia/semantic-layer-generator-floor.js +319 -0
  757. package/src/projects/cyberia/semantic-layer-generator-resource.js +259 -0
  758. package/src/projects/cyberia/semantic-layer-generator-skin.js +1164 -0
  759. package/src/projects/cyberia/semantic-layer-generator.js +753 -0
  760. package/src/projects/cyberia/shape-generator.js +1060 -0
  761. package/src/projects/underpost/catalog-underpost.js +60 -0
  762. package/src/proxy.js +19 -0
  763. package/src/runtime/cyberia-client/Dockerfile +49 -0
  764. package/src/runtime/cyberia-client/Dockerfile.dev +75 -0
  765. package/src/runtime/cyberia-server/Dockerfile +43 -0
  766. package/src/runtime/cyberia-server/Dockerfile.dev +56 -0
  767. package/src/runtime/engine-cyberia/Dockerfile +161 -0
  768. package/src/runtime/engine-cyberia/Dockerfile.dev +161 -0
  769. package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
  770. package/src/runtime/engine-cyberia/compose.env +144 -0
  771. package/src/runtime/engine-cyberia/docker-compose.yml +434 -0
  772. package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
  773. package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
  774. package/src/runtime/engine-cyberia/nginx.conf +128 -0
  775. package/src/runtime/express/Dockerfile +45 -0
  776. package/src/runtime/express/Express.js +285 -0
  777. package/src/runtime/lampp/Dockerfile +62 -0
  778. package/src/runtime/lampp/Lampp.js +359 -0
  779. package/src/runtime/nginx/Nginx.js +250 -0
  780. package/src/runtime/wp/Dockerfile +62 -0
  781. package/src/runtime/wp/Wp.js +642 -0
  782. package/src/server/auth.js +713 -0
  783. package/src/server/backup.js +102 -0
  784. package/src/server/catalog.js +72 -0
  785. package/src/server/conf.js +2316 -0
  786. package/src/server/cron.js +476 -0
  787. package/src/server/crypto.js +210 -0
  788. package/src/server/data-query.js +461 -0
  789. package/src/server/dns.js +553 -0
  790. package/src/server/downloader.js +73 -0
  791. package/src/server/logger.js +229 -0
  792. package/src/server/middlewares.js +152 -0
  793. package/src/server/peer.js +75 -0
  794. package/src/server/process.js +299 -0
  795. package/src/server/proxy.js +139 -0
  796. package/src/server/runtime-status.js +252 -0
  797. package/src/server/runtime.js +183 -0
  798. package/src/server/start.js +272 -0
  799. package/src/server/tls.js +254 -0
  800. package/src/server/valkey.js +201 -0
  801. package/src/server.js +25 -0
  802. package/src/ws/IoInterface.js +129 -0
  803. package/src/ws/IoServer.js +68 -0
  804. package/src/ws/core/channels/core.ws.chat.js +68 -0
  805. package/src/ws/core/channels/core.ws.mailer.js +116 -0
  806. package/src/ws/core/channels/core.ws.stream.js +90 -0
  807. package/src/ws/core/core.ws.connection.js +41 -0
  808. package/src/ws/core/core.ws.emit.js +37 -0
  809. package/src/ws/core/core.ws.server.js +44 -0
  810. package/src/ws/default/channels/default.ws.main.js +57 -0
  811. package/src/ws/default/default.ws.connection.js +35 -0
  812. package/src/ws/default/default.ws.emit.js +14 -0
  813. package/src/ws/default/default.ws.server.js +39 -0
  814. package/test/api.test.js +53 -0
  815. package/test/crypto.test.js +117 -0
  816. package/test/cyberia-instance-conf-defaults.test.js +140 -0
  817. package/test/deploy-monitor.test.js +267 -0
  818. package/test/shape-generator.test.js +463 -0
  819. package/tsconfig.docs.json +15 -0
  820. package/typedoc.dd-cyberia.json +31 -0
  821. package/typedoc.json +31 -0
@@ -0,0 +1,1867 @@
1
+ import { BtnIcon } from '../core/BtnIcon.js';
2
+ import { Input } from '../core/Input.js';
3
+ import { commonModeratorGuard } from '../core/CommonJs.js';
4
+ import { htmls, s } from '../core/VanillaJs.js';
5
+ import { NotificationManager } from '../core/NotificationManager.js';
6
+ import { Translate } from '../core/Translate.js';
7
+ import { darkTheme, dynamicCol, ThemeEvents } from '../core/Css.js';
8
+ import { DropDown } from '../core/DropDown.js';
9
+ import { getProxyPath } from '../core/Router.js';
10
+ import { ObjectLayerService } from '../../services/object-layer/object-layer.service.js';
11
+ import { CyberiaMapService } from '../../services/cyberia-map/cyberia-map.service.js';
12
+ import { CyberiaQuestService } from '../../services/cyberia-quest/cyberia-quest.service.js';
13
+ import { CyberiaActionService } from '../../services/cyberia-action/cyberia-action.service.js';
14
+ import { CyberiaDialogueService } from '../../services/cyberia-dialogue/cyberia-dialogue.service.js';
15
+ import { CyberiaSkillService } from '../../services/cyberia-skill/cyberia-skill.service.js';
16
+ import { QUEST_STEPS_TYPES, getDefaultCyberiaItemById, SKILL_LOGIC_IDS } from './SharedDefaultsCyberia.js';
17
+
18
+ const textFilter = (filter) => ({ filterType: 'text', type: 'equals', filter });
19
+ // DropDown invokes optionData.onClick on selection, so every option (including
20
+ // serviceProvider results) must carry one.
21
+ const dropdownOption = (value) => ({ value, display: value, data: value, onClick: () => {} });
22
+ const groupBorder = () => (darkTheme ? '#3a3a3a' : '#d4d4d4');
23
+ const subtleBorder = () => (darkTheme ? '#444' : '#e0e0e0');
24
+
25
+ // ActionEngineCyberia — a map-driven engine that unifies quest, action and
26
+ // dialogue authoring around a shared, visual map surface. It is intentionally
27
+ // decoupled from MapEngineCyberia: the only integration points are the public
28
+ // data services (maps, quests, actions, dialogues, object-layer item ids) and
29
+ // the same object-layer texture-loading convention. The engine reads a map
30
+ // purely as a placement backdrop and never mutates map documents. CRUD is
31
+ // surfaced as per-entity card lists (mirroring the map-engine entity list),
32
+ // not management grids.
33
+ class ActionEngineCyberia {
34
+ static mode = 'quest';
35
+ static mapCode = '';
36
+ static mapDoc = null;
37
+ static imageCache = {};
38
+ static assignments = { quests: [], actions: [] };
39
+
40
+ static questListCache = [];
41
+ static actionListCache = [];
42
+ static dialogueListCache = [];
43
+ static skillListCache = [];
44
+
45
+ static currentQuestId = null;
46
+ static currentActionId = null;
47
+ static currentDialogueId = null;
48
+ static currentSkillId = null;
49
+
50
+ static questSteps = [];
51
+ static questRewards = [];
52
+ static actionQuestDialogues = [];
53
+ static loadedActionPayloadExtras = {};
54
+ // Editable `skills[]` for the loaded CyberiaSkill (logicEventIds are derived
55
+ // from these on save, mirroring DefaultSkillConfig).
56
+ static skillDefs = [];
57
+
58
+ static ids = {
59
+ mapDropdown: 'action-engine-map-dropdown',
60
+ objectiveItemPicker: 'action-engine-objective-item-picker',
61
+ rewardItemPicker: 'action-engine-reward-item-picker',
62
+ objectiveType: 'action-engine-objective-type',
63
+ actionDialogPicker: 'action-engine-action-dialog-picker',
64
+ actionQuestDialogPicker: 'action-engine-action-quest-dialog-picker',
65
+ skillTriggerItemPicker: 'action-engine-skill-trigger-item-picker',
66
+ skillSummonedItemPicker: 'action-engine-skill-summoned-item-picker',
67
+ };
68
+
69
+ // ── Searchable dropdown helpers ─────────────────────────────────────────
70
+ static getSingleDropdownValue(id) {
71
+ const value = DropDown.Tokens[id]?.value;
72
+ return typeof value === 'string' ? value.trim() : '';
73
+ }
74
+
75
+ static setSingleDropdownValue(id, value) {
76
+ if (!DropDown.Tokens[id]) return;
77
+ const v = value || '';
78
+ DropDown.Tokens[id].value = v;
79
+ if (s(`.${id}`)) s(`.${id}`).value = v;
80
+ htmls(`.dropdown-current-${id}`, v || '');
81
+ }
82
+
83
+ // itemId references the item/object-layer collection, so the picker is a fast
84
+ // server-backed autocomplete instead of a free-text input.
85
+ static async buildItemIdDropdown(id, label) {
86
+ return await DropDown.instance({
87
+ id,
88
+ label,
89
+ data: [],
90
+ containerClass: 'inl',
91
+ serviceProvider: async (q) => {
92
+ const result = await ObjectLayerService.searchItemIds({ q });
93
+ if (result.status === 'success' && result.data?.itemIds) {
94
+ return result.data.itemIds.map((itemId) => dropdownOption(itemId));
95
+ }
96
+ return [];
97
+ },
98
+ });
99
+ }
100
+
101
+ static async buildMapDropdown(id) {
102
+ return await DropDown.instance({
103
+ id,
104
+ label: html`Map`,
105
+ data: [],
106
+ containerClass: 'inl',
107
+ serviceProvider: async (q) => {
108
+ const result = await CyberiaMapService.searchCodes({ q });
109
+ if (result.status === 'success' && result.data?.codes) {
110
+ return result.data.codes.map((code) => dropdownOption(code));
111
+ }
112
+ return [];
113
+ },
114
+ });
115
+ }
116
+
117
+ // Dialogue groups are keyed by `code`; the picker derives distinct codes from
118
+ // the dialogue collection so authors can reuse existing dialogue groups.
119
+ static async buildDialogCodeDropdown(id, label) {
120
+ return await DropDown.instance({
121
+ id,
122
+ label,
123
+ data: [],
124
+ containerClass: 'inl',
125
+ serviceProvider: async (q) => {
126
+ const result = await CyberiaDialogueService.get({
127
+ limit: 100,
128
+ filterModel: { code: { filterType: 'text', type: 'contains', filter: q } },
129
+ });
130
+ const rows = result?.data?.data || [];
131
+ const codes = [...new Set(rows.map((r) => r.code).filter(Boolean))];
132
+ return codes.map((code) => dropdownOption(code));
133
+ },
134
+ });
135
+ }
136
+
137
+ // ── Object-layer textures (same loading convention as MapEngineCyberia) ──
138
+ static loadObjectLayerImage(itemId, onLoad) {
139
+ if (ActionEngineCyberia.imageCache[itemId]) return;
140
+ ActionEngineCyberia.imageCache[itemId] = { img: null, loaded: false, error: false };
141
+
142
+ const loadImage = (type, id) => {
143
+ const img = new Image();
144
+ img.onload = () => {
145
+ ActionEngineCyberia.imageCache[itemId].img = img;
146
+ ActionEngineCyberia.imageCache[itemId].loaded = true;
147
+ if (onLoad) onLoad();
148
+ };
149
+ img.onerror = () => {
150
+ ActionEngineCyberia.imageCache[itemId].error = true;
151
+ };
152
+ img.src = `${getProxyPath()}assets/${type}/${id}/08/0.png`;
153
+ };
154
+
155
+ const sharedItem = getDefaultCyberiaItemById(itemId)?.item;
156
+ if (sharedItem?.type && sharedItem?.id) {
157
+ loadImage(sharedItem.type, sharedItem.id);
158
+ return;
159
+ }
160
+
161
+ ObjectLayerService.get({
162
+ limit: 1,
163
+ filterModel: { 'data.item.id': { filterType: 'text', type: 'equals', filter: itemId } },
164
+ })
165
+ .then((res) => {
166
+ const doc = res?.data?.data?.[0];
167
+ if (!doc || !doc.data?.item?.type || !doc.data?.item?.id) {
168
+ ActionEngineCyberia.imageCache[itemId].error = true;
169
+ return;
170
+ }
171
+ loadImage(doc.data.item.type, doc.data.item.id);
172
+ })
173
+ .catch(() => {
174
+ ActionEngineCyberia.imageCache[itemId].error = true;
175
+ });
176
+ }
177
+
178
+ static preloadMapObjectLayers(onLoad) {
179
+ for (const entity of ActionEngineCyberia.mapDoc?.entities || [])
180
+ for (const itemId of entity.objectLayerItemIds || []) ActionEngineCyberia.loadObjectLayerImage(itemId, onLoad);
181
+ }
182
+
183
+ // ── Map canvas ──────────────────────────────────────────────────────────
184
+ static getMapGeometry() {
185
+ const map = ActionEngineCyberia.mapDoc;
186
+ return {
187
+ cols: map?.gridX || 16,
188
+ rows: map?.gridY || 16,
189
+ cellW: Math.max(8, Math.min(28, map?.cellWidth || 18)),
190
+ cellH: Math.max(8, Math.min(28, map?.cellHeight || 18)),
191
+ };
192
+ }
193
+
194
+ static getSelectedCell() {
195
+ return {
196
+ x: parseInt(s('.action-engine-cell-x')?.value),
197
+ y: parseInt(s('.action-engine-cell-y')?.value),
198
+ };
199
+ }
200
+
201
+ static renderMapCanvas() {
202
+ const canvas = s('.action-engine-canvas');
203
+ if (!canvas) return;
204
+ const { cols, rows, cellW, cellH } = ActionEngineCyberia.getMapGeometry();
205
+ canvas.width = cols * cellW;
206
+ canvas.height = rows * cellH;
207
+ const ctx = canvas.getContext('2d');
208
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
209
+
210
+ for (const entity of ActionEngineCyberia.mapDoc?.entities || []) {
211
+ const x = entity.initCellX * cellW;
212
+ const y = entity.initCellY * cellH;
213
+ const w = entity.dimX * cellW;
214
+ const h = entity.dimY * cellH;
215
+ let drew = false;
216
+ if (entity.objectLayerItemIds?.length) {
217
+ for (const itemId of entity.objectLayerItemIds) {
218
+ const cached = ActionEngineCyberia.imageCache[itemId];
219
+ if (cached?.loaded && cached.img) {
220
+ ctx.drawImage(cached.img, x, y, w, h);
221
+ drew = true;
222
+ }
223
+ }
224
+ }
225
+ if (!drew) {
226
+ ctx.fillStyle = entity.color || 'rgba(80,80,80,0.6)';
227
+ ctx.fillRect(x, y, w, h);
228
+ }
229
+ }
230
+
231
+ ctx.strokeStyle = darkTheme ? '#3a3a3a' : '#cccccc';
232
+ ctx.lineWidth = 1;
233
+ for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) ctx.strokeRect(c * cellW, r * cellH, cellW, cellH);
234
+
235
+ const drawMarker = (x, y, color, glyph) => {
236
+ const cx = x * cellW + cellW / 2;
237
+ const cy = y * cellH + cellH / 2;
238
+ ctx.beginPath();
239
+ ctx.arc(cx, cy, Math.min(cellW, cellH) / 2.4, 0, Math.PI * 2);
240
+ ctx.fillStyle = color;
241
+ ctx.fill();
242
+ ctx.fillStyle = '#fff';
243
+ ctx.font = `bold ${Math.floor(Math.min(cellW, cellH) * 0.7)}px monospace`;
244
+ ctx.textAlign = 'center';
245
+ ctx.textBaseline = 'middle';
246
+ ctx.fillText(glyph, cx, cy);
247
+ };
248
+ for (const q of ActionEngineCyberia.assignments.quests)
249
+ if (q.sourceCellX != null) drawMarker(q.sourceCellX, q.sourceCellY, 'rgba(40,120,220,0.92)', 'Q');
250
+ for (const a of ActionEngineCyberia.assignments.actions)
251
+ if (a.sourceCellX != null) drawMarker(a.sourceCellX, a.sourceCellY, 'rgba(220,120,40,0.92)', 'A');
252
+
253
+ const sel = ActionEngineCyberia.getSelectedCell();
254
+ if (Number.isFinite(sel.x) && Number.isFinite(sel.y)) {
255
+ ctx.strokeStyle = '#1fd11f';
256
+ ctx.lineWidth = 3;
257
+ ctx.strokeRect(sel.x * cellW + 1, sel.y * cellH + 1, cellW - 2, cellH - 2);
258
+ }
259
+ }
260
+
261
+ static async loadMap(code) {
262
+ if (!code) {
263
+ NotificationManager.Push({ html: 'Select a map first.', status: 'warning' });
264
+ return;
265
+ }
266
+ const result = await CyberiaMapService.get({ limit: 1, filterModel: { code: textFilter(code) } });
267
+ const doc = result?.data?.data?.[0];
268
+ if (!doc) {
269
+ NotificationManager.Push({ html: `Map "${code}" not found`, status: 'error' });
270
+ return;
271
+ }
272
+ ActionEngineCyberia.mapCode = code;
273
+ ActionEngineCyberia.mapDoc = doc;
274
+ htmls('.action-engine-map-meta', `${doc.code} · ${doc.gridX || 16}×${doc.gridY || 16} cells`);
275
+ ActionEngineCyberia.preloadMapObjectLayers(() => ActionEngineCyberia.renderMapCanvas());
276
+ await ActionEngineCyberia.refreshAssignments();
277
+ ActionEngineCyberia.renderMapCanvas();
278
+ }
279
+
280
+ static async refreshAssignments() {
281
+ const code = ActionEngineCyberia.mapCode;
282
+ if (!code) {
283
+ ActionEngineCyberia.assignments = { quests: [], actions: [] };
284
+ ActionEngineCyberia.renderAssignmentReview();
285
+ return;
286
+ }
287
+ const [questRes, actionRes] = await Promise.all([
288
+ CyberiaQuestService.get({ limit: 500, filterModel: { sourceMapCode: textFilter(code) } }),
289
+ CyberiaActionService.get({ limit: 500, filterModel: { sourceMapCode: textFilter(code) } }),
290
+ ]);
291
+ ActionEngineCyberia.assignments = {
292
+ quests: questRes?.data?.data || [],
293
+ actions: actionRes?.data?.data || [],
294
+ };
295
+ ActionEngineCyberia.renderMapCanvas();
296
+ ActionEngineCyberia.renderAssignmentReview();
297
+ }
298
+
299
+ static renderAssignmentReview() {
300
+ const container = s('.action-engine-assignment-review');
301
+ if (!container) return;
302
+ const { quests, actions } = ActionEngineCyberia.assignments;
303
+ const row = (kind, color, doc) =>
304
+ html`<div
305
+ class="fl action-engine-assignment-row"
306
+ style="border-bottom:1px solid ${subtleBorder()};padding:4px 0;align-items:center;cursor:pointer;"
307
+ data-kind="${kind}"
308
+ data-id="${doc._id}"
309
+ >
310
+ <div
311
+ class="in fll"
312
+ style="width:18px;height:18px;border-radius:50%;background:${color};color:#fff;text-align:center;font-size:11px;line-height:18px;margin-right:6px;"
313
+ >
314
+ ${kind === 'quest' ? 'Q' : 'A'}
315
+ </div>
316
+ <div class="in fll" style="flex:1;font-size:12px;font-family:monospace;">
317
+ ${doc.code}
318
+ ${doc.sourceCellX != null
319
+ ? `(${doc.sourceCellX}, ${doc.sourceCellY})`
320
+ : '<span style="color:#999;">unplaced</span>'}
321
+ </div>
322
+ </div>`;
323
+ let out = '';
324
+ for (const q of quests) out += row('quest', 'rgba(40,120,220,0.92)', q);
325
+ for (const a of actions) out += row('action', 'rgba(220,120,40,0.92)', a);
326
+ if (!out)
327
+ out = `<div style="color:#888;font-size:13px;">${ActionEngineCyberia.mapCode ? 'No assignments on this map yet.' : 'Load a map to review assignments.'}</div>`;
328
+ htmls('.action-engine-assignment-review', out);
329
+
330
+ container.querySelectorAll('.action-engine-assignment-row').forEach((rowEl) => {
331
+ rowEl.onclick = async () => {
332
+ const { kind, id } = rowEl.dataset;
333
+ if (kind === 'quest') {
334
+ ActionEngineCyberia.setMode('quest');
335
+ const res = await CyberiaQuestService.get({ id });
336
+ if (res.status === 'success' && res.data) ActionEngineCyberia.loadQuest(res.data);
337
+ } else {
338
+ ActionEngineCyberia.setMode('action');
339
+ const res = await CyberiaActionService.get({ id });
340
+ if (res.status === 'success' && res.data) ActionEngineCyberia.loadAction(res.data);
341
+ }
342
+ };
343
+ });
344
+ }
345
+
346
+ // ── Mode switching ──────────────────────────────────────────────────────
347
+ static setMode(mode) {
348
+ ActionEngineCyberia.mode = mode;
349
+ for (const m of ['quest', 'action', 'dialogue', 'skill']) {
350
+ const panel = s(`.action-engine-panel-${m}`);
351
+ if (panel) panel.classList[m === mode ? 'remove' : 'add']('hide');
352
+ const tab = s(`.action-engine-tab-${m}`);
353
+ if (tab) {
354
+ tab.style.opacity = m === mode ? '1' : '0.5';
355
+ tab.style.fontWeight = m === mode ? 'bold' : 'normal';
356
+ tab.style.borderBottomColor = m === mode ? '#1fd11f' : 'transparent';
357
+ }
358
+ }
359
+ }
360
+
361
+ // Keeps the shared map surface in sync with the loaded document: reflects the
362
+ // map dropdown, loads the matching map (with object-layer textures) and
363
+ // highlights the assigned cell so the editor is reactive on load.
364
+ static async syncMapForLoadedDoc(doc) {
365
+ if (doc.sourceMapCode) {
366
+ ActionEngineCyberia.setSingleDropdownValue(ActionEngineCyberia.ids.mapDropdown, doc.sourceMapCode);
367
+ if (doc.sourceMapCode !== ActionEngineCyberia.mapCode) {
368
+ await ActionEngineCyberia.loadMap(doc.sourceMapCode);
369
+ return;
370
+ }
371
+ }
372
+ ActionEngineCyberia.renderMapCanvas();
373
+ }
374
+
375
+ // ── Multi-step quest editor ─────────────────────────────────────────────
376
+ static syncStepDescriptions() {
377
+ ActionEngineCyberia.questSteps.forEach((step, i) => {
378
+ const el = s(`.action-engine-step-desc-${i}`);
379
+ if (el) step.description = el.value;
380
+ });
381
+ }
382
+
383
+ static async renderStepList() {
384
+ const container = s('.action-engine-step-list');
385
+ if (!container) return;
386
+ const steps = ActionEngineCyberia.questSteps;
387
+ let out = '';
388
+ for (let i = 0; i < steps.length; i++) {
389
+ const step = steps[i];
390
+ let objOut = '';
391
+ for (let j = 0; j < step.objectives.length; j++) {
392
+ const o = step.objectives[j];
393
+ objOut += html`<div
394
+ class="fl"
395
+ style="border-bottom:1px solid ${subtleBorder()};padding:3px 0;align-items:center;"
396
+ >
397
+ <div class="in fll" style="flex:1;font-family:monospace;font-size:12px;">
398
+ <span style="color:${darkTheme ? '#8cf' : '#246'};">${o.type}</span> · ${o.itemId} ×${o.quantity}
399
+ </div>
400
+ ${await BtnIcon.instance({
401
+ class: `btn-aeq-rmobj-${i}-${j}`,
402
+ label: html`<i class="fa-solid fa-xmark"></i>`,
403
+ style: 'min-width:30px;padding:2px 8px;',
404
+ })}
405
+ </div>`;
406
+ }
407
+ if (!objOut) objOut = '<div style="color:#888;font-size:11px;padding:2px 0;">No objectives in this step.</div>';
408
+ out += html`<div
409
+ class="in"
410
+ style="border:1px solid ${subtleBorder()};border-radius:6px;padding:8px;margin-bottom:8px;"
411
+ >
412
+ <div class="fl" style="align-items:center;margin-bottom:6px;">
413
+ <div class="in fll" style="flex:1;font-weight:bold;font-size:12px;">
414
+ <i class="fa-solid fa-layer-group"></i> Step ${i + 1}
415
+ </div>
416
+ ${await BtnIcon.instance({
417
+ class: `btn-aeq-rmstep-${i}`,
418
+ label: html`<i class="fa-solid fa-trash"></i>`,
419
+ style: 'min-width:30px;padding:2px 8px;',
420
+ })}
421
+ </div>
422
+ ${await Input.instance({
423
+ id: `action-engine-step-desc-${i}`,
424
+ label: html`Step description`,
425
+ containerClass: 'inl',
426
+ type: 'text',
427
+ value: step.description || '',
428
+ })}
429
+ <div class="in" style="margin-top:6px;">${objOut}</div>
430
+ </div>`;
431
+ }
432
+ if (!out) out = '<div style="color:#888;font-size:12px;">No steps yet. Add a step to begin.</div>';
433
+ htmls('.action-engine-step-list', out);
434
+
435
+ for (let i = 0; i < steps.length; i++) {
436
+ if (s(`.btn-aeq-rmstep-${i}`))
437
+ s(`.btn-aeq-rmstep-${i}`).onclick = () => {
438
+ ActionEngineCyberia.syncStepDescriptions();
439
+ ActionEngineCyberia.questSteps.splice(i, 1);
440
+ ActionEngineCyberia.renderStepList();
441
+ };
442
+ for (let j = 0; j < steps[i].objectives.length; j++) {
443
+ if (s(`.btn-aeq-rmobj-${i}-${j}`))
444
+ s(`.btn-aeq-rmobj-${i}-${j}`).onclick = () => {
445
+ ActionEngineCyberia.syncStepDescriptions();
446
+ ActionEngineCyberia.questSteps[i].objectives.splice(j, 1);
447
+ ActionEngineCyberia.renderStepList();
448
+ };
449
+ }
450
+ }
451
+ }
452
+
453
+ static addStep() {
454
+ ActionEngineCyberia.syncStepDescriptions();
455
+ const n = ActionEngineCyberia.questSteps.length + 1;
456
+ ActionEngineCyberia.questSteps.push({ id: `step-${n}`, description: '', objectives: [] });
457
+ ActionEngineCyberia.renderStepList();
458
+ if (s('.action-engine-objective-step')) s('.action-engine-objective-step').value = n;
459
+ }
460
+
461
+ static addObjective() {
462
+ const itemId = ActionEngineCyberia.getSingleDropdownValue(ActionEngineCyberia.ids.objectiveItemPicker);
463
+ const type = DropDown.Tokens[ActionEngineCyberia.ids.objectiveType]?.value || QUEST_STEPS_TYPES[0];
464
+ const quantity = Math.max(1, parseInt(s('.action-engine-objective-qty')?.value) || 1);
465
+ if (!itemId) {
466
+ NotificationManager.Push({ html: 'Select an itemId for the objective.', status: 'error' });
467
+ return;
468
+ }
469
+ ActionEngineCyberia.syncStepDescriptions();
470
+ let stepIdx = (parseInt(s('.action-engine-objective-step')?.value) || 1) - 1;
471
+ if (ActionEngineCyberia.questSteps.length === 0) {
472
+ ActionEngineCyberia.questSteps.push({ id: 'step-1', description: '', objectives: [] });
473
+ stepIdx = 0;
474
+ if (s('.action-engine-objective-step')) s('.action-engine-objective-step').value = 1;
475
+ }
476
+ if (stepIdx < 0 || stepIdx >= ActionEngineCyberia.questSteps.length) {
477
+ NotificationManager.Push({ html: `Step #${stepIdx + 1} does not exist.`, status: 'error' });
478
+ return;
479
+ }
480
+ const duplicate = ActionEngineCyberia.questSteps[stepIdx].objectives.some(
481
+ (o) => o.type === type && o.itemId === itemId,
482
+ );
483
+ if (duplicate) {
484
+ NotificationManager.Push({
485
+ html: `Step ${stepIdx + 1} already has a "${type}" objective for "${itemId}".`,
486
+ status: 'error',
487
+ });
488
+ return;
489
+ }
490
+ ActionEngineCyberia.questSteps[stepIdx].objectives.push({ type, itemId, quantity });
491
+ ActionEngineCyberia.renderStepList();
492
+ }
493
+
494
+ static async renderRewardList() {
495
+ const container = s('.action-engine-reward-list');
496
+ if (!container) return;
497
+ let out = '';
498
+ for (let i = 0; i < ActionEngineCyberia.questRewards.length; i++) {
499
+ const r = ActionEngineCyberia.questRewards[i];
500
+ out += html`<div
501
+ class="fl"
502
+ style="border-bottom:1px solid ${subtleBorder()};padding:3px 0;align-items:center;font-size:12px;font-family:monospace;"
503
+ >
504
+ <div class="in fll" style="flex:1;">${r.itemId} ×${r.quantity}</div>
505
+ ${await BtnIcon.instance({
506
+ class: `btn-aeq-rmreward-${i}`,
507
+ label: html`<i class="fa-solid fa-xmark"></i>`,
508
+ style: 'min-width:30px;padding:2px 8px;',
509
+ })}
510
+ </div>`;
511
+ }
512
+ if (!out) out = '<div style="color:#888;font-size:12px;">No rewards.</div>';
513
+ htmls('.action-engine-reward-list', out);
514
+ for (let i = 0; i < ActionEngineCyberia.questRewards.length; i++) {
515
+ if (s(`.btn-aeq-rmreward-${i}`))
516
+ s(`.btn-aeq-rmreward-${i}`).onclick = () => {
517
+ ActionEngineCyberia.questRewards.splice(i, 1);
518
+ ActionEngineCyberia.renderRewardList();
519
+ };
520
+ }
521
+ }
522
+
523
+ static addReward() {
524
+ const itemId = ActionEngineCyberia.getSingleDropdownValue(ActionEngineCyberia.ids.rewardItemPicker);
525
+ const quantity = Math.max(1, parseInt(s('.action-engine-reward-qty')?.value) || 1);
526
+ if (!itemId) {
527
+ NotificationManager.Push({ html: 'Select an itemId for the reward.', status: 'error' });
528
+ return;
529
+ }
530
+ ActionEngineCyberia.questRewards.push({ itemId, quantity });
531
+ ActionEngineCyberia.renderRewardList();
532
+ }
533
+
534
+ static async renderQuestDialogueList() {
535
+ const container = s('.action-engine-quest-dialogue-list');
536
+ if (!container) return;
537
+ let out = '';
538
+ for (let i = 0; i < ActionEngineCyberia.actionQuestDialogues.length; i++) {
539
+ const d = ActionEngineCyberia.actionQuestDialogues[i];
540
+ out += html`<div
541
+ class="fl"
542
+ style="border-bottom:1px solid ${subtleBorder()};padding:3px 0;align-items:center;font-size:12px;font-family:monospace;"
543
+ >
544
+ <div class="in fll" style="flex:1;">
545
+ <span style="color:${darkTheme ? '#8cf' : '#246'};">${d.questCode}</span>
546
+ <span style="margin:0 4px;">&rarr;</span>${d.dialogCode}
547
+ </div>
548
+ ${await BtnIcon.instance({
549
+ class: `btn-aea-rmqd-${i}`,
550
+ label: html`<i class="fa-solid fa-xmark"></i>`,
551
+ style: 'min-width:30px;padding:2px 8px;',
552
+ })}
553
+ </div>`;
554
+ }
555
+ if (!out) out = '<div style="color:#888;font-size:12px;">No quest dialogues mapped.</div>';
556
+ htmls('.action-engine-quest-dialogue-list', out);
557
+ for (let i = 0; i < ActionEngineCyberia.actionQuestDialogues.length; i++) {
558
+ if (s(`.btn-aea-rmqd-${i}`))
559
+ s(`.btn-aea-rmqd-${i}`).onclick = () => {
560
+ ActionEngineCyberia.actionQuestDialogues.splice(i, 1);
561
+ ActionEngineCyberia.renderQuestDialogueList();
562
+ };
563
+ }
564
+ }
565
+
566
+ // ── Card lists (CRUD surface, mirrors the map-engine entity list) ────────
567
+ static cardActionsHtml(loadCls, delCls) {
568
+ return Promise.all([
569
+ BtnIcon.instance({
570
+ class: loadCls,
571
+ label: html`<i class="fa-solid fa-upload"></i>`,
572
+ style: 'min-width:34px;padding:3px 9px;',
573
+ }),
574
+ ActionEngineCyberia.canMutate
575
+ ? BtnIcon.instance({
576
+ class: delCls,
577
+ label: html`<i class="fa-solid fa-trash"></i>`,
578
+ style: 'min-width:34px;padding:3px 9px;',
579
+ })
580
+ : '',
581
+ ]);
582
+ }
583
+
584
+ static cardWrap(inner) {
585
+ return html`<div
586
+ class="in"
587
+ style="border:1px solid ${subtleBorder()};border-radius:6px;padding:8px;margin-bottom:6px;"
588
+ >
589
+ ${inner}
590
+ </div>`;
591
+ }
592
+
593
+ static async renderQuestCards() {
594
+ const container = s('.action-engine-quest-cards');
595
+ if (!container) return;
596
+ const filter = (s('.action-engine-quest-search')?.value || '').toLowerCase();
597
+ const items = ActionEngineCyberia.questListCache.filter(
598
+ (d) => !filter || (d.code || '').toLowerCase().includes(filter) || (d.title || '').toLowerCase().includes(filter),
599
+ );
600
+ let out = '';
601
+ for (const doc of items) {
602
+ const stepCount = (doc.steps || []).length;
603
+ const objCount = (doc.steps || []).reduce((a, st) => a + (st.objectives || []).length, 0);
604
+ const [loadBtn, delBtn] = await ActionEngineCyberia.cardActionsHtml(
605
+ `btn-aeq-load-${doc._id}`,
606
+ `btn-aeq-del-${doc._id}`,
607
+ );
608
+ out += ActionEngineCyberia.cardWrap(
609
+ html`<div class="fl" style="align-items:center;">
610
+ <div class="in fll" style="flex:1;">
611
+ <div style="font-weight:bold;font-family:monospace;font-size:12px;">${doc.code}</div>
612
+ <div style="font-size:11px;color:#888;">${doc.title || ''}</div>
613
+ <div style="font-size:11px;color:#888;">
614
+ ${doc.sourceMapCode ? `${doc.sourceMapCode} (${doc.sourceCellX}, ${doc.sourceCellY})` : 'unplaced'} ·
615
+ ${stepCount} step(s) · ${objCount} objective(s)
616
+ </div>
617
+ </div>
618
+ <div class="in fll" style="display:flex;gap:4px;">${loadBtn}${delBtn}</div>
619
+ </div>`,
620
+ );
621
+ }
622
+ if (!out) out = '<div style="color:#888;font-size:12px;">No quests found.</div>';
623
+ htmls('.action-engine-quest-cards', out);
624
+ for (const doc of items) {
625
+ if (s(`.btn-aeq-load-${doc._id}`))
626
+ s(`.btn-aeq-load-${doc._id}`).onclick = async () => {
627
+ const res = await CyberiaQuestService.get({ id: doc._id });
628
+ if (res.status === 'success' && res.data) ActionEngineCyberia.loadQuest(res.data);
629
+ };
630
+ if (s(`.btn-aeq-del-${doc._id}`))
631
+ s(`.btn-aeq-del-${doc._id}`).onclick = () => ActionEngineCyberia.deleteQuest(doc._id);
632
+ }
633
+ }
634
+
635
+ static async renderActionCards() {
636
+ const container = s('.action-engine-action-cards');
637
+ if (!container) return;
638
+ const filter = (s('.action-engine-action-search')?.value || '').toLowerCase();
639
+ const items = ActionEngineCyberia.actionListCache.filter(
640
+ (d) => !filter || (d.code || '').toLowerCase().includes(filter) || (d.label || '').toLowerCase().includes(filter),
641
+ );
642
+ let out = '';
643
+ for (const doc of items) {
644
+ const [loadBtn, delBtn] = await ActionEngineCyberia.cardActionsHtml(
645
+ `btn-aea-load-${doc._id}`,
646
+ `btn-aea-del-${doc._id}`,
647
+ );
648
+ out += ActionEngineCyberia.cardWrap(
649
+ html`<div class="fl" style="align-items:center;">
650
+ <div class="in fll" style="flex:1;">
651
+ <div style="font-weight:bold;font-family:monospace;font-size:12px;">${doc.code}</div>
652
+ <div style="font-size:11px;color:#888;">${doc.label || ''}</div>
653
+ <div style="font-size:11px;color:#888;">
654
+ ${doc.sourceMapCode ? `${doc.sourceMapCode} (${doc.sourceCellX}, ${doc.sourceCellY})` : 'unplaced'} ·
655
+ dialog: ${doc.dialogCode || '—'} · ${(doc.questDialogueCodes || []).length} quest-dialog(s)
656
+ </div>
657
+ </div>
658
+ <div class="in fll" style="display:flex;gap:4px;">${loadBtn}${delBtn}</div>
659
+ </div>`,
660
+ );
661
+ }
662
+ if (!out) out = '<div style="color:#888;font-size:12px;">No actions found.</div>';
663
+ htmls('.action-engine-action-cards', out);
664
+ for (const doc of items) {
665
+ if (s(`.btn-aea-load-${doc._id}`))
666
+ s(`.btn-aea-load-${doc._id}`).onclick = async () => {
667
+ const res = await CyberiaActionService.get({ id: doc._id });
668
+ if (res.status === 'success' && res.data) ActionEngineCyberia.loadAction(res.data);
669
+ };
670
+ if (s(`.btn-aea-del-${doc._id}`))
671
+ s(`.btn-aea-del-${doc._id}`).onclick = () => ActionEngineCyberia.deleteAction(doc._id);
672
+ }
673
+ }
674
+
675
+ static async renderDialogueCards() {
676
+ const container = s('.action-engine-dialogue-cards');
677
+ if (!container) return;
678
+ const filter = (s('.action-engine-dialogue-search')?.value || '').toLowerCase();
679
+ const items = ActionEngineCyberia.dialogueListCache.filter(
680
+ (d) =>
681
+ !filter ||
682
+ (d.code || '').toLowerCase().includes(filter) ||
683
+ (d.speaker || '').toLowerCase().includes(filter) ||
684
+ (d.text || '').toLowerCase().includes(filter),
685
+ );
686
+ let out = '';
687
+ for (const doc of items) {
688
+ const [loadBtn, delBtn] = await ActionEngineCyberia.cardActionsHtml(
689
+ `btn-aed-load-${doc._id}`,
690
+ `btn-aed-del-${doc._id}`,
691
+ );
692
+ const snippet = (doc.text || '').length > 60 ? `${doc.text.slice(0, 60)}…` : doc.text || '';
693
+ out += ActionEngineCyberia.cardWrap(
694
+ html`<div class="fl" style="align-items:center;">
695
+ <div class="in fll" style="flex:1;">
696
+ <div style="font-weight:bold;font-family:monospace;font-size:12px;">${doc.code} #${doc.order ?? 0}</div>
697
+ <div style="font-size:11px;color:#888;">${doc.speaker || '—'} · ${doc.mood || 'neutral'}</div>
698
+ <div style="font-size:11px;color:#888;">${snippet}</div>
699
+ </div>
700
+ <div class="in fll" style="display:flex;gap:4px;">${loadBtn}${delBtn}</div>
701
+ </div>`,
702
+ );
703
+ }
704
+ if (!out) out = '<div style="color:#888;font-size:12px;">No dialogue lines found.</div>';
705
+ htmls('.action-engine-dialogue-cards', out);
706
+ for (const doc of items) {
707
+ if (s(`.btn-aed-load-${doc._id}`))
708
+ s(`.btn-aed-load-${doc._id}`).onclick = async () => {
709
+ const res = await CyberiaDialogueService.get({ id: doc._id });
710
+ if (res.status === 'success' && res.data) ActionEngineCyberia.loadDialogue(res.data);
711
+ };
712
+ if (s(`.btn-aed-del-${doc._id}`))
713
+ s(`.btn-aed-del-${doc._id}`).onclick = () => ActionEngineCyberia.deleteDialogue(doc._id);
714
+ }
715
+ }
716
+
717
+ static async refreshQuestList() {
718
+ const res = await CyberiaQuestService.get({ limit: 500 });
719
+ ActionEngineCyberia.questListCache = res?.data?.data || [];
720
+ await ActionEngineCyberia.renderQuestCards();
721
+ }
722
+
723
+ static async refreshActionList() {
724
+ const res = await CyberiaActionService.get({ limit: 500 });
725
+ ActionEngineCyberia.actionListCache = res?.data?.data || [];
726
+ await ActionEngineCyberia.renderActionCards();
727
+ }
728
+
729
+ static async refreshDialogueList() {
730
+ const res = await CyberiaDialogueService.get({ limit: 500 });
731
+ ActionEngineCyberia.dialogueListCache = res?.data?.data || [];
732
+ await ActionEngineCyberia.renderDialogueCards();
733
+ }
734
+
735
+ static notifyResult(result, isUpdate) {
736
+ NotificationManager.Push({
737
+ html:
738
+ result.status === 'error'
739
+ ? result.message
740
+ : isUpdate
741
+ ? Translate.instance('success-update-item')
742
+ : Translate.instance('success-create-item'),
743
+ status: result.status,
744
+ });
745
+ }
746
+
747
+ // ── Quest payload / load / persistence ──────────────────────────────────
748
+ static getQuestPayload() {
749
+ ActionEngineCyberia.syncStepDescriptions();
750
+ const csv = (v) =>
751
+ (v || '')
752
+ .split(',')
753
+ .map((t) => t.trim())
754
+ .filter(Boolean);
755
+ const payload = {
756
+ code: s('.action-engine-quest-code')?.value?.trim() || '',
757
+ title: s('.action-engine-quest-title')?.value?.trim() || '',
758
+ description: s('.action-engine-quest-description')?.value || '',
759
+ prerequisiteCodes: csv(s('.action-engine-quest-prerequisites')?.value),
760
+ unlocksQuestCodes: csv(s('.action-engine-quest-unlocks')?.value),
761
+ steps: ActionEngineCyberia.questSteps.map((st, i) => ({
762
+ id: st.id || `step-${i + 1}`,
763
+ description: st.description || '',
764
+ objectives: st.objectives.map((o) => ({ ...o })),
765
+ })),
766
+ rewards: ActionEngineCyberia.questRewards.map((r) => ({ ...r })),
767
+ };
768
+ const mapCode = s('.action-engine-quest-map-code')?.value?.trim();
769
+ const cellX = parseInt(s('.action-engine-cell-x')?.value);
770
+ const cellY = parseInt(s('.action-engine-cell-y')?.value);
771
+ if (mapCode) payload.sourceMapCode = mapCode;
772
+ if (Number.isFinite(cellX)) payload.sourceCellX = cellX;
773
+ if (Number.isFinite(cellY)) payload.sourceCellY = cellY;
774
+ return payload;
775
+ }
776
+
777
+ static async loadQuest(doc) {
778
+ ActionEngineCyberia.setMode('quest');
779
+ ActionEngineCyberia.currentQuestId = doc._id || null;
780
+ if (s('.action-engine-quest-code')) s('.action-engine-quest-code').value = doc.code || '';
781
+ if (s('.action-engine-quest-title')) s('.action-engine-quest-title').value = doc.title || '';
782
+ if (s('.action-engine-quest-description')) s('.action-engine-quest-description').value = doc.description || '';
783
+ if (s('.action-engine-quest-prerequisites'))
784
+ s('.action-engine-quest-prerequisites').value = (doc.prerequisiteCodes || []).join(', ');
785
+ if (s('.action-engine-quest-unlocks'))
786
+ s('.action-engine-quest-unlocks').value = (doc.unlocksQuestCodes || []).join(', ');
787
+ if (s('.action-engine-quest-map-code')) s('.action-engine-quest-map-code').value = doc.sourceMapCode || '';
788
+ if (s('.action-engine-cell-x')) s('.action-engine-cell-x').value = doc.sourceCellX ?? '';
789
+ if (s('.action-engine-cell-y')) s('.action-engine-cell-y').value = doc.sourceCellY ?? '';
790
+ ActionEngineCyberia.questSteps = (doc.steps || []).map((st, i) => ({
791
+ id: st.id || `step-${i + 1}`,
792
+ description: st.description || '',
793
+ objectives: (st.objectives || []).map((o) => ({ type: o.type, itemId: o.itemId, quantity: o.quantity || 1 })),
794
+ }));
795
+ ActionEngineCyberia.questRewards = (doc.rewards || []).map((r) => ({
796
+ itemId: r.itemId,
797
+ quantity: r.quantity || 1,
798
+ }));
799
+ ActionEngineCyberia.renderStepList();
800
+ ActionEngineCyberia.renderRewardList();
801
+ await ActionEngineCyberia.syncMapForLoadedDoc(doc);
802
+ NotificationManager.Push({ html: `Quest "${doc.code}" loaded`, status: 'success' });
803
+ }
804
+
805
+ static resetQuest() {
806
+ ActionEngineCyberia.currentQuestId = null;
807
+ ActionEngineCyberia.questSteps = [];
808
+ ActionEngineCyberia.questRewards = [];
809
+ for (const cls of [
810
+ 'action-engine-quest-code',
811
+ 'action-engine-quest-title',
812
+ 'action-engine-quest-description',
813
+ 'action-engine-quest-prerequisites',
814
+ 'action-engine-quest-unlocks',
815
+ ])
816
+ if (s(`.${cls}`)) s(`.${cls}`).value = '';
817
+ ActionEngineCyberia.renderStepList();
818
+ ActionEngineCyberia.renderRewardList();
819
+ }
820
+
821
+ static async saveQuest() {
822
+ const body = ActionEngineCyberia.getQuestPayload();
823
+ if (!body.code || !body.title) {
824
+ NotificationManager.Push({ html: 'Quest code and title are required.', status: 'error' });
825
+ return;
826
+ }
827
+ const isUpdate = !!ActionEngineCyberia.currentQuestId;
828
+ const result = isUpdate
829
+ ? await CyberiaQuestService.put({ id: ActionEngineCyberia.currentQuestId, body })
830
+ : await CyberiaQuestService.post({ body });
831
+ ActionEngineCyberia.notifyResult(result, isUpdate);
832
+ if (result.status === 'success') {
833
+ if (result.data?._id) ActionEngineCyberia.currentQuestId = result.data._id;
834
+ await ActionEngineCyberia.refreshQuestList();
835
+ await ActionEngineCyberia.refreshAssignments();
836
+ }
837
+ }
838
+
839
+ static async cloneQuest() {
840
+ if (!ActionEngineCyberia.currentQuestId) {
841
+ NotificationManager.Push({ html: 'Load a quest to clone first.', status: 'warning' });
842
+ return;
843
+ }
844
+ const body = ActionEngineCyberia.getQuestPayload();
845
+ body.code = `${body.code}-clone`;
846
+ const result = await CyberiaQuestService.post({ body });
847
+ ActionEngineCyberia.notifyResult(result, false);
848
+ if (result.status === 'success') {
849
+ if (result.data?._id) ActionEngineCyberia.currentQuestId = result.data._id;
850
+ if (s('.action-engine-quest-code')) s('.action-engine-quest-code').value = body.code;
851
+ await ActionEngineCyberia.refreshQuestList();
852
+ await ActionEngineCyberia.refreshAssignments();
853
+ }
854
+ }
855
+
856
+ static async deleteQuest(id) {
857
+ const targetId = id || ActionEngineCyberia.currentQuestId;
858
+ if (!targetId) return;
859
+ const result = await CyberiaQuestService.delete({ id: targetId });
860
+ NotificationManager.Push({
861
+ html: result.status === 'error' ? result.message : Translate.instance('item-success-delete'),
862
+ status: result.status,
863
+ });
864
+ if (result.status === 'success') {
865
+ if (targetId === ActionEngineCyberia.currentQuestId) ActionEngineCyberia.resetQuest();
866
+ await ActionEngineCyberia.refreshQuestList();
867
+ await ActionEngineCyberia.refreshAssignments();
868
+ }
869
+ }
870
+
871
+ // ── Action payload / load / persistence ─────────────────────────────────
872
+ static getActionPayload() {
873
+ const payload = {
874
+ ...ActionEngineCyberia.loadedActionPayloadExtras,
875
+ code: s('.action-engine-action-code')?.value?.trim() || '',
876
+ label: s('.action-engine-action-label')?.value?.trim() || '',
877
+ dialogCode: ActionEngineCyberia.getSingleDropdownValue(ActionEngineCyberia.ids.actionDialogPicker),
878
+ questDialogueCodes: ActionEngineCyberia.actionQuestDialogues.map((d) => ({ ...d })),
879
+ };
880
+ const mapCode = s('.action-engine-action-map-code')?.value?.trim();
881
+ const cellX = parseInt(s('.action-engine-cell-x')?.value);
882
+ const cellY = parseInt(s('.action-engine-cell-y')?.value);
883
+ if (mapCode) payload.sourceMapCode = mapCode;
884
+ if (Number.isFinite(cellX)) payload.sourceCellX = cellX;
885
+ if (Number.isFinite(cellY)) payload.sourceCellY = cellY;
886
+ return payload;
887
+ }
888
+
889
+ static async loadAction(doc) {
890
+ ActionEngineCyberia.setMode('action');
891
+ ActionEngineCyberia.currentActionId = doc._id || null;
892
+ // Out-of-scope payloads (shop/craft/storage) are preserved verbatim so
893
+ // editing an action here never drops data the engine doesn't manage yet.
894
+ const {
895
+ _id,
896
+ code,
897
+ label,
898
+ dialogCode,
899
+ questDialogueCodes,
900
+ sourceMapCode,
901
+ sourceCellX,
902
+ sourceCellY,
903
+ createdAt,
904
+ updatedAt,
905
+ __v,
906
+ ...extras
907
+ } = doc;
908
+ ActionEngineCyberia.loadedActionPayloadExtras = extras;
909
+ if (s('.action-engine-action-code')) s('.action-engine-action-code').value = doc.code || '';
910
+ if (s('.action-engine-action-label')) s('.action-engine-action-label').value = doc.label || '';
911
+ if (s('.action-engine-action-map-code')) s('.action-engine-action-map-code').value = doc.sourceMapCode || '';
912
+ if (s('.action-engine-cell-x')) s('.action-engine-cell-x').value = doc.sourceCellX ?? '';
913
+ if (s('.action-engine-cell-y')) s('.action-engine-cell-y').value = doc.sourceCellY ?? '';
914
+ ActionEngineCyberia.setSingleDropdownValue(ActionEngineCyberia.ids.actionDialogPicker, doc.dialogCode || '');
915
+ ActionEngineCyberia.actionQuestDialogues = (doc.questDialogueCodes || []).map((d) => ({
916
+ questCode: d.questCode,
917
+ dialogCode: d.dialogCode,
918
+ }));
919
+ ActionEngineCyberia.renderQuestDialogueList();
920
+ await ActionEngineCyberia.syncMapForLoadedDoc(doc);
921
+ NotificationManager.Push({ html: `Action "${doc.code}" loaded`, status: 'success' });
922
+ }
923
+
924
+ static resetAction() {
925
+ ActionEngineCyberia.currentActionId = null;
926
+ ActionEngineCyberia.actionQuestDialogues = [];
927
+ ActionEngineCyberia.loadedActionPayloadExtras = {};
928
+ for (const cls of ['action-engine-action-code', 'action-engine-action-label'])
929
+ if (s(`.${cls}`)) s(`.${cls}`).value = '';
930
+ ActionEngineCyberia.setSingleDropdownValue(ActionEngineCyberia.ids.actionDialogPicker, '');
931
+ ActionEngineCyberia.renderQuestDialogueList();
932
+ }
933
+
934
+ static async saveAction() {
935
+ const body = ActionEngineCyberia.getActionPayload();
936
+ if (!body.code) {
937
+ NotificationManager.Push({ html: 'Action code is required.', status: 'error' });
938
+ return;
939
+ }
940
+ const isUpdate = !!ActionEngineCyberia.currentActionId;
941
+ const result = isUpdate
942
+ ? await CyberiaActionService.put({ id: ActionEngineCyberia.currentActionId, body })
943
+ : await CyberiaActionService.post({ body });
944
+ ActionEngineCyberia.notifyResult(result, isUpdate);
945
+ if (result.status === 'success') {
946
+ if (result.data?._id) ActionEngineCyberia.currentActionId = result.data._id;
947
+ await ActionEngineCyberia.refreshActionList();
948
+ await ActionEngineCyberia.refreshAssignments();
949
+ }
950
+ }
951
+
952
+ static async cloneAction() {
953
+ if (!ActionEngineCyberia.currentActionId) {
954
+ NotificationManager.Push({ html: 'Load an action to clone first.', status: 'warning' });
955
+ return;
956
+ }
957
+ const body = ActionEngineCyberia.getActionPayload();
958
+ body.code = `${body.code}-clone`;
959
+ const result = await CyberiaActionService.post({ body });
960
+ ActionEngineCyberia.notifyResult(result, false);
961
+ if (result.status === 'success') {
962
+ if (result.data?._id) ActionEngineCyberia.currentActionId = result.data._id;
963
+ if (s('.action-engine-action-code')) s('.action-engine-action-code').value = body.code;
964
+ await ActionEngineCyberia.refreshActionList();
965
+ await ActionEngineCyberia.refreshAssignments();
966
+ }
967
+ }
968
+
969
+ static async deleteAction(id) {
970
+ const targetId = id || ActionEngineCyberia.currentActionId;
971
+ if (!targetId) return;
972
+ const result = await CyberiaActionService.delete({ id: targetId });
973
+ NotificationManager.Push({
974
+ html: result.status === 'error' ? result.message : Translate.instance('item-success-delete'),
975
+ status: result.status,
976
+ });
977
+ if (result.status === 'success') {
978
+ if (targetId === ActionEngineCyberia.currentActionId) ActionEngineCyberia.resetAction();
979
+ await ActionEngineCyberia.refreshActionList();
980
+ await ActionEngineCyberia.refreshAssignments();
981
+ }
982
+ }
983
+
984
+ // ── Dialogue line persistence ───────────────────────────────────────────
985
+ static getDialoguePayload() {
986
+ return {
987
+ code: s('.action-engine-dialogue-code')?.value?.trim() || '',
988
+ order: parseInt(s('.action-engine-dialogue-order')?.value) || 0,
989
+ speaker: s('.action-engine-dialogue-speaker')?.value || '',
990
+ text: s('.action-engine-dialogue-text')?.value || '',
991
+ mood: s('.action-engine-dialogue-mood')?.value?.trim() || 'neutral',
992
+ };
993
+ }
994
+
995
+ static loadDialogue(doc) {
996
+ ActionEngineCyberia.setMode('dialogue');
997
+ ActionEngineCyberia.currentDialogueId = doc._id || null;
998
+ if (s('.action-engine-dialogue-code')) s('.action-engine-dialogue-code').value = doc.code || '';
999
+ if (s('.action-engine-dialogue-order')) s('.action-engine-dialogue-order').value = doc.order ?? 0;
1000
+ if (s('.action-engine-dialogue-speaker')) s('.action-engine-dialogue-speaker').value = doc.speaker || '';
1001
+ if (s('.action-engine-dialogue-text')) s('.action-engine-dialogue-text').value = doc.text || '';
1002
+ if (s('.action-engine-dialogue-mood')) s('.action-engine-dialogue-mood').value = doc.mood || 'neutral';
1003
+ NotificationManager.Push({ html: `Dialogue line "${doc.code}" loaded`, status: 'success' });
1004
+ }
1005
+
1006
+ static resetDialogue() {
1007
+ ActionEngineCyberia.currentDialogueId = null;
1008
+ if (s('.action-engine-dialogue-code')) s('.action-engine-dialogue-code').value = '';
1009
+ if (s('.action-engine-dialogue-order')) s('.action-engine-dialogue-order').value = 0;
1010
+ if (s('.action-engine-dialogue-speaker')) s('.action-engine-dialogue-speaker').value = '';
1011
+ if (s('.action-engine-dialogue-text')) s('.action-engine-dialogue-text').value = '';
1012
+ if (s('.action-engine-dialogue-mood')) s('.action-engine-dialogue-mood').value = 'neutral';
1013
+ }
1014
+
1015
+ static async saveDialogue() {
1016
+ const body = ActionEngineCyberia.getDialoguePayload();
1017
+ if (!body.code || !body.text) {
1018
+ NotificationManager.Push({ html: 'Dialogue code and text are required.', status: 'error' });
1019
+ return;
1020
+ }
1021
+ const isUpdate = !!ActionEngineCyberia.currentDialogueId;
1022
+ const result = isUpdate
1023
+ ? await CyberiaDialogueService.put({ id: ActionEngineCyberia.currentDialogueId, body })
1024
+ : await CyberiaDialogueService.post({ body });
1025
+ ActionEngineCyberia.notifyResult(result, isUpdate);
1026
+ if (result.status === 'success') {
1027
+ if (result.data?._id) ActionEngineCyberia.currentDialogueId = result.data._id;
1028
+ await ActionEngineCyberia.refreshDialogueList();
1029
+ }
1030
+ }
1031
+
1032
+ static async deleteDialogue(id) {
1033
+ const targetId = id || ActionEngineCyberia.currentDialogueId;
1034
+ if (!targetId) return;
1035
+ const result = await CyberiaDialogueService.delete({ id: targetId });
1036
+ NotificationManager.Push({
1037
+ html: result.status === 'error' ? result.message : Translate.instance('item-success-delete'),
1038
+ status: result.status,
1039
+ });
1040
+ if (result.status === 'success') {
1041
+ if (targetId === ActionEngineCyberia.currentDialogueId) ActionEngineCyberia.resetDialogue();
1042
+ await ActionEngineCyberia.refreshDialogueList();
1043
+ }
1044
+ }
1045
+
1046
+ // ── Skill list editor + persistence (own model: CyberiaSkill) ────────────
1047
+ static async renderSkillDefList() {
1048
+ const container = s('.action-engine-skill-def-list');
1049
+ if (!container) return;
1050
+ let out = '';
1051
+ for (let i = 0; i < ActionEngineCyberia.skillDefs.length; i++) {
1052
+ const sk = ActionEngineCyberia.skillDefs[i];
1053
+ out += html`<div
1054
+ class="fl"
1055
+ style="border-bottom:1px solid ${subtleBorder()};padding:3px 0;align-items:center;font-size:12px;font-family:monospace;"
1056
+ >
1057
+ <div class="in fll" style="flex:1;">
1058
+ <span style="color:${darkTheme ? '#8cf' : '#246'};">${sk.logicEventId}</span>${sk.name
1059
+ ? ` · ${sk.name}`
1060
+ : ''}${sk.summonedEntityItemId
1061
+ ? html`<span style="margin:0 4px;">&rarr;</span>${sk.summonedEntityItemId}`
1062
+ : ''}
1063
+ </div>
1064
+ ${await BtnIcon.instance({
1065
+ class: `btn-aes-rmskill-${i}`,
1066
+ label: html`<i class="fa-solid fa-xmark"></i>`,
1067
+ style: 'min-width:30px;padding:2px 8px;',
1068
+ })}
1069
+ </div>`;
1070
+ }
1071
+ if (!out) out = '<div style="color:#888;font-size:12px;">No skills. Add at least one logic event.</div>';
1072
+ htmls('.action-engine-skill-def-list', out);
1073
+ for (let i = 0; i < ActionEngineCyberia.skillDefs.length; i++) {
1074
+ if (s(`.btn-aes-rmskill-${i}`))
1075
+ s(`.btn-aes-rmskill-${i}`).onclick = () => {
1076
+ ActionEngineCyberia.skillDefs.splice(i, 1);
1077
+ ActionEngineCyberia.renderSkillDefList();
1078
+ };
1079
+ }
1080
+ }
1081
+
1082
+ static addSkillDef() {
1083
+ const logicEventId = ActionEngineCyberia.getSingleDropdownValue('action-engine-skill-logic-event');
1084
+ const name = s('.action-engine-skill-name')?.value?.trim() || '';
1085
+ const description = s('.action-engine-skill-description')?.value || '';
1086
+ const summonedEntityItemId = ActionEngineCyberia.getSingleDropdownValue(
1087
+ ActionEngineCyberia.ids.skillSummonedItemPicker,
1088
+ );
1089
+ if (!logicEventId) {
1090
+ NotificationManager.Push({ html: 'Select a canonical logic event id first.', status: 'error' });
1091
+ return;
1092
+ }
1093
+ if (ActionEngineCyberia.skillDefs.some((sk) => sk.logicEventId === logicEventId)) {
1094
+ NotificationManager.Push({ html: `Logic event "${logicEventId}" already added.`, status: 'error' });
1095
+ return;
1096
+ }
1097
+ ActionEngineCyberia.skillDefs.push({ logicEventId, name, description, summonedEntityItemId });
1098
+ ActionEngineCyberia.setSingleDropdownValue('action-engine-skill-logic-event', '');
1099
+ for (const cls of ['action-engine-skill-name', 'action-engine-skill-description'])
1100
+ if (s(`.${cls}`)) s(`.${cls}`).value = '';
1101
+ ActionEngineCyberia.setSingleDropdownValue(ActionEngineCyberia.ids.skillSummonedItemPicker, '');
1102
+ ActionEngineCyberia.renderSkillDefList();
1103
+ }
1104
+
1105
+ static async renderSkillCards() {
1106
+ const container = s('.action-engine-skill-cards');
1107
+ if (!container) return;
1108
+ const filter = (s('.action-engine-skill-search')?.value || '').toLowerCase();
1109
+ const items = ActionEngineCyberia.skillListCache.filter(
1110
+ (d) =>
1111
+ !filter ||
1112
+ (d.triggerItemId || '').toLowerCase().includes(filter) ||
1113
+ (d.logicEventIds || []).some((l) => (l || '').toLowerCase().includes(filter)),
1114
+ );
1115
+ let out = '';
1116
+ for (const doc of items) {
1117
+ const [loadBtn, delBtn] = await ActionEngineCyberia.cardActionsHtml(
1118
+ `btn-aes-load-${doc._id}`,
1119
+ `btn-aes-del-${doc._id}`,
1120
+ );
1121
+ out += ActionEngineCyberia.cardWrap(
1122
+ html`<div class="fl" style="align-items:center;">
1123
+ <div class="in fll" style="flex:1;">
1124
+ <div style="font-weight:bold;font-family:monospace;font-size:12px;">${doc.triggerItemId}</div>
1125
+ <div style="font-size:11px;color:#888;">${(doc.logicEventIds || []).join(', ') || '—'}</div>
1126
+ <div style="font-size:11px;color:#888;">${(doc.skills || []).length} skill(s)</div>
1127
+ </div>
1128
+ <div class="in fll" style="display:flex;gap:4px;">${loadBtn}${delBtn}</div>
1129
+ </div>`,
1130
+ );
1131
+ }
1132
+ if (!out) out = '<div style="color:#888;font-size:12px;">No skills found.</div>';
1133
+ htmls('.action-engine-skill-cards', out);
1134
+ for (const doc of items) {
1135
+ if (s(`.btn-aes-load-${doc._id}`))
1136
+ s(`.btn-aes-load-${doc._id}`).onclick = async () => {
1137
+ const res = await CyberiaSkillService.get({ id: doc._id });
1138
+ if (res.status === 'success' && res.data) ActionEngineCyberia.loadSkill(res.data);
1139
+ };
1140
+ if (s(`.btn-aes-del-${doc._id}`))
1141
+ s(`.btn-aes-del-${doc._id}`).onclick = () => ActionEngineCyberia.deleteSkill(doc._id);
1142
+ }
1143
+ }
1144
+
1145
+ static async refreshSkillList() {
1146
+ const res = await CyberiaSkillService.get({ limit: 500 });
1147
+ ActionEngineCyberia.skillListCache = res?.data?.data || [];
1148
+ await ActionEngineCyberia.renderSkillCards();
1149
+ }
1150
+
1151
+ static getSkillPayload() {
1152
+ const triggerItemId = ActionEngineCyberia.getSingleDropdownValue(ActionEngineCyberia.ids.skillTriggerItemPicker);
1153
+ const skills = ActionEngineCyberia.skillDefs.map((sk) => ({
1154
+ logicEventId: sk.logicEventId,
1155
+ name: sk.name || '',
1156
+ description: sk.description || '',
1157
+ summonedEntityItemId: sk.summonedEntityItemId || '',
1158
+ }));
1159
+ // logicEventIds is the compact discriminator list — derived from the skills
1160
+ // so the two can never drift (matches DefaultSkillConfig).
1161
+ const logicEventIds = [...new Set(skills.map((sk) => sk.logicEventId).filter(Boolean))];
1162
+ return { triggerItemId, logicEventIds, skills };
1163
+ }
1164
+
1165
+ static loadSkill(doc) {
1166
+ ActionEngineCyberia.setMode('skill');
1167
+ ActionEngineCyberia.currentSkillId = doc._id || null;
1168
+ ActionEngineCyberia.setSingleDropdownValue(ActionEngineCyberia.ids.skillTriggerItemPicker, doc.triggerItemId || '');
1169
+ ActionEngineCyberia.skillDefs = (doc.skills || []).map((sk) => ({
1170
+ logicEventId: sk.logicEventId,
1171
+ name: sk.name || '',
1172
+ description: sk.description || '',
1173
+ summonedEntityItemId: sk.summonedEntityItemId || '',
1174
+ }));
1175
+ ActionEngineCyberia.renderSkillDefList();
1176
+ NotificationManager.Push({ html: `Skill "${doc.triggerItemId}" loaded`, status: 'success' });
1177
+ }
1178
+
1179
+ static resetSkill() {
1180
+ ActionEngineCyberia.currentSkillId = null;
1181
+ ActionEngineCyberia.skillDefs = [];
1182
+ ActionEngineCyberia.setSingleDropdownValue(ActionEngineCyberia.ids.skillTriggerItemPicker, '');
1183
+ ActionEngineCyberia.setSingleDropdownValue(ActionEngineCyberia.ids.skillSummonedItemPicker, '');
1184
+ ActionEngineCyberia.setSingleDropdownValue('action-engine-skill-logic-event', '');
1185
+ for (const cls of ['action-engine-skill-name', 'action-engine-skill-description'])
1186
+ if (s(`.${cls}`)) s(`.${cls}`).value = '';
1187
+ ActionEngineCyberia.renderSkillDefList();
1188
+ }
1189
+
1190
+ static async saveSkill() {
1191
+ const body = ActionEngineCyberia.getSkillPayload();
1192
+ if (!body.triggerItemId) {
1193
+ NotificationManager.Push({ html: 'Trigger item id is required.', status: 'error' });
1194
+ return;
1195
+ }
1196
+ if (body.skills.length === 0) {
1197
+ NotificationManager.Push({ html: 'Add at least one skill (logic event).', status: 'error' });
1198
+ return;
1199
+ }
1200
+ const isUpdate = !!ActionEngineCyberia.currentSkillId;
1201
+ const result = isUpdate
1202
+ ? await CyberiaSkillService.put({ id: ActionEngineCyberia.currentSkillId, body })
1203
+ : await CyberiaSkillService.post({ body });
1204
+ ActionEngineCyberia.notifyResult(result, isUpdate);
1205
+ if (result.status === 'success') {
1206
+ if (result.data?._id) ActionEngineCyberia.currentSkillId = result.data._id;
1207
+ await ActionEngineCyberia.refreshSkillList();
1208
+ }
1209
+ }
1210
+
1211
+ static async deleteSkill(id) {
1212
+ const targetId = id || ActionEngineCyberia.currentSkillId;
1213
+ if (!targetId) return;
1214
+ const result = await CyberiaSkillService.delete({ id: targetId });
1215
+ NotificationManager.Push({
1216
+ html: result.status === 'error' ? result.message : Translate.instance('item-success-delete'),
1217
+ status: result.status,
1218
+ });
1219
+ if (result.status === 'success') {
1220
+ if (targetId === ActionEngineCyberia.currentSkillId) ActionEngineCyberia.resetSkill();
1221
+ await ActionEngineCyberia.refreshSkillList();
1222
+ }
1223
+ }
1224
+
1225
+ // ── Render ──────────────────────────────────────────────────────────────
1226
+ static async render(options = {}) {
1227
+ const { appStore } = options;
1228
+ const role = appStore?.Data?.user?.main?.model?.user?.role || 'guest';
1229
+ ActionEngineCyberia.canMutate = commonModeratorGuard(role);
1230
+ ActionEngineCyberia.mode = 'quest';
1231
+ ActionEngineCyberia.mapCode = '';
1232
+ ActionEngineCyberia.mapDoc = null;
1233
+ ActionEngineCyberia.imageCache = {};
1234
+ ActionEngineCyberia.assignments = { quests: [], actions: [] };
1235
+ ActionEngineCyberia.questListCache = [];
1236
+ ActionEngineCyberia.actionListCache = [];
1237
+ ActionEngineCyberia.dialogueListCache = [];
1238
+ ActionEngineCyberia.skillListCache = [];
1239
+ ActionEngineCyberia.currentQuestId = null;
1240
+ ActionEngineCyberia.currentActionId = null;
1241
+ ActionEngineCyberia.currentDialogueId = null;
1242
+ ActionEngineCyberia.currentSkillId = null;
1243
+ ActionEngineCyberia.questSteps = [];
1244
+ ActionEngineCyberia.questRewards = [];
1245
+ ActionEngineCyberia.actionQuestDialogues = [];
1246
+ ActionEngineCyberia.loadedActionPayloadExtras = {};
1247
+ ActionEngineCyberia.skillDefs = [];
1248
+
1249
+ const ids = ActionEngineCyberia.ids;
1250
+ const objectiveTypeOptions = QUEST_STEPS_TYPES.map((t) => ({ value: t, display: t, data: t, onClick: () => {} }));
1251
+
1252
+ // Responsive column groups: dynamicCol collapses these to full width on
1253
+ // narrow / mobile widths (observing .action-engine-container).
1254
+ const cont = 'action-engine-container';
1255
+ const dc = {
1256
+ cell: 'action-engine-dc-cell',
1257
+ questCt: 'action-engine-dc-quest-ct',
1258
+ questChain: 'action-engine-dc-quest-chain',
1259
+ objective: 'action-engine-dc-objective',
1260
+ reward: 'action-engine-dc-reward',
1261
+ actionCl: 'action-engine-dc-action-cl',
1262
+ actionQd: 'action-engine-dc-action-qd',
1263
+ dialogue: 'action-engine-dc-dialogue',
1264
+ skillDef: 'action-engine-dc-skill-def',
1265
+ };
1266
+
1267
+ const group = (title, icon, inner) =>
1268
+ html`<div class="in" style="border:1px solid ${groupBorder()};border-radius:8px;padding:12px;margin-bottom:14px;">
1269
+ <div
1270
+ class="in"
1271
+ style="font-size:12px;font-weight:bold;text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px;opacity:.85;"
1272
+ >
1273
+ <i class="${icon}"></i> ${title}
1274
+ </div>
1275
+ ${inner}
1276
+ </div>`;
1277
+
1278
+ // Buttons grow equally and wrap to their own line once they drop below the
1279
+ // flex-basis, which keeps them tappable on narrow / mobile widths.
1280
+ const crudButtons = async (prefix, includeClone) =>
1281
+ html`<div class="fl" style="margin-top:4px;flex-wrap:wrap;">
1282
+ ${ActionEngineCyberia.canMutate
1283
+ ? html`<div class="in fll" style="flex:1 1 120px;padding:3px;">
1284
+ ${await BtnIcon.instance({
1285
+ class: `wfa btn-action-engine-${prefix}-save`,
1286
+ label: html`<i class="fa-solid fa-floppy-disk"></i> Save`,
1287
+ })}
1288
+ </div>
1289
+ ${includeClone
1290
+ ? html`<div class="in fll" style="flex:1 1 120px;padding:3px;">
1291
+ ${await BtnIcon.instance({
1292
+ class: `wfa btn-action-engine-${prefix}-clone`,
1293
+ label: html`<i class="fa-solid fa-clone"></i> Clone`,
1294
+ })}
1295
+ </div>`
1296
+ : ''}
1297
+ <div class="in fll" style="flex:1 1 120px;padding:3px;">
1298
+ ${await BtnIcon.instance({
1299
+ class: `wfa btn-action-engine-${prefix}-delete`,
1300
+ label: html`<i class="fa-solid fa-trash"></i> Delete`,
1301
+ })}
1302
+ </div>`
1303
+ : ''}
1304
+ <div class="in fll" style="flex:1 1 120px;padding:3px;">
1305
+ ${await BtnIcon.instance({
1306
+ class: `wfa btn-action-engine-${prefix}-new`,
1307
+ label: html`<i class="fa-solid fa-file"></i> New`,
1308
+ })}
1309
+ </div>
1310
+ </div>`;
1311
+
1312
+ const listToolbar = async (prefix) =>
1313
+ html`<div class="fl" style="align-items:flex-end;margin-bottom:8px;flex-wrap:wrap;">
1314
+ <div class="in fll" style="flex:1 1 160px;">
1315
+ ${await Input.instance({
1316
+ id: `action-engine-${prefix}-search`,
1317
+ label: html`Search`,
1318
+ containerClass: 'inl',
1319
+ type: 'text',
1320
+ placeholder: true,
1321
+ })}
1322
+ </div>
1323
+ <div class="in fll" style="padding-left:6px;">
1324
+ ${await BtnIcon.instance({
1325
+ class: `wfa btn-action-engine-${prefix}-refresh`,
1326
+ label: html`<i class="fa-solid fa-rotate"></i> Refresh`,
1327
+ })}
1328
+ </div>
1329
+ </div>`;
1330
+
1331
+ setTimeout(async () => {
1332
+ ThemeEvents['action-engine-theme'] = () => {
1333
+ ActionEngineCyberia.renderMapCanvas();
1334
+ ActionEngineCyberia.renderAssignmentReview();
1335
+ };
1336
+
1337
+ for (const m of ['quest', 'action', 'dialogue', 'skill'])
1338
+ if (s(`.action-engine-tab-${m}`)) s(`.action-engine-tab-${m}`).onclick = () => ActionEngineCyberia.setMode(m);
1339
+ ActionEngineCyberia.setMode('quest');
1340
+
1341
+ if (s('.btn-action-engine-load-map'))
1342
+ s('.btn-action-engine-load-map').onclick = () =>
1343
+ ActionEngineCyberia.loadMap(ActionEngineCyberia.getSingleDropdownValue(ids.mapDropdown));
1344
+
1345
+ const canvas = s('.action-engine-canvas');
1346
+ if (canvas)
1347
+ canvas.onclick = (e) => {
1348
+ if (!ActionEngineCyberia.mapDoc) return;
1349
+ const rect = canvas.getBoundingClientRect();
1350
+ const { cellW, cellH } = ActionEngineCyberia.getMapGeometry();
1351
+ const col = Math.floor(((e.clientX - rect.left) * (canvas.width / rect.width)) / cellW);
1352
+ const row = Math.floor(((e.clientY - rect.top) * (canvas.height / rect.height)) / cellH);
1353
+ if (s('.action-engine-cell-x')) s('.action-engine-cell-x').value = col;
1354
+ if (s('.action-engine-cell-y')) s('.action-engine-cell-y').value = row;
1355
+ const mapField =
1356
+ ActionEngineCyberia.mode === 'action' ? '.action-engine-action-map-code' : '.action-engine-quest-map-code';
1357
+ if (s(mapField)) s(mapField).value = ActionEngineCyberia.mapCode;
1358
+ htmls('.action-engine-cell-coords', `Cell: (${col}, ${row})`);
1359
+ ActionEngineCyberia.renderMapCanvas();
1360
+ };
1361
+
1362
+ // Quest wiring
1363
+ if (s('.btn-action-engine-add-step'))
1364
+ s('.btn-action-engine-add-step').onclick = () => ActionEngineCyberia.addStep();
1365
+ if (s('.btn-action-engine-add-objective'))
1366
+ s('.btn-action-engine-add-objective').onclick = () => ActionEngineCyberia.addObjective();
1367
+ if (s('.btn-action-engine-add-reward'))
1368
+ s('.btn-action-engine-add-reward').onclick = () => ActionEngineCyberia.addReward();
1369
+ if (s('.btn-action-engine-quest-save'))
1370
+ s('.btn-action-engine-quest-save').onclick = () => ActionEngineCyberia.saveQuest();
1371
+ if (s('.btn-action-engine-quest-clone'))
1372
+ s('.btn-action-engine-quest-clone').onclick = () => ActionEngineCyberia.cloneQuest();
1373
+ if (s('.btn-action-engine-quest-delete'))
1374
+ s('.btn-action-engine-quest-delete').onclick = () => ActionEngineCyberia.deleteQuest();
1375
+ if (s('.btn-action-engine-quest-new'))
1376
+ s('.btn-action-engine-quest-new').onclick = () => ActionEngineCyberia.resetQuest();
1377
+ if (s('.btn-action-engine-quest-refresh'))
1378
+ s('.btn-action-engine-quest-refresh').onclick = () => ActionEngineCyberia.refreshQuestList();
1379
+ if (s('.action-engine-quest-search'))
1380
+ s('.action-engine-quest-search').addEventListener('input', () => ActionEngineCyberia.renderQuestCards());
1381
+
1382
+ // Action wiring
1383
+ if (s('.btn-action-engine-add-quest-dialogue'))
1384
+ s('.btn-action-engine-add-quest-dialogue').onclick = () => {
1385
+ const questCode = s('.action-engine-action-quest-code')?.value?.trim();
1386
+ const dialogCode = ActionEngineCyberia.getSingleDropdownValue(ids.actionQuestDialogPicker);
1387
+ if (!questCode || !dialogCode) {
1388
+ NotificationManager.Push({ html: 'Quest code and dialog code are required.', status: 'error' });
1389
+ return;
1390
+ }
1391
+ ActionEngineCyberia.actionQuestDialogues.push({ questCode, dialogCode });
1392
+ ActionEngineCyberia.renderQuestDialogueList();
1393
+ };
1394
+ if (s('.btn-action-engine-action-save'))
1395
+ s('.btn-action-engine-action-save').onclick = () => ActionEngineCyberia.saveAction();
1396
+ if (s('.btn-action-engine-action-clone'))
1397
+ s('.btn-action-engine-action-clone').onclick = () => ActionEngineCyberia.cloneAction();
1398
+ if (s('.btn-action-engine-action-delete'))
1399
+ s('.btn-action-engine-action-delete').onclick = () => ActionEngineCyberia.deleteAction();
1400
+ if (s('.btn-action-engine-action-new'))
1401
+ s('.btn-action-engine-action-new').onclick = () => ActionEngineCyberia.resetAction();
1402
+ if (s('.btn-action-engine-action-refresh'))
1403
+ s('.btn-action-engine-action-refresh').onclick = () => ActionEngineCyberia.refreshActionList();
1404
+ if (s('.action-engine-action-search'))
1405
+ s('.action-engine-action-search').addEventListener('input', () => ActionEngineCyberia.renderActionCards());
1406
+
1407
+ // Dialogue wiring
1408
+ if (s('.btn-action-engine-dialogue-save'))
1409
+ s('.btn-action-engine-dialogue-save').onclick = () => ActionEngineCyberia.saveDialogue();
1410
+ if (s('.btn-action-engine-dialogue-delete'))
1411
+ s('.btn-action-engine-dialogue-delete').onclick = () => ActionEngineCyberia.deleteDialogue();
1412
+ if (s('.btn-action-engine-dialogue-new'))
1413
+ s('.btn-action-engine-dialogue-new').onclick = () => ActionEngineCyberia.resetDialogue();
1414
+ if (s('.btn-action-engine-dialogue-refresh'))
1415
+ s('.btn-action-engine-dialogue-refresh').onclick = () => ActionEngineCyberia.refreshDialogueList();
1416
+ if (s('.action-engine-dialogue-search'))
1417
+ s('.action-engine-dialogue-search').addEventListener('input', () => ActionEngineCyberia.renderDialogueCards());
1418
+
1419
+ // Skill wiring
1420
+ if (s('.btn-action-engine-add-skill-def'))
1421
+ s('.btn-action-engine-add-skill-def').onclick = () => ActionEngineCyberia.addSkillDef();
1422
+ if (s('.btn-action-engine-skill-save'))
1423
+ s('.btn-action-engine-skill-save').onclick = () => ActionEngineCyberia.saveSkill();
1424
+ if (s('.btn-action-engine-skill-delete'))
1425
+ s('.btn-action-engine-skill-delete').onclick = () => ActionEngineCyberia.deleteSkill();
1426
+ if (s('.btn-action-engine-skill-new'))
1427
+ s('.btn-action-engine-skill-new').onclick = () => ActionEngineCyberia.resetSkill();
1428
+ if (s('.btn-action-engine-skill-refresh'))
1429
+ s('.btn-action-engine-skill-refresh').onclick = () => ActionEngineCyberia.refreshSkillList();
1430
+ if (s('.action-engine-skill-search'))
1431
+ s('.action-engine-skill-search').addEventListener('input', () => ActionEngineCyberia.renderSkillCards());
1432
+
1433
+ ActionEngineCyberia.renderStepList();
1434
+ ActionEngineCyberia.renderRewardList();
1435
+ ActionEngineCyberia.renderQuestDialogueList();
1436
+ ActionEngineCyberia.renderSkillDefList();
1437
+ ActionEngineCyberia.renderAssignmentReview();
1438
+ await ActionEngineCyberia.refreshQuestList();
1439
+ await ActionEngineCyberia.refreshActionList();
1440
+ await ActionEngineCyberia.refreshDialogueList();
1441
+ await ActionEngineCyberia.refreshSkillList();
1442
+ });
1443
+
1444
+ const tabBtn = (mode, label) =>
1445
+ html`<div
1446
+ class="in fll action-engine-tab-${mode}"
1447
+ style="cursor:pointer;padding:9px 16px;font-size:13px;border-bottom:3px solid transparent;text-align:center;"
1448
+ >
1449
+ ${label}
1450
+ </div>`;
1451
+
1452
+ return html`<div class="in section-mp action-engine-container">
1453
+ ${group(
1454
+ 'Map Placement',
1455
+ 'fa-solid fa-map-location-dot',
1456
+ html`<div class="fl" style="align-items:flex-end;">
1457
+ <div class="in fll" style="flex:1;">${await ActionEngineCyberia.buildMapDropdown(ids.mapDropdown)}</div>
1458
+ <div class="in fll" style="padding-left:6px;">
1459
+ ${await BtnIcon.instance({
1460
+ class: 'wfa btn-action-engine-load-map',
1461
+ label: html`<i class="fa-solid fa-download"></i> Load Map`,
1462
+ })}
1463
+ </div>
1464
+ </div>
1465
+ <div
1466
+ class="in action-engine-map-meta"
1467
+ style="font-size:12px;color:#888;font-family:monospace;margin:6px 0 2px;"
1468
+ ></div>
1469
+ <div class="in action-engine-cell-coords" style="font-size:12px;font-family:monospace;margin-bottom:6px;">
1470
+ Cell: (—, —)
1471
+ </div>
1472
+ <div class="in" style="overflow:auto;max-width:100%;border:1px solid ${subtleBorder()};border-radius:4px;">
1473
+ <canvas class="action-engine-canvas" style="display:block;cursor:crosshair;"></canvas>
1474
+ </div>
1475
+ ${dynamicCol({ containerSelector: cont, id: dc.cell, type: 'a-50-b-50' })}
1476
+ <div class="fl" style="margin-top:8px;">
1477
+ <div class="in fll ${dc.cell}-col-a" style="padding-right:4px;">
1478
+ ${await Input.instance({
1479
+ id: 'action-engine-cell-x',
1480
+ label: html`Cell X`,
1481
+ containerClass: 'inl',
1482
+ type: 'number',
1483
+ min: 0,
1484
+ })}
1485
+ </div>
1486
+ <div class="in fll ${dc.cell}-col-b" style="padding-left:4px;">
1487
+ ${await Input.instance({
1488
+ id: 'action-engine-cell-y',
1489
+ label: html`Cell Y`,
1490
+ containerClass: 'inl',
1491
+ type: 'number',
1492
+ min: 0,
1493
+ })}
1494
+ </div>
1495
+ </div>
1496
+ <div class="in input-label" style="font-size:12px;margin:10px 0 4px;opacity:.8;">
1497
+ <i class="fa-solid fa-list-check"></i> Assignments on map
1498
+ </div>
1499
+ <div class="in action-engine-assignment-review" style="max-height:140px;overflow-y:auto;"></div>`,
1500
+ )}
1501
+
1502
+ <div class="fl" style="border-bottom:1px solid ${groupBorder()};margin-bottom:14px;">
1503
+ ${tabBtn('quest', html`<i class="fa-solid fa-scroll"></i> Quests`)}
1504
+ ${tabBtn('action', html`<i class="fa-solid fa-handshake"></i> Actions`)}
1505
+ ${tabBtn('dialogue', html`<i class="fa-solid fa-comments"></i> Dialogues`)}
1506
+ ${tabBtn('skill', html`<i class="fa-solid fa-wand-sparkles"></i> Skills`)}
1507
+ </div>
1508
+
1509
+ <!-- Quest panel -->
1510
+ <div class="in action-engine-panel-quest">
1511
+ ${group(
1512
+ 'Quest Details',
1513
+ 'fa-solid fa-circle-info',
1514
+ html`${dynamicCol({ containerSelector: cont, id: dc.questCt, type: 'a-50-b-50' })}
1515
+ <div class="fl">
1516
+ <div class="in fll ${dc.questCt}-col-a" style="padding-right:4px;">
1517
+ ${await Input.instance({
1518
+ id: 'action-engine-quest-code',
1519
+ label: html`Quest Code`,
1520
+ containerClass: 'inl',
1521
+ type: 'text',
1522
+ })}
1523
+ </div>
1524
+ <div class="in fll ${dc.questCt}-col-b" style="padding-left:4px;">
1525
+ ${await Input.instance({
1526
+ id: 'action-engine-quest-title',
1527
+ label: html`Title`,
1528
+ containerClass: 'inl',
1529
+ type: 'text',
1530
+ })}
1531
+ </div>
1532
+ </div>
1533
+ <div class="in" style="margin-top:6px;">
1534
+ ${await Input.instance({
1535
+ id: 'action-engine-quest-description',
1536
+ label: html`Description`,
1537
+ containerClass: 'inl',
1538
+ type: 'text',
1539
+ })}
1540
+ </div>
1541
+ <div class="in" style="margin-top:6px;">
1542
+ ${await Input.instance({
1543
+ id: 'action-engine-quest-map-code',
1544
+ label: html`Source Map Code (click a cell to assign)`,
1545
+ containerClass: 'inl',
1546
+ type: 'text',
1547
+ })}
1548
+ </div>`,
1549
+ )}
1550
+ ${group(
1551
+ 'Quest Chain',
1552
+ 'fa-solid fa-diagram-project',
1553
+ html`${dynamicCol({ containerSelector: cont, id: dc.questChain, type: 'a-50-b-50' })}
1554
+ <div class="fl">
1555
+ <div class="in fll ${dc.questChain}-col-a" style="padding-right:4px;">
1556
+ ${await Input.instance({
1557
+ id: 'action-engine-quest-prerequisites',
1558
+ label: html`Prerequisite Codes (csv)`,
1559
+ containerClass: 'inl',
1560
+ type: 'text',
1561
+ })}
1562
+ </div>
1563
+ <div class="in fll ${dc.questChain}-col-b" style="padding-left:4px;">
1564
+ ${await Input.instance({
1565
+ id: 'action-engine-quest-unlocks',
1566
+ label: html`Unlocks Codes (csv)`,
1567
+ containerClass: 'inl',
1568
+ type: 'text',
1569
+ })}
1570
+ </div>
1571
+ </div>`,
1572
+ )}
1573
+ ${group(
1574
+ 'Steps & Objectives',
1575
+ 'fa-solid fa-list-ol',
1576
+ html`<div class="in" style="margin-bottom:8px;">
1577
+ ${await BtnIcon.instance({
1578
+ class: 'wfa btn-action-engine-add-step',
1579
+ label: html`<i class="fa-solid fa-plus"></i> Add Step`,
1580
+ })}
1581
+ </div>
1582
+ <div class="in action-engine-step-list" style="margin-bottom:10px;"></div>
1583
+ <div class="in input-label" style="font-size:12px;margin-bottom:6px;opacity:.8;">Add objective to step</div>
1584
+ ${dynamicCol({ containerSelector: cont, id: dc.objective, type: 'search-inputs' })}
1585
+ <div class="fl" style="align-items:flex-end;">
1586
+ <div class="in fll ${dc.objective}-col-a" style="padding-right:4px;">
1587
+ ${await Input.instance({
1588
+ id: 'action-engine-objective-step',
1589
+ label: html`Step #`,
1590
+ containerClass: 'inl',
1591
+ type: 'number',
1592
+ min: 1,
1593
+ value: 1,
1594
+ })}
1595
+ </div>
1596
+ <div class="in fll ${dc.objective}-col-b" style="padding-right:4px;">
1597
+ ${await DropDown.instance({
1598
+ id: ids.objectiveType,
1599
+ label: html`Type`,
1600
+ data: objectiveTypeOptions,
1601
+ value: QUEST_STEPS_TYPES[0],
1602
+ containerClass: 'inl',
1603
+ })}
1604
+ </div>
1605
+ <div class="in fll ${dc.objective}-col-c" style="padding-right:4px;">
1606
+ ${await Input.instance({
1607
+ id: 'action-engine-objective-qty',
1608
+ label: html`Qty`,
1609
+ containerClass: 'inl',
1610
+ type: 'number',
1611
+ min: 1,
1612
+ value: 1,
1613
+ })}
1614
+ </div>
1615
+ </div>
1616
+ <div class="in" style="margin-top:6px;">
1617
+ ${await ActionEngineCyberia.buildItemIdDropdown(ids.objectiveItemPicker, html`Search itemId`)}
1618
+ </div>
1619
+ <div class="in" style="margin-top:6px;">
1620
+ ${await BtnIcon.instance({
1621
+ class: 'wfa btn-action-engine-add-objective',
1622
+ label: html`<i class="fa-solid fa-plus"></i> Add Objective`,
1623
+ })}
1624
+ </div>`,
1625
+ )}
1626
+ ${group(
1627
+ 'Rewards',
1628
+ 'fa-solid fa-gift',
1629
+ html`${dynamicCol({ containerSelector: cont, id: dc.reward, type: 'default' })}
1630
+ <div class="fl" style="align-items:flex-end;">
1631
+ <div class="in fll ${dc.reward}-col-a" style="padding-right:4px;">
1632
+ ${await Input.instance({
1633
+ id: 'action-engine-reward-qty',
1634
+ label: html`Qty`,
1635
+ containerClass: 'inl',
1636
+ type: 'number',
1637
+ min: 1,
1638
+ value: 1,
1639
+ })}
1640
+ </div>
1641
+ <div class="in fll ${dc.reward}-col-b">
1642
+ ${await ActionEngineCyberia.buildItemIdDropdown(ids.rewardItemPicker, html`Search itemId`)}
1643
+ </div>
1644
+ </div>
1645
+ <div class="in" style="margin-top:6px;">
1646
+ ${await BtnIcon.instance({
1647
+ class: 'wfa btn-action-engine-add-reward',
1648
+ label: html`<i class="fa-solid fa-plus"></i> Add Reward`,
1649
+ })}
1650
+ </div>
1651
+ <div class="in action-engine-reward-list" style="margin-top:8px;"></div>`,
1652
+ )}
1653
+ ${group('Save Quest', 'fa-solid fa-floppy-disk', await crudButtons('quest', true))}
1654
+ ${group(
1655
+ 'Existing Quests',
1656
+ 'fa-solid fa-scroll',
1657
+ html`${await listToolbar('quest')}
1658
+ <div class="in action-engine-quest-cards" style="max-height:300px;overflow-y:auto;"></div>`,
1659
+ )}
1660
+ </div>
1661
+
1662
+ <!-- Action panel -->
1663
+ <div class="in action-engine-panel-action hide">
1664
+ ${group(
1665
+ 'Action Details',
1666
+ 'fa-solid fa-circle-info',
1667
+ html`${dynamicCol({ containerSelector: cont, id: dc.actionCl, type: 'a-50-b-50' })}
1668
+ <div class="fl">
1669
+ <div class="in fll ${dc.actionCl}-col-a" style="padding-right:4px;">
1670
+ ${await Input.instance({
1671
+ id: 'action-engine-action-code',
1672
+ label: html`Action Code`,
1673
+ containerClass: 'inl',
1674
+ type: 'text',
1675
+ })}
1676
+ </div>
1677
+ <div class="in fll ${dc.actionCl}-col-b" style="padding-left:4px;">
1678
+ ${await Input.instance({
1679
+ id: 'action-engine-action-label',
1680
+ label: html`Label (nameplate)`,
1681
+ containerClass: 'inl',
1682
+ type: 'text',
1683
+ })}
1684
+ </div>
1685
+ </div>
1686
+ <div class="in" style="margin-top:6px;">
1687
+ ${await Input.instance({
1688
+ id: 'action-engine-action-map-code',
1689
+ label: html`Source Map Code (click a cell to assign)`,
1690
+ containerClass: 'inl',
1691
+ type: 'text',
1692
+ })}
1693
+ </div>
1694
+ <div class="in" style="margin-top:8px;">
1695
+ ${await ActionEngineCyberia.buildDialogCodeDropdown(
1696
+ ids.actionDialogPicker,
1697
+ html`Default greeting dialog code`,
1698
+ )}
1699
+ </div>`,
1700
+ )}
1701
+ ${group(
1702
+ 'Per-quest Dialogue Mapping',
1703
+ 'fa-solid fa-comments',
1704
+ html`${dynamicCol({ containerSelector: cont, id: dc.actionQd, type: 'a-50-b-50' })}
1705
+ <div class="fl" style="align-items:flex-end;">
1706
+ <div class="in fll ${dc.actionQd}-col-a" style="padding-right:4px;">
1707
+ ${await Input.instance({
1708
+ id: 'action-engine-action-quest-code',
1709
+ label: html`Quest Code`,
1710
+ containerClass: 'inl',
1711
+ type: 'text',
1712
+ })}
1713
+ </div>
1714
+ <div class="in fll ${dc.actionQd}-col-b">
1715
+ ${await ActionEngineCyberia.buildDialogCodeDropdown(ids.actionQuestDialogPicker, html`Dialog Code`)}
1716
+ </div>
1717
+ </div>
1718
+ <div class="in" style="margin-top:6px;">
1719
+ ${await BtnIcon.instance({
1720
+ class: 'wfa btn-action-engine-add-quest-dialogue',
1721
+ label: html`<i class="fa-solid fa-plus"></i> Map Dialogue`,
1722
+ })}
1723
+ </div>
1724
+ <div class="in action-engine-quest-dialogue-list" style="margin-top:8px;"></div>`,
1725
+ )}
1726
+ ${group('Save Action', 'fa-solid fa-floppy-disk', await crudButtons('action', true))}
1727
+ ${group(
1728
+ 'Existing Actions',
1729
+ 'fa-solid fa-handshake',
1730
+ html`${await listToolbar('action')}
1731
+ <div class="in action-engine-action-cards" style="max-height:300px;overflow-y:auto;"></div>`,
1732
+ )}
1733
+ </div>
1734
+
1735
+ <!-- Dialogue panel -->
1736
+ <div class="in action-engine-panel-dialogue hide">
1737
+ ${group(
1738
+ 'Dialogue Line',
1739
+ 'fa-solid fa-comment-dots',
1740
+ html`${dynamicCol({ containerSelector: cont, id: dc.dialogue, type: 'search-inputs' })}
1741
+ <div class="fl">
1742
+ <div class="in fll ${dc.dialogue}-col-a" style="padding-right:4px;">
1743
+ ${await Input.instance({
1744
+ id: 'action-engine-dialogue-code',
1745
+ label: html`Dialogue Code`,
1746
+ containerClass: 'inl',
1747
+ type: 'text',
1748
+ })}
1749
+ </div>
1750
+ <div class="in fll ${dc.dialogue}-col-b" style="padding-right:4px;">
1751
+ ${await Input.instance({
1752
+ id: 'action-engine-dialogue-order',
1753
+ label: html`Order`,
1754
+ containerClass: 'inl',
1755
+ type: 'number',
1756
+ min: 0,
1757
+ value: 0,
1758
+ })}
1759
+ </div>
1760
+ <div class="in fll ${dc.dialogue}-col-c">
1761
+ ${await Input.instance({
1762
+ id: 'action-engine-dialogue-mood',
1763
+ label: html`Mood`,
1764
+ containerClass: 'inl',
1765
+ type: 'text',
1766
+ })}
1767
+ </div>
1768
+ </div>
1769
+ <div class="in" style="margin-top:6px;">
1770
+ ${await Input.instance({
1771
+ id: 'action-engine-dialogue-speaker',
1772
+ label: html`Speaker`,
1773
+ containerClass: 'inl',
1774
+ type: 'text',
1775
+ })}
1776
+ </div>
1777
+ <div class="in" style="margin-top:6px;">
1778
+ ${await Input.instance({
1779
+ id: 'action-engine-dialogue-text',
1780
+ label: html`Text`,
1781
+ containerClass: 'inl',
1782
+ type: 'text',
1783
+ })}
1784
+ </div>`,
1785
+ )}
1786
+ ${group('Save Dialogue Line', 'fa-solid fa-floppy-disk', await crudButtons('dialogue', false))}
1787
+ ${group(
1788
+ 'Existing Dialogue Lines',
1789
+ 'fa-solid fa-comments',
1790
+ html`${await listToolbar('dialogue')}
1791
+ <div class="in action-engine-dialogue-cards" style="max-height:300px;overflow-y:auto;"></div>`,
1792
+ )}
1793
+ </div>
1794
+
1795
+ <!-- Skill panel -->
1796
+ <div class="in action-engine-panel-skill hide">
1797
+ ${group(
1798
+ 'Skill Definition',
1799
+ 'fa-solid fa-circle-info',
1800
+ html`<div class="in input-label" style="font-size:12px;margin-bottom:6px;opacity:.8;">
1801
+ Trigger item — the ObjectLayer item whose active layer fires these skills.
1802
+ </div>
1803
+ ${await ActionEngineCyberia.buildItemIdDropdown(ids.skillTriggerItemPicker, html`Search trigger itemId`)}`,
1804
+ )}
1805
+ ${group(
1806
+ 'Logic Skills',
1807
+ 'fa-solid fa-bolt',
1808
+ html`<div class="in input-label" style="font-size:12px;margin-bottom:6px;opacity:.8;">Add logic event</div>
1809
+ ${dynamicCol({ containerSelector: cont, id: dc.skillDef, type: 'a-50-b-50' })}
1810
+ <div class="fl">
1811
+ <div class="in fll ${dc.skillDef}-col-a" style="padding-right:4px;">
1812
+ ${await DropDown.instance({
1813
+ id: 'action-engine-skill-logic-event',
1814
+ label: html`Logic Event Id`,
1815
+ containerClass: 'inl',
1816
+ data: SKILL_LOGIC_IDS.map((l) => ({
1817
+ value: l.id,
1818
+ display: l.name ? `${l.id} — ${l.name}` : l.id,
1819
+ data: l.id,
1820
+ onClick: () => {},
1821
+ })),
1822
+ })}
1823
+ </div>
1824
+ <div class="in fll ${dc.skillDef}-col-b" style="padding-left:4px;">
1825
+ ${await Input.instance({
1826
+ id: 'action-engine-skill-name',
1827
+ label: html`Name`,
1828
+ containerClass: 'inl',
1829
+ type: 'text',
1830
+ })}
1831
+ </div>
1832
+ </div>
1833
+ <div class="in" style="margin-top:6px;">
1834
+ ${await Input.instance({
1835
+ id: 'action-engine-skill-description',
1836
+ label: html`Description`,
1837
+ containerClass: 'inl',
1838
+ type: 'text',
1839
+ })}
1840
+ </div>
1841
+ <div class="in" style="margin-top:6px;">
1842
+ ${await ActionEngineCyberia.buildItemIdDropdown(
1843
+ ids.skillSummonedItemPicker,
1844
+ html`Summoned entity itemId`,
1845
+ )}
1846
+ </div>
1847
+ <div class="in" style="margin-top:6px;">
1848
+ ${await BtnIcon.instance({
1849
+ class: 'wfa btn-action-engine-add-skill-def',
1850
+ label: html`<i class="fa-solid fa-plus"></i> Add Skill`,
1851
+ })}
1852
+ </div>
1853
+ <div class="in action-engine-skill-def-list" style="margin-top:8px;"></div>`,
1854
+ )}
1855
+ ${group('Save Skill', 'fa-solid fa-floppy-disk', await crudButtons('skill', false))}
1856
+ ${group(
1857
+ 'Existing Skills',
1858
+ 'fa-solid fa-wand-sparkles',
1859
+ html`${await listToolbar('skill')}
1860
+ <div class="in action-engine-skill-cards" style="max-height:300px;overflow-y:auto;"></div>`,
1861
+ )}
1862
+ </div>
1863
+ </div>`;
1864
+ }
1865
+ }
1866
+
1867
+ export { ActionEngineCyberia };