@ultrst-armada/krnl 1.0.2 → 1.0.4

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 (237) hide show
  1. package/README.md +67 -65
  2. package/package.json +15 -8
  3. package/packages/cli/dist/bin/krnl.cjs +808 -331
  4. package/packages/cli/dist/bin/krnl.js +50 -65
  5. package/packages/cli/dist/chunk-GHWYEAPD.js +1642 -0
  6. package/packages/cli/dist/index.cjs +761 -263
  7. package/packages/cli/dist/index.d.cts +45 -2
  8. package/packages/cli/dist/index.d.ts +45 -2
  9. package/packages/cli/dist/index.js +5 -1
  10. package/packages/cli/package.json +17 -10
  11. package/packages/core/dist/index.cjs +2912 -140
  12. package/packages/core/dist/index.d.cts +614 -27
  13. package/packages/core/dist/index.d.ts +614 -27
  14. package/packages/core/dist/index.js +2903 -140
  15. package/packages/core/package.json +15 -6
  16. package/packages/create-krnl/dist/index.cjs +102 -65
  17. package/packages/create-krnl/dist/index.js +102 -65
  18. package/packages/create-krnl/package.json +5 -3
  19. package/packages/eslint-plugin/dist/index.cjs +17 -55
  20. package/packages/eslint-plugin/dist/index.d.cts +8 -43
  21. package/packages/eslint-plugin/dist/index.d.ts +8 -43
  22. package/packages/eslint-plugin/dist/index.js +17 -55
  23. package/packages/eslint-plugin/package.json +5 -4
  24. package/packages/validate/dist/index.cjs +378 -85
  25. package/packages/validate/dist/index.d.cts +11 -1
  26. package/packages/validate/dist/index.d.ts +11 -1
  27. package/packages/validate/dist/index.js +375 -82
  28. package/packages/validate/package.json +14 -7
  29. package/packages/cli/dist/K_ENT_Entity-PQ5MURQY-ZG6FZXXX.js +0 -13
  30. package/packages/cli/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-K4CRQYA5.js +0 -8
  31. package/packages/cli/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-LNPXZYOE.js +0 -110
  32. package/packages/cli/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-3ALZPNX4.js +0 -11
  33. package/packages/cli/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-PJTICYSW.js +0 -11
  34. package/packages/cli/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-M5TYU55O.js +0 -8
  35. package/packages/cli/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-H6STOXRK.js +0 -20
  36. package/packages/cli/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-XPJXSF7D.js +0 -19
  37. package/packages/cli/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-CUYGTWOW.js +0 -7
  38. package/packages/cli/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-VR6T4N6J.js +0 -17
  39. package/packages/cli/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-RLJ4QWDP.js +0 -58
  40. package/packages/cli/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-BOQGUASC.js +0 -22
  41. package/packages/cli/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-XJI7KIT5.js +0 -8
  42. package/packages/cli/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-K5VCWAFW.js +0 -9
  43. package/packages/cli/dist/chunk-3TRM6CYL.js +0 -152
  44. package/packages/cli/dist/chunk-5JDK35TG.js +0 -3872
  45. package/packages/cli/dist/chunk-63PTK33I.js +0 -997
  46. package/packages/cli/dist/chunk-6NIKAMUZ.js +0 -82
  47. package/packages/cli/dist/chunk-BOXWWXQG.js +0 -400
  48. package/packages/cli/dist/chunk-EEKLK7QJ.js +0 -565
  49. package/packages/cli/dist/chunk-EG7T2IAD.js +0 -107
  50. package/packages/cli/dist/chunk-ESTKOQ7T.js +0 -1146
  51. package/packages/cli/dist/chunk-F5EHXE2O.js +0 -1036
  52. package/packages/cli/dist/chunk-F65ANSQG.js +0 -269
  53. package/packages/cli/dist/chunk-FDNCADFE.js +0 -71
  54. package/packages/cli/dist/chunk-FZ6X7PHR.js +0 -1146
  55. package/packages/cli/dist/chunk-GDLRMDAN.js +0 -127
  56. package/packages/cli/dist/chunk-HWMTCMOS.js +0 -656
  57. package/packages/cli/dist/chunk-I74QIQOD.js +0 -5536
  58. package/packages/cli/dist/chunk-JTLSTO25.js +0 -71
  59. package/packages/cli/dist/chunk-JULBBJ27.js +0 -1033
  60. package/packages/cli/dist/chunk-OHVMVRBP.js +0 -117
  61. package/packages/cli/dist/chunk-P2PCLEKX.js +0 -666
  62. package/packages/cli/dist/chunk-PJQIUOX2.js +0 -7767
  63. package/packages/cli/dist/chunk-PPOF3DJW.js +0 -471
  64. package/packages/cli/dist/chunk-Q45257P6.js +0 -173
  65. package/packages/cli/dist/chunk-QNROWGBI.js +0 -1036
  66. package/packages/cli/dist/chunk-RJNYUXZD.js +0 -977
  67. package/packages/cli/dist/chunk-SRTOPJ5W.js +0 -104
  68. package/packages/cli/dist/chunk-TCEX3MPS.js +0 -702
  69. package/packages/cli/dist/chunk-TNOAJO2J.js +0 -675
  70. package/packages/cli/dist/chunk-UR2SUCSF.js +0 -701
  71. package/packages/cli/dist/chunk-X5LOIFM7.js +0 -68
  72. package/packages/cli/dist/chunk-XIRFOO3H.js +0 -589
  73. package/packages/cli/dist/chunk-ZAAV3JXQ.js +0 -809
  74. package/packages/core/dist/K_ENT_Entity-4MB7CU4A.js +0 -12
  75. package/packages/core/dist/K_ENT_Entity-DEJGIDGH.js +0 -12
  76. package/packages/core/dist/K_ENT_Entity-FXZO5UZ2.js +0 -12
  77. package/packages/core/dist/K_ENT_Entity-HDSCXC5F.js +0 -12
  78. package/packages/core/dist/K_ENT_Entity-JILABHLW.js +0 -12
  79. package/packages/core/dist/K_ENT_Entity-PQ5MURQY.js +0 -13
  80. package/packages/core/dist/K_ENT_Entity-ROAKDIHW.js +0 -12
  81. package/packages/core/dist/K_ENT_Entity-TBVFSKDV.js +0 -12
  82. package/packages/core/dist/K_ENT_Entity-ZFLKG3JO.js +0 -12
  83. package/packages/core/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW.js +0 -8
  84. package/packages/core/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF.js +0 -110
  85. package/packages/core/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF.js +0 -9
  86. package/packages/core/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X.js +0 -9
  87. package/packages/core/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW.js +0 -8
  88. package/packages/core/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2.js +0 -20
  89. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-4C6GBQAG.js +0 -18
  90. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-6V23A6F6.js +0 -18
  91. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK.js +0 -19
  92. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-DYLLC5G2.js +0 -18
  93. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-F4FOFAUP.js +0 -18
  94. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-FBOXXMI3.js +0 -18
  95. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-Q73BWLNP.js +0 -18
  96. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QM2C4MVA.js +0 -18
  97. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QUGI6SMP.js +0 -18
  98. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RGMTBHNP.js +0 -18
  99. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RWR7UNDW.js +0 -18
  100. package/packages/core/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7.js +0 -7
  101. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-32MFJJ5Z.js +0 -17
  102. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-4J4CEWRY.js +0 -17
  103. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-A366YHF2.js +0 -17
  104. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-LK4LYJHI.js +0 -17
  105. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU.js +0 -17
  106. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-NW3K63II.js +0 -17
  107. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-QDHTI5ZV.js +0 -17
  108. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-VHCT6VIW.js +0 -17
  109. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-WBRKF24R.js +0 -17
  110. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-XKYQYT6Q.js +0 -17
  111. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-Z2PWZYDZ.js +0 -17
  112. package/packages/core/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5.js +0 -58
  113. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-2EZU7QF4.js +0 -21
  114. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-6G3AQNAV.js +0 -21
  115. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-B2NZMQUG.js +0 -21
  116. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-C2RABMZP.js +0 -21
  117. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-HHE3NZ4F.js +0 -21
  118. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-LHFGR6WK.js +0 -21
  119. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-M74BLM3N.js +0 -21
  120. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-PT6ZA6EA.js +0 -21
  121. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-SP52V3CX.js +0 -21
  122. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF.js +0 -22
  123. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-YNSEUHJJ.js +0 -21
  124. package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-M6SBHIVA.js +0 -8
  125. package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG.js +0 -8
  126. package/packages/core/dist/K_LIB_Krnl_Telemetry-77FRZ3KV.js +0 -9
  127. package/packages/core/dist/chunk-33L2J7V7.js +0 -82
  128. package/packages/core/dist/chunk-3ES4K2ZP.js +0 -817
  129. package/packages/core/dist/chunk-3SMHVVLL.js +0 -82
  130. package/packages/core/dist/chunk-3UEZ7JQA.js +0 -4877
  131. package/packages/core/dist/chunk-3XIE6KBD.js +0 -4877
  132. package/packages/core/dist/chunk-46ZLCML6.js +0 -107
  133. package/packages/core/dist/chunk-4UEL3DWJ.js +0 -107
  134. package/packages/core/dist/chunk-54GWZ5PC.js +0 -82
  135. package/packages/core/dist/chunk-6QCYRS5L.js +0 -127
  136. package/packages/core/dist/chunk-6ULMHNXR.js +0 -104
  137. package/packages/core/dist/chunk-7WUETJEV.js +0 -117
  138. package/packages/core/dist/chunk-A5KRAYDB.js +0 -107
  139. package/packages/core/dist/chunk-ANEAZ3PO.js +0 -107
  140. package/packages/core/dist/chunk-BE76RL6F.js +0 -82
  141. package/packages/core/dist/chunk-CIARVYDO.js +0 -107
  142. package/packages/core/dist/chunk-CR2ANE62.js +0 -107
  143. package/packages/core/dist/chunk-DI27MOR7.js +0 -173
  144. package/packages/core/dist/chunk-E6RLFHM5.js +0 -596
  145. package/packages/core/dist/chunk-EELAPMNV.js +0 -4877
  146. package/packages/core/dist/chunk-ENZ2RQVJ.js +0 -107
  147. package/packages/core/dist/chunk-F2U6CX4F.js +0 -4877
  148. package/packages/core/dist/chunk-FKG64OHL.js +0 -82
  149. package/packages/core/dist/chunk-FYAZJYM5.js +0 -772
  150. package/packages/core/dist/chunk-G4NP4RDQ.js +0 -82
  151. package/packages/core/dist/chunk-GADIRLRS.js +0 -473
  152. package/packages/core/dist/chunk-GR6CSLQJ.js +0 -772
  153. package/packages/core/dist/chunk-HDS7AT6O.js +0 -772
  154. package/packages/core/dist/chunk-HIMEZUU2.js +0 -71
  155. package/packages/core/dist/chunk-IB4UHG5I.js +0 -596
  156. package/packages/core/dist/chunk-J7ZYNVRS.js +0 -772
  157. package/packages/core/dist/chunk-JVLZU4K7.js +0 -604
  158. package/packages/core/dist/chunk-JYXXI7Q3.js +0 -82
  159. package/packages/core/dist/chunk-K54K2KZW.js +0 -192
  160. package/packages/core/dist/chunk-K6VHLBNH.js +0 -3316
  161. package/packages/core/dist/chunk-KCBP7ZYA.js +0 -596
  162. package/packages/core/dist/chunk-KFQGP6VL.js +0 -33
  163. package/packages/core/dist/chunk-KIUEUI7K.js +0 -82
  164. package/packages/core/dist/chunk-LB67NIZP.js +0 -949
  165. package/packages/core/dist/chunk-LDTYFUNW.js +0 -596
  166. package/packages/core/dist/chunk-MLVWARD4.js +0 -596
  167. package/packages/core/dist/chunk-MPYVXHKO.js +0 -2777
  168. package/packages/core/dist/chunk-MRQ3MITU.js +0 -772
  169. package/packages/core/dist/chunk-MVUN7I7O.js +0 -107
  170. package/packages/core/dist/chunk-NDDOIRW7.js +0 -596
  171. package/packages/core/dist/chunk-O3KDB5MH.js +0 -3330
  172. package/packages/core/dist/chunk-OEINUDSE.js +0 -400
  173. package/packages/core/dist/chunk-OPDENPT4.js +0 -4877
  174. package/packages/core/dist/chunk-P6XEM2LA.js +0 -429
  175. package/packages/core/dist/chunk-QKMRSQV4.js +0 -82
  176. package/packages/core/dist/chunk-R6CCWALL.js +0 -4877
  177. package/packages/core/dist/chunk-RAMMOO73.js +0 -774
  178. package/packages/core/dist/chunk-S3FTZFG6.js +0 -772
  179. package/packages/core/dist/chunk-T2ZVN434.js +0 -4877
  180. package/packages/core/dist/chunk-TFHLDMIA.js +0 -107
  181. package/packages/core/dist/chunk-TI3QUCIJ.js +0 -107
  182. package/packages/core/dist/chunk-TR2BAIGI.js +0 -107
  183. package/packages/core/dist/chunk-UGVVJKC6.js +0 -4877
  184. package/packages/core/dist/chunk-V2DX7YIU.js +0 -984
  185. package/packages/core/dist/chunk-W4IHYUYD.js +0 -772
  186. package/packages/core/dist/chunk-W6WBG5OY.js +0 -4877
  187. package/packages/core/dist/chunk-WN3M7O7H.js +0 -3312
  188. package/packages/core/dist/chunk-WR3NCU7U.js +0 -4877
  189. package/packages/core/dist/chunk-XC6B6L27.js +0 -82
  190. package/packages/core/dist/chunk-XNSE64ZY.js +0 -3316
  191. package/packages/core/dist/chunk-XROES2WN.js +0 -634
  192. package/packages/core/dist/chunk-XSCKNRCZ.js +0 -5020
  193. package/packages/core/dist/chunk-XVABPAUZ.js +0 -82
  194. package/packages/core/dist/chunk-YOBCB5C5.js +0 -596
  195. package/packages/core/dist/chunk-YUK6Q563.js +0 -666
  196. package/packages/core/dist/chunk-YXGL3ZEU.js +0 -1030
  197. package/packages/core/dist/chunk-ZINJ5OUX.js +0 -596
  198. package/packages/core/dist/chunk-ZPFD4BIQ.js +0 -604
  199. package/packages/core/dist/chunk-ZQZBFG6F.js +0 -4826
  200. package/packages/core/dist/chunk-ZZFRJGXM.js +0 -4877
  201. package/packages/create-krnl/dist/K_ENT_Entity-PQ5MURQY-R2OH4VXE.js +0 -14
  202. package/packages/create-krnl/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-AZM5HF4M.js +0 -9
  203. package/packages/create-krnl/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-X5N6SV2S.js +0 -111
  204. package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-5PFHF4JU.js +0 -12
  205. package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-ED33OQPH.js +0 -12
  206. package/packages/create-krnl/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-YGQLVR4P.js +0 -9
  207. package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-VM7CTU2L.js +0 -21
  208. package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-Y2GQFJ7I.js +0 -21
  209. package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-L3JQWO54.js +0 -8
  210. package/packages/create-krnl/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-KQARASPR.js +0 -19
  211. package/packages/create-krnl/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-OF3IGLVL.js +0 -59
  212. package/packages/create-krnl/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-JOLHKOEX.js +0 -24
  213. package/packages/create-krnl/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-L46LSHKQ.js +0 -10
  214. package/packages/create-krnl/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-4JUPRR3X.js +0 -10
  215. package/packages/create-krnl/dist/chunk-3TRM6CYL.js +0 -152
  216. package/packages/create-krnl/dist/chunk-3URCB3QS.js +0 -127
  217. package/packages/create-krnl/dist/chunk-5JDK35TG.js +0 -3872
  218. package/packages/create-krnl/dist/chunk-A2XAXDOW.js +0 -812
  219. package/packages/create-krnl/dist/chunk-AN6TMQHG.js +0 -1033
  220. package/packages/create-krnl/dist/chunk-BOXWWXQG.js +0 -400
  221. package/packages/create-krnl/dist/chunk-BPTBREBG.js +0 -38
  222. package/packages/create-krnl/dist/chunk-FDNCADFE.js +0 -71
  223. package/packages/create-krnl/dist/chunk-JTLSTO25.js +0 -71
  224. package/packages/create-krnl/dist/chunk-M3NCUIPL.js +0 -107
  225. package/packages/create-krnl/dist/chunk-N2EONSOC.js +0 -7769
  226. package/packages/create-krnl/dist/chunk-OHVMVRBP.js +0 -117
  227. package/packages/create-krnl/dist/chunk-Q45257P6.js +0 -173
  228. package/packages/create-krnl/dist/chunk-QGM4M3NI.js +0 -37
  229. package/packages/create-krnl/dist/chunk-RJNYUXZD.js +0 -977
  230. package/packages/create-krnl/dist/chunk-SRTOPJ5W.js +0 -104
  231. package/packages/create-krnl/dist/chunk-UPBIGEC3.js +0 -666
  232. package/packages/create-krnl/dist/chunk-V2DB7UD7.js +0 -82
  233. package/packages/create-krnl/dist/chunk-WMHLIUI6.js +0 -3644
  234. package/packages/create-krnl/dist/chunk-XIRFOO3H.js +0 -589
  235. package/packages/create-krnl/dist/chunk-Y2CEEVZP.js +0 -5536
  236. package/packages/create-krnl/dist/main-DXUMPBND.js +0 -2217
  237. package/packages/create-krnl/dist/wrapper-SDF2VKX3.js +0 -31
@@ -1,1033 +0,0 @@
1
- import {
2
- __esm,
3
- __export,
4
- __require
5
- } from "./chunk-X5LOIFM7.js";
6
-
7
- // ../core/dist/chunk-YXGL3ZEU.js
8
- import fs from "fs";
9
- import path from "path";
10
- import { fileURLToPath } from "url";
11
- var K_LIB_Krnl_Frontend_Generator_exports = {};
12
- __export(K_LIB_Krnl_Frontend_Generator_exports, {
13
- KrnlFrontendGenerator: () => KrnlFrontendGenerator
14
- });
15
- var _metaUrl;
16
- var _filename;
17
- var _dirname;
18
- var KrnlFrontendGenerator;
19
- var init_K_LIB_Krnl_Frontend_Generator = __esm({
20
- "src/core/K_LIB_Krnl_Frontend_Generator.ts"() {
21
- _metaUrl = typeof import.meta !== "undefined" && import.meta.url ? import.meta.url : null;
22
- _filename = _metaUrl ? fileURLToPath(_metaUrl) : "";
23
- _dirname = _metaUrl ? path.dirname(_filename) : "";
24
- KrnlFrontendGenerator = class {
25
- /**
26
- * Main entrypoint: ensures skeleton exists, generates React components & index.tsx
27
- */
28
- static async generate(entity, frontendDir) {
29
- this.ensureFrontendSkeleton(entity, frontendDir);
30
- const uiAllowlist = entity._frontend?.options?.uiEndpoints;
31
- const uiEndpointsMap = /* @__PURE__ */ new Map();
32
- const collectEndpoints = (ent) => {
33
- const funcs = [
34
- ...ent.localFunctionalities?.values() || [],
35
- ...ent.remoteFunctionalities?.values() || []
36
- ];
37
- for (const fn of funcs) {
38
- if (uiAllowlist && !uiAllowlist.includes(fn.id)) {
39
- continue;
40
- }
41
- const uiSpec = fn.uiSpec || fn.ui_spec;
42
- if (!uiSpec) {
43
- continue;
44
- }
45
- if (uiSpec && uiSpec.ui_component_enabled) {
46
- if (!uiEndpointsMap.has(fn.id)) {
47
- uiEndpointsMap.set(fn.id, { id: fn.id, uiSpec, schema: fn.schema });
48
- }
49
- }
50
- }
51
- const children = ent.hierarchyManager?.ownedComponents || ent.components;
52
- if (children && typeof children.values === "function") {
53
- for (const child of children.values()) {
54
- collectEndpoints(child);
55
- }
56
- }
57
- };
58
- collectEndpoints(entity);
59
- const uiEndpoints = Array.from(uiEndpointsMap.values());
60
- if (uiEndpoints.length > 0) {
61
- this.checkReactDependencies(frontendDir);
62
- const contractVal = this.validateExecutionContract(entity, uiEndpoints);
63
- if (!contractVal.valid) {
64
- console.error(`[KrnlFrontendGenerator] Execution Contract Validation Failed:
65
- ${contractVal.errors.map((e) => ` - ${e}`).join("\n")}`);
66
- throw new Error(`[KrnlExecutionContractError] ${contractVal.errors.join("; ")}`);
67
- }
68
- }
69
- this.generateAtoms(frontendDir);
70
- const componentsDir = path.join(frontendDir, "src", "components");
71
- fs.mkdirSync(componentsDir, { recursive: true });
72
- for (const ep of uiEndpoints) {
73
- const epDir = path.join(componentsDir, ep.id);
74
- fs.mkdirSync(epDir, { recursive: true });
75
- const compSource = this.generateComponentSource(ep.id, ep.uiSpec, ep.schema);
76
- fs.writeFileSync(path.join(epDir, "index.ts"), compSource, "utf-8");
77
- }
78
- const subAppPrefixesSet = new Set(entity._subAppRegistry?.keys() || []);
79
- if (entity.hierarchyManager?.ownedComponents) {
80
- for (const [childId, childEntity] of entity.hierarchyManager.ownedComponents.entries()) {
81
- if (childEntity && childEntity._frontend) {
82
- subAppPrefixesSet.add(childId.toLowerCase());
83
- }
84
- }
85
- }
86
- const subAppPrefixes = Array.from(subAppPrefixesSet);
87
- const indexSource = this.generateIndexSource(entity.entityName, uiEndpoints, subAppPrefixes);
88
- fs.writeFileSync(path.join(frontendDir, "src", "index.ts"), indexSource, "utf-8");
89
- }
90
- /**
91
- * Ensures essential files (package.json, index.html, index.css, atoms/) exist in frontendDir
92
- */
93
- static ensureFrontendSkeleton(entity, frontendDir) {
94
- const srcDir = path.join(frontendDir, "src");
95
- const atomsDir = path.join(srcDir, "atoms");
96
- fs.mkdirSync(atomsDir, { recursive: true });
97
- const candidates = [
98
- path.resolve(_dirname, "../src/krnl/frontend/atoms"),
99
- path.resolve(_dirname, "../../src/krnl/frontend/atoms"),
100
- path.resolve(_dirname, "../frontend/atoms"),
101
- path.resolve(process.cwd(), "node_modules/krnlts/src/krnl/frontend/atoms"),
102
- path.resolve(process.cwd(), "node_modules/krnlts/dist/frontend/atoms")
103
- ];
104
- const libAtomsDir = candidates.find((c) => fs.existsSync(c));
105
- if (libAtomsDir && fs.existsSync(libAtomsDir)) {
106
- const files = fs.readdirSync(libAtomsDir);
107
- for (const file of files) {
108
- const srcFile = path.join(libAtomsDir, file);
109
- const destFile = path.join(atomsDir, file);
110
- fs.copyFileSync(srcFile, destFile);
111
- }
112
- }
113
- const pkgPath = path.join(frontendDir, "package.json");
114
- if (!fs.existsSync(pkgPath)) {
115
- const pkgContent = {
116
- name: `${entity.entityName.toLowerCase()}-frontend`,
117
- version: "1.0.0",
118
- type: "module",
119
- dependencies: {
120
- react: "^18.2.0",
121
- "react-dom": "^18.2.0"
122
- },
123
- devDependencies: {
124
- "@types/react": "^18.2.0",
125
- "@types/react-dom": "^18.2.0"
126
- }
127
- };
128
- fs.writeFileSync(pkgPath, JSON.stringify(pkgContent, null, 2), "utf-8");
129
- }
130
- const htmlPath = path.join(frontendDir, "index.html");
131
- if (!fs.existsSync(htmlPath)) {
132
- const htmlContent = `<!DOCTYPE html>
133
- <html lang="en" data-theme="glass-brutalism">
134
- <head>
135
- <meta charset="UTF-8">
136
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
137
- <title>${entity.entityName} Frontend</title>
138
- <link rel="preconnect" href="https://fonts.googleapis.com">
139
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
140
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
141
- <link rel="stylesheet" href="styles.css">
142
- <link rel="stylesheet" href="/frontend/index.css">
143
- </head>
144
- <body>
145
- <div class="krnl-sphere krnl-sphere-accent"></div>
146
- <div class="krnl-sphere krnl-sphere-neutral"></div>
147
- <div id="root"></div>
148
- <script src="bundle.js"></script>
149
- </body>
150
- </html>`;
151
- fs.writeFileSync(htmlPath, htmlContent, "utf-8");
152
- }
153
- const cssPath = path.join(frontendDir, "index.css");
154
- if (!fs.existsSync(cssPath)) {
155
- const defaultCss = `/* =============================================================
156
- KRNL Glass-Brutalism Fallback Design System
157
- Apple precision + glassmorphism depth + neo-brutalist structure
158
- Override any --krnl-* token in your own index.css to restyle.
159
- ============================================================= */
160
-
161
- /* \u2500\u2500 Google Fonts fallback \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
162
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
163
-
164
- /* \u2500\u2500 Design Tokens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
165
- :root {
166
- /* Typography */
167
- --krnl-font: 'Inter', -apple-system, 'SF Pro Display', 'Segoe UI', sans-serif;
168
- --krnl-font-mono: 'SF Mono', 'JetBrains Mono', 'Fira Code', monospace;
169
-
170
- /* Page base \u2014 Pastel Matte Black */
171
- --krnl-bg: #16181d;
172
- --krnl-text: #f1f5f9;
173
- --krnl-text-muted: #8e95a5;
174
-
175
- /* Matte glass \u2014 panels, cards, containers (recede back) */
176
- --krnl-matte-bg: rgba(26, 29, 36, 0.75);
177
- --krnl-matte-bg-hover: rgba(34, 38, 48, 0.88);
178
- --krnl-matte-blur: blur(24px);
179
-
180
- /* Gloss \u2014 buttons, focused inputs (float forward) */
181
- --krnl-gloss-bg: linear-gradient(160deg, rgba(255,255,255,0.12) 0%, rgba(255,255,255,0.03) 100%);
182
- --krnl-gloss-shine: inset 0 1px 0 rgba(255,255,255,0.20);
183
-
184
- /* Neo-brutalist borders & shadows */
185
- --krnl-border: 2px solid rgba(255, 255, 255, 0.85);
186
- --krnl-border-input: 2px solid rgba(255, 255, 255, 0.85);
187
- --krnl-radius: 14px;
188
- --krnl-radius-sm: 8px;
189
- --krnl-shadow: 5px 5px 0px rgba(0, 0, 0, 0.90);
190
- --krnl-shadow-hover: 8px 8px 0px rgba(0, 0, 0, 0.95);
191
- --krnl-shadow-btn: 4px 4px 0px rgba(0, 0, 0, 0.90);
192
- --krnl-shadow-btn-hover: 6px 6px 0px rgba(0, 0, 0, 0.95);
193
- --krnl-shadow-btn-active: 2px 2px 0px rgba(0, 0, 0, 0.90);
194
-
195
- /* Semantic accents \u2014 desaturated glow pastels for dark bg */
196
- --krnl-accent: #2b4c7e; /* Dark powder blue */
197
- --krnl-accent-fg: #f1f5f9;
198
- --krnl-success: #1f4d36; /* Dark mint */
199
- --krnl-success-fg: #f1f5f9;
200
- --krnl-danger: #5c2424; /* Dark blush red */
201
- --krnl-danger-fg: #f1f5f9;
202
- --krnl-warning: #5e4e19; /* Dark butter yellow */
203
- --krnl-warning-fg: #f1f5f9;
204
-
205
- /* Phantom / live-mode indicator */
206
- --krnl-phantom-glow: 0 0 24px rgba(48, 209, 88, 0.50);
207
- --krnl-phantom-border: 2px solid #30d158;
208
- }
209
-
210
- /* \u2500\u2500 Reset \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
211
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
212
-
213
- body {
214
- font-family: var(--krnl-font);
215
- background-color: var(--krnl-bg);
216
- color: var(--krnl-text);
217
- min-height: 100vh;
218
- overflow-x: hidden;
219
- position: relative;
220
- }
221
-
222
- /* \u2500\u2500 Ambient Sphere Orbs (hidden for solid pastel black theme) \u2500\u2500 */
223
- .krnl-sphere { display: none; }
224
-
225
- /* \u2500\u2500 App Shell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
226
- .krnl-app-shell {
227
- position: relative;
228
- z-index: 1;
229
- display: flex;
230
- flex-direction: column;
231
- min-height: 100vh;
232
- align-items: center;
233
- }
234
-
235
- /* \u2500\u2500 Header (matte glass) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
236
- .krnl-header {
237
- display: flex;
238
- align-items: center;
239
- justify-content: center;
240
- width: 100%;
241
- padding: 1rem 2rem;
242
- background: var(--krnl-matte-bg);
243
- backdrop-filter: var(--krnl-matte-blur);
244
- -webkit-backdrop-filter: var(--krnl-matte-blur);
245
- border-bottom: var(--krnl-border);
246
- box-shadow: 0 3px 0px rgba(0, 0, 0, 0.90);
247
- letter-spacing: -0.02em;
248
- position: sticky;
249
- top: 0;
250
- z-index: 100;
251
- }
252
- .krnl-header h1 {
253
- font-size: 1.15rem;
254
- font-weight: 800;
255
- letter-spacing: -0.04em;
256
- color: var(--krnl-text);
257
- text-align: center;
258
- }
259
-
260
- /* \u2500\u2500 Nav Tabs (matte glass bar, wrapping + icon-only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
261
- .krnl-nav-tabs {
262
- display: flex;
263
- flex-wrap: wrap;
264
- justify-content: center;
265
- width: 100%;
266
- gap: 0.5rem;
267
- padding: 0.75rem 2rem;
268
- background: var(--krnl-matte-bg);
269
- backdrop-filter: var(--krnl-matte-blur);
270
- -webkit-backdrop-filter: var(--krnl-matte-blur);
271
- border-bottom: var(--krnl-border);
272
- }
273
-
274
- /* Tab buttons \u2014 icon-only by default, reveal label on hover */
275
- .krnl-tab-btn {
276
- position: relative;
277
- display: inline-flex;
278
- align-items: center;
279
- justify-content: center;
280
- gap: 0;
281
- overflow: hidden;
282
- background: var(--krnl-gloss-bg);
283
- box-shadow: var(--krnl-gloss-shine);
284
- border: var(--krnl-border);
285
- color: var(--krnl-text);
286
- font-family: var(--krnl-font);
287
- font-size: 0.82rem;
288
- font-weight: 600;
289
- letter-spacing: -0.01em;
290
- /* Collapsed icon-only size */
291
- padding: 0.45rem 0.65rem;
292
- min-width: 2.4rem;
293
- max-width: 2.4rem;
294
- border-radius: var(--krnl-radius-sm);
295
- cursor: pointer;
296
- white-space: nowrap;
297
- transition: box-shadow 0.15s ease, transform 0.15s ease, max-width 0.25s ease, padding 0.25s ease, gap 0.25s ease;
298
- }
299
-
300
- /* Tab button label \u2014 hidden until hover */
301
- .krnl-tab-btn .krnl-tab-label {
302
- display: inline-block;
303
- max-width: 0;
304
- opacity: 0;
305
- overflow: hidden;
306
- transition: max-width 0.25s ease, opacity 0.2s ease;
307
- white-space: nowrap;
308
- pointer-events: none;
309
- }
310
-
311
- /* Expand on hover */
312
- .krnl-tab-btn:hover,
313
- .krnl-tab-btn:focus-visible {
314
- max-width: 14rem;
315
- padding: 0.45rem 1rem;
316
- gap: 0.4rem;
317
- transform: translateY(-1px);
318
- box-shadow: var(--krnl-gloss-shine), 4px 4px 0px rgba(0, 0, 0, 0.95);
319
- }
320
- .krnl-tab-btn:hover .krnl-tab-label,
321
- .krnl-tab-btn:focus-visible .krnl-tab-label {
322
- max-width: 10rem;
323
- opacity: 1;
324
- }
325
-
326
- /* Active tab \u2014 always show icon + label */
327
- .krnl-tab-btn.active {
328
- background: var(--krnl-accent);
329
- color: var(--krnl-accent-fg);
330
- box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow-btn);
331
- transform: translateY(-1px);
332
- max-width: 14rem;
333
- padding: 0.45rem 1rem;
334
- gap: 0.4rem;
335
- }
336
- .krnl-tab-btn.active .krnl-tab-label {
337
- max-width: 10rem;
338
- opacity: 1;
339
- }
340
-
341
- /* \u2500\u2500 Main Content Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
342
- .krnl-main-body {
343
- flex: 1;
344
- width: 100%;
345
- max-width: 1100px;
346
- padding: 2.5rem 2rem;
347
- display: flex;
348
- flex-wrap: wrap;
349
- justify-content: center;
350
- align-items: flex-start;
351
- gap: 2rem;
352
- overflow-y: auto;
353
- }
354
-
355
- /* \u2500\u2500 Endpoint Card (matte glass panel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
356
- .krnl-endpoint-card {
357
- background: var(--krnl-matte-bg);
358
- backdrop-filter: var(--krnl-matte-blur);
359
- -webkit-backdrop-filter: var(--krnl-matte-blur);
360
- border: var(--krnl-border);
361
- border-radius: var(--krnl-radius);
362
- box-shadow: var(--krnl-shadow);
363
- padding: 1.75rem;
364
- min-width: 320px;
365
- max-width: 480px;
366
- width: 100%;
367
- transition: box-shadow 0.15s ease, transform 0.15s ease;
368
- position: relative;
369
- margin: 0 auto;
370
- }
371
- .krnl-endpoint-card:hover {
372
- transform: translate(-2px, -2px);
373
- box-shadow: var(--krnl-shadow-hover);
374
- background: var(--krnl-matte-bg-hover);
375
- }
376
- .krnl-endpoint-card.krnl-phantom-active {
377
- border: var(--krnl-phantom-border);
378
- box-shadow: var(--krnl-phantom-glow);
379
- }
380
-
381
- .krnl-endpoint-title {
382
- font-size: 0.72rem;
383
- font-weight: 700;
384
- letter-spacing: 0.08em;
385
- text-transform: uppercase;
386
- color: var(--krnl-text-muted);
387
- margin-bottom: 1.25rem;
388
- }
389
-
390
- /* \u2500\u2500 Form Atoms (centered, icon-first, hover-reveal text) \u2500\u2500\u2500\u2500\u2500 */
391
- .krnl-form-group {
392
- display: flex;
393
- flex-direction: column;
394
- align-items: center;
395
- gap: 0.45rem;
396
- margin-bottom: 1.25rem;
397
- width: 100%;
398
- }
399
-
400
- .krnl-label {
401
- font-size: 0.72rem;
402
- font-weight: 700;
403
- letter-spacing: 0.06em;
404
- text-transform: uppercase;
405
- color: var(--krnl-text-muted);
406
- text-align: center;
407
- opacity: 0;
408
- max-height: 0px;
409
- overflow: hidden;
410
- transform: translateY(-4px);
411
- transition: opacity 0.2s ease, max-height 0.2s ease, transform 0.2s ease;
412
- }
413
-
414
- .krnl-form-group:hover .krnl-label,
415
- .krnl-form-group:focus-within .krnl-label {
416
- opacity: 1;
417
- max-height: 30px;
418
- transform: translateY(0);
419
- margin-bottom: 0.2rem;
420
- }
421
-
422
- /* Inputs \u2014 gloss surface (floats above matte panel) */
423
- .krnl-input,
424
- .krnl-select,
425
- .krnl-textarea {
426
- background: var(--krnl-gloss-bg);
427
- box-shadow: var(--krnl-gloss-shine);
428
- border: var(--krnl-border-input);
429
- border-radius: var(--krnl-radius-sm);
430
- color: var(--krnl-text);
431
- font-family: var(--krnl-font);
432
- font-size: 0.9rem;
433
- padding: 0.65rem 0.85rem;
434
- width: 100%;
435
- text-align: center;
436
- outline: none;
437
- transition: border-color 0.12s ease, box-shadow 0.12s ease;
438
- }
439
- .krnl-input:focus,
440
- .krnl-select:focus,
441
- .krnl-textarea:focus {
442
- border-color: #1d1d1f;
443
- box-shadow: var(--krnl-gloss-shine), 0 0 0 3px rgba(200, 223, 245, 0.50);
444
- }
445
- .krnl-textarea { resize: vertical; min-height: 90px; text-align: left; }
446
-
447
- .krnl-toggle-group {
448
- flex-direction: row;
449
- justify-content: center;
450
- align-items: center;
451
- gap: 0.75rem;
452
- }
453
-
454
- /* \u2500\u2500 Buttons (gloss, centered icon-first, hover-to-reveal text) \u2500\u2500 */
455
- .krnl-btn {
456
- display: inline-flex;
457
- align-items: center;
458
- justify-content: center;
459
- gap: 0.5rem;
460
- font-family: var(--krnl-font);
461
- font-size: 0.88rem;
462
- font-weight: 700;
463
- letter-spacing: -0.01em;
464
- padding: 0.65rem 1rem;
465
- border-radius: var(--krnl-radius-sm);
466
- border: var(--krnl-border);
467
- cursor: pointer;
468
- user-select: none;
469
- margin: 0 auto;
470
- transition: box-shadow 0.15s ease, transform 0.15s ease, padding 0.2s ease;
471
- background: var(--krnl-gloss-bg);
472
- box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow-btn);
473
- color: var(--krnl-text);
474
- }
475
-
476
- .krnl-btn-icon {
477
- font-size: 1.1rem;
478
- filter: grayscale(100%);
479
- display: inline-block;
480
- line-height: 1;
481
- }
482
-
483
- .krnl-btn-label {
484
- display: inline-block;
485
- max-width: 0px;
486
- opacity: 0;
487
- overflow: hidden;
488
- white-space: nowrap;
489
- transition: max-width 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease;
490
- }
491
-
492
- .krnl-btn:hover .krnl-btn-label,
493
- .krnl-btn:focus .krnl-btn-label,
494
- .krnl-btn:disabled .krnl-btn-label {
495
- max-width: 200px;
496
- opacity: 1;
497
- margin-left: 0.25rem;
498
- }
499
-
500
- .krnl-btn:hover .krnl-btn-icon {
501
- filter: grayscale(0%);
502
- }
503
-
504
- .krnl-btn:hover:not(:disabled) {
505
- transform: translate(-1px, -1px);
506
- box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow-btn-hover);
507
- padding: 0.65rem 1.35rem;
508
- }
509
- .krnl-btn:active:not(:disabled) {
510
- transform: translate(2px, 2px);
511
- box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow-btn-active);
512
- }
513
- .krnl-btn:disabled {
514
- opacity: 0.45;
515
- cursor: not-allowed;
516
- }
517
-
518
- /* Semantic button variants */
519
- .krnl-btn-primary { background: var(--krnl-accent); color: var(--krnl-accent-fg); }
520
- .krnl-btn-success { background: var(--krnl-success); color: var(--krnl-success-fg); }
521
- .krnl-btn-danger { background: var(--krnl-danger); color: var(--krnl-danger-fg); }
522
- .krnl-btn-warning { background: var(--krnl-warning); color: var(--krnl-warning-fg); }
523
-
524
- /* \u2500\u2500 JSON Viewer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
525
- .krnl-json-viewer {
526
- background: rgba(245, 245, 247, 0.30);
527
- backdrop-filter: blur(8px);
528
- border: var(--krnl-border);
529
- border-radius: var(--krnl-radius-sm);
530
- padding: 1rem;
531
- margin-top: 0.5rem;
532
- }
533
- .krnl-json-code {
534
- font-family: var(--krnl-font-mono);
535
- font-size: 0.8rem;
536
- white-space: pre-wrap;
537
- word-break: break-all;
538
- color: var(--krnl-text);
539
- max-height: 320px;
540
- overflow-y: auto;
541
- }
542
-
543
- /* \u2500\u2500 Tile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
544
- .krnl-tile {
545
- background: var(--krnl-matte-bg);
546
- backdrop-filter: var(--krnl-matte-blur);
547
- border: var(--krnl-border);
548
- border-radius: var(--krnl-radius);
549
- box-shadow: var(--krnl-shadow);
550
- padding: 1.25rem;
551
- display: flex;
552
- flex-direction: column;
553
- gap: 0.4rem;
554
- }
555
- .krnl-tile-title { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--krnl-text-muted); }
556
- .krnl-tile-value { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.04em; color: var(--krnl-text); }
557
- .krnl-tile-subtitle { font-size: 0.75rem; color: var(--krnl-text-muted); }
558
-
559
- /* \u2500\u2500 Carousel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
560
- .krnl-carousel {
561
- display: flex;
562
- gap: 1rem;
563
- overflow-x: auto;
564
- scroll-snap-type: x mandatory;
565
- padding-bottom: 0.5rem;
566
- scrollbar-width: thin;
567
- }
568
- .krnl-carousel-item {
569
- scroll-snap-align: start;
570
- flex: 0 0 220px;
571
- background: var(--krnl-gloss-bg);
572
- box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow);
573
- border: var(--krnl-border);
574
- border-radius: var(--krnl-radius-sm);
575
- padding: 1rem;
576
- font-size: 0.88rem;
577
- }
578
-
579
- /* \u2500\u2500 Phantom Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
580
- .krnl-phantom-badge {
581
- position: absolute;
582
- top: -10px; right: -10px;
583
- background: #30d158;
584
- color: #fff;
585
- font-family: var(--krnl-font-mono);
586
- font-size: 0.65rem;
587
- font-weight: 700;
588
- letter-spacing: 0.04em;
589
- padding: 2px 8px;
590
- border-radius: 999px;
591
- border: 2px solid #1d1d1f;
592
- box-shadow: 2px 2px 0px #1d1d1f;
593
- }
594
-
595
- /* \u2500\u2500 JSON Builder Rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
596
- .krnl-json-rows { display: flex; flex-direction: column; gap: 0.5rem; }
597
- .krnl-json-row { display: flex; gap: 0.5rem; align-items: center; }
598
- .krnl-json-key { flex: 2; }
599
- .krnl-json-type { flex: 1; }
600
- .krnl-json-val { flex: 3; }
601
-
602
- /* \u2500\u2500 Empty State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
603
- .krnl-empty {
604
- color: var(--krnl-text-muted);
605
- font-size: 0.88rem;
606
- text-align: center;
607
- padding: 2rem;
608
- }
609
-
610
- /* \u2500\u2500 Responsive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
611
- @media (max-width: 768px) {
612
- .krnl-header, .krnl-nav-tabs { padding-left: 1rem; padding-right: 1rem; }
613
- .krnl-main-body { padding: 1rem; }
614
- .krnl-endpoint-card { max-width: 100%; min-width: 0; }
615
- .krnl-sphere-accent { width: 320px; height: 320px; }
616
- .krnl-sphere-neutral { width: 260px; height: 260px; }
617
- }
618
- `;
619
- fs.writeFileSync(cssPath, defaultCss, "utf-8");
620
- }
621
- }
622
- /**
623
- * Verifies React dependencies are installed in frontend/
624
- */
625
- static checkReactDependencies(frontendDir) {
626
- const nodeModulesReact = path.join(frontendDir, "node_modules", "react");
627
- const rootNodeModulesReact = path.resolve(frontendDir, "../../node_modules/react");
628
- let hasReact = fs.existsSync(nodeModulesReact) || fs.existsSync(rootNodeModulesReact);
629
- if (!hasReact) {
630
- try {
631
- __require.resolve("react", { paths: [frontendDir, process.cwd()] });
632
- hasReact = true;
633
- } catch (_) {
634
- }
635
- }
636
- if (!hasReact) {
637
- console.error(`
638
- \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
639
- \u2502 [KrnlFrontend] WARNING: UI components are enabled but React is missing! \u2502
640
- \u2502 Please run inside your frontend/ directory: \u2502
641
- \u2502 npm install react react-dom \u2502
642
- \u2502 npm install -D @types/react @types/react-dom \u2502
643
- \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
644
- `);
645
- }
646
- }
647
- /**
648
- * Validates uiSpec definitions at build time to enforce protocol constraints and catch errors early
649
- */
650
- static validateUiSpec(fid, uiSpec) {
651
- const errors = [];
652
- const ALLOWED_INPUT_COMPONENTS = /* @__PURE__ */ new Set([
653
- "KrnlTextInput",
654
- "KrnlNumberInput",
655
- "KrnlToggle",
656
- "KrnlDropdown",
657
- "KrnlTextArea",
658
- "KrnlJsonInput"
659
- ]);
660
- const ALLOWED_OUTPUT_COMPONENTS = /* @__PURE__ */ new Set([
661
- "KrnlJsonViewer",
662
- "KrnlCarousel",
663
- "KrnlTile",
664
- "KrnlSSELog"
665
- ]);
666
- (uiSpec.inputs || []).forEach((spec) => {
667
- if (!spec.field || typeof spec.field !== "string") {
668
- errors.push(`[ERR_UISPEC_MISSING_FIELD] Endpoint '${fid}' input is missing required 'field' name.`);
669
- }
670
- if (spec.component && !ALLOWED_INPUT_COMPONENTS.has(spec.component)) {
671
- errors.push(`[ERR_UISPEC_INVALID_INPUT_COMPONENT] Endpoint '${fid}' input '${spec.field}' uses invalid component '${spec.component}'. Allowed input components: ${Array.from(ALLOWED_INPUT_COMPONENTS).join(", ")}`);
672
- }
673
- if (spec.sharedComponentId && typeof spec.sharedComponentId !== "string") {
674
- errors.push(`[ERR_UISPEC_INVALID_SLOT] Endpoint '${fid}' input '${spec.field}' sharedComponentId must be a string.`);
675
- }
676
- if (spec.sourceEndpointId && typeof spec.sourceEndpointId !== "string") {
677
- errors.push(`[ERR_UISPEC_INVALID_SOURCE_ENDPOINT] Endpoint '${fid}' input '${spec.field}' sourceEndpointId must be a string.`);
678
- }
679
- if (spec.sourceEndpointId && spec.component !== "KrnlDropdown") {
680
- errors.push(`[ERR_UISPEC_SOURCE_ENDPOINT_REQUIRES_DROPDOWN] Endpoint '${fid}' input '${spec.field}' sets sourceEndpointId='${spec.sourceEndpointId}' but uses component '${spec.component}'. Selection-source fields MUST use component 'KrnlDropdown'.`);
681
- }
682
- if (spec.component === "KrnlDropdown" && !spec.sharedComponentId && !spec.sourceEndpointId && (!spec.options || spec.options.length === 0)) {
683
- errors.push(`[ERR_UISPEC_DROPDOWN_MISSING_SOURCE] Endpoint '${fid}' input '${spec.field}' uses component 'KrnlDropdown' but provides no selection source. Must set 'sharedComponentId', 'sourceEndpointId', or static 'options'.`);
684
- }
685
- const SELECTION_FIELD_PATTERNS = ["provider", "model", "modelName", "model_name", "profile", "key_id", "keyId"];
686
- if (spec.component === "KrnlTextInput" && SELECTION_FIELD_PATTERNS.includes(spec.field)) {
687
- console.warn(`
688
- \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`);
689
- console.warn(`\u2502 [KrnlUiSpecValidator] SELECTION-SOURCE PATTERN WARNING: \u2502`);
690
- console.warn(`\u2502 Endpoint '${fid}' input '${spec.field}' uses KrnlTextInput. \u2502`);
691
- console.warn(`\u2502 Field '${spec.field}' is a finite-choice selection candidate. \u2502`);
692
- console.warn(`\u2502 KRNL ARCHITECTURE RULE: Users MUST NOT type finite selections manually. \u2502`);
693
- console.warn(`\u2502 FIX: Change component to 'KrnlDropdown' and specify 'sharedComponentId' \u2502`);
694
- console.warn(`\u2502 or 'sourceEndpointId' pointing to the options-providing endpoint. \u2502`);
695
- console.warn(`\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
696
- `);
697
- }
698
- if (spec.bindMode && spec.bindMode !== "selected" && spec.bindMode !== "all") {
699
- errors.push(`[ERR_UISPEC_INVALID_BINDMODE] Endpoint '${fid}' input '${spec.field}' bindMode '${spec.bindMode}' is invalid. Allowed: 'selected' | 'all'`);
700
- }
701
- if (spec.autoFetch) {
702
- errors.push(`[ERR_UISPEC_INVALID_AUTOFETCH] Endpoint '${fid}' input '${spec.field}' cannot use autoFetch. autoFetch is only allowed on output provider components.`);
703
- }
704
- });
705
- (uiSpec.outputs || []).forEach((spec) => {
706
- if (!spec.field || typeof spec.field !== "string") {
707
- errors.push(`[ERR_UISPEC_MISSING_FIELD] Endpoint '${fid}' output is missing required 'field' name.`);
708
- }
709
- if (spec.component && !ALLOWED_OUTPUT_COMPONENTS.has(spec.component)) {
710
- errors.push(`[ERR_UISPEC_INVALID_OUTPUT_COMPONENT] Endpoint '${fid}' output '${spec.field}' uses input component '${spec.component}'. Output fields must use display components: ${Array.from(ALLOWED_OUTPUT_COMPONENTS).join(", ")}.`);
711
- }
712
- if (spec.autoFetch && !spec.sharedComponentId) {
713
- errors.push(`[ERR_UISPEC_AUTOFETCH_MISSING_SLOT] Endpoint '${fid}' output '${spec.field}' sets autoFetch=true but lacks sharedComponentId.`);
714
- }
715
- });
716
- return {
717
- valid: errors.length === 0,
718
- errors
719
- };
720
- }
721
- /**
722
- * Validates that all UI components reference real registered endpoints on the entity.
723
- */
724
- static validateExecutionContract(entity, uiEndpoints) {
725
- const errors = [];
726
- const registeredFids = /* @__PURE__ */ new Set([
727
- ...entity.localFunctionalities?.keys() || [],
728
- ...entity.remoteFunctionalities?.keys() || []
729
- ]);
730
- for (const ep of uiEndpoints) {
731
- if (!registeredFids.has(ep.id)) {
732
- errors.push(`[ERR_UI_DEAD_FETCH] UI component generated for endpoint '${ep.id}', but no handler is registered on entity '${entity.entityName}'.`);
733
- }
734
- for (const input of ep.uiSpec.inputs || []) {
735
- if (input.sourceEndpointId && !registeredFids.has(input.sourceEndpointId)) {
736
- errors.push(`[ERR_UI_STALE_ENDPOINT] Endpoint '${ep.id}' input '${input.field}' references sourceEndpointId '${input.sourceEndpointId}', which is not a registered endpoint.`);
737
- }
738
- }
739
- }
740
- return { valid: errors.length === 0, errors };
741
- }
742
- /**
743
- * Generates TS source code for a single endpoint UI component using VanJS
744
- */
745
- static generateComponentSource(fid, uiSpec, schema) {
746
- const validation = this.validateUiSpec(fid, uiSpec);
747
- if (!validation.valid) {
748
- console.error(`
749
- \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
750
- \u2502 [KrnlUiSpecValidator] BUILD ERROR IN ENDPOINT '${fid}':
751
- ${validation.errors.map((e) => `\u2502 - ${e}`).join("\n")}
752
- \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
753
- `);
754
- throw new Error(`[KrnlUiSpecValidationError] Invalid uiSpec in endpoint '${fid}': ${validation.errors.join("; ")}`);
755
- }
756
- let inputs = uiSpec.inputs || [];
757
- const outputs = uiSpec.outputs || [];
758
- if (inputs.length === 0 && schema && schema.input) {
759
- try {
760
- let shape = {};
761
- if (typeof schema.input._def?.shape === "function") {
762
- shape = schema.input._def.shape();
763
- } else if (schema.input.shape) {
764
- shape = schema.input.shape;
765
- }
766
- inputs = Object.keys(shape).map((field) => ({
767
- field,
768
- label: field,
769
- component: "KrnlTextInput",
770
- placeholder: `Enter ${field}`
771
- }));
772
- } catch (e) {
773
- console.warn(`[KrnlFrontendGenerator] Could not infer input fields for '${fid}':`, e);
774
- }
775
- }
776
- const sharedInputIds = inputs.filter((i) => i.sharedComponentId).map((i) => i.sharedComponentId);
777
- const sharedOutputIds = outputs.filter((o) => o.sharedComponentId).map((o) => o.sharedComponentId);
778
- const allSharedIds = Array.from(/* @__PURE__ */ new Set([...sharedInputIds, ...sharedOutputIds]));
779
- const sourceEndpointInputs = inputs.filter((i) => i.sourceEndpointId);
780
- const autoFetchOutput = outputs.find((o) => o.autoFetch && o.sharedComponentId);
781
- return `import van from 'vanjs-core';
782
- import { KrnlButton, KrnlTextInput, KrnlDropdown, KrnlTextArea, KrnlJsonViewer, KrnlTile, getSharedSlot, KrnlAgentBridge } from '../../atoms/index.js';
783
-
784
- const { div, h3, h4, form } = van.tags;
785
-
786
- export default function Endpoint_${fid.replace(/[^a-zA-Z0-9]/g, "_")}() {
787
- const formData = van.state<Record<string, any>>({});
788
- const response = van.state<any>(null);
789
- const loading = van.state(false);
790
- const error = van.state<string | null>(null);
791
- const phantomActive = van.state(false);
792
-
793
- ${allSharedIds.map(
794
- (id) => `const slot_${id.replace(/[^a-zA-Z0-9]/g, "_")} = getSharedSlot('${id}');`
795
- ).join("\n ")}
796
-
797
- KrnlAgentBridge.register('${fid}', {
798
- trigger: async (overrideInput?: Record<string, any>, ticketId?: string) => {
799
- return await executeRequest(overrideInput, ticketId);
800
- }
801
- });
802
-
803
- const handleInputChange = (field: string, val: any) => {
804
- formData.val = { ...formData.val, [field]: val };
805
- KrnlAgentBridge.setState('${fid}', {
806
- fid: '${fid}',
807
- formData: formData.val,
808
- response: response.val,
809
- loading: false,
810
- error: null,
811
- lastUpdated: Date.now()
812
- });
813
- };
814
-
815
- const executeRequest = async (overrideData?: Record<string, any>, ticketId?: string) => {
816
- loading.val = true;
817
- error.val = null;
818
- if (ticketId || overrideData) {
819
- phantomActive.val = true;
820
- }
821
- const activeInput = overrideData || formData.val;
822
- if (overrideData) formData.val = { ...formData.val, ...overrideData };
823
-
824
- try {
825
- const res = await fetch('/frontend/api/${fid}', {
826
- method: 'POST',
827
- headers: { 'Content-Type': 'application/json' },
828
- body: JSON.stringify(activeInput),
829
- });
830
- const data = await res.json();
831
- const payload = data.payload !== undefined ? data.payload : data;
832
- response.val = payload;
833
-
834
- if (payload && (payload.status === 'error' || payload.message)) {
835
- if (payload.status === 'error') error.val = payload.message || 'Request failed';
836
- }
837
-
838
- ${outputs.filter((o) => o.sharedComponentId).map((o) => {
839
- const varName = `slot_${o.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}`;
840
- const fieldKey = o.field;
841
- const dataPath = o.dataPath;
842
- return `
843
- let extracted: any = null;
844
- if (payload && typeof payload === 'object') {
845
- ${dataPath ? `extracted = payload['${dataPath}'];` : `extracted = payload['${fieldKey}'] !== undefined ? payload['${fieldKey}'] : payload;`}
846
- }
847
- if (extracted && typeof extracted === 'object' && !Array.isArray(extracted)) {
848
- if (extracted.status === 'success' && extracted.data) extracted = extracted.data;
849
- else if (extracted.providers && Array.isArray(extracted.providers)) extracted = extracted.providers;
850
- else if (extracted.models && Array.isArray(extracted.models)) extracted = extracted.models;
851
- else if (extracted.agents && Array.isArray(extracted.agents)) extracted = extracted.agents;
852
- else if (extracted.keys && typeof extracted.keys === 'object') {
853
- extracted = Object.keys(extracted).map(k => ({ id: k, name: k, value: k }));
854
- }
855
- }
856
- if (Array.isArray(extracted)) {
857
- ${varName}.options.val = extracted;
858
- }`;
859
- }).join("\n ")}
860
-
861
- if (ticketId) KrnlAgentBridge.sendJobComplete(ticketId, '${fid}', payload, undefined);
862
- return payload;
863
- } catch (err: any) {
864
- const errMsg = err.message || 'Request failed';
865
- error.val = errMsg;
866
- if (ticketId) KrnlAgentBridge.sendJobComplete(ticketId, '${fid}', null, errMsg);
867
- } finally {
868
- loading.val = false;
869
- if (ticketId || overrideData) {
870
- setTimeout(() => { phantomActive.val = false; }, 2000);
871
- }
872
- }
873
- };
874
-
875
- ${autoFetchOutput ? `executeRequest({});` : ""}
876
- ${sourceEndpointInputs.map(
877
- (i) => `
878
- fetch('/frontend/api/${i.sourceEndpointId}', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
879
- .then((r) => r.json())
880
- .then((d) => {
881
- const payload = d.payload !== undefined ? d.payload : d;
882
- let list = payload.${i.dataPath || i.field} || payload.data || payload.items || payload.providers || payload.models || payload.agents || payload;
883
- if (list && typeof list === 'object' && !Array.isArray(list)) list = Object.keys(list).map(k => ({ id: k, name: k, value: k }));
884
- if (Array.isArray(list) && ${i.sharedComponentId ? `slot_${i.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}` : "null"}) {
885
- slot_${i.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}.options.val = list;
886
- }
887
- })
888
- .catch(() => {});`
889
- ).join("\n ")}
890
-
891
- const handleSubmit = async (e: Event) => {
892
- e.preventDefault();
893
- await executeRequest();
894
- };
895
-
896
- return div(
897
- {
898
- className: () => \`krnl-endpoint-card \${phantomActive.val ? 'krnl-phantom-active' : ''}\`,
899
- style: () => \`
900
- background: var(--krnl-bg-surface);
901
- padding: 1.5rem;
902
- border-radius: 12px;
903
- border: \${phantomActive.val ? '2px solid var(--krnl-accent)' : '1px solid var(--krnl-border-color)'};
904
- transition: all 0.3s ease;
905
- position: relative;
906
- \`
907
- },
908
- () => (phantomActive.val ? div({ className: 'krnl-phantom-badge' }, '\u{1F916} Phantom Execution Active') : null),
909
- h3({ style: 'margin-bottom: 1rem; color: var(--krnl-primary);' }, 'Endpoint: ${fid}'),
910
- form(
911
- { onsubmit: handleSubmit },
912
- ${inputs.map((inSpec) => this.renderVanInputAtom(inSpec)).join(",\n ")}${inputs.length > 0 ? "," : ""}
913
- KrnlButton({ type: 'submit', loading, disabled: loading, label: 'Execute ${fid}', icon: '\u26A1' })
914
- ),
915
- () => (error.val ? div({ style: 'color: var(--krnl-danger); margin-top: 1rem;' }, 'Error: ', error.val) : null),
916
- () =>
917
- response.val !== null
918
- ? div(
919
- { style: 'margin-top: 1.5rem; border-top: 1px solid var(--krnl-border-color); padding-top: 1rem;' },
920
- h4({ style: 'margin-bottom: 0.5rem; color: var(--krnl-accent);' }, 'Output Response'),
921
- ${outputs.length > 0 ? outputs.map((outSpec) => this.renderVanOutputAtom(outSpec)).join(",\n ") : `KrnlJsonViewer({ data: response })`}
922
- )
923
- : null
924
- );
925
- }
926
- `;
927
- }
928
- static renderVanInputAtom(spec) {
929
- const label = spec.label || spec.field;
930
- if (spec.sharedComponentId) {
931
- const varName = `slot_${spec.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}`;
932
- const bindMode = spec.bindMode || "selected";
933
- return `KrnlDropdown({
934
- label: "${label}",
935
- value: van.derive(() => formData.val['${spec.field}'] !== undefined ? formData.val['${spec.field}'] : (${varName}.value.val || '')),
936
- options: ${varName}.options,
937
- onChange: (v) => {
938
- ${varName}.value.val = v;
939
- handleInputChange('${spec.field}', ${bindMode === "all" ? `${varName}.options.val` : "v"});
940
- }
941
- })`;
942
- }
943
- switch (spec.component) {
944
- case "KrnlTextInput":
945
- return `KrnlTextInput({ label: "${label}", value: van.derive(() => formData.val['${spec.field}'] || ''), onChange: (v) => handleInputChange('${spec.field}', v), placeholder: "${spec.placeholder || ""}" })`;
946
- case "KrnlTextArea":
947
- return `KrnlTextArea({ label: "${label}", value: van.derive(() => formData.val['${spec.field}'] || ''), onChange: (v) => handleInputChange('${spec.field}', v), placeholder: "${spec.placeholder || ""}" })`;
948
- case "KrnlDropdown":
949
- const opts = JSON.stringify(spec.options || []);
950
- return `KrnlDropdown({ label: "${label}", value: van.derive(() => formData.val['${spec.field}'] || ''), options: ${opts}, onChange: (v) => handleInputChange('${spec.field}', v) })`;
951
- default:
952
- return `KrnlTextInput({ label: "${label}", value: van.derive(() => formData.val['${spec.field}'] || ''), onChange: (v) => handleInputChange('${spec.field}', v) })`;
953
- }
954
- }
955
- static renderVanOutputAtom(spec) {
956
- const label = spec.label || spec.field;
957
- if (spec.sharedComponentId) {
958
- const varName = `slot_${spec.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}`;
959
- return `KrnlJsonViewer({ label: "${label}", data: ${varName}.options })`;
960
- }
961
- switch (spec.component) {
962
- case "KrnlTile":
963
- return `KrnlTile({ label: "${label}", value: van.derive(() => response.val ? (response.val['${spec.field}'] !== undefined ? response.val['${spec.field}'] : response.val) : '') })`;
964
- default:
965
- return `KrnlJsonViewer({ label: "${label}", data: van.derive(() => response.val ? (response.val['${spec.field}'] !== undefined ? response.val['${spec.field}'] : response.val) : response.val) })`;
966
- }
967
- }
968
- /**
969
- * Generates root index.ts aggregator using VanJS
970
- */
971
- static generateIndexSource(appName, uiEndpoints, subAppPrefixes) {
972
- const imports = uiEndpoints.map(
973
- (ep) => `import Endpoint_${ep.id.replace(/[^a-zA-Z0-9]/g, "_")} from './components/${ep.id}/index.js';`
974
- ).join("\n");
975
- const initialTab = uiEndpoints.length > 0 ? uiEndpoints[0].id : subAppPrefixes.length > 0 ? subAppPrefixes[0] : "welcome";
976
- return `import van from 'vanjs-core';
977
- ${imports}
978
-
979
- const { div, header, span, h1, button, main, iframe, h2, p } = van.tags;
980
-
981
- function AppShell() {
982
- const activeTab = van.state<string>('${initialTab}');
983
-
984
- return div(
985
- { className: 'krnl-app-shell' },
986
- header(
987
- { className: 'krnl-header' },
988
- div(
989
- { style: 'display: flex; align-items: center; gap: 0.65rem;' },
990
- span({ style: 'font-size: 1.2rem; line-height: 1;' }, '\u2B21'),
991
- h1('${appName}')
992
- )
993
- ),
994
- div(
995
- { className: 'krnl-nav-tabs' },
996
- ${uiEndpoints.map(
997
- (ep) => `button({ className: () => \`krnl-tab-btn \${activeTab.val === '${ep.id}' ? 'active' : ''}\`, onclick: () => activeTab.val = '${ep.id}', title: '${ep.id}' }, span('\u2B21'), span({ className: 'krnl-tab-label' }, '${ep.id}'))`
998
- ).join(",\n ")}${uiEndpoints.length > 0 && subAppPrefixes.length > 0 ? "," : ""}
999
- ${subAppPrefixes.map(
1000
- (sub) => `button({ className: () => \`krnl-tab-btn \${activeTab.val === '${sub}' ? 'active' : ''}\`, onclick: () => activeTab.val = '${sub}', title: '${sub}' }, span('\u{1F4E6}'), span({ className: 'krnl-tab-label' }, '${sub}'))`
1001
- ).join(",\n ")}
1002
- ),
1003
- main(
1004
- { className: 'krnl-main-body' },
1005
- ${uiEndpoints.map(
1006
- (ep) => `div({ style: () => \`display: \${activeTab.val === '${ep.id}' ? 'block' : 'none'}\` }, Endpoint_${ep.id.replace(/[^a-zA-Z0-9]/g, "_")}())`
1007
- ).join(",\n ")}${uiEndpoints.length > 0 && subAppPrefixes.length > 0 ? "," : ""}
1008
- ${subAppPrefixes.map(
1009
- (sub) => `() => activeTab.val === '${sub}' ? iframe({ src: '/${sub}/frontend/', title: '${sub}', style: 'width: 100%; height: calc(100vh - 160px); border: none; border-radius: 8px;' }) : null`
1010
- ).join(",\n ")}
1011
- )
1012
- );
1013
- }
1014
-
1015
- const rootElement = document.getElementById('root');
1016
- if (rootElement) {
1017
- van.add(rootElement, AppShell());
1018
- }
1019
- `;
1020
- }
1021
- static generateAtoms(frontendDir) {
1022
- const atomsDir = path.join(frontendDir, "src", "atoms");
1023
- fs.mkdirSync(atomsDir, { recursive: true });
1024
- }
1025
- };
1026
- }
1027
- });
1028
-
1029
- export {
1030
- K_LIB_Krnl_Frontend_Generator_exports,
1031
- KrnlFrontendGenerator,
1032
- init_K_LIB_Krnl_Frontend_Generator
1033
- };