cyberia 3.0.3 → 3.2.5

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 (296) hide show
  1. package/{.env.production → .env.example} +20 -4
  2. package/.github/workflows/engine-cyberia.cd.yml +43 -10
  3. package/.github/workflows/engine-cyberia.ci.yml +48 -26
  4. package/.github/workflows/ghpkg.ci.yml +5 -5
  5. package/.github/workflows/gitlab.ci.yml +1 -1
  6. package/.github/workflows/hardhat.ci.yml +82 -0
  7. package/.github/workflows/npmpkg.ci.yml +60 -14
  8. package/.github/workflows/publish.ci.yml +26 -7
  9. package/.github/workflows/publish.cyberia.ci.yml +5 -5
  10. package/.github/workflows/pwa-microservices-template-page.cd.yml +6 -7
  11. package/.github/workflows/pwa-microservices-template-test.ci.yml +4 -4
  12. package/.github/workflows/release.cd.yml +14 -8
  13. package/.vscode/extensions.json +9 -8
  14. package/.vscode/settings.json +3 -2
  15. package/CHANGELOG.md +643 -1
  16. package/CLI-HELP.md +132 -57
  17. package/Dockerfile +4 -2
  18. package/README.md +347 -22
  19. package/WHITE-PAPER.md +1540 -0
  20. package/bin/build.js +21 -12
  21. package/bin/cyberia.js +2640 -106
  22. package/bin/deploy.js +258 -372
  23. package/bin/file.js +5 -1
  24. package/bin/index.js +2640 -106
  25. package/bin/vs.js +3 -3
  26. package/conf.js +169 -105
  27. package/deployment.yaml +236 -20
  28. package/hardhat/.env.example +31 -0
  29. package/hardhat/README.md +531 -0
  30. package/hardhat/WHITE-PAPER.md +1540 -0
  31. package/hardhat/contracts/ObjectLayerToken.sol +391 -0
  32. package/hardhat/deployments/.gitkeep +0 -0
  33. package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
  34. package/hardhat/hardhat.config.js +136 -0
  35. package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
  36. package/hardhat/networks/besu-object-layer.network.json +138 -0
  37. package/hardhat/package-lock.json +4323 -0
  38. package/hardhat/package.json +36 -0
  39. package/hardhat/scripts/deployObjectLayerToken.js +98 -0
  40. package/hardhat/test/ObjectLayerToken.js +592 -0
  41. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  42. package/hardhat/types/ethers-contracts/common.ts +92 -0
  43. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  44. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  45. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  46. package/hardhat/types/ethers-contracts/index.ts +6 -0
  47. package/jsdoc.dd-cyberia.json +68 -0
  48. package/jsdoc.json +65 -49
  49. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
  50. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
  51. package/manifests/deployment/dd-cyberia-development/deployment.yaml +562 -0
  52. package/manifests/deployment/dd-cyberia-development/proxy.yaml +297 -0
  53. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +132 -0
  54. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  55. package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
  56. package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
  57. package/manifests/deployment/playwright/deployment.yaml +1 -1
  58. package/manifests/pv-pvc-dd.yaml +1 -1
  59. package/nodemon.json +1 -1
  60. package/package.json +60 -48
  61. package/proxy.yaml +118 -10
  62. package/pv-pvc.yaml +132 -0
  63. package/scripts/k3s-node-setup.sh +1 -1
  64. package/scripts/ports-ls.sh +2 -0
  65. package/scripts/rhel-grpc-setup.sh +56 -0
  66. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +47 -1
  67. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +17 -2
  68. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  69. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
  70. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
  71. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  72. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
  73. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
  74. package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
  75. package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
  76. package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
  77. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  78. package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
  79. package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
  80. package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
  81. package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
  82. package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
  83. package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
  84. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
  85. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
  86. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
  87. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
  88. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
  89. package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
  90. package/src/api/cyberia-map/cyberia-map.model.js +30 -0
  91. package/src/api/cyberia-map/cyberia-map.router.js +40 -0
  92. package/src/api/cyberia-map/cyberia-map.service.js +74 -0
  93. package/src/api/document/document.service.js +1 -1
  94. package/src/api/file/file.controller.js +3 -1
  95. package/src/api/file/file.ref.json +18 -0
  96. package/src/api/file/file.service.js +28 -5
  97. package/src/api/ipfs/ipfs.controller.js +4 -25
  98. package/src/api/ipfs/ipfs.model.js +43 -34
  99. package/src/api/ipfs/ipfs.router.js +8 -13
  100. package/src/api/ipfs/ipfs.service.js +56 -104
  101. package/src/api/object-layer/README.md +347 -22
  102. package/src/api/object-layer/object-layer.controller.js +6 -2
  103. package/src/api/object-layer/object-layer.model.js +12 -8
  104. package/src/api/object-layer/object-layer.router.js +698 -42
  105. package/src/api/object-layer/object-layer.service.js +119 -37
  106. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +1 -2
  107. package/src/api/user/user.router.js +10 -5
  108. package/src/api/user/user.service.js +15 -14
  109. package/src/cli/baremetal.js +6 -10
  110. package/src/cli/cloud-init.js +0 -3
  111. package/src/cli/cluster.js +7 -7
  112. package/src/cli/db.js +723 -857
  113. package/src/cli/deploy.js +215 -105
  114. package/src/cli/env.js +34 -5
  115. package/src/cli/fs.js +5 -4
  116. package/src/cli/image.js +0 -3
  117. package/src/cli/index.js +83 -15
  118. package/src/cli/kubectl.js +211 -0
  119. package/src/cli/monitor.js +5 -6
  120. package/src/cli/release.js +284 -0
  121. package/src/cli/repository.js +708 -62
  122. package/src/cli/run.js +371 -151
  123. package/src/cli/secrets.js +73 -2
  124. package/src/cli/ssh.js +1 -1
  125. package/src/cli/test.js +3 -3
  126. package/src/client/Cryptokoyn.index.js +3 -4
  127. package/src/client/CyberiaPortal.index.js +3 -4
  128. package/src/client/Default.index.js +3 -4
  129. package/src/client/Itemledger.index.js +4 -963
  130. package/src/client/Underpost.index.js +3 -4
  131. package/src/client/components/core/AgGrid.js +20 -5
  132. package/src/client/components/core/Alert.js +2 -2
  133. package/src/client/components/core/AppStore.js +69 -0
  134. package/src/client/components/core/CalendarCore.js +2 -2
  135. package/src/client/components/core/Content.js +22 -3
  136. package/src/client/components/core/Docs.js +30 -6
  137. package/src/client/components/core/DropDown.js +137 -17
  138. package/src/client/components/core/FileExplorer.js +71 -4
  139. package/src/client/components/core/Input.js +1 -1
  140. package/src/client/components/core/Keyboard.js +2 -2
  141. package/src/client/components/core/LogIn.js +2 -2
  142. package/src/client/components/core/LogOut.js +2 -2
  143. package/src/client/components/core/Modal.js +20 -7
  144. package/src/client/components/core/Panel.js +0 -1
  145. package/src/client/components/core/PanelForm.js +19 -19
  146. package/src/client/components/core/RichText.js +1 -2
  147. package/src/client/components/core/SocketIo.js +82 -29
  148. package/src/client/components/core/SocketIoHandler.js +75 -0
  149. package/src/client/components/core/Stream.js +143 -95
  150. package/src/client/components/core/Webhook.js +40 -7
  151. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  152. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
  153. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
  154. package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
  155. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
  156. package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
  157. package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
  158. package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
  159. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
  160. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  161. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +217 -30
  162. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +44 -2
  163. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -4
  164. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
  165. package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +104 -9
  166. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +5 -0
  167. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
  168. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  169. package/src/client/components/default/AppStoreDefault.js +5 -0
  170. package/src/client/components/default/LogInDefault.js +3 -3
  171. package/src/client/components/default/LogOutDefault.js +2 -2
  172. package/src/client/components/default/MenuDefault.js +5 -5
  173. package/src/client/components/default/SocketIoDefault.js +3 -51
  174. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  175. package/src/client/components/itemledger/LogInItemledger.js +3 -3
  176. package/src/client/components/itemledger/LogOutItemledger.js +2 -2
  177. package/src/client/components/itemledger/MenuItemledger.js +3 -3
  178. package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
  179. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  180. package/src/client/components/underpost/CssUnderpost.js +59 -0
  181. package/src/client/components/underpost/LogInUnderpost.js +6 -3
  182. package/src/client/components/underpost/LogOutUnderpost.js +4 -2
  183. package/src/client/components/underpost/MenuUnderpost.js +104 -18
  184. package/src/client/components/underpost/RoutesUnderpost.js +2 -0
  185. package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
  186. package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
  187. package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
  188. package/src/client/public/cryptokoyn/microdata.json +85 -0
  189. package/src/client/public/cryptokoyn/site.webmanifest +57 -0
  190. package/src/client/public/cryptokoyn/sitemap +3 -3
  191. package/src/client/public/default/sitemap +3 -3
  192. package/src/client/public/itemledger/browserconfig.xml +2 -2
  193. package/src/client/public/itemledger/manifest.webmanifest +4 -4
  194. package/src/client/public/itemledger/microdata.json +71 -0
  195. package/src/client/public/itemledger/sitemap +3 -3
  196. package/src/client/public/itemledger/yandex-browser-manifest.json +2 -2
  197. package/src/client/public/test/sitemap +3 -3
  198. package/src/client/services/core/core.service.js +20 -8
  199. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
  200. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  201. package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
  202. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  203. package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
  204. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
  205. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  206. package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
  207. package/src/client/services/instance/instance.management.js +2 -2
  208. package/src/client/services/ipfs/ipfs.service.js +3 -23
  209. package/src/client/services/object-layer/object-layer.management.js +3 -3
  210. package/src/client/services/object-layer/object-layer.service.js +21 -0
  211. package/src/client/services/user/user.management.js +2 -2
  212. package/src/client/ssr/body/404.js +15 -11
  213. package/src/client/ssr/body/500.js +15 -11
  214. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  215. package/src/client/ssr/head/PwaItemledger.js +60 -0
  216. package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
  217. package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
  218. package/src/client/ssr/pages/Test.js +11 -10
  219. package/src/client.build.js +0 -3
  220. package/src/client.dev.js +0 -3
  221. package/src/db/DataBaseProvider.js +17 -2
  222. package/src/db/mariadb/MariaDB.js +14 -9
  223. package/src/db/mongo/MongooseDB.js +17 -1
  224. package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
  225. package/src/grpc/cyberia/README.md +326 -0
  226. package/src/grpc/cyberia/grpc-server.js +530 -0
  227. package/src/index.js +24 -1
  228. package/src/proxy.js +0 -3
  229. package/src/runtime/express/Dockerfile +4 -0
  230. package/src/runtime/express/Express.js +33 -10
  231. package/src/runtime/lampp/Dockerfile +13 -2
  232. package/src/runtime/lampp/Lampp.js +33 -17
  233. package/src/runtime/wp/Dockerfile +68 -0
  234. package/src/runtime/wp/Wp.js +639 -0
  235. package/src/server/auth.js +36 -15
  236. package/src/server/backup.js +39 -12
  237. package/src/server/besu-genesis-generator.js +1630 -0
  238. package/src/server/client-build-docs.js +133 -17
  239. package/src/server/client-build-live.js +9 -18
  240. package/src/server/client-build.js +229 -101
  241. package/src/server/client-dev-server.js +14 -13
  242. package/src/server/client-formatted.js +109 -57
  243. package/src/server/conf.js +391 -164
  244. package/src/server/cron.js +27 -24
  245. package/src/server/dns.js +29 -12
  246. package/src/server/downloader.js +0 -2
  247. package/src/server/ipfs-client.js +24 -1
  248. package/src/server/logger.js +27 -9
  249. package/src/server/object-layer.js +217 -103
  250. package/src/server/peer.js +8 -2
  251. package/src/server/process.js +1 -50
  252. package/src/server/proxy.js +4 -8
  253. package/src/server/runtime.js +30 -9
  254. package/src/server/semantic-layer-generator-floor.js +359 -0
  255. package/src/server/semantic-layer-generator-skin.js +1294 -0
  256. package/src/server/semantic-layer-generator.js +116 -555
  257. package/src/server/ssr.js +0 -3
  258. package/src/server/start.js +19 -12
  259. package/src/server/tls.js +0 -2
  260. package/src/server.js +0 -4
  261. package/src/ws/IoInterface.js +1 -10
  262. package/src/ws/IoServer.js +14 -33
  263. package/src/ws/core/channels/core.ws.chat.js +65 -20
  264. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  265. package/src/ws/core/channels/core.ws.stream.js +90 -31
  266. package/src/ws/core/core.ws.connection.js +12 -33
  267. package/src/ws/core/core.ws.emit.js +10 -26
  268. package/src/ws/core/core.ws.server.js +25 -58
  269. package/src/ws/default/channels/default.ws.main.js +53 -12
  270. package/src/ws/default/default.ws.connection.js +26 -13
  271. package/src/ws/default/default.ws.server.js +30 -12
  272. package/.env.development +0 -43
  273. package/.env.test +0 -43
  274. package/hardhat/contracts/CryptoKoyn.sol +0 -59
  275. package/hardhat/contracts/ItemLedger.sol +0 -73
  276. package/hardhat/contracts/Lock.sol +0 -34
  277. package/hardhat/hardhat.config.cjs +0 -45
  278. package/hardhat/ignition/modules/Lock.js +0 -18
  279. package/hardhat/networks/cryptokoyn-itemledger.network.json +0 -29
  280. package/hardhat/scripts/deployCryptokoyn.cjs +0 -25
  281. package/hardhat/scripts/deployItemledger.cjs +0 -25
  282. package/hardhat/test/Lock.js +0 -126
  283. package/hardhat/white-paper.md +0 -581
  284. package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
  285. package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
  286. package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
  287. package/src/client/components/default/ElementsDefault.js +0 -38
  288. package/src/client/components/itemledger/CommonItemledger.js +0 -29
  289. package/src/client/components/itemledger/ElementsItemledger.js +0 -38
  290. package/src/client/components/underpost/CommonUnderpost.js +0 -29
  291. package/src/client/components/underpost/ElementsUnderpost.js +0 -38
  292. package/src/ws/core/management/core.ws.chat.js +0 -8
  293. package/src/ws/core/management/core.ws.mailer.js +0 -16
  294. package/src/ws/core/management/core.ws.stream.js +0 -8
  295. package/src/ws/default/management/default.ws.main.js +0 -8
  296. package/white-paper.md +0 -581
@@ -0,0 +1,700 @@
1
+ import { BtnIcon } from '../core/BtnIcon.js';
2
+ import { Input, InputFile, getFileFromBlobEndpoint } from '../core/Input.js';
3
+ import { htmls, s } from '../core/VanillaJs.js';
4
+ import { NotificationManager } from '../core/NotificationManager.js';
5
+ import { Translate } from '../core/Translate.js';
6
+ import { dynamicCol, darkTheme, ThemeEvents } from '../core/Css.js';
7
+ import { DropDown } from '../core/DropDown.js';
8
+ import { CyberiaInstanceManagement } from '../../services/cyberia-instance/cyberia-instance.management.js';
9
+ import { CyberiaInstanceService } from '../../services/cyberia-instance/cyberia-instance.service.js';
10
+ import { CyberiaMapService } from '../../services/cyberia-map/cyberia-map.service.js';
11
+ import { FileService } from '../../services/file/file.service.js';
12
+ import { DefaultManagement } from '../../services/default/default.management.js';
13
+ import { getApiBaseUrl } from '../../services/core/core.service.js';
14
+
15
+ class InstanceEngineCyberia {
16
+ static currentInstanceId = null;
17
+ static currentThumbnailId = null;
18
+ static thumbnailDirty = false;
19
+ static portals = [];
20
+
21
+ static renderPortalList(containerId) {
22
+ const container = s(`.${containerId}`);
23
+ if (!container) return;
24
+
25
+ const filterSource = s('.instance-engine-filter-source')?.value?.trim().toLowerCase() || '';
26
+ const filterTarget = s('.instance-engine-filter-target')?.value?.trim().toLowerCase() || '';
27
+
28
+ const filtered = [];
29
+ InstanceEngineCyberia.portals.forEach((portal, i) => {
30
+ if (filterSource && !(portal.sourceMapCode || '').toLowerCase().includes(filterSource)) return;
31
+ if (filterTarget && !(portal.targetMapCode || '').toLowerCase().includes(filterTarget)) return;
32
+ filtered.push({ portal, i });
33
+ });
34
+
35
+ let listHtml = '';
36
+ filtered.forEach(({ portal, i }) => {
37
+ listHtml += html`<div class="fl" style="border-bottom:1px solid #444; padding:4px 0; align-items:center;">
38
+ <div class="in fll" style="flex:1;font-size:12px;font-family:monospace;">
39
+ <span style="color:${darkTheme ? '#8cf' : '#246'};">${portal.sourceMapCode}</span>
40
+ (${portal.sourceCellX}, ${portal.sourceCellY})
41
+ <span style="margin:0 4px;">&rarr;</span>
42
+ <span style="color:${darkTheme ? '#fc8' : '#842'};">${portal.targetMapCode}</span>
43
+ (${portal.targetCellX}, ${portal.targetCellY})
44
+ </div>
45
+ <div class="in fll" style="display:flex;gap:3px;">
46
+ <button
47
+ class="btn-instance-engine-load-portal"
48
+ data-index="${i}"
49
+ style="cursor:pointer;background:#36a;color:#fff;border:none;padding:2px 8px;font-size:12px;"
50
+ >
51
+ <i class="fa-solid fa-clone"></i>
52
+ </button>
53
+ <button
54
+ class="btn-instance-engine-remove-portal"
55
+ data-index="${i}"
56
+ style="cursor:pointer;background:#a00;color:#fff;border:none;padding:2px 8px;font-size:12px;"
57
+ >
58
+ <i class="fa-solid fa-trash"></i>
59
+ </button>
60
+ </div>
61
+ </div>`;
62
+ });
63
+
64
+ if (!listHtml)
65
+ listHtml = `<div style="color:#888;font-size:13px;">${
66
+ InstanceEngineCyberia.portals.length > 0 ? 'No matching portals.' : 'No portals added yet.'
67
+ }</div>`;
68
+
69
+ htmls(`.${containerId}`, listHtml);
70
+
71
+ container.querySelectorAll('.btn-instance-engine-remove-portal').forEach((btn) => {
72
+ btn.onclick = () => {
73
+ const idx = parseInt(btn.dataset.index);
74
+ InstanceEngineCyberia.portals.splice(idx, 1);
75
+ InstanceEngineCyberia.renderPortalList(containerId);
76
+ };
77
+ });
78
+
79
+ container.querySelectorAll('.btn-instance-engine-load-portal').forEach((btn) => {
80
+ btn.onclick = () => {
81
+ const idx = parseInt(btn.dataset.index);
82
+ const portal = InstanceEngineCyberia.portals[idx];
83
+ if (!portal) return;
84
+ if (s('.instance-engine-source-map-code'))
85
+ s('.instance-engine-source-map-code').value = portal.sourceMapCode || '';
86
+ if (s('.instance-engine-source-cell-x')) s('.instance-engine-source-cell-x').value = portal.sourceCellX || 0;
87
+ if (s('.instance-engine-source-cell-y')) s('.instance-engine-source-cell-y').value = portal.sourceCellY || 0;
88
+ if (s('.instance-engine-target-map-code'))
89
+ s('.instance-engine-target-map-code').value = portal.targetMapCode || '';
90
+ if (s('.instance-engine-target-cell-x')) s('.instance-engine-target-cell-x').value = portal.targetCellX || 0;
91
+ if (s('.instance-engine-target-cell-y')) s('.instance-engine-target-cell-y').value = portal.targetCellY || 0;
92
+ };
93
+ });
94
+ }
95
+
96
+ static async render(options = {}) {
97
+ const { appStore } = options;
98
+ const idCode = 'instance-engine-input-code';
99
+ const idName = 'instance-engine-input-name';
100
+ const idDescription = 'instance-engine-input-description';
101
+ const idTags = 'instance-engine-input-tags';
102
+ const idStatus = 'instance-engine-input-status';
103
+ const idThumbnail = 'instance-engine-input-thumbnail';
104
+ const idMapCodesDropdown = 'instance-engine-map-codes-dropdown';
105
+ const managementId = 'modal-cyberia-instance-engine';
106
+ const portalListId = 'instance-engine-portal-list';
107
+ const idSourceMapCode = 'instance-engine-source-map-code';
108
+ const idSourceCellX = 'instance-engine-source-cell-x';
109
+ const idSourceCellY = 'instance-engine-source-cell-y';
110
+ const idTargetMapCode = 'instance-engine-target-map-code';
111
+ const idTargetCellX = 'instance-engine-target-cell-x';
112
+ const idTargetCellY = 'instance-engine-target-cell-y';
113
+ const idFilterSource = 'instance-engine-filter-source';
114
+ const idFilterTarget = 'instance-engine-filter-target';
115
+
116
+ InstanceEngineCyberia.currentInstanceId = null;
117
+ InstanceEngineCyberia.currentThumbnailId = null;
118
+ InstanceEngineCyberia.thumbnailDirty = false;
119
+ InstanceEngineCyberia.portals = [];
120
+
121
+ const getInstancePayload = () => {
122
+ const tagsRaw = s(`.${idTags}`)?.value || '';
123
+ const tags = tagsRaw
124
+ .split(',')
125
+ .map((t) => t.trim())
126
+ .filter((t) => t);
127
+ const cyberiaMapCodes = DropDown.Tokens[idMapCodesDropdown]?.value
128
+ ? [...DropDown.Tokens[idMapCodesDropdown].value]
129
+ : [];
130
+ const payload = {
131
+ code: s(`.${idCode}`)?.value || '',
132
+ name: s(`.${idName}`)?.value || '',
133
+ description: s(`.${idDescription}`)?.value || '',
134
+ tags,
135
+ status: DropDown.Tokens[idStatus]?.value || 'unlisted',
136
+ cyberiaMapCodes,
137
+ portals: InstanceEngineCyberia.portals,
138
+ };
139
+ if (InstanceEngineCyberia.currentThumbnailId) payload.thumbnail = InstanceEngineCyberia.currentThumbnailId;
140
+ return payload;
141
+ };
142
+
143
+ const saveInstance = async () => {
144
+ // Upload thumbnail file only if user selected a new one
145
+ const thumbnailInput = s(`.${idThumbnail}`);
146
+ if (
147
+ InstanceEngineCyberia.thumbnailDirty &&
148
+ thumbnailInput &&
149
+ thumbnailInput.files &&
150
+ thumbnailInput.files.length > 0
151
+ ) {
152
+ const formData = new FormData();
153
+ formData.append('file', thumbnailInput.files[0]);
154
+ const uploadResult = await FileService.post({ body: formData });
155
+ if (uploadResult.status === 'success' && uploadResult.data && uploadResult.data.length > 0) {
156
+ InstanceEngineCyberia.currentThumbnailId = uploadResult.data[0]._id;
157
+ } else {
158
+ NotificationManager.Push({
159
+ html: uploadResult.message || 'Failed to upload thumbnail',
160
+ status: 'error',
161
+ });
162
+ return;
163
+ }
164
+ }
165
+
166
+ const body = getInstancePayload();
167
+ let result;
168
+ if (InstanceEngineCyberia.currentInstanceId) {
169
+ result = await CyberiaInstanceService.put({ id: InstanceEngineCyberia.currentInstanceId, body });
170
+ } else {
171
+ result = await CyberiaInstanceService.post({ body });
172
+ }
173
+ NotificationManager.Push({
174
+ html:
175
+ result.status === 'error'
176
+ ? result.message
177
+ : InstanceEngineCyberia.currentInstanceId
178
+ ? Translate.Render('success-update-item')
179
+ : Translate.Render('success-create-item'),
180
+ status: result.status,
181
+ });
182
+ if (result.status === 'success') {
183
+ if (result.data?._id) InstanceEngineCyberia.currentInstanceId = result.data._id;
184
+ await DefaultManagement.loadTable(managementId, { force: true, reload: true });
185
+ }
186
+ };
187
+
188
+ const loadInstance = async (instanceData) => {
189
+ InstanceEngineCyberia.currentInstanceId = instanceData._id || null;
190
+ if (s(`.${idCode}`)) s(`.${idCode}`).value = instanceData.code || '';
191
+ if (s(`.${idName}`)) s(`.${idName}`).value = instanceData.name || '';
192
+ if (s(`.${idDescription}`)) s(`.${idDescription}`).value = instanceData.description || '';
193
+ if (s(`.${idTags}`)) s(`.${idTags}`).value = (instanceData.tags || []).join(', ');
194
+ const statusValue = instanceData.status || 'unlisted';
195
+ if (DropDown.Tokens[idStatus]) {
196
+ const statusIndex = statusOptions.findIndex((opt) => opt.value === statusValue);
197
+ if (statusIndex > -1) s(`.dropdown-option-${idStatus}-${statusIndex}`).click();
198
+ }
199
+
200
+ // Thumbnail
201
+ InstanceEngineCyberia.currentThumbnailId = instanceData.thumbnail || null;
202
+ const thumbnailPreview = s(`.instance-engine-thumbnail-preview`);
203
+ if (InstanceEngineCyberia.currentThumbnailId) {
204
+ const thumbId =
205
+ typeof InstanceEngineCyberia.currentThumbnailId === 'object'
206
+ ? InstanceEngineCyberia.currentThumbnailId._id
207
+ : InstanceEngineCyberia.currentThumbnailId;
208
+
209
+ // Set preview image
210
+ if (thumbnailPreview) {
211
+ thumbnailPreview.innerHTML = html`<img
212
+ src="${getApiBaseUrl({ id: thumbId, endpoint: 'file/blob' })}"
213
+ style="max-width:120px;max-height:120px;border:1px solid #555;"
214
+ onerror="this.style.display='none';"
215
+ />`;
216
+ }
217
+
218
+ // Populate InputFile with the actual file from server
219
+ if (s(`.${idThumbnail}`)) {
220
+ const fileData = await getFileFromBlobEndpoint({ _id: thumbId, mimetype: 'image/png' });
221
+ if (fileData) {
222
+ const dataTransfer = new DataTransfer();
223
+ dataTransfer.items.add(fileData);
224
+ s(`.${idThumbnail}`).files = dataTransfer.files;
225
+ s(`.${idThumbnail}`).onchange({ target: s(`.${idThumbnail}`) });
226
+ }
227
+ }
228
+ InstanceEngineCyberia.thumbnailDirty = false;
229
+ } else {
230
+ if (thumbnailPreview) thumbnailPreview.innerHTML = '';
231
+ if (s(`.${idThumbnail}`)) {
232
+ s(`.${idThumbnail}`).value = '';
233
+ s(`.${idThumbnail}`).onchange({ target: s(`.${idThumbnail}`) });
234
+ }
235
+ }
236
+
237
+ // Creator display
238
+ const creatorDisplay = s(`.instance-engine-creator-display`);
239
+ if (creatorDisplay) {
240
+ if (instanceData.creator) {
241
+ const creatorUsername =
242
+ typeof instanceData.creator === 'object'
243
+ ? instanceData.creator.username || instanceData.creator._id
244
+ : instanceData.creator;
245
+ creatorDisplay.innerHTML = html`<span style="font-family:monospace;font-size:12px;"
246
+ >${creatorUsername}</span
247
+ >`;
248
+ } else {
249
+ creatorDisplay.innerHTML = html`<span style="color:#888;font-size:12px;">—</span>`;
250
+ }
251
+ }
252
+
253
+ // Load cyberiaMapCodes into the dropdown
254
+ if (DropDown.Tokens[idMapCodesDropdown]) {
255
+ DropDown.Tokens[idMapCodesDropdown].oncheckvalues = {};
256
+ const mapCodes = instanceData.cyberiaMapCodes || [];
257
+ for (const code of mapCodes) {
258
+ const key = code.trim().replaceAll(' ', '-');
259
+ DropDown.Tokens[idMapCodesDropdown].oncheckvalues[key] = { data: code, display: code, value: code };
260
+ }
261
+ DropDown.Tokens[idMapCodesDropdown].value = mapCodes;
262
+ if (s(`.${idMapCodesDropdown}`)) s(`.${idMapCodesDropdown}`).value = mapCodes;
263
+ if (s(`.dropdown-current-${idMapCodesDropdown}`)) {
264
+ DropDown.Tokens[idMapCodesDropdown]._renderSelectedBadges?.();
265
+ }
266
+ }
267
+
268
+ // Load portals
269
+ InstanceEngineCyberia.portals = (instanceData.portals || []).map((p) => ({
270
+ sourceMapCode: p.sourceMapCode || '',
271
+ sourceCellX: p.sourceCellX || 0,
272
+ sourceCellY: p.sourceCellY || 0,
273
+ targetMapCode: p.targetMapCode || '',
274
+ targetCellX: p.targetCellX || 0,
275
+ targetCellY: p.targetCellY || 0,
276
+ }));
277
+ InstanceEngineCyberia.renderPortalList(portalListId);
278
+ };
279
+
280
+ const resetForm = () => {
281
+ InstanceEngineCyberia.currentInstanceId = null;
282
+ InstanceEngineCyberia.currentThumbnailId = null;
283
+ InstanceEngineCyberia.thumbnailDirty = false;
284
+ if (s(`.${idCode}`)) s(`.${idCode}`).value = '';
285
+ if (s(`.${idName}`)) s(`.${idName}`).value = '';
286
+ if (s(`.${idDescription}`)) s(`.${idDescription}`).value = '';
287
+ if (s(`.${idTags}`)) s(`.${idTags}`).value = '';
288
+ if (DropDown.Tokens[idStatus]) {
289
+ const resetIndex = statusOptions.findIndex((opt) => opt.value === 'unlisted');
290
+ if (resetIndex > -1) s(`.dropdown-option-${idStatus}-${resetIndex}`).click();
291
+ }
292
+ const thumbnailPreview = s(`.instance-engine-thumbnail-preview`);
293
+ if (thumbnailPreview) thumbnailPreview.innerHTML = '';
294
+ if (s(`.${idThumbnail}`)) {
295
+ s(`.${idThumbnail}`).value = '';
296
+ s(`.${idThumbnail}`).onchange({ target: s(`.${idThumbnail}`) });
297
+ }
298
+ const creatorDisplay = s(`.instance-engine-creator-display`);
299
+ if (creatorDisplay) creatorDisplay.innerHTML = '<span style="color:#888;font-size:12px;">—</span>';
300
+ if (DropDown.Tokens[idMapCodesDropdown]) {
301
+ DropDown.Tokens[idMapCodesDropdown].oncheckvalues = {};
302
+ DropDown.Tokens[idMapCodesDropdown].value = [];
303
+ htmls(`.dropdown-current-${idMapCodesDropdown}`, '');
304
+ htmls(`.${idMapCodesDropdown}-render-container`, '');
305
+ }
306
+ InstanceEngineCyberia.portals = [];
307
+ InstanceEngineCyberia.renderPortalList(portalListId);
308
+ };
309
+
310
+ setTimeout(() => {
311
+ if (s(`.btn-instance-engine-save`)) s(`.btn-instance-engine-save`).onclick = () => saveInstance();
312
+ if (s(`.btn-instance-engine-new`)) s(`.btn-instance-engine-new`).onclick = () => resetForm();
313
+
314
+ if (s(`.btn-instance-engine-toggle-thumbnail`))
315
+ s(`.btn-instance-engine-toggle-thumbnail`).onclick = () => {
316
+ const body = s(`.instance-engine-thumbnail-body`);
317
+ const caret = s(`.instance-engine-thumbnail-caret`);
318
+ if (body) body.classList.toggle('hide');
319
+ if (caret) {
320
+ caret.classList.toggle('fa-caret-right');
321
+ caret.classList.toggle('fa-caret-down');
322
+ }
323
+ };
324
+
325
+ // Portal management
326
+ if (s(`.btn-instance-engine-portal-connect`))
327
+ s(`.btn-instance-engine-portal-connect`).onclick = async () => {
328
+ if (!InstanceEngineCyberia.currentInstanceId) {
329
+ NotificationManager.Push({
330
+ html: Translate.Render('save-instance-first') || 'Save the instance first.',
331
+ status: 'warning',
332
+ });
333
+ return;
334
+ }
335
+ const btn = s(`.btn-instance-engine-portal-connect`);
336
+ if (btn) btn.disabled = true;
337
+ try {
338
+ const result = await CyberiaInstanceService.portalConnect({ id: InstanceEngineCyberia.currentInstanceId });
339
+ if (result.status === 'error') {
340
+ NotificationManager.Push({ html: result.message, status: 'error' });
341
+ return;
342
+ }
343
+ const { portals: generated, topology, message } = result.data || {};
344
+ if (!generated || generated.length === 0) {
345
+ NotificationManager.Push({
346
+ html: message || 'No portals could be generated.',
347
+ status: 'warning',
348
+ });
349
+ return;
350
+ }
351
+ // Append generated portals (skip duplicates by source+target pair)
352
+ const existing = new Set(InstanceEngineCyberia.portals.map((p) => `${p.sourceMapCode}>${p.targetMapCode}`));
353
+ let added = 0;
354
+ for (const p of generated) {
355
+ const key = `${p.sourceMapCode}>${p.targetMapCode}`;
356
+ if (!existing.has(key)) {
357
+ InstanceEngineCyberia.portals.push(p);
358
+ existing.add(key);
359
+ added++;
360
+ }
361
+ }
362
+ InstanceEngineCyberia.renderPortalList(portalListId);
363
+ NotificationManager.Push({
364
+ html: `${topology} ring — ${added} portal(s) added.`,
365
+ status: added > 0 ? 'success' : 'warning',
366
+ });
367
+ } catch (e) {
368
+ NotificationManager.Push({ html: e.message, status: 'error' });
369
+ } finally {
370
+ if (btn) btn.disabled = false;
371
+ }
372
+ };
373
+
374
+ if (s(`.btn-instance-engine-add-portal`))
375
+ s(`.btn-instance-engine-add-portal`).onclick = () => {
376
+ const portal = {
377
+ sourceMapCode: s(`.${idSourceMapCode}`)?.value || '',
378
+ sourceCellX: parseInt(s(`.${idSourceCellX}`)?.value) || 0,
379
+ sourceCellY: parseInt(s(`.${idSourceCellY}`)?.value) || 0,
380
+ targetMapCode: s(`.${idTargetMapCode}`)?.value || '',
381
+ targetCellX: parseInt(s(`.${idTargetCellX}`)?.value) || 0,
382
+ targetCellY: parseInt(s(`.${idTargetCellY}`)?.value) || 0,
383
+ };
384
+ InstanceEngineCyberia.portals.push(portal);
385
+ InstanceEngineCyberia.renderPortalList(portalListId);
386
+ };
387
+
388
+ if (s('.btn-instance-engine-toggle-portal-filter'))
389
+ s('.btn-instance-engine-toggle-portal-filter').onclick = () => {
390
+ const body = s('.instance-engine-portal-filter-body');
391
+ const caret = s('.instance-engine-portal-filter-caret');
392
+ if (body) body.classList.toggle('hide');
393
+ if (caret) {
394
+ caret.classList.toggle('fa-caret-right');
395
+ caret.classList.toggle('fa-caret-down');
396
+ }
397
+ };
398
+
399
+ let portalFilterTimeout = null;
400
+ const applyPortalFilter = () => {
401
+ clearTimeout(portalFilterTimeout);
402
+ portalFilterTimeout = setTimeout(() => {
403
+ InstanceEngineCyberia.renderPortalList(portalListId);
404
+ }, 300);
405
+ };
406
+ [idFilterSource, idFilterTarget].forEach((cls) => {
407
+ if (s(`.${cls}`)) s(`.${cls}`).addEventListener('input', applyPortalFilter);
408
+ });
409
+
410
+ if (s('.btn-instance-engine-clear-portal-filter'))
411
+ s('.btn-instance-engine-clear-portal-filter').onclick = () => {
412
+ [idFilterSource, idFilterTarget].forEach((cls) => {
413
+ if (s(`.${cls}`)) s(`.${cls}`).value = '';
414
+ });
415
+ InstanceEngineCyberia.renderPortalList(portalListId);
416
+ };
417
+
418
+ ThemeEvents['instance-engine-theme'] = () => {
419
+ InstanceEngineCyberia.renderPortalList(portalListId);
420
+ };
421
+ });
422
+
423
+ const statusOptions = [
424
+ { value: 'unlisted', display: 'unlisted', data: 'unlisted', onClick: () => {} },
425
+ { value: 'draft', display: 'draft', data: 'draft', onClick: () => {} },
426
+ { value: 'published', display: 'published', data: 'published', onClick: () => {} },
427
+ { value: 'archived', display: 'archived', data: 'archived', onClick: () => {} },
428
+ ];
429
+
430
+ const managementTableHtml = await CyberiaInstanceManagement.RenderTable({
431
+ idModal: managementId,
432
+ loadInstanceCallback: loadInstance,
433
+ appStore,
434
+ readyRowDataEvent: {
435
+ 'instance-engine-check-deleted': (rowData) => {
436
+ if (InstanceEngineCyberia.currentInstanceId) {
437
+ const stillExists = rowData.some((row) => row._id === InstanceEngineCyberia.currentInstanceId);
438
+ if (!stillExists) InstanceEngineCyberia.currentInstanceId = null;
439
+ }
440
+ },
441
+ },
442
+ });
443
+
444
+ const dcFields = 'instance-engine-dc-fields';
445
+ const dcMetaFields = 'instance-engine-dc-meta';
446
+ const dcSaveNew = 'instance-engine-dc-save-new';
447
+ const dcPortalSource = 'instance-engine-dc-portal-source';
448
+ const dcPortalTarget = 'instance-engine-dc-portal-target';
449
+ const dcPortalFilter = 'instance-engine-dc-portal-filter';
450
+
451
+ return html`<div class="in section-mp instance-engine-container">
452
+ ${dynamicCol({ containerSelector: 'instance-engine-container', id: dcFields, type: 'search-inputs' })}
453
+ <div class="fl">
454
+ <div class="in fll ${dcFields}-col-a">
455
+ ${await Input.Render({
456
+ id: idCode,
457
+ label: html`Code`,
458
+ containerClass: 'inl',
459
+ type: 'text',
460
+ })}
461
+ </div>
462
+ <div class="in fll ${dcFields}-col-b">
463
+ ${await Input.Render({
464
+ id: idName,
465
+ label: html`Name`,
466
+ containerClass: 'inl',
467
+ type: 'text',
468
+ })}
469
+ </div>
470
+ <div class="in fll ${dcFields}-col-c">
471
+ ${await Input.Render({
472
+ id: idDescription,
473
+ label: html`Description`,
474
+ containerClass: 'inl',
475
+ type: 'text',
476
+ })}
477
+ </div>
478
+ </div>
479
+ ${dynamicCol({ containerSelector: 'instance-engine-container', id: dcMetaFields, type: 'search-inputs' })}
480
+ <div class="fl">
481
+ <div class="in fll ${dcMetaFields}-col-a">
482
+ ${await Input.Render({
483
+ id: idTags,
484
+ label: html`Tags (comma separated)`,
485
+ containerClass: 'inl',
486
+ type: 'text',
487
+ })}
488
+ </div>
489
+ <div class="in fll ${dcMetaFields}-col-b">
490
+ ${await DropDown.Render({
491
+ id: idStatus,
492
+ label: html`Status`,
493
+ data: statusOptions.map((opt) => ({ ...opt })),
494
+ value: 'unlisted',
495
+ containerClass: 'inl',
496
+ })}
497
+ </div>
498
+ <div class="in fll ${dcMetaFields}-col-c">
499
+ <div class="inl">
500
+ <div class="in input-label">Creator</div>
501
+ <div class="in instance-engine-creator-display">
502
+ <span style="color:#888;font-size:12px;">—</span>
503
+ </div>
504
+ </div>
505
+ </div>
506
+ </div>
507
+ <div class="in section-mp" style="margin-top: 5px;">
508
+ <div class="in instance-engine-thumbnail-preview" style="margin-bottom: 5px;"></div>
509
+ ${await BtnIcon.Render({
510
+ class: 'wfa btn-instance-engine-toggle-thumbnail',
511
+ label: html`<i class="fa-solid fa-caret-right instance-engine-thumbnail-caret"></i> Thumbnail`,
512
+ })}
513
+ <div class="in instance-engine-thumbnail-body hide">
514
+ ${await InputFile.Render(
515
+ {
516
+ id: idThumbnail,
517
+ multiple: false,
518
+ extensionsAccept: ['image/png', 'image/jpeg'],
519
+ },
520
+ {
521
+ change: (e) => {
522
+ const file = e.target.files[0];
523
+ if (file) {
524
+ InstanceEngineCyberia.thumbnailDirty = true;
525
+ const url = URL.createObjectURL(file);
526
+ const preview = s('.instance-engine-thumbnail-preview');
527
+ if (preview)
528
+ preview.innerHTML = html`<img
529
+ src="${url}"
530
+ class="in"
531
+ style="max-width:300px;height:auto;border:1px solid #555;margin:auto"
532
+ />`;
533
+ }
534
+ },
535
+ clear: () => {
536
+ InstanceEngineCyberia.thumbnailDirty = true;
537
+ InstanceEngineCyberia.currentThumbnailId = null;
538
+ const preview = s('.instance-engine-thumbnail-preview');
539
+ if (preview) preview.innerHTML = '';
540
+ },
541
+ },
542
+ )}
543
+ </div>
544
+ </div>
545
+ <div class="in section-mp" style="margin-top: 10px;">
546
+ ${await DropDown.Render({
547
+ id: idMapCodesDropdown,
548
+ label: html`Cyberia Map Codes`,
549
+ data: [],
550
+ type: 'checkbox',
551
+ containerClass: 'inl',
552
+ excludeSelected: true,
553
+ serviceProvider: async (q) => {
554
+ const result = await CyberiaMapService.searchCodes({ q });
555
+ if (result.status === 'success' && result.data?.codes) {
556
+ return result.data.codes.map((code) => ({
557
+ value: code,
558
+ display: code,
559
+ data: code,
560
+ onClick: () => {},
561
+ }));
562
+ }
563
+ return [];
564
+ },
565
+ })}
566
+ </div>
567
+ <div class="in section-mp" style="margin-top: 10px;">
568
+ <div class="in input-label" style="font-size:14px;margin-bottom:5px;">Portals</div>
569
+ ${dynamicCol({ containerSelector: 'instance-engine-container', id: dcPortalSource, type: 'search-inputs' })}
570
+ <div class="fl">
571
+ <div class="in fll ${dcPortalSource}-col-a">
572
+ ${await Input.Render({
573
+ id: idSourceMapCode,
574
+ label: html`Source Map Code`,
575
+ containerClass: 'inl',
576
+ type: 'text',
577
+ })}
578
+ </div>
579
+ <div class="in fll ${dcPortalSource}-col-b">
580
+ ${await Input.Render({
581
+ id: idSourceCellX,
582
+ label: html`Source Cell X`,
583
+ containerClass: 'inl',
584
+ type: 'number',
585
+ min: 0,
586
+ value: 0,
587
+ })}
588
+ </div>
589
+ <div class="in fll ${dcPortalSource}-col-c">
590
+ ${await Input.Render({
591
+ id: idSourceCellY,
592
+ label: html`Source Cell Y`,
593
+ containerClass: 'inl',
594
+ type: 'number',
595
+ min: 0,
596
+ value: 0,
597
+ })}
598
+ </div>
599
+ </div>
600
+ ${dynamicCol({ containerSelector: 'instance-engine-container', id: dcPortalTarget, type: 'search-inputs' })}
601
+ <div class="fl">
602
+ <div class="in fll ${dcPortalTarget}-col-a">
603
+ ${await Input.Render({
604
+ id: idTargetMapCode,
605
+ label: html`Target Map Code`,
606
+ containerClass: 'inl',
607
+ type: 'text',
608
+ })}
609
+ </div>
610
+ <div class="in fll ${dcPortalTarget}-col-b">
611
+ ${await Input.Render({
612
+ id: idTargetCellX,
613
+ label: html`Target Cell X`,
614
+ containerClass: 'inl',
615
+ type: 'number',
616
+ min: 0,
617
+ value: 0,
618
+ })}
619
+ </div>
620
+ <div class="in fll ${dcPortalTarget}-col-c">
621
+ ${await Input.Render({
622
+ id: idTargetCellY,
623
+ label: html`Target Cell Y`,
624
+ containerClass: 'inl',
625
+ type: 'number',
626
+ min: 0,
627
+ value: 0,
628
+ })}
629
+ </div>
630
+ </div>
631
+ <div class="in" style="display:flex;gap:5px;flex-wrap:wrap;">
632
+ ${await BtnIcon.Render({
633
+ class: 'wfa btn-instance-engine-add-portal',
634
+ label: html`<i class="fa-solid fa-plus"></i> Add Portal`,
635
+ })}
636
+ ${await BtnIcon.Render({
637
+ class: 'wfa btn-instance-engine-portal-connect',
638
+ label: html`<i class="fa-solid fa-circle-nodes"></i> Portal Connector`,
639
+ })}
640
+ </div>
641
+ <div class="in" style="margin-top: 10px;">
642
+ ${await BtnIcon.Render({
643
+ class: 'wfa btn-instance-engine-toggle-portal-filter',
644
+ label: html`<i class="fa-solid fa-caret-right instance-engine-portal-filter-caret"></i> Filters`,
645
+ })}
646
+ <div class="in instance-engine-portal-filter-body hide">
647
+ ${dynamicCol({ containerSelector: 'instance-engine-container', id: dcPortalFilter, type: 'a-50-b-50' })}
648
+ <div class="fl">
649
+ <div class="in fll ${dcPortalFilter}-col-a">
650
+ ${await Input.Render({
651
+ id: idFilterSource,
652
+ label: html`Source Map Code`,
653
+ containerClass: 'inl',
654
+ type: 'text',
655
+ placeholder: true,
656
+ })}
657
+ </div>
658
+ <div class="in fll ${dcPortalFilter}-col-b">
659
+ ${await Input.Render({
660
+ id: idFilterTarget,
661
+ label: html`Target Map Code`,
662
+ containerClass: 'inl',
663
+ type: 'text',
664
+ placeholder: true,
665
+ })}
666
+ </div>
667
+ </div>
668
+ <div class="in" style="margin-top:5px;">
669
+ ${await BtnIcon.Render({
670
+ class: 'wfa btn-instance-engine-clear-portal-filter',
671
+ label: html`<i class="fa-solid fa-broom"></i> Clear Filters`,
672
+ })}
673
+ </div>
674
+ </div>
675
+ </div>
676
+ <div class="in ${portalListId}" style="margin-top: 10px; max-height: 200px; overflow-y: auto;"></div>
677
+ </div>
678
+ <div class="in section-mp" style="margin-top: 10px;">
679
+ ${dynamicCol({ containerSelector: 'instance-engine-container', id: dcSaveNew, type: 'a-50-b-50' })}
680
+ <div class="fl">
681
+ <div class="in fll ${dcSaveNew}-col-a" style="padding: 5px;">
682
+ ${await BtnIcon.Render({
683
+ class: 'wfa btn-instance-engine-save',
684
+ label: html`<i class="fa-solid fa-floppy-disk"></i> Save Instance`,
685
+ })}
686
+ </div>
687
+ <div class="in fll ${dcSaveNew}-col-b" style="padding: 5px;">
688
+ ${await BtnIcon.Render({
689
+ class: 'wfa btn-instance-engine-new',
690
+ label: html`<i class="fa-solid fa-file"></i> New Instance`,
691
+ })}
692
+ </div>
693
+ </div>
694
+ <div class="in" style="margin-top: 10px;">${managementTableHtml}</div>
695
+ </div>
696
+ </div>`;
697
+ }
698
+ }
699
+
700
+ export { InstanceEngineCyberia };