@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,1540 @@
1
+ import { darkTheme, renderChessPattern } from '../core/Css.js';
2
+ import { NotificationManager } from '../core/NotificationManager.js';
3
+
4
+ class ObjectLayerEngineElement extends HTMLElement {
5
+ constructor() {
6
+ super();
7
+ this.attachShadow({ mode: 'open' });
8
+ this.shadowRoot.innerHTML = html`
9
+ <style>
10
+ :host {
11
+ --border: 1px solid #bbb;
12
+ --gap: 8px;
13
+ display: inline-block;
14
+ font-family:
15
+ system-ui,
16
+ -apple-system,
17
+ 'Segoe UI',
18
+ Roboto,
19
+ 'Helvetica Neue',
20
+ Arial;
21
+ }
22
+ .wrap {
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: var(--gap);
26
+ align-items: flex-start;
27
+ }
28
+ .canvas-frame {
29
+ border: var(--border);
30
+ display: inline-block;
31
+ line-height: 0;
32
+ position: relative;
33
+ background: transparent;
34
+ image-rendering: pixelated;
35
+ }
36
+ canvas.canvas-layer {
37
+ display: block;
38
+ touch-action: none;
39
+ cursor: crosshair;
40
+ }
41
+ canvas.grid-layer {
42
+ position: absolute;
43
+ left: 0;
44
+ top: 0;
45
+ pointer-events: none;
46
+ }
47
+ .toolbar {
48
+ display: flex;
49
+ gap: 8px;
50
+ flex-wrap: wrap;
51
+ align-items: center;
52
+ }
53
+ .toolbar label {
54
+ display: inline-flex;
55
+ gap: 6px;
56
+ align-items: center;
57
+ }
58
+ .group {
59
+ display: inline-flex;
60
+ gap: 6px;
61
+ align-items: center;
62
+ }
63
+ </style>
64
+
65
+ <div class="wrap">
66
+ <div class="toolbar">
67
+ <button part="undo" title="Undo" disabled>Undo</button>
68
+ <button part="redo" title="Redo" disabled>Redo</button>
69
+
70
+ <input type="color" part="color" title="Brush color" value="#000000" />
71
+ <label
72
+ >hex
73
+ <input
74
+ type="text"
75
+ part="hex-input"
76
+ title="Hex color (e.g., #FF0000 or #FF0000FF)"
77
+ placeholder="#000000FF"
78
+ style="width:9ch"
79
+ /></label>
80
+ <label
81
+ >rgba
82
+ <input type="number" part="r-input" min="0" max="255" value="0" title="Red (0-255)" style="width:5ch" />
83
+ <input type="number" part="g-input" min="0" max="255" value="0" title="Green (0-255)" style="width:5ch" />
84
+ <input type="number" part="b-input" min="0" max="255" value="0" title="Blue (0-255)" style="width:5ch" />
85
+ <input type="number" part="a-input" min="0" max="255" value="255" title="Alpha (0-255)" style="width:5ch" />
86
+ </label>
87
+ <select part="tool">
88
+ <option value="pencil">pencil</option>
89
+ <option value="eraser">eraser</option>
90
+ <option value="fill">fill</option>
91
+ <option value="eyedropper">eyedropper</option>
92
+ </select>
93
+
94
+ <label>brush <input type="number" part="brush-size" min="1" value="1" /></label>
95
+ <label>pixel-size <input type="number" part="pixel-size" min="1" value="16" /></label>
96
+
97
+ <!-- New: cell dimensions (width x height) -->
98
+ <label
99
+ >cells <input type="number" part="cell-width" min="1" value="16" style="width:6ch" /> x
100
+ <input type="number" part="cell-height" min="1" value="16" style="width:6ch"
101
+ /></label>
102
+
103
+ <label class="switch"> <input type="checkbox" part="toggle-grid" /> grid </label>
104
+
105
+ <!-- New: transform tools -->
106
+ <div class="group">
107
+ <button part="flip-h" title="Flip horizontally">Flip H</button>
108
+ <button part="flip-v" title="Flip vertically">Flip V</button>
109
+ <button part="rot-ccw" title="Rotate -90°">⟲</button>
110
+ <button part="rot-cw" title="Rotate +90°">⟳</button>
111
+ </div>
112
+
113
+ <label
114
+ >opacity <input type="range" part="opacity" min="0" max="255" value="255" style="width:10rem" /><input
115
+ type="number"
116
+ part="opacity-num"
117
+ min="0"
118
+ max="255"
119
+ value="255"
120
+ style="width:5ch;margin-left:4px"
121
+ /></label>
122
+
123
+ <button part="clear" title="Clear (make fully transparent)">Clear</button>
124
+
125
+ <button part="export">Export PNG</button>
126
+ <button part="export-json">Export JSON</button>
127
+ <button part="import-json">Import JSON</button>
128
+ </div>
129
+ <div class="canvas-frame" style="${renderChessPattern()}">
130
+ <canvas part="canvas" class="canvas-layer"></canvas>
131
+ <canvas part="grid" class="grid-layer"></canvas>
132
+ </div>
133
+ </div>
134
+ `;
135
+
136
+ // DOM
137
+ this._pixelCanvas = this.shadowRoot.querySelector('canvas[part="canvas"]');
138
+ this._gridCanvas = this.shadowRoot.querySelector('canvas[part="grid"]');
139
+ this._colorInput = this.shadowRoot.querySelector('input[part="color"]');
140
+ this._hexInput = this.shadowRoot.querySelector('input[part="hex-input"]');
141
+ this._rInput = this.shadowRoot.querySelector('input[part="r-input"]');
142
+ this._gInput = this.shadowRoot.querySelector('input[part="g-input"]');
143
+ this._bInput = this.shadowRoot.querySelector('input[part="b-input"]');
144
+ this._aInput = this.shadowRoot.querySelector('input[part="a-input"]');
145
+ this._toolSelect = this.shadowRoot.querySelector('select[part="tool"]');
146
+ this._brushSizeInput = this.shadowRoot.querySelector('input[part="brush-size"]');
147
+ this._pixelSizeInput = this.shadowRoot.querySelector('input[part="pixel-size"]');
148
+ this._exportBtn = this.shadowRoot.querySelector('button[part="export"]');
149
+ this._exportJsonBtn = this.shadowRoot.querySelector('button[part="export-json"]');
150
+ this._importJsonBtn = this.shadowRoot.querySelector('button[part="import-json"]');
151
+ this._toggleGrid = this.shadowRoot.querySelector('input[part="toggle-grid"]');
152
+
153
+ // new controls
154
+ this._widthInput = this.shadowRoot.querySelector('input[part="cell-width"]');
155
+ this._heightInput = this.shadowRoot.querySelector('input[part="cell-height"]');
156
+ this._flipHBtn = this.shadowRoot.querySelector('button[part="flip-h"]');
157
+ this._flipVBtn = this.shadowRoot.querySelector('button[part="flip-v"]');
158
+ this._rotCCWBtn = this.shadowRoot.querySelector('button[part="rot-ccw"]');
159
+ this._rotCWBtn = this.shadowRoot.querySelector('button[part="rot-cw"]');
160
+ this._clearBtn = this.shadowRoot.querySelector('button[part="clear"]');
161
+ this._opacityRange = this.shadowRoot.querySelector('input[part="opacity"]');
162
+ this._opacityNumber = this.shadowRoot.querySelector('input[part="opacity-num"]');
163
+
164
+ // undo/redo buttons
165
+ this._undoBtn = this.shadowRoot.querySelector('button[part="undo"]');
166
+ this._redoBtn = this.shadowRoot.querySelector('button[part="redo"]');
167
+
168
+ // internal state
169
+ this._width = 16;
170
+ this._height = 16;
171
+ this._pixelSize = 16;
172
+ this._brushSize = 1;
173
+ // brush color stored as [r,g,b,a]
174
+ this._brushColor = [0, 0, 0, 255];
175
+ this._matrix = this._createEmptyMatrix(this._width, this._height);
176
+
177
+ this._pixelCtx = null;
178
+ this._gridCtx = null;
179
+
180
+ this._isPointerDown = false;
181
+ this._tool = 'pencil';
182
+ this._showGrid = false;
183
+
184
+ // history (undo/redo)
185
+ this._undoStack = [];
186
+ this._redoStack = [];
187
+ this._maxHistory = 200;
188
+ this._transactionActive = false; // grouping for pointer drags
189
+
190
+ // binds
191
+ this._onPointerDown = this._onPointerDown.bind(this);
192
+ this._onPointerMove = this._onPointerMove.bind(this);
193
+ this._onPointerUp = this._onPointerUp.bind(this);
194
+ this._onKeyDown = this._onKeyDown.bind(this);
195
+
196
+ // transform methods bound (useful if passing as callbacks)
197
+ this.flipHorizontal = this.flipHorizontal.bind(this);
198
+ this.flipVertical = this.flipVertical.bind(this);
199
+ this.rotateCW = this.rotateCW.bind(this);
200
+ this.rotateCCW = this.rotateCCW.bind(this);
201
+
202
+ // ensure keyboard handlers bound for undo/redo
203
+ }
204
+
205
+ static get observedAttributes() {
206
+ return ['width', 'height', 'pixel-size'];
207
+ }
208
+ attributeChangedCallback(name, oldV, newV) {
209
+ if (oldV === newV) return;
210
+ if (name === 'width') this.width = parseInt(newV, 10) || this._width;
211
+ if (name === 'height') this.height = parseInt(newV, 10) || this._height;
212
+ if (name === 'pixel-size') this.pixelSize = parseInt(newV, 10) || this._pixelSize;
213
+ }
214
+
215
+ connectedCallback() {
216
+ // respect attributes if present
217
+ if (this.hasAttribute('width')) this._width = Math.max(1, parseInt(this.getAttribute('width'), 10));
218
+ if (this.hasAttribute('height')) this._height = Math.max(1, parseInt(this.getAttribute('height'), 10));
219
+ if (this.hasAttribute('pixel-size')) this._pixelSize = Math.max(1, parseInt(this.getAttribute('pixel-size'), 10));
220
+
221
+ this._setupContextsAndSize();
222
+
223
+ // set initial UI control values (keeps in sync with attributes)
224
+ if (this._widthInput) this._widthInput.value = String(this._width);
225
+ if (this._heightInput) this._heightInput.value = String(this._height);
226
+ if (this._pixelSizeInput) this._pixelSizeInput.value = String(this._pixelSize);
227
+ if (this._brushSizeInput) this._brushSizeInput.value = String(this._brushSize);
228
+
229
+ // initialize color & opacity UI
230
+ if (this._colorInput) this._colorInput.value = this._rgbaToHex(this._brushColor);
231
+ if (this._hexInput) this._hexInput.value = this._rgbaToHexWithAlpha(this._brushColor);
232
+ if (this._rInput) this._rInput.value = String(this._brushColor[0]);
233
+ if (this._gInput) this._gInput.value = String(this._brushColor[1]);
234
+ if (this._bInput) this._bInput.value = String(this._brushColor[2]);
235
+ if (this._aInput) this._aInput.value = String(this._brushColor[3]);
236
+ if (this._opacityRange) this._opacityRange.value = String(this._brushColor[3]);
237
+ if (this._opacityNumber) this._opacityNumber.value = String(this._brushColor[3]);
238
+ this._emitBrushColorChange();
239
+
240
+ // UI events
241
+ this._colorInput.addEventListener('input', (e) => {
242
+ const rgb = this._hexToRgba(e.target.value);
243
+ // keep current alpha
244
+ this.setBrushColor([rgb[0], rgb[1], rgb[2], this._brushColor[3]]);
245
+ });
246
+
247
+ // hex text input with optional alpha
248
+ if (this._hexInput) {
249
+ this._hexInput.addEventListener('change', (e) => {
250
+ const rgba = this._hexToRgbaWithAlpha(e.target.value);
251
+ this.setBrushColor(rgba);
252
+ });
253
+ }
254
+
255
+ // individual RGBA inputs
256
+ const updateFromRGBAInputs = () => {
257
+ const r = Math.max(0, Math.min(255, parseInt(this._rInput.value, 10) || 0));
258
+ const g = Math.max(0, Math.min(255, parseInt(this._gInput.value, 10) || 0));
259
+ const b = Math.max(0, Math.min(255, parseInt(this._bInput.value, 10) || 0));
260
+ const a = Math.max(0, Math.min(255, parseInt(this._aInput.value, 10) || 0));
261
+ this.setBrushColor([r, g, b, a]);
262
+ };
263
+ if (this._rInput) this._rInput.addEventListener('change', updateFromRGBAInputs);
264
+ if (this._gInput) this._gInput.addEventListener('change', updateFromRGBAInputs);
265
+ if (this._bInput) this._bInput.addEventListener('change', updateFromRGBAInputs);
266
+ if (this._aInput) this._aInput.addEventListener('change', updateFromRGBAInputs);
267
+
268
+ this._toolSelect.addEventListener('change', (e) => this.setTool(e.target.value));
269
+ this._brushSizeInput.addEventListener('change', (e) => this.setBrushSize(parseInt(e.target.value, 10) || 1));
270
+ this._pixelSizeInput.addEventListener('change', (e) => {
271
+ this.pixelSize = Math.max(1, parseInt(e.target.value, 10) || 1);
272
+ });
273
+ this._toggleGrid.addEventListener('change', (e) => {
274
+ this._showGrid = !!e.target.checked;
275
+ this._renderGrid();
276
+ });
277
+
278
+ // opacity controls - keep range and number in sync
279
+ if (this._opacityRange) {
280
+ this._opacityRange.addEventListener('input', (e) => {
281
+ const v = Math.max(0, Math.min(255, parseInt(e.target.value, 10) || 0));
282
+ this.setBrushAlpha(v);
283
+ });
284
+ }
285
+ if (this._opacityNumber) {
286
+ this._opacityNumber.addEventListener('change', (e) => {
287
+ const v = Math.max(0, Math.min(255, parseInt(e.target.value, 10) || 0));
288
+ this.setBrushAlpha(v);
289
+ });
290
+ }
291
+
292
+ // width/height change -> resize (preserve existing content)
293
+ if (this._widthInput)
294
+ this._widthInput.addEventListener('change', (e) => {
295
+ const val = Math.max(1, parseInt(e.target.value, 10) || 1);
296
+ // keep value synced (will update input again in resize)
297
+ this.resize(val, this._height, { preserve: true });
298
+ });
299
+ if (this._heightInput)
300
+ this._heightInput.addEventListener('change', (e) => {
301
+ const val = Math.max(1, parseInt(e.target.value, 10) || 1);
302
+ this.resize(this._width, val, { preserve: true });
303
+ });
304
+
305
+ // transform buttons
306
+ if (this._flipHBtn)
307
+ this._flipHBtn.addEventListener('click', () => {
308
+ this._beginTransaction();
309
+ this.flipHorizontal();
310
+ this._endTransaction();
311
+ });
312
+ if (this._flipVBtn)
313
+ this._flipVBtn.addEventListener('click', () => {
314
+ this._beginTransaction();
315
+ this.flipVertical();
316
+ this._endTransaction();
317
+ });
318
+ if (this._rotCWBtn)
319
+ this._rotCWBtn.addEventListener('click', () => {
320
+ this._beginTransaction();
321
+ this.rotateCW();
322
+ this._endTransaction();
323
+ });
324
+ if (this._rotCCWBtn)
325
+ this._rotCCWBtn.addEventListener('click', () => {
326
+ this._beginTransaction();
327
+ this.rotateCCW();
328
+ this._endTransaction();
329
+ });
330
+
331
+ // clear button (makes canvas fully transparent)
332
+ if (this._clearBtn)
333
+ this._clearBtn.addEventListener('click', () => {
334
+ this._beginTransaction();
335
+ this.clear([0, 0, 0, 0]);
336
+ this._endTransaction();
337
+ });
338
+
339
+ // Export/Import
340
+ this._exportBtn.addEventListener('click', () => this.exportPNG());
341
+ this._exportJsonBtn.addEventListener('click', () => {
342
+ const json = this.exportMatrixJSON();
343
+ const blob = new Blob([json], { type: 'application/json' });
344
+ const url = URL.createObjectURL(blob);
345
+ const a = document.createElement('a');
346
+ a.href = url;
347
+ a.download = 'object-layer.json';
348
+ a.click();
349
+ URL.revokeObjectURL(url);
350
+ });
351
+
352
+ this._importJsonBtn.addEventListener('click', async () => {
353
+ const file = await this._pickFile();
354
+ if (!file) return;
355
+ const text = await file.text();
356
+ try {
357
+ this._beginTransaction();
358
+ this.importMatrixJSON(text);
359
+ this._endTransaction();
360
+ } catch (err) {
361
+ console.error(err);
362
+ alert('Invalid JSON');
363
+ }
364
+ });
365
+
366
+ // undo/redo
367
+ if (this._undoBtn) this._undoBtn.addEventListener('click', () => this.undo());
368
+ if (this._redoBtn) this._redoBtn.addEventListener('click', () => this.redo());
369
+
370
+ // Pointer events
371
+ this._pixelCanvas.addEventListener('pointerdown', this._onPointerDown);
372
+ window.addEventListener('pointermove', this._onPointerMove);
373
+ window.addEventListener('pointerup', this._onPointerUp);
374
+
375
+ // keyboard for undo/redo
376
+ window.addEventListener('keydown', this._onKeyDown);
377
+
378
+ // initial render and clear history
379
+ this.render();
380
+ this._clearHistory();
381
+ this._updateToolbarButtons();
382
+ }
383
+
384
+ disconnectedCallback() {
385
+ this._pixelCanvas.removeEventListener('pointerdown', this._onPointerDown);
386
+ window.removeEventListener('pointermove', this._onPointerMove);
387
+ window.removeEventListener('pointerup', this._onPointerUp);
388
+
389
+ if (this._flipHBtn) this._flipHBtn.removeEventListener('click', this.flipHorizontal);
390
+ if (this._flipVBtn) this._flipVBtn.removeEventListener('click', this.flipVertical);
391
+ if (this._rotCWBtn) this._rotCWBtn.removeEventListener('click', this.rotateCW);
392
+ if (this._rotCCWBtn) this._rotCCWBtn.removeEventListener('click', this.rotateCCW);
393
+ if (this._clearBtn) this._clearBtn.removeEventListener('click', () => this.clear([0, 0, 0, 0]));
394
+ if (this._opacityRange) this._opacityRange.removeEventListener('input', () => {});
395
+ if (this._opacityNumber) this._opacityNumber.removeEventListener('change', () => {});
396
+
397
+ if (this._undoBtn) this._undoBtn.removeEventListener('click', () => this.undo());
398
+ if (this._redoBtn) this._redoBtn.removeEventListener('click', () => this.redo());
399
+
400
+ window.removeEventListener('keydown', this._onKeyDown);
401
+ }
402
+
403
+ // ---------------- Matrix helpers ----------------
404
+ _createEmptyMatrix(w, h) {
405
+ const mat = new Array(h);
406
+ for (let y = 0; y < h; y++) {
407
+ mat[y] = new Array(w);
408
+ for (let x = 0; x < w; x++) mat[y][x] = [0, 0, 0, 0];
409
+ }
410
+ return mat;
411
+ }
412
+
413
+ createMatrix(width, height, fill = [0, 0, 0, 0]) {
414
+ const w = Math.max(1, Math.floor(width));
415
+ const h = Math.max(1, Math.floor(height));
416
+ const mat = this._createEmptyMatrix(w, h);
417
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) mat[y][x] = fill.slice();
418
+ return mat;
419
+ }
420
+
421
+ loadMatrix(matrix) {
422
+ if (!Array.isArray(matrix) || matrix.length === 0) throw new TypeError('matrix must be non-empty 2D array');
423
+ const h = matrix.length;
424
+ const w = matrix[0].length;
425
+ for (let y = 0; y < h; y++) {
426
+ if (!Array.isArray(matrix[y]) || matrix[y].length !== w) throw new TypeError('matrix must be rectangular');
427
+ for (let x = 0; x < w; x++) {
428
+ const v = matrix[y][x];
429
+ if (!Array.isArray(v) || v.length !== 4) throw new TypeError('each cell must be [r,g,b,a]');
430
+ matrix[y][x] = v.map((n) => this._clampInt(n));
431
+ }
432
+ }
433
+ this._width = w;
434
+ this._height = h;
435
+ this._matrix = matrix.map((r) => r.map((c) => c.slice()));
436
+ this._setupContextsAndSize();
437
+ this.render();
438
+ this.dispatchEvent(new CustomEvent('matrixload', { detail: { width: w, height: h } }));
439
+ }
440
+
441
+ clear(fill = [0, 0, 0, 0]) {
442
+ for (let y = 0; y < this._height; y++) for (let x = 0; x < this._width; x++) this._matrix[y][x] = fill.slice();
443
+ this.render();
444
+ this.dispatchEvent(new CustomEvent('clear'));
445
+ }
446
+
447
+ resize(w, h, { preserve = true } = {}) {
448
+ const nw = Math.max(1, Math.floor(w));
449
+ const nh = Math.max(1, Math.floor(h));
450
+ const newMat = this._createEmptyMatrix(nw, nh);
451
+ if (preserve) {
452
+ const minW = Math.min(nw, this._width);
453
+ const minH = Math.min(nh, this._height);
454
+ for (let y = 0; y < minH; y++) for (let x = 0; x < minW; x++) newMat[y][x] = this._matrix[y][x].slice();
455
+ }
456
+ this._width = nw;
457
+ this._height = nh;
458
+ this._matrix = newMat;
459
+
460
+ // keep inputs and attributes in sync
461
+ if (this._widthInput) this._widthInput.value = String(this._width);
462
+ if (this._heightInput) this._heightInput.value = String(this._height);
463
+ this.setAttribute('width', String(this._width));
464
+ this.setAttribute('height', String(this._height));
465
+
466
+ this._setupContextsAndSize();
467
+ this.render();
468
+ this.dispatchEvent(new CustomEvent('resize', { detail: { width: nw, height: nh } }));
469
+ }
470
+
471
+ setPixel(x, y, rgba, renderNow = true) {
472
+ if (!this._inBounds(x, y)) return false;
473
+ this._matrix[y][x] = rgba.map((n) => this._clampInt(n));
474
+ if (renderNow) this.render();
475
+ this.dispatchEvent(new CustomEvent('pixelchange', { detail: { x, y, rgba: this._matrix[y][x].slice() } }));
476
+ return true;
477
+ }
478
+
479
+ getPixel(x, y) {
480
+ return this._inBounds(x, y) ? this._matrix[y][x].slice() : null;
481
+ }
482
+ _inBounds(x, y) {
483
+ return x >= 0 && y >= 0 && x < this._width && y < this._height;
484
+ }
485
+ _clampInt(v) {
486
+ const n = Number(v) || 0;
487
+ return Math.min(255, Math.max(0, Math.floor(n)));
488
+ }
489
+
490
+ // ---------------- Canvas sizing and contexts ----------------
491
+ _setupContextsAndSize() {
492
+ // logical canvas (one logical pixel per image pixel). CSS scales by pixelSize.
493
+ this._pixelCanvas.width = this._width;
494
+ this._pixelCanvas.height = this._height;
495
+ this._pixelCanvas.style.width = `${this._width * this._pixelSize}px`;
496
+ this._pixelCanvas.style.height = `${this._height * this._pixelSize}px`;
497
+
498
+ // grid overlay uses CSS pixel coordinates
499
+ this._gridCanvas.width = this._width * this._pixelSize;
500
+ this._gridCanvas.height = this._height * this._pixelSize;
501
+ this._gridCanvas.style.width = this._pixelCanvas.style.width;
502
+ this._gridCanvas.style.height = this._pixelCanvas.style.height;
503
+
504
+ this._pixelCtx = this._pixelCanvas.getContext('2d');
505
+ this._gridCtx = this._gridCanvas.getContext('2d');
506
+ try {
507
+ this._pixelCtx.imageSmoothingEnabled = false;
508
+ this._gridCtx.imageSmoothingEnabled = false;
509
+ } catch (e) {}
510
+ this._renderGrid();
511
+ }
512
+
513
+ render() {
514
+ // sanity: ensure matrix shape matches
515
+ if (
516
+ !Array.isArray(this._matrix) ||
517
+ this._matrix.length !== this._height ||
518
+ !Array.isArray(this._matrix[0]) ||
519
+ this._matrix[0].length !== this._width
520
+ ) {
521
+ this._matrix = this._createEmptyMatrix(this._width, this._height);
522
+ }
523
+
524
+ // detect transparency (fast bailout)
525
+ let hasTransparent = false;
526
+ for (let y = 0; y < this._height && !hasTransparent; y++) {
527
+ for (let x = 0; x < this._width; x++) {
528
+ const a = this._matrix[y] && this._matrix[y][x] ? this._matrix[y][x][3] : 0;
529
+ if (a !== 255) {
530
+ hasTransparent = true;
531
+ break;
532
+ }
533
+ }
534
+ }
535
+
536
+ // clear and optionally draw checkerboard (visual only)
537
+ this._pixelCtx.clearRect(0, 0, this._pixelCanvas.width, this._pixelCanvas.height);
538
+ if (hasTransparent) this._drawCheckerboard();
539
+
540
+ // write image data
541
+ const img = this._pixelCtx.createImageData(this._width, this._height);
542
+ const data = img.data;
543
+ let p = 0;
544
+ for (let y = 0; y < this._height; y++) {
545
+ for (let x = 0; x < this._width; x++) {
546
+ const cell = this._matrix[y] && this._matrix[y][x] ? this._matrix[y][x] : [0, 0, 0, 0];
547
+ data[p++] = this._clampInt(cell[0]);
548
+ data[p++] = this._clampInt(cell[1]);
549
+ data[p++] = this._clampInt(cell[2]);
550
+ data[p++] = this._clampInt(cell[3]);
551
+ }
552
+ }
553
+ this._pixelCtx.putImageData(img, 0, 0);
554
+
555
+ if (this._showGrid) this._renderGrid();
556
+ }
557
+
558
+ _drawCheckerboard() {
559
+ const ctx = this._pixelCtx;
560
+ const w = this._width;
561
+ const h = this._height;
562
+ const light = '#e9e9e9',
563
+ dark = '#cfcfcf';
564
+ // draw one logical pixel per matrix cell
565
+ for (let y = 0; y < h; y++) {
566
+ for (let x = 0; x < w; x++) {
567
+ ctx.fillStyle = ((x + y) & 1) === 0 ? light : dark;
568
+ ctx.fillRect(x, y, 1, 1);
569
+ }
570
+ }
571
+ }
572
+
573
+ _renderGrid() {
574
+ const ctx = this._gridCtx;
575
+ if (!ctx) return;
576
+ const w = this._gridCanvas.width;
577
+ const h = this._gridCanvas.height;
578
+ ctx.clearRect(0, 0, w, h);
579
+ if (!this._showGrid) return;
580
+ const ps = this._pixelSize;
581
+ ctx.save();
582
+ ctx.strokeStyle = darkTheme ? '#e1e1e1' : '#272727';
583
+ ctx.lineWidth = 2;
584
+ ctx.beginPath();
585
+ for (let x = 0; x <= this._width; x++) {
586
+ const xx = x * ps + 0.5;
587
+ ctx.moveTo(xx, 0);
588
+ ctx.lineTo(xx, h);
589
+ }
590
+ for (let y = 0; y <= this._height; y++) {
591
+ const yy = y * ps + 0.5;
592
+ ctx.moveTo(0, yy);
593
+ ctx.lineTo(w, yy);
594
+ }
595
+ ctx.stroke();
596
+ ctx.restore();
597
+ }
598
+
599
+ // ---------------- Tools & painting ----------------
600
+ setTool(name) {
601
+ this._tool = name;
602
+ if (this._toolSelect) this._toolSelect.value = name;
603
+ }
604
+
605
+ // set full RGBA brush color (alpha optional)
606
+ setBrushColor(rgba) {
607
+ if (!Array.isArray(rgba) || rgba.length < 3) return;
608
+ const r = this._clampInt(rgba[0]);
609
+ const g = this._clampInt(rgba[1]);
610
+ const b = this._clampInt(rgba[2]);
611
+ const a = typeof rgba[3] === 'number' ? this._clampInt(rgba[3]) : this._brushColor[3];
612
+ this._brushColor = [r, g, b, a];
613
+ if (this._colorInput) this._colorInput.value = this._rgbaToHex(this._brushColor);
614
+ if (this._hexInput) this._hexInput.value = this._rgbaToHexWithAlpha(this._brushColor);
615
+ if (this._rInput) this._rInput.value = String(this._brushColor[0]);
616
+ if (this._gInput) this._gInput.value = String(this._brushColor[1]);
617
+ if (this._bInput) this._bInput.value = String(this._brushColor[2]);
618
+ if (this._aInput) this._aInput.value = String(this._brushColor[3]);
619
+ if (this._opacityRange) this._opacityRange.value = String(this._brushColor[3]);
620
+ if (this._opacityNumber) this._opacityNumber.value = String(this._brushColor[3]);
621
+ this._emitBrushColorChange();
622
+ }
623
+
624
+ getBrushColor() {
625
+ return this._brushColor.slice();
626
+ }
627
+
628
+ // set brush alpha (0-255)
629
+ setBrushAlpha(a) {
630
+ const v = Math.max(0, Math.min(255, Math.floor(Number(a) || 0)));
631
+ this._brushColor[3] = v;
632
+ if (this._opacityRange) this._opacityRange.value = String(v);
633
+ if (this._opacityNumber) this._opacityNumber.value = String(v);
634
+ if (this._aInput) this._aInput.value = String(v);
635
+ // keep color input (hex) representing rgb only
636
+ if (this._colorInput) this._colorInput.value = this._rgbaToHex(this._brushColor);
637
+ if (this._hexInput) this._hexInput.value = this._rgbaToHexWithAlpha(this._brushColor);
638
+ this._emitBrushColorChange();
639
+ }
640
+ getBrushAlpha() {
641
+ return this._brushColor[3];
642
+ }
643
+
644
+ _emitBrushColorChange() {
645
+ this.dispatchEvent(
646
+ new CustomEvent('brushcolorchange', {
647
+ detail: {
648
+ rgba: this._brushColor.slice(),
649
+ hex: this._rgbaToHex(this._brushColor),
650
+ hexWithAlpha: this._rgbaToHexWithAlpha(this._brushColor),
651
+ },
652
+ }),
653
+ );
654
+ }
655
+
656
+ setBrushSize(n) {
657
+ this._brushSize = Math.max(1, Math.floor(n));
658
+ if (this._brushSizeInput) this._brushSizeInput.value = this._brushSize;
659
+ }
660
+
661
+ _applyBrush(x, y, color, renderAfter = false) {
662
+ const half = Math.floor(this._brushSize / 2);
663
+ for (let oy = -half; oy <= half; oy++)
664
+ for (let ox = -half; ox <= half; ox++) {
665
+ const tx = x + ox,
666
+ ty = y + oy;
667
+ if (this._inBounds(tx, ty)) this._matrix[ty][tx] = color.slice();
668
+ }
669
+ if (renderAfter) this.render();
670
+ }
671
+
672
+ fillBucket(x, y, targetColor = null) {
673
+ if (!this._inBounds(x, y)) return;
674
+ this._beginTransaction();
675
+ const src = this.getPixel(x, y);
676
+ const newColor = targetColor ? targetColor.slice() : this._brushColor.slice();
677
+ if (this._colorsEqual(src, newColor)) {
678
+ this._endTransaction();
679
+ return;
680
+ }
681
+ const stack = [[x, y]];
682
+ while (stack.length) {
683
+ const [cx, cy] = stack.pop();
684
+ if (!this._inBounds(cx, cy)) continue;
685
+ const cur = this.getPixel(cx, cy);
686
+ if (!this._colorsEqual(cur, src)) continue;
687
+ this._matrix[cy][cx] = newColor.slice();
688
+ stack.push([cx + 1, cy], [cx - 1, cy], [cx, cy + 1], [cx, cy - 1]);
689
+ }
690
+ this.render();
691
+ this.dispatchEvent(new CustomEvent('fill', { detail: { x, y } }));
692
+ this._endTransaction();
693
+ }
694
+
695
+ _colorsEqual(a, b) {
696
+ if (!a || !b) return false;
697
+ return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
698
+ }
699
+
700
+ // ---------------- Pointer handling ----------------
701
+ _toGridCoords(evt) {
702
+ const rect = this._pixelCanvas.getBoundingClientRect();
703
+ const cssX = evt.clientX - rect.left;
704
+ const cssY = evt.clientY - rect.top;
705
+ const scaleX = this._pixelCanvas.width / rect.width;
706
+ const scaleY = this._pixelCanvas.height / rect.height;
707
+ const x = Math.floor(cssX * scaleX);
708
+ const y = Math.floor(cssY * scaleY);
709
+ return [x, y];
710
+ }
711
+
712
+ _onPointerDown(evt) {
713
+ evt.preventDefault();
714
+ this._isPointerDown = true;
715
+ try {
716
+ this._pixelCanvas.setPointerCapture(evt.pointerId);
717
+ } catch (e) {}
718
+ const [x, y] = this._toGridCoords(evt);
719
+ // start transaction for continuous stroke
720
+ this._beginTransaction();
721
+ this._applyToolAt(x, y, evt);
722
+ }
723
+ _onPointerMove(evt) {
724
+ if (!this._isPointerDown) return;
725
+ const [x, y] = this._toGridCoords(evt);
726
+ this._applyToolAt(x, y, evt, true);
727
+ }
728
+ _onPointerUp(evt) {
729
+ this._isPointerDown = false;
730
+ try {
731
+ this._pixelCanvas.releasePointerCapture(evt.pointerId);
732
+ } catch (e) {}
733
+ // finish transaction for the stroke
734
+ this._endTransaction();
735
+ }
736
+
737
+ _applyToolAt(x, y, evt, continuous = false) {
738
+ if (!this._inBounds(x, y)) return;
739
+ switch (this._tool) {
740
+ case 'pencil':
741
+ this._applyBrush(x, y, this._brushColor, true);
742
+ break;
743
+ case 'eraser':
744
+ this._applyBrush(x, y, [0, 0, 0, 0], true);
745
+ break;
746
+ case 'fill':
747
+ if (!continuous) this.fillBucket(x, y);
748
+ break;
749
+ case 'eyedropper':
750
+ const picked = this.getPixel(x, y);
751
+ if (picked) this.setBrushColor(picked);
752
+ break;
753
+ }
754
+ }
755
+
756
+ // ---------------- Import / Export ----------------
757
+ exportMatrixJSON() {
758
+ return JSON.stringify({ width: this._width, height: this._height, matrix: this._matrix });
759
+ }
760
+ importMatrixJSON(json) {
761
+ const data = typeof json === 'string' ? JSON.parse(json) : json;
762
+ if (!data || !Array.isArray(data.matrix)) throw new TypeError('Invalid matrix JSON');
763
+ // wrap import as transactional change
764
+ this.loadMatrix(data.matrix);
765
+ }
766
+
767
+ async _pickFile() {
768
+ return new Promise((resolve) => {
769
+ const input = document.createElement('input');
770
+ input.type = 'file';
771
+ input.accept = 'application/json';
772
+ input.addEventListener('change', () => {
773
+ resolve(input.files && input.files[0] ? input.files[0] : null);
774
+ });
775
+ input.click();
776
+ });
777
+ }
778
+
779
+ // Create a PNG data URL at the requested scale (scale = number of CSS pixels per logical pixel)
780
+ toDataURL(scale = this._pixelSize) {
781
+ const w = this._width,
782
+ h = this._height;
783
+ const outW = Math.max(1, Math.floor(w * scale));
784
+ const outH = Math.max(1, Math.floor(h * scale));
785
+
786
+ // create logical image at native resolution
787
+ const src = document.createElement('canvas');
788
+ src.width = w;
789
+ src.height = h;
790
+ const sctx = src.getContext('2d');
791
+ const img = sctx.createImageData(w, h);
792
+ const data = img.data;
793
+ let p = 0;
794
+ for (let y = 0; y < h; y++)
795
+ for (let x = 0; x < w; x++) {
796
+ const c = this._matrix[y][x] || [0, 0, 0, 0];
797
+ data[p++] = this._clampInt(c[0]);
798
+ data[p++] = this._clampInt(c[1]);
799
+ data[p++] = this._clampInt(c[2]);
800
+ data[p++] = this._clampInt(c[3]);
801
+ }
802
+ sctx.putImageData(img, 0, 0);
803
+
804
+ // scale into output canvas (nearest-neighbor)
805
+ const out = document.createElement('canvas');
806
+ out.width = outW;
807
+ out.height = outH;
808
+ const octx = out.getContext('2d');
809
+ try {
810
+ octx.imageSmoothingEnabled = false;
811
+ } catch (e) {}
812
+ octx.drawImage(src, 0, 0, outW, outH);
813
+ return out.toDataURL('image/png');
814
+ }
815
+
816
+ // Async blob version (recommended for large images)
817
+ toBlob(scale = this._pixelSize) {
818
+ return new Promise((resolve) => {
819
+ const w = this._width,
820
+ h = this._height;
821
+ const outW = Math.max(1, Math.floor(w * scale));
822
+ const outH = Math.max(1, Math.floor(h * scale));
823
+ const src = document.createElement('canvas');
824
+ src.width = w;
825
+ src.height = h;
826
+ const sctx = src.getContext('2d');
827
+ const img = sctx.createImageData(w, h);
828
+ const data = img.data;
829
+ let p = 0;
830
+ for (let y = 0; y < h; y++)
831
+ for (let x = 0; x < w; x++) {
832
+ const c = this._matrix[y][x] || [0, 0, 0, 0];
833
+ data[p++] = this._clampInt(c[0]);
834
+ data[p++] = this._clampInt(c[1]);
835
+ data[p++] = this._clampInt(c[2]);
836
+ data[p++] = this._clampInt(c[3]);
837
+ }
838
+ sctx.putImageData(img, 0, 0);
839
+ const out = document.createElement('canvas');
840
+ out.width = outW;
841
+ out.height = outH;
842
+ const octx = out.getContext('2d');
843
+ try {
844
+ octx.imageSmoothingEnabled = false;
845
+ } catch (e) {}
846
+ octx.drawImage(src, 0, 0, outW, outH);
847
+ out.toBlob((b) => resolve(b), 'image/png');
848
+ });
849
+ }
850
+
851
+ // Trigger download of PNG (uses blob to avoid huge data URLs on big exports)
852
+ async exportPNG(filename = 'object-layer.png', scale = this._pixelSize) {
853
+ const blob = await this.toBlob(scale);
854
+ const url = URL.createObjectURL(blob);
855
+ const a = document.createElement('a');
856
+ a.href = url;
857
+ a.download = filename;
858
+ a.click();
859
+ // revoke after a tick to ensure download started
860
+ setTimeout(() => URL.revokeObjectURL(url), 5000);
861
+ }
862
+
863
+ // ---------------- Undo/Redo helpers ----------------
864
+ _snapshot() {
865
+ return {
866
+ width: this._width,
867
+ height: this._height,
868
+ matrix: this._matrix.map((r) => r.map((c) => c.slice())),
869
+ };
870
+ }
871
+
872
+ _loadSnapshot(snap) {
873
+ if (!snap) return;
874
+ this._width = snap.width;
875
+ this._height = snap.height;
876
+ this._matrix = snap.matrix.map((r) => r.map((c) => c.slice()));
877
+ if (this._widthInput) this._widthInput.value = String(this._width);
878
+ if (this._heightInput) this._heightInput.value = String(this._height);
879
+ this.setAttribute('width', String(this._width));
880
+ this.setAttribute('height', String(this._height));
881
+ this._setupContextsAndSize();
882
+ this.render();
883
+ this.dispatchEvent(new CustomEvent('matrixload', { detail: { width: this._width, height: this._height } }));
884
+ }
885
+
886
+ _matricesEqual(a, b) {
887
+ if (!a || !b) return false;
888
+ if (a.width !== b.width || a.height !== b.height) return false;
889
+ const h = a.height;
890
+ for (let y = 0; y < h; y++) {
891
+ for (let x = 0; x < a.width; x++) {
892
+ const ac = a.matrix[y][x];
893
+ const bc = b.matrix[y][x];
894
+ for (let i = 0; i < 4; i++) if (ac[i] !== bc[i]) return false;
895
+ }
896
+ }
897
+ return true;
898
+ }
899
+
900
+ _pushUndo(snap) {
901
+ this._undoStack.push(snap);
902
+ if (this._undoStack.length > this._maxHistory) this._undoStack.shift();
903
+ // clear redo
904
+ this._redoStack.length = 0;
905
+ this._updateToolbarButtons();
906
+ }
907
+
908
+ _clearHistory() {
909
+ this._undoStack.length = 0;
910
+ this._redoStack.length = 0;
911
+ this._updateToolbarButtons();
912
+ }
913
+
914
+ _beginTransaction() {
915
+ if (this._transactionActive) return;
916
+ this._transactionActive = true;
917
+ const before = this._snapshot();
918
+ this._pushUndo(before);
919
+ }
920
+
921
+ _endTransaction() {
922
+ if (!this._transactionActive) return;
923
+ this._transactionActive = false;
924
+ // if the last undo state is identical to current (no-op), remove it
925
+ const last = this._undoStack[this._undoStack.length - 1];
926
+ const now = this._snapshot();
927
+ if (this._matricesEqual(last, now)) this._undoStack.pop();
928
+ this._updateToolbarButtons();
929
+ }
930
+
931
+ undo() {
932
+ if (!this._undoStack.length) return;
933
+ const snap = this._undoStack.pop();
934
+ // push current to redo
935
+ this._redoStack.push(this._snapshot());
936
+ this._loadSnapshot(snap);
937
+ this._updateToolbarButtons();
938
+ this.dispatchEvent(new CustomEvent('undo'));
939
+ }
940
+
941
+ redo() {
942
+ if (!this._redoStack.length) return;
943
+ const snap = this._redoStack.pop();
944
+ // push current to undo
945
+ this._undoStack.push(this._snapshot());
946
+ this._loadSnapshot(snap);
947
+ this._updateToolbarButtons();
948
+ this.dispatchEvent(new CustomEvent('redo'));
949
+ }
950
+
951
+ _updateToolbarButtons() {
952
+ if (this._undoBtn) this._undoBtn.disabled = this._undoStack.length === 0;
953
+ if (this._redoBtn) this._redoBtn.disabled = this._redoStack.length === 0;
954
+ }
955
+
956
+ _onKeyDown(e) {
957
+ const meta = e.ctrlKey || e.metaKey;
958
+ if (!meta) return;
959
+ // ctrl/cmd+z -> undo, ctrl/cmd+shift+z or ctrl+ y -> redo
960
+ if (e.key === 'z' || e.key === 'Z') {
961
+ if (e.shiftKey) {
962
+ e.preventDefault();
963
+ this.redo();
964
+ } else {
965
+ e.preventDefault();
966
+ this.undo();
967
+ }
968
+ } else if (e.key === 'y' || e.key === 'Y') {
969
+ e.preventDefault();
970
+ this.redo();
971
+ }
972
+ }
973
+
974
+ // ---------------- Helpers ----------------
975
+ _hexToRgba(hex) {
976
+ const h = (hex || '').replace('#', '');
977
+ if (h.length === 3) {
978
+ return [parseInt(h[0] + h[0], 16), parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), 255];
979
+ }
980
+ if (h.length === 6) {
981
+ return [parseInt(h.substring(0, 2), 16), parseInt(h.substring(2, 4), 16), parseInt(h.substring(4, 6), 16), 255];
982
+ }
983
+ return [0, 0, 0, 255];
984
+ }
985
+ _rgbaToHex(rgba) {
986
+ const [r, g, b] = rgba;
987
+ return `#${((1 << 24) + (this._clampInt(r) << 16) + (this._clampInt(g) << 8) + this._clampInt(b))
988
+ .toString(16)
989
+ .slice(1)}`;
990
+ }
991
+
992
+ // convert RGBA to hex with alpha channel
993
+ _rgbaToHexWithAlpha(rgba) {
994
+ const [r, g, b, a] = rgba;
995
+ const rHex = this._clampInt(r).toString(16).padStart(2, '0');
996
+ const gHex = this._clampInt(g).toString(16).padStart(2, '0');
997
+ const bHex = this._clampInt(b).toString(16).padStart(2, '0');
998
+ const aHex = this._clampInt(a).toString(16).padStart(2, '0');
999
+ return `#${rHex}${gHex}${bHex}${aHex}`.toUpperCase();
1000
+ }
1001
+
1002
+ // convert hex to RGBA with optional alpha channel support
1003
+ _hexToRgbaWithAlpha(hex) {
1004
+ const h = (hex || '').replace('#', '');
1005
+ if (h.length === 3) {
1006
+ // #RGB -> expand to RRGGBB
1007
+ return [parseInt(h[0] + h[0], 16), parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), 255];
1008
+ }
1009
+ if (h.length === 4) {
1010
+ // #RGBA -> expand to RRGGBBAA
1011
+ return [
1012
+ parseInt(h[0] + h[0], 16),
1013
+ parseInt(h[1] + h[1], 16),
1014
+ parseInt(h[2] + h[2], 16),
1015
+ parseInt(h[3] + h[3], 16),
1016
+ ];
1017
+ }
1018
+ if (h.length === 6) {
1019
+ // #RRGGBB
1020
+ return [parseInt(h.substring(0, 2), 16), parseInt(h.substring(2, 4), 16), parseInt(h.substring(4, 6), 16), 255];
1021
+ }
1022
+ if (h.length === 8) {
1023
+ // #RRGGBBAA
1024
+ return [
1025
+ parseInt(h.substring(0, 2), 16),
1026
+ parseInt(h.substring(2, 4), 16),
1027
+ parseInt(h.substring(4, 6), 16),
1028
+ parseInt(h.substring(6, 8), 16),
1029
+ ];
1030
+ }
1031
+ return [0, 0, 0, 255];
1032
+ }
1033
+
1034
+ // ---------------- Transform helpers (flip/rotate) ----------------
1035
+ flipHorizontal() {
1036
+ // reverse each row (mirror horizontally)
1037
+ for (let y = 0; y < this._height; y++) {
1038
+ this._matrix[y].reverse();
1039
+ }
1040
+ this.render();
1041
+ this.dispatchEvent(new CustomEvent('transform', { detail: { type: 'flip-horizontal' } }));
1042
+ }
1043
+
1044
+ flipVertical() {
1045
+ // reverse the order of rows (mirror vertically)
1046
+ this._matrix.reverse();
1047
+ this.render();
1048
+ this.dispatchEvent(new CustomEvent('transform', { detail: { type: 'flip-vertical' } }));
1049
+ }
1050
+
1051
+ rotateCW() {
1052
+ // rotate +90 degrees (clockwise)
1053
+ const oldH = this._height;
1054
+ const oldW = this._width;
1055
+ const newW = oldH;
1056
+ const newH = oldW;
1057
+ const newMat = this._createEmptyMatrix(newW, newH);
1058
+ for (let y = 0; y < oldH; y++) {
1059
+ for (let x = 0; x < oldW; x++) {
1060
+ const px = this._matrix[y][x] ? this._matrix[y][x].slice() : [0, 0, 0, 0];
1061
+ const newX = oldH - 1 - y; // column in new matrix
1062
+ const newY = x; // row in new matrix
1063
+ newMat[newY][newX] = px;
1064
+ }
1065
+ }
1066
+ this._width = newW;
1067
+ this._height = newH;
1068
+ this._matrix = newMat;
1069
+ // keep inputs/attributes in sync
1070
+ if (this._widthInput) this._widthInput.value = String(this._width);
1071
+ if (this._heightInput) this._heightInput.value = String(this._height);
1072
+ this.setAttribute('width', String(this._width));
1073
+ this.setAttribute('height', String(this._height));
1074
+
1075
+ this._setupContextsAndSize();
1076
+ this.render();
1077
+ this.dispatchEvent(
1078
+ new CustomEvent('transform', { detail: { type: 'rotate-cw', width: this._width, height: this._height } }),
1079
+ );
1080
+ }
1081
+
1082
+ rotateCCW() {
1083
+ // rotate -90 degrees (counter-clockwise)
1084
+ const oldH = this._height;
1085
+ const oldW = this._width;
1086
+ const newW = oldH;
1087
+ const newH = oldW;
1088
+ const newMat = this._createEmptyMatrix(newW, newH);
1089
+ for (let y = 0; y < oldH; y++) {
1090
+ for (let x = 0; x < oldW; x++) {
1091
+ const px = this._matrix[y][x] ? this._matrix[y][x].slice() : [0, 0, 0, 0];
1092
+ const newX = y; // column in new matrix
1093
+ const newY = oldW - 1 - x; // row in new matrix
1094
+ newMat[newY][newX] = px;
1095
+ }
1096
+ }
1097
+ this._width = newW;
1098
+ this._height = newH;
1099
+ this._matrix = newMat;
1100
+ if (this._widthInput) this._widthInput.value = String(this._width);
1101
+ if (this._heightInput) this._heightInput.value = String(this._height);
1102
+ this.setAttribute('width', String(this._width));
1103
+ this.setAttribute('height', String(this._height));
1104
+
1105
+ this._setupContextsAndSize();
1106
+ this.render();
1107
+ this.dispatchEvent(
1108
+ new CustomEvent('transform', { detail: { type: 'rotate-ccw', width: this._width, height: this._height } }),
1109
+ );
1110
+ }
1111
+
1112
+ // ---------------- Properties ----------------
1113
+ get width() {
1114
+ return this._width;
1115
+ }
1116
+ set width(v) {
1117
+ this._width = Math.max(1, Math.floor(v));
1118
+ this.setAttribute('width', String(this._width));
1119
+ this._setupContextsAndSize();
1120
+ this.render();
1121
+ }
1122
+ get height() {
1123
+ return this._height;
1124
+ }
1125
+ set height(v) {
1126
+ this._height = Math.max(1, Math.floor(v));
1127
+ this.setAttribute('height', String(this._height));
1128
+ this._setupContextsAndSize();
1129
+ this.render();
1130
+ }
1131
+ get pixelSize() {
1132
+ return this._pixelSize;
1133
+ }
1134
+ set pixelSize(v) {
1135
+ this._pixelSize = Math.max(1, Math.floor(v));
1136
+ this.setAttribute('pixel-size', String(this._pixelSize));
1137
+ this._setupContextsAndSize();
1138
+ this.render();
1139
+ }
1140
+ get brushSize() {
1141
+ return this._brushSize;
1142
+ }
1143
+ set brushSize(v) {
1144
+ this.setBrushSize(v);
1145
+ }
1146
+ get matrix() {
1147
+ return this._matrix.map((row) => row.map((c) => c.slice()));
1148
+ }
1149
+
1150
+ exportJSON() {
1151
+ return this.exportMatrixJSON();
1152
+ }
1153
+ importJSON(json) {
1154
+ return this.importMatrixJSON(json);
1155
+ }
1156
+ }
1157
+
1158
+ customElements.define('object-layer-engine', ObjectLayerEngineElement);
1159
+
1160
+ /*
1161
+ Example usage:
1162
+ <object-layer-engine id="ole" width="20" height="12" pixel-size="20"></object-layer-engine>
1163
+ */
1164
+
1165
+ class ObjectLayerPngLoader extends HTMLElement {
1166
+ constructor() {
1167
+ super();
1168
+ this.attachShadow({ mode: 'open' });
1169
+ this.shadowRoot.innerHTML = html`
1170
+ <style>
1171
+ :host {
1172
+ display: block;
1173
+ font-family:
1174
+ system-ui,
1175
+ -apple-system,
1176
+ 'Segoe UI',
1177
+ Roboto,
1178
+ Arial;
1179
+ }
1180
+ .wrap {
1181
+ display: flex;
1182
+ flex-direction: column;
1183
+ gap: 8px;
1184
+ }
1185
+ .controls {
1186
+ display: flex;
1187
+ gap: 8px;
1188
+ align-items: center;
1189
+ flex-wrap: wrap;
1190
+ }
1191
+ .drop-area {
1192
+ border: 2px dashed #999;
1193
+ padding: 12px;
1194
+ border-radius: 8px;
1195
+ text-align: center;
1196
+ color: #555;
1197
+ user-select: none;
1198
+ }
1199
+ .drop-area.dragover {
1200
+ border-color: #4a90e2;
1201
+ color: #1a73e8;
1202
+ background: rgba(74, 144, 226, 0.04);
1203
+ }
1204
+ input[type='file'] {
1205
+ display: inline-block;
1206
+ }
1207
+ .hint {
1208
+ font-size: 0.9rem;
1209
+ color: #666;
1210
+ }
1211
+ </style>
1212
+
1213
+ <div class="wrap">
1214
+ <div class="controls">
1215
+ <label title="Load PNG file">
1216
+ <input type="file" accept="image/png" part="file-input" />
1217
+ <span class="btn">Choose PNG</span>
1218
+ </label>
1219
+ <div class="hint">Only PNG images accepted. Drop PNG onto the box below.</div>
1220
+ </div>
1221
+
1222
+ <div class="drop-area" part="drop-area">Drop PNG here or click "Choose PNG"</div>
1223
+ </div>
1224
+ `;
1225
+
1226
+ this._fileInput = this.shadowRoot.querySelector('input[type="file"]');
1227
+ this._dropArea = this.shadowRoot.querySelector('.drop-area');
1228
+
1229
+ this._editor = null; // will hold external editor instance
1230
+ this._options = { fitMode: 'contain' };
1231
+
1232
+ // Bind handlers
1233
+ this._onFileChange = this._onFileChange.bind(this);
1234
+ this._onDrop = this._onDrop.bind(this);
1235
+ this._onDragOver = this._onDragOver.bind(this);
1236
+ this._onDragLeave = this._onDragLeave.bind(this);
1237
+ }
1238
+
1239
+ static get observedAttributes() {
1240
+ return ['editor-selector', 'fit-mode', 'target-cells-x', 'target-cells-y'];
1241
+ }
1242
+
1243
+ attributeChangedCallback(name, oldVal, newVal) {
1244
+ if (name === 'editor-selector' && newVal) {
1245
+ const el = document.querySelector(newVal);
1246
+ if (el) this.setEditor(el);
1247
+ }
1248
+ if (name === 'fit-mode') {
1249
+ this._options.fitMode = newVal || 'contain';
1250
+ }
1251
+ }
1252
+
1253
+ connectedCallback() {
1254
+ this._fileInput.addEventListener('change', this._onFileChange);
1255
+ this._dropArea.addEventListener('dragover', this._onDragOver);
1256
+ this._dropArea.addEventListener('dragleave', this._onDragLeave);
1257
+ this._dropArea.addEventListener('drop', this._onDrop);
1258
+ this.addEventListener('dragover', this._onDragOver);
1259
+ this.addEventListener('dragleave', this._onDragLeave);
1260
+ this.addEventListener('drop', this._onDrop);
1261
+
1262
+ // If editor-selector attribute was present at creation, try to resolve
1263
+ const sel = this.getAttribute('editor-selector');
1264
+ if (sel) {
1265
+ const target = document.querySelector(sel);
1266
+ if (target) this.setEditor(target);
1267
+ }
1268
+
1269
+ // read fit-mode
1270
+ const fit = this.getAttribute('fit-mode');
1271
+ if (fit) this._options.fitMode = fit;
1272
+ }
1273
+
1274
+ disconnectedCallback() {
1275
+ this._fileInput.removeEventListener('change', this._onFileChange);
1276
+ this._dropArea.removeEventListener('dragover', this._onDragOver);
1277
+ this._dropArea.removeEventListener('dragleave', this._onDragLeave);
1278
+ this._dropArea.removeEventListener('drop', this._onDrop);
1279
+ this.removeEventListener('dragover', this._onDragOver);
1280
+ this.removeEventListener('dragleave', this._onDragLeave);
1281
+ this.removeEventListener('drop', this._onDrop);
1282
+ }
1283
+
1284
+ // ----------------- Public API -----------------
1285
+ setEditor(editor) {
1286
+ if (!editor) throw new Error('Editor cannot be null/undefined');
1287
+ if (typeof editor.loadMatrix !== 'function') {
1288
+ throw new Error('Provided editor does not expose loadMatrix(matrix)');
1289
+ }
1290
+ this._editor = editor;
1291
+ this.dispatchEvent(new CustomEvent('editorconnected', { detail: { editor } }));
1292
+ }
1293
+
1294
+ setOptions(options = {}) {
1295
+ if (options.fitMode) this._options.fitMode = options.fitMode;
1296
+ if (options.targetCellsX) this.setAttribute('target-cells-x', String(options.targetCellsX));
1297
+ if (options.targetCellsY) this.setAttribute('target-cells-y', String(options.targetCellsY));
1298
+ }
1299
+
1300
+ get editor() {
1301
+ return this._editor;
1302
+ }
1303
+
1304
+ // ----------------- Events -----------------
1305
+ _onFileChange(e) {
1306
+ const file = e.target.files && e.target.files[0] ? e.target.files[0] : null;
1307
+ if (!file) return;
1308
+ this._handleFile(file);
1309
+ this._fileInput.value = '';
1310
+ }
1311
+
1312
+ _onDragOver(e) {
1313
+ e.preventDefault();
1314
+ e.dataTransfer.dropEffect = 'copy';
1315
+ this._dropArea.classList.add('dragover');
1316
+ }
1317
+ _onDragLeave(e) {
1318
+ e.preventDefault();
1319
+ this._dropArea.classList.remove('dragover');
1320
+ }
1321
+
1322
+ _onDrop(e) {
1323
+ e.preventDefault();
1324
+ this._dropArea.classList.remove('dragover');
1325
+ const file = e.dataTransfer.files && e.dataTransfer.files[0] ? e.dataTransfer.files[0] : null;
1326
+ if (!file) return;
1327
+ this._handleFile(file);
1328
+ }
1329
+
1330
+ // ----------------- File handling -----------------
1331
+ async _handleFile(file) {
1332
+ const isPngByType = file.type === 'image/png';
1333
+ const isPngByName = file.name && file.name.toLowerCase().endsWith('.png');
1334
+ if (!isPngByType && !isPngByName) {
1335
+ this._showError('Only PNG files are supported.');
1336
+ return;
1337
+ }
1338
+
1339
+ if (!this._editor) {
1340
+ this._showError('No editor connected. Use setEditor(editor) or provide editor-selector attribute.');
1341
+ return;
1342
+ }
1343
+
1344
+ try {
1345
+ await this._loadPngToEditorAdaptive(file);
1346
+ this._dispatchLoadedEvent(file.name);
1347
+ } catch (err) {
1348
+ console.error('Failed to load PNG', err);
1349
+ this._showError('Failed to load PNG (see console).');
1350
+ }
1351
+ }
1352
+
1353
+ _showError(msg) {
1354
+ NotificationManager.Push({
1355
+ status: 'error',
1356
+ html: msg,
1357
+ });
1358
+ }
1359
+ _dispatchLoadedEvent(filename) {
1360
+ this.dispatchEvent(new CustomEvent('pngloaded', { detail: { filename } }));
1361
+ }
1362
+
1363
+ // ----------------- Adaptive load -----------------
1364
+ _readEditorConfig() {
1365
+ const ed = this._editor;
1366
+ const cfg = { pixelSize: null, cellsX: null, cellsY: null };
1367
+
1368
+ if (!ed) return cfg;
1369
+
1370
+ // pixel size detection (try multiple forms)
1371
+ cfg.pixelSize = ed.pixelSize || ed.pixel_size || null;
1372
+ if (!cfg.pixelSize) {
1373
+ const attr = ed.getAttribute && (ed.getAttribute('pixel-size') || ed.getAttribute('pixelSize'));
1374
+ if (attr) cfg.pixelSize = parseInt(attr, 10);
1375
+ }
1376
+ if (typeof cfg.pixelSize === 'string') cfg.pixelSize = parseInt(cfg.pixelSize, 10);
1377
+
1378
+ // cells detection (common attribute names: width/height on engine represent cells)
1379
+ const widthAttr = ed.getAttribute && ed.getAttribute('width');
1380
+ const heightAttr = ed.getAttribute && ed.getAttribute('height');
1381
+ if (widthAttr && heightAttr) {
1382
+ cfg.cellsX = parseInt(widthAttr, 10);
1383
+ cfg.cellsY = parseInt(heightAttr, 10);
1384
+ }
1385
+
1386
+ // alternative property names
1387
+ cfg.cellsX = cfg.cellsX || ed.cellsX || ed.cellCountX || (ed.cells && ed.cells.x) || null;
1388
+ cfg.cellsY = cfg.cellsY || ed.cellsY || ed.cellCountY || (ed.cells && ed.cells.y) || null;
1389
+
1390
+ // if editor exposes getCells() prefer that
1391
+ try {
1392
+ if ((!cfg.cellsX || !cfg.cellsY) && typeof ed.getCells === 'function') {
1393
+ const c = ed.getCells();
1394
+ if (c && c.x && c.y) {
1395
+ cfg.cellsX = cfg.cellsX || c.x;
1396
+ cfg.cellsY = cfg.cellsY || c.y;
1397
+ }
1398
+ }
1399
+ } catch (e) {
1400
+ /* ignore */
1401
+ }
1402
+
1403
+ return cfg;
1404
+ }
1405
+
1406
+ // core adaptive loader: scales image to editor cells (or computes fallback)
1407
+ async _loadPngToEditorAdaptive(blobOrFile) {
1408
+ const imgBitmap = await createImageBitmap(blobOrFile);
1409
+ const srcW = imgBitmap.width;
1410
+ const srcH = imgBitmap.height;
1411
+
1412
+ // read editor config and loader explicit overrides
1413
+ const editorCfg = this._readEditorConfig();
1414
+ const overrideX = this.getAttribute('target-cells-x');
1415
+ const overrideY = this.getAttribute('target-cells-y');
1416
+
1417
+ let targetCellsX = overrideX ? parseInt(overrideX, 10) : editorCfg.cellsX || null;
1418
+ let targetCellsY = overrideY ? parseInt(overrideY, 10) : editorCfg.cellsY || null;
1419
+
1420
+ // if cells unknown but pixelSize known, compute approximate cells from image dimensions
1421
+ if ((!targetCellsX || !targetCellsY) && editorCfg.pixelSize) {
1422
+ const px = parseInt(editorCfg.pixelSize, 10);
1423
+ if (px > 0) {
1424
+ if (!targetCellsX) targetCellsX = Math.max(1, Math.round(srcW / px));
1425
+ if (!targetCellsY) targetCellsY = Math.max(1, Math.round(srcH / px));
1426
+ }
1427
+ }
1428
+
1429
+ // if still missing, fallback to native image pixels
1430
+ if (!targetCellsX) targetCellsX = srcW;
1431
+ if (!targetCellsY) targetCellsY = srcH;
1432
+
1433
+ // Decide fit mode
1434
+ const fitMode = this._options.fitMode || this.getAttribute('fit-mode') || 'contain';
1435
+
1436
+ // Create a small canvas sized to the target cells (we will render the image into this canvas
1437
+ // with smoothing disabled to preserve blocky/pixel look). Then read each pixel as a cell.
1438
+ const small = document.createElement('canvas');
1439
+ small.width = targetCellsX;
1440
+ small.height = targetCellsY;
1441
+ const sctx = small.getContext('2d');
1442
+
1443
+ // nearest-neighbour / crisp scaling
1444
+ sctx.imageSmoothingEnabled = false;
1445
+ sctx.clearRect(0, 0, small.width, small.height);
1446
+
1447
+ if (fitMode === 'stretch') {
1448
+ // non-uniform scale to fill exactly
1449
+ sctx.drawImage(imgBitmap, 0, 0, srcW, srcH, 0, 0, small.width, small.height);
1450
+ } else {
1451
+ // compute uniform scale to contain or cover
1452
+ let scaleX = small.width / srcW;
1453
+ let scaleY = small.height / srcH;
1454
+ let scale = fitMode === 'cover' ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
1455
+ // compute destination size in small-canvas pixels
1456
+ const destW = Math.max(1, Math.round(srcW * scale));
1457
+ const destH = Math.max(1, Math.round(srcH * scale));
1458
+ const dx = Math.floor((small.width - destW) / 2);
1459
+ const dy = Math.floor((small.height - destH) / 2);
1460
+ sctx.drawImage(imgBitmap, 0, 0, srcW, srcH, dx, dy, destW, destH);
1461
+ }
1462
+
1463
+ // read pixel data from the small canvas
1464
+ const imageData = sctx.getImageData(0, 0, small.width, small.height).data;
1465
+
1466
+ // build matrix[y][x] = [r,g,b,a]
1467
+ const matrix = new Array(small.height);
1468
+ let p = 0;
1469
+ for (let y = 0; y < small.height; y++) {
1470
+ const row = new Array(small.width);
1471
+ for (let x = 0; x < small.width; x++) {
1472
+ const r = imageData[p++];
1473
+ const g = imageData[p++];
1474
+ const b = imageData[p++];
1475
+ const a = imageData[p++];
1476
+ row[x] = [r, g, b, a];
1477
+ }
1478
+ matrix[y] = row;
1479
+ }
1480
+
1481
+ // attempt to optionally align editor settings (best-effort)
1482
+ try {
1483
+ // if editor has setCells(x,y) or setCellCount, call it
1484
+ if (typeof this._editor.setCells === 'function') {
1485
+ this._editor.setCells(small.width, small.height);
1486
+ } else if (typeof this._editor.setCellCount === 'function') {
1487
+ this._editor.setCellCount(small.width, small.height);
1488
+ } else {
1489
+ // try common attribute setter
1490
+ if (this._editor.setAttribute) {
1491
+ this._editor.setAttribute('width', String(small.width));
1492
+ this._editor.setAttribute('height', String(small.height));
1493
+ }
1494
+ }
1495
+
1496
+ // if editor has setPixelSize and editorCfg.pixelSize exists, keep it
1497
+ if (editorCfg.pixelSize && typeof this._editor.setPixelSize === 'function') {
1498
+ this._editor.setPixelSize(parseInt(editorCfg.pixelSize, 10));
1499
+ }
1500
+ } catch (e) {
1501
+ // non-critical; continue
1502
+ console.warn('Failed to align editor config:', e);
1503
+ }
1504
+
1505
+ // finally, hand matrix to editor
1506
+ if (!this._editor || typeof this._editor.loadMatrix !== 'function') {
1507
+ throw new Error('Editor disconnected or does not expose loadMatrix');
1508
+ }
1509
+
1510
+ this._editor.loadMatrix(matrix);
1511
+ }
1512
+
1513
+ // Public helpers
1514
+ async loadPngBlob(blob) {
1515
+ return this._handleFile(blob);
1516
+ }
1517
+ async loadPngUrl(url) {
1518
+ const resp = await fetch(url);
1519
+ const blob = await resp.blob();
1520
+ return this._handleFile(blob);
1521
+ }
1522
+ }
1523
+
1524
+ customElements.define('object-layer-png-loader', ObjectLayerPngLoader);
1525
+
1526
+ /*
1527
+
1528
+ Example usage:
1529
+
1530
+ // HTML
1531
+ <object-layer-engine id="editor"></object-layer-engine>
1532
+ <object-layer-png-loader id="loader" editor-selector="#editor"></object-layer-png-loader>
1533
+
1534
+ // JS (programmatic)
1535
+ const editor = document.getElementById('editor');
1536
+ const loader = document.getElementById('loader');
1537
+ // Alternatively: loader.setEditor(editor);
1538
+ loader.addEventListener('pngloaded', (e) => console.log('Loaded', e.detail.filename));
1539
+
1540
+ */