@gnggln/ng-ui-system 1.0.0-alpha.2 → 1.0.0-alpha.21

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 (721) hide show
  1. package/accordion/entry-accordion.d.ts +4 -0
  2. package/accordion/index.d.ts +5 -0
  3. package/accordion/lib/components/accordion/accordion.component.d.ts +125 -0
  4. package/accordion/lib/components/accordion/accordion.types.d.ts +64 -0
  5. package/accordion/lib/components/accordion/index.d.ts +2 -0
  6. package/accordion/lib/core/types/index.d.ts +133 -0
  7. package/base-layout/entry-base-layout.d.ts +4 -0
  8. package/base-layout/index.d.ts +5 -0
  9. package/base-layout/lib/components/base-layout/base-layout.component.d.ts +108 -0
  10. package/base-layout/lib/components/base-layout/base-layout.types.d.ts +40 -0
  11. package/base-layout/lib/components/base-layout/index.d.ts +13 -0
  12. package/base-layout/lib/components/blackbox/blackbox-fingerprint.service.d.ts +36 -0
  13. package/base-layout/lib/components/blackbox/blackbox-storage.service.d.ts +55 -0
  14. package/base-layout/lib/components/blackbox/blackbox.service.d.ts +144 -0
  15. package/base-layout/lib/components/blackbox/blackbox.types.d.ts +238 -0
  16. package/base-layout/lib/components/button/button-area.component.d.ts +93 -0
  17. package/base-layout/lib/components/button/button.component.d.ts +61 -0
  18. package/base-layout/lib/components/button/button.types.d.ts +75 -0
  19. package/base-layout/lib/components/page-header/breadcrumb.service.d.ts +96 -0
  20. package/base-layout/lib/components/page-header/page-header.component.d.ts +59 -0
  21. package/base-layout/lib/components/page-header/page-header.types.d.ts +96 -0
  22. package/base-layout/lib/core/types/index.d.ts +133 -0
  23. package/blackbox/entry-blackbox.d.ts +4 -0
  24. package/blackbox/index.d.ts +5 -0
  25. package/blackbox/lib/components/blackbox/blackbox-debugger.component.d.ts +80 -0
  26. package/blackbox/lib/components/blackbox/blackbox-debugger.service.d.ts +34 -0
  27. package/blackbox/lib/components/blackbox/blackbox-fingerprint.service.d.ts +36 -0
  28. package/blackbox/lib/components/blackbox/blackbox-json-viewer.component.d.ts +18 -0
  29. package/blackbox/lib/components/blackbox/blackbox-storage.service.d.ts +55 -0
  30. package/blackbox/lib/components/blackbox/blackbox.interceptor.d.ts +38 -0
  31. package/blackbox/lib/components/blackbox/blackbox.service.d.ts +144 -0
  32. package/blackbox/lib/components/blackbox/blackbox.types.d.ts +238 -0
  33. package/blackbox/lib/components/blackbox/index.d.ts +23 -0
  34. package/blackbox/lib/components/blackbox/ui-track.directive.d.ts +20 -0
  35. package/blackbox/lib/components/button/button-area.component.d.ts +93 -0
  36. package/blackbox/lib/components/button/button.component.d.ts +61 -0
  37. package/blackbox/lib/components/button/button.types.d.ts +75 -0
  38. package/blackbox/lib/components/button/index.d.ts +15 -0
  39. package/blackbox/lib/components/modal/confirm-dialog.component.d.ts +46 -0
  40. package/blackbox/lib/components/modal/index.d.ts +4 -0
  41. package/blackbox/lib/components/modal/modal.component.d.ts +44 -0
  42. package/blackbox/lib/components/modal/modal.service.d.ts +93 -0
  43. package/blackbox/lib/components/modal/modal.types.d.ts +110 -0
  44. package/blackbox/lib/core/types/index.d.ts +133 -0
  45. package/button/entry-button.d.ts +4 -0
  46. package/button/index.d.ts +5 -0
  47. package/button/lib/components/blackbox/blackbox-fingerprint.service.d.ts +36 -0
  48. package/button/lib/components/blackbox/blackbox-storage.service.d.ts +55 -0
  49. package/button/lib/components/blackbox/blackbox.service.d.ts +144 -0
  50. package/button/lib/components/blackbox/blackbox.types.d.ts +238 -0
  51. package/button/lib/components/button/button-area.component.d.ts +93 -0
  52. package/button/lib/components/button/button.component.d.ts +61 -0
  53. package/button/lib/components/button/button.types.d.ts +75 -0
  54. package/button/lib/components/button/index.d.ts +15 -0
  55. package/button/lib/core/types/index.d.ts +133 -0
  56. package/core/entry-core.d.ts +5 -0
  57. package/core/index.d.ts +5 -0
  58. package/core/lib/core/types/index.d.ts +133 -0
  59. package/core/lib/core/utils/index.d.ts +60 -0
  60. package/crud-table/entry-crud-table.d.ts +4 -0
  61. package/crud-table/index.d.ts +5 -0
  62. package/crud-table/lib/components/accordion/accordion.component.d.ts +125 -0
  63. package/crud-table/lib/components/accordion/accordion.types.d.ts +64 -0
  64. package/crud-table/lib/components/accordion/index.d.ts +2 -0
  65. package/crud-table/lib/components/blackbox/blackbox-fingerprint.service.d.ts +36 -0
  66. package/crud-table/lib/components/blackbox/blackbox-storage.service.d.ts +55 -0
  67. package/crud-table/lib/components/blackbox/blackbox.service.d.ts +144 -0
  68. package/crud-table/lib/components/blackbox/blackbox.types.d.ts +238 -0
  69. package/crud-table/lib/components/button/button-area.component.d.ts +93 -0
  70. package/crud-table/lib/components/button/button.component.d.ts +61 -0
  71. package/crud-table/lib/components/button/button.types.d.ts +75 -0
  72. package/crud-table/lib/components/button/index.d.ts +15 -0
  73. package/crud-table/lib/components/crud-table/crud-table.component.d.ts +143 -0
  74. package/crud-table/lib/components/crud-table/crud-table.types.d.ts +207 -0
  75. package/crud-table/lib/components/crud-table/index.d.ts +15 -0
  76. package/crud-table/lib/components/form-builder/adapters/it-date-adapter.d.ts +32 -0
  77. package/crud-table/lib/components/form-builder/directives/currency-input.directive.d.ts +48 -0
  78. package/crud-table/lib/components/form-builder/form-builder.component.d.ts +279 -0
  79. package/crud-table/lib/components/form-builder/services/form-condition.service.d.ts +46 -0
  80. package/crud-table/lib/components/form-builder/services/form-error-state.matcher.d.ts +9 -0
  81. package/crud-table/lib/components/form-builder/services/form-field-error.service.d.ts +38 -0
  82. package/crud-table/lib/components/form-builder/services/form-validation.service.d.ts +63 -0
  83. package/crud-table/lib/components/form-builder/services/location.service.d.ts +83 -0
  84. package/crud-table/lib/components/form-builder/services/nominatim-geocoding.service.d.ts +37 -0
  85. package/crud-table/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.d.ts +31 -0
  86. package/crud-table/lib/components/form-builder/sub-components/file-input/file-input.component.d.ts +41 -0
  87. package/crud-table/lib/components/form-builder/sub-components/form-fields/form-number-field.component.d.ts +42 -0
  88. package/crud-table/lib/components/form-builder/sub-components/form-fields/form-radio-field.component.d.ts +45 -0
  89. package/crud-table/lib/components/form-builder/sub-components/form-fields/form-select-field.component.d.ts +44 -0
  90. package/crud-table/lib/components/form-builder/sub-components/form-fields/form-text-field.component.d.ts +62 -0
  91. package/crud-table/lib/components/form-builder/sub-components/form-fields/form-textarea-field.component.d.ts +39 -0
  92. package/crud-table/lib/components/form-builder/sub-components/form-fields/index.d.ts +5 -0
  93. package/crud-table/lib/components/form-builder/sub-components/location-geocoded/location-geocoded.component.d.ts +84 -0
  94. package/crud-table/lib/components/form-builder/sub-components/specifica-territoriale/specifica-territoriale.component.d.ts +145 -0
  95. package/crud-table/lib/components/form-builder/sub-components/table-territoriale/table-territoriale.component.d.ts +108 -0
  96. package/crud-table/lib/components/form-builder/types/condition.types.d.ts +51 -0
  97. package/crud-table/lib/components/form-builder/types/field.types.d.ts +330 -0
  98. package/crud-table/lib/components/form-builder/types/geocoded-location.types.d.ts +116 -0
  99. package/crud-table/lib/components/form-builder/types/schema.types.d.ts +304 -0
  100. package/crud-table/lib/components/form-builder/types/territoriale.types.d.ts +170 -0
  101. package/crud-table/lib/components/form-builder/types/validation.types.d.ts +179 -0
  102. package/crud-table/lib/components/modal/confirm-dialog.component.d.ts +46 -0
  103. package/crud-table/lib/components/modal/modal.service.d.ts +93 -0
  104. package/crud-table/lib/components/modal/modal.types.d.ts +110 -0
  105. package/crud-table/lib/core/logging/logger.config.d.ts +18 -0
  106. package/crud-table/lib/core/logging/logger.service.d.ts +75 -0
  107. package/crud-table/lib/core/logging/logger.types.d.ts +70 -0
  108. package/crud-table/lib/core/types/index.d.ts +133 -0
  109. package/esm2022/accordion/entry-accordion.mjs +5 -0
  110. package/esm2022/accordion/gnggln-ng-ui-system-accordion.mjs +5 -0
  111. package/esm2022/accordion/lib/components/accordion/accordion.component.mjs +398 -0
  112. package/esm2022/accordion/lib/components/accordion/accordion.types.mjs +6 -0
  113. package/esm2022/accordion/lib/components/accordion/index.mjs +2 -0
  114. package/esm2022/accordion/lib/core/types/index.mjs +6 -0
  115. package/esm2022/base-layout/entry-base-layout.mjs +5 -0
  116. package/esm2022/base-layout/gnggln-ng-ui-system-base-layout.mjs +5 -0
  117. package/esm2022/base-layout/lib/components/base-layout/base-layout.component.mjs +272 -0
  118. package/esm2022/base-layout/lib/components/base-layout/base-layout.types.mjs +6 -0
  119. package/esm2022/base-layout/lib/components/base-layout/index.mjs +14 -0
  120. package/esm2022/base-layout/lib/components/blackbox/blackbox-fingerprint.service.mjs +116 -0
  121. package/esm2022/base-layout/lib/components/blackbox/blackbox-storage.service.mjs +286 -0
  122. package/esm2022/base-layout/lib/components/blackbox/blackbox.service.mjs +323 -0
  123. package/esm2022/base-layout/lib/components/blackbox/blackbox.types.mjs +25 -0
  124. package/esm2022/base-layout/lib/components/button/button-area.component.mjs +210 -0
  125. package/esm2022/base-layout/lib/components/button/button.component.mjs +180 -0
  126. package/esm2022/base-layout/lib/components/button/button.types.mjs +6 -0
  127. package/esm2022/base-layout/lib/components/page-header/breadcrumb.service.mjs +243 -0
  128. package/esm2022/base-layout/lib/components/page-header/page-header.component.mjs +243 -0
  129. package/esm2022/base-layout/lib/components/page-header/page-header.types.mjs +21 -0
  130. package/esm2022/base-layout/lib/core/types/index.mjs +6 -0
  131. package/esm2022/blackbox/entry-blackbox.mjs +5 -0
  132. package/esm2022/blackbox/gnggln-ng-ui-system-blackbox.mjs +5 -0
  133. package/esm2022/blackbox/lib/components/blackbox/blackbox-debugger.component.mjs +904 -0
  134. package/esm2022/blackbox/lib/components/blackbox/blackbox-debugger.service.mjs +51 -0
  135. package/esm2022/blackbox/lib/components/blackbox/blackbox-fingerprint.service.mjs +116 -0
  136. package/esm2022/blackbox/lib/components/blackbox/blackbox-json-viewer.component.mjs +66 -0
  137. package/esm2022/blackbox/lib/components/blackbox/blackbox-storage.service.mjs +286 -0
  138. package/esm2022/blackbox/lib/components/blackbox/blackbox.interceptor.mjs +135 -0
  139. package/esm2022/blackbox/lib/components/blackbox/blackbox.service.mjs +323 -0
  140. package/esm2022/blackbox/lib/components/blackbox/blackbox.types.mjs +25 -0
  141. package/esm2022/blackbox/lib/components/blackbox/index.mjs +27 -0
  142. package/esm2022/blackbox/lib/components/blackbox/ui-track.directive.mjs +69 -0
  143. package/esm2022/blackbox/lib/components/button/button-area.component.mjs +210 -0
  144. package/esm2022/blackbox/lib/components/button/button.component.mjs +180 -0
  145. package/esm2022/blackbox/lib/components/button/button.types.mjs +6 -0
  146. package/esm2022/blackbox/lib/components/button/index.mjs +16 -0
  147. package/esm2022/blackbox/lib/components/modal/confirm-dialog.component.mjs +151 -0
  148. package/esm2022/blackbox/lib/components/modal/index.mjs +4 -0
  149. package/esm2022/blackbox/lib/components/modal/modal.component.mjs +139 -0
  150. package/esm2022/blackbox/lib/components/modal/modal.service.mjs +197 -0
  151. package/esm2022/blackbox/lib/components/modal/modal.types.mjs +6 -0
  152. package/esm2022/blackbox/lib/core/types/index.mjs +6 -0
  153. package/esm2022/button/entry-button.mjs +5 -0
  154. package/esm2022/button/gnggln-ng-ui-system-button.mjs +5 -0
  155. package/esm2022/button/lib/components/blackbox/blackbox-fingerprint.service.mjs +116 -0
  156. package/esm2022/button/lib/components/blackbox/blackbox-storage.service.mjs +286 -0
  157. package/esm2022/button/lib/components/blackbox/blackbox.service.mjs +323 -0
  158. package/esm2022/button/lib/components/blackbox/blackbox.types.mjs +25 -0
  159. package/esm2022/button/lib/components/button/button-area.component.mjs +210 -0
  160. package/esm2022/button/lib/components/button/button.component.mjs +180 -0
  161. package/esm2022/button/lib/components/button/button.types.mjs +6 -0
  162. package/esm2022/button/lib/components/button/index.mjs +16 -0
  163. package/esm2022/button/lib/core/types/index.mjs +6 -0
  164. package/esm2022/core/entry-core.mjs +6 -0
  165. package/esm2022/core/gnggln-ng-ui-system-core.mjs +5 -0
  166. package/esm2022/core/lib/core/types/index.mjs +6 -0
  167. package/esm2022/core/lib/core/utils/index.mjs +102 -0
  168. package/esm2022/crud-table/entry-crud-table.mjs +5 -0
  169. package/esm2022/crud-table/gnggln-ng-ui-system-crud-table.mjs +5 -0
  170. package/esm2022/crud-table/lib/components/accordion/accordion.component.mjs +398 -0
  171. package/esm2022/crud-table/lib/components/accordion/accordion.types.mjs +6 -0
  172. package/esm2022/crud-table/lib/components/accordion/index.mjs +2 -0
  173. package/esm2022/crud-table/lib/components/blackbox/blackbox-fingerprint.service.mjs +116 -0
  174. package/esm2022/crud-table/lib/components/blackbox/blackbox-storage.service.mjs +286 -0
  175. package/esm2022/crud-table/lib/components/blackbox/blackbox.service.mjs +323 -0
  176. package/esm2022/crud-table/lib/components/blackbox/blackbox.types.mjs +25 -0
  177. package/esm2022/crud-table/lib/components/button/button-area.component.mjs +210 -0
  178. package/esm2022/crud-table/lib/components/button/button.component.mjs +180 -0
  179. package/esm2022/crud-table/lib/components/button/button.types.mjs +6 -0
  180. package/esm2022/crud-table/lib/components/button/index.mjs +16 -0
  181. package/esm2022/crud-table/lib/components/crud-table/crud-table.component.mjs +789 -0
  182. package/esm2022/crud-table/lib/components/crud-table/crud-table.types.mjs +6 -0
  183. package/esm2022/crud-table/lib/components/crud-table/index.mjs +16 -0
  184. package/esm2022/crud-table/lib/components/form-builder/adapters/it-date-adapter.mjs +82 -0
  185. package/esm2022/crud-table/lib/components/form-builder/directives/currency-input.directive.mjs +184 -0
  186. package/esm2022/crud-table/lib/components/form-builder/form-builder.component.mjs +1351 -0
  187. package/esm2022/crud-table/lib/components/form-builder/services/form-condition.service.mjs +132 -0
  188. package/esm2022/crud-table/lib/components/form-builder/services/form-error-state.matcher.mjs +10 -0
  189. package/esm2022/crud-table/lib/components/form-builder/services/form-field-error.service.mjs +103 -0
  190. package/esm2022/crud-table/lib/components/form-builder/services/form-validation.service.mjs +381 -0
  191. package/esm2022/crud-table/lib/components/form-builder/services/location.service.mjs +141 -0
  192. package/esm2022/crud-table/lib/components/form-builder/services/nominatim-geocoding.service.mjs +120 -0
  193. package/esm2022/crud-table/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.mjs +190 -0
  194. package/esm2022/crud-table/lib/components/form-builder/sub-components/file-input/file-input.component.mjs +310 -0
  195. package/esm2022/crud-table/lib/components/form-builder/sub-components/form-fields/form-number-field.component.mjs +113 -0
  196. package/esm2022/crud-table/lib/components/form-builder/sub-components/form-fields/form-radio-field.component.mjs +105 -0
  197. package/esm2022/crud-table/lib/components/form-builder/sub-components/form-fields/form-select-field.component.mjs +126 -0
  198. package/esm2022/crud-table/lib/components/form-builder/sub-components/form-fields/form-text-field.component.mjs +147 -0
  199. package/esm2022/crud-table/lib/components/form-builder/sub-components/form-fields/form-textarea-field.component.mjs +99 -0
  200. package/esm2022/crud-table/lib/components/form-builder/sub-components/form-fields/index.mjs +6 -0
  201. package/esm2022/crud-table/lib/components/form-builder/sub-components/location-geocoded/location-geocoded.component.mjs +322 -0
  202. package/esm2022/crud-table/lib/components/form-builder/sub-components/specifica-territoriale/specifica-territoriale.component.mjs +648 -0
  203. package/esm2022/crud-table/lib/components/form-builder/sub-components/table-territoriale/table-territoriale.component.mjs +432 -0
  204. package/esm2022/crud-table/lib/components/form-builder/types/condition.types.mjs +6 -0
  205. package/esm2022/crud-table/lib/components/form-builder/types/field.types.mjs +6 -0
  206. package/esm2022/crud-table/lib/components/form-builder/types/geocoded-location.types.mjs +6 -0
  207. package/esm2022/crud-table/lib/components/form-builder/types/schema.types.mjs +6 -0
  208. package/esm2022/crud-table/lib/components/form-builder/types/territoriale.types.mjs +6 -0
  209. package/esm2022/crud-table/lib/components/form-builder/types/validation.types.mjs +10 -0
  210. package/esm2022/crud-table/lib/components/modal/confirm-dialog.component.mjs +151 -0
  211. package/esm2022/crud-table/lib/components/modal/modal.service.mjs +197 -0
  212. package/esm2022/crud-table/lib/components/modal/modal.types.mjs +6 -0
  213. package/esm2022/crud-table/lib/core/logging/logger.config.mjs +18 -0
  214. package/esm2022/crud-table/lib/core/logging/logger.service.mjs +295 -0
  215. package/esm2022/crud-table/lib/core/logging/logger.types.mjs +7 -0
  216. package/esm2022/crud-table/lib/core/types/index.mjs +6 -0
  217. package/esm2022/crud-table/lib/sources/location-data.opt.json +8942 -0
  218. package/esm2022/crud-table/lib/sources/nazioni.opt.json +215 -0
  219. package/esm2022/form-builder/entry-form-builder.mjs +5 -0
  220. package/esm2022/form-builder/gnggln-ng-ui-system-form-builder.mjs +5 -0
  221. package/esm2022/form-builder/lib/components/accordion/accordion.component.mjs +398 -0
  222. package/esm2022/form-builder/lib/components/accordion/accordion.types.mjs +6 -0
  223. package/esm2022/form-builder/lib/components/accordion/index.mjs +2 -0
  224. package/esm2022/form-builder/lib/components/blackbox/blackbox-fingerprint.service.mjs +116 -0
  225. package/esm2022/form-builder/lib/components/blackbox/blackbox-storage.service.mjs +286 -0
  226. package/esm2022/form-builder/lib/components/blackbox/blackbox.service.mjs +323 -0
  227. package/esm2022/form-builder/lib/components/blackbox/blackbox.types.mjs +25 -0
  228. package/esm2022/form-builder/lib/components/button/button-area.component.mjs +210 -0
  229. package/esm2022/form-builder/lib/components/button/button.component.mjs +180 -0
  230. package/esm2022/form-builder/lib/components/button/button.types.mjs +6 -0
  231. package/esm2022/form-builder/lib/components/button/index.mjs +16 -0
  232. package/esm2022/form-builder/lib/components/form-builder/adapters/it-date-adapter.mjs +82 -0
  233. package/esm2022/form-builder/lib/components/form-builder/directives/currency-input.directive.mjs +184 -0
  234. package/esm2022/form-builder/lib/components/form-builder/form-builder.component.mjs +1351 -0
  235. package/esm2022/form-builder/lib/components/form-builder/form-wizard.component.mjs +1064 -0
  236. package/esm2022/form-builder/lib/components/form-builder/index.mjs +24 -0
  237. package/esm2022/form-builder/lib/components/form-builder/services/form-condition.service.mjs +132 -0
  238. package/esm2022/form-builder/lib/components/form-builder/services/form-error-state.matcher.mjs +10 -0
  239. package/esm2022/form-builder/lib/components/form-builder/services/form-field-error.service.mjs +103 -0
  240. package/esm2022/form-builder/lib/components/form-builder/services/form-validation.service.mjs +381 -0
  241. package/esm2022/form-builder/lib/components/form-builder/services/location.service.mjs +141 -0
  242. package/esm2022/form-builder/lib/components/form-builder/services/nominatim-geocoding.service.mjs +120 -0
  243. package/esm2022/form-builder/lib/components/form-builder/services/wizard-sync.service.mjs +84 -0
  244. package/esm2022/form-builder/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.mjs +190 -0
  245. package/esm2022/form-builder/lib/components/form-builder/sub-components/file-input/file-input.component.mjs +310 -0
  246. package/esm2022/form-builder/lib/components/form-builder/sub-components/form-fields/form-number-field.component.mjs +113 -0
  247. package/esm2022/form-builder/lib/components/form-builder/sub-components/form-fields/form-radio-field.component.mjs +105 -0
  248. package/esm2022/form-builder/lib/components/form-builder/sub-components/form-fields/form-select-field.component.mjs +126 -0
  249. package/esm2022/form-builder/lib/components/form-builder/sub-components/form-fields/form-text-field.component.mjs +147 -0
  250. package/esm2022/form-builder/lib/components/form-builder/sub-components/form-fields/form-textarea-field.component.mjs +99 -0
  251. package/esm2022/form-builder/lib/components/form-builder/sub-components/form-fields/index.mjs +6 -0
  252. package/esm2022/form-builder/lib/components/form-builder/sub-components/location-geocoded/location-geocoded.component.mjs +322 -0
  253. package/esm2022/form-builder/lib/components/form-builder/sub-components/specifica-territoriale/specifica-territoriale.component.mjs +648 -0
  254. package/esm2022/form-builder/lib/components/form-builder/sub-components/table-territoriale/table-territoriale.component.mjs +432 -0
  255. package/esm2022/form-builder/lib/components/form-builder/types/condition.types.mjs +6 -0
  256. package/esm2022/form-builder/lib/components/form-builder/types/field.types.mjs +6 -0
  257. package/esm2022/form-builder/lib/components/form-builder/types/geocoded-location.types.mjs +6 -0
  258. package/esm2022/form-builder/lib/components/form-builder/types/index.mjs +3 -0
  259. package/esm2022/form-builder/lib/components/form-builder/types/schema.types.mjs +6 -0
  260. package/esm2022/form-builder/lib/components/form-builder/types/territoriale.types.mjs +6 -0
  261. package/esm2022/form-builder/lib/components/form-builder/types/validation.types.mjs +10 -0
  262. package/esm2022/form-builder/lib/core/logging/logger.config.mjs +18 -0
  263. package/esm2022/form-builder/lib/core/logging/logger.service.mjs +295 -0
  264. package/esm2022/form-builder/lib/core/logging/logger.types.mjs +7 -0
  265. package/esm2022/form-builder/lib/core/types/index.mjs +6 -0
  266. package/esm2022/form-builder/lib/sources/location-data.opt.json +8942 -0
  267. package/esm2022/form-builder/lib/sources/nazioni.opt.json +215 -0
  268. package/esm2022/form-builder-editor/entry-form-builder-editor.mjs +5 -0
  269. package/esm2022/form-builder-editor/gnggln-ng-ui-system-form-builder-editor.mjs +5 -0
  270. package/esm2022/form-builder-editor/lib/components/accordion/accordion.component.mjs +398 -0
  271. package/esm2022/form-builder-editor/lib/components/accordion/accordion.types.mjs +6 -0
  272. package/esm2022/form-builder-editor/lib/components/accordion/index.mjs +2 -0
  273. package/esm2022/form-builder-editor/lib/components/blackbox/blackbox-fingerprint.service.mjs +116 -0
  274. package/esm2022/form-builder-editor/lib/components/blackbox/blackbox-storage.service.mjs +286 -0
  275. package/esm2022/form-builder-editor/lib/components/blackbox/blackbox.service.mjs +323 -0
  276. package/esm2022/form-builder-editor/lib/components/blackbox/blackbox.types.mjs +25 -0
  277. package/esm2022/form-builder-editor/lib/components/button/button-area.component.mjs +210 -0
  278. package/esm2022/form-builder-editor/lib/components/button/button.component.mjs +180 -0
  279. package/esm2022/form-builder-editor/lib/components/button/button.types.mjs +6 -0
  280. package/esm2022/form-builder-editor/lib/components/button/index.mjs +16 -0
  281. package/esm2022/form-builder-editor/lib/components/form-builder/adapters/it-date-adapter.mjs +82 -0
  282. package/esm2022/form-builder-editor/lib/components/form-builder/directives/currency-input.directive.mjs +184 -0
  283. package/esm2022/form-builder-editor/lib/components/form-builder/form-builder.component.mjs +1351 -0
  284. package/esm2022/form-builder-editor/lib/components/form-builder/services/form-condition.service.mjs +132 -0
  285. package/esm2022/form-builder-editor/lib/components/form-builder/services/form-error-state.matcher.mjs +10 -0
  286. package/esm2022/form-builder-editor/lib/components/form-builder/services/form-field-error.service.mjs +103 -0
  287. package/esm2022/form-builder-editor/lib/components/form-builder/services/form-validation.service.mjs +381 -0
  288. package/esm2022/form-builder-editor/lib/components/form-builder/services/location.service.mjs +141 -0
  289. package/esm2022/form-builder-editor/lib/components/form-builder/services/nominatim-geocoding.service.mjs +120 -0
  290. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.mjs +190 -0
  291. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/file-input/file-input.component.mjs +310 -0
  292. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-number-field.component.mjs +113 -0
  293. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-radio-field.component.mjs +105 -0
  294. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-select-field.component.mjs +126 -0
  295. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-text-field.component.mjs +147 -0
  296. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-textarea-field.component.mjs +99 -0
  297. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/form-fields/index.mjs +6 -0
  298. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/location-geocoded/location-geocoded.component.mjs +322 -0
  299. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/specifica-territoriale/specifica-territoriale.component.mjs +648 -0
  300. package/esm2022/form-builder-editor/lib/components/form-builder/sub-components/table-territoriale/table-territoriale.component.mjs +432 -0
  301. package/esm2022/form-builder-editor/lib/components/form-builder/types/condition.types.mjs +6 -0
  302. package/esm2022/form-builder-editor/lib/components/form-builder/types/field.types.mjs +6 -0
  303. package/esm2022/form-builder-editor/lib/components/form-builder/types/geocoded-location.types.mjs +6 -0
  304. package/esm2022/form-builder-editor/lib/components/form-builder/types/index.mjs +3 -0
  305. package/esm2022/form-builder-editor/lib/components/form-builder/types/schema.types.mjs +6 -0
  306. package/esm2022/form-builder-editor/lib/components/form-builder/types/territoriale.types.mjs +6 -0
  307. package/esm2022/form-builder-editor/lib/components/form-builder/types/validation.types.mjs +10 -0
  308. package/esm2022/form-builder-editor/lib/components/form-builder-editor/form-builder-editor.component.mjs +730 -0
  309. package/esm2022/form-builder-editor/lib/components/form-builder-editor/form-builder-editor.service.mjs +56 -0
  310. package/esm2022/form-builder-editor/lib/components/form-builder-editor/index.mjs +23 -0
  311. package/esm2022/form-builder-editor/lib/components/form-builder-editor/presets/editor-presets.mjs +395 -0
  312. package/esm2022/form-builder-editor/lib/components/form-builder-editor/services/editor-persistence.service.mjs +190 -0
  313. package/esm2022/form-builder-editor/lib/components/form-builder-editor/services/editor-state.service.mjs +324 -0
  314. package/esm2022/form-builder-editor/lib/components/form-builder-editor/services/field-factory.service.mjs +188 -0
  315. package/esm2022/form-builder-editor/lib/components/form-builder-editor/sub-components/condition-editor/condition-editor.component.mjs +667 -0
  316. package/esm2022/form-builder-editor/lib/components/form-builder-editor/sub-components/editor-toolbar/editor-toolbar.component.mjs +317 -0
  317. package/esm2022/form-builder-editor/lib/components/form-builder-editor/sub-components/field-config-panel/field-config-panel.component.mjs +611 -0
  318. package/esm2022/form-builder-editor/lib/components/form-builder-editor/sub-components/field-palette/field-palette.component.mjs +267 -0
  319. package/esm2022/form-builder-editor/lib/components/form-builder-editor/sub-components/form-values-panel/form-values-panel.component.mjs +276 -0
  320. package/esm2022/form-builder-editor/lib/components/form-builder-editor/sub-components/options-editor/options-editor.component.mjs +323 -0
  321. package/esm2022/form-builder-editor/lib/components/form-builder-editor/sub-components/preview-container/preview-container.component.mjs +238 -0
  322. package/esm2022/form-builder-editor/lib/components/form-builder-editor/sub-components/section-editor/section-editor.component.mjs +473 -0
  323. package/esm2022/form-builder-editor/lib/components/form-builder-editor/sub-components/validation-editor/validation-editor.component.mjs +473 -0
  324. package/esm2022/form-builder-editor/lib/components/form-builder-editor/types/editor.types.mjs +6 -0
  325. package/esm2022/form-builder-editor/lib/components/modal/confirm-dialog.component.mjs +151 -0
  326. package/esm2022/form-builder-editor/lib/components/modal/index.mjs +4 -0
  327. package/esm2022/form-builder-editor/lib/components/modal/modal.component.mjs +139 -0
  328. package/esm2022/form-builder-editor/lib/components/modal/modal.service.mjs +197 -0
  329. package/esm2022/form-builder-editor/lib/components/modal/modal.types.mjs +6 -0
  330. package/esm2022/form-builder-editor/lib/core/logging/logger.config.mjs +18 -0
  331. package/esm2022/form-builder-editor/lib/core/logging/logger.service.mjs +295 -0
  332. package/esm2022/form-builder-editor/lib/core/logging/logger.types.mjs +7 -0
  333. package/esm2022/form-builder-editor/lib/core/types/index.mjs +6 -0
  334. package/esm2022/form-builder-editor/lib/sources/location-data.opt.json +8942 -0
  335. package/esm2022/form-builder-editor/lib/sources/nazioni.opt.json +215 -0
  336. package/esm2022/http/entry-http.mjs +5 -0
  337. package/esm2022/http/gnggln-ng-ui-system-http.mjs +5 -0
  338. package/esm2022/http/lib/components/blackbox/blackbox-fingerprint.service.mjs +116 -0
  339. package/esm2022/http/lib/components/blackbox/blackbox-storage.service.mjs +286 -0
  340. package/esm2022/http/lib/components/blackbox/blackbox.service.mjs +323 -0
  341. package/esm2022/http/lib/components/blackbox/blackbox.types.mjs +25 -0
  342. package/esm2022/http/lib/components/http/http-message.handler.mjs +143 -0
  343. package/esm2022/http/lib/components/http/http.service.mjs +228 -0
  344. package/esm2022/http/lib/components/http/http.tokens.mjs +29 -0
  345. package/esm2022/http/lib/components/http/http.types.mjs +6 -0
  346. package/esm2022/http/lib/components/http/index.mjs +19 -0
  347. package/esm2022/http/lib/components/layout-builder/layout-builder.types.mjs +9 -0
  348. package/esm2022/http/lib/components/layout-builder/layout.service.mjs +239 -0
  349. package/esm2022/http/lib/components/toast/toast-container.component.mjs +80 -0
  350. package/esm2022/http/lib/components/toast/toast.component.mjs +151 -0
  351. package/esm2022/http/lib/components/toast/toast.service.mjs +156 -0
  352. package/esm2022/http/lib/components/toast/toast.types.mjs +12 -0
  353. package/esm2022/http/lib/core/types/index.mjs +6 -0
  354. package/esm2022/http/lib/core/utils/index.mjs +102 -0
  355. package/esm2022/layout-builder/entry-layout-builder.mjs +5 -0
  356. package/esm2022/layout-builder/gnggln-ng-ui-system-layout-builder.mjs +5 -0
  357. package/esm2022/layout-builder/lib/components/layout-builder/index.mjs +18 -0
  358. package/esm2022/layout-builder/lib/components/layout-builder/layout-builder.component.mjs +1804 -0
  359. package/esm2022/layout-builder/lib/components/layout-builder/layout-builder.types.mjs +9 -0
  360. package/esm2022/layout-builder/lib/components/layout-builder/layout.service.mjs +239 -0
  361. package/esm2022/layout-builder/lib/core/types/index.mjs +6 -0
  362. package/esm2022/lib/components/accordion/accordion.component.mjs +106 -61
  363. package/esm2022/lib/components/accordion/accordion.types.mjs +1 -1
  364. package/esm2022/lib/components/base-layout/base-layout.component.mjs +88 -34
  365. package/esm2022/lib/components/base-layout/base-layout.types.mjs +1 -1
  366. package/esm2022/lib/components/base-layout/index.mjs +1 -1
  367. package/esm2022/lib/components/blackbox/blackbox-debugger.component.mjs +904 -0
  368. package/esm2022/lib/components/blackbox/blackbox-debugger.service.mjs +51 -0
  369. package/esm2022/lib/components/blackbox/blackbox-fingerprint.service.mjs +116 -0
  370. package/esm2022/lib/components/blackbox/blackbox-json-viewer.component.mjs +66 -0
  371. package/esm2022/lib/components/blackbox/blackbox-storage.service.mjs +286 -0
  372. package/esm2022/lib/components/blackbox/blackbox.interceptor.mjs +135 -0
  373. package/esm2022/lib/components/blackbox/blackbox.service.mjs +323 -0
  374. package/esm2022/lib/components/blackbox/blackbox.types.mjs +25 -0
  375. package/esm2022/lib/components/blackbox/index.mjs +27 -0
  376. package/esm2022/lib/components/blackbox/ui-track.directive.mjs +69 -0
  377. package/esm2022/lib/components/button/button-area.component.mjs +16 -2
  378. package/esm2022/lib/components/button/button.component.mjs +23 -7
  379. package/esm2022/lib/components/button/button.types.mjs +1 -1
  380. package/esm2022/lib/components/crud-table/crud-table.component.mjs +14 -14
  381. package/esm2022/lib/components/form-builder/form-builder.component.mjs +710 -183
  382. package/esm2022/lib/components/form-builder/form-wizard.component.mjs +789 -235
  383. package/esm2022/lib/components/form-builder/index.mjs +7 -2
  384. package/esm2022/lib/components/form-builder/services/form-error-state.matcher.mjs +10 -0
  385. package/esm2022/lib/components/form-builder/services/form-field-error.service.mjs +103 -0
  386. package/esm2022/lib/components/form-builder/services/location.service.mjs +4 -3
  387. package/esm2022/lib/components/form-builder/services/nominatim-geocoding.service.mjs +120 -0
  388. package/esm2022/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.mjs +142 -113
  389. package/esm2022/lib/components/form-builder/sub-components/form-fields/form-number-field.component.mjs +113 -0
  390. package/esm2022/lib/components/form-builder/sub-components/form-fields/form-radio-field.component.mjs +105 -0
  391. package/esm2022/lib/components/form-builder/sub-components/form-fields/form-select-field.component.mjs +126 -0
  392. package/esm2022/lib/components/form-builder/sub-components/form-fields/form-text-field.component.mjs +147 -0
  393. package/esm2022/lib/components/form-builder/sub-components/form-fields/form-textarea-field.component.mjs +99 -0
  394. package/esm2022/lib/components/form-builder/sub-components/form-fields/index.mjs +6 -0
  395. package/esm2022/lib/components/form-builder/sub-components/location-geocoded/location-geocoded.component.mjs +322 -0
  396. package/esm2022/lib/components/form-builder/sub-components/table-territoriale/table-territoriale.component.mjs +3 -3
  397. package/esm2022/lib/components/form-builder/types/field.types.mjs +1 -1
  398. package/esm2022/lib/components/form-builder/types/geocoded-location.types.mjs +6 -0
  399. package/esm2022/lib/components/form-builder/types/index.mjs +3 -2
  400. package/esm2022/lib/components/form-builder/types/schema.types.mjs +1 -1
  401. package/esm2022/lib/components/form-builder/types/validation.types.mjs +6 -2
  402. package/esm2022/lib/components/form-builder-editor/form-builder-editor.component.mjs +3 -3
  403. package/esm2022/lib/components/form-builder-editor/index.mjs +3 -1
  404. package/esm2022/lib/components/form-builder-editor/presets/editor-presets.mjs +395 -0
  405. package/esm2022/lib/components/form-builder-editor/sub-components/preview-container/preview-container.component.mjs +2 -2
  406. package/esm2022/lib/components/form-builder-editor/sub-components/section-editor/section-editor.component.mjs +2 -1
  407. package/esm2022/lib/components/http/http-message.handler.mjs +143 -0
  408. package/esm2022/lib/components/http/http.service.mjs +228 -0
  409. package/esm2022/lib/components/http/http.tokens.mjs +29 -0
  410. package/esm2022/lib/components/http/http.types.mjs +6 -0
  411. package/esm2022/lib/components/http/index.mjs +19 -0
  412. package/esm2022/lib/components/layout-builder/layout-builder.component.mjs +27 -15
  413. package/esm2022/lib/components/modal/confirm-dialog.component.mjs +3 -3
  414. package/esm2022/lib/components/modal/modal.service.mjs +5 -2
  415. package/esm2022/lib/components/page-header/breadcrumb.service.mjs +5 -4
  416. package/esm2022/lib/components/table/paginated-table.component.mjs +17 -3
  417. package/esm2022/lib/components/table/table.types.mjs +1 -1
  418. package/esm2022/lib/components/toast/index.mjs +18 -0
  419. package/esm2022/lib/components/toast/toast-container.component.mjs +80 -0
  420. package/esm2022/lib/components/toast/toast.component.mjs +151 -0
  421. package/esm2022/lib/components/toast/toast.service.mjs +156 -0
  422. package/esm2022/lib/components/toast/toast.types.mjs +12 -0
  423. package/esm2022/lib/core/logging/index.mjs +10 -0
  424. package/esm2022/lib/core/logging/logger.config.mjs +18 -0
  425. package/esm2022/lib/core/logging/logger.service.mjs +295 -0
  426. package/esm2022/lib/core/logging/logger.types.mjs +7 -0
  427. package/esm2022/lib/core/types/index.mjs +1 -1
  428. package/esm2022/lib/core/utils/index.mjs +50 -1
  429. package/esm2022/lib/version/ng-ui-system-version.mjs +12 -0
  430. package/esm2022/modal/entry-modal.mjs +5 -0
  431. package/esm2022/modal/gnggln-ng-ui-system-modal.mjs +5 -0
  432. package/esm2022/modal/lib/components/button/button.component.mjs +180 -0
  433. package/esm2022/modal/lib/components/button/button.types.mjs +6 -0
  434. package/esm2022/modal/lib/components/modal/confirm-dialog.component.mjs +151 -0
  435. package/esm2022/modal/lib/components/modal/index.mjs +4 -0
  436. package/esm2022/modal/lib/components/modal/modal.component.mjs +139 -0
  437. package/esm2022/modal/lib/components/modal/modal.service.mjs +197 -0
  438. package/esm2022/modal/lib/components/modal/modal.types.mjs +6 -0
  439. package/esm2022/modal/lib/core/types/index.mjs +6 -0
  440. package/esm2022/page-header/entry-page-header.mjs +5 -0
  441. package/esm2022/page-header/gnggln-ng-ui-system-page-header.mjs +5 -0
  442. package/esm2022/page-header/lib/components/page-header/breadcrumb.service.mjs +243 -0
  443. package/esm2022/page-header/lib/components/page-header/index.mjs +20 -0
  444. package/esm2022/page-header/lib/components/page-header/page-header.component.mjs +243 -0
  445. package/esm2022/page-header/lib/components/page-header/page-header.types.mjs +21 -0
  446. package/esm2022/public-api.mjs +17 -8
  447. package/esm2022/table/entry-table.mjs +5 -0
  448. package/esm2022/table/gnggln-ng-ui-system-table.mjs +5 -0
  449. package/esm2022/table/lib/components/table/index.mjs +2 -0
  450. package/esm2022/table/lib/components/table/paginated-table.component.mjs +421 -0
  451. package/esm2022/table/lib/components/table/table.types.mjs +6 -0
  452. package/esm2022/table/lib/core/utils/index.mjs +102 -0
  453. package/esm2022/toast/entry-toast.mjs +5 -0
  454. package/esm2022/toast/gnggln-ng-ui-system-toast.mjs +5 -0
  455. package/esm2022/toast/lib/components/toast/index.mjs +18 -0
  456. package/esm2022/toast/lib/components/toast/toast-container.component.mjs +80 -0
  457. package/esm2022/toast/lib/components/toast/toast.component.mjs +151 -0
  458. package/esm2022/toast/lib/components/toast/toast.service.mjs +156 -0
  459. package/esm2022/toast/lib/components/toast/toast.types.mjs +12 -0
  460. package/esm2022/toast/lib/core/types/index.mjs +6 -0
  461. package/esm2022/toast/lib/core/utils/index.mjs +102 -0
  462. package/fesm2022/gnggln-ng-ui-system-accordion.mjs +409 -0
  463. package/fesm2022/gnggln-ng-ui-system-accordion.mjs.map +1 -0
  464. package/fesm2022/gnggln-ng-ui-system-base-layout.mjs +1905 -0
  465. package/fesm2022/gnggln-ng-ui-system-base-layout.mjs.map +1 -0
  466. package/fesm2022/gnggln-ng-ui-system-blackbox.mjs +2829 -0
  467. package/fesm2022/gnggln-ng-ui-system-blackbox.mjs.map +1 -0
  468. package/fesm2022/gnggln-ng-ui-system-button.mjs +1148 -0
  469. package/fesm2022/gnggln-ng-ui-system-button.mjs.map +1 -0
  470. package/fesm2022/gnggln-ng-ui-system-core.mjs +117 -0
  471. package/fesm2022/gnggln-ng-ui-system-core.mjs.map +1 -0
  472. package/fesm2022/gnggln-ng-ui-system-crud-table.mjs +49600 -0
  473. package/fesm2022/gnggln-ng-ui-system-crud-table.mjs.map +1 -0
  474. package/fesm2022/gnggln-ng-ui-system-form-builder-editor.mjs +54332 -0
  475. package/fesm2022/gnggln-ng-ui-system-form-builder-editor.mjs.map +1 -0
  476. package/fesm2022/gnggln-ng-ui-system-form-builder.mjs +49609 -0
  477. package/fesm2022/gnggln-ng-ui-system-form-builder.mjs.map +1 -0
  478. package/fesm2022/gnggln-ng-ui-system-http.mjs +1878 -0
  479. package/fesm2022/gnggln-ng-ui-system-http.mjs.map +1 -0
  480. package/fesm2022/gnggln-ng-ui-system-layout-builder.mjs +2064 -0
  481. package/fesm2022/gnggln-ng-ui-system-layout-builder.mjs.map +1 -0
  482. package/fesm2022/gnggln-ng-ui-system-modal.mjs +664 -0
  483. package/fesm2022/gnggln-ng-ui-system-modal.mjs.map +1 -0
  484. package/fesm2022/gnggln-ng-ui-system-page-header.mjs +526 -0
  485. package/fesm2022/gnggln-ng-ui-system-page-header.mjs.map +1 -0
  486. package/fesm2022/gnggln-ng-ui-system-table.mjs +533 -0
  487. package/fesm2022/gnggln-ng-ui-system-table.mjs.map +1 -0
  488. package/fesm2022/gnggln-ng-ui-system-toast.mjs +516 -0
  489. package/fesm2022/gnggln-ng-ui-system-toast.mjs.map +1 -0
  490. package/fesm2022/gnggln-ng-ui-system.mjs +10068 -4234
  491. package/fesm2022/gnggln-ng-ui-system.mjs.map +1 -1
  492. package/form-builder/entry-form-builder.d.ts +4 -0
  493. package/form-builder/index.d.ts +5 -0
  494. package/form-builder/lib/components/accordion/accordion.component.d.ts +125 -0
  495. package/form-builder/lib/components/accordion/accordion.types.d.ts +64 -0
  496. package/form-builder/lib/components/accordion/index.d.ts +2 -0
  497. package/form-builder/lib/components/blackbox/blackbox-fingerprint.service.d.ts +36 -0
  498. package/form-builder/lib/components/blackbox/blackbox-storage.service.d.ts +55 -0
  499. package/form-builder/lib/components/blackbox/blackbox.service.d.ts +144 -0
  500. package/form-builder/lib/components/blackbox/blackbox.types.d.ts +238 -0
  501. package/form-builder/lib/components/button/button-area.component.d.ts +93 -0
  502. package/form-builder/lib/components/button/button.component.d.ts +61 -0
  503. package/form-builder/lib/components/button/button.types.d.ts +75 -0
  504. package/form-builder/lib/components/button/index.d.ts +15 -0
  505. package/form-builder/lib/components/form-builder/adapters/it-date-adapter.d.ts +32 -0
  506. package/form-builder/lib/components/form-builder/directives/currency-input.directive.d.ts +48 -0
  507. package/form-builder/lib/components/form-builder/form-builder.component.d.ts +279 -0
  508. package/form-builder/lib/components/form-builder/form-wizard.component.d.ts +172 -0
  509. package/form-builder/lib/components/form-builder/index.d.ts +19 -0
  510. package/form-builder/lib/components/form-builder/services/form-condition.service.d.ts +46 -0
  511. package/form-builder/lib/components/form-builder/services/form-error-state.matcher.d.ts +9 -0
  512. package/form-builder/lib/components/form-builder/services/form-field-error.service.d.ts +38 -0
  513. package/form-builder/lib/components/form-builder/services/form-validation.service.d.ts +63 -0
  514. package/form-builder/lib/components/form-builder/services/location.service.d.ts +83 -0
  515. package/form-builder/lib/components/form-builder/services/nominatim-geocoding.service.d.ts +37 -0
  516. package/form-builder/lib/components/form-builder/services/wizard-sync.service.d.ts +63 -0
  517. package/form-builder/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.d.ts +31 -0
  518. package/form-builder/lib/components/form-builder/sub-components/file-input/file-input.component.d.ts +41 -0
  519. package/form-builder/lib/components/form-builder/sub-components/form-fields/form-number-field.component.d.ts +42 -0
  520. package/form-builder/lib/components/form-builder/sub-components/form-fields/form-radio-field.component.d.ts +45 -0
  521. package/form-builder/lib/components/form-builder/sub-components/form-fields/form-select-field.component.d.ts +44 -0
  522. package/form-builder/lib/components/form-builder/sub-components/form-fields/form-text-field.component.d.ts +62 -0
  523. package/form-builder/lib/components/form-builder/sub-components/form-fields/form-textarea-field.component.d.ts +39 -0
  524. package/form-builder/lib/components/form-builder/sub-components/form-fields/index.d.ts +5 -0
  525. package/form-builder/lib/components/form-builder/sub-components/location-geocoded/location-geocoded.component.d.ts +84 -0
  526. package/form-builder/lib/components/form-builder/sub-components/specifica-territoriale/specifica-territoriale.component.d.ts +145 -0
  527. package/form-builder/lib/components/form-builder/sub-components/table-territoriale/table-territoriale.component.d.ts +108 -0
  528. package/form-builder/lib/components/form-builder/types/condition.types.d.ts +51 -0
  529. package/form-builder/lib/components/form-builder/types/field.types.d.ts +330 -0
  530. package/form-builder/lib/components/form-builder/types/geocoded-location.types.d.ts +116 -0
  531. package/form-builder/lib/components/form-builder/types/index.d.ts +6 -0
  532. package/form-builder/lib/components/form-builder/types/schema.types.d.ts +304 -0
  533. package/form-builder/lib/components/form-builder/types/territoriale.types.d.ts +170 -0
  534. package/form-builder/lib/components/form-builder/types/validation.types.d.ts +179 -0
  535. package/form-builder/lib/core/logging/logger.config.d.ts +18 -0
  536. package/form-builder/lib/core/logging/logger.service.d.ts +75 -0
  537. package/form-builder/lib/core/logging/logger.types.d.ts +70 -0
  538. package/form-builder/lib/core/types/index.d.ts +133 -0
  539. package/form-builder-editor/entry-form-builder-editor.d.ts +4 -0
  540. package/form-builder-editor/index.d.ts +5 -0
  541. package/form-builder-editor/lib/components/accordion/accordion.component.d.ts +125 -0
  542. package/form-builder-editor/lib/components/accordion/accordion.types.d.ts +64 -0
  543. package/form-builder-editor/lib/components/accordion/index.d.ts +2 -0
  544. package/form-builder-editor/lib/components/blackbox/blackbox-fingerprint.service.d.ts +36 -0
  545. package/form-builder-editor/lib/components/blackbox/blackbox-storage.service.d.ts +55 -0
  546. package/form-builder-editor/lib/components/blackbox/blackbox.service.d.ts +144 -0
  547. package/form-builder-editor/lib/components/blackbox/blackbox.types.d.ts +238 -0
  548. package/form-builder-editor/lib/components/button/button-area.component.d.ts +93 -0
  549. package/form-builder-editor/lib/components/button/button.component.d.ts +61 -0
  550. package/form-builder-editor/lib/components/button/button.types.d.ts +75 -0
  551. package/form-builder-editor/lib/components/button/index.d.ts +15 -0
  552. package/form-builder-editor/lib/components/form-builder/adapters/it-date-adapter.d.ts +32 -0
  553. package/form-builder-editor/lib/components/form-builder/directives/currency-input.directive.d.ts +48 -0
  554. package/form-builder-editor/lib/components/form-builder/form-builder.component.d.ts +279 -0
  555. package/form-builder-editor/lib/components/form-builder/services/form-condition.service.d.ts +46 -0
  556. package/form-builder-editor/lib/components/form-builder/services/form-error-state.matcher.d.ts +9 -0
  557. package/form-builder-editor/lib/components/form-builder/services/form-field-error.service.d.ts +38 -0
  558. package/form-builder-editor/lib/components/form-builder/services/form-validation.service.d.ts +63 -0
  559. package/form-builder-editor/lib/components/form-builder/services/location.service.d.ts +83 -0
  560. package/form-builder-editor/lib/components/form-builder/services/nominatim-geocoding.service.d.ts +37 -0
  561. package/form-builder-editor/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.d.ts +31 -0
  562. package/form-builder-editor/lib/components/form-builder/sub-components/file-input/file-input.component.d.ts +41 -0
  563. package/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-number-field.component.d.ts +42 -0
  564. package/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-radio-field.component.d.ts +45 -0
  565. package/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-select-field.component.d.ts +44 -0
  566. package/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-text-field.component.d.ts +62 -0
  567. package/form-builder-editor/lib/components/form-builder/sub-components/form-fields/form-textarea-field.component.d.ts +39 -0
  568. package/form-builder-editor/lib/components/form-builder/sub-components/form-fields/index.d.ts +5 -0
  569. package/form-builder-editor/lib/components/form-builder/sub-components/location-geocoded/location-geocoded.component.d.ts +84 -0
  570. package/form-builder-editor/lib/components/form-builder/sub-components/specifica-territoriale/specifica-territoriale.component.d.ts +145 -0
  571. package/form-builder-editor/lib/components/form-builder/sub-components/table-territoriale/table-territoriale.component.d.ts +108 -0
  572. package/form-builder-editor/lib/components/form-builder/types/condition.types.d.ts +51 -0
  573. package/form-builder-editor/lib/components/form-builder/types/field.types.d.ts +330 -0
  574. package/form-builder-editor/lib/components/form-builder/types/geocoded-location.types.d.ts +116 -0
  575. package/form-builder-editor/lib/components/form-builder/types/index.d.ts +6 -0
  576. package/form-builder-editor/lib/components/form-builder/types/schema.types.d.ts +304 -0
  577. package/form-builder-editor/lib/components/form-builder/types/territoriale.types.d.ts +170 -0
  578. package/form-builder-editor/lib/components/form-builder/types/validation.types.d.ts +179 -0
  579. package/form-builder-editor/lib/components/form-builder-editor/form-builder-editor.component.d.ts +117 -0
  580. package/form-builder-editor/lib/components/form-builder-editor/form-builder-editor.service.d.ts +38 -0
  581. package/form-builder-editor/lib/components/form-builder-editor/index.d.ts +17 -0
  582. package/form-builder-editor/lib/components/form-builder-editor/presets/editor-presets.d.ts +25 -0
  583. package/form-builder-editor/lib/components/form-builder-editor/services/editor-persistence.service.d.ts +42 -0
  584. package/form-builder-editor/lib/components/form-builder-editor/services/editor-state.service.d.ts +66 -0
  585. package/form-builder-editor/lib/components/form-builder-editor/services/field-factory.service.d.ts +28 -0
  586. package/form-builder-editor/lib/components/form-builder-editor/sub-components/condition-editor/condition-editor.component.d.ts +139 -0
  587. package/form-builder-editor/lib/components/form-builder-editor/sub-components/editor-toolbar/editor-toolbar.component.d.ts +43 -0
  588. package/form-builder-editor/lib/components/form-builder-editor/sub-components/field-config-panel/field-config-panel.component.d.ts +83 -0
  589. package/form-builder-editor/lib/components/form-builder-editor/sub-components/field-palette/field-palette.component.d.ts +40 -0
  590. package/form-builder-editor/lib/components/form-builder-editor/sub-components/form-values-panel/form-values-panel.component.d.ts +51 -0
  591. package/form-builder-editor/lib/components/form-builder-editor/sub-components/options-editor/options-editor.component.d.ts +63 -0
  592. package/form-builder-editor/lib/components/form-builder-editor/sub-components/preview-container/preview-container.component.d.ts +68 -0
  593. package/form-builder-editor/lib/components/form-builder-editor/sub-components/section-editor/section-editor.component.d.ts +82 -0
  594. package/form-builder-editor/lib/components/form-builder-editor/sub-components/validation-editor/validation-editor.component.d.ts +112 -0
  595. package/form-builder-editor/lib/components/form-builder-editor/types/editor.types.d.ts +124 -0
  596. package/form-builder-editor/lib/components/modal/confirm-dialog.component.d.ts +46 -0
  597. package/form-builder-editor/lib/components/modal/index.d.ts +4 -0
  598. package/form-builder-editor/lib/components/modal/modal.component.d.ts +44 -0
  599. package/form-builder-editor/lib/components/modal/modal.service.d.ts +93 -0
  600. package/form-builder-editor/lib/components/modal/modal.types.d.ts +110 -0
  601. package/form-builder-editor/lib/core/logging/logger.config.d.ts +18 -0
  602. package/form-builder-editor/lib/core/logging/logger.service.d.ts +75 -0
  603. package/form-builder-editor/lib/core/logging/logger.types.d.ts +70 -0
  604. package/form-builder-editor/lib/core/types/index.d.ts +133 -0
  605. package/http/entry-http.d.ts +4 -0
  606. package/http/index.d.ts +5 -0
  607. package/http/lib/components/blackbox/blackbox-fingerprint.service.d.ts +36 -0
  608. package/http/lib/components/blackbox/blackbox-storage.service.d.ts +55 -0
  609. package/http/lib/components/blackbox/blackbox.service.d.ts +144 -0
  610. package/http/lib/components/blackbox/blackbox.types.d.ts +238 -0
  611. package/http/lib/components/http/http-message.handler.d.ts +59 -0
  612. package/http/lib/components/http/http.service.d.ts +98 -0
  613. package/http/lib/components/http/http.tokens.d.ts +29 -0
  614. package/http/lib/components/http/http.types.d.ts +50 -0
  615. package/http/lib/components/http/index.d.ts +17 -0
  616. package/http/lib/components/layout-builder/layout-builder.types.d.ts +436 -0
  617. package/http/lib/components/layout-builder/layout.service.d.ts +100 -0
  618. package/http/lib/components/toast/toast-container.component.d.ts +27 -0
  619. package/http/lib/components/toast/toast.component.d.ts +37 -0
  620. package/http/lib/components/toast/toast.service.d.ts +39 -0
  621. package/http/lib/components/toast/toast.types.d.ts +52 -0
  622. package/http/lib/core/types/index.d.ts +133 -0
  623. package/http/lib/core/utils/index.d.ts +60 -0
  624. package/layout-builder/entry-layout-builder.d.ts +4 -0
  625. package/layout-builder/index.d.ts +5 -0
  626. package/layout-builder/lib/components/layout-builder/index.d.ts +16 -0
  627. package/layout-builder/lib/components/layout-builder/layout-builder.component.d.ts +85 -0
  628. package/layout-builder/lib/components/layout-builder/layout-builder.types.d.ts +436 -0
  629. package/layout-builder/lib/components/layout-builder/layout.service.d.ts +100 -0
  630. package/layout-builder/lib/core/types/index.d.ts +133 -0
  631. package/lib/components/accordion/accordion.component.d.ts +10 -3
  632. package/lib/components/accordion/accordion.types.d.ts +2 -0
  633. package/lib/components/base-layout/base-layout.component.d.ts +36 -11
  634. package/lib/components/base-layout/base-layout.types.d.ts +14 -0
  635. package/lib/components/base-layout/index.d.ts +1 -1
  636. package/lib/components/blackbox/blackbox-debugger.component.d.ts +80 -0
  637. package/lib/components/blackbox/blackbox-debugger.service.d.ts +34 -0
  638. package/lib/components/blackbox/blackbox-fingerprint.service.d.ts +36 -0
  639. package/lib/components/blackbox/blackbox-json-viewer.component.d.ts +18 -0
  640. package/lib/components/blackbox/blackbox-storage.service.d.ts +55 -0
  641. package/lib/components/blackbox/blackbox.interceptor.d.ts +38 -0
  642. package/lib/components/blackbox/blackbox.service.d.ts +144 -0
  643. package/lib/components/blackbox/blackbox.types.d.ts +238 -0
  644. package/lib/components/blackbox/index.d.ts +23 -0
  645. package/lib/components/blackbox/ui-track.directive.d.ts +20 -0
  646. package/lib/components/button/button-area.component.d.ts +6 -1
  647. package/lib/components/button/button.component.d.ts +8 -2
  648. package/lib/components/button/button.types.d.ts +5 -0
  649. package/lib/components/form-builder/form-builder.component.d.ts +125 -29
  650. package/lib/components/form-builder/form-wizard.component.d.ts +89 -4
  651. package/lib/components/form-builder/index.d.ts +7 -1
  652. package/lib/components/form-builder/services/form-error-state.matcher.d.ts +9 -0
  653. package/lib/components/form-builder/services/form-field-error.service.d.ts +38 -0
  654. package/lib/components/form-builder/services/nominatim-geocoding.service.d.ts +37 -0
  655. package/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.d.ts +9 -6
  656. package/lib/components/form-builder/sub-components/form-fields/form-number-field.component.d.ts +42 -0
  657. package/lib/components/form-builder/sub-components/form-fields/form-radio-field.component.d.ts +45 -0
  658. package/lib/components/form-builder/sub-components/form-fields/form-select-field.component.d.ts +44 -0
  659. package/lib/components/form-builder/sub-components/form-fields/form-text-field.component.d.ts +62 -0
  660. package/lib/components/form-builder/sub-components/form-fields/form-textarea-field.component.d.ts +39 -0
  661. package/lib/components/form-builder/sub-components/form-fields/index.d.ts +5 -0
  662. package/lib/components/form-builder/sub-components/location-geocoded/location-geocoded.component.d.ts +84 -0
  663. package/lib/components/form-builder/types/field.types.d.ts +48 -6
  664. package/lib/components/form-builder/types/geocoded-location.types.d.ts +116 -0
  665. package/lib/components/form-builder/types/index.d.ts +4 -3
  666. package/lib/components/form-builder/types/schema.types.d.ts +83 -6
  667. package/lib/components/form-builder/types/validation.types.d.ts +5 -0
  668. package/lib/components/form-builder-editor/index.d.ts +2 -0
  669. package/lib/components/form-builder-editor/presets/editor-presets.d.ts +25 -0
  670. package/lib/components/http/http-message.handler.d.ts +59 -0
  671. package/lib/components/http/http.service.d.ts +98 -0
  672. package/lib/components/http/http.tokens.d.ts +29 -0
  673. package/lib/components/http/http.types.d.ts +50 -0
  674. package/lib/components/http/index.d.ts +17 -0
  675. package/lib/components/page-header/breadcrumb.service.d.ts +2 -2
  676. package/lib/components/table/table.types.d.ts +5 -0
  677. package/lib/components/toast/index.d.ts +17 -0
  678. package/lib/components/toast/toast-container.component.d.ts +27 -0
  679. package/lib/components/toast/toast.component.d.ts +37 -0
  680. package/lib/components/toast/toast.service.d.ts +39 -0
  681. package/lib/components/toast/toast.types.d.ts +52 -0
  682. package/lib/core/logging/index.d.ts +8 -0
  683. package/lib/core/logging/logger.config.d.ts +18 -0
  684. package/lib/core/logging/logger.service.d.ts +75 -0
  685. package/lib/core/logging/logger.types.d.ts +70 -0
  686. package/lib/core/types/index.d.ts +76 -0
  687. package/lib/core/utils/index.d.ts +31 -0
  688. package/lib/version/ng-ui-system-version.d.ts +9 -0
  689. package/modal/entry-modal.d.ts +4 -0
  690. package/modal/index.d.ts +5 -0
  691. package/modal/lib/components/button/button.component.d.ts +61 -0
  692. package/modal/lib/components/button/button.types.d.ts +75 -0
  693. package/modal/lib/components/modal/confirm-dialog.component.d.ts +46 -0
  694. package/modal/lib/components/modal/index.d.ts +4 -0
  695. package/modal/lib/components/modal/modal.component.d.ts +44 -0
  696. package/modal/lib/components/modal/modal.service.d.ts +93 -0
  697. package/modal/lib/components/modal/modal.types.d.ts +110 -0
  698. package/modal/lib/core/types/index.d.ts +133 -0
  699. package/package.json +93 -8
  700. package/page-header/entry-page-header.d.ts +4 -0
  701. package/page-header/index.d.ts +5 -0
  702. package/page-header/lib/components/page-header/breadcrumb.service.d.ts +96 -0
  703. package/page-header/lib/components/page-header/index.d.ts +16 -0
  704. package/page-header/lib/components/page-header/page-header.component.d.ts +59 -0
  705. package/page-header/lib/components/page-header/page-header.types.d.ts +96 -0
  706. package/public-api.d.ts +11 -6
  707. package/table/entry-table.d.ts +4 -0
  708. package/table/index.d.ts +5 -0
  709. package/table/lib/components/table/index.d.ts +2 -0
  710. package/table/lib/components/table/paginated-table.component.d.ts +85 -0
  711. package/table/lib/components/table/table.types.d.ts +86 -0
  712. package/table/lib/core/utils/index.d.ts +60 -0
  713. package/toast/entry-toast.d.ts +4 -0
  714. package/toast/index.d.ts +5 -0
  715. package/toast/lib/components/toast/index.d.ts +17 -0
  716. package/toast/lib/components/toast/toast-container.component.d.ts +27 -0
  717. package/toast/lib/components/toast/toast.component.d.ts +37 -0
  718. package/toast/lib/components/toast/toast.service.d.ts +39 -0
  719. package/toast/lib/components/toast/toast.types.d.ts +52 -0
  720. package/toast/lib/core/types/index.d.ts +133 -0
  721. package/toast/lib/core/utils/index.d.ts +60 -0
@@ -0,0 +1,2829 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, PLATFORM_ID, Injectable, NgZone, EventEmitter, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, Output, Optional, Inject, ChangeDetectorRef, ElementRef, Directive, HostListener } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
+ import { isPlatformBrowser, CommonModule } from '@angular/common';
5
+ import { Router, NavigationEnd } from '@angular/router';
6
+ import { Subject, Observable, map, interval, throwError } from 'rxjs';
7
+ import { filter, takeUntil, tap, catchError } from 'rxjs/operators';
8
+ import * as i1$2 from '@angular/material/dialog';
9
+ import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';
10
+ import * as i1 from 'lucide-angular';
11
+ import { LucideAngularModule } from 'lucide-angular';
12
+ import * as i1$1 from '@angular/material/tooltip';
13
+ import { MatTooltipModule } from '@angular/material/tooltip';
14
+ import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
15
+ import * as i3 from '@angular/material/tabs';
16
+ import { MatTabsModule } from '@angular/material/tabs';
17
+
18
+ /**
19
+ * @module ng-ui-system/blackbox
20
+ * Types and interfaces for the BlackBox observability service.
21
+ *
22
+ * The BlackBox service acts as a "black box recorder" for user sessions,
23
+ * capturing navigation, UI interactions, form events and HTTP calls
24
+ * in a compressed, opaque format stored in IndexedDB.
25
+ */
26
+ // ─── Defaults ────────────────────────────────────────────────────────
27
+ /** Default configuration values. */
28
+ const UI_BLACKBOX_DEFAULTS = {
29
+ maxStorageMb: 50,
30
+ flushIntervalMs: 5000,
31
+ flushThreshold: 20,
32
+ bufferDebounceMs: 500,
33
+ formTracking: {
34
+ trackValues: false,
35
+ trackFocus: true,
36
+ trackBlur: true,
37
+ trackValueChanges: true,
38
+ trackValidation: true,
39
+ debounceMs: 500,
40
+ },
41
+ };
42
+
43
+ /**
44
+ * @module ng-ui-system/blackbox
45
+ * Fingerprint service — generates a stable device/browser hash
46
+ * using the open-source FingerprintJS library (MIT, no API key required).
47
+ *
48
+ * The fingerprint is computed once and cached in `localStorage` under
49
+ * the key `__ui_bb_fp` to avoid redundant recalculations.
50
+ */
51
+ /** localStorage key for the cached fingerprint. */
52
+ const FP_CACHE_KEY = '__ui_bb_fp';
53
+ /**
54
+ * Generates and caches a stable browser/device fingerprint.
55
+ *
56
+ * Uses `@fingerprintjs/fingerprintjs` (open-source, MIT) which analyses
57
+ * canvas rendering, WebGL, audio context, screen properties, timezone,
58
+ * user-agent, and other browser signals to produce a stable hash.
59
+ *
60
+ * @usageNotes
61
+ * ```typescript
62
+ * const fp = inject(UiBlackboxFingerprintService);
63
+ * const hash = await fp.getFingerprint();
64
+ * // => "a1b2c3d4e5f6..." (stable across sessions)
65
+ * ```
66
+ */
67
+ class UiBlackboxFingerprintService {
68
+ constructor() {
69
+ this.platformId = inject(PLATFORM_ID);
70
+ this.cached = null;
71
+ }
72
+ /**
73
+ * Returns the stable fingerprint for this device/browser.
74
+ *
75
+ * On first call, loads FingerprintJS dynamically, computes the hash,
76
+ * and caches it in both memory and `localStorage`.
77
+ * Subsequent calls return the cached value immediately.
78
+ */
79
+ async getFingerprint() {
80
+ // Return cached value if available
81
+ if (this.cached) {
82
+ return this.cached;
83
+ }
84
+ // SSR guard
85
+ if (!isPlatformBrowser(this.platformId)) {
86
+ return 'ssr-placeholder';
87
+ }
88
+ // Try localStorage cache first
89
+ try {
90
+ const stored = localStorage.getItem(FP_CACHE_KEY);
91
+ if (stored) {
92
+ this.cached = stored;
93
+ return stored;
94
+ }
95
+ }
96
+ catch {
97
+ // localStorage may be unavailable (privacy mode, quota exceeded)
98
+ }
99
+ // Compute fingerprint via FingerprintJS
100
+ try {
101
+ const FingerprintJS = await import('@fingerprintjs/fingerprintjs');
102
+ const agent = await FingerprintJS.load();
103
+ const result = await agent.get();
104
+ this.cached = result.visitorId;
105
+ // Persist to localStorage
106
+ try {
107
+ localStorage.setItem(FP_CACHE_KEY, this.cached);
108
+ }
109
+ catch {
110
+ // Silently ignore storage errors
111
+ }
112
+ return this.cached;
113
+ }
114
+ catch {
115
+ // Fallback: generate a pseudo-fingerprint from available signals
116
+ const fallback = this.generateFallbackFingerprint();
117
+ this.cached = fallback;
118
+ try {
119
+ localStorage.setItem(FP_CACHE_KEY, fallback);
120
+ }
121
+ catch {
122
+ // Silently ignore
123
+ }
124
+ return fallback;
125
+ }
126
+ }
127
+ /**
128
+ * Fallback fingerprint when FingerprintJS is unavailable.
129
+ * Combines basic browser signals into a hash.
130
+ * @internal
131
+ */
132
+ generateFallbackFingerprint() {
133
+ const signals = [
134
+ navigator.userAgent,
135
+ navigator.language,
136
+ screen.width + 'x' + screen.height,
137
+ screen.colorDepth?.toString() ?? '',
138
+ Intl.DateTimeFormat().resolvedOptions().timeZone,
139
+ navigator.hardwareConcurrency?.toString() ?? '',
140
+ ].join('|');
141
+ // Simple string hash (djb2)
142
+ let hash = 5381;
143
+ for (let i = 0; i < signals.length; i++) {
144
+ hash = ((hash << 5) + hash + signals.charCodeAt(i)) >>> 0;
145
+ }
146
+ return hash.toString(36).padStart(12, '0');
147
+ }
148
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxFingerprintService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
149
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxFingerprintService, providedIn: 'root' }); }
150
+ }
151
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxFingerprintService, decorators: [{
152
+ type: Injectable,
153
+ args: [{ providedIn: 'root' }]
154
+ }] });
155
+
156
+ /**
157
+ * @module ng-ui-system/blackbox
158
+ * IndexedDB storage adapter for BlackBox sessions.
159
+ *
160
+ * Handles persistence, compression (via native CompressionStream gzip),
161
+ * quota enforcement, and JSONL export.
162
+ *
163
+ * Design notes:
164
+ * - DB name: `ui-blackbox`, object store: `sessions`
165
+ * - Each session is gzip-compressed before storage for both size
166
+ * reduction and inherent obfuscation
167
+ * - Quota enforced by total stored size; oldest sessions are pruned first
168
+ */
169
+ const DB_NAME = 'ui-blackbox';
170
+ const DB_VERSION = 1;
171
+ const STORE_NAME = 'sessions';
172
+ /**
173
+ * Low-level IndexedDB adapter for reading/writing compressed BlackBox sessions.
174
+ *
175
+ * @usageNotes
176
+ * This service is used internally by `UiBlackboxService`.
177
+ * Consumers should interact with the higher-level `UiBlackboxService` API.
178
+ */
179
+ class UiBlackboxStorageService {
180
+ constructor() {
181
+ this.platformId = inject(PLATFORM_ID);
182
+ this.db = null;
183
+ this.maxStorageMb = UI_BLACKBOX_DEFAULTS.maxStorageMb;
184
+ }
185
+ // ─── Configuration ─────────────────────────────────────────────────
186
+ /** Update the storage quota from the parent service config. */
187
+ setMaxStorageMb(mb) {
188
+ this.maxStorageMb = mb;
189
+ }
190
+ // ─── Database lifecycle ────────────────────────────────────────────
191
+ /** Open (or create) the IndexedDB database. */
192
+ async openDb() {
193
+ if (this.db)
194
+ return this.db;
195
+ if (!isPlatformBrowser(this.platformId)) {
196
+ throw new Error('IndexedDB is not available in SSR');
197
+ }
198
+ return new Promise((resolve, reject) => {
199
+ const request = indexedDB.open(DB_NAME, DB_VERSION);
200
+ request.onupgradeneeded = () => {
201
+ const db = request.result;
202
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
203
+ const store = db.createObjectStore(STORE_NAME, { keyPath: 'sessionId' });
204
+ store.createIndex('fingerprint', 'fingerprint', { unique: false });
205
+ store.createIndex('startedAt', 'startedAt', { unique: false });
206
+ }
207
+ };
208
+ request.onsuccess = () => {
209
+ this.db = request.result;
210
+ resolve(this.db);
211
+ };
212
+ request.onerror = () => reject(request.error);
213
+ });
214
+ }
215
+ // ─── Compression helpers ───────────────────────────────────────────
216
+ /** Compress a string to a gzip Uint8Array using native CompressionStream. */
217
+ async compress(data) {
218
+ const encoder = new TextEncoder();
219
+ const stream = new Blob([encoder.encode(data)])
220
+ .stream()
221
+ .pipeThrough(new CompressionStream('gzip'));
222
+ const reader = stream.getReader();
223
+ const chunks = [];
224
+ let done = false;
225
+ while (!done) {
226
+ const result = await reader.read();
227
+ done = result.done;
228
+ if (result.value) {
229
+ chunks.push(result.value);
230
+ }
231
+ }
232
+ // Merge chunks into a single ArrayBuffer-backed Uint8Array
233
+ const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
234
+ const buffer = new ArrayBuffer(totalLength);
235
+ const merged = new Uint8Array(buffer);
236
+ let offset = 0;
237
+ for (const chunk of chunks) {
238
+ merged.set(chunk, offset);
239
+ offset += chunk.length;
240
+ }
241
+ return merged;
242
+ }
243
+ /** Decompress a gzip Uint8Array back to a string. */
244
+ async decompress(data) {
245
+ const stream = new Blob([data])
246
+ .stream()
247
+ .pipeThrough(new DecompressionStream('gzip'));
248
+ const reader = stream.getReader();
249
+ const decoder = new TextDecoder();
250
+ let result = '';
251
+ let done = false;
252
+ while (!done) {
253
+ const chunk = await reader.read();
254
+ done = chunk.done;
255
+ if (chunk.value) {
256
+ result += decoder.decode(chunk.value, { stream: !done });
257
+ }
258
+ }
259
+ return result;
260
+ }
261
+ // ─── CRUD ──────────────────────────────────────────────────────────
262
+ /**
263
+ * Persist a session to IndexedDB (gzip-compressed).
264
+ * Enforces quota after saving.
265
+ */
266
+ async saveSession(session) {
267
+ const db = await this.openDb();
268
+ const json = JSON.stringify(session);
269
+ const compressed = await this.compress(json);
270
+ return new Promise((resolve, reject) => {
271
+ const tx = db.transaction(STORE_NAME, 'readwrite');
272
+ const store = tx.objectStore(STORE_NAME);
273
+ // Store as { sessionId, fingerprint, startedAt, data: Uint8Array }
274
+ store.put({
275
+ sessionId: session.sessionId,
276
+ fingerprint: session.fingerprint,
277
+ startedAt: session.startedAt,
278
+ sizeBytes: compressed.byteLength,
279
+ data: compressed,
280
+ });
281
+ tx.oncomplete = () => {
282
+ this.enforceQuota().then(resolve).catch(resolve);
283
+ };
284
+ tx.onerror = () => reject(tx.error);
285
+ });
286
+ }
287
+ /**
288
+ * Read and decompress a single session by ID.
289
+ */
290
+ async getSession(sessionId) {
291
+ const db = await this.openDb();
292
+ return new Promise((resolve, reject) => {
293
+ const tx = db.transaction(STORE_NAME, 'readonly');
294
+ const store = tx.objectStore(STORE_NAME);
295
+ const req = store.get(sessionId);
296
+ req.onsuccess = async () => {
297
+ if (!req.result) {
298
+ resolve(null);
299
+ return;
300
+ }
301
+ try {
302
+ const json = await this.decompress(req.result.data);
303
+ resolve(JSON.parse(json));
304
+ }
305
+ catch (e) {
306
+ reject(e);
307
+ }
308
+ };
309
+ req.onerror = () => reject(req.error);
310
+ });
311
+ }
312
+ /**
313
+ * Get all stored sessions (decompressed).
314
+ */
315
+ async getAllSessions() {
316
+ const db = await this.openDb();
317
+ return new Promise((resolve, reject) => {
318
+ const tx = db.transaction(STORE_NAME, 'readonly');
319
+ const store = tx.objectStore(STORE_NAME);
320
+ const req = store.getAll();
321
+ req.onsuccess = async () => {
322
+ try {
323
+ const sessions = [];
324
+ for (const record of req.result) {
325
+ const json = await this.decompress(record.data);
326
+ sessions.push(JSON.parse(json));
327
+ }
328
+ resolve(sessions);
329
+ }
330
+ catch (e) {
331
+ reject(e);
332
+ }
333
+ };
334
+ req.onerror = () => reject(req.error);
335
+ });
336
+ }
337
+ /**
338
+ * Get all sessions for a given fingerprint.
339
+ */
340
+ async getSessionsByFingerprint(fingerprint) {
341
+ const db = await this.openDb();
342
+ return new Promise((resolve, reject) => {
343
+ const tx = db.transaction(STORE_NAME, 'readonly');
344
+ const store = tx.objectStore(STORE_NAME);
345
+ const index = store.index('fingerprint');
346
+ const req = index.getAll(fingerprint);
347
+ req.onsuccess = async () => {
348
+ try {
349
+ const sessions = [];
350
+ for (const record of req.result) {
351
+ const json = await this.decompress(record.data);
352
+ sessions.push(JSON.parse(json));
353
+ }
354
+ resolve(sessions);
355
+ }
356
+ catch (e) {
357
+ reject(e);
358
+ }
359
+ };
360
+ req.onerror = () => reject(req.error);
361
+ });
362
+ }
363
+ /**
364
+ * Generate a downloadable JSONL Blob of all sessions.
365
+ * Each line is one JSON-serialised session (decompressed).
366
+ */
367
+ async exportDump() {
368
+ const sessions = await this.getAllSessions();
369
+ const lines = sessions.map((s) => JSON.stringify(s)).join('\n');
370
+ return new Blob([lines], { type: 'application/x-ndjson' });
371
+ }
372
+ /**
373
+ * Delete all stored sessions.
374
+ */
375
+ async clearAll() {
376
+ const db = await this.openDb();
377
+ return new Promise((resolve, reject) => {
378
+ const tx = db.transaction(STORE_NAME, 'readwrite');
379
+ const store = tx.objectStore(STORE_NAME);
380
+ store.clear();
381
+ tx.oncomplete = () => resolve();
382
+ tx.onerror = () => reject(tx.error);
383
+ });
384
+ }
385
+ // ─── Quota enforcement ─────────────────────────────────────────────
386
+ /**
387
+ * Prune oldest sessions until total stored size is under `maxStorageMb`.
388
+ * @internal
389
+ */
390
+ async enforceQuota() {
391
+ const db = await this.openDb();
392
+ const maxBytes = this.maxStorageMb * 1024 * 1024;
393
+ return new Promise((resolve, reject) => {
394
+ const tx = db.transaction(STORE_NAME, 'readwrite');
395
+ const store = tx.objectStore(STORE_NAME);
396
+ const index = store.index('startedAt');
397
+ // Collect all records sorted by startedAt (ascending = oldest first)
398
+ const cursorReq = index.openCursor();
399
+ const records = [];
400
+ let totalSize = 0;
401
+ cursorReq.onsuccess = () => {
402
+ const cursor = cursorReq.result;
403
+ if (cursor) {
404
+ const size = cursor.value.sizeBytes || 0;
405
+ totalSize += size;
406
+ records.push({ key: cursor.primaryKey, size });
407
+ cursor.continue();
408
+ }
409
+ else {
410
+ // Cursor exhausted — prune if over quota
411
+ if (totalSize <= maxBytes) {
412
+ resolve();
413
+ return;
414
+ }
415
+ let freed = 0;
416
+ const target = totalSize - maxBytes;
417
+ for (const record of records) {
418
+ if (freed >= target)
419
+ break;
420
+ store.delete(record.key);
421
+ freed += record.size;
422
+ }
423
+ resolve();
424
+ }
425
+ };
426
+ cursorReq.onerror = () => reject(cursorReq.error);
427
+ tx.onerror = () => reject(tx.error);
428
+ });
429
+ }
430
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
431
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxStorageService, providedIn: 'root' }); }
432
+ }
433
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxStorageService, decorators: [{
434
+ type: Injectable,
435
+ args: [{ providedIn: 'root' }]
436
+ }] });
437
+
438
+ /**
439
+ * @module ng-ui-system/blackbox
440
+ * Core BlackBox observability service — the main entry point for session
441
+ * recording, event buffering, and export.
442
+ *
443
+ * Architecture:
444
+ * - Root singleton (`providedIn: 'root'`)
445
+ * - On first inject: generates fingerprint, creates session, subscribes to Router events
446
+ * - Centralised in-memory buffer with periodic/threshold-based flush to IndexedDB
447
+ * - `window:beforeunload` triggers a final best-effort async flush
448
+ */
449
+ /**
450
+ * Central BlackBox observability service.
451
+ *
452
+ * Records navigation, UI interactions, form events, and HTTP calls
453
+ * into compressed sessions stored in IndexedDB.
454
+ *
455
+ * @usageNotes
456
+ * The service activates automatically when injected. Provide it at root level
457
+ * and it will start recording immediately.
458
+ *
459
+ * ```typescript
460
+ * // app.config.ts — just importing the service is enough
461
+ * import { UiBlackboxService } from '@gnggln/ng-ui-system';
462
+ *
463
+ * export const appConfig: ApplicationConfig = {
464
+ * providers: [
465
+ * // The service is providedIn: 'root', so it's auto-provided.
466
+ * // To force eager initialisation:
467
+ * { provide: APP_INITIALIZER, useFactory: () => () => inject(UiBlackboxService), multi: true },
468
+ * ],
469
+ * };
470
+ * ```
471
+ */
472
+ class UiBlackboxService {
473
+ constructor() {
474
+ this.platformId = inject(PLATFORM_ID);
475
+ this.router = inject(Router);
476
+ this.zone = inject(NgZone);
477
+ this.fingerprint = inject(UiBlackboxFingerprintService);
478
+ this.storage = inject(UiBlackboxStorageService);
479
+ this.destroy$ = new Subject();
480
+ this.config = { ...UI_BLACKBOX_DEFAULTS };
481
+ this.currentStep = null;
482
+ this.initialised = false;
483
+ // ─── Centralised event buffer ──────────────────────────────────────
484
+ this.buffer = [];
485
+ this.flushTimerId = null;
486
+ this.beforeUnloadHandler = null;
487
+ if (isPlatformBrowser(this.platformId)) {
488
+ this.init();
489
+ }
490
+ }
491
+ // ─── Initialisation ────────────────────────────────────────────────
492
+ async init() {
493
+ // Generate session ID
494
+ const sessionId = this.generateUuid();
495
+ const fp = await this.fingerprint.getFingerprint();
496
+ this.session = {
497
+ sessionId,
498
+ fingerprint: fp,
499
+ startedAt: Date.now(),
500
+ userAgent: navigator.userAgent,
501
+ steps: [],
502
+ formEvents: [],
503
+ calls: [],
504
+ };
505
+ // Create initial step from current URL
506
+ this.currentStep = {
507
+ timestamp: Date.now(),
508
+ route: this.router.url,
509
+ actions: [],
510
+ };
511
+ this.session.steps.push(this.currentStep);
512
+ // Subscribe to router navigation
513
+ this.router.events
514
+ .pipe(filter((e) => e instanceof NavigationEnd), takeUntil(this.destroy$))
515
+ .subscribe((event) => {
516
+ const previousRoute = this.currentStep?.route;
517
+ this.currentStep = {
518
+ timestamp: Date.now(),
519
+ route: event.urlAfterRedirects,
520
+ previousRoute,
521
+ actions: [],
522
+ };
523
+ this.pushBufferEvent({ kind: 'step', payload: this.currentStep });
524
+ });
525
+ // Setup periodic flush (outside Angular zone to avoid triggering CD)
526
+ this.zone.runOutsideAngular(() => {
527
+ this.flushTimerId = setInterval(() => this.flush(), this.config.flushIntervalMs);
528
+ // Best-effort flush on tab close (IndexedDB is async, no sync fallback)
529
+ this.beforeUnloadHandler = () => {
530
+ this.session.endedAt = Date.now();
531
+ void this.flush();
532
+ };
533
+ window.addEventListener('beforeunload', this.beforeUnloadHandler);
534
+ });
535
+ this.initialised = true;
536
+ }
537
+ // ─── Public API: Configuration ─────────────────────────────────────
538
+ /**
539
+ * Update the BlackBox configuration.
540
+ * Can be called at any time; changes take effect immediately.
541
+ */
542
+ configure(config) {
543
+ this.config = {
544
+ ...this.config,
545
+ ...config,
546
+ formTracking: {
547
+ ...this.config.formTracking,
548
+ ...(config.formTracking ?? {}),
549
+ },
550
+ };
551
+ this.storage.setMaxStorageMb(this.config.maxStorageMb);
552
+ // Restart flush timer with new interval
553
+ if (this.flushTimerId !== null) {
554
+ clearInterval(this.flushTimerId);
555
+ this.zone.runOutsideAngular(() => {
556
+ this.flushTimerId = setInterval(() => this.flush(), this.config.flushIntervalMs);
557
+ });
558
+ }
559
+ }
560
+ /** Returns the current configuration (readonly snapshot). */
561
+ getConfig() {
562
+ return { ...this.config };
563
+ }
564
+ // ─── Public API: Tracking ──────────────────────────────────────────
565
+ /**
566
+ * Manually track a UI action (click).
567
+ *
568
+ * Called automatically by `UiButtonAreaComponent` and the `[uiTrack]` directive.
569
+ * Can also be called programmatically for custom interactions.
570
+ *
571
+ * @param trackId - Identifier for the action (e.g. button id, link name).
572
+ * @param meta - Optional metadata: `tag`, `text`, `id` of the element.
573
+ */
574
+ trackAction(trackId, meta) {
575
+ if (!this.initialised)
576
+ return;
577
+ const action = {
578
+ timestamp: Date.now(),
579
+ type: 'click',
580
+ target: {
581
+ tag: meta?.tag ?? 'unknown',
582
+ id: meta?.id,
583
+ trackId,
584
+ text: meta?.text?.substring(0, 50),
585
+ },
586
+ };
587
+ // Add to current step immediately (for in-memory session access)
588
+ this.currentStep?.actions.push(action);
589
+ // Also push to buffer for persistence
590
+ this.pushBufferEvent({
591
+ kind: 'action',
592
+ payload: { ...action, route: this.currentStep?.route ?? this.router.url },
593
+ });
594
+ }
595
+ /**
596
+ * Track a form interaction event.
597
+ *
598
+ * Called automatically by `UiFormBuilderComponent` when the BlackBox
599
+ * service is available.
600
+ */
601
+ trackFormEvent(event) {
602
+ if (!this.initialised)
603
+ return;
604
+ this.session.formEvents.push(event);
605
+ this.pushBufferEvent({ kind: 'formEvent', payload: event });
606
+ }
607
+ /**
608
+ * Track an intercepted HTTP call.
609
+ * Called by `UiBlackboxInterceptor`.
610
+ * @internal
611
+ */
612
+ trackHttpCall(call) {
613
+ if (!this.initialised)
614
+ return;
615
+ this.session.calls.push(call);
616
+ this.pushBufferEvent({ kind: 'httpCall', payload: call });
617
+ }
618
+ // ─── Public API: Session access ────────────────────────────────────
619
+ /**
620
+ * Returns the current in-memory session (live, not yet flushed).
621
+ * Useful for debugging or real-time inspection.
622
+ */
623
+ getCurrentSession() {
624
+ return this.session;
625
+ }
626
+ /**
627
+ * Retrieve all sessions stored in IndexedDB (decompressed).
628
+ */
629
+ async getSessionHistory() {
630
+ return this.storage.getAllSessions();
631
+ }
632
+ /**
633
+ * Retrieve sessions for the current device fingerprint.
634
+ */
635
+ async getSessionsForDevice() {
636
+ const fp = await this.fingerprint.getFingerprint();
637
+ return this.storage.getSessionsByFingerprint(fp);
638
+ }
639
+ // ─── Public API: Export ────────────────────────────────────────────
640
+ /**
641
+ * Export all sessions as a downloadable JSONL Blob.
642
+ *
643
+ * @returns A Blob containing JSONL data (one session per line).
644
+ *
645
+ * @example
646
+ * ```typescript
647
+ * const blob = await blackbox.exportSessions();
648
+ * const url = URL.createObjectURL(blob);
649
+ * const a = document.createElement('a');
650
+ * a.href = url;
651
+ * a.download = 'blackbox-sessions.jsonl';
652
+ * a.click();
653
+ * ```
654
+ */
655
+ async exportSessions() {
656
+ // Flush current session first
657
+ await this.flush();
658
+ return this.storage.exportDump();
659
+ }
660
+ // ─── Public API: Lifecycle ─────────────────────────────────────────
661
+ /**
662
+ * Explicitly end the current session.
663
+ * Flushes all buffered events and marks the session as ended.
664
+ */
665
+ async endSession() {
666
+ if (!this.initialised)
667
+ return;
668
+ this.session.endedAt = Date.now();
669
+ await this.flush();
670
+ }
671
+ /**
672
+ * Clear all stored sessions from IndexedDB.
673
+ */
674
+ async clearHistory() {
675
+ return this.storage.clearAll();
676
+ }
677
+ // ─── Buffer management ─────────────────────────────────────────────
678
+ /**
679
+ * Push an event into the centralised buffer.
680
+ * Triggers an immediate flush if the buffer exceeds the threshold.
681
+ * @internal
682
+ */
683
+ pushBufferEvent(event) {
684
+ this.buffer.push(event);
685
+ if (this.buffer.length >= this.config.flushThreshold) {
686
+ this.flush();
687
+ }
688
+ }
689
+ /**
690
+ * Flush buffered events to IndexedDB asynchronously.
691
+ * @internal
692
+ */
693
+ async flush() {
694
+ if (!this.initialised || this.buffer.length === 0)
695
+ return;
696
+ // Drain buffer
697
+ const events = this.buffer.splice(0);
698
+ // Apply step events to the session
699
+ for (const event of events) {
700
+ if (event.kind === 'step') {
701
+ // Steps are already pushed in real-time; ensure they're in the session
702
+ if (!this.session.steps.includes(event.payload)) {
703
+ this.session.steps.push(event.payload);
704
+ }
705
+ }
706
+ // Actions, formEvents, httpCalls are already pushed in real-time
707
+ // to the in-memory session by trackAction/trackFormEvent/trackHttpCall
708
+ }
709
+ // Save the full session snapshot
710
+ try {
711
+ await this.storage.saveSession(this.session);
712
+ }
713
+ catch (e) {
714
+ // storage error — re-enqueue events for next flush
715
+ console.warn('[UiBlackbox] Storage flush failed:', e);
716
+ }
717
+ }
718
+ // ─── Utilities ─────────────────────────────────────────────────────
719
+ /** Generate a UUID v4. */
720
+ generateUuid() {
721
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
722
+ return crypto.randomUUID();
723
+ }
724
+ // Fallback for older browsers
725
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
726
+ const r = (Math.random() * 16) | 0;
727
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
728
+ return v.toString(16);
729
+ });
730
+ }
731
+ // ─── Cleanup ───────────────────────────────────────────────────────
732
+ ngOnDestroy() {
733
+ this.destroy$.next();
734
+ this.destroy$.complete();
735
+ if (this.flushTimerId !== null) {
736
+ clearInterval(this.flushTimerId);
737
+ }
738
+ if (this.beforeUnloadHandler) {
739
+ window.removeEventListener('beforeunload', this.beforeUnloadHandler);
740
+ }
741
+ // Final flush
742
+ void this.flush();
743
+ }
744
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
745
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxService, providedIn: 'root' }); }
746
+ }
747
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxService, decorators: [{
748
+ type: Injectable,
749
+ args: [{ providedIn: 'root' }]
750
+ }], ctorParameters: () => [] });
751
+
752
+ /**
753
+ * Componente modale standalone con struttura header/body/footer.
754
+ *
755
+ * Da utilizzare come wrapper all'interno di componenti aperti
756
+ * tramite `UiModalService.open()`. Fornisce la struttura visuale
757
+ * e gestisce la chiusura programmatica.
758
+ *
759
+ * **Content projection:**
760
+ * - Contenuto di default: va nel body
761
+ * - `[uiModalHeader]`: sostituisce il titolo nell'header
762
+ * - `[uiModalFooter]`: azioni nel footer
763
+ *
764
+ * @selector ui-modal
765
+ *
766
+ * @example
767
+ * ```html
768
+ * <ui-modal [config]="{ title: 'Dettagli', size: 'md' }">
769
+ * <p>Contenuto del body della modale.</p>
770
+ * <div uiModalFooter>
771
+ * <ui-button label="Chiudi" variant="ghost" (click)="close()" />
772
+ * <ui-button label="Salva" variant="primary" (click)="save()" />
773
+ * </div>
774
+ * </ui-modal>
775
+ * ```
776
+ */
777
+ class UiModalComponent {
778
+ constructor() {
779
+ /** Configurazione strutturale della modale. */
780
+ this.config = {};
781
+ /**
782
+ * Emesso quando la modale viene chiusa.
783
+ * Utile se il componente e usato direttamente nel template
784
+ * senza UiModalService.
785
+ */
786
+ this.closed = new EventEmitter();
787
+ /** @internal Riferimento al dialogo Material per la chiusura. */
788
+ this.dialogRef = inject(MatDialogRef, { optional: true });
789
+ }
790
+ /** Chiude la modale con un risultato opzionale. */
791
+ close(result) {
792
+ if (this.dialogRef) {
793
+ this.dialogRef.close(result);
794
+ }
795
+ else {
796
+ this.closed.emit(result);
797
+ }
798
+ }
799
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
800
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: UiModalComponent, isStandalone: true, selector: "ui-modal", inputs: { config: "config" }, outputs: { closed: "closed" }, host: { attributes: { "role": "dialog", "aria-modal": "true" }, properties: { "attr.aria-label": "config.title || null" }, classAttribute: "ui-modal-host" }, ngImport: i0, template: `
801
+ <!-- Header -->
802
+ @if (config.title || config.showCloseButton !== false) {
803
+ <div class="ui-modal__header">
804
+ <div class="ui-modal__header-content">
805
+ <ng-content select="[uiModalHeader]">
806
+ @if (config.title) {
807
+ <h2 class="ui-modal__title">{{ config.title }}</h2>
808
+ }
809
+ </ng-content>
810
+ </div>
811
+ @if (config.showCloseButton !== false) {
812
+ <button
813
+ class="ui-modal__close"
814
+ (click)="close()"
815
+ aria-label="Chiudi modale"
816
+ type="button"
817
+ >
818
+ <lucide-icon name="x" [size]="18" aria-hidden="true" />
819
+ </button>
820
+ }
821
+ </div>
822
+ }
823
+
824
+ <!-- Body -->
825
+ <div class="ui-modal__body">
826
+ <ng-content />
827
+ </div>
828
+
829
+ <!-- Footer -->
830
+ @if (!config.hideFooter) {
831
+ <div class="ui-modal__footer">
832
+ <ng-content select="[uiModalFooter]" />
833
+ </div>
834
+ }
835
+ `, isInline: true, styles: [".ui-modal-host{display:flex;flex-direction:column;max-height:90vh;background:var(--ui-color-surface);border-radius:var(--ui-radius-lg);overflow:hidden;box-shadow:var(--ui-shadow-lg)}.ui-modal__header{display:flex;align-items:center;justify-content:space-between;padding:var(--ui-spacing-4) var(--ui-spacing-6);border-bottom:1px solid var(--ui-color-border);background:var(--ui-color-bg-subtle);flex-shrink:0;gap:var(--ui-spacing-3)}.ui-modal__header-content{flex:1;min-width:0}.ui-modal__title{margin:0;font-size:var(--ui-font-size-lg);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);line-height:var(--ui-line-height-tight);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-modal__close{appearance:none;border:none;background:transparent;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:var(--ui-radius-md);color:var(--ui-color-text-secondary);flex-shrink:0;transition:background-color var(--ui-transition-fast),color var(--ui-transition-fast)}.ui-modal__close:focus{outline:none}.ui-modal__close:focus-visible{outline:var(--ui-focus-ring-width) solid var(--ui-focus-ring-color);outline-offset:var(--ui-focus-ring-offset)}.ui-modal__close:hover{background:var(--ui-color-surface-hover);color:var(--ui-color-text)}.ui-modal__close:active{background:var(--ui-color-neutral-200)}.ui-modal__body{flex:1;overflow-y:auto;padding:var(--ui-spacing-6)}.ui-modal__body::-webkit-scrollbar{width:6px}.ui-modal__body::-webkit-scrollbar-track{background:transparent}.ui-modal__body::-webkit-scrollbar-thumb{background:var(--ui-color-neutral-300);border-radius:var(--ui-radius-full)}.ui-modal__body::-webkit-scrollbar-thumb:hover{background:var(--ui-color-neutral-400)}.ui-modal__footer{display:flex;justify-content:flex-end;align-items:center;gap:var(--ui-spacing-2);padding:var(--ui-spacing-4) var(--ui-spacing-6);border-top:1px solid var(--ui-color-border);background:var(--ui-color-bg-subtle);flex-shrink:0}.ui-modal__footer:empty{display:none}.ui-modal-panel .mat-mdc-dialog-container{--mdc-dialog-container-color: transparent;--mdc-dialog-container-shape: var(--ui-radius-lg)}.ui-modal-panel .mat-mdc-dialog-container .mdc-dialog__surface{background:transparent!important;box-shadow:none!important;border-radius:var(--ui-radius-lg)!important;overflow:visible}.ui-modal-panel--sm .mat-mdc-dialog-container{max-width:400px}.ui-modal-panel--md .mat-mdc-dialog-container{max-width:560px}.ui-modal-panel--lg .mat-mdc-dialog-container{max-width:720px}.ui-modal-panel--xl .mat-mdc-dialog-container{max-width:900px}.ui-modal-panel--fullscreen .ui-modal-host{height:95vh;max-height:95vh;border-radius:var(--ui-radius-md)}.ui-modal-backdrop{background:var(--ui-color-backdrop)}.ui-confirm-backdrop--stacked{background:#0000004d}@media (max-width: 768px){.ui-modal__header,.ui-modal__footer{padding-left:var(--ui-spacing-4);padding-right:var(--ui-spacing-4)}.ui-modal__body{padding:var(--ui-spacing-4)}}\n"], dependencies: [{ kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
836
+ }
837
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiModalComponent, decorators: [{
838
+ type: Component,
839
+ args: [{ selector: 'ui-modal', standalone: true, imports: [LucideAngularModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
840
+ class: 'ui-modal-host',
841
+ role: 'dialog',
842
+ 'aria-modal': 'true',
843
+ '[attr.aria-label]': 'config.title || null',
844
+ }, template: `
845
+ <!-- Header -->
846
+ @if (config.title || config.showCloseButton !== false) {
847
+ <div class="ui-modal__header">
848
+ <div class="ui-modal__header-content">
849
+ <ng-content select="[uiModalHeader]">
850
+ @if (config.title) {
851
+ <h2 class="ui-modal__title">{{ config.title }}</h2>
852
+ }
853
+ </ng-content>
854
+ </div>
855
+ @if (config.showCloseButton !== false) {
856
+ <button
857
+ class="ui-modal__close"
858
+ (click)="close()"
859
+ aria-label="Chiudi modale"
860
+ type="button"
861
+ >
862
+ <lucide-icon name="x" [size]="18" aria-hidden="true" />
863
+ </button>
864
+ }
865
+ </div>
866
+ }
867
+
868
+ <!-- Body -->
869
+ <div class="ui-modal__body">
870
+ <ng-content />
871
+ </div>
872
+
873
+ <!-- Footer -->
874
+ @if (!config.hideFooter) {
875
+ <div class="ui-modal__footer">
876
+ <ng-content select="[uiModalFooter]" />
877
+ </div>
878
+ }
879
+ `, styles: [".ui-modal-host{display:flex;flex-direction:column;max-height:90vh;background:var(--ui-color-surface);border-radius:var(--ui-radius-lg);overflow:hidden;box-shadow:var(--ui-shadow-lg)}.ui-modal__header{display:flex;align-items:center;justify-content:space-between;padding:var(--ui-spacing-4) var(--ui-spacing-6);border-bottom:1px solid var(--ui-color-border);background:var(--ui-color-bg-subtle);flex-shrink:0;gap:var(--ui-spacing-3)}.ui-modal__header-content{flex:1;min-width:0}.ui-modal__title{margin:0;font-size:var(--ui-font-size-lg);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);line-height:var(--ui-line-height-tight);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-modal__close{appearance:none;border:none;background:transparent;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:var(--ui-radius-md);color:var(--ui-color-text-secondary);flex-shrink:0;transition:background-color var(--ui-transition-fast),color var(--ui-transition-fast)}.ui-modal__close:focus{outline:none}.ui-modal__close:focus-visible{outline:var(--ui-focus-ring-width) solid var(--ui-focus-ring-color);outline-offset:var(--ui-focus-ring-offset)}.ui-modal__close:hover{background:var(--ui-color-surface-hover);color:var(--ui-color-text)}.ui-modal__close:active{background:var(--ui-color-neutral-200)}.ui-modal__body{flex:1;overflow-y:auto;padding:var(--ui-spacing-6)}.ui-modal__body::-webkit-scrollbar{width:6px}.ui-modal__body::-webkit-scrollbar-track{background:transparent}.ui-modal__body::-webkit-scrollbar-thumb{background:var(--ui-color-neutral-300);border-radius:var(--ui-radius-full)}.ui-modal__body::-webkit-scrollbar-thumb:hover{background:var(--ui-color-neutral-400)}.ui-modal__footer{display:flex;justify-content:flex-end;align-items:center;gap:var(--ui-spacing-2);padding:var(--ui-spacing-4) var(--ui-spacing-6);border-top:1px solid var(--ui-color-border);background:var(--ui-color-bg-subtle);flex-shrink:0}.ui-modal__footer:empty{display:none}.ui-modal-panel .mat-mdc-dialog-container{--mdc-dialog-container-color: transparent;--mdc-dialog-container-shape: var(--ui-radius-lg)}.ui-modal-panel .mat-mdc-dialog-container .mdc-dialog__surface{background:transparent!important;box-shadow:none!important;border-radius:var(--ui-radius-lg)!important;overflow:visible}.ui-modal-panel--sm .mat-mdc-dialog-container{max-width:400px}.ui-modal-panel--md .mat-mdc-dialog-container{max-width:560px}.ui-modal-panel--lg .mat-mdc-dialog-container{max-width:720px}.ui-modal-panel--xl .mat-mdc-dialog-container{max-width:900px}.ui-modal-panel--fullscreen .ui-modal-host{height:95vh;max-height:95vh;border-radius:var(--ui-radius-md)}.ui-modal-backdrop{background:var(--ui-color-backdrop)}.ui-confirm-backdrop--stacked{background:#0000004d}@media (max-width: 768px){.ui-modal__header,.ui-modal__footer{padding-left:var(--ui-spacing-4);padding-right:var(--ui-spacing-4)}.ui-modal__body{padding:var(--ui-spacing-4)}}\n"] }]
880
+ }], propDecorators: { config: [{
881
+ type: Input
882
+ }], closed: [{
883
+ type: Output
884
+ }] } });
885
+
886
+ /** @internal Size-to-icon-pixel mapping for consistent icon sizing. */
887
+ const ICON_SIZE_MAP = {
888
+ xs: 14,
889
+ sm: 16,
890
+ md: 18,
891
+ lg: 20,
892
+ xl: 22,
893
+ };
894
+ /**
895
+ * Standalone button component with design-token-driven theming,
896
+ * variant/size system, Lucide icon support, loading states, and WCAG 2.1 AA accessibility.
897
+ *
898
+ * Uses native `<button>` click events — listen with `(click)` on the host element.
899
+ *
900
+ * @selector ui-button
901
+ *
902
+ * @example
903
+ * ```html
904
+ * <!-- Basic -->
905
+ * <ui-button label="Save" variant="primary" icon="save" (click)="save()" />
906
+ *
907
+ * <!-- Loading -->
908
+ * <ui-button label="Submitting..." variant="primary" [loading]="true" />
909
+ *
910
+ * <!-- Icon-only: tooltip o ariaLabel forniscono il nome accessibile -->
911
+ * <ui-button icon="trash-2" variant="warn" tooltip="Elimina" (click)="delete()" />
912
+ * ```
913
+ */
914
+ class UiButtonComponent {
915
+ constructor() {
916
+ /** Button label text. */
917
+ this.label = '';
918
+ /** Tooltip text shown on hover (uses Angular Material tooltip). */
919
+ this.tooltip = '';
920
+ /** Visual style variant. */
921
+ this.variant = 'primary';
922
+ /** Size variant. */
923
+ this.size = 'md';
924
+ /** Icon position relative to the label. */
925
+ this.iconPosition = 'trailing';
926
+ /** Whether the button is in a loading state. Disables interaction and shows spinner. */
927
+ this.loading = false;
928
+ /** Whether the button is disabled. */
929
+ this.disabled = false;
930
+ /** Expand button to full container width. */
931
+ this.fullWidth = false;
932
+ /** HTML button type attribute. */
933
+ this.type = 'button';
934
+ }
935
+ /**
936
+ * Nome accessibile risolto per il pulsante nativo.
937
+ * Usa `ariaLabel` se impostato; per pulsanti solo icona senza testo visibile
938
+ * ricade sul `tooltip` (il tooltip Material non sostituisce il nome accessibile).
939
+ */
940
+ get effectiveAriaLabel() {
941
+ const explicit = this.ariaLabel?.trim();
942
+ if (explicit)
943
+ return explicit;
944
+ if (this.label?.trim())
945
+ return undefined;
946
+ const tip = this.tooltip?.trim();
947
+ if (this.icon && tip)
948
+ return tip;
949
+ return undefined;
950
+ }
951
+ /** Computed icon pixel size based on button size. */
952
+ get iconSize() {
953
+ return ICON_SIZE_MAP[this.size] ?? 18;
954
+ }
955
+ /** Assembled CSS class string for the native button element. */
956
+ get buttonClasses() {
957
+ return [
958
+ 'ui-button',
959
+ `ui-button--${this.variant}`,
960
+ `ui-button--${this.size}`,
961
+ this.loading ? 'ui-button--loading' : '',
962
+ this.fullWidth ? 'ui-button--full-width' : '',
963
+ !this.label && this.icon ? 'ui-button--icon-only' : '',
964
+ this.customClass ?? '',
965
+ ]
966
+ .filter(Boolean)
967
+ .join(' ');
968
+ }
969
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
970
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: UiButtonComponent, isStandalone: true, selector: "ui-button", inputs: { label: "label", tooltip: "tooltip", variant: "variant", size: "size", icon: "icon", iconPosition: "iconPosition", loading: "loading", disabled: "disabled", fullWidth: "fullWidth", type: "type", ariaLabel: "ariaLabel", customClass: "customClass" }, host: { properties: { "class.ui-button-host--full-width": "fullWidth" }, classAttribute: "ui-button-host" }, ngImport: i0, template: `
971
+ <button
972
+ [type]="type"
973
+ [class]="buttonClasses"
974
+ [disabled]="disabled || loading"
975
+ [attr.aria-label]="effectiveAriaLabel || null"
976
+ [attr.aria-busy]="loading || null"
977
+ [matTooltip]="tooltip"
978
+ >
979
+ <span class="ui-button__content" [class.ui-button__content--hidden]="loading">
980
+ @if (icon && iconPosition === 'leading') {
981
+ <lucide-icon [name]="icon!" [size]="iconSize" aria-hidden="true" />
982
+ }
983
+ @if (label) {
984
+ <span class="ui-button__label">{{ label }}</span>
985
+ }
986
+ <ng-content />
987
+ @if (icon && iconPosition === 'trailing') {
988
+ <lucide-icon [name]="icon!" [size]="iconSize" aria-hidden="true" />
989
+ }
990
+ </span>
991
+ @if (loading) {
992
+ <span class="ui-button__spinner-overlay">
993
+ <span class="ui-button__spinner" aria-hidden="true"></span>
994
+ <span class="ui-button__sr-only">Loading</span>
995
+ </span>
996
+ }
997
+ </button>
998
+ `, isInline: true, styles: [".ui-button-host{display:inline-flex}.ui-button-host--full-width{display:flex;width:100%}.ui-button{appearance:none;border:none;cursor:pointer;font-family:var(--ui-font-family);font-weight:var(--ui-font-weight-medium);letter-spacing:.01em;position:relative;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--ui-radius-md);white-space:nowrap;text-decoration:none;overflow:hidden;transition:background-color var(--ui-transition-fast),color var(--ui-transition-fast),border-color var(--ui-transition-fast),box-shadow var(--ui-transition-fast),opacity var(--ui-transition-fast)}.ui-button:focus{outline:none}.ui-button:focus-visible{outline:var(--ui-focus-ring-width) solid var(--ui-focus-ring-color);outline-offset:var(--ui-focus-ring-offset)}.ui-button:disabled{opacity:.5;cursor:not-allowed}.ui-button--full-width{width:100%}.ui-button--xs{height:28px;padding:0 var(--ui-spacing-2);font-size:var(--ui-font-size-xs);border-radius:var(--ui-radius-sm);gap:var(--ui-spacing-1)}.ui-button--sm{height:32px;padding:0 var(--ui-spacing-3);font-size:var(--ui-font-size-xs);gap:6px}.ui-button--md{height:36px;padding:0 var(--ui-spacing-4);font-size:var(--ui-font-size-sm);gap:var(--ui-spacing-2)}.ui-button--lg{height:40px;padding:0 var(--ui-spacing-5);font-size:var(--ui-font-size-sm);gap:var(--ui-spacing-2)}.ui-button--xl{height:48px;padding:0 var(--ui-spacing-6);font-size:var(--ui-font-size-md);gap:var(--ui-spacing-3)}.ui-button--icon-only.ui-button--xs{width:28px;padding:0}.ui-button--icon-only.ui-button--sm{width:32px;padding:0}.ui-button--icon-only.ui-button--md{width:36px;padding:0}.ui-button--icon-only.ui-button--lg{width:40px;padding:0}.ui-button--icon-only.ui-button--xl{width:48px;padding:0}.ui-button--primary{background:var(--ui-color-primary);color:var(--ui-color-primary-contrast)}.ui-button--primary:hover:not(:disabled){background:var(--ui-color-primary-hover)}.ui-button--primary:active:not(:disabled){background:var(--ui-color-primary-active)}.ui-button--accent{background:var(--ui-color-accent);color:var(--ui-color-accent-contrast)}.ui-button--accent:hover:not(:disabled){background:var(--ui-color-accent-hover)}.ui-button--accent:active:not(:disabled){background:var(--ui-color-accent-active)}.ui-button--warn{background:var(--ui-color-warn);color:var(--ui-color-warn-contrast)}.ui-button--warn:hover:not(:disabled){background:var(--ui-color-warn-hover)}.ui-button--warn:active:not(:disabled){background:var(--ui-color-warn-active)}.ui-button--neutral{background:var(--ui-color-neutral-200);color:var(--ui-color-text)}.ui-button--neutral:hover:not(:disabled){background:var(--ui-color-neutral-300)}.ui-button--neutral:active:not(:disabled){background:var(--ui-color-neutral-400);color:var(--ui-color-text-inverse)}.ui-button--ghost{background:transparent;color:var(--ui-color-text-secondary)}.ui-button--ghost:hover:not(:disabled){background:var(--ui-color-surface-hover);color:var(--ui-color-text)}.ui-button--ghost:active:not(:disabled){background:var(--ui-color-neutral-200)}.ui-button--outline{background:transparent;color:var(--ui-color-primary);border:1.5px solid var(--ui-color-border-strong)}.ui-button--outline:hover:not(:disabled){background:var(--ui-color-primary-light);border-color:var(--ui-color-primary)}.ui-button--outline:active:not(:disabled){background:var(--ui-color-primary-light);border-color:var(--ui-color-primary-hover)}.ui-button--loading{cursor:wait;pointer-events:none}.ui-button__content{display:inline-flex;align-items:center;gap:inherit;transition:visibility var(--ui-transition-fast)}.ui-button__content--hidden{visibility:hidden}.ui-button__label{line-height:1;background-color:transparent!important}.ui-button__spinner-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.ui-button__spinner{width:1.1em;height:1.1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:ui-button-spin .6s linear infinite}@keyframes ui-button-spin{to{transform:rotate(360deg)}}.ui-button__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
999
+ }
1000
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiButtonComponent, decorators: [{
1001
+ type: Component,
1002
+ args: [{ selector: 'ui-button', standalone: true, imports: [MatTooltipModule, LucideAngularModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
1003
+ class: 'ui-button-host',
1004
+ '[class.ui-button-host--full-width]': 'fullWidth',
1005
+ }, template: `
1006
+ <button
1007
+ [type]="type"
1008
+ [class]="buttonClasses"
1009
+ [disabled]="disabled || loading"
1010
+ [attr.aria-label]="effectiveAriaLabel || null"
1011
+ [attr.aria-busy]="loading || null"
1012
+ [matTooltip]="tooltip"
1013
+ >
1014
+ <span class="ui-button__content" [class.ui-button__content--hidden]="loading">
1015
+ @if (icon && iconPosition === 'leading') {
1016
+ <lucide-icon [name]="icon!" [size]="iconSize" aria-hidden="true" />
1017
+ }
1018
+ @if (label) {
1019
+ <span class="ui-button__label">{{ label }}</span>
1020
+ }
1021
+ <ng-content />
1022
+ @if (icon && iconPosition === 'trailing') {
1023
+ <lucide-icon [name]="icon!" [size]="iconSize" aria-hidden="true" />
1024
+ }
1025
+ </span>
1026
+ @if (loading) {
1027
+ <span class="ui-button__spinner-overlay">
1028
+ <span class="ui-button__spinner" aria-hidden="true"></span>
1029
+ <span class="ui-button__sr-only">Loading</span>
1030
+ </span>
1031
+ }
1032
+ </button>
1033
+ `, styles: [".ui-button-host{display:inline-flex}.ui-button-host--full-width{display:flex;width:100%}.ui-button{appearance:none;border:none;cursor:pointer;font-family:var(--ui-font-family);font-weight:var(--ui-font-weight-medium);letter-spacing:.01em;position:relative;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--ui-radius-md);white-space:nowrap;text-decoration:none;overflow:hidden;transition:background-color var(--ui-transition-fast),color var(--ui-transition-fast),border-color var(--ui-transition-fast),box-shadow var(--ui-transition-fast),opacity var(--ui-transition-fast)}.ui-button:focus{outline:none}.ui-button:focus-visible{outline:var(--ui-focus-ring-width) solid var(--ui-focus-ring-color);outline-offset:var(--ui-focus-ring-offset)}.ui-button:disabled{opacity:.5;cursor:not-allowed}.ui-button--full-width{width:100%}.ui-button--xs{height:28px;padding:0 var(--ui-spacing-2);font-size:var(--ui-font-size-xs);border-radius:var(--ui-radius-sm);gap:var(--ui-spacing-1)}.ui-button--sm{height:32px;padding:0 var(--ui-spacing-3);font-size:var(--ui-font-size-xs);gap:6px}.ui-button--md{height:36px;padding:0 var(--ui-spacing-4);font-size:var(--ui-font-size-sm);gap:var(--ui-spacing-2)}.ui-button--lg{height:40px;padding:0 var(--ui-spacing-5);font-size:var(--ui-font-size-sm);gap:var(--ui-spacing-2)}.ui-button--xl{height:48px;padding:0 var(--ui-spacing-6);font-size:var(--ui-font-size-md);gap:var(--ui-spacing-3)}.ui-button--icon-only.ui-button--xs{width:28px;padding:0}.ui-button--icon-only.ui-button--sm{width:32px;padding:0}.ui-button--icon-only.ui-button--md{width:36px;padding:0}.ui-button--icon-only.ui-button--lg{width:40px;padding:0}.ui-button--icon-only.ui-button--xl{width:48px;padding:0}.ui-button--primary{background:var(--ui-color-primary);color:var(--ui-color-primary-contrast)}.ui-button--primary:hover:not(:disabled){background:var(--ui-color-primary-hover)}.ui-button--primary:active:not(:disabled){background:var(--ui-color-primary-active)}.ui-button--accent{background:var(--ui-color-accent);color:var(--ui-color-accent-contrast)}.ui-button--accent:hover:not(:disabled){background:var(--ui-color-accent-hover)}.ui-button--accent:active:not(:disabled){background:var(--ui-color-accent-active)}.ui-button--warn{background:var(--ui-color-warn);color:var(--ui-color-warn-contrast)}.ui-button--warn:hover:not(:disabled){background:var(--ui-color-warn-hover)}.ui-button--warn:active:not(:disabled){background:var(--ui-color-warn-active)}.ui-button--neutral{background:var(--ui-color-neutral-200);color:var(--ui-color-text)}.ui-button--neutral:hover:not(:disabled){background:var(--ui-color-neutral-300)}.ui-button--neutral:active:not(:disabled){background:var(--ui-color-neutral-400);color:var(--ui-color-text-inverse)}.ui-button--ghost{background:transparent;color:var(--ui-color-text-secondary)}.ui-button--ghost:hover:not(:disabled){background:var(--ui-color-surface-hover);color:var(--ui-color-text)}.ui-button--ghost:active:not(:disabled){background:var(--ui-color-neutral-200)}.ui-button--outline{background:transparent;color:var(--ui-color-primary);border:1.5px solid var(--ui-color-border-strong)}.ui-button--outline:hover:not(:disabled){background:var(--ui-color-primary-light);border-color:var(--ui-color-primary)}.ui-button--outline:active:not(:disabled){background:var(--ui-color-primary-light);border-color:var(--ui-color-primary-hover)}.ui-button--loading{cursor:wait;pointer-events:none}.ui-button__content{display:inline-flex;align-items:center;gap:inherit;transition:visibility var(--ui-transition-fast)}.ui-button__content--hidden{visibility:hidden}.ui-button__label{line-height:1;background-color:transparent!important}.ui-button__spinner-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.ui-button__spinner{width:1.1em;height:1.1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:ui-button-spin .6s linear infinite}@keyframes ui-button-spin{to{transform:rotate(360deg)}}.ui-button__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
1034
+ }], propDecorators: { label: [{
1035
+ type: Input
1036
+ }], tooltip: [{
1037
+ type: Input
1038
+ }], variant: [{
1039
+ type: Input
1040
+ }], size: [{
1041
+ type: Input
1042
+ }], icon: [{
1043
+ type: Input
1044
+ }], iconPosition: [{
1045
+ type: Input
1046
+ }], loading: [{
1047
+ type: Input
1048
+ }], disabled: [{
1049
+ type: Input
1050
+ }], fullWidth: [{
1051
+ type: Input
1052
+ }], type: [{
1053
+ type: Input
1054
+ }], ariaLabel: [{
1055
+ type: Input
1056
+ }], customClass: [{
1057
+ type: Input
1058
+ }] } });
1059
+
1060
+ /** @internal Mappatura variante -> icona Lucide di default. */
1061
+ const VARIANT_ICON_MAP = {
1062
+ confirm: 'check-circle',
1063
+ delete: 'trash-2',
1064
+ warn: 'alert-triangle',
1065
+ info: 'info',
1066
+ };
1067
+ /** @internal Mappatura variante -> colore pulsante di conferma. */
1068
+ const VARIANT_BUTTON_MAP = {
1069
+ confirm: 'primary',
1070
+ delete: 'warn',
1071
+ warn: 'warn',
1072
+ info: 'primary',
1073
+ };
1074
+ /**
1075
+ * Dialogo di conferma leggero per azioni condizionali.
1076
+ *
1077
+ * Non va utilizzato direttamente nel template: viene aperto
1078
+ * programmaticamente tramite `UiModalService.confirm()` e i suoi
1079
+ * metodi scorciatoia (`confirmDelete`, `confirmDiscard`, `confirmSave`).
1080
+ *
1081
+ * Supporta quattro varianti semantiche — confirm, delete, warn, info —
1082
+ * ciascuna con icona e colore preconfigurati.
1083
+ *
1084
+ * @selector ui-confirm-dialog
1085
+ *
1086
+ * @example
1087
+ * ```typescript
1088
+ * // Apertura tramite servizio
1089
+ * this.modalService.confirm({
1090
+ * title: 'Conferma eliminazione',
1091
+ * message: 'Questa azione non puo essere annullata.',
1092
+ * variant: 'delete',
1093
+ * }).subscribe(confirmed => {
1094
+ * if (confirmed) this.deleteItem();
1095
+ * });
1096
+ * ```
1097
+ */
1098
+ class UiConfirmDialogComponent {
1099
+ constructor() {
1100
+ /** @internal Dati iniettati dal servizio modale. */
1101
+ this.data = inject(MAT_DIALOG_DATA);
1102
+ /** @internal Riferimento al dialogo per la chiusura. */
1103
+ this.dialogRef = inject(MatDialogRef);
1104
+ }
1105
+ /** Variante semantica corrente. */
1106
+ get variant() {
1107
+ return this.data.variant || 'confirm';
1108
+ }
1109
+ /** Nome icona Lucide da visualizzare. */
1110
+ get iconName() {
1111
+ return this.data.icon || VARIANT_ICON_MAP[this.variant];
1112
+ }
1113
+ /** Variante del pulsante di conferma. */
1114
+ get confirmButtonVariant() {
1115
+ return VARIANT_BUTTON_MAP[this.variant];
1116
+ }
1117
+ /** Classi CSS per il contenitore icona. */
1118
+ get iconClasses() {
1119
+ return `ui-confirm-dialog__icon ui-confirm-dialog__icon--${this.variant}`;
1120
+ }
1121
+ /** Conferma e chiude il dialogo con `true`. */
1122
+ confirm() {
1123
+ this.dialogRef.close(true);
1124
+ }
1125
+ /** Annulla e chiude il dialogo con `false`. */
1126
+ cancel() {
1127
+ this.dialogRef.close(false);
1128
+ }
1129
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiConfirmDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1130
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: UiConfirmDialogComponent, isStandalone: true, selector: "ui-confirm-dialog", host: { attributes: { "role": "alertdialog", "aria-modal": "true" }, properties: { "attr.aria-labelledby": "\"ui-confirm-title\"", "attr.aria-describedby": "\"ui-confirm-message\"" }, classAttribute: "ui-confirm-dialog-host" }, ngImport: i0, template: `
1131
+ <div class="ui-confirm-dialog">
1132
+ <!-- Icona -->
1133
+ <div [class]="iconClasses">
1134
+ <lucide-icon [name]="iconName" [size]="32" aria-hidden="true" />
1135
+ </div>
1136
+
1137
+ <!-- Contenuto -->
1138
+ <div class="ui-confirm-dialog__content">
1139
+ @if (data.title) {
1140
+ <h3 class="ui-confirm-dialog__title" id="ui-confirm-title">{{ data.title }}</h3>
1141
+ }
1142
+ <p class="ui-confirm-dialog__message" id="ui-confirm-message">{{ data.message }}</p>
1143
+ </div>
1144
+
1145
+ <!-- Azioni -->
1146
+ <div class="ui-confirm-dialog__actions">
1147
+ <ui-button
1148
+ [label]="data.cancelText || 'Annulla'"
1149
+ variant="ghost"
1150
+ size="md"
1151
+ (click)="cancel()"
1152
+ />
1153
+ <ui-button
1154
+ [label]="data.confirmText || 'Conferma'"
1155
+ [variant]="confirmButtonVariant"
1156
+ size="md"
1157
+ (click)="confirm()"
1158
+ />
1159
+ </div>
1160
+ </div>
1161
+ `, isInline: true, styles: [".ui-confirm-dialog-host{display:block}.ui-confirm-dialog{display:flex;flex-direction:column;align-items:center;text-align:center;padding:var(--ui-spacing-8) var(--ui-spacing-6);background:var(--ui-color-surface);border-radius:var(--ui-radius-lg);box-shadow:var(--ui-shadow-lg)}.ui-confirm-dialog__icon{display:flex;align-items:center;justify-content:center;width:56px;height:56px;border-radius:var(--ui-radius-full);margin-bottom:var(--ui-spacing-4)}.ui-confirm-dialog__icon--confirm{background:var(--ui-color-primary-light);color:var(--ui-color-primary)}.ui-confirm-dialog__icon--delete{background:var(--ui-color-warn-light);color:var(--ui-color-warn)}.ui-confirm-dialog__icon--warn{background:var(--ui-color-warning-light);color:var(--ui-color-warning)}.ui-confirm-dialog__icon--info{background:var(--ui-color-info-light);color:var(--ui-color-info)}.ui-confirm-dialog__content{margin-bottom:var(--ui-spacing-6)}.ui-confirm-dialog__title{margin:0 0 var(--ui-spacing-2);font-size:var(--ui-font-size-lg);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);line-height:var(--ui-line-height-tight)}.ui-confirm-dialog__message{margin:0;font-size:var(--ui-font-size-sm);color:var(--ui-color-text-secondary);line-height:var(--ui-line-height-normal);max-width:340px}.ui-confirm-dialog__actions{display:flex;gap:var(--ui-spacing-2);width:100%;justify-content:center}.ui-confirm-panel .mat-mdc-dialog-container{--mdc-dialog-container-color: transparent;--mdc-dialog-container-shape: var(--ui-radius-lg)}.ui-confirm-panel .mat-mdc-dialog-container .mdc-dialog__surface{background:transparent!important;box-shadow:none!important;border-radius:var(--ui-radius-lg)!important;overflow:visible}@media (max-width: 480px){.ui-confirm-dialog{padding:var(--ui-spacing-6) var(--ui-spacing-4)}.ui-confirm-dialog__actions{flex-direction:column-reverse;gap:var(--ui-spacing-2)}.ui-confirm-dialog__actions .ui-button-host{width:100%}}.ui-modal-backdrop{background:var(--ui-color-backdrop)}.ui-confirm-backdrop--stacked{background:#0000004d}\n"], dependencies: [{ kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["label", "tooltip", "variant", "size", "icon", "iconPosition", "loading", "disabled", "fullWidth", "type", "ariaLabel", "customClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1162
+ }
1163
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiConfirmDialogComponent, decorators: [{
1164
+ type: Component,
1165
+ args: [{ selector: 'ui-confirm-dialog', standalone: true, imports: [LucideAngularModule, UiButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
1166
+ class: 'ui-confirm-dialog-host',
1167
+ role: 'alertdialog',
1168
+ 'aria-modal': 'true',
1169
+ '[attr.aria-labelledby]': '"ui-confirm-title"',
1170
+ '[attr.aria-describedby]': '"ui-confirm-message"',
1171
+ }, template: `
1172
+ <div class="ui-confirm-dialog">
1173
+ <!-- Icona -->
1174
+ <div [class]="iconClasses">
1175
+ <lucide-icon [name]="iconName" [size]="32" aria-hidden="true" />
1176
+ </div>
1177
+
1178
+ <!-- Contenuto -->
1179
+ <div class="ui-confirm-dialog__content">
1180
+ @if (data.title) {
1181
+ <h3 class="ui-confirm-dialog__title" id="ui-confirm-title">{{ data.title }}</h3>
1182
+ }
1183
+ <p class="ui-confirm-dialog__message" id="ui-confirm-message">{{ data.message }}</p>
1184
+ </div>
1185
+
1186
+ <!-- Azioni -->
1187
+ <div class="ui-confirm-dialog__actions">
1188
+ <ui-button
1189
+ [label]="data.cancelText || 'Annulla'"
1190
+ variant="ghost"
1191
+ size="md"
1192
+ (click)="cancel()"
1193
+ />
1194
+ <ui-button
1195
+ [label]="data.confirmText || 'Conferma'"
1196
+ [variant]="confirmButtonVariant"
1197
+ size="md"
1198
+ (click)="confirm()"
1199
+ />
1200
+ </div>
1201
+ </div>
1202
+ `, styles: [".ui-confirm-dialog-host{display:block}.ui-confirm-dialog{display:flex;flex-direction:column;align-items:center;text-align:center;padding:var(--ui-spacing-8) var(--ui-spacing-6);background:var(--ui-color-surface);border-radius:var(--ui-radius-lg);box-shadow:var(--ui-shadow-lg)}.ui-confirm-dialog__icon{display:flex;align-items:center;justify-content:center;width:56px;height:56px;border-radius:var(--ui-radius-full);margin-bottom:var(--ui-spacing-4)}.ui-confirm-dialog__icon--confirm{background:var(--ui-color-primary-light);color:var(--ui-color-primary)}.ui-confirm-dialog__icon--delete{background:var(--ui-color-warn-light);color:var(--ui-color-warn)}.ui-confirm-dialog__icon--warn{background:var(--ui-color-warning-light);color:var(--ui-color-warning)}.ui-confirm-dialog__icon--info{background:var(--ui-color-info-light);color:var(--ui-color-info)}.ui-confirm-dialog__content{margin-bottom:var(--ui-spacing-6)}.ui-confirm-dialog__title{margin:0 0 var(--ui-spacing-2);font-size:var(--ui-font-size-lg);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);line-height:var(--ui-line-height-tight)}.ui-confirm-dialog__message{margin:0;font-size:var(--ui-font-size-sm);color:var(--ui-color-text-secondary);line-height:var(--ui-line-height-normal);max-width:340px}.ui-confirm-dialog__actions{display:flex;gap:var(--ui-spacing-2);width:100%;justify-content:center}.ui-confirm-panel .mat-mdc-dialog-container{--mdc-dialog-container-color: transparent;--mdc-dialog-container-shape: var(--ui-radius-lg)}.ui-confirm-panel .mat-mdc-dialog-container .mdc-dialog__surface{background:transparent!important;box-shadow:none!important;border-radius:var(--ui-radius-lg)!important;overflow:visible}@media (max-width: 480px){.ui-confirm-dialog{padding:var(--ui-spacing-6) var(--ui-spacing-4)}.ui-confirm-dialog__actions{flex-direction:column-reverse;gap:var(--ui-spacing-2)}.ui-confirm-dialog__actions .ui-button-host{width:100%}}.ui-modal-backdrop{background:var(--ui-color-backdrop)}.ui-confirm-backdrop--stacked{background:#0000004d}\n"] }]
1203
+ }] });
1204
+
1205
+ /** @internal Mappatura dimensione -> larghezza CSS per il pannello overlay. */
1206
+ const SIZE_WIDTH_MAP = {
1207
+ sm: '400px',
1208
+ md: '560px',
1209
+ lg: '720px',
1210
+ xl: '900px',
1211
+ fullscreen: '95vw',
1212
+ };
1213
+ /**
1214
+ * Servizio per aprire modali e dialoghi di conferma.
1215
+ *
1216
+ * Gestisce lo stacking intelligente: una modale regolare puo
1217
+ * ospitare al massimo un dialogo di conferma sopra di se.
1218
+ * Non e possibile stackare due modali regolari.
1219
+ *
1220
+ * @usageNotes
1221
+ *
1222
+ * ### Aprire una modale personalizzata
1223
+ * ```typescript
1224
+ * const ref = this.modalService.open(MyDialogComponent, {
1225
+ * size: 'lg',
1226
+ * data: { userId: 123 },
1227
+ * });
1228
+ *
1229
+ * ref.afterClosed().subscribe(result => {
1230
+ * console.log('Risultato:', result);
1231
+ * });
1232
+ * ```
1233
+ *
1234
+ * ### Conferma rapida
1235
+ * ```typescript
1236
+ * this.modalService.confirm('Confermi questa azione?')
1237
+ * .subscribe(ok => { if (ok) this.procedi(); });
1238
+ * ```
1239
+ *
1240
+ * ### Preset di conferma
1241
+ * ```typescript
1242
+ * this.modalService.confirmDelete('Progetto Alpha')
1243
+ * .subscribe(ok => { if (ok) this.elimina(); });
1244
+ * ```
1245
+ */
1246
+ class UiModalService {
1247
+ constructor() {
1248
+ /** @internal Istanza MatDialog per la gestione overlay. */
1249
+ this.dialog = inject(MatDialog);
1250
+ /** @internal Stack delle modali regolari aperte. */
1251
+ this._modalStack = [];
1252
+ /** @internal Traccia se un dialogo di conferma e attualmente aperto. */
1253
+ this._confirmOpen = false;
1254
+ }
1255
+ /**
1256
+ * Apre una modale con un componente personalizzato.
1257
+ *
1258
+ * Il componente aperto puo iniettare `MAT_DIALOG_DATA` per accedere
1259
+ * ai dati e `MatDialogRef` per chiudere la modale.
1260
+ *
1261
+ * @param component - Componente standalone da renderizzare nella modale
1262
+ * @param config - Configurazione (dimensione, dati, comportamento)
1263
+ * @returns Riferimento alla modale aperta
1264
+ */
1265
+ open(component, config) {
1266
+ // Regola di stacking: non stackare due modali regolari
1267
+ if (this._modalStack.length > 0) {
1268
+ console.warn('[UiModalService] Una modale e gia aperta. ' +
1269
+ 'Usa confirm() per aprire un dialogo di conferma sopra la modale corrente.');
1270
+ }
1271
+ const size = config?.size || 'md';
1272
+ const dialogConfig = {
1273
+ width: SIZE_WIDTH_MAP[size],
1274
+ maxWidth: size === 'fullscreen' ? '95vw' : '90vw',
1275
+ maxHeight: size === 'fullscreen' ? '95vh' : '90vh',
1276
+ height: size === 'fullscreen' ? '95vh' : undefined,
1277
+ disableClose: config?.disableClose ?? false,
1278
+ autoFocus: true,
1279
+ restoreFocus: true,
1280
+ data: config?.data,
1281
+ panelClass: ['ui-modal-panel', `ui-modal-panel--${size}`, ...(config?.panelClass ? [config.panelClass] : [])],
1282
+ hasBackdrop: true,
1283
+ backdropClass: ['cdk-overlay-dark-backdrop', 'ui-modal-backdrop'],
1284
+ };
1285
+ const ref = this.dialog.open(component, dialogConfig);
1286
+ this._modalStack.push(ref);
1287
+ ref.afterClosed().subscribe(() => {
1288
+ this._modalStack = this._modalStack.filter((r) => r !== ref);
1289
+ });
1290
+ return ref;
1291
+ }
1292
+ /**
1293
+ * Apre un dialogo di conferma.
1294
+ *
1295
+ * Accetta una stringa semplice (messaggio) o un oggetto di
1296
+ * configurazione completo. Puo essere aperto sopra una modale
1297
+ * esistente (stacking consentito).
1298
+ *
1299
+ * @param messageOrConfig - Messaggio o configurazione completa
1300
+ * @returns Observable che emette `true` se confermato, `false` se annullato
1301
+ */
1302
+ confirm(messageOrConfig) {
1303
+ const data = typeof messageOrConfig === 'string' ? { message: messageOrConfig } : messageOrConfig;
1304
+ // Un solo dialogo di conferma alla volta
1305
+ if (this._confirmOpen) {
1306
+ console.warn('[UiModalService] Un dialogo di conferma e gia aperto.');
1307
+ return new Observable((subscriber) => {
1308
+ subscriber.next(false);
1309
+ subscriber.complete();
1310
+ });
1311
+ }
1312
+ this._confirmOpen = true;
1313
+ const dialogConfig = {
1314
+ width: '420px',
1315
+ maxWidth: '90vw',
1316
+ disableClose: false,
1317
+ autoFocus: false,
1318
+ restoreFocus: true,
1319
+ data,
1320
+ panelClass: ['ui-confirm-panel'],
1321
+ hasBackdrop: true,
1322
+ backdropClass: this._modalStack.length > 0
1323
+ ? ['cdk-overlay-dark-backdrop', 'ui-confirm-backdrop--stacked']
1324
+ : ['cdk-overlay-dark-backdrop', 'ui-modal-backdrop'],
1325
+ };
1326
+ const ref = this.dialog.open(UiConfirmDialogComponent, dialogConfig);
1327
+ return ref.afterClosed().pipe(map((result) => {
1328
+ this._confirmOpen = false;
1329
+ return !!result;
1330
+ }));
1331
+ }
1332
+ /**
1333
+ * Scorciatoia: conferma eliminazione.
1334
+ * Variante `delete` con icona cestino e pulsante warn.
1335
+ *
1336
+ * @param itemName - Nome opzionale dell'elemento da eliminare
1337
+ */
1338
+ confirmDelete(itemName) {
1339
+ return this.confirm({
1340
+ title: 'Conferma eliminazione',
1341
+ message: itemName
1342
+ ? `Sei sicuro di voler eliminare "${itemName}"? Questa azione non puo essere annullata.`
1343
+ : 'Sei sicuro di voler eliminare questo elemento? Questa azione non puo essere annullata.',
1344
+ confirmText: 'Elimina',
1345
+ cancelText: 'Annulla',
1346
+ variant: 'delete',
1347
+ });
1348
+ }
1349
+ /**
1350
+ * Scorciatoia: conferma abbandono modifiche.
1351
+ * Variante `warn` con icona avviso.
1352
+ */
1353
+ confirmDiscard() {
1354
+ return this.confirm({
1355
+ title: 'Modifiche non salvate',
1356
+ message: 'Ci sono modifiche non salvate. Sei sicuro di voler uscire senza salvare?',
1357
+ confirmText: 'Esci senza salvare',
1358
+ cancelText: 'Rimani',
1359
+ variant: 'warn',
1360
+ });
1361
+ }
1362
+ /**
1363
+ * Scorciatoia: conferma salvataggio.
1364
+ * Variante `confirm` con icona check.
1365
+ */
1366
+ confirmSave() {
1367
+ return this.confirm({
1368
+ title: 'Conferma salvataggio',
1369
+ message: 'Sei sicuro di voler salvare le modifiche?',
1370
+ confirmText: 'Salva',
1371
+ cancelText: 'Annulla',
1372
+ variant: 'confirm',
1373
+ });
1374
+ }
1375
+ /** Chiude tutte le modali e dialoghi aperti. */
1376
+ closeAll() {
1377
+ this.dialog.closeAll();
1378
+ this._modalStack = [];
1379
+ this._confirmOpen = false;
1380
+ }
1381
+ /** Indica se ci sono modali o dialoghi attualmente aperti. */
1382
+ get hasOpenDialogs() {
1383
+ return this.dialog.openDialogs.length > 0;
1384
+ }
1385
+ /** Numero di modali regolari attualmente aperte. */
1386
+ get openModalCount() {
1387
+ return this._modalStack.length;
1388
+ }
1389
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiModalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1390
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiModalService, providedIn: 'root' }); }
1391
+ }
1392
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiModalService, decorators: [{
1393
+ type: Injectable,
1394
+ args: [{ providedIn: 'root' }]
1395
+ }] });
1396
+
1397
+ /**
1398
+ * Standalone button-group component driven by a `UiButtonDescriptor[]` configuration.
1399
+ *
1400
+ * Renders a group of `UiButton` instances from declarative descriptors.
1401
+ * Supports per-button loading/disabled states, dynamic visibility,
1402
+ * alignment control, and optional navigation via Angular Router.
1403
+ *
1404
+ * When the `UiBlackboxService` is provided, button clicks are
1405
+ * automatically tracked in the active session.
1406
+ *
1407
+ * @selector ui-button-area
1408
+ *
1409
+ * @example
1410
+ * ```html
1411
+ * <ui-button-area
1412
+ * [buttons]="actions"
1413
+ * align="end"
1414
+ * gap="sm"
1415
+ * [loadingIds]="currentlyLoading"
1416
+ * />
1417
+ * ```
1418
+ *
1419
+ * @example
1420
+ * ```typescript
1421
+ * // Dynamic configuration
1422
+ * actions: UiButtonDescriptor[] = [
1423
+ * { id: 'export', label: 'Export Excel', icon: 'file-spreadsheet', variant: 'primary',
1424
+ * action: () => this.exportExcel() },
1425
+ * { id: 'delete', label: 'Delete', icon: 'trash-2', variant: 'warn',
1426
+ * action: () => this.deleteSelected(), hidden: !this.hasSelection },
1427
+ * ];
1428
+ *
1429
+ * // Set loading state from outside:
1430
+ * currentlyLoading: string | string[] | null = 'export';
1431
+ * ```
1432
+ */
1433
+ class UiButtonAreaComponent {
1434
+ constructor() {
1435
+ /**
1436
+ * Array of button descriptors to render.
1437
+ * Buttons with `hidden: true` are filtered out.
1438
+ */
1439
+ this.buttons = [];
1440
+ /** Horizontal alignment of the button group. */
1441
+ this.align = 'end';
1442
+ /** Accessible group label for screen readers. */
1443
+ this.ariaLabel = 'Actions';
1444
+ /** Gap between buttons (maps to design token spacing). */
1445
+ this.gap = 'sm';
1446
+ /** Stack buttons vertically on mobile viewports (<600px). */
1447
+ this.stackOnMobile = true;
1448
+ /**
1449
+ * When `true`, all buttons are disabled while any button is in a loading state.
1450
+ * Useful to prevent double-actions during async operations.
1451
+ */
1452
+ this.disableWhileLoading = false;
1453
+ /** @internal */
1454
+ this._loadingIds = new Set();
1455
+ /** @internal Optional router for href navigation. */
1456
+ this.router = inject(Router, { optional: true });
1457
+ /** @internal Optional BlackBox service — auto-tracks button clicks when available. */
1458
+ this.blackbox = inject(UiBlackboxService, { optional: true });
1459
+ }
1460
+ /**
1461
+ * Set one or more buttons to a loading state by their `id`.
1462
+ * Accepts a single string, an array of strings, or `null` to clear.
1463
+ *
1464
+ * @example
1465
+ * ```html
1466
+ * <!-- Single -->
1467
+ * <ui-button-area [loadingIds]="'save'" />
1468
+ *
1469
+ * <!-- Multiple -->
1470
+ * <ui-button-area [loadingIds]="['save', 'export']" />
1471
+ * ```
1472
+ */
1473
+ set loadingIds(value) {
1474
+ if (value === null || value === undefined) {
1475
+ this._loadingIds = new Set();
1476
+ }
1477
+ else if (Array.isArray(value)) {
1478
+ this._loadingIds = new Set(value);
1479
+ }
1480
+ else {
1481
+ this._loadingIds = new Set([value]);
1482
+ }
1483
+ }
1484
+ /** Buttons filtered to visible (non-hidden) entries. */
1485
+ get visibleButtons() {
1486
+ return this.buttons.filter((b) => !b.hidden);
1487
+ }
1488
+ /** Assembled CSS class string for the button area container. */
1489
+ get areaClasses() {
1490
+ return [
1491
+ 'ui-button-area',
1492
+ `ui-button-area--align-${this.align}`,
1493
+ `ui-button-area--gap-${this.gap}`,
1494
+ this.stackOnMobile ? 'ui-button-area--stack-mobile' : '',
1495
+ ]
1496
+ .filter(Boolean)
1497
+ .join(' ');
1498
+ }
1499
+ /** @internal Track function for @for loop. */
1500
+ trackButton(index, button) {
1501
+ return button.id ?? `idx-${index}`;
1502
+ }
1503
+ /** Whether a specific button is in a loading state. */
1504
+ isButtonLoading(button) {
1505
+ return !!button.loading || (!!button.id && this._loadingIds.has(button.id));
1506
+ }
1507
+ /** Whether a specific button is disabled (includes loading logic). */
1508
+ isButtonDisabled(button) {
1509
+ if (button.disabled)
1510
+ return true;
1511
+ if (this.isButtonLoading(button))
1512
+ return true;
1513
+ if (this.disableWhileLoading && this._loadingIds.size > 0)
1514
+ return true;
1515
+ return false;
1516
+ }
1517
+ /** @internal Handles button click: tracks action, calls callback and/or navigates. */
1518
+ handleButtonClick(button, event) {
1519
+ // BlackBox auto-tracking (no-op if service is not provided)
1520
+ this.blackbox?.trackAction(button.id ?? button.label, {
1521
+ tag: 'ui-button',
1522
+ id: button.id,
1523
+ text: button.label,
1524
+ });
1525
+ if (button.action) {
1526
+ button.action(event);
1527
+ }
1528
+ if (button.href && this.router) {
1529
+ this.router.navigateByUrl(button.href);
1530
+ }
1531
+ }
1532
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiButtonAreaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1533
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: UiButtonAreaComponent, isStandalone: true, selector: "ui-button-area", inputs: { buttons: "buttons", align: "align", ariaLabel: "ariaLabel", gap: "gap", stackOnMobile: "stackOnMobile", disableWhileLoading: "disableWhileLoading", loadingIds: "loadingIds" }, host: { classAttribute: "ui-button-area-host" }, ngImport: i0, template: `
1534
+ <div
1535
+ [class]="areaClasses"
1536
+ role="group"
1537
+ [attr.aria-label]="ariaLabel"
1538
+ >
1539
+ @for (button of visibleButtons; track trackButton($index, button)) {
1540
+ <ui-button
1541
+ [attr.data-button-id]="button.id || null"
1542
+ [label]="button.label"
1543
+ [tooltip]="button.tooltip ?? ''"
1544
+ [ariaLabel]="button.ariaLabel"
1545
+ [icon]="button.icon"
1546
+ [iconPosition]="button.iconPosition ?? 'trailing'"
1547
+ [variant]="button.variant ?? 'primary'"
1548
+ [size]="button.size ?? 'md'"
1549
+ [loading]="isButtonLoading(button)"
1550
+ [disabled]="isButtonDisabled(button)"
1551
+ [customClass]="button.customClass"
1552
+ (click)="handleButtonClick(button, $event)"
1553
+ />
1554
+ }
1555
+ </div>
1556
+ `, isInline: true, styles: [".ui-button-area-host{display:block}@media (max-width: 767.98px){.ui-button-area-host{width:100%}}.ui-button-area{display:flex;flex-wrap:wrap;align-items:center}.ui-button-area--gap-xs{gap:var(--ui-spacing-1)}.ui-button-area--gap-sm{gap:var(--ui-spacing-2)}.ui-button-area--gap-md{gap:var(--ui-spacing-3)}.ui-button-area--gap-lg{gap:var(--ui-spacing-4)}.ui-button-area--gap-xl{gap:var(--ui-spacing-5)}.ui-button-area--align-start{justify-content:flex-start}.ui-button-area--align-center{justify-content:center}.ui-button-area--align-end{justify-content:flex-end}.ui-button-area--align-between{justify-content:space-between}@media (max-width: 767.98px){.ui-button-area--stack-mobile{flex-direction:column;width:100%}.ui-button-area--stack-mobile .ui-button-host{display:flex;width:100%}.ui-button-area--stack-mobile .ui-button{width:100%}}\n"], dependencies: [{ kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["label", "tooltip", "variant", "size", "icon", "iconPosition", "loading", "disabled", "fullWidth", "type", "ariaLabel", "customClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1557
+ }
1558
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiButtonAreaComponent, decorators: [{
1559
+ type: Component,
1560
+ args: [{ selector: 'ui-button-area', standalone: true, imports: [UiButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
1561
+ class: 'ui-button-area-host',
1562
+ }, template: `
1563
+ <div
1564
+ [class]="areaClasses"
1565
+ role="group"
1566
+ [attr.aria-label]="ariaLabel"
1567
+ >
1568
+ @for (button of visibleButtons; track trackButton($index, button)) {
1569
+ <ui-button
1570
+ [attr.data-button-id]="button.id || null"
1571
+ [label]="button.label"
1572
+ [tooltip]="button.tooltip ?? ''"
1573
+ [ariaLabel]="button.ariaLabel"
1574
+ [icon]="button.icon"
1575
+ [iconPosition]="button.iconPosition ?? 'trailing'"
1576
+ [variant]="button.variant ?? 'primary'"
1577
+ [size]="button.size ?? 'md'"
1578
+ [loading]="isButtonLoading(button)"
1579
+ [disabled]="isButtonDisabled(button)"
1580
+ [customClass]="button.customClass"
1581
+ (click)="handleButtonClick(button, $event)"
1582
+ />
1583
+ }
1584
+ </div>
1585
+ `, styles: [".ui-button-area-host{display:block}@media (max-width: 767.98px){.ui-button-area-host{width:100%}}.ui-button-area{display:flex;flex-wrap:wrap;align-items:center}.ui-button-area--gap-xs{gap:var(--ui-spacing-1)}.ui-button-area--gap-sm{gap:var(--ui-spacing-2)}.ui-button-area--gap-md{gap:var(--ui-spacing-3)}.ui-button-area--gap-lg{gap:var(--ui-spacing-4)}.ui-button-area--gap-xl{gap:var(--ui-spacing-5)}.ui-button-area--align-start{justify-content:flex-start}.ui-button-area--align-center{justify-content:center}.ui-button-area--align-end{justify-content:flex-end}.ui-button-area--align-between{justify-content:space-between}@media (max-width: 767.98px){.ui-button-area--stack-mobile{flex-direction:column;width:100%}.ui-button-area--stack-mobile .ui-button-host{display:flex;width:100%}.ui-button-area--stack-mobile .ui-button{width:100%}}\n"] }]
1586
+ }], propDecorators: { buttons: [{
1587
+ type: Input
1588
+ }], align: [{
1589
+ type: Input
1590
+ }], ariaLabel: [{
1591
+ type: Input
1592
+ }], gap: [{
1593
+ type: Input
1594
+ }], stackOnMobile: [{
1595
+ type: Input
1596
+ }], disableWhileLoading: [{
1597
+ type: Input
1598
+ }], loadingIds: [{
1599
+ type: Input
1600
+ }] } });
1601
+
1602
+ /**
1603
+ * ng-ui-system — Button entry point.
1604
+ *
1605
+ * @example
1606
+ * ```typescript
1607
+ * import {
1608
+ * UiButtonComponent,
1609
+ * UiButtonAreaComponent,
1610
+ * UiButtonDescriptor,
1611
+ * } from 'ng-ui-system';
1612
+ * ```
1613
+ */
1614
+ // Components
1615
+
1616
+ /**
1617
+ * @module ng-ui-system/blackbox
1618
+ * Componente modale per la visualizzazione di dati JSON nel debugger.
1619
+ *
1620
+ * Aperto da `UiBlackboxDebuggerComponent` tramite `UiModalService.open()`.
1621
+ * Mostra il JSON formattato in un code block con syntax highlighting.
1622
+ */
1623
+ class UiBlackboxJsonViewerComponent {
1624
+ constructor(dialogRef, data) {
1625
+ this.dialogRef = dialogRef;
1626
+ this.data = data;
1627
+ this.copied = false;
1628
+ this.formattedJson = JSON.stringify(data?.json ?? {}, null, 2);
1629
+ }
1630
+ copyToClipboard() {
1631
+ navigator.clipboard.writeText(this.formattedJson).then(() => {
1632
+ this.copied = true;
1633
+ setTimeout(() => { this.copied = false; }, 2000);
1634
+ });
1635
+ }
1636
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxJsonViewerComponent, deps: [{ token: i1$2.MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
1637
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: UiBlackboxJsonViewerComponent, isStandalone: true, selector: "ui-blackbox-json-viewer", ngImport: i0, template: `
1638
+ <ui-modal [config]="{ title: data.title || 'JSON', showCloseButton: true, hideFooter: true }">
1639
+ <div class="json-viewer">
1640
+ <div class="json-viewer__toolbar">
1641
+ <button class="json-viewer__copy" (click)="copyToClipboard()">
1642
+ <lucide-angular name="clipboard" [size]="12"></lucide-angular>
1643
+ {{ copied ? 'Copiato!' : 'Copia' }}
1644
+ </button>
1645
+ </div>
1646
+ <pre class="json-viewer__code"><code>{{ formattedJson }}</code></pre>
1647
+ </div>
1648
+ </ui-modal>
1649
+ `, isInline: true, styles: [".json-viewer{display:flex;flex-direction:column;gap:8px;min-height:120px}.json-viewer__toolbar{display:flex;justify-content:flex-end}.json-viewer__copy{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border-radius:var(--ui-radius-sm, 4px);font-size:11px;font-weight:600;cursor:pointer;background:var(--ui-color-bg-subtle, #f5f5f5);color:var(--ui-color-text-secondary, #666);border:1px solid var(--ui-color-border, #e5e5e5);transition:all .12s;&:hover{background:var(--ui-color-surface-hover, #eee);color:var(--ui-color-primary, #3b82f6);border-color:var(--ui-color-primary, #3b82f6)}}.json-viewer__code{margin:0;padding:16px;background:var(--ui-color-bg-subtle, #1e1e2e);border:1px solid var(--ui-color-border, #e5e5e5);border-radius:var(--ui-radius-md, 8px);overflow:auto;max-height:70vh;font-family:var(--ui-font-family-mono, \"Fira Code\", monospace);font-size:12px;line-height:1.6;color:var(--ui-color-text, #1a1a1a);white-space:pre-wrap;word-break:break-word;tab-size:2}\n"], dependencies: [{ kind: "component", type: UiModalComponent, selector: "ui-modal", inputs: ["config"], outputs: ["closed"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1650
+ }
1651
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxJsonViewerComponent, decorators: [{
1652
+ type: Component,
1653
+ args: [{ selector: 'ui-blackbox-json-viewer', standalone: true, imports: [UiModalComponent, LucideAngularModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
1654
+ <ui-modal [config]="{ title: data.title || 'JSON', showCloseButton: true, hideFooter: true }">
1655
+ <div class="json-viewer">
1656
+ <div class="json-viewer__toolbar">
1657
+ <button class="json-viewer__copy" (click)="copyToClipboard()">
1658
+ <lucide-angular name="clipboard" [size]="12"></lucide-angular>
1659
+ {{ copied ? 'Copiato!' : 'Copia' }}
1660
+ </button>
1661
+ </div>
1662
+ <pre class="json-viewer__code"><code>{{ formattedJson }}</code></pre>
1663
+ </div>
1664
+ </ui-modal>
1665
+ `, styles: [".json-viewer{display:flex;flex-direction:column;gap:8px;min-height:120px}.json-viewer__toolbar{display:flex;justify-content:flex-end}.json-viewer__copy{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border-radius:var(--ui-radius-sm, 4px);font-size:11px;font-weight:600;cursor:pointer;background:var(--ui-color-bg-subtle, #f5f5f5);color:var(--ui-color-text-secondary, #666);border:1px solid var(--ui-color-border, #e5e5e5);transition:all .12s;&:hover{background:var(--ui-color-surface-hover, #eee);color:var(--ui-color-primary, #3b82f6);border-color:var(--ui-color-primary, #3b82f6)}}.json-viewer__code{margin:0;padding:16px;background:var(--ui-color-bg-subtle, #1e1e2e);border:1px solid var(--ui-color-border, #e5e5e5);border-radius:var(--ui-radius-md, 8px);overflow:auto;max-height:70vh;font-family:var(--ui-font-family-mono, \"Fira Code\", monospace);font-size:12px;line-height:1.6;color:var(--ui-color-text, #1a1a1a);white-space:pre-wrap;word-break:break-word;tab-size:2}\n"] }]
1666
+ }], ctorParameters: () => [{ type: i1$2.MatDialogRef, decorators: [{
1667
+ type: Optional
1668
+ }] }, { type: undefined, decorators: [{
1669
+ type: Optional
1670
+ }, {
1671
+ type: Inject,
1672
+ args: [MAT_DIALOG_DATA]
1673
+ }] }] });
1674
+
1675
+ /**
1676
+ * @module ng-ui-system/blackbox
1677
+ * Componente debugger BlackBox — aperto in modale fullscreen.
1678
+ *
1679
+ * Layout:
1680
+ * - Header con titolo e chiusura
1681
+ * - Tab group: Dashboard | Timeline | Form Events | HTTP Calls
1682
+ *
1683
+ * Funzionalita:
1684
+ * - Timeline unificata (navigazioni + click + form events + HTTP calls)
1685
+ * - Form events con valori del form visibili
1686
+ * - JSON detail modal al click su dati strutturati
1687
+ */
1688
+ class UiBlackboxDebuggerComponent {
1689
+ constructor(dialogRef, dialogData) {
1690
+ this.dialogRef = dialogRef;
1691
+ this.dialogData = dialogData;
1692
+ this.blackbox = inject(UiBlackboxService);
1693
+ this.http = inject(HttpClient);
1694
+ this.modalService = inject(UiModalService);
1695
+ this.cdr = inject(ChangeDetectorRef);
1696
+ this.destroy$ = new Subject();
1697
+ /** Whether opened inside a dialog. */
1698
+ this.isDialogMode = false;
1699
+ this.selectedTab = 0;
1700
+ this.session = null;
1701
+ this.viewingSession = null;
1702
+ this.pastSessions = [];
1703
+ this.statsCards = [];
1704
+ /** Unified timeline entries sorted by timestamp. */
1705
+ this.timelineEntries = [];
1706
+ this.isDialogMode = !!this.dialogRef;
1707
+ }
1708
+ get sessionDuration() {
1709
+ if (!this.session)
1710
+ return '—';
1711
+ const ms = Date.now() - this.session.startedAt;
1712
+ const s = Math.floor(ms / 1000);
1713
+ const m = Math.floor(s / 60);
1714
+ const h = Math.floor(m / 60);
1715
+ if (h > 0)
1716
+ return `${h}h ${m % 60}m`;
1717
+ if (m > 0)
1718
+ return `${m}m ${s % 60}s`;
1719
+ return `${s}s`;
1720
+ }
1721
+ ngOnInit() {
1722
+ this.refresh();
1723
+ // Auto-refresh every 2s
1724
+ interval(2000)
1725
+ .pipe(takeUntil(this.destroy$))
1726
+ .subscribe(() => {
1727
+ this.refresh();
1728
+ this.cdr.markForCheck();
1729
+ });
1730
+ // Load imported sessions if provided
1731
+ if (this.dialogData?.importedSessions?.length) {
1732
+ this.pastSessions = this.dialogData.importedSessions;
1733
+ this.viewingSession = this.pastSessions[0];
1734
+ }
1735
+ }
1736
+ ngOnDestroy() {
1737
+ this.destroy$.next();
1738
+ this.destroy$.complete();
1739
+ }
1740
+ refresh() {
1741
+ this.session = this.blackbox.getCurrentSession();
1742
+ this.viewingSession = this.viewingSession ?? this.session;
1743
+ this.updateStats();
1744
+ this.buildTimeline();
1745
+ this.blackbox.getSessionHistory().then(sessions => {
1746
+ this.pastSessions = sessions.filter(s => s.sessionId !== this.session?.sessionId);
1747
+ this.cdr.markForCheck();
1748
+ });
1749
+ }
1750
+ updateStats() {
1751
+ const s = this.viewingSession;
1752
+ this.statsCards = [
1753
+ { icon: 'git-branch', value: s?.steps?.length ?? 0, label: 'Navigazioni' },
1754
+ { icon: 'mouse-pointer-click', value: s?.steps?.reduce((sum, st) => sum + st.actions.length, 0) ?? 0, label: 'Click' },
1755
+ { icon: 'file-text', value: s?.formEvents?.length ?? 0, label: 'Form Events' },
1756
+ { icon: 'external-link', value: s?.calls?.length ?? 0, label: 'HTTP Calls' },
1757
+ ];
1758
+ }
1759
+ /** Builds a unified timeline from all event sources. */
1760
+ buildTimeline() {
1761
+ const entries = [];
1762
+ const s = this.viewingSession;
1763
+ if (!s) {
1764
+ this.timelineEntries = [];
1765
+ return;
1766
+ }
1767
+ // Navigation steps + their child actions
1768
+ for (const step of s.steps) {
1769
+ entries.push({
1770
+ timestamp: step.timestamp,
1771
+ kind: 'navigation',
1772
+ route: step.route,
1773
+ previousRoute: step.previousRoute,
1774
+ actionsCount: step.actions.length,
1775
+ });
1776
+ for (const action of step.actions) {
1777
+ entries.push({
1778
+ timestamp: action.timestamp,
1779
+ kind: 'click',
1780
+ tag: action.target.tag,
1781
+ trackId: action.target.trackId,
1782
+ text: action.target.text,
1783
+ });
1784
+ }
1785
+ }
1786
+ // Form events
1787
+ for (const fe of s.formEvents) {
1788
+ entries.push({
1789
+ timestamp: fe.timestamp,
1790
+ kind: 'formEvent',
1791
+ route: fe.route,
1792
+ formId: fe.formId,
1793
+ fieldKey: fe.fieldKey,
1794
+ eventType: fe.eventType,
1795
+ metadata: fe.metadata,
1796
+ });
1797
+ }
1798
+ // HTTP calls
1799
+ for (const call of s.calls) {
1800
+ entries.push({
1801
+ timestamp: call.timestamp,
1802
+ kind: 'httpCall',
1803
+ method: call.method,
1804
+ url: call.url,
1805
+ status: call.status,
1806
+ durationMs: call.durationMs,
1807
+ requestPayload: call.requestPayload,
1808
+ responseHeaders: call.responseHeaders,
1809
+ });
1810
+ }
1811
+ entries.sort((a, b) => a.timestamp - b.timestamp);
1812
+ this.timelineEntries = entries;
1813
+ }
1814
+ viewSession(s) {
1815
+ this.viewingSession = s;
1816
+ this.updateStats();
1817
+ this.buildTimeline();
1818
+ this.selectedTab = 1; // switch to timeline
1819
+ this.cdr.markForCheck();
1820
+ }
1821
+ closeDialog() {
1822
+ this.dialogRef?.close();
1823
+ }
1824
+ // ─── JSON Modal ──────────────────────────────────────────────
1825
+ openJsonModal(title, data) {
1826
+ this.modalService.open(UiBlackboxJsonViewerComponent, {
1827
+ size: 'md',
1828
+ data: { title, json: data },
1829
+ });
1830
+ }
1831
+ // ─── HTTP Simulator ────────────────────────────────────────────
1832
+ simulateGet() {
1833
+ this.http.get('https://jsonplaceholder.typicode.com/todos/1', {
1834
+ headers: new HttpHeaders({
1835
+ 'X-Ui-Blackbox': 'true',
1836
+ 'X-Ui-Blackbox-Headers': 'content-type',
1837
+ }),
1838
+ }).subscribe({ next: () => this.refresh(), error: () => this.refresh() });
1839
+ }
1840
+ simulatePost() {
1841
+ this.http.post('https://jsonplaceholder.typicode.com/posts', {
1842
+ title: 'BlackBox Test',
1843
+ body: 'Payload fittizio dalla demo',
1844
+ userId: 42,
1845
+ }, {
1846
+ headers: new HttpHeaders({ 'X-Ui-Blackbox': 'true' }),
1847
+ }).subscribe({ next: () => this.refresh(), error: () => this.refresh() });
1848
+ }
1849
+ simulateFail() {
1850
+ this.http.get('https://jsonplaceholder.typicode.com/todos/99999999', {
1851
+ headers: new HttpHeaders({ 'X-Ui-Blackbox': 'true' }),
1852
+ }).subscribe({ next: () => this.refresh(), error: () => this.refresh() });
1853
+ }
1854
+ // ─── Export / Import / Clear ───────────────────────────────────
1855
+ async exportSessions() {
1856
+ const blob = await this.blackbox.exportSessions();
1857
+ const url = URL.createObjectURL(blob);
1858
+ const a = document.createElement('a');
1859
+ a.href = url;
1860
+ a.download = `blackbox-${new Date().toISOString().slice(0, 10)}.jsonl`;
1861
+ a.click();
1862
+ URL.revokeObjectURL(url);
1863
+ }
1864
+ importJsonl() {
1865
+ const input = document.createElement('input');
1866
+ input.type = 'file';
1867
+ input.accept = '.jsonl,.json';
1868
+ input.onchange = async (e) => {
1869
+ const file = e.target.files[0];
1870
+ if (!file)
1871
+ return;
1872
+ try {
1873
+ const text = await file.text();
1874
+ const lines = text.trim().split('\n').filter((l) => l.trim());
1875
+ const sessions = lines.map((l) => JSON.parse(l));
1876
+ this.pastSessions = [...sessions, ...this.pastSessions];
1877
+ if (sessions.length > 0) {
1878
+ this.viewingSession = sessions[0];
1879
+ this.updateStats();
1880
+ this.buildTimeline();
1881
+ this.selectedTab = 1;
1882
+ }
1883
+ this.cdr.markForCheck();
1884
+ }
1885
+ catch {
1886
+ console.error('[BlackBox Debugger] Errore parsing JSONL');
1887
+ }
1888
+ };
1889
+ input.click();
1890
+ }
1891
+ async clearHistory() {
1892
+ await this.blackbox.clearHistory();
1893
+ this.pastSessions = [];
1894
+ this.cdr.markForCheck();
1895
+ }
1896
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxDebuggerComponent, deps: [{ token: i1$2.MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
1897
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: UiBlackboxDebuggerComponent, isStandalone: true, selector: "ui-blackbox-debugger", ngImport: i0, template: `
1898
+ <div class="dbg-root" [class.dialog-mode]="isDialogMode">
1899
+
1900
+ <!-- Header -->
1901
+ @if (isDialogMode) {
1902
+ <div class="dbg-header">
1903
+ <div class="dbg-header__left">
1904
+ <lucide-angular name="activity" [size]="20" class="dbg-header__icon"></lucide-angular>
1905
+ <h2>BlackBox Debugger</h2>
1906
+ </div>
1907
+ <ui-button icon="x" variant="ghost" size="md" ariaLabel="Chiudi" tooltip="Chiudi" (click)="closeDialog()" />
1908
+ </div>
1909
+ }
1910
+
1911
+ <!-- Tabs -->
1912
+ <mat-tab-group
1913
+ [selectedIndex]="selectedTab"
1914
+ (selectedIndexChange)="selectedTab = $event"
1915
+ class="dbg-tabs"
1916
+ >
1917
+ <!-- TAB: Dashboard -->
1918
+ <mat-tab>
1919
+ <ng-template mat-tab-label>
1920
+ <lucide-angular name="monitor" [size]="14" style="margin-right:6px"></lucide-angular>
1921
+ Dashboard
1922
+ </ng-template>
1923
+
1924
+ <div class="dbg-tab-content dbg-dashboard">
1925
+ <!-- Stats -->
1926
+ <div class="dbg-stats">
1927
+ <div class="dbg-stat" *ngFor="let s of statsCards">
1928
+ <lucide-angular [name]="s.icon" [size]="18" class="dbg-stat__icon"></lucide-angular>
1929
+ <span class="dbg-stat__value">{{ s.value }}</span>
1930
+ <span class="dbg-stat__label">{{ s.label }}</span>
1931
+ </div>
1932
+ </div>
1933
+ <div class="dbg-dashboard-grid">
1934
+ <!-- Session info -->
1935
+ <div class="dbg-panel" *ngIf="session">
1936
+ <div class="dbg-panel__header">
1937
+ <lucide-angular name="info" [size]="14"></lucide-angular>
1938
+ Sessione corrente
1939
+ </div>
1940
+ <div class="dbg-kv">
1941
+ <span class="dbg-kv__k">Session ID</span>
1942
+ <code class="dbg-kv__v dbg-mono">{{ session.sessionId }}</code>
1943
+ <span class="dbg-kv__k">Fingerprint</span>
1944
+ <code class="dbg-kv__v dbg-mono">{{ session.fingerprint }}</code>
1945
+ <span class="dbg-kv__k">Inizio</span>
1946
+ <span class="dbg-kv__v">{{ session.startedAt | date:'dd/MM/yyyy HH:mm:ss' }}</span>
1947
+ <span class="dbg-kv__k">Durata</span>
1948
+ <span class="dbg-kv__v">{{ sessionDuration }}</span>
1949
+ </div>
1950
+ </div>
1951
+
1952
+ <!-- HTTP Simulator -->
1953
+ <div class="dbg-panel">
1954
+ <div class="dbg-panel__header">
1955
+ <lucide-angular name="zap" [size]="14"></lucide-angular>
1956
+ HTTP Simulator
1957
+ </div>
1958
+ <p class="dbg-panel__desc">Genera chiamate fittizie per testare l'interceptor BlackBox.</p>
1959
+ <div class="dbg-sim-btns">
1960
+ <button class="dbg-sim-btn dbg-sim-btn--get" (click)="simulateGet()">
1961
+ <lucide-angular name="play" [size]="12"></lucide-angular> GET /todos/1
1962
+ </button>
1963
+ <button class="dbg-sim-btn dbg-sim-btn--post" (click)="simulatePost()">
1964
+ <lucide-angular name="play" [size]="12"></lucide-angular> POST /posts
1965
+ </button>
1966
+ <button class="dbg-sim-btn dbg-sim-btn--fail" (click)="simulateFail()">
1967
+ <lucide-angular name="play" [size]="12"></lucide-angular> GET /404
1968
+ </button>
1969
+ </div>
1970
+ </div>
1971
+
1972
+ <!-- Actions -->
1973
+ <div class="dbg-panel">
1974
+ <div class="dbg-panel__header">
1975
+ <lucide-angular name="settings" [size]="14"></lucide-angular>
1976
+ Azioni
1977
+ </div>
1978
+ <div class="dbg-sim-btns">
1979
+ <button class="dbg-action-btn" (click)="refresh()">
1980
+ <lucide-angular name="refresh-cw" [size]="12"></lucide-angular> Aggiorna
1981
+ </button>
1982
+ <button class="dbg-action-btn" (click)="exportSessions()">
1983
+ <lucide-angular name="download" [size]="12"></lucide-angular> Esporta JSONL
1984
+ </button>
1985
+ <button class="dbg-action-btn" (click)="importJsonl()">
1986
+ <lucide-angular name="upload" [size]="12"></lucide-angular> Importa JSONL
1987
+ </button>
1988
+ <button class="dbg-action-btn dbg-action-btn--danger" (click)="clearHistory()">
1989
+ <lucide-angular name="trash-2" [size]="12"></lucide-angular> Svuota
1990
+ </button>
1991
+ </div>
1992
+ </div>
1993
+
1994
+ <!-- Past sessions -->
1995
+ <div class="dbg-panel" *ngIf="pastSessions.length > 0">
1996
+ <div class="dbg-panel__header">
1997
+ <lucide-angular name="clock" [size]="14"></lucide-angular>
1998
+ Sessioni archiviate ({{ pastSessions.length }})
1999
+ </div>
2000
+ <div class="dbg-session-list">
2001
+ <div class="dbg-session-row" *ngFor="let s of pastSessions" (click)="viewSession(s)">
2002
+ <code class="dbg-mono">{{ s.sessionId | slice:0:12 }}...</code>
2003
+ <span>{{ s.startedAt | date:'dd/MM HH:mm' }}</span>
2004
+ <span>{{ s.steps.length }} steps</span>
2005
+ <lucide-angular name="chevron-right" [size]="14" class="dbg-session-row__arrow"></lucide-angular>
2006
+ </div>
2007
+ </div>
2008
+ </div>
2009
+ </div>
2010
+
2011
+ </div>
2012
+ </mat-tab>
2013
+
2014
+ <!-- TAB: Timeline (unified) -->
2015
+ <mat-tab>
2016
+ <ng-template mat-tab-label>
2017
+ <lucide-angular name="git-branch" [size]="14" style="margin-right:6px"></lucide-angular>
2018
+ Timeline
2019
+ </ng-template>
2020
+
2021
+ <div class="dbg-tab-content">
2022
+ <div class="dbg-timeline" *ngIf="timelineEntries.length; else noData">
2023
+ <div class="dbg-tl-entry"
2024
+ *ngFor="let entry of timelineEntries; let last = last"
2025
+ >
2026
+ <div class="dbg-tl-entry__row">
2027
+ <!-- Dot -->
2028
+ <div class="dbg-tl-dot" [ngClass]="'dbg-tl-dot--' + entry.kind"></div>
2029
+
2030
+ <!-- Time -->
2031
+ <span class="dbg-tl-entry__time dbg-mono">{{ entry.timestamp | date:'HH:mm:ss.SSS' }}</span>
2032
+
2033
+ <!-- Kind badge -->
2034
+ <span class="dbg-badge" [ngClass]="'dbg-badge--tl-' + entry.kind">{{ entry.kind }}</span>
2035
+
2036
+ <!-- Content based on kind -->
2037
+ <ng-container [ngSwitch]="entry.kind">
2038
+
2039
+ <!-- Navigation -->
2040
+ <ng-container *ngSwitchCase="'navigation'">
2041
+ <code class="dbg-tl-entry__route">{{ entry.route }}</code>
2042
+ <span class="dbg-tl-entry__detail">{{ entry.actionsCount }} azioni</span>
2043
+ </ng-container>
2044
+
2045
+ <!-- Click -->
2046
+ <ng-container *ngSwitchCase="'click'">
2047
+ <code class="dbg-tl-entry__target">&lt;{{ entry.tag }}&gt;</code>
2048
+ <span class="dbg-badge dbg-badge--id" *ngIf="entry.trackId">#{{ entry.trackId }}</span>
2049
+ <span class="dbg-tl-entry__text" *ngIf="entry.text">{{ entry.text }}</span>
2050
+ </ng-container>
2051
+
2052
+ <!-- Form event -->
2053
+ <ng-container *ngSwitchCase="'formEvent'">
2054
+ <span class="dbg-badge" [ngClass]="'dbg-badge--' + entry.eventType">{{ entry.eventType }}</span>
2055
+ <code class="dbg-tl-entry__field">{{ entry.formId }}.{{ entry.fieldKey }}</code>
2056
+ <button class="dbg-json-btn" *ngIf="entry.metadata" (click)="openJsonModal('Form Event — ' + entry.fieldKey, entry.metadata)">
2057
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2058
+ </button>
2059
+ </ng-container>
2060
+
2061
+ <!-- HTTP call -->
2062
+ <ng-container *ngSwitchCase="'httpCall'">
2063
+ <span class="dbg-badge" [ngClass]="'dbg-badge--' + (entry.method ?? '').toLowerCase()">{{ entry.method }}</span>
2064
+ <code class="dbg-tl-entry__url">{{ entry.url }}</code>
2065
+ <span class="dbg-status" [class.dbg-status--ok]="entry.status && entry.status < 400"
2066
+ [class.dbg-status--err]="!entry.status || entry.status! >= 400">
2067
+ {{ entry.status ?? '—' }}
2068
+ </span>
2069
+ <span class="dbg-tl-entry__detail" *ngIf="entry.durationMs">{{ entry.durationMs }}ms</span>
2070
+ <button class="dbg-json-btn" *ngIf="entry.requestPayload" (click)="openJsonModal('HTTP Payload', entry.requestPayload)">
2071
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2072
+ </button>
2073
+ </ng-container>
2074
+
2075
+ </ng-container>
2076
+ </div>
2077
+
2078
+ <div class="dbg-tl-line" *ngIf="!last"></div>
2079
+ </div>
2080
+ </div>
2081
+
2082
+ <ng-template #noData>
2083
+ <div class="dbg-empty">
2084
+ <lucide-angular name="git-branch" [size]="48" style="opacity:0.3"></lucide-angular>
2085
+ <p>Nessun evento registrato</p>
2086
+ </div>
2087
+ </ng-template>
2088
+ </div>
2089
+ </mat-tab>
2090
+
2091
+ <!-- TAB: Form Events -->
2092
+ <mat-tab>
2093
+ <ng-template mat-tab-label>
2094
+ <lucide-angular name="file-text" [size]="14" style="margin-right:6px"></lucide-angular>
2095
+ Form Events
2096
+ </ng-template>
2097
+
2098
+ <div class="dbg-tab-content">
2099
+ <div class="dbg-table-wrap" *ngIf="viewingSession?.formEvents?.length; else noFormData">
2100
+ <table class="dbg-table">
2101
+ <thead>
2102
+ <tr>
2103
+ <th>Ora</th>
2104
+ <th>Rotta</th>
2105
+ <th>Form</th>
2106
+ <th>Campo</th>
2107
+ <th>Evento</th>
2108
+ <th>Valore campo</th>
2109
+ <th>Form completo</th>
2110
+ <th>Dettagli</th>
2111
+ </tr>
2112
+ </thead>
2113
+ <tbody>
2114
+ <tr *ngFor="let ev of viewingSession!.formEvents">
2115
+ <td class="dbg-mono">{{ ev.timestamp | date:'HH:mm:ss.SSS' }}</td>
2116
+ <td class="dbg-mono dbg-truncate">{{ ev.route }}</td>
2117
+ <td>{{ ev.formId }}</td>
2118
+ <td><code>{{ ev.fieldKey }}</code></td>
2119
+ <td>
2120
+ <span class="dbg-badge" [ngClass]="'dbg-badge--' + ev.eventType">{{ ev.eventType }}</span>
2121
+ </td>
2122
+ <td>
2123
+ <button class="dbg-json-btn" *ngIf="ev.metadata?.value !== undefined"
2124
+ (click)="openJsonModal('Valore: ' + ev.fieldKey, ev.metadata!.value)">
2125
+ <lucide-angular name="code" [size]="12"></lucide-angular> valore
2126
+ </button>
2127
+ <span class="dbg-na" *ngIf="ev.metadata?.value === undefined">—</span>
2128
+ </td>
2129
+ <td>
2130
+ <button class="dbg-json-btn" *ngIf="ev.metadata?.formValues"
2131
+ (click)="openJsonModal('Form Values — ' + ev.formId, ev.metadata!.formValues)">
2132
+ <lucide-angular name="code" [size]="12"></lucide-angular> form
2133
+ </button>
2134
+ <span class="dbg-na" *ngIf="!ev.metadata?.formValues">—</span>
2135
+ </td>
2136
+ <td>
2137
+ <button class="dbg-json-btn" *ngIf="ev.metadata"
2138
+ (click)="openJsonModal('Dettagli evento', ev.metadata)">
2139
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2140
+ </button>
2141
+ </td>
2142
+ </tr>
2143
+ </tbody>
2144
+ </table>
2145
+ </div>
2146
+
2147
+ <ng-template #noFormData>
2148
+ <div class="dbg-empty">
2149
+ <lucide-angular name="file-text" [size]="48" style="opacity:0.3"></lucide-angular>
2150
+ <p>Nessun form event registrato</p>
2151
+ </div>
2152
+ </ng-template>
2153
+ </div>
2154
+ </mat-tab>
2155
+
2156
+ <!-- TAB: HTTP Calls -->
2157
+ <mat-tab>
2158
+ <ng-template mat-tab-label>
2159
+ <lucide-angular name="external-link" [size]="14" style="margin-right:6px"></lucide-angular>
2160
+ HTTP Calls
2161
+ </ng-template>
2162
+
2163
+ <div class="dbg-tab-content">
2164
+ <div class="dbg-table-wrap" *ngIf="viewingSession?.calls?.length; else noHttpData">
2165
+ <table class="dbg-table">
2166
+ <thead>
2167
+ <tr>
2168
+ <th>Ora</th>
2169
+ <th>Metodo</th>
2170
+ <th>URL</th>
2171
+ <th>Status</th>
2172
+ <th>Durata</th>
2173
+ <th>Payload</th>
2174
+ <th>Headers</th>
2175
+ </tr>
2176
+ </thead>
2177
+ <tbody>
2178
+ <tr *ngFor="let call of viewingSession!.calls">
2179
+ <td class="dbg-mono">{{ call.timestamp | date:'HH:mm:ss.SSS' }}</td>
2180
+ <td>
2181
+ <span class="dbg-badge" [ngClass]="'dbg-badge--' + call.method.toLowerCase()">{{ call.method }}</span>
2182
+ </td>
2183
+ <td class="dbg-mono dbg-truncate">{{ call.url }}</td>
2184
+ <td>
2185
+ <span class="dbg-status" [class.dbg-status--ok]="call.status && call.status < 400"
2186
+ [class.dbg-status--err]="!call.status || call.status >= 400">
2187
+ {{ call.status ?? '—' }}
2188
+ </span>
2189
+ </td>
2190
+ <td class="dbg-mono">{{ call.durationMs }}ms</td>
2191
+ <td>
2192
+ <button class="dbg-json-btn" *ngIf="call.requestPayload"
2193
+ (click)="openJsonModal('Payload — ' + call.method + ' ' + call.url, call.requestPayload)">
2194
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2195
+ </button>
2196
+ <span class="dbg-na" *ngIf="!call.requestPayload">—</span>
2197
+ </td>
2198
+ <td>
2199
+ <button class="dbg-json-btn" *ngIf="call.responseHeaders"
2200
+ (click)="openJsonModal('Response Headers', call.responseHeaders)">
2201
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2202
+ </button>
2203
+ <span class="dbg-na" *ngIf="!call.responseHeaders">—</span>
2204
+ </td>
2205
+ </tr>
2206
+ </tbody>
2207
+ </table>
2208
+ </div>
2209
+
2210
+ <ng-template #noHttpData>
2211
+ <div class="dbg-empty">
2212
+ <lucide-angular name="external-link" [size]="48" style="opacity:0.3"></lucide-angular>
2213
+ <p>Nessuna chiamata HTTP intercettata</p>
2214
+ </div>
2215
+ </ng-template>
2216
+ </div>
2217
+ </mat-tab>
2218
+ </mat-tab-group>
2219
+ </div>
2220
+ `, isInline: true, styles: ["@use \"../../core/tokens/mixins\" as ui;.dbg-root{display:flex;flex-direction:column;height:100vh;background:var(--ui-color-bg-subtle);&.dialog-mode{height:100%}}.dbg-header{display:flex;align-items:center;justify-content:space-between;padding:var(--ui-spacing-3) var(--ui-spacing-4);background:var(--ui-color-surface);border-bottom:1px solid var(--ui-color-border);box-shadow:var(--ui-shadow-sm);h2{margin:0;font-size:var(--ui-font-size-lg);font-weight:500;color:var(--ui-color-text)}}.dbg-header__left{display:flex;align-items:center;gap:var(--ui-spacing-2)}.dbg-header__icon{color:var(--ui-color-primary)}.dbg-tabs{flex:1;display:flex;flex-direction:column;overflow:hidden;::ng-deep .mat-mdc-tab-header{background:var(--ui-color-surface);border-bottom:1px solid var(--ui-color-border)}::ng-deep .mat-mdc-tab .mdc-tab__text-label{color:var(--ui-color-text-secondary);font-weight:500;font-size:var(--ui-font-size-sm)}::ng-deep .mat-mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--ui-color-primary);font-weight:600}::ng-deep .mat-mdc-tab-header .mdc-tab-indicator__content--underline{border-color:var(--ui-color-primary);border-width:3px}::ng-deep .mat-mdc-tab-body-wrapper{flex:1;overflow:hidden}::ng-deep .mat-mdc-tab-body{height:100%}::ng-deep .mat-mdc-tab-body-content{height:100%;overflow:auto}}.dbg-tab-content{padding:var(--ui-spacing-4);max-width:1200px;margin:0 auto;width:100%}.dbg-dashboard{display:flex;flex-direction:column;gap:var(--ui-spacing-4)}.dbg-dashboard-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--ui-spacing-4)}.dbg-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--ui-spacing-3)}.dbg-stat{display:flex;flex-direction:column;align-items:center;gap:4px;padding:var(--ui-spacing-4);background:var(--ui-color-surface);border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-lg);transition:box-shadow .15s;&:hover{box-shadow:var(--ui-shadow-md)}}.dbg-stat__icon{color:var(--ui-color-primary);opacity:.7}.dbg-stat__value{font-size:var(--ui-font-size-2xl);font-weight:var(--ui-font-weight-bold);color:var(--ui-color-primary);font-family:var(--ui-font-family-mono)}.dbg-stat__label{font-size:11px;color:var(--ui-color-text-muted);text-transform:uppercase;letter-spacing:.05em}.dbg-panel{background:var(--ui-color-surface);border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-lg);padding:var(--ui-spacing-4)}.dbg-panel__header{display:flex;align-items:center;gap:var(--ui-spacing-2);font-size:var(--ui-font-size-sm);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);margin-bottom:var(--ui-spacing-3)}.dbg-panel__desc{font-size:var(--ui-font-size-xs);color:var(--ui-color-text-secondary);margin:0 0 var(--ui-spacing-3)}.dbg-kv{display:grid;grid-template-columns:100px 1fr;gap:var(--ui-spacing-2) var(--ui-spacing-4);font-size:var(--ui-font-size-sm)}.dbg-kv__k{font-weight:600;color:var(--ui-color-text-secondary);font-size:11px;text-transform:uppercase;letter-spacing:.03em}.dbg-kv__v{color:var(--ui-color-text)}.dbg-mono{font-family:var(--ui-font-family-mono);font-size:var(--ui-font-size-xs)}.dbg-sim-btns{display:flex;gap:var(--ui-spacing-2);flex-wrap:wrap}.dbg-sim-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:var(--ui-radius-md);font-size:var(--ui-font-size-xs);font-weight:600;cursor:pointer;border:1px solid transparent;transition:all .15s}.dbg-sim-btn--get{background:#dcfce7;color:#166534;border-color:#86efac;&:hover{background:#bbf7d0}}.dbg-sim-btn--post{background:#dbeafe;color:#1e40af;border-color:#93c5fd;&:hover{background:#bfdbfe}}.dbg-sim-btn--fail{background:#fee2e2;color:#991b1b;border-color:#fca5a5;&:hover{background:#fecaca}}.dbg-action-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:var(--ui-radius-md);font-size:var(--ui-font-size-xs);font-weight:600;cursor:pointer;background:var(--ui-color-surface);color:var(--ui-color-text);border:1px solid var(--ui-color-border);transition:all .15s;&:hover{background:var(--ui-color-surface-hover)}}.dbg-action-btn--danger{color:#dc2626;border-color:#fca5a5}.dbg-action-btn--danger:hover{background:#fee2e2}.dbg-session-list{display:flex;flex-direction:column;gap:var(--ui-spacing-1)}.dbg-session-row{display:flex;align-items:center;gap:var(--ui-spacing-3);padding:var(--ui-spacing-2) var(--ui-spacing-3);border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-md);font-size:var(--ui-font-size-xs);cursor:pointer;transition:all .12s;&:hover{border-color:var(--ui-color-primary);background:color-mix(in srgb,var(--ui-color-primary) 4%,transparent)}}.dbg-session-row__arrow{margin-left:auto;color:var(--ui-color-text-muted)}.dbg-timeline{position:relative;padding-left:24px}.dbg-tl-entry{position:relative;margin-bottom:2px}.dbg-tl-entry__row{display:flex;align-items:center;gap:var(--ui-spacing-2);padding:4px var(--ui-spacing-3);border-radius:var(--ui-radius-md);font-size:12px;transition:background .12s;&:hover{background:var(--ui-color-surface-hover)}}.dbg-tl-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;position:absolute;left:-16px;top:10px}.dbg-tl-dot--navigation{background:var(--ui-color-primary);width:10px;height:10px;left:-17px;top:9px}.dbg-tl-dot--click{background:var(--ui-color-border-strong)}.dbg-tl-dot--formEvent{background:#f59e0b}.dbg-tl-dot--httpCall{background:#3b82f6}.dbg-tl-line{position:absolute;left:-12px;top:24px;bottom:-2px;width:2px;background:var(--ui-color-border)}.dbg-tl-entry__time{color:var(--ui-color-text-muted);min-width:85px;font-size:11px}.dbg-tl-entry__route{font-family:var(--ui-font-family-mono);font-weight:600;color:var(--ui-color-text);font-size:12px}.dbg-tl-entry__target,.dbg-tl-entry__field{font-family:var(--ui-font-family-mono);color:var(--ui-color-text-secondary);font-size:11px}.dbg-tl-entry__url{font-family:var(--ui-font-family-mono);color:var(--ui-color-text-secondary);font-size:11px;max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dbg-tl-entry__detail{font-size:11px;color:var(--ui-color-text-muted)}.dbg-tl-entry__text{color:var(--ui-color-text-muted);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}.dbg-json-btn{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:var(--ui-radius-sm);font-size:10px;font-weight:600;cursor:pointer;background:var(--ui-color-bg-subtle);color:var(--ui-color-text-secondary);border:1px solid var(--ui-color-border);transition:all .12s;&:hover{background:var(--ui-color-surface-hover);color:var(--ui-color-primary);border-color:var(--ui-color-primary)}}.dbg-na{color:var(--ui-color-text-muted);font-size:11px}.dbg-table-wrap{overflow-x:auto;border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-lg)}.dbg-table{width:100%;border-collapse:collapse;font-size:var(--ui-font-size-xs);th,td{padding:8px 12px;text-align:left;border-bottom:1px solid var(--ui-color-border)}th{background:var(--ui-color-bg-subtle);font-weight:600;color:var(--ui-color-text-secondary);text-transform:uppercase;letter-spacing:.05em;font-size:10px}tbody tr:hover{background:var(--ui-color-surface-hover)}tbody tr:last-child td{border-bottom:none}}.dbg-truncate{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dbg-badge{display:inline-block;padding:1px 8px;border-radius:var(--ui-radius-full);font-size:10px;font-weight:600;white-space:nowrap}.dbg-badge--tl-navigation{background:color-mix(in srgb,var(--ui-color-primary) 12%,transparent);color:var(--ui-color-primary)}.dbg-badge--tl-click{background:#f1f5f9;color:#475569}.dbg-badge--tl-formEvent{background:#fef3c7;color:#92400e}.dbg-badge--tl-httpCall{background:#dbeafe;color:#1e40af}.dbg-badge--action{background:color-mix(in srgb,var(--ui-color-primary) 10%,transparent);color:var(--ui-color-primary);text-transform:uppercase}.dbg-badge--id{background:color-mix(in srgb,var(--ui-color-primary) 8%,transparent);color:var(--ui-color-primary)}.dbg-badge--focus{background:#dbeafe;color:#1e40af}.dbg-badge--blur{background:#f1f5f9;color:#475569}.dbg-badge--valueChange{background:#fef3c7;color:#92400e}.dbg-badge--validationError{background:#fee2e2;color:#991b1b}.dbg-badge--get{background:#dcfce7;color:#166534}.dbg-badge--post{background:#dbeafe;color:#1e40af}.dbg-badge--put{background:#fef3c7;color:#92400e}.dbg-badge--delete{background:#fee2e2;color:#991b1b}.dbg-status{font-family:var(--ui-font-family-mono);font-weight:600}.dbg-status--ok{color:#16a34a}.dbg-status--err{color:#dc2626}.dbg-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--ui-spacing-12);color:var(--ui-color-text-muted);text-align:center;p{margin:var(--ui-spacing-3) 0 0;font-size:var(--ui-font-size-sm)}}@media (max-width: 640px){.dbg-stats{grid-template-columns:repeat(2,1fr)}.dbg-dashboard-grid{grid-template-columns:1fr}.dbg-sim-btns{flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "pipe", type: i2.SlicePipe, name: "slice" }, { kind: "pipe", type: i2.DatePipe, name: "date" }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i3.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i3.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i3.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["label", "tooltip", "variant", "size", "icon", "iconPosition", "loading", "disabled", "fullWidth", "type", "ariaLabel", "customClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2221
+ }
2222
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxDebuggerComponent, decorators: [{
2223
+ type: Component,
2224
+ args: [{ selector: 'ui-blackbox-debugger', standalone: true, imports: [
2225
+ CommonModule,
2226
+ MatTabsModule,
2227
+ LucideAngularModule,
2228
+ UiButtonComponent,
2229
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: `
2230
+ <div class="dbg-root" [class.dialog-mode]="isDialogMode">
2231
+
2232
+ <!-- Header -->
2233
+ @if (isDialogMode) {
2234
+ <div class="dbg-header">
2235
+ <div class="dbg-header__left">
2236
+ <lucide-angular name="activity" [size]="20" class="dbg-header__icon"></lucide-angular>
2237
+ <h2>BlackBox Debugger</h2>
2238
+ </div>
2239
+ <ui-button icon="x" variant="ghost" size="md" ariaLabel="Chiudi" tooltip="Chiudi" (click)="closeDialog()" />
2240
+ </div>
2241
+ }
2242
+
2243
+ <!-- Tabs -->
2244
+ <mat-tab-group
2245
+ [selectedIndex]="selectedTab"
2246
+ (selectedIndexChange)="selectedTab = $event"
2247
+ class="dbg-tabs"
2248
+ >
2249
+ <!-- TAB: Dashboard -->
2250
+ <mat-tab>
2251
+ <ng-template mat-tab-label>
2252
+ <lucide-angular name="monitor" [size]="14" style="margin-right:6px"></lucide-angular>
2253
+ Dashboard
2254
+ </ng-template>
2255
+
2256
+ <div class="dbg-tab-content dbg-dashboard">
2257
+ <!-- Stats -->
2258
+ <div class="dbg-stats">
2259
+ <div class="dbg-stat" *ngFor="let s of statsCards">
2260
+ <lucide-angular [name]="s.icon" [size]="18" class="dbg-stat__icon"></lucide-angular>
2261
+ <span class="dbg-stat__value">{{ s.value }}</span>
2262
+ <span class="dbg-stat__label">{{ s.label }}</span>
2263
+ </div>
2264
+ </div>
2265
+ <div class="dbg-dashboard-grid">
2266
+ <!-- Session info -->
2267
+ <div class="dbg-panel" *ngIf="session">
2268
+ <div class="dbg-panel__header">
2269
+ <lucide-angular name="info" [size]="14"></lucide-angular>
2270
+ Sessione corrente
2271
+ </div>
2272
+ <div class="dbg-kv">
2273
+ <span class="dbg-kv__k">Session ID</span>
2274
+ <code class="dbg-kv__v dbg-mono">{{ session.sessionId }}</code>
2275
+ <span class="dbg-kv__k">Fingerprint</span>
2276
+ <code class="dbg-kv__v dbg-mono">{{ session.fingerprint }}</code>
2277
+ <span class="dbg-kv__k">Inizio</span>
2278
+ <span class="dbg-kv__v">{{ session.startedAt | date:'dd/MM/yyyy HH:mm:ss' }}</span>
2279
+ <span class="dbg-kv__k">Durata</span>
2280
+ <span class="dbg-kv__v">{{ sessionDuration }}</span>
2281
+ </div>
2282
+ </div>
2283
+
2284
+ <!-- HTTP Simulator -->
2285
+ <div class="dbg-panel">
2286
+ <div class="dbg-panel__header">
2287
+ <lucide-angular name="zap" [size]="14"></lucide-angular>
2288
+ HTTP Simulator
2289
+ </div>
2290
+ <p class="dbg-panel__desc">Genera chiamate fittizie per testare l'interceptor BlackBox.</p>
2291
+ <div class="dbg-sim-btns">
2292
+ <button class="dbg-sim-btn dbg-sim-btn--get" (click)="simulateGet()">
2293
+ <lucide-angular name="play" [size]="12"></lucide-angular> GET /todos/1
2294
+ </button>
2295
+ <button class="dbg-sim-btn dbg-sim-btn--post" (click)="simulatePost()">
2296
+ <lucide-angular name="play" [size]="12"></lucide-angular> POST /posts
2297
+ </button>
2298
+ <button class="dbg-sim-btn dbg-sim-btn--fail" (click)="simulateFail()">
2299
+ <lucide-angular name="play" [size]="12"></lucide-angular> GET /404
2300
+ </button>
2301
+ </div>
2302
+ </div>
2303
+
2304
+ <!-- Actions -->
2305
+ <div class="dbg-panel">
2306
+ <div class="dbg-panel__header">
2307
+ <lucide-angular name="settings" [size]="14"></lucide-angular>
2308
+ Azioni
2309
+ </div>
2310
+ <div class="dbg-sim-btns">
2311
+ <button class="dbg-action-btn" (click)="refresh()">
2312
+ <lucide-angular name="refresh-cw" [size]="12"></lucide-angular> Aggiorna
2313
+ </button>
2314
+ <button class="dbg-action-btn" (click)="exportSessions()">
2315
+ <lucide-angular name="download" [size]="12"></lucide-angular> Esporta JSONL
2316
+ </button>
2317
+ <button class="dbg-action-btn" (click)="importJsonl()">
2318
+ <lucide-angular name="upload" [size]="12"></lucide-angular> Importa JSONL
2319
+ </button>
2320
+ <button class="dbg-action-btn dbg-action-btn--danger" (click)="clearHistory()">
2321
+ <lucide-angular name="trash-2" [size]="12"></lucide-angular> Svuota
2322
+ </button>
2323
+ </div>
2324
+ </div>
2325
+
2326
+ <!-- Past sessions -->
2327
+ <div class="dbg-panel" *ngIf="pastSessions.length > 0">
2328
+ <div class="dbg-panel__header">
2329
+ <lucide-angular name="clock" [size]="14"></lucide-angular>
2330
+ Sessioni archiviate ({{ pastSessions.length }})
2331
+ </div>
2332
+ <div class="dbg-session-list">
2333
+ <div class="dbg-session-row" *ngFor="let s of pastSessions" (click)="viewSession(s)">
2334
+ <code class="dbg-mono">{{ s.sessionId | slice:0:12 }}...</code>
2335
+ <span>{{ s.startedAt | date:'dd/MM HH:mm' }}</span>
2336
+ <span>{{ s.steps.length }} steps</span>
2337
+ <lucide-angular name="chevron-right" [size]="14" class="dbg-session-row__arrow"></lucide-angular>
2338
+ </div>
2339
+ </div>
2340
+ </div>
2341
+ </div>
2342
+
2343
+ </div>
2344
+ </mat-tab>
2345
+
2346
+ <!-- TAB: Timeline (unified) -->
2347
+ <mat-tab>
2348
+ <ng-template mat-tab-label>
2349
+ <lucide-angular name="git-branch" [size]="14" style="margin-right:6px"></lucide-angular>
2350
+ Timeline
2351
+ </ng-template>
2352
+
2353
+ <div class="dbg-tab-content">
2354
+ <div class="dbg-timeline" *ngIf="timelineEntries.length; else noData">
2355
+ <div class="dbg-tl-entry"
2356
+ *ngFor="let entry of timelineEntries; let last = last"
2357
+ >
2358
+ <div class="dbg-tl-entry__row">
2359
+ <!-- Dot -->
2360
+ <div class="dbg-tl-dot" [ngClass]="'dbg-tl-dot--' + entry.kind"></div>
2361
+
2362
+ <!-- Time -->
2363
+ <span class="dbg-tl-entry__time dbg-mono">{{ entry.timestamp | date:'HH:mm:ss.SSS' }}</span>
2364
+
2365
+ <!-- Kind badge -->
2366
+ <span class="dbg-badge" [ngClass]="'dbg-badge--tl-' + entry.kind">{{ entry.kind }}</span>
2367
+
2368
+ <!-- Content based on kind -->
2369
+ <ng-container [ngSwitch]="entry.kind">
2370
+
2371
+ <!-- Navigation -->
2372
+ <ng-container *ngSwitchCase="'navigation'">
2373
+ <code class="dbg-tl-entry__route">{{ entry.route }}</code>
2374
+ <span class="dbg-tl-entry__detail">{{ entry.actionsCount }} azioni</span>
2375
+ </ng-container>
2376
+
2377
+ <!-- Click -->
2378
+ <ng-container *ngSwitchCase="'click'">
2379
+ <code class="dbg-tl-entry__target">&lt;{{ entry.tag }}&gt;</code>
2380
+ <span class="dbg-badge dbg-badge--id" *ngIf="entry.trackId">#{{ entry.trackId }}</span>
2381
+ <span class="dbg-tl-entry__text" *ngIf="entry.text">{{ entry.text }}</span>
2382
+ </ng-container>
2383
+
2384
+ <!-- Form event -->
2385
+ <ng-container *ngSwitchCase="'formEvent'">
2386
+ <span class="dbg-badge" [ngClass]="'dbg-badge--' + entry.eventType">{{ entry.eventType }}</span>
2387
+ <code class="dbg-tl-entry__field">{{ entry.formId }}.{{ entry.fieldKey }}</code>
2388
+ <button class="dbg-json-btn" *ngIf="entry.metadata" (click)="openJsonModal('Form Event — ' + entry.fieldKey, entry.metadata)">
2389
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2390
+ </button>
2391
+ </ng-container>
2392
+
2393
+ <!-- HTTP call -->
2394
+ <ng-container *ngSwitchCase="'httpCall'">
2395
+ <span class="dbg-badge" [ngClass]="'dbg-badge--' + (entry.method ?? '').toLowerCase()">{{ entry.method }}</span>
2396
+ <code class="dbg-tl-entry__url">{{ entry.url }}</code>
2397
+ <span class="dbg-status" [class.dbg-status--ok]="entry.status && entry.status < 400"
2398
+ [class.dbg-status--err]="!entry.status || entry.status! >= 400">
2399
+ {{ entry.status ?? '—' }}
2400
+ </span>
2401
+ <span class="dbg-tl-entry__detail" *ngIf="entry.durationMs">{{ entry.durationMs }}ms</span>
2402
+ <button class="dbg-json-btn" *ngIf="entry.requestPayload" (click)="openJsonModal('HTTP Payload', entry.requestPayload)">
2403
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2404
+ </button>
2405
+ </ng-container>
2406
+
2407
+ </ng-container>
2408
+ </div>
2409
+
2410
+ <div class="dbg-tl-line" *ngIf="!last"></div>
2411
+ </div>
2412
+ </div>
2413
+
2414
+ <ng-template #noData>
2415
+ <div class="dbg-empty">
2416
+ <lucide-angular name="git-branch" [size]="48" style="opacity:0.3"></lucide-angular>
2417
+ <p>Nessun evento registrato</p>
2418
+ </div>
2419
+ </ng-template>
2420
+ </div>
2421
+ </mat-tab>
2422
+
2423
+ <!-- TAB: Form Events -->
2424
+ <mat-tab>
2425
+ <ng-template mat-tab-label>
2426
+ <lucide-angular name="file-text" [size]="14" style="margin-right:6px"></lucide-angular>
2427
+ Form Events
2428
+ </ng-template>
2429
+
2430
+ <div class="dbg-tab-content">
2431
+ <div class="dbg-table-wrap" *ngIf="viewingSession?.formEvents?.length; else noFormData">
2432
+ <table class="dbg-table">
2433
+ <thead>
2434
+ <tr>
2435
+ <th>Ora</th>
2436
+ <th>Rotta</th>
2437
+ <th>Form</th>
2438
+ <th>Campo</th>
2439
+ <th>Evento</th>
2440
+ <th>Valore campo</th>
2441
+ <th>Form completo</th>
2442
+ <th>Dettagli</th>
2443
+ </tr>
2444
+ </thead>
2445
+ <tbody>
2446
+ <tr *ngFor="let ev of viewingSession!.formEvents">
2447
+ <td class="dbg-mono">{{ ev.timestamp | date:'HH:mm:ss.SSS' }}</td>
2448
+ <td class="dbg-mono dbg-truncate">{{ ev.route }}</td>
2449
+ <td>{{ ev.formId }}</td>
2450
+ <td><code>{{ ev.fieldKey }}</code></td>
2451
+ <td>
2452
+ <span class="dbg-badge" [ngClass]="'dbg-badge--' + ev.eventType">{{ ev.eventType }}</span>
2453
+ </td>
2454
+ <td>
2455
+ <button class="dbg-json-btn" *ngIf="ev.metadata?.value !== undefined"
2456
+ (click)="openJsonModal('Valore: ' + ev.fieldKey, ev.metadata!.value)">
2457
+ <lucide-angular name="code" [size]="12"></lucide-angular> valore
2458
+ </button>
2459
+ <span class="dbg-na" *ngIf="ev.metadata?.value === undefined">—</span>
2460
+ </td>
2461
+ <td>
2462
+ <button class="dbg-json-btn" *ngIf="ev.metadata?.formValues"
2463
+ (click)="openJsonModal('Form Values — ' + ev.formId, ev.metadata!.formValues)">
2464
+ <lucide-angular name="code" [size]="12"></lucide-angular> form
2465
+ </button>
2466
+ <span class="dbg-na" *ngIf="!ev.metadata?.formValues">—</span>
2467
+ </td>
2468
+ <td>
2469
+ <button class="dbg-json-btn" *ngIf="ev.metadata"
2470
+ (click)="openJsonModal('Dettagli evento', ev.metadata)">
2471
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2472
+ </button>
2473
+ </td>
2474
+ </tr>
2475
+ </tbody>
2476
+ </table>
2477
+ </div>
2478
+
2479
+ <ng-template #noFormData>
2480
+ <div class="dbg-empty">
2481
+ <lucide-angular name="file-text" [size]="48" style="opacity:0.3"></lucide-angular>
2482
+ <p>Nessun form event registrato</p>
2483
+ </div>
2484
+ </ng-template>
2485
+ </div>
2486
+ </mat-tab>
2487
+
2488
+ <!-- TAB: HTTP Calls -->
2489
+ <mat-tab>
2490
+ <ng-template mat-tab-label>
2491
+ <lucide-angular name="external-link" [size]="14" style="margin-right:6px"></lucide-angular>
2492
+ HTTP Calls
2493
+ </ng-template>
2494
+
2495
+ <div class="dbg-tab-content">
2496
+ <div class="dbg-table-wrap" *ngIf="viewingSession?.calls?.length; else noHttpData">
2497
+ <table class="dbg-table">
2498
+ <thead>
2499
+ <tr>
2500
+ <th>Ora</th>
2501
+ <th>Metodo</th>
2502
+ <th>URL</th>
2503
+ <th>Status</th>
2504
+ <th>Durata</th>
2505
+ <th>Payload</th>
2506
+ <th>Headers</th>
2507
+ </tr>
2508
+ </thead>
2509
+ <tbody>
2510
+ <tr *ngFor="let call of viewingSession!.calls">
2511
+ <td class="dbg-mono">{{ call.timestamp | date:'HH:mm:ss.SSS' }}</td>
2512
+ <td>
2513
+ <span class="dbg-badge" [ngClass]="'dbg-badge--' + call.method.toLowerCase()">{{ call.method }}</span>
2514
+ </td>
2515
+ <td class="dbg-mono dbg-truncate">{{ call.url }}</td>
2516
+ <td>
2517
+ <span class="dbg-status" [class.dbg-status--ok]="call.status && call.status < 400"
2518
+ [class.dbg-status--err]="!call.status || call.status >= 400">
2519
+ {{ call.status ?? '—' }}
2520
+ </span>
2521
+ </td>
2522
+ <td class="dbg-mono">{{ call.durationMs }}ms</td>
2523
+ <td>
2524
+ <button class="dbg-json-btn" *ngIf="call.requestPayload"
2525
+ (click)="openJsonModal('Payload — ' + call.method + ' ' + call.url, call.requestPayload)">
2526
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2527
+ </button>
2528
+ <span class="dbg-na" *ngIf="!call.requestPayload">—</span>
2529
+ </td>
2530
+ <td>
2531
+ <button class="dbg-json-btn" *ngIf="call.responseHeaders"
2532
+ (click)="openJsonModal('Response Headers', call.responseHeaders)">
2533
+ <lucide-angular name="code" [size]="12"></lucide-angular>
2534
+ </button>
2535
+ <span class="dbg-na" *ngIf="!call.responseHeaders">—</span>
2536
+ </td>
2537
+ </tr>
2538
+ </tbody>
2539
+ </table>
2540
+ </div>
2541
+
2542
+ <ng-template #noHttpData>
2543
+ <div class="dbg-empty">
2544
+ <lucide-angular name="external-link" [size]="48" style="opacity:0.3"></lucide-angular>
2545
+ <p>Nessuna chiamata HTTP intercettata</p>
2546
+ </div>
2547
+ </ng-template>
2548
+ </div>
2549
+ </mat-tab>
2550
+ </mat-tab-group>
2551
+ </div>
2552
+ `, styles: ["@use \"../../core/tokens/mixins\" as ui;.dbg-root{display:flex;flex-direction:column;height:100vh;background:var(--ui-color-bg-subtle);&.dialog-mode{height:100%}}.dbg-header{display:flex;align-items:center;justify-content:space-between;padding:var(--ui-spacing-3) var(--ui-spacing-4);background:var(--ui-color-surface);border-bottom:1px solid var(--ui-color-border);box-shadow:var(--ui-shadow-sm);h2{margin:0;font-size:var(--ui-font-size-lg);font-weight:500;color:var(--ui-color-text)}}.dbg-header__left{display:flex;align-items:center;gap:var(--ui-spacing-2)}.dbg-header__icon{color:var(--ui-color-primary)}.dbg-tabs{flex:1;display:flex;flex-direction:column;overflow:hidden;::ng-deep .mat-mdc-tab-header{background:var(--ui-color-surface);border-bottom:1px solid var(--ui-color-border)}::ng-deep .mat-mdc-tab .mdc-tab__text-label{color:var(--ui-color-text-secondary);font-weight:500;font-size:var(--ui-font-size-sm)}::ng-deep .mat-mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--ui-color-primary);font-weight:600}::ng-deep .mat-mdc-tab-header .mdc-tab-indicator__content--underline{border-color:var(--ui-color-primary);border-width:3px}::ng-deep .mat-mdc-tab-body-wrapper{flex:1;overflow:hidden}::ng-deep .mat-mdc-tab-body{height:100%}::ng-deep .mat-mdc-tab-body-content{height:100%;overflow:auto}}.dbg-tab-content{padding:var(--ui-spacing-4);max-width:1200px;margin:0 auto;width:100%}.dbg-dashboard{display:flex;flex-direction:column;gap:var(--ui-spacing-4)}.dbg-dashboard-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--ui-spacing-4)}.dbg-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--ui-spacing-3)}.dbg-stat{display:flex;flex-direction:column;align-items:center;gap:4px;padding:var(--ui-spacing-4);background:var(--ui-color-surface);border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-lg);transition:box-shadow .15s;&:hover{box-shadow:var(--ui-shadow-md)}}.dbg-stat__icon{color:var(--ui-color-primary);opacity:.7}.dbg-stat__value{font-size:var(--ui-font-size-2xl);font-weight:var(--ui-font-weight-bold);color:var(--ui-color-primary);font-family:var(--ui-font-family-mono)}.dbg-stat__label{font-size:11px;color:var(--ui-color-text-muted);text-transform:uppercase;letter-spacing:.05em}.dbg-panel{background:var(--ui-color-surface);border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-lg);padding:var(--ui-spacing-4)}.dbg-panel__header{display:flex;align-items:center;gap:var(--ui-spacing-2);font-size:var(--ui-font-size-sm);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);margin-bottom:var(--ui-spacing-3)}.dbg-panel__desc{font-size:var(--ui-font-size-xs);color:var(--ui-color-text-secondary);margin:0 0 var(--ui-spacing-3)}.dbg-kv{display:grid;grid-template-columns:100px 1fr;gap:var(--ui-spacing-2) var(--ui-spacing-4);font-size:var(--ui-font-size-sm)}.dbg-kv__k{font-weight:600;color:var(--ui-color-text-secondary);font-size:11px;text-transform:uppercase;letter-spacing:.03em}.dbg-kv__v{color:var(--ui-color-text)}.dbg-mono{font-family:var(--ui-font-family-mono);font-size:var(--ui-font-size-xs)}.dbg-sim-btns{display:flex;gap:var(--ui-spacing-2);flex-wrap:wrap}.dbg-sim-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:var(--ui-radius-md);font-size:var(--ui-font-size-xs);font-weight:600;cursor:pointer;border:1px solid transparent;transition:all .15s}.dbg-sim-btn--get{background:#dcfce7;color:#166534;border-color:#86efac;&:hover{background:#bbf7d0}}.dbg-sim-btn--post{background:#dbeafe;color:#1e40af;border-color:#93c5fd;&:hover{background:#bfdbfe}}.dbg-sim-btn--fail{background:#fee2e2;color:#991b1b;border-color:#fca5a5;&:hover{background:#fecaca}}.dbg-action-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:var(--ui-radius-md);font-size:var(--ui-font-size-xs);font-weight:600;cursor:pointer;background:var(--ui-color-surface);color:var(--ui-color-text);border:1px solid var(--ui-color-border);transition:all .15s;&:hover{background:var(--ui-color-surface-hover)}}.dbg-action-btn--danger{color:#dc2626;border-color:#fca5a5}.dbg-action-btn--danger:hover{background:#fee2e2}.dbg-session-list{display:flex;flex-direction:column;gap:var(--ui-spacing-1)}.dbg-session-row{display:flex;align-items:center;gap:var(--ui-spacing-3);padding:var(--ui-spacing-2) var(--ui-spacing-3);border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-md);font-size:var(--ui-font-size-xs);cursor:pointer;transition:all .12s;&:hover{border-color:var(--ui-color-primary);background:color-mix(in srgb,var(--ui-color-primary) 4%,transparent)}}.dbg-session-row__arrow{margin-left:auto;color:var(--ui-color-text-muted)}.dbg-timeline{position:relative;padding-left:24px}.dbg-tl-entry{position:relative;margin-bottom:2px}.dbg-tl-entry__row{display:flex;align-items:center;gap:var(--ui-spacing-2);padding:4px var(--ui-spacing-3);border-radius:var(--ui-radius-md);font-size:12px;transition:background .12s;&:hover{background:var(--ui-color-surface-hover)}}.dbg-tl-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;position:absolute;left:-16px;top:10px}.dbg-tl-dot--navigation{background:var(--ui-color-primary);width:10px;height:10px;left:-17px;top:9px}.dbg-tl-dot--click{background:var(--ui-color-border-strong)}.dbg-tl-dot--formEvent{background:#f59e0b}.dbg-tl-dot--httpCall{background:#3b82f6}.dbg-tl-line{position:absolute;left:-12px;top:24px;bottom:-2px;width:2px;background:var(--ui-color-border)}.dbg-tl-entry__time{color:var(--ui-color-text-muted);min-width:85px;font-size:11px}.dbg-tl-entry__route{font-family:var(--ui-font-family-mono);font-weight:600;color:var(--ui-color-text);font-size:12px}.dbg-tl-entry__target,.dbg-tl-entry__field{font-family:var(--ui-font-family-mono);color:var(--ui-color-text-secondary);font-size:11px}.dbg-tl-entry__url{font-family:var(--ui-font-family-mono);color:var(--ui-color-text-secondary);font-size:11px;max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dbg-tl-entry__detail{font-size:11px;color:var(--ui-color-text-muted)}.dbg-tl-entry__text{color:var(--ui-color-text-muted);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}.dbg-json-btn{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:var(--ui-radius-sm);font-size:10px;font-weight:600;cursor:pointer;background:var(--ui-color-bg-subtle);color:var(--ui-color-text-secondary);border:1px solid var(--ui-color-border);transition:all .12s;&:hover{background:var(--ui-color-surface-hover);color:var(--ui-color-primary);border-color:var(--ui-color-primary)}}.dbg-na{color:var(--ui-color-text-muted);font-size:11px}.dbg-table-wrap{overflow-x:auto;border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-lg)}.dbg-table{width:100%;border-collapse:collapse;font-size:var(--ui-font-size-xs);th,td{padding:8px 12px;text-align:left;border-bottom:1px solid var(--ui-color-border)}th{background:var(--ui-color-bg-subtle);font-weight:600;color:var(--ui-color-text-secondary);text-transform:uppercase;letter-spacing:.05em;font-size:10px}tbody tr:hover{background:var(--ui-color-surface-hover)}tbody tr:last-child td{border-bottom:none}}.dbg-truncate{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dbg-badge{display:inline-block;padding:1px 8px;border-radius:var(--ui-radius-full);font-size:10px;font-weight:600;white-space:nowrap}.dbg-badge--tl-navigation{background:color-mix(in srgb,var(--ui-color-primary) 12%,transparent);color:var(--ui-color-primary)}.dbg-badge--tl-click{background:#f1f5f9;color:#475569}.dbg-badge--tl-formEvent{background:#fef3c7;color:#92400e}.dbg-badge--tl-httpCall{background:#dbeafe;color:#1e40af}.dbg-badge--action{background:color-mix(in srgb,var(--ui-color-primary) 10%,transparent);color:var(--ui-color-primary);text-transform:uppercase}.dbg-badge--id{background:color-mix(in srgb,var(--ui-color-primary) 8%,transparent);color:var(--ui-color-primary)}.dbg-badge--focus{background:#dbeafe;color:#1e40af}.dbg-badge--blur{background:#f1f5f9;color:#475569}.dbg-badge--valueChange{background:#fef3c7;color:#92400e}.dbg-badge--validationError{background:#fee2e2;color:#991b1b}.dbg-badge--get{background:#dcfce7;color:#166534}.dbg-badge--post{background:#dbeafe;color:#1e40af}.dbg-badge--put{background:#fef3c7;color:#92400e}.dbg-badge--delete{background:#fee2e2;color:#991b1b}.dbg-status{font-family:var(--ui-font-family-mono);font-weight:600}.dbg-status--ok{color:#16a34a}.dbg-status--err{color:#dc2626}.dbg-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--ui-spacing-12);color:var(--ui-color-text-muted);text-align:center;p{margin:var(--ui-spacing-3) 0 0;font-size:var(--ui-font-size-sm)}}@media (max-width: 640px){.dbg-stats{grid-template-columns:repeat(2,1fr)}.dbg-dashboard-grid{grid-template-columns:1fr}.dbg-sim-btns{flex-direction:column}}\n"] }]
2553
+ }], ctorParameters: () => [{ type: i1$2.MatDialogRef, decorators: [{
2554
+ type: Optional
2555
+ }] }, { type: undefined, decorators: [{
2556
+ type: Optional
2557
+ }, {
2558
+ type: Inject,
2559
+ args: [MAT_DIALOG_DATA]
2560
+ }] }] });
2561
+
2562
+ /**
2563
+ * @module ng-ui-system/blackbox
2564
+ * Servizio per aprire il debugger BlackBox in una modale fullscreen.
2565
+ */
2566
+ /**
2567
+ * Servizio per aprire il debugger BlackBox in una modale fullscreen
2568
+ * tramite il UiModalService.
2569
+ *
2570
+ * @usageNotes
2571
+ *
2572
+ * ### Apertura base
2573
+ * ```typescript
2574
+ * const ref = this.debuggerService.open();
2575
+ * ```
2576
+ *
2577
+ * ### Apertura con sessioni importate (JSONL)
2578
+ * ```typescript
2579
+ * this.debuggerService.open({ importedSessions: mySessions });
2580
+ * ```
2581
+ */
2582
+ class UiBlackboxDebuggerService {
2583
+ constructor() {
2584
+ /** @internal Servizio modale per l'apertura. */
2585
+ this.modalService = inject(UiModalService);
2586
+ }
2587
+ /**
2588
+ * Apre il debugger in una modale fullscreen.
2589
+ *
2590
+ * @param config - Configurazione opzionale
2591
+ * @returns Riferimento alla dialog per gestire chiusura
2592
+ */
2593
+ open(config) {
2594
+ return this.modalService.open(UiBlackboxDebuggerComponent, {
2595
+ size: 'fullscreen',
2596
+ disableClose: false,
2597
+ data: config ?? {},
2598
+ panelClass: 'ui-blackbox-debugger-dialog',
2599
+ });
2600
+ }
2601
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxDebuggerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2602
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxDebuggerService, providedIn: 'root' }); }
2603
+ }
2604
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiBlackboxDebuggerService, decorators: [{
2605
+ type: Injectable,
2606
+ args: [{ providedIn: 'root' }]
2607
+ }] });
2608
+
2609
+ /**
2610
+ * @module ng-ui-system/blackbox
2611
+ * HTTP interceptor for BlackBox — captures requests marked with
2612
+ * the `X-Ui-Blackbox` header.
2613
+ *
2614
+ * @usageNotes
2615
+ * ### Setup (functional interceptor, Angular 17+)
2616
+ * ```typescript
2617
+ * // app.config.ts
2618
+ * import { provideHttpClient, withInterceptors } from '@angular/common/http';
2619
+ * import { uiBlackboxInterceptor } from '@gnggln/ng-ui-system';
2620
+ *
2621
+ * export const appConfig: ApplicationConfig = {
2622
+ * providers: [
2623
+ * provideHttpClient(withInterceptors([uiBlackboxInterceptor])),
2624
+ * ],
2625
+ * };
2626
+ * ```
2627
+ *
2628
+ * ### Marking a request
2629
+ * ```typescript
2630
+ * this.http.get('/api/data', {
2631
+ * headers: new HttpHeaders({
2632
+ * 'X-Ui-Blackbox': 'true',
2633
+ * // Optional: filter response headers to capture
2634
+ * 'X-Ui-Blackbox-Headers': 'x-request-id|x-correlation-.*',
2635
+ * }),
2636
+ * });
2637
+ * ```
2638
+ */
2639
+ /** Header name used to mark a request for BlackBox tracking. */
2640
+ const MARKER_HEADER = 'X-Ui-Blackbox';
2641
+ /** Header name specifying a regex to filter response headers to capture. */
2642
+ const HEADER_FILTER = 'X-Ui-Blackbox-Headers';
2643
+ /**
2644
+ * Angular functional HTTP interceptor for BlackBox.
2645
+ *
2646
+ * Only processes requests that include the `X-Ui-Blackbox` header.
2647
+ * Strips the custom headers before forwarding the request to the server.
2648
+ */
2649
+ const uiBlackboxInterceptor = (req, next) => {
2650
+ // Only intercept marked requests
2651
+ if (!req.headers.has(MARKER_HEADER)) {
2652
+ return next(req);
2653
+ }
2654
+ const blackbox = inject(UiBlackboxService);
2655
+ const headerFilter = req.headers.get(HEADER_FILTER) ?? undefined;
2656
+ const startTime = Date.now();
2657
+ // Strip custom headers before forwarding
2658
+ const cleanReq = req.clone({
2659
+ headers: req.headers.delete(MARKER_HEADER).delete(HEADER_FILTER),
2660
+ });
2661
+ // Capture request payload
2662
+ const requestPayload = extractPayload(cleanReq);
2663
+ return next(cleanReq).pipe(tap((event) => {
2664
+ if (event instanceof HttpResponse) {
2665
+ const call = {
2666
+ timestamp: startTime,
2667
+ method: cleanReq.method,
2668
+ url: cleanReq.urlWithParams,
2669
+ status: event.status,
2670
+ durationMs: Date.now() - startTime,
2671
+ requestPayload,
2672
+ responseHeaders: filterHeaders(event.headers, headerFilter),
2673
+ headerFilter,
2674
+ };
2675
+ blackbox.trackHttpCall(call);
2676
+ }
2677
+ }), catchError((error) => {
2678
+ const call = {
2679
+ timestamp: startTime,
2680
+ method: cleanReq.method,
2681
+ url: cleanReq.urlWithParams,
2682
+ status: error.status ?? 0,
2683
+ durationMs: Date.now() - startTime,
2684
+ requestPayload,
2685
+ headerFilter,
2686
+ };
2687
+ blackbox.trackHttpCall(call);
2688
+ return throwError(() => error);
2689
+ }));
2690
+ };
2691
+ // ─── Helpers ─────────────────────────────────────────────────────────
2692
+ /**
2693
+ * Extract the request body in a serialisable form.
2694
+ * @internal
2695
+ */
2696
+ function extractPayload(req) {
2697
+ if (req.body === null || req.body === undefined)
2698
+ return undefined;
2699
+ // Already serialisable (object / string / number)
2700
+ if (typeof req.body !== 'object')
2701
+ return req.body;
2702
+ try {
2703
+ // Deep-clone to avoid mutation issues
2704
+ return JSON.parse(JSON.stringify(req.body));
2705
+ }
2706
+ catch {
2707
+ return '[unserializable]';
2708
+ }
2709
+ }
2710
+ /**
2711
+ * Filter response headers using a regex pattern.
2712
+ * Returns undefined if no pattern is provided or no headers match.
2713
+ * @internal
2714
+ */
2715
+ function filterHeaders(headers, pattern) {
2716
+ if (!pattern)
2717
+ return undefined;
2718
+ try {
2719
+ const regex = new RegExp(pattern, 'i');
2720
+ const result = {};
2721
+ let found = false;
2722
+ for (const key of headers.keys()) {
2723
+ if (regex.test(key)) {
2724
+ const value = headers.get(key);
2725
+ if (value !== null) {
2726
+ result[key] = value;
2727
+ found = true;
2728
+ }
2729
+ }
2730
+ }
2731
+ return found ? result : undefined;
2732
+ }
2733
+ catch {
2734
+ // Invalid regex — skip header filtering
2735
+ return undefined;
2736
+ }
2737
+ }
2738
+
2739
+ /**
2740
+ * @module ng-ui-system/blackbox
2741
+ * Attribute directive for explicit UI action tracking.
2742
+ *
2743
+ * Apply `[uiTrack]` to any element to record clicks in the BlackBox session.
2744
+ * Works with the `UiBlackboxService` — if the service is not provided,
2745
+ * the directive is a no-op.
2746
+ *
2747
+ * @usageNotes
2748
+ * ```html
2749
+ * <!-- Link in a paragraph -->
2750
+ * <a [uiTrack]="'learn-more'" href="/docs">Scopri di più</a>
2751
+ *
2752
+ * <!-- Card with custom tracking ID -->
2753
+ * <div uiTrack="promo-card" (click)="openPromo()">
2754
+ * <h3>Offerta speciale</h3>
2755
+ * </div>
2756
+ *
2757
+ * <!-- Button outside of ui-button-area -->
2758
+ * <button [uiTrack]="'legacy-save'" (click)="save()">Salva</button>
2759
+ * ```
2760
+ */
2761
+ /**
2762
+ * Standalone directive that tracks click actions on the host element.
2763
+ *
2764
+ * The tracking identifier is provided via the `uiTrack` input binding.
2765
+ * Clicking the host element records an action in the active BlackBox session
2766
+ * with the element's tag, id, and truncated innerText.
2767
+ */
2768
+ class UiTrackDirective {
2769
+ constructor() {
2770
+ /**
2771
+ * Tracking identifier for this element.
2772
+ * Used as the `trackId` in the recorded action.
2773
+ */
2774
+ this.trackId = '';
2775
+ this.blackbox = inject(UiBlackboxService, { optional: true });
2776
+ this.el = inject((ElementRef));
2777
+ }
2778
+ onClick() {
2779
+ if (!this.blackbox || !this.trackId)
2780
+ return;
2781
+ const nativeEl = this.el.nativeElement;
2782
+ this.blackbox.trackAction(this.trackId, {
2783
+ tag: nativeEl.tagName?.toLowerCase() ?? 'unknown',
2784
+ id: nativeEl.id || undefined,
2785
+ text: nativeEl.innerText?.substring(0, 50)?.trim() || undefined,
2786
+ });
2787
+ }
2788
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiTrackDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2789
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: UiTrackDirective, isStandalone: true, selector: "[uiTrack]", inputs: { trackId: ["uiTrack", "trackId"] }, host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); }
2790
+ }
2791
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiTrackDirective, decorators: [{
2792
+ type: Directive,
2793
+ args: [{
2794
+ selector: '[uiTrack]',
2795
+ standalone: true,
2796
+ }]
2797
+ }], propDecorators: { trackId: [{
2798
+ type: Input,
2799
+ args: ['uiTrack']
2800
+ }], onClick: [{
2801
+ type: HostListener,
2802
+ args: ['click']
2803
+ }] } });
2804
+
2805
+ /**
2806
+ * ng-ui-system — BlackBox Observability entry point.
2807
+ *
2808
+ * @example
2809
+ * ```typescript
2810
+ * import {
2811
+ * UiBlackboxService,
2812
+ * UiBlackboxConfig,
2813
+ * UiTrackDirective,
2814
+ * uiBlackboxInterceptor,
2815
+ * } from '@gnggln/ng-ui-system';
2816
+ * ```
2817
+ */
2818
+ // Services
2819
+
2820
+ /**
2821
+ * Bootstrap del secondary entry `@gnggln/ng-ui-system/blackbox`. BlackBox observability.
2822
+ */
2823
+
2824
+ /**
2825
+ * Generated bundle index. Do not edit.
2826
+ */
2827
+
2828
+ export { UI_BLACKBOX_DEFAULTS, UiBlackboxDebuggerComponent, UiBlackboxDebuggerService, UiBlackboxFingerprintService, UiBlackboxJsonViewerComponent, UiBlackboxService, UiBlackboxStorageService, UiTrackDirective, uiBlackboxInterceptor };
2829
+ //# sourceMappingURL=gnggln-ng-ui-system-blackbox.mjs.map