@caribeng/ui 22.2.0 → 22.5.0

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 (285) hide show
  1. package/README.md +311 -231
  2. package/fesm2022/caribeng-ui-accordion.mjs +2 -2
  3. package/fesm2022/caribeng-ui-account-picker.mjs +533 -0
  4. package/fesm2022/caribeng-ui-aging.mjs +488 -0
  5. package/fesm2022/{caribeng-ui-caribe-alert.mjs → caribeng-ui-alert.mjs} +45 -45
  6. package/fesm2022/caribeng-ui-amount.mjs +249 -0
  7. package/fesm2022/caribeng-ui-anchor.mjs +2 -2
  8. package/fesm2022/{caribeng-ui-caribe-animation-lottie.mjs → caribeng-ui-animation-lottie.mjs} +41 -41
  9. package/fesm2022/caribeng-ui-audit-log.mjs +323 -0
  10. package/fesm2022/caribeng-ui-avatar.mjs +4 -4
  11. package/fesm2022/caribeng-ui-badge.mjs +8 -2
  12. package/fesm2022/caribeng-ui-breadcrumb.mjs +26 -9
  13. package/fesm2022/{caribeng-ui-caribe-button-group.mjs → caribeng-ui-button-group.mjs} +15 -15
  14. package/fesm2022/caribeng-ui-button.mjs +2 -2
  15. package/fesm2022/{caribeng-ui-caribe-card-content.mjs → caribeng-ui-card-content.mjs} +39 -39
  16. package/fesm2022/{caribeng-ui-caribe-card-move.mjs → caribeng-ui-card-move.mjs} +65 -65
  17. package/fesm2022/{caribeng-ui-caribe-card-primary.mjs → caribeng-ui-card-primary.mjs} +31 -31
  18. package/fesm2022/{caribeng-ui-caribe-card-select.mjs → caribeng-ui-card-select.mjs} +67 -67
  19. package/fesm2022/caribeng-ui-carousel.mjs +2 -2
  20. package/fesm2022/caribeng-ui-cascadeselect.mjs +45 -9
  21. package/fesm2022/caribeng-ui-chart.mjs +8 -5
  22. package/fesm2022/caribeng-ui-chat.mjs +1303 -0
  23. package/fesm2022/{caribeng-ui-caribe-coachmark.mjs → caribeng-ui-coachmark.mjs} +85 -85
  24. package/fesm2022/caribeng-ui-code-block.mjs +332 -0
  25. package/fesm2022/caribeng-ui-colorpicker.mjs +99 -5
  26. package/fesm2022/caribeng-ui-command-palette.mjs +476 -0
  27. package/fesm2022/caribeng-ui-comments.mjs +864 -0
  28. package/fesm2022/caribeng-ui-confirmdialog.mjs +78 -13
  29. package/fesm2022/caribeng-ui-confirmpopup.mjs +91 -16
  30. package/fesm2022/caribeng-ui-contextmenu.mjs +8 -4
  31. package/fesm2022/caribeng-ui-copy-button.mjs +230 -0
  32. package/fesm2022/caribeng-ui-datagrid.mjs +1635 -0
  33. package/fesm2022/caribeng-ui-dataview.mjs +6 -3
  34. package/fesm2022/caribeng-ui-datepicker.mjs +2 -2
  35. package/fesm2022/caribeng-ui-dock.mjs +52 -12
  36. package/fesm2022/caribeng-ui-document.mjs +495 -0
  37. package/fesm2022/{caribeng-ui-caribe-dots-indicator.mjs → caribeng-ui-dots-indicator.mjs} +41 -41
  38. package/fesm2022/caribeng-ui-drawer.mjs +9 -4
  39. package/fesm2022/caribeng-ui-dynamic-form.mjs +1581 -0
  40. package/fesm2022/caribeng-ui-editor.mjs +2324 -1311
  41. package/fesm2022/{caribeng-ui-caribe-expansion-panel.mjs → caribeng-ui-expansion-panel.mjs} +39 -39
  42. package/fesm2022/{caribeng-ui-caribe-fab-menu.mjs → caribeng-ui-fab-menu.mjs} +75 -75
  43. package/fesm2022/caribeng-ui-fieldset.mjs +2 -2
  44. package/fesm2022/caribeng-ui-file-card.mjs +134 -17
  45. package/fesm2022/{caribeng-ui-caribe-file-download.mjs → caribeng-ui-file-download.mjs} +63 -63
  46. package/fesm2022/caribeng-ui-financial-report.mjs +380 -0
  47. package/fesm2022/{caribeng-ui-caribe-float-menu.mjs → caribeng-ui-float-menu.mjs} +31 -31
  48. package/fesm2022/caribeng-ui-floatlabel.mjs +2 -2
  49. package/fesm2022/caribeng-ui-flow.mjs +1508 -0
  50. package/fesm2022/caribeng-ui-folder-card.mjs +122 -12
  51. package/fesm2022/{caribeng-ui-caribe-footer.mjs → caribeng-ui-footer.mjs} +19 -19
  52. package/fesm2022/caribeng-ui-gantt.mjs +1094 -0
  53. package/fesm2022/{caribeng-ui-caribe-header.mjs → caribeng-ui-header.mjs} +49 -49
  54. package/fesm2022/{caribeng-ui-caribe-icon-button.mjs → caribeng-ui-icon-button.mjs} +23 -23
  55. package/fesm2022/caribeng-ui-image-cropper.mjs +671 -0
  56. package/fesm2022/caribeng-ui-import-wizard.mjs +746 -0
  57. package/fesm2022/caribeng-ui-infinite-scroll.mjs +226 -0
  58. package/fesm2022/caribeng-ui-inlinemessage.mjs +2 -2
  59. package/fesm2022/caribeng-ui-input-number.mjs +22 -6
  60. package/fesm2022/{caribeng-ui-caribe-input-search.mjs → caribeng-ui-input-search.mjs} +65 -65
  61. package/fesm2022/{caribeng-ui-caribe-input-token.mjs → caribeng-ui-input-token.mjs} +59 -59
  62. package/fesm2022/caribeng-ui-inputmask.mjs +8 -2
  63. package/fesm2022/caribeng-ui-inputotp.mjs +2 -2
  64. package/fesm2022/caribeng-ui-journal-entry.mjs +340 -0
  65. package/fesm2022/caribeng-ui-kanban.mjs +4612 -0
  66. package/fesm2022/caribeng-ui-kbd.mjs +122 -0
  67. package/fesm2022/caribeng-ui-kpi.mjs +189 -0
  68. package/fesm2022/{caribeng-ui-caribe-link.mjs → caribeng-ui-link.mjs} +37 -37
  69. package/fesm2022/caribeng-ui-masonry.mjs +93 -0
  70. package/fesm2022/caribeng-ui-media-player.mjs +821 -0
  71. package/fesm2022/caribeng-ui-megamenu.mjs +13 -9
  72. package/fesm2022/caribeng-ui-mentions.mjs +495 -0
  73. package/fesm2022/caribeng-ui-menu.mjs +8 -4
  74. package/fesm2022/caribeng-ui-menubar.mjs +9 -5
  75. package/fesm2022/caribeng-ui-message.mjs +6 -3
  76. package/fesm2022/caribeng-ui-notification-center.mjs +636 -0
  77. package/fesm2022/{caribeng-ui-caribe-off-canvas.mjs → caribeng-ui-off-canvas.mjs} +55 -55
  78. package/fesm2022/{caribeng-ui-caribe-onboarding.mjs → caribeng-ui-onboarding.mjs} +105 -105
  79. package/fesm2022/caribeng-ui-orderlist.mjs +2 -2
  80. package/fesm2022/caribeng-ui-organizationchart.mjs +6 -10
  81. package/fesm2022/caribeng-ui-panel.mjs +6 -6
  82. package/fesm2022/caribeng-ui-panelmenu.mjs +9 -6
  83. package/fesm2022/caribeng-ui-pdfviewer.mjs +1755 -25
  84. package/fesm2022/caribeng-ui-period-picker.mjs +637 -0
  85. package/fesm2022/caribeng-ui-phone-input.mjs +688 -0
  86. package/fesm2022/caribeng-ui-picklist.mjs +2 -2
  87. package/fesm2022/caribeng-ui-pivot.mjs +419 -0
  88. package/fesm2022/caribeng-ui-pricing.mjs +263 -69
  89. package/fesm2022/caribeng-ui-qrcode.mjs +564 -0
  90. package/fesm2022/caribeng-ui-rating.mjs +14 -17
  91. package/fesm2022/caribeng-ui-reconciliation.mjs +386 -0
  92. package/fesm2022/caribeng-ui-ripple.mjs +2 -0
  93. package/fesm2022/caribeng-ui-scheduler.mjs +1027 -0
  94. package/fesm2022/caribeng-ui-scrolltop.mjs +3 -1
  95. package/fesm2022/caribeng-ui-select.mjs +2 -2
  96. package/fesm2022/{caribeng-ui-caribe-shortcut.mjs → caribeng-ui-shortcut.mjs} +29 -29
  97. package/fesm2022/caribeng-ui-sidebar.mjs +573 -0
  98. package/fesm2022/caribeng-ui-signature-pad.mjs +654 -0
  99. package/fesm2022/{caribeng-ui-caribe-status.mjs → caribeng-ui-status.mjs} +11 -11
  100. package/fesm2022/{caribeng-ui-caribe-step-indicator.mjs → caribeng-ui-step-indicator.mjs} +53 -53
  101. package/fesm2022/caribeng-ui-stepper.mjs +12 -5
  102. package/fesm2022/caribeng-ui-steps.mjs +2 -2
  103. package/fesm2022/caribeng-ui-table.mjs +333 -19
  104. package/fesm2022/caribeng-ui-tabmenu.mjs +10 -6
  105. package/fesm2022/caribeng-ui-tabs.mjs +2 -2
  106. package/fesm2022/{caribeng-ui-caribe-tag-button.mjs → caribeng-ui-tag-button.mjs} +25 -25
  107. package/fesm2022/caribeng-ui-tag.mjs +2 -2
  108. package/fesm2022/caribeng-ui-tieredmenu.mjs +111 -21
  109. package/fesm2022/{caribeng-ui-caribe-tracker.mjs → caribeng-ui-tracker.mjs} +29 -29
  110. package/fesm2022/{caribeng-ui-caribe-transaction-status.mjs → caribeng-ui-transaction-status.mjs} +11 -11
  111. package/fesm2022/caribeng-ui-treeselect.mjs +44 -9
  112. package/fesm2022/caribeng-ui-treetable.mjs +236 -17
  113. package/fesm2022/{caribeng-ui-caribe-voucher.mjs → caribeng-ui-voucher.mjs} +45 -45
  114. package/fesm2022/caribeng-ui-xlsx.mjs +486 -0
  115. package/fesm2022/caribeng-ui.mjs +1568 -1
  116. package/package.json +258 -178
  117. package/schematics/collection.json +5 -0
  118. package/schematics/migrate-from-primeng/core-icons.json +1 -0
  119. package/schematics/migrate-from-primeng/icons.js +269 -0
  120. package/schematics/migrate-from-primeng/index.js +290 -0
  121. package/schematics/migrate-from-primeng/mappings/button.js +93 -0
  122. package/schematics/migrate-from-primeng/mappings/card.js +33 -0
  123. package/schematics/migrate-from-primeng/mappings/checkbox.js +44 -0
  124. package/schematics/migrate-from-primeng/mappings/confirm.js +221 -0
  125. package/schematics/migrate-from-primeng/mappings/datepicker.js +121 -0
  126. package/schematics/migrate-from-primeng/mappings/dialog.js +84 -0
  127. package/schematics/migrate-from-primeng/mappings/index.js +88 -0
  128. package/schematics/migrate-from-primeng/mappings/inputnumber.js +62 -0
  129. package/schematics/migrate-from-primeng/mappings/inputtext.js +60 -0
  130. package/schematics/migrate-from-primeng/mappings/menu.js +468 -0
  131. package/schematics/migrate-from-primeng/mappings/multiselect.js +49 -0
  132. package/schematics/migrate-from-primeng/mappings/radiobutton.js +22 -0
  133. package/schematics/migrate-from-primeng/mappings/rules.js +206 -0
  134. package/schematics/migrate-from-primeng/mappings/select.js +97 -0
  135. package/schematics/migrate-from-primeng/mappings/service-calls.js +100 -0
  136. package/schematics/migrate-from-primeng/mappings/table.js +613 -0
  137. package/schematics/migrate-from-primeng/mappings/tabs.js +225 -0
  138. package/schematics/migrate-from-primeng/mappings/tag.js +29 -0
  139. package/schematics/migrate-from-primeng/mappings/textarea.js +44 -0
  140. package/schematics/migrate-from-primeng/mappings/toast.js +173 -0
  141. package/schematics/migrate-from-primeng/mappings/toggleswitch.js +37 -0
  142. package/schematics/migrate-from-primeng/mappings/tooltip.js +44 -0
  143. package/schematics/migrate-from-primeng/mappings/types.js +10 -0
  144. package/schematics/migrate-from-primeng/report.js +79 -0
  145. package/schematics/migrate-from-primeng/schema.js +2 -0
  146. package/schematics/migrate-from-primeng/schema.json +34 -0
  147. package/schematics/migrate-from-primeng/template.js +387 -0
  148. package/schematics/migrate-from-primeng/todo.js +5 -0
  149. package/schematics/migrate-from-primeng/typescript.js +442 -0
  150. package/schematics/migrate-from-primeng/workspace.js +117 -0
  151. package/schematics/ng-add/index.js +16 -1
  152. package/types/caribeng-ui-account-picker.d.ts +142 -0
  153. package/types/caribeng-ui-aging.d.ts +123 -0
  154. package/types/{caribeng-ui-caribe-alert.d.ts → caribeng-ui-alert.d.ts} +2 -2
  155. package/types/caribeng-ui-amount.d.ts +82 -0
  156. package/types/{caribeng-ui-caribe-animation-lottie.d.ts → caribeng-ui-animation-lottie.d.ts} +2 -2
  157. package/types/caribeng-ui-audit-log.d.ts +86 -0
  158. package/types/caribeng-ui-badge.d.ts +3 -1
  159. package/types/caribeng-ui-breadcrumb.d.ts +9 -1
  160. package/types/{caribeng-ui-caribe-button-group.d.ts → caribeng-ui-button-group.d.ts} +2 -2
  161. package/types/{caribeng-ui-caribe-card-content.d.ts → caribeng-ui-card-content.d.ts} +2 -2
  162. package/types/{caribeng-ui-caribe-card-move.d.ts → caribeng-ui-card-move.d.ts} +1 -1
  163. package/types/{caribeng-ui-caribe-card-primary.d.ts → caribeng-ui-card-primary.d.ts} +2 -2
  164. package/types/{caribeng-ui-caribe-card-select.d.ts → caribeng-ui-card-select.d.ts} +2 -2
  165. package/types/caribeng-ui-cascadeselect.d.ts +6 -0
  166. package/types/caribeng-ui-chart.d.ts +3 -1
  167. package/types/caribeng-ui-chat.d.ts +299 -0
  168. package/types/{caribeng-ui-caribe-coachmark.d.ts → caribeng-ui-coachmark.d.ts} +2 -2
  169. package/types/caribeng-ui-code-block.d.ts +65 -0
  170. package/types/caribeng-ui-colorpicker.d.ts +5 -1
  171. package/types/caribeng-ui-command-palette.d.ts +133 -0
  172. package/types/caribeng-ui-comments.d.ts +214 -0
  173. package/types/caribeng-ui-confirmdialog.d.ts +34 -2
  174. package/types/caribeng-ui-confirmpopup.d.ts +39 -2
  175. package/types/caribeng-ui-contextmenu.d.ts +7 -0
  176. package/types/caribeng-ui-copy-button.d.ts +90 -0
  177. package/types/caribeng-ui-datagrid.d.ts +269 -0
  178. package/types/caribeng-ui-dataview.d.ts +2 -0
  179. package/types/caribeng-ui-dock.d.ts +13 -1
  180. package/types/caribeng-ui-document.d.ts +138 -0
  181. package/types/{caribeng-ui-caribe-dots-indicator.d.ts → caribeng-ui-dots-indicator.d.ts} +2 -2
  182. package/types/caribeng-ui-drawer.d.ts +3 -1
  183. package/types/caribeng-ui-dynamic-form.d.ts +409 -0
  184. package/types/caribeng-ui-editor.d.ts +110 -12
  185. package/types/{caribeng-ui-caribe-expansion-panel.d.ts → caribeng-ui-expansion-panel.d.ts} +1 -1
  186. package/types/{caribeng-ui-caribe-fab-menu.d.ts → caribeng-ui-fab-menu.d.ts} +2 -2
  187. package/types/caribeng-ui-file-card.d.ts +27 -2
  188. package/types/{caribeng-ui-caribe-file-download.d.ts → caribeng-ui-file-download.d.ts} +1 -1
  189. package/types/caribeng-ui-financial-report.d.ts +102 -0
  190. package/types/{caribeng-ui-caribe-float-menu.d.ts → caribeng-ui-float-menu.d.ts} +2 -2
  191. package/types/caribeng-ui-flow.d.ts +228 -0
  192. package/types/caribeng-ui-folder-card.d.ts +27 -5
  193. package/types/{caribeng-ui-caribe-footer.d.ts → caribeng-ui-footer.d.ts} +2 -2
  194. package/types/caribeng-ui-gantt.d.ts +244 -0
  195. package/types/{caribeng-ui-caribe-header.d.ts → caribeng-ui-header.d.ts} +2 -2
  196. package/types/{caribeng-ui-caribe-icon-button.d.ts → caribeng-ui-icon-button.d.ts} +2 -2
  197. package/types/{caribeng-ui-caribe-illustration.d.ts → caribeng-ui-illustration.d.ts} +1 -1
  198. package/types/caribeng-ui-image-cropper.d.ts +169 -0
  199. package/types/caribeng-ui-import-wizard.d.ts +149 -0
  200. package/types/caribeng-ui-infinite-scroll.d.ts +75 -0
  201. package/types/caribeng-ui-input-number.d.ts +1 -1
  202. package/types/{caribeng-ui-caribe-input-search.d.ts → caribeng-ui-input-search.d.ts} +2 -2
  203. package/types/{caribeng-ui-caribe-input-token.d.ts → caribeng-ui-input-token.d.ts} +2 -2
  204. package/types/caribeng-ui-inputmask.d.ts +3 -1
  205. package/types/caribeng-ui-journal-entry.d.ts +110 -0
  206. package/types/caribeng-ui-kanban.d.ts +783 -0
  207. package/types/caribeng-ui-kbd.d.ts +36 -0
  208. package/types/caribeng-ui-kpi.d.ts +64 -0
  209. package/types/{caribeng-ui-caribe-link.d.ts → caribeng-ui-link.d.ts} +2 -2
  210. package/types/{caribeng-ui-caribe-logo.d.ts → caribeng-ui-logo.d.ts} +1 -1
  211. package/types/caribeng-ui-masonry.d.ts +32 -0
  212. package/types/caribeng-ui-media-player.d.ts +140 -0
  213. package/types/caribeng-ui-megamenu.d.ts +11 -0
  214. package/types/caribeng-ui-mentions.d.ts +127 -0
  215. package/types/caribeng-ui-menu.d.ts +7 -0
  216. package/types/caribeng-ui-menubar.d.ts +7 -0
  217. package/types/caribeng-ui-message.d.ts +2 -0
  218. package/types/caribeng-ui-notification-center.d.ts +153 -0
  219. package/types/{caribeng-ui-caribe-off-canvas.d.ts → caribeng-ui-off-canvas.d.ts} +2 -2
  220. package/types/{caribeng-ui-caribe-onboarding.d.ts → caribeng-ui-onboarding.d.ts} +2 -2
  221. package/types/caribeng-ui-panelmenu.d.ts +7 -0
  222. package/types/caribeng-ui-pdfviewer.d.ts +245 -5
  223. package/types/caribeng-ui-period-picker.d.ts +148 -0
  224. package/types/caribeng-ui-phone-input.d.ts +168 -0
  225. package/types/caribeng-ui-pivot.d.ts +116 -0
  226. package/types/caribeng-ui-pricing.d.ts +41 -4
  227. package/types/caribeng-ui-qrcode.d.ts +88 -0
  228. package/types/caribeng-ui-reconciliation.d.ts +109 -0
  229. package/types/caribeng-ui-scheduler.d.ts +188 -0
  230. package/types/{caribeng-ui-caribe-shortcut.d.ts → caribeng-ui-shortcut.d.ts} +2 -2
  231. package/types/caribeng-ui-sidebar.d.ts +167 -0
  232. package/types/caribeng-ui-signature-pad.d.ts +151 -0
  233. package/types/{caribeng-ui-caribe-status.d.ts → caribeng-ui-status.d.ts} +2 -2
  234. package/types/{caribeng-ui-caribe-step-indicator.d.ts → caribeng-ui-step-indicator.d.ts} +2 -2
  235. package/types/caribeng-ui-stepper.d.ts +2 -0
  236. package/types/caribeng-ui-table.d.ts +67 -3
  237. package/types/caribeng-ui-tabmenu.d.ts +7 -0
  238. package/types/{caribeng-ui-caribe-tag-button.d.ts → caribeng-ui-tag-button.d.ts} +2 -2
  239. package/types/caribeng-ui-tieredmenu.d.ts +17 -1
  240. package/types/{caribeng-ui-caribe-tracker.d.ts → caribeng-ui-tracker.d.ts} +2 -2
  241. package/types/{caribeng-ui-caribe-transaction-status.d.ts → caribeng-ui-transaction-status.d.ts} +2 -2
  242. package/types/caribeng-ui-treeselect.d.ts +6 -0
  243. package/types/caribeng-ui-treetable.d.ts +67 -2
  244. package/types/{caribeng-ui-caribe-voucher.d.ts → caribeng-ui-voucher.d.ts} +2 -2
  245. package/types/caribeng-ui-xlsx.d.ts +79 -0
  246. package/types/caribeng-ui.d.ts +985 -2
  247. package/fesm2022/caribeng-ui-caribe-calendar.mjs +0 -92
  248. package/fesm2022/caribeng-ui-caribe-circle-loading.mjs +0 -130
  249. package/fesm2022/caribeng-ui-caribe-data-table.mjs +0 -94
  250. package/fesm2022/caribeng-ui-caribe-file-select.mjs +0 -88
  251. package/fesm2022/caribeng-ui-caribe-inline-alert.mjs +0 -100
  252. package/fesm2022/caribeng-ui-caribe-input-date.mjs +0 -138
  253. package/fesm2022/caribeng-ui-caribe-input-select.mjs +0 -154
  254. package/fesm2022/caribeng-ui-caribe-loader.mjs +0 -48
  255. package/fesm2022/caribeng-ui-caribe-modal.mjs +0 -119
  256. package/fesm2022/caribeng-ui-caribe-pictogram.mjs +0 -52
  257. package/fesm2022/caribeng-ui-caribe-pop-over.mjs +0 -104
  258. package/fesm2022/caribeng-ui-caribe-preloader.mjs +0 -57
  259. package/fesm2022/caribeng-ui-caribe-progress-bar.mjs +0 -84
  260. package/fesm2022/caribeng-ui-caribe-stroke.mjs +0 -69
  261. package/fesm2022/caribeng-ui-caribe-switch.mjs +0 -182
  262. package/styles/caribeng.css +0 -380
  263. package/styles/scss/_mixins.scss +0 -46
  264. package/styles/scss/_overlay.scss +0 -15
  265. package/styles/scss/_tokens.scss +0 -267
  266. package/styles/scss/_utilities.scss +0 -73
  267. package/styles/scss/_variables.scss +0 -33
  268. package/styles/scss/index.scss +0 -16
  269. package/types/caribeng-ui-caribe-calendar.d.ts +0 -27
  270. package/types/caribeng-ui-caribe-circle-loading.d.ts +0 -28
  271. package/types/caribeng-ui-caribe-data-table.d.ts +0 -26
  272. package/types/caribeng-ui-caribe-file-select.d.ts +0 -31
  273. package/types/caribeng-ui-caribe-inline-alert.d.ts +0 -28
  274. package/types/caribeng-ui-caribe-input-date.d.ts +0 -40
  275. package/types/caribeng-ui-caribe-input-select.d.ts +0 -47
  276. package/types/caribeng-ui-caribe-loader.d.ts +0 -22
  277. package/types/caribeng-ui-caribe-modal.d.ts +0 -21
  278. package/types/caribeng-ui-caribe-pictogram.d.ts +0 -15
  279. package/types/caribeng-ui-caribe-pop-over.d.ts +0 -17
  280. package/types/caribeng-ui-caribe-preloader.d.ts +0 -24
  281. package/types/caribeng-ui-caribe-progress-bar.d.ts +0 -28
  282. package/types/caribeng-ui-caribe-stroke.d.ts +0 -17
  283. package/types/caribeng-ui-caribe-switch.d.ts +0 -55
  284. /package/fesm2022/{caribeng-ui-caribe-illustration.mjs → caribeng-ui-illustration.mjs} +0 -0
  285. /package/fesm2022/{caribeng-ui-caribe-logo.mjs → caribeng-ui-logo.mjs} +0 -0
@@ -0,0 +1,1303 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, booleanAttribute, model, output, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, inject, DestroyRef, viewChild, signal, afterNextRender, effect, untracked, Injector } from '@angular/core';
3
+ import { injectCbTranslation, cbFormat } from '@caribeng/ui';
4
+ import { CbCodeBlock } from '@caribeng/ui/code-block';
5
+ import { CbCopyButton } from '@caribeng/ui/copy-button';
6
+ import { CbIcon } from '@caribeng/ui/icon';
7
+ import { DOCUMENT } from '@angular/common';
8
+
9
+ /*
10
+ * Markdown ligero y seguro de CaribeNG para mensajes de chat.
11
+ *
12
+ * Todo el texto de entrada se escapa: el HTML que llega se muestra como texto,
13
+ * nunca se interpreta. Solo se generan etiquetas conocidas y enlaces a
14
+ * http(s), mailto, rutas relativas o anclas. Tolera markdown incompleto (lo
15
+ * habitual mientras llega una respuesta en streaming): un bloque de código sin
16
+ * cerrar se pinta igualmente como código.
17
+ *
18
+ * Soporta: párrafos (un salto de línea = <br>), encabezados, negrita, cursiva,
19
+ * tachado, código en línea y en bloque (con lenguaje), enlaces, URLs sueltas,
20
+ * listas con viñetas, numeradas y de tareas, citas, separadores y tablas.
21
+ *
22
+ * Solo emite etiquetas y atributos que conserva el sanitizador de Angular
23
+ * (`[innerHTML]`): nada de `style` ni `<input>`.
24
+ */
25
+ function escapeHtml(text) {
26
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
27
+ }
28
+ /** URL permitida en un enlace, o `null`. */
29
+ function cbSafeUrl(url) {
30
+ const trimmed = url.trim();
31
+ if (/^(https?:|mailto:)/i.test(trimmed))
32
+ return trimmed;
33
+ if (/^[a-z][a-z\d+.-]*:/i.test(trimmed))
34
+ return null;
35
+ // Relativas, anclas y consultas; nunca "//dominio" (abriría otro origen sin esquema explícito).
36
+ if (/^(\/(?!\/)|#|\?|\.{1,2}\/)/.test(trimmed) || /^[\w-][\w./-]*$/.test(trimmed))
37
+ return trimmed;
38
+ return null;
39
+ }
40
+ function link(href, label) {
41
+ const external = /^(https?:)?\/\//i.test(href);
42
+ const attrs = external ? ' target="_blank" rel="noopener noreferrer"' : '';
43
+ return `<a href="${escapeHtml(href)}"${attrs}>${label}</a>`;
44
+ }
45
+ /** Formato en línea sobre texto aún sin escapar. */
46
+ function inline(text) {
47
+ const tokens = [];
48
+ const stash = (html) => `\u0000${tokens.push(html) - 1}\u0000`;
49
+ // 1) Código en línea: su contenido no se formatea.
50
+ let out = text.replace(/(`+)([\s\S]*?[^`])\1(?!`)/g, (_m, _ticks, code) => stash(`<code>${escapeHtml(code.trim() === '' ? code : code.replace(/^ (.*) $/, '$1'))}</code>`));
51
+ // 2) Enlaces [texto](url).
52
+ out = out.replace(/\[([^\]\n]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, (m, label, url) => {
53
+ const safe = cbSafeUrl(url);
54
+ return safe ? stash(link(safe, formatEmphasis(escapeHtml(label)))) : m;
55
+ });
56
+ // 3) URLs sueltas.
57
+ out = out.replace(/\bhttps?:\/\/[^\s<>()]+[^\s<>().,;:!?'"\]]/g, (url) => stash(link(url, escapeHtml(url))));
58
+ // 4) Resto: escapar y aplicar énfasis.
59
+ out = formatEmphasis(escapeHtml(out));
60
+ // Los tokens pueden contener otros (código dentro del texto de un enlace).
61
+ for (let depth = 0; depth < 4 && out.includes('\u0000'); depth++) {
62
+ out = out.replace(/\u0000(\d+)\u0000/g, (_m, i) => tokens[Number(i)]);
63
+ }
64
+ return out;
65
+ }
66
+ /** Negrita, cursiva y tachado sobre texto ya escapado. */
67
+ function formatEmphasis(html) {
68
+ return html
69
+ .replace(/\*\*\*(?=\S)([\s\S]*?\S)\*\*\*/g, '<strong><em>$1</em></strong>')
70
+ .replace(/\*\*(?=\S)([\s\S]*?\S)\*\*/g, '<strong>$1</strong>')
71
+ .replace(/(^|[^\w])__(?=\S)([\s\S]*?\S)__(?!\w)/g, '$1<strong>$2</strong>')
72
+ .replace(/(^|[^*\w])\*([^\s*](?:[^*\n]*?[^\s*])?)\*(?!\*)/g, '$1<em>$2</em>')
73
+ .replace(/(^|[^\w])_([^\s_](?:[^_\n]*?[^\s_])?)_(?!\w)/g, '$1<em>$2</em>')
74
+ .replace(/~~(?=\S)([\s\S]*?\S)~~/g, '<del>$1</del>');
75
+ }
76
+ const FENCE = /^ {0,3}(`{3,}|~{3,})\s*([\w#+.-]*)\s*$/;
77
+ const HEADING = /^ {0,3}(#{1,6})\s+(.*?)\s*#*\s*$/;
78
+ const HR = /^ {0,3}([-*_])(\s*\1){2,}\s*$/;
79
+ const QUOTE = /^ {0,3}>\s?/;
80
+ const BULLET = /^(\s*)([-*+])\s+(.*)$/;
81
+ const ORDERED = /^(\s*)(\d{1,9})[.)]\s+(.*)$/;
82
+ const TABLE_SEP = /^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/;
83
+ function splitRow(line) {
84
+ let row = line.trim();
85
+ if (row.startsWith('|'))
86
+ row = row.slice(1);
87
+ if (row.endsWith('|') && !row.endsWith('\\|'))
88
+ row = row.slice(0, -1);
89
+ return row.split(/(?<!\\)\|/).map((cell) => cell.trim().replace(/\\\|/g, '|'));
90
+ }
91
+ function isBlockStart(line) {
92
+ return FENCE.test(line) || HEADING.test(line) || HR.test(line) || QUOTE.test(line) || BULLET.test(line) || ORDERED.test(line);
93
+ }
94
+ function renderList(lines, start) {
95
+ const ordered = ORDERED.test(lines[start]);
96
+ const pattern = ordered ? ORDERED : BULLET;
97
+ const first = lines[start].match(pattern);
98
+ const indent = first[1].length;
99
+ const items = [];
100
+ let i = start;
101
+ while (i < lines.length) {
102
+ const m = lines[i].match(pattern);
103
+ if (!m || m[1].length !== indent)
104
+ break;
105
+ let body = m[3];
106
+ i++;
107
+ // Líneas de continuación y sublistas más indentadas.
108
+ const nested = [];
109
+ while (i < lines.length && lines[i].trim() !== '' && !(lines[i].match(pattern)?.[1].length === indent) && (/^\s+/.test(lines[i]) || !isBlockStart(lines[i]))) {
110
+ nested.push(lines[i]);
111
+ i++;
112
+ }
113
+ let sub = '';
114
+ const subStart = nested.findIndex((l) => BULLET.test(l) || ORDERED.test(l));
115
+ if (subStart >= 0) {
116
+ body += nested.slice(0, subStart).map((l) => '\n' + l.trim()).join('');
117
+ sub = renderBlocks(nested.slice(subStart).map((l) => l.replace(new RegExp(`^\\s{0,${indent + 4}}`), '')));
118
+ }
119
+ else {
120
+ body += nested.map((l) => '\n' + l.trim()).join('');
121
+ }
122
+ const task = !ordered ? body.match(/^\[([ xX])\]\s+(.*)$/s) : null;
123
+ const content = task
124
+ ? `<span class="cb-md-check">${task[1].trim() ? '☑' : '☐'}</span> ${inline(task[2]).replace(/\n/g, '<br>')}`
125
+ : inline(body).replace(/\n/g, '<br>');
126
+ items.push(`<li${task ? ' class="cb-md-task"' : ''}>${content}${sub}</li>`);
127
+ if (i < lines.length && lines[i].trim() === '' && lines[i + 1]?.match(pattern)?.[1].length === indent)
128
+ i++;
129
+ }
130
+ const startAttr = ordered && first[2] !== '1' ? ` start="${Number(first[2])}"` : '';
131
+ return { html: ordered ? `<ol${startAttr}>${items.join('')}</ol>` : `<ul>${items.join('')}</ul>`, next: i };
132
+ }
133
+ /**
134
+ * Pinta los bloques. Con `sink`, los bloques de código del nivel superior no se
135
+ * pintan como HTML sino que se entregan aparte (para un componente de código).
136
+ */
137
+ function renderBlocks(lines, sink) {
138
+ const out = [];
139
+ const flush = () => {
140
+ if (sink && out.length)
141
+ sink.push({ kind: 'html', html: out.join('') });
142
+ out.length = 0;
143
+ };
144
+ let i = 0;
145
+ while (i < lines.length) {
146
+ const line = lines[i];
147
+ if (line.trim() === '') {
148
+ i++;
149
+ continue;
150
+ }
151
+ const fence = line.match(FENCE);
152
+ if (fence) {
153
+ const marker = fence[1];
154
+ const lang = fence[2];
155
+ const code = [];
156
+ i++;
157
+ while (i < lines.length && !new RegExp(`^ {0,3}${marker[0]}{${marker.length},}\\s*$`).test(lines[i]))
158
+ code.push(lines[i++]);
159
+ i++; // cierre (o fin del texto si llega incompleto)
160
+ if (sink) {
161
+ flush();
162
+ sink.push({ kind: 'code', code: code.join('\n'), language: lang });
163
+ continue;
164
+ }
165
+ const cls = lang ? ` class="language-${escapeHtml(lang)}"` : '';
166
+ const label = lang ? `<span class="cb-md-lang">${escapeHtml(lang)}</span>` : '';
167
+ out.push(`<pre>${label}<code${cls}>${escapeHtml(code.join('\n'))}</code></pre>`);
168
+ continue;
169
+ }
170
+ const heading = line.match(HEADING);
171
+ if (heading) {
172
+ const level = heading[1].length;
173
+ out.push(`<h${level}>${inline(heading[2])}</h${level}>`);
174
+ i++;
175
+ continue;
176
+ }
177
+ if (HR.test(line)) {
178
+ out.push('<hr />');
179
+ i++;
180
+ continue;
181
+ }
182
+ if (QUOTE.test(line)) {
183
+ const quoted = [];
184
+ while (i < lines.length && (QUOTE.test(lines[i]) || (lines[i].trim() !== '' && !isBlockStart(lines[i]) && quoted.length))) {
185
+ quoted.push(lines[i].replace(QUOTE, ''));
186
+ i++;
187
+ }
188
+ out.push(`<blockquote>${renderBlocks(quoted)}</blockquote>`);
189
+ continue;
190
+ }
191
+ if (BULLET.test(line) || ORDERED.test(line)) {
192
+ const list = renderList(lines, i);
193
+ out.push(list.html);
194
+ i = list.next;
195
+ continue;
196
+ }
197
+ if (line.includes('|') && i + 1 < lines.length && TABLE_SEP.test(lines[i + 1]) && lines[i + 1].includes('-')) {
198
+ const header = splitRow(line);
199
+ const aligns = splitRow(lines[i + 1]).map((c) => (c.startsWith(':') && c.endsWith(':') ? 'center' : c.endsWith(':') ? 'right' : c.startsWith(':') ? 'left' : ''));
200
+ // Alineación con clases: el sanitizador de Angular elimina los atributos style.
201
+ const cell = (tag, text, col) => `<${tag}${aligns[col] ? ` class="cb-md-${aligns[col]}"` : ''}>${inline(text)}</${tag}>`;
202
+ const rows = [];
203
+ i += 2;
204
+ while (i < lines.length && lines[i].includes('|') && lines[i].trim() !== '') {
205
+ const cells = splitRow(lines[i]);
206
+ rows.push(`<tr>${header.map((_h, c) => cell('td', cells[c] ?? '', c)).join('')}</tr>`);
207
+ i++;
208
+ }
209
+ out.push(`<div class="cb-md-table"><table><thead><tr>${header.map((h, c) => cell('th', h, c)).join('')}</tr></thead>` +
210
+ `<tbody>${rows.join('')}</tbody></table></div>`);
211
+ continue;
212
+ }
213
+ // Párrafo: hasta una línea vacía o el inicio de otro bloque.
214
+ const para = [];
215
+ while (i < lines.length && lines[i].trim() !== '' && (para.length === 0 || !isBlockStart(lines[i])))
216
+ para.push(lines[i++].trim());
217
+ out.push(`<p>${inline(para.join('\n')).replace(/\n/g, '<br>')}</p>`);
218
+ }
219
+ const html = out.join('');
220
+ flush();
221
+ return html;
222
+ }
223
+ function toLines(markdown) {
224
+ return (markdown ?? '').replace(/\r\n?/g, '\n').split('\n');
225
+ }
226
+ /** Convierte markdown en HTML seguro (todo el HTML de entrada se escapa). */
227
+ function cbMarkdownToHtml(markdown) {
228
+ return renderBlocks(toLines(markdown));
229
+ }
230
+ /**
231
+ * Como `cbMarkdownToHtml`, pero separa los bloques de código del nivel
232
+ * superior para pintarlos con un componente (resaltado, copiar).
233
+ */
234
+ function cbMarkdownToBlocks(markdown) {
235
+ const blocks = [];
236
+ renderBlocks(toLines(markdown), blocks);
237
+ return blocks;
238
+ }
239
+
240
+ function formatSize$1(bytes) {
241
+ if (bytes === undefined)
242
+ return '';
243
+ if (bytes < 1024)
244
+ return `${bytes} B`;
245
+ if (bytes < 1024 * 1024)
246
+ return `${(bytes / 1024).toFixed(0)} KB`;
247
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
248
+ }
249
+ /**
250
+ * Mensaje de chat de CaribeNG: burbuja con autor, hora, avatar y estado de
251
+ * envío. `text` se pinta como markdown seguro y sus bloques de código con
252
+ * `cb-code-block` (resaltado y copiar). Admite adjuntos, valoración 👍/👎 y
253
+ * "Regenerar"; con `streaming` muestra un cursor mientras llega la respuesta.
254
+ * El contenido proyectado (p. ej. `cb-chat-reasoning`, `cb-chat-tool`) va
255
+ * antes del texto.
256
+ *
257
+ * @example
258
+ * ```html
259
+ * <cb-chat-message role="user" text="¿Cuál es mi saldo?" time="10:30" />
260
+ * <cb-chat-message role="assistant" [text]="respuesta" [streaming]="generando" copyable showFeedback [(feedback)]="voto" />
261
+ * ```
262
+ */
263
+ class CbChatMessage {
264
+ constructor() {
265
+ /** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
266
+ this.t = injectCbTranslation();
267
+ this.role = input('assistant', /* @ts-ignore */
268
+ ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
269
+ /** Contenido del mensaje (markdown por defecto). */
270
+ this.text = input('', /* @ts-ignore */
271
+ ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
272
+ /** Interpreta `text` como markdown seguro. Con `false` se muestra tal cual. */
273
+ this.markdown = input(true, { ...(ngDevMode ? { debugName: "markdown" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
274
+ /** Nombre del autor. Por defecto, "Tú" o "Asistente". */
275
+ this.author = input(undefined, /* @ts-ignore */
276
+ ...(ngDevMode ? [{ debugName: "author" }] : /* istanbul ignore next */ []));
277
+ /** Hora o fecha ya formateada ("10:30"). */
278
+ this.time = input('', /* @ts-ignore */
279
+ ...(ngDevMode ? [{ debugName: "time" }] : /* istanbul ignore next */ []));
280
+ /** Imagen del avatar. Sin ella, iniciales (usuario) o un icono (asistente). */
281
+ this.avatar = input('', /* @ts-ignore */
282
+ ...(ngDevMode ? [{ debugName: "avatar" }] : /* istanbul ignore next */ []));
283
+ this.showAvatar = input(true, { ...(ngDevMode ? { debugName: "showAvatar" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
284
+ this.showAuthor = input(false, { ...(ngDevMode ? { debugName: "showAuthor" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
285
+ /** Estado de envío (mensajes propios). `error` muestra "Reintentar". */
286
+ this.status = input(undefined, /* @ts-ignore */
287
+ ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
288
+ /** La respuesta sigue llegando: muestra un cursor y marca el mensaje como ocupado. */
289
+ this.streaming = input(false, { ...(ngDevMode ? { debugName: "streaming" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
290
+ /** Botón para copiar el texto (cuando termina de llegar). */
291
+ this.copyable = input(false, { ...(ngDevMode ? { debugName: "copyable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
292
+ /** Archivos adjuntos (las imágenes con URL, en miniatura). */
293
+ this.attachments = input([], /* @ts-ignore */
294
+ ...(ngDevMode ? [{ debugName: "attachments" }] : /* istanbul ignore next */ []));
295
+ /** Muestra los botones 👍 / 👎. */
296
+ this.showFeedback = input(false, { ...(ngDevMode ? { debugName: "showFeedback" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
297
+ /** Valoración elegida (bidireccional). Pulsar la misma la quita. */
298
+ this.feedback = model(null, /* @ts-ignore */
299
+ ...(ngDevMode ? [{ debugName: "feedback" }] : /* istanbul ignore next */ []));
300
+ /** Muestra el botón "Regenerar respuesta". */
301
+ this.regenerable = input(false, { ...(ngDevMode ? { debugName: "regenerable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
302
+ /** Se pulsó "Reintentar" en un mensaje con error. */
303
+ this.retry = output();
304
+ /** Se pulsó "Regenerar respuesta". */
305
+ this.regenerate = output();
306
+ this.blocks = computed(() => cbMarkdownToBlocks(this.text()), /* @ts-ignore */
307
+ ...(ngDevMode ? [{ debugName: "blocks" }] : /* istanbul ignore next */ []));
308
+ this.authorName = computed(() => this.author() ?? (this.role() === 'user' ? this.t().chatYou : this.t().chatAssistant), /* @ts-ignore */
309
+ ...(ngDevMode ? [{ debugName: "authorName" }] : /* istanbul ignore next */ []));
310
+ this.initials = computed(() => this.authorName()
311
+ .split(/\s+/)
312
+ .filter(Boolean)
313
+ .slice(0, 2)
314
+ .map((w) => w.charAt(0).toUpperCase())
315
+ .join(''), /* @ts-ignore */
316
+ ...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
317
+ this.label = computed(() => {
318
+ if (this.role() === 'system')
319
+ return null;
320
+ return this.time() ? `${this.authorName()}, ${this.time()}` : this.authorName();
321
+ }, /* @ts-ignore */
322
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
323
+ }
324
+ isImage(a) {
325
+ return !!a.type?.startsWith('image/');
326
+ }
327
+ size(a) {
328
+ return formatSize$1(a.size);
329
+ }
330
+ vote(value) {
331
+ this.feedback.set(this.feedback() === value ? null : value);
332
+ }
333
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatMessage, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
334
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbChatMessage, isStandalone: true, selector: "cb-chat-message", inputs: { role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, markdown: { classPropertyName: "markdown", publicName: "markdown", isSignal: true, isRequired: false, transformFunction: null }, author: { classPropertyName: "author", publicName: "author", isSignal: true, isRequired: false, transformFunction: null }, time: { classPropertyName: "time", publicName: "time", isSignal: true, isRequired: false, transformFunction: null }, avatar: { classPropertyName: "avatar", publicName: "avatar", isSignal: true, isRequired: false, transformFunction: null }, showAvatar: { classPropertyName: "showAvatar", publicName: "showAvatar", isSignal: true, isRequired: false, transformFunction: null }, showAuthor: { classPropertyName: "showAuthor", publicName: "showAuthor", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, copyable: { classPropertyName: "copyable", publicName: "copyable", isSignal: true, isRequired: false, transformFunction: null }, attachments: { classPropertyName: "attachments", publicName: "attachments", isSignal: true, isRequired: false, transformFunction: null }, showFeedback: { classPropertyName: "showFeedback", publicName: "showFeedback", isSignal: true, isRequired: false, transformFunction: null }, feedback: { classPropertyName: "feedback", publicName: "feedback", isSignal: true, isRequired: false, transformFunction: null }, regenerable: { classPropertyName: "regenerable", publicName: "regenerable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { feedback: "feedbackChange", retry: "retry", regenerate: "regenerate" }, host: { attributes: { "role": "article" }, properties: { "class.cb-chat-message--user": "role() === 'user'", "class.cb-chat-message--assistant": "role() === 'assistant'", "class.cb-chat-message--system": "role() === 'system'", "class.cb-chat-message--error": "status() === 'error'", "attr.aria-label": "label()", "attr.aria-busy": "streaming() ? \"true\" : null" }, classAttribute: "cb-chat-message" }, ngImport: i0, template: `
335
+ @if (role() !== 'system' && showAvatar()) {
336
+ <span class="cb-chat-message__avatar" aria-hidden="true">
337
+ @if (avatar()) {
338
+ <img [src]="avatar()" alt="" />
339
+ } @else if (role() === 'assistant') {
340
+ <cb-icon name="sparkles" [size]="16" />
341
+ } @else {
342
+ {{ initials() }}
343
+ }
344
+ </span>
345
+ }
346
+ <div class="cb-chat-message__body">
347
+ @if (role() !== 'system' && (showAuthor() || time())) {
348
+ <div class="cb-chat-message__meta" aria-hidden="true">
349
+ @if (showAuthor()) {
350
+ <span class="cb-chat-message__author">{{ authorName() }}</span>
351
+ }
352
+ @if (time()) {
353
+ <span class="cb-chat-message__time">{{ time() }}</span>
354
+ }
355
+ </div>
356
+ }
357
+ <ng-content select="cb-chat-reasoning, cb-chat-tool, [cb-chat-message-before]" />
358
+ @if (attachments().length) {
359
+ <ul class="cb-chat-message__attachments" role="list" [attr.aria-label]="t().chatAttachments">
360
+ @for (a of attachments(); track $index) {
361
+ <li class="cb-chat-message__attachment">
362
+ @if (isImage(a) && a.url) {
363
+ <a class="cb-chat-message__thumb" [href]="a.url" target="_blank" rel="noopener noreferrer">
364
+ <img [src]="a.url" [alt]="a.name" loading="lazy" />
365
+ </a>
366
+ } @else {
367
+ <a class="cb-chat-message__file" [attr.href]="a.url || null" [attr.target]="a.url ? '_blank' : null" [attr.rel]="a.url ? 'noopener noreferrer' : null">
368
+ <cb-icon [name]="isImage(a) ? 'image' : 'file'" [size]="18" aria-hidden="true" />
369
+ <span class="cb-chat-message__file-text">
370
+ <span class="cb-chat-message__file-name">{{ a.name }}</span>
371
+ @if (a.size !== undefined) {
372
+ <span class="cb-chat-message__file-size">{{ size(a) }}</span>
373
+ }
374
+ </span>
375
+ </a>
376
+ }
377
+ </li>
378
+ }
379
+ </ul>
380
+ }
381
+ <!-- Sin texto ni contenido proyectado queda vacía y el CSS la oculta (:empty). -->
382
+ <div class="cb-chat-message__bubble">
383
+ @if (text()) {
384
+ @if (markdown()) {
385
+ @for (block of blocks(); track $index) {
386
+ @if (block.kind === 'html') {
387
+ <div class="cb-chat-message__markdown" [innerHTML]="block.html"></div>
388
+ } @else {
389
+ <cb-code-block class="cb-chat-message__code" [code]="block.code" [language]="block.language" />
390
+ }
391
+ }
392
+ } @else {
393
+ <p class="cb-chat-message__plain">{{ text() }}</p>
394
+ }
395
+ }
396
+ <ng-content />
397
+ @if (streaming()) {
398
+ <span class="cb-chat-message__caret" aria-hidden="true"></span>
399
+ }
400
+ </div>
401
+ <div class="cb-chat-message__footer">
402
+ @switch (status()) {
403
+ @case ('sending') {
404
+ <span class="cb-chat-message__status">{{ t().chatSending }}</span>
405
+ }
406
+ @case ('error') {
407
+ <span class="cb-chat-message__status cb-chat-message__status--error">
408
+ <cb-icon name="circle-alert" [size]="14" aria-hidden="true" /> {{ t().chatError }}
409
+ </span>
410
+ <button type="button" class="cb-chat-message__action" (click)="retry.emit()">
411
+ <cb-icon name="rotate-ccw" [size]="14" aria-hidden="true" /> {{ t().chatRetry }}
412
+ </button>
413
+ }
414
+ }
415
+ <ng-content select="[cb-chat-message-actions]" />
416
+ @if (!streaming()) {
417
+ @if (copyable() && text()) {
418
+ <cb-copy-button [value]="text()" size="sm" />
419
+ }
420
+ @if (showFeedback()) {
421
+ <button type="button" class="cb-chat-message__icon-btn" [attr.aria-pressed]="feedback() === 'up'" [attr.aria-label]="t().chatHelpful" (click)="vote('up')">
422
+ <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 10v11" /><path d="M15 5.9 14 10h5.8a2 2 0 0 1 1.9 2.6l-2.3 7A2 2 0 0 1 17.5 21H7V10l4.6-7.5a1.9 1.9 0 0 1 3.4 1.5z" /></svg>
423
+ </button>
424
+ <button type="button" class="cb-chat-message__icon-btn" [attr.aria-pressed]="feedback() === 'down'" [attr.aria-label]="t().chatNotHelpful" (click)="vote('down')">
425
+ <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 14V3" /><path d="M9 18.1 10 14H4.2a2 2 0 0 1-1.9-2.6l2.3-7A2 2 0 0 1 6.5 3H17v11l-4.6 7.5a1.9 1.9 0 0 1-3.4-1.5z" /></svg>
426
+ </button>
427
+ }
428
+ @if (regenerable()) {
429
+ <button type="button" class="cb-chat-message__icon-btn" [attr.aria-label]="t().chatRegenerate" (click)="regenerate.emit()">
430
+ <cb-icon name="refresh-cw" [size]="15" aria-hidden="true" />
431
+ </button>
432
+ }
433
+ }
434
+ </div>
435
+ </div>
436
+ `, isInline: true, styles: [":host{display:flex;align-items:flex-start;gap:.625rem;max-width:100%;color:var(--cb-color-text);font-family:var(--cb-font-sans)}:host(.cb-chat-message--user){flex-direction:row-reverse}:host(.cb-chat-message--system){justify-content:center}.cb-chat-message__avatar{display:inline-flex;flex:none;align-items:center;justify-content:center;width:2rem;height:2rem;overflow:hidden;border-radius:50%;background:var(--cb-color-surface-hover);color:var(--cb-color-text);font-size:.75rem;font-weight:var(--cb-font-weight-semibold, 600)}:host(.cb-chat-message--assistant) .cb-chat-message__avatar{background:color-mix(in srgb,var(--cb-color-primary) 14%,transparent);color:var(--cb-color-primary-text)}.cb-chat-message__avatar img{width:100%;height:100%;object-fit:cover}.cb-chat-message__body{display:flex;flex-direction:column;gap:.25rem;min-width:0;max-width:min(46rem,85%)}:host(.cb-chat-message--user) .cb-chat-message__body{align-items:flex-end}.cb-chat-message__meta{display:flex;gap:.5rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}.cb-chat-message__author{font-weight:var(--cb-font-weight-semibold, 600)}.cb-chat-message__bubble{max-width:100%;padding:.625rem .875rem;border-radius:var(--cb-radius-lg, 12px);border-top-left-radius:var(--cb-radius-sm, 4px);background:var(--cb-color-surface-subtle);font-size:var(--cb-text-sm, .875rem);line-height:1.55;overflow-wrap:anywhere}:host(.cb-chat-message--user) .cb-chat-message__bubble{border-top-left-radius:var(--cb-radius-lg, 12px);border-top-right-radius:var(--cb-radius-sm, 4px);background:var(--cb-color-primary);color:var(--cb-color-primary-contrast)}:host(.cb-chat-message--system) .cb-chat-message__bubble{padding:.25rem .75rem;border-radius:var(--cb-radius-full, 999px);background:transparent;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);text-align:center}.cb-chat-message__bubble:empty{display:none}.cb-chat-message__bubble>.cb-chat-message__code{margin:.5rem 0}.cb-chat-message__bubble>.cb-chat-message__code:first-child{margin-top:0}.cb-chat-message__bubble>.cb-chat-message__code:last-child{margin-bottom:0}.cb-chat-message__attachments{display:flex;flex-wrap:wrap;gap:.5rem;margin:0;padding:0;list-style:none}:host(.cb-chat-message--user) .cb-chat-message__attachments{justify-content:flex-end}.cb-chat-message__thumb{display:block;overflow:hidden;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md, 8px)}.cb-chat-message__thumb img{display:block;width:7.5rem;height:7.5rem;object-fit:cover}.cb-chat-message__file{display:inline-flex;align-items:center;gap:.5rem;max-width:16rem;padding:.5rem .75rem;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md, 8px);background:var(--cb-color-surface-raised);color:inherit;text-decoration:none}a.cb-chat-message__file[href]:hover{background:var(--cb-color-surface-hover)}.cb-chat-message__thumb:focus-visible,.cb-chat-message__file:focus-visible,.cb-chat-message__icon-btn:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-message__file-text{display:flex;flex-direction:column;min-width:0}.cb-chat-message__file-name{overflow:hidden;font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500);text-overflow:ellipsis;white-space:nowrap}.cb-chat-message__file-size{color:var(--cb-color-text-muted);font-size:.6875rem}.cb-chat-message__icon-btn{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;border:0;border-radius:var(--cb-radius-sm, 4px);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-chat-message__icon-btn:hover{background:var(--cb-color-surface-hover);color:var(--cb-color-text)}.cb-chat-message__icon-btn[aria-pressed=true]{color:var(--cb-color-primary-text)}.cb-chat-message__icon-btn[aria-pressed=true] svg{fill:color-mix(in srgb,currentColor 22%,transparent)}:host(.cb-chat-message--error) .cb-chat-message__bubble{opacity:.75}.cb-chat-message__plain{margin:0;white-space:pre-wrap}.cb-chat-message__caret{display:inline-block;width:.5em;height:1em;margin-left:2px;vertical-align:text-bottom;background:currentColor;animation:cb-chat-caret 1s steps(1) infinite}@keyframes cb-chat-caret{50%{opacity:0}}.cb-chat-message__footer{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;min-height:0;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}.cb-chat-message__footer:empty{display:none}.cb-chat-message__status{display:inline-flex;align-items:center;gap:.25rem}.cb-chat-message__status--error{color:var(--cb-color-danger-700)}:host-context([data-theme=\"dark\"]) .cb-chat-message__status--error{color:var(--cb-color-danger-400)}.cb-chat-message__action{display:inline-flex;align-items:center;gap:.25rem;padding:.125rem .375rem;border:0;border-radius:var(--cb-radius-sm, 4px);background:transparent;color:var(--cb-color-text);font:inherit;text-decoration:underline;cursor:pointer}.cb-chat-message__action:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-message__markdown>:first-child{margin-top:0}.cb-chat-message__markdown>:last-child{margin-bottom:0}.cb-chat-message__markdown ::ng-deep p,.cb-chat-message__markdown ::ng-deep ul,.cb-chat-message__markdown ::ng-deep ol,.cb-chat-message__markdown ::ng-deep blockquote,.cb-chat-message__markdown ::ng-deep pre,.cb-chat-message__markdown ::ng-deep .cb-md-table{margin:0 0 .625rem}.cb-chat-message__markdown ::ng-deep :is(p,ul,ol,pre,blockquote,.cb-md-table):last-child{margin-bottom:0}.cb-chat-message__markdown ::ng-deep :is(h1,h2,h3,h4,h5,h6){margin:.75rem 0 .375rem;font-size:1em;font-weight:var(--cb-font-weight-bold, 700)}.cb-chat-message__markdown ::ng-deep h1{font-size:1.2em}.cb-chat-message__markdown ::ng-deep h2{font-size:1.1em}.cb-chat-message__markdown ::ng-deep :is(ul,ol){padding-left:1.25rem}.cb-chat-message__markdown ::ng-deep li+li{margin-top:.125rem}.cb-chat-message__markdown ::ng-deep .cb-md-task{list-style:none;margin-left:-1.25rem}.cb-chat-message__markdown ::ng-deep a{color:inherit;text-decoration:underline}.cb-chat-message__markdown ::ng-deep code{padding:.0625rem .3125rem;border-radius:var(--cb-radius-sm, 4px);background:color-mix(in srgb,currentColor 10%,transparent);font-family:var(--cb-font-mono);font-size:.9em}.cb-chat-message__markdown ::ng-deep pre{position:relative;overflow-x:auto;padding:.75rem;border-radius:var(--cb-radius-md, 8px);background:var(--cb-color-surface-950, #0b1220);color:#e5e7eb}.cb-chat-message__markdown ::ng-deep pre code{padding:0;background:transparent;font-size:.85em;white-space:pre}.cb-chat-message__markdown ::ng-deep .cb-md-lang{display:block;margin-bottom:.375rem;color:#9ca3af;font-family:var(--cb-font-sans);font-size:.7rem;text-transform:uppercase}.cb-chat-message__markdown ::ng-deep blockquote{padding-left:.75rem;border-left:3px solid color-mix(in srgb,currentColor 30%,transparent);opacity:.9}.cb-chat-message__markdown ::ng-deep hr{border:0;border-top:1px solid color-mix(in srgb,currentColor 20%,transparent)}.cb-chat-message__markdown ::ng-deep .cb-md-table{overflow-x:auto}.cb-chat-message__markdown ::ng-deep table{border-collapse:collapse;font-size:.95em}.cb-chat-message__markdown ::ng-deep .cb-md-left{text-align:left}.cb-chat-message__markdown ::ng-deep .cb-md-center{text-align:center}.cb-chat-message__markdown ::ng-deep .cb-md-right{text-align:right}.cb-chat-message__markdown ::ng-deep .cb-md-check{margin-right:.125rem}.cb-chat-message__markdown ::ng-deep :is(th,td){padding:.25rem .625rem;border:1px solid color-mix(in srgb,currentColor 18%,transparent)}@media(prefers-reduced-motion:reduce){.cb-chat-message__caret{animation:none}}\n"], dependencies: [{ kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }, { kind: "component", type: CbCopyButton, selector: "cb-copy-button", inputs: ["value", "showLabel", "label", "copiedLabel", "timeout", "variant", "severity", "size", "disabled"], outputs: ["copied", "copyError"] }, { kind: "component", type: CbCodeBlock, selector: "cb-code-block", inputs: ["code", "language", "filename", "lineNumbers", "startLine", "highlightLines", "wrap", "copyable", "header", "maxHeight", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
437
+ }
438
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatMessage, decorators: [{
439
+ type: Component,
440
+ args: [{ selector: 'cb-chat-message', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon, CbCopyButton, CbCodeBlock], host: {
441
+ class: 'cb-chat-message',
442
+ role: 'article',
443
+ '[class.cb-chat-message--user]': "role() === 'user'",
444
+ '[class.cb-chat-message--assistant]': "role() === 'assistant'",
445
+ '[class.cb-chat-message--system]': "role() === 'system'",
446
+ '[class.cb-chat-message--error]': "status() === 'error'",
447
+ '[attr.aria-label]': 'label()',
448
+ '[attr.aria-busy]': 'streaming() ? "true" : null',
449
+ }, template: `
450
+ @if (role() !== 'system' && showAvatar()) {
451
+ <span class="cb-chat-message__avatar" aria-hidden="true">
452
+ @if (avatar()) {
453
+ <img [src]="avatar()" alt="" />
454
+ } @else if (role() === 'assistant') {
455
+ <cb-icon name="sparkles" [size]="16" />
456
+ } @else {
457
+ {{ initials() }}
458
+ }
459
+ </span>
460
+ }
461
+ <div class="cb-chat-message__body">
462
+ @if (role() !== 'system' && (showAuthor() || time())) {
463
+ <div class="cb-chat-message__meta" aria-hidden="true">
464
+ @if (showAuthor()) {
465
+ <span class="cb-chat-message__author">{{ authorName() }}</span>
466
+ }
467
+ @if (time()) {
468
+ <span class="cb-chat-message__time">{{ time() }}</span>
469
+ }
470
+ </div>
471
+ }
472
+ <ng-content select="cb-chat-reasoning, cb-chat-tool, [cb-chat-message-before]" />
473
+ @if (attachments().length) {
474
+ <ul class="cb-chat-message__attachments" role="list" [attr.aria-label]="t().chatAttachments">
475
+ @for (a of attachments(); track $index) {
476
+ <li class="cb-chat-message__attachment">
477
+ @if (isImage(a) && a.url) {
478
+ <a class="cb-chat-message__thumb" [href]="a.url" target="_blank" rel="noopener noreferrer">
479
+ <img [src]="a.url" [alt]="a.name" loading="lazy" />
480
+ </a>
481
+ } @else {
482
+ <a class="cb-chat-message__file" [attr.href]="a.url || null" [attr.target]="a.url ? '_blank' : null" [attr.rel]="a.url ? 'noopener noreferrer' : null">
483
+ <cb-icon [name]="isImage(a) ? 'image' : 'file'" [size]="18" aria-hidden="true" />
484
+ <span class="cb-chat-message__file-text">
485
+ <span class="cb-chat-message__file-name">{{ a.name }}</span>
486
+ @if (a.size !== undefined) {
487
+ <span class="cb-chat-message__file-size">{{ size(a) }}</span>
488
+ }
489
+ </span>
490
+ </a>
491
+ }
492
+ </li>
493
+ }
494
+ </ul>
495
+ }
496
+ <!-- Sin texto ni contenido proyectado queda vacía y el CSS la oculta (:empty). -->
497
+ <div class="cb-chat-message__bubble">
498
+ @if (text()) {
499
+ @if (markdown()) {
500
+ @for (block of blocks(); track $index) {
501
+ @if (block.kind === 'html') {
502
+ <div class="cb-chat-message__markdown" [innerHTML]="block.html"></div>
503
+ } @else {
504
+ <cb-code-block class="cb-chat-message__code" [code]="block.code" [language]="block.language" />
505
+ }
506
+ }
507
+ } @else {
508
+ <p class="cb-chat-message__plain">{{ text() }}</p>
509
+ }
510
+ }
511
+ <ng-content />
512
+ @if (streaming()) {
513
+ <span class="cb-chat-message__caret" aria-hidden="true"></span>
514
+ }
515
+ </div>
516
+ <div class="cb-chat-message__footer">
517
+ @switch (status()) {
518
+ @case ('sending') {
519
+ <span class="cb-chat-message__status">{{ t().chatSending }}</span>
520
+ }
521
+ @case ('error') {
522
+ <span class="cb-chat-message__status cb-chat-message__status--error">
523
+ <cb-icon name="circle-alert" [size]="14" aria-hidden="true" /> {{ t().chatError }}
524
+ </span>
525
+ <button type="button" class="cb-chat-message__action" (click)="retry.emit()">
526
+ <cb-icon name="rotate-ccw" [size]="14" aria-hidden="true" /> {{ t().chatRetry }}
527
+ </button>
528
+ }
529
+ }
530
+ <ng-content select="[cb-chat-message-actions]" />
531
+ @if (!streaming()) {
532
+ @if (copyable() && text()) {
533
+ <cb-copy-button [value]="text()" size="sm" />
534
+ }
535
+ @if (showFeedback()) {
536
+ <button type="button" class="cb-chat-message__icon-btn" [attr.aria-pressed]="feedback() === 'up'" [attr.aria-label]="t().chatHelpful" (click)="vote('up')">
537
+ <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 10v11" /><path d="M15 5.9 14 10h5.8a2 2 0 0 1 1.9 2.6l-2.3 7A2 2 0 0 1 17.5 21H7V10l4.6-7.5a1.9 1.9 0 0 1 3.4 1.5z" /></svg>
538
+ </button>
539
+ <button type="button" class="cb-chat-message__icon-btn" [attr.aria-pressed]="feedback() === 'down'" [attr.aria-label]="t().chatNotHelpful" (click)="vote('down')">
540
+ <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 14V3" /><path d="M9 18.1 10 14H4.2a2 2 0 0 1-1.9-2.6l2.3-7A2 2 0 0 1 6.5 3H17v11l-4.6 7.5a1.9 1.9 0 0 1-3.4-1.5z" /></svg>
541
+ </button>
542
+ }
543
+ @if (regenerable()) {
544
+ <button type="button" class="cb-chat-message__icon-btn" [attr.aria-label]="t().chatRegenerate" (click)="regenerate.emit()">
545
+ <cb-icon name="refresh-cw" [size]="15" aria-hidden="true" />
546
+ </button>
547
+ }
548
+ }
549
+ </div>
550
+ </div>
551
+ `, styles: [":host{display:flex;align-items:flex-start;gap:.625rem;max-width:100%;color:var(--cb-color-text);font-family:var(--cb-font-sans)}:host(.cb-chat-message--user){flex-direction:row-reverse}:host(.cb-chat-message--system){justify-content:center}.cb-chat-message__avatar{display:inline-flex;flex:none;align-items:center;justify-content:center;width:2rem;height:2rem;overflow:hidden;border-radius:50%;background:var(--cb-color-surface-hover);color:var(--cb-color-text);font-size:.75rem;font-weight:var(--cb-font-weight-semibold, 600)}:host(.cb-chat-message--assistant) .cb-chat-message__avatar{background:color-mix(in srgb,var(--cb-color-primary) 14%,transparent);color:var(--cb-color-primary-text)}.cb-chat-message__avatar img{width:100%;height:100%;object-fit:cover}.cb-chat-message__body{display:flex;flex-direction:column;gap:.25rem;min-width:0;max-width:min(46rem,85%)}:host(.cb-chat-message--user) .cb-chat-message__body{align-items:flex-end}.cb-chat-message__meta{display:flex;gap:.5rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}.cb-chat-message__author{font-weight:var(--cb-font-weight-semibold, 600)}.cb-chat-message__bubble{max-width:100%;padding:.625rem .875rem;border-radius:var(--cb-radius-lg, 12px);border-top-left-radius:var(--cb-radius-sm, 4px);background:var(--cb-color-surface-subtle);font-size:var(--cb-text-sm, .875rem);line-height:1.55;overflow-wrap:anywhere}:host(.cb-chat-message--user) .cb-chat-message__bubble{border-top-left-radius:var(--cb-radius-lg, 12px);border-top-right-radius:var(--cb-radius-sm, 4px);background:var(--cb-color-primary);color:var(--cb-color-primary-contrast)}:host(.cb-chat-message--system) .cb-chat-message__bubble{padding:.25rem .75rem;border-radius:var(--cb-radius-full, 999px);background:transparent;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);text-align:center}.cb-chat-message__bubble:empty{display:none}.cb-chat-message__bubble>.cb-chat-message__code{margin:.5rem 0}.cb-chat-message__bubble>.cb-chat-message__code:first-child{margin-top:0}.cb-chat-message__bubble>.cb-chat-message__code:last-child{margin-bottom:0}.cb-chat-message__attachments{display:flex;flex-wrap:wrap;gap:.5rem;margin:0;padding:0;list-style:none}:host(.cb-chat-message--user) .cb-chat-message__attachments{justify-content:flex-end}.cb-chat-message__thumb{display:block;overflow:hidden;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md, 8px)}.cb-chat-message__thumb img{display:block;width:7.5rem;height:7.5rem;object-fit:cover}.cb-chat-message__file{display:inline-flex;align-items:center;gap:.5rem;max-width:16rem;padding:.5rem .75rem;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md, 8px);background:var(--cb-color-surface-raised);color:inherit;text-decoration:none}a.cb-chat-message__file[href]:hover{background:var(--cb-color-surface-hover)}.cb-chat-message__thumb:focus-visible,.cb-chat-message__file:focus-visible,.cb-chat-message__icon-btn:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-message__file-text{display:flex;flex-direction:column;min-width:0}.cb-chat-message__file-name{overflow:hidden;font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500);text-overflow:ellipsis;white-space:nowrap}.cb-chat-message__file-size{color:var(--cb-color-text-muted);font-size:.6875rem}.cb-chat-message__icon-btn{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;border:0;border-radius:var(--cb-radius-sm, 4px);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-chat-message__icon-btn:hover{background:var(--cb-color-surface-hover);color:var(--cb-color-text)}.cb-chat-message__icon-btn[aria-pressed=true]{color:var(--cb-color-primary-text)}.cb-chat-message__icon-btn[aria-pressed=true] svg{fill:color-mix(in srgb,currentColor 22%,transparent)}:host(.cb-chat-message--error) .cb-chat-message__bubble{opacity:.75}.cb-chat-message__plain{margin:0;white-space:pre-wrap}.cb-chat-message__caret{display:inline-block;width:.5em;height:1em;margin-left:2px;vertical-align:text-bottom;background:currentColor;animation:cb-chat-caret 1s steps(1) infinite}@keyframes cb-chat-caret{50%{opacity:0}}.cb-chat-message__footer{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;min-height:0;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}.cb-chat-message__footer:empty{display:none}.cb-chat-message__status{display:inline-flex;align-items:center;gap:.25rem}.cb-chat-message__status--error{color:var(--cb-color-danger-700)}:host-context([data-theme=\"dark\"]) .cb-chat-message__status--error{color:var(--cb-color-danger-400)}.cb-chat-message__action{display:inline-flex;align-items:center;gap:.25rem;padding:.125rem .375rem;border:0;border-radius:var(--cb-radius-sm, 4px);background:transparent;color:var(--cb-color-text);font:inherit;text-decoration:underline;cursor:pointer}.cb-chat-message__action:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-message__markdown>:first-child{margin-top:0}.cb-chat-message__markdown>:last-child{margin-bottom:0}.cb-chat-message__markdown ::ng-deep p,.cb-chat-message__markdown ::ng-deep ul,.cb-chat-message__markdown ::ng-deep ol,.cb-chat-message__markdown ::ng-deep blockquote,.cb-chat-message__markdown ::ng-deep pre,.cb-chat-message__markdown ::ng-deep .cb-md-table{margin:0 0 .625rem}.cb-chat-message__markdown ::ng-deep :is(p,ul,ol,pre,blockquote,.cb-md-table):last-child{margin-bottom:0}.cb-chat-message__markdown ::ng-deep :is(h1,h2,h3,h4,h5,h6){margin:.75rem 0 .375rem;font-size:1em;font-weight:var(--cb-font-weight-bold, 700)}.cb-chat-message__markdown ::ng-deep h1{font-size:1.2em}.cb-chat-message__markdown ::ng-deep h2{font-size:1.1em}.cb-chat-message__markdown ::ng-deep :is(ul,ol){padding-left:1.25rem}.cb-chat-message__markdown ::ng-deep li+li{margin-top:.125rem}.cb-chat-message__markdown ::ng-deep .cb-md-task{list-style:none;margin-left:-1.25rem}.cb-chat-message__markdown ::ng-deep a{color:inherit;text-decoration:underline}.cb-chat-message__markdown ::ng-deep code{padding:.0625rem .3125rem;border-radius:var(--cb-radius-sm, 4px);background:color-mix(in srgb,currentColor 10%,transparent);font-family:var(--cb-font-mono);font-size:.9em}.cb-chat-message__markdown ::ng-deep pre{position:relative;overflow-x:auto;padding:.75rem;border-radius:var(--cb-radius-md, 8px);background:var(--cb-color-surface-950, #0b1220);color:#e5e7eb}.cb-chat-message__markdown ::ng-deep pre code{padding:0;background:transparent;font-size:.85em;white-space:pre}.cb-chat-message__markdown ::ng-deep .cb-md-lang{display:block;margin-bottom:.375rem;color:#9ca3af;font-family:var(--cb-font-sans);font-size:.7rem;text-transform:uppercase}.cb-chat-message__markdown ::ng-deep blockquote{padding-left:.75rem;border-left:3px solid color-mix(in srgb,currentColor 30%,transparent);opacity:.9}.cb-chat-message__markdown ::ng-deep hr{border:0;border-top:1px solid color-mix(in srgb,currentColor 20%,transparent)}.cb-chat-message__markdown ::ng-deep .cb-md-table{overflow-x:auto}.cb-chat-message__markdown ::ng-deep table{border-collapse:collapse;font-size:.95em}.cb-chat-message__markdown ::ng-deep .cb-md-left{text-align:left}.cb-chat-message__markdown ::ng-deep .cb-md-center{text-align:center}.cb-chat-message__markdown ::ng-deep .cb-md-right{text-align:right}.cb-chat-message__markdown ::ng-deep .cb-md-check{margin-right:.125rem}.cb-chat-message__markdown ::ng-deep :is(th,td){padding:.25rem .625rem;border:1px solid color-mix(in srgb,currentColor 18%,transparent)}@media(prefers-reduced-motion:reduce){.cb-chat-message__caret{animation:none}}\n"] }]
552
+ }], propDecorators: { role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], markdown: [{ type: i0.Input, args: [{ isSignal: true, alias: "markdown", required: false }] }], author: [{ type: i0.Input, args: [{ isSignal: true, alias: "author", required: false }] }], time: [{ type: i0.Input, args: [{ isSignal: true, alias: "time", required: false }] }], avatar: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatar", required: false }] }], showAvatar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAvatar", required: false }] }], showAuthor: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAuthor", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], copyable: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyable", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }], showFeedback: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFeedback", required: false }] }], feedback: [{ type: i0.Input, args: [{ isSignal: true, alias: "feedback", required: false }] }, { type: i0.Output, args: ["feedbackChange"] }], regenerable: [{ type: i0.Input, args: [{ isSignal: true, alias: "regenerable", required: false }] }], retry: [{ type: i0.Output, args: ["retry"] }], regenerate: [{ type: i0.Output, args: ["regenerate"] }] } });
553
+
554
+ /** Distancia (px) al final por debajo de la cual la lista sigue los mensajes nuevos. */
555
+ const PIN_THRESHOLD = 48;
556
+ /**
557
+ * Lista de mensajes de chat de CaribeNG (`role="log"`). Se mantiene pegada al
558
+ * final mientras llegan mensajes o se alarga una respuesta en streaming, pero
559
+ * no mueve el scroll si el usuario subió a leer: entonces ofrece "Ir al último
560
+ * mensaje". Con `typing` muestra el indicador de escritura.
561
+ *
562
+ * Accesibilidad: con `busy` (respuesta generándose) el log se marca
563
+ * `aria-busy` para que el lector no lea cada fragmento, y al terminar se
564
+ * anuncia "Respuesta lista".
565
+ *
566
+ * @example
567
+ * ```html
568
+ * <cb-chat-messages [busy]="generando()" [typing]="pensando()" style="height: 30rem">
569
+ * @for (m of mensajes(); track m.id) {
570
+ * <cb-chat-message [role]="m.role" [text]="m.text" />
571
+ * }
572
+ * </cb-chat-messages>
573
+ * ```
574
+ */
575
+ class CbChatMessages {
576
+ constructor() {
577
+ /** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
578
+ this.t = injectCbTranslation();
579
+ this.document = inject(DOCUMENT);
580
+ this.destroyRef = inject(DestroyRef);
581
+ this.scroller = viewChild.required('scroller');
582
+ this.content = viewChild.required('content');
583
+ /** Una respuesta se está generando (marca el log como ocupado). */
584
+ this.busy = input(false, { ...(ngDevMode ? { debugName: "busy" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
585
+ /** Muestra el indicador "Escribiendo…". */
586
+ this.typing = input(false, { ...(ngDevMode ? { debugName: "typing" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
587
+ /** Texto del indicador (p. ej. "Laura está escribiendo…"). */
588
+ this.typingLabel = input(undefined, /* @ts-ignore */
589
+ ...(ngDevMode ? [{ debugName: "typingLabel" }] : /* istanbul ignore next */ []));
590
+ /** Nombre del log. Por defecto, "Conversación". */
591
+ this.ariaLabel = input(undefined, /* @ts-ignore */
592
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
593
+ /** La vista está en el final (sigue los mensajes nuevos). */
594
+ this.pinned = signal(true, /* @ts-ignore */
595
+ ...(ngDevMode ? [{ debugName: "pinned" }] : /* istanbul ignore next */ []));
596
+ this.announcement = signal('', /* @ts-ignore */
597
+ ...(ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []));
598
+ this.wasBusy = false;
599
+ afterNextRender(() => {
600
+ this.scrollToBottom(false);
601
+ const view = this.document.defaultView;
602
+ const follow = () => {
603
+ if (this.pinned())
604
+ this.scrollToBottom(false);
605
+ };
606
+ const content = this.content().nativeElement;
607
+ if (view?.ResizeObserver) {
608
+ const resize = new view.ResizeObserver(follow);
609
+ resize.observe(content);
610
+ this.destroyRef.onDestroy(() => resize.disconnect());
611
+ }
612
+ if (view?.MutationObserver) {
613
+ const mutation = new view.MutationObserver(follow);
614
+ mutation.observe(content, { childList: true, subtree: true, characterData: true });
615
+ this.destroyRef.onDestroy(() => mutation.disconnect());
616
+ }
617
+ });
618
+ // Al terminar una respuesta, se anuncia una sola vez (en vez de cada fragmento).
619
+ effect(() => {
620
+ const busy = this.busy();
621
+ untracked(() => {
622
+ if (this.wasBusy && !busy)
623
+ this.announcement.set(this.t().chatResponseReady);
624
+ else if (busy)
625
+ this.announcement.set('');
626
+ this.wasBusy = busy;
627
+ });
628
+ });
629
+ }
630
+ onScroll() {
631
+ const el = this.scroller().nativeElement;
632
+ this.pinned.set(el.scrollHeight - el.scrollTop - el.clientHeight <= PIN_THRESHOLD);
633
+ }
634
+ /** Lleva la vista al último mensaje y vuelve a seguir los nuevos. */
635
+ scrollToBottom(smooth = true) {
636
+ const el = this.scroller().nativeElement;
637
+ const reduce = this.document.defaultView?.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
638
+ if (typeof el.scrollTo === 'function')
639
+ el.scrollTo({ top: el.scrollHeight, behavior: smooth && !reduce ? 'smooth' : 'auto' });
640
+ else
641
+ el.scrollTop = el.scrollHeight;
642
+ this.pinned.set(true);
643
+ }
644
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatMessages, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
645
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbChatMessages, isStandalone: true, selector: "cb-chat-messages", inputs: { busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, typing: { classPropertyName: "typing", publicName: "typing", isSignal: true, isRequired: false, transformFunction: null }, typingLabel: { classPropertyName: "typingLabel", publicName: "typingLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "cb-chat-messages" }, viewQueries: [{ propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true, isSignal: true }, { propertyName: "content", first: true, predicate: ["content"], descendants: true, isSignal: true }], ngImport: i0, template: `
646
+ <div
647
+ #scroller
648
+ class="cb-chat-messages__scroller"
649
+ role="log"
650
+ tabindex="0"
651
+ [attr.aria-label]="ariaLabel() ?? t().chatConversation"
652
+ [attr.aria-busy]="busy() ? 'true' : null"
653
+ (scroll)="onScroll()"
654
+ >
655
+ <div #content class="cb-chat-messages__content">
656
+ <ng-content />
657
+ @if (typing()) {
658
+ <div class="cb-chat-messages__typing">
659
+ <span class="cb-chat-messages__dots" aria-hidden="true"><span></span><span></span><span></span></span>
660
+ <span class="cb-chat-messages__typing-label">{{ typingLabel() ?? t().chatTyping }}</span>
661
+ </div>
662
+ }
663
+ </div>
664
+ </div>
665
+ @if (!pinned()) {
666
+ <button type="button" class="cb-chat-messages__jump" (click)="scrollToBottom(true)">
667
+ <cb-icon name="arrow-down" [size]="16" aria-hidden="true" />
668
+ <span>{{ t().chatJumpToLatest }}</span>
669
+ </button>
670
+ }
671
+ <div class="cb-chat-messages__status" role="status">{{ announcement() }}</div>
672
+ `, isInline: true, styles: [":host{position:relative;display:flex;flex-direction:column;min-height:0}.cb-chat-messages__scroller{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.cb-chat-messages__scroller:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:-2px}.cb-chat-messages__content{display:flex;flex-direction:column;gap:1rem;padding:1rem}.cb-chat-messages__typing{display:flex;align-items:center;gap:.5rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}.cb-chat-messages__dots{display:inline-flex;gap:.25rem;padding:.625rem .75rem;border-radius:var(--cb-radius-lg, 12px);background:var(--cb-color-surface-subtle)}.cb-chat-messages__dots span{width:.375rem;height:.375rem;border-radius:50%;background:currentColor;animation:cb-chat-dot 1.2s ease-in-out infinite}.cb-chat-messages__dots span:nth-child(2){animation-delay:.15s}.cb-chat-messages__dots span:nth-child(3){animation-delay:.3s}@keyframes cb-chat-dot{0%,60%,to{opacity:.3;transform:none}30%{opacity:1;transform:translateY(-2px)}}.cb-chat-messages__jump{position:absolute;bottom:.75rem;left:50%;display:inline-flex;align-items:center;gap:.375rem;padding:.375rem .75rem;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-full, 999px);background:var(--cb-color-surface-raised);box-shadow:var(--cb-shadow-md);color:var(--cb-color-text);font:inherit;font-size:var(--cb-text-xs, .75rem);transform:translate(-50%);cursor:pointer}.cb-chat-messages__jump:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-messages__status{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}@media(prefers-reduced-motion:reduce){.cb-chat-messages__dots span{animation:none}}\n"], dependencies: [{ kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
673
+ }
674
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatMessages, decorators: [{
675
+ type: Component,
676
+ args: [{ selector: 'cb-chat-messages', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon], host: { class: 'cb-chat-messages' }, template: `
677
+ <div
678
+ #scroller
679
+ class="cb-chat-messages__scroller"
680
+ role="log"
681
+ tabindex="0"
682
+ [attr.aria-label]="ariaLabel() ?? t().chatConversation"
683
+ [attr.aria-busy]="busy() ? 'true' : null"
684
+ (scroll)="onScroll()"
685
+ >
686
+ <div #content class="cb-chat-messages__content">
687
+ <ng-content />
688
+ @if (typing()) {
689
+ <div class="cb-chat-messages__typing">
690
+ <span class="cb-chat-messages__dots" aria-hidden="true"><span></span><span></span><span></span></span>
691
+ <span class="cb-chat-messages__typing-label">{{ typingLabel() ?? t().chatTyping }}</span>
692
+ </div>
693
+ }
694
+ </div>
695
+ </div>
696
+ @if (!pinned()) {
697
+ <button type="button" class="cb-chat-messages__jump" (click)="scrollToBottom(true)">
698
+ <cb-icon name="arrow-down" [size]="16" aria-hidden="true" />
699
+ <span>{{ t().chatJumpToLatest }}</span>
700
+ </button>
701
+ }
702
+ <div class="cb-chat-messages__status" role="status">{{ announcement() }}</div>
703
+ `, styles: [":host{position:relative;display:flex;flex-direction:column;min-height:0}.cb-chat-messages__scroller{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.cb-chat-messages__scroller:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:-2px}.cb-chat-messages__content{display:flex;flex-direction:column;gap:1rem;padding:1rem}.cb-chat-messages__typing{display:flex;align-items:center;gap:.5rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}.cb-chat-messages__dots{display:inline-flex;gap:.25rem;padding:.625rem .75rem;border-radius:var(--cb-radius-lg, 12px);background:var(--cb-color-surface-subtle)}.cb-chat-messages__dots span{width:.375rem;height:.375rem;border-radius:50%;background:currentColor;animation:cb-chat-dot 1.2s ease-in-out infinite}.cb-chat-messages__dots span:nth-child(2){animation-delay:.15s}.cb-chat-messages__dots span:nth-child(3){animation-delay:.3s}@keyframes cb-chat-dot{0%,60%,to{opacity:.3;transform:none}30%{opacity:1;transform:translateY(-2px)}}.cb-chat-messages__jump{position:absolute;bottom:.75rem;left:50%;display:inline-flex;align-items:center;gap:.375rem;padding:.375rem .75rem;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-full, 999px);background:var(--cb-color-surface-raised);box-shadow:var(--cb-shadow-md);color:var(--cb-color-text);font:inherit;font-size:var(--cb-text-xs, .75rem);transform:translate(-50%);cursor:pointer}.cb-chat-messages__jump:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-messages__status{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}@media(prefers-reduced-motion:reduce){.cb-chat-messages__dots span{animation:none}}\n"] }]
704
+ }], ctorParameters: () => [], propDecorators: { scroller: [{ type: i0.ViewChild, args: ['scroller', { isSignal: true }] }], content: [{ type: i0.ViewChild, args: ['content', { isSignal: true }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], typing: [{ type: i0.Input, args: [{ isSignal: true, alias: "typing", required: false }] }], typingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "typingLabel", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
705
+
706
+ let nextUid$2 = 0;
707
+ function formatSize(bytes) {
708
+ if (bytes < 1024)
709
+ return `${bytes} B`;
710
+ if (bytes < 1024 * 1024)
711
+ return `${(bytes / 1024).toFixed(0)} KB`;
712
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
713
+ }
714
+ /**
715
+ * Campo para escribir mensajes (prompt) de CaribeNG: crece con el texto hasta
716
+ * `maxRows`, envía con Enter (Shift + Enter = salto de línea, respetando la
717
+ * composición de teclados asiáticos), adjunta archivos (botón, pegar o
718
+ * arrastrar), muestra sugerencias y, mientras se genera una respuesta, cambia
719
+ * "Enviar" por "Detener".
720
+ *
721
+ * @example
722
+ * ```html
723
+ * <cb-chat-input [streaming]="generando()" attachments (send)="enviar($event)" (stop)="detener()" />
724
+ * ```
725
+ */
726
+ class CbChatInput {
727
+ constructor() {
728
+ /** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
729
+ this.t = injectCbTranslation();
730
+ this.fmt = cbFormat;
731
+ this.injector = inject(Injector);
732
+ this.textarea = viewChild.required('textarea');
733
+ this.inputId = `cb-chat-input-${++nextUid$2}`;
734
+ /** Texto escrito (bidireccional). */
735
+ this.value = model('', /* @ts-ignore */
736
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
737
+ /** Archivos adjuntos (bidireccional). */
738
+ this.files = model([], /* @ts-ignore */
739
+ ...(ngDevMode ? [{ debugName: "files" }] : /* istanbul ignore next */ []));
740
+ this.placeholder = input(undefined, /* @ts-ignore */
741
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
742
+ /** Nombre del campo. Por defecto, "Mensaje". */
743
+ this.ariaLabel = input(undefined, /* @ts-ignore */
744
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
745
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
746
+ /** Se está generando una respuesta: muestra "Detener" y no deja enviar. */
747
+ this.streaming = input(false, { ...(ngDevMode ? { debugName: "streaming" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
748
+ /** Permite adjuntar archivos (botón, pegar o arrastrar). */
749
+ this.attachments = input(false, { ...(ngDevMode ? { debugName: "attachments" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
750
+ /** Tipos aceptados (`image/*,.pdf`). */
751
+ this.accept = input('', /* @ts-ignore */
752
+ ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
753
+ this.multiple = input(true, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
754
+ /** Máximo de caracteres, con contador. 0 = sin límite. */
755
+ this.maxLength = input(0, /* @ts-ignore */
756
+ ...(ngDevMode ? [{ debugName: "maxLength" }] : /* istanbul ignore next */ []));
757
+ /** Filas máximas antes de hacer scroll. */
758
+ this.maxRows = input(8, /* @ts-ignore */
759
+ ...(ngDevMode ? [{ debugName: "maxRows" }] : /* istanbul ignore next */ []));
760
+ /** Enter envía (Shift + Enter, salto de línea). Con `false`, Ctrl/⌘ + Enter envía. */
761
+ this.submitOnEnter = input(true, { ...(ngDevMode ? { debugName: "submitOnEnter" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
762
+ /** Vacía el campo y los adjuntos al enviar. */
763
+ this.clearOnSend = input(true, { ...(ngDevMode ? { debugName: "clearOnSend" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
764
+ /** Sugerencias de inicio (se muestran con el campo vacío; al pulsar, se envían). */
765
+ this.suggestions = input([], /* @ts-ignore */
766
+ ...(ngDevMode ? [{ debugName: "suggestions" }] : /* istanbul ignore next */ []));
767
+ /** El usuario envió un mensaje. */
768
+ this.send = output();
769
+ /** El usuario pidió detener la respuesta. */
770
+ this.stop = output();
771
+ this.dragging = signal(false, /* @ts-ignore */
772
+ ...(ngDevMode ? [{ debugName: "dragging" }] : /* istanbul ignore next */ []));
773
+ this.canSend = computed(() => !this.disabled() && !this.streaming() && (this.value().trim().length > 0 || this.files().length > 0), /* @ts-ignore */
774
+ ...(ngDevMode ? [{ debugName: "canSend" }] : /* istanbul ignore next */ []));
775
+ /** URLs temporales de las miniaturas, por archivo. */
776
+ this.previews = new Map();
777
+ // Ajusta la altura cuando el valor cambia desde fuera (p. ej. al vaciarse tras enviar).
778
+ effect(() => {
779
+ this.value();
780
+ untracked(() => afterNextRender(() => this.resize(), { injector: this.injector }));
781
+ });
782
+ // Libera las miniaturas de los archivos que ya no están.
783
+ effect(() => {
784
+ const current = new Set(this.files());
785
+ untracked(() => {
786
+ for (const [file, url] of this.previews) {
787
+ if (!current.has(file)) {
788
+ URL.revokeObjectURL?.(url);
789
+ this.previews.delete(file);
790
+ }
791
+ }
792
+ });
793
+ });
794
+ inject(DestroyRef).onDestroy(() => {
795
+ for (const url of this.previews.values())
796
+ URL.revokeObjectURL?.(url);
797
+ this.previews.clear();
798
+ });
799
+ }
800
+ /** Miniatura de una imagen adjunta (`null` si no es imagen o el navegador no la admite). */
801
+ preview(file) {
802
+ if (!file.type.startsWith('image/') || typeof URL.createObjectURL !== 'function')
803
+ return null;
804
+ let url = this.previews.get(file);
805
+ if (!url) {
806
+ url = URL.createObjectURL(file);
807
+ this.previews.set(file, url);
808
+ }
809
+ return url;
810
+ }
811
+ /** Enfoca el campo. */
812
+ focus() {
813
+ this.textarea().nativeElement.focus();
814
+ }
815
+ /** Envía el mensaje si hay texto o adjuntos. */
816
+ submit() {
817
+ if (!this.canSend())
818
+ return;
819
+ this.send.emit({ text: this.value().trim(), files: [...this.files()] });
820
+ if (this.clearOnSend()) {
821
+ this.value.set('');
822
+ this.files.set([]);
823
+ }
824
+ }
825
+ size(file) {
826
+ return formatSize(file.size);
827
+ }
828
+ onInput(event) {
829
+ this.value.set(event.target.value);
830
+ this.resize();
831
+ }
832
+ onKeydown(event) {
833
+ if (event.key !== 'Enter' || event.isComposing || event.keyCode === 229)
834
+ return;
835
+ const mod = event.ctrlKey || event.metaKey;
836
+ const sends = this.submitOnEnter() ? !event.shiftKey : mod;
837
+ if (!sends)
838
+ return;
839
+ event.preventDefault();
840
+ this.submit();
841
+ }
842
+ useSuggestion(text) {
843
+ this.value.set(text);
844
+ this.submit();
845
+ }
846
+ onFilesChosen(event) {
847
+ const input = event.target;
848
+ this.addFiles(Array.from(input.files ?? []));
849
+ input.value = '';
850
+ }
851
+ onPaste(event) {
852
+ if (!this.attachments())
853
+ return;
854
+ const pasted = Array.from(event.clipboardData?.files ?? []);
855
+ if (!pasted.length)
856
+ return;
857
+ event.preventDefault();
858
+ this.addFiles(pasted);
859
+ }
860
+ onDragOver(event) {
861
+ if (!this.attachments() || this.disabled() || !event.dataTransfer?.types.includes('Files'))
862
+ return;
863
+ event.preventDefault();
864
+ this.dragging.set(true);
865
+ }
866
+ onDrop(event) {
867
+ this.dragging.set(false);
868
+ if (!this.attachments() || this.disabled())
869
+ return;
870
+ const dropped = Array.from(event.dataTransfer?.files ?? []);
871
+ if (!dropped.length)
872
+ return;
873
+ event.preventDefault();
874
+ this.addFiles(dropped);
875
+ }
876
+ removeFile(index) {
877
+ this.files.update((list) => list.filter((_f, i) => i !== index));
878
+ this.focus();
879
+ }
880
+ addFiles(incoming) {
881
+ const accepted = incoming.filter((file) => this.isAccepted(file));
882
+ if (!accepted.length)
883
+ return;
884
+ this.files.update((list) => (this.multiple() ? [...list, ...accepted] : [accepted[0]]));
885
+ }
886
+ isAccepted(file) {
887
+ const accept = this.accept().trim();
888
+ if (!accept)
889
+ return true;
890
+ return accept.split(',').some((rule) => {
891
+ const r = rule.trim().toLowerCase();
892
+ if (!r)
893
+ return false;
894
+ if (r.startsWith('.'))
895
+ return file.name.toLowerCase().endsWith(r);
896
+ if (r.endsWith('/*'))
897
+ return file.type.toLowerCase().startsWith(r.slice(0, -1));
898
+ return file.type.toLowerCase() === r;
899
+ });
900
+ }
901
+ resize() {
902
+ const el = this.textarea().nativeElement;
903
+ const style = el.ownerDocument.defaultView?.getComputedStyle(el);
904
+ const line = parseFloat(style?.lineHeight ?? '') || 20;
905
+ const padding = (parseFloat(style?.paddingTop ?? '') || 0) + (parseFloat(style?.paddingBottom ?? '') || 0);
906
+ el.style.height = 'auto';
907
+ const max = line * Math.max(1, this.maxRows()) + padding;
908
+ el.style.height = `${Math.min(el.scrollHeight, max)}px`;
909
+ el.style.overflowY = el.scrollHeight > max ? 'auto' : 'hidden';
910
+ }
911
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatInput, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
912
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbChatInput, isStandalone: true, selector: "cb-chat-input", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, attachments: { classPropertyName: "attachments", publicName: "attachments", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null }, submitOnEnter: { classPropertyName: "submitOnEnter", publicName: "submitOnEnter", isSignal: true, isRequired: false, transformFunction: null }, clearOnSend: { classPropertyName: "clearOnSend", publicName: "clearOnSend", isSignal: true, isRequired: false, transformFunction: null }, suggestions: { classPropertyName: "suggestions", publicName: "suggestions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", files: "filesChange", send: "send", stop: "stop" }, host: { properties: { "class.cb-chat-input--disabled": "disabled()", "class.cb-chat-input--dragging": "dragging()" }, classAttribute: "cb-chat-input" }, viewQueries: [{ propertyName: "textarea", first: true, predicate: ["textarea"], descendants: true, isSignal: true }], ngImport: i0, template: `
913
+ @if (suggestions().length && !value() && !files().length) {
914
+ <div class="cb-chat-input__suggestions">
915
+ @for (s of suggestions(); track s) {
916
+ <button type="button" class="cb-chat-input__suggestion" [disabled]="disabled() || streaming()" (click)="useSuggestion(s)">{{ s }}</button>
917
+ }
918
+ </div>
919
+ }
920
+ <form
921
+ class="cb-chat-input__box"
922
+ (submit)="$event.preventDefault(); submit()"
923
+ (dragover)="onDragOver($event)"
924
+ (dragleave)="dragging.set(false)"
925
+ (drop)="onDrop($event)"
926
+ >
927
+ @if (files().length) {
928
+ <ul class="cb-chat-input__files" role="list">
929
+ @for (file of files(); track $index) {
930
+ <li class="cb-chat-input__file">
931
+ @if (preview(file); as src) {
932
+ <img class="cb-chat-input__file-thumb" [src]="src" alt="" />
933
+ } @else {
934
+ <cb-icon [name]="file.type.startsWith('image/') ? 'image' : 'file'" [size]="16" aria-hidden="true" />
935
+ }
936
+ <span class="cb-chat-input__file-name">{{ file.name }}</span>
937
+ <span class="cb-chat-input__file-size">{{ size(file) }}</span>
938
+ <button type="button" class="cb-chat-input__file-remove" [attr.aria-label]="fmt(t().removeItem, file.name)" (click)="removeFile($index)">
939
+ <cb-icon name="x" [size]="14" aria-hidden="true" />
940
+ </button>
941
+ </li>
942
+ }
943
+ </ul>
944
+ }
945
+ <div class="cb-chat-input__row">
946
+ @if (attachments()) {
947
+ <button type="button" class="cb-chat-input__icon-btn" [attr.aria-label]="t().chatAttach" [disabled]="disabled()" (click)="fileInput.click()">
948
+ <cb-icon name="plus" [size]="20" aria-hidden="true" />
949
+ </button>
950
+ <input #fileInput type="file" hidden [attr.accept]="accept() || null" [multiple]="multiple()" (change)="onFilesChosen($event)" />
951
+ }
952
+ <textarea
953
+ #textarea
954
+ class="cb-chat-input__textarea"
955
+ rows="1"
956
+ [id]="inputId"
957
+ [value]="value()"
958
+ [placeholder]="placeholder() ?? t().chatPlaceholder"
959
+ [disabled]="disabled()"
960
+ [attr.maxlength]="maxLength() || null"
961
+ [attr.aria-label]="ariaLabel() ?? t().chatMessage"
962
+ [attr.aria-describedby]="maxLength() ? inputId + '-count' : null"
963
+ (input)="onInput($event)"
964
+ (keydown)="onKeydown($event)"
965
+ (paste)="onPaste($event)"
966
+ ></textarea>
967
+ @if (streaming()) {
968
+ <button type="button" class="cb-chat-input__send cb-chat-input__send--stop" [attr.aria-label]="t().chatStop" (click)="stop.emit()">
969
+ <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><rect x="5" y="5" width="14" height="14" rx="2" fill="currentColor" /></svg>
970
+ </button>
971
+ } @else {
972
+ <button type="submit" class="cb-chat-input__send" [attr.aria-label]="t().chatSend" [disabled]="!canSend()">
973
+ <cb-icon name="arrow-up" [size]="18" aria-hidden="true" />
974
+ </button>
975
+ }
976
+ </div>
977
+ </form>
978
+ @if (maxLength()) {
979
+ <div class="cb-chat-input__count" [id]="inputId + '-count'">{{ value().length }} / {{ maxLength() }}</div>
980
+ }
981
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:.5rem;color:var(--cb-color-text);font-family:var(--cb-font-sans)}.cb-chat-input__suggestions{display:flex;flex-wrap:wrap;gap:.5rem}.cb-chat-input__suggestion{padding:.375rem .75rem;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-full, 999px);background:var(--cb-color-surface-raised);color:inherit;font:inherit;font-size:var(--cb-text-sm, .875rem);cursor:pointer}.cb-chat-input__suggestion:hover:not(:disabled){background:var(--cb-color-surface-hover)}.cb-chat-input__suggestion:focus-visible,.cb-chat-input__icon-btn:focus-visible,.cb-chat-input__send:focus-visible,.cb-chat-input__file-remove:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-input__box{display:flex;flex-direction:column;gap:.5rem;margin:0;padding:.5rem;border:1px solid var(--cb-color-border-strong);border-radius:var(--cb-radius-lg, 12px);background:var(--cb-color-surface-raised);transition:border-color var(--cb-duration-fast, .15s) ease}.cb-chat-input__box:focus-within{border-color:var(--cb-color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--cb-color-primary) 18%,transparent)}:host(.cb-chat-input--dragging) .cb-chat-input__box{border-style:dashed;border-color:var(--cb-color-primary);background:color-mix(in srgb,var(--cb-color-primary) 6%,var(--cb-color-surface-raised))}:host(.cb-chat-input--disabled) .cb-chat-input__box{opacity:.6}.cb-chat-input__files{display:flex;flex-wrap:wrap;gap:.375rem;margin:0;padding:0;list-style:none}.cb-chat-input__file{display:inline-flex;align-items:center;gap:.375rem;max-width:16rem;padding:.25rem .25rem .25rem .5rem;border-radius:var(--cb-radius-md, 8px);background:var(--cb-color-surface-subtle);font-size:var(--cb-text-xs, .75rem)}.cb-chat-input__file-thumb{flex:none;width:1.75rem;height:1.75rem;border-radius:var(--cb-radius-sm, 4px);object-fit:cover}.cb-chat-input__file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cb-chat-input__file-size{flex:none;color:var(--cb-color-text-muted)}.cb-chat-input__file-remove{display:inline-flex;flex:none;padding:.125rem;border:0;border-radius:var(--cb-radius-sm, 4px);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-chat-input__row{display:flex;align-items:flex-end;gap:.375rem}.cb-chat-input__textarea{flex:1;min-width:0;max-height:none;margin:0;padding:.375rem .25rem;border:0;outline:none;resize:none;overflow-y:hidden;background:transparent;color:inherit;font:inherit;font-size:var(--cb-text-sm, .875rem);line-height:1.5}.cb-chat-input__textarea::placeholder{color:var(--cb-color-text-muted)}.cb-chat-input__icon-btn,.cb-chat-input__send{display:inline-flex;flex:none;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border:0;border-radius:50%;cursor:pointer}.cb-chat-input__icon-btn{background:transparent;color:var(--cb-color-text-muted)}.cb-chat-input__icon-btn:hover:not(:disabled){background:var(--cb-color-surface-hover);color:var(--cb-color-text)}.cb-chat-input__send{background:var(--cb-color-primary);color:var(--cb-color-primary-contrast)}.cb-chat-input__send:disabled{background:var(--cb-color-surface-hover);color:var(--cb-color-text-muted);cursor:not-allowed}.cb-chat-input__send--stop{background:var(--cb-color-text-strong);color:var(--cb-color-surface-raised)}.cb-chat-input__count{align-self:flex-end;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}\n"], dependencies: [{ kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
982
+ }
983
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatInput, decorators: [{
984
+ type: Component,
985
+ args: [{ selector: 'cb-chat-input', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon], host: {
986
+ class: 'cb-chat-input',
987
+ '[class.cb-chat-input--disabled]': 'disabled()',
988
+ '[class.cb-chat-input--dragging]': 'dragging()',
989
+ }, template: `
990
+ @if (suggestions().length && !value() && !files().length) {
991
+ <div class="cb-chat-input__suggestions">
992
+ @for (s of suggestions(); track s) {
993
+ <button type="button" class="cb-chat-input__suggestion" [disabled]="disabled() || streaming()" (click)="useSuggestion(s)">{{ s }}</button>
994
+ }
995
+ </div>
996
+ }
997
+ <form
998
+ class="cb-chat-input__box"
999
+ (submit)="$event.preventDefault(); submit()"
1000
+ (dragover)="onDragOver($event)"
1001
+ (dragleave)="dragging.set(false)"
1002
+ (drop)="onDrop($event)"
1003
+ >
1004
+ @if (files().length) {
1005
+ <ul class="cb-chat-input__files" role="list">
1006
+ @for (file of files(); track $index) {
1007
+ <li class="cb-chat-input__file">
1008
+ @if (preview(file); as src) {
1009
+ <img class="cb-chat-input__file-thumb" [src]="src" alt="" />
1010
+ } @else {
1011
+ <cb-icon [name]="file.type.startsWith('image/') ? 'image' : 'file'" [size]="16" aria-hidden="true" />
1012
+ }
1013
+ <span class="cb-chat-input__file-name">{{ file.name }}</span>
1014
+ <span class="cb-chat-input__file-size">{{ size(file) }}</span>
1015
+ <button type="button" class="cb-chat-input__file-remove" [attr.aria-label]="fmt(t().removeItem, file.name)" (click)="removeFile($index)">
1016
+ <cb-icon name="x" [size]="14" aria-hidden="true" />
1017
+ </button>
1018
+ </li>
1019
+ }
1020
+ </ul>
1021
+ }
1022
+ <div class="cb-chat-input__row">
1023
+ @if (attachments()) {
1024
+ <button type="button" class="cb-chat-input__icon-btn" [attr.aria-label]="t().chatAttach" [disabled]="disabled()" (click)="fileInput.click()">
1025
+ <cb-icon name="plus" [size]="20" aria-hidden="true" />
1026
+ </button>
1027
+ <input #fileInput type="file" hidden [attr.accept]="accept() || null" [multiple]="multiple()" (change)="onFilesChosen($event)" />
1028
+ }
1029
+ <textarea
1030
+ #textarea
1031
+ class="cb-chat-input__textarea"
1032
+ rows="1"
1033
+ [id]="inputId"
1034
+ [value]="value()"
1035
+ [placeholder]="placeholder() ?? t().chatPlaceholder"
1036
+ [disabled]="disabled()"
1037
+ [attr.maxlength]="maxLength() || null"
1038
+ [attr.aria-label]="ariaLabel() ?? t().chatMessage"
1039
+ [attr.aria-describedby]="maxLength() ? inputId + '-count' : null"
1040
+ (input)="onInput($event)"
1041
+ (keydown)="onKeydown($event)"
1042
+ (paste)="onPaste($event)"
1043
+ ></textarea>
1044
+ @if (streaming()) {
1045
+ <button type="button" class="cb-chat-input__send cb-chat-input__send--stop" [attr.aria-label]="t().chatStop" (click)="stop.emit()">
1046
+ <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><rect x="5" y="5" width="14" height="14" rx="2" fill="currentColor" /></svg>
1047
+ </button>
1048
+ } @else {
1049
+ <button type="submit" class="cb-chat-input__send" [attr.aria-label]="t().chatSend" [disabled]="!canSend()">
1050
+ <cb-icon name="arrow-up" [size]="18" aria-hidden="true" />
1051
+ </button>
1052
+ }
1053
+ </div>
1054
+ </form>
1055
+ @if (maxLength()) {
1056
+ <div class="cb-chat-input__count" [id]="inputId + '-count'">{{ value().length }} / {{ maxLength() }}</div>
1057
+ }
1058
+ `, styles: [":host{display:flex;flex-direction:column;gap:.5rem;color:var(--cb-color-text);font-family:var(--cb-font-sans)}.cb-chat-input__suggestions{display:flex;flex-wrap:wrap;gap:.5rem}.cb-chat-input__suggestion{padding:.375rem .75rem;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-full, 999px);background:var(--cb-color-surface-raised);color:inherit;font:inherit;font-size:var(--cb-text-sm, .875rem);cursor:pointer}.cb-chat-input__suggestion:hover:not(:disabled){background:var(--cb-color-surface-hover)}.cb-chat-input__suggestion:focus-visible,.cb-chat-input__icon-btn:focus-visible,.cb-chat-input__send:focus-visible,.cb-chat-input__file-remove:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-input__box{display:flex;flex-direction:column;gap:.5rem;margin:0;padding:.5rem;border:1px solid var(--cb-color-border-strong);border-radius:var(--cb-radius-lg, 12px);background:var(--cb-color-surface-raised);transition:border-color var(--cb-duration-fast, .15s) ease}.cb-chat-input__box:focus-within{border-color:var(--cb-color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--cb-color-primary) 18%,transparent)}:host(.cb-chat-input--dragging) .cb-chat-input__box{border-style:dashed;border-color:var(--cb-color-primary);background:color-mix(in srgb,var(--cb-color-primary) 6%,var(--cb-color-surface-raised))}:host(.cb-chat-input--disabled) .cb-chat-input__box{opacity:.6}.cb-chat-input__files{display:flex;flex-wrap:wrap;gap:.375rem;margin:0;padding:0;list-style:none}.cb-chat-input__file{display:inline-flex;align-items:center;gap:.375rem;max-width:16rem;padding:.25rem .25rem .25rem .5rem;border-radius:var(--cb-radius-md, 8px);background:var(--cb-color-surface-subtle);font-size:var(--cb-text-xs, .75rem)}.cb-chat-input__file-thumb{flex:none;width:1.75rem;height:1.75rem;border-radius:var(--cb-radius-sm, 4px);object-fit:cover}.cb-chat-input__file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cb-chat-input__file-size{flex:none;color:var(--cb-color-text-muted)}.cb-chat-input__file-remove{display:inline-flex;flex:none;padding:.125rem;border:0;border-radius:var(--cb-radius-sm, 4px);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-chat-input__row{display:flex;align-items:flex-end;gap:.375rem}.cb-chat-input__textarea{flex:1;min-width:0;max-height:none;margin:0;padding:.375rem .25rem;border:0;outline:none;resize:none;overflow-y:hidden;background:transparent;color:inherit;font:inherit;font-size:var(--cb-text-sm, .875rem);line-height:1.5}.cb-chat-input__textarea::placeholder{color:var(--cb-color-text-muted)}.cb-chat-input__icon-btn,.cb-chat-input__send{display:inline-flex;flex:none;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border:0;border-radius:50%;cursor:pointer}.cb-chat-input__icon-btn{background:transparent;color:var(--cb-color-text-muted)}.cb-chat-input__icon-btn:hover:not(:disabled){background:var(--cb-color-surface-hover);color:var(--cb-color-text)}.cb-chat-input__send{background:var(--cb-color-primary);color:var(--cb-color-primary-contrast)}.cb-chat-input__send:disabled{background:var(--cb-color-surface-hover);color:var(--cb-color-text-muted);cursor:not-allowed}.cb-chat-input__send--stop{background:var(--cb-color-text-strong);color:var(--cb-color-surface-raised)}.cb-chat-input__count{align-self:flex-end;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}\n"] }]
1059
+ }], ctorParameters: () => [], propDecorators: { textarea: [{ type: i0.ViewChild, args: ['textarea', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }, { type: i0.Output, args: ["filesChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }], submitOnEnter: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitOnEnter", required: false }] }], clearOnSend: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearOnSend", required: false }] }], suggestions: [{ type: i0.Input, args: [{ isSignal: true, alias: "suggestions", required: false }] }], send: [{ type: i0.Output, args: ["send"] }], stop: [{ type: i0.Output, args: ["stop"] }] } });
1060
+
1061
+ let nextUid$1 = 0;
1062
+ /**
1063
+ * Razonamiento del modelo ("Pensando…"), plegable. Mientras llega se abre y
1064
+ * muestra un brillo; al terminar se pliega solo (`autoCollapse`) y resume
1065
+ * "Razonó durante N s". El texto admite markdown seguro.
1066
+ *
1067
+ * @example
1068
+ * ```html
1069
+ * <cb-chat-reasoning [text]="m.reasoning" [streaming]="m.pensando" [duration]="m.segundos" />
1070
+ * ```
1071
+ */
1072
+ class CbChatReasoning {
1073
+ constructor() {
1074
+ /** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
1075
+ this.t = injectCbTranslation();
1076
+ this.uid = `cb-chat-reasoning-${++nextUid$1}`;
1077
+ /** Texto del razonamiento (markdown). */
1078
+ this.text = input('', /* @ts-ignore */
1079
+ ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
1080
+ /** El razonamiento sigue llegando. */
1081
+ this.streaming = input(false, { ...(ngDevMode ? { debugName: "streaming" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1082
+ /** Segundos que duró (se muestra al terminar). */
1083
+ this.duration = input(undefined, /* @ts-ignore */
1084
+ ...(ngDevMode ? [{ debugName: "duration" }] : /* istanbul ignore next */ []));
1085
+ /** Desplegado. */
1086
+ this.open = model(false, /* @ts-ignore */
1087
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
1088
+ /** Se abre mientras llega y se pliega al terminar. */
1089
+ this.autoCollapse = input(true, { ...(ngDevMode ? { debugName: "autoCollapse" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1090
+ this.html = computed(() => cbMarkdownToHtml(this.text()), /* @ts-ignore */
1091
+ ...(ngDevMode ? [{ debugName: "html" }] : /* istanbul ignore next */ []));
1092
+ this.summary = computed(() => {
1093
+ if (this.streaming())
1094
+ return this.t().chatThinking;
1095
+ const seconds = this.duration();
1096
+ return seconds !== undefined ? cbFormat(this.t().chatThoughtFor, String(Math.max(1, Math.round(seconds)))) : this.t().chatReasoning;
1097
+ }, /* @ts-ignore */
1098
+ ...(ngDevMode ? [{ debugName: "summary" }] : /* istanbul ignore next */ []));
1099
+ effect(() => {
1100
+ const streaming = this.streaming();
1101
+ untracked(() => {
1102
+ if (this.autoCollapse())
1103
+ this.open.set(streaming);
1104
+ });
1105
+ });
1106
+ }
1107
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatReasoning, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1108
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbChatReasoning, isStandalone: true, selector: "cb-chat-reasoning", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, duration: { classPropertyName: "duration", publicName: "duration", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, autoCollapse: { classPropertyName: "autoCollapse", publicName: "autoCollapse", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { properties: { "class.cb-chat-reasoning--streaming": "streaming()" }, classAttribute: "cb-chat-reasoning" }, ngImport: i0, template: `
1109
+ <button
1110
+ type="button"
1111
+ class="cb-chat-reasoning__toggle"
1112
+ [attr.aria-expanded]="open()"
1113
+ [attr.aria-controls]="uid"
1114
+ (click)="open.set(!open())"
1115
+ >
1116
+ <cb-icon name="sparkles" [size]="14" aria-hidden="true" />
1117
+ <span class="cb-chat-reasoning__label">{{ summary() }}</span>
1118
+ <cb-icon class="cb-chat-reasoning__chevron" name="chevron-down" [size]="14" aria-hidden="true" />
1119
+ </button>
1120
+ @if (open()) {
1121
+ <div class="cb-chat-reasoning__body" [id]="uid" role="region" [attr.aria-label]="t().chatReasoning" [attr.aria-busy]="streaming() ? 'true' : null">
1122
+ @if (text()) {
1123
+ <div class="cb-chat-reasoning__markdown" [innerHTML]="html()"></div>
1124
+ }
1125
+ <ng-content />
1126
+ </div>
1127
+ }
1128
+ `, isInline: true, styles: [":host{display:block;max-width:100%;color:var(--cb-color-text-muted);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm, .875rem)}.cb-chat-reasoning__toggle{display:inline-flex;align-items:center;gap:.375rem;padding:.25rem .5rem .25rem .25rem;border:0;border-radius:var(--cb-radius-sm, 4px);background:transparent;color:inherit;font:inherit;cursor:pointer}.cb-chat-reasoning__toggle:hover{color:var(--cb-color-text)}.cb-chat-reasoning__toggle:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-reasoning__chevron{transition:transform var(--cb-duration-fast, .15s) ease}.cb-chat-reasoning__toggle[aria-expanded=true] .cb-chat-reasoning__chevron{transform:rotate(180deg)}:host(.cb-chat-reasoning--streaming) .cb-chat-reasoning__label{background:linear-gradient(90deg,currentColor 25%,var(--cb-color-text-strong) 50%,currentColor 75%);background-size:200% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:cb-chat-shimmer 1.6s linear infinite}@keyframes cb-chat-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}.cb-chat-reasoning__body{margin:.25rem 0 0 .5rem;padding:.25rem 0 .25rem .875rem;border-left:2px solid var(--cb-color-border);line-height:1.55}.cb-chat-reasoning__markdown ::ng-deep p{margin:0 0 .5rem}.cb-chat-reasoning__markdown ::ng-deep p:last-child{margin-bottom:0}.cb-chat-reasoning__markdown ::ng-deep code{font-family:var(--cb-font-mono);font-size:.9em}@media(prefers-reduced-motion:reduce){:host(.cb-chat-reasoning--streaming) .cb-chat-reasoning__label{animation:none;background:none;color:inherit}}\n"], dependencies: [{ kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1129
+ }
1130
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatReasoning, decorators: [{
1131
+ type: Component,
1132
+ args: [{ selector: 'cb-chat-reasoning', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon], host: { class: 'cb-chat-reasoning', '[class.cb-chat-reasoning--streaming]': 'streaming()' }, template: `
1133
+ <button
1134
+ type="button"
1135
+ class="cb-chat-reasoning__toggle"
1136
+ [attr.aria-expanded]="open()"
1137
+ [attr.aria-controls]="uid"
1138
+ (click)="open.set(!open())"
1139
+ >
1140
+ <cb-icon name="sparkles" [size]="14" aria-hidden="true" />
1141
+ <span class="cb-chat-reasoning__label">{{ summary() }}</span>
1142
+ <cb-icon class="cb-chat-reasoning__chevron" name="chevron-down" [size]="14" aria-hidden="true" />
1143
+ </button>
1144
+ @if (open()) {
1145
+ <div class="cb-chat-reasoning__body" [id]="uid" role="region" [attr.aria-label]="t().chatReasoning" [attr.aria-busy]="streaming() ? 'true' : null">
1146
+ @if (text()) {
1147
+ <div class="cb-chat-reasoning__markdown" [innerHTML]="html()"></div>
1148
+ }
1149
+ <ng-content />
1150
+ </div>
1151
+ }
1152
+ `, styles: [":host{display:block;max-width:100%;color:var(--cb-color-text-muted);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm, .875rem)}.cb-chat-reasoning__toggle{display:inline-flex;align-items:center;gap:.375rem;padding:.25rem .5rem .25rem .25rem;border:0;border-radius:var(--cb-radius-sm, 4px);background:transparent;color:inherit;font:inherit;cursor:pointer}.cb-chat-reasoning__toggle:hover{color:var(--cb-color-text)}.cb-chat-reasoning__toggle:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-chat-reasoning__chevron{transition:transform var(--cb-duration-fast, .15s) ease}.cb-chat-reasoning__toggle[aria-expanded=true] .cb-chat-reasoning__chevron{transform:rotate(180deg)}:host(.cb-chat-reasoning--streaming) .cb-chat-reasoning__label{background:linear-gradient(90deg,currentColor 25%,var(--cb-color-text-strong) 50%,currentColor 75%);background-size:200% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:cb-chat-shimmer 1.6s linear infinite}@keyframes cb-chat-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}.cb-chat-reasoning__body{margin:.25rem 0 0 .5rem;padding:.25rem 0 .25rem .875rem;border-left:2px solid var(--cb-color-border);line-height:1.55}.cb-chat-reasoning__markdown ::ng-deep p{margin:0 0 .5rem}.cb-chat-reasoning__markdown ::ng-deep p:last-child{margin-bottom:0}.cb-chat-reasoning__markdown ::ng-deep code{font-family:var(--cb-font-mono);font-size:.9em}@media(prefers-reduced-motion:reduce){:host(.cb-chat-reasoning--streaming) .cb-chat-reasoning__label{animation:none;background:none;color:inherit}}\n"] }]
1153
+ }], ctorParameters: () => [], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], duration: [{ type: i0.Input, args: [{ isSignal: true, alias: "duration", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], autoCollapse: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCollapse", required: false }] }] } });
1154
+
1155
+ let nextUid = 0;
1156
+ /** Objeto → JSON legible; texto, tal cual. */
1157
+ function show(value) {
1158
+ if (value === undefined || value === null || value === '')
1159
+ return null;
1160
+ if (typeof value === 'string') {
1161
+ try {
1162
+ return { code: JSON.stringify(JSON.parse(value), null, 2), language: 'json' };
1163
+ }
1164
+ catch {
1165
+ return { code: value, language: '' };
1166
+ }
1167
+ }
1168
+ try {
1169
+ return { code: JSON.stringify(value, null, 2), language: 'json' };
1170
+ }
1171
+ catch {
1172
+ return { code: String(value), language: '' };
1173
+ }
1174
+ }
1175
+ /**
1176
+ * Llamada a una herramienta (función) del asistente: nombre, estado
1177
+ * ("Usando buscar_movimientos…", "Usó…", "Falló…") y, al desplegar, la
1178
+ * entrada y el resultado como JSON resaltado.
1179
+ *
1180
+ * @example
1181
+ * ```html
1182
+ * <cb-chat-tool name="buscar_movimientos" status="done" [input]="{ cuenta: '4829' }" [output]="resultado" />
1183
+ * ```
1184
+ */
1185
+ class CbChatTool {
1186
+ constructor() {
1187
+ /** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
1188
+ this.t = injectCbTranslation();
1189
+ this.uid = `cb-chat-tool-${++nextUid}`;
1190
+ /** Nombre de la herramienta (`buscar_movimientos`). */
1191
+ this.name = input('', /* @ts-ignore */
1192
+ ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
1193
+ /** Texto que sustituye al nombre en la cabecera ("Buscando movimientos"). */
1194
+ this.title = input('', /* @ts-ignore */
1195
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
1196
+ this.status = input('done', /* @ts-ignore */
1197
+ ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
1198
+ /** Argumentos (objeto o JSON). */
1199
+ this.input = input(undefined, /* @ts-ignore */
1200
+ ...(ngDevMode ? [{ debugName: "input" }] : /* istanbul ignore next */ []));
1201
+ /** Resultado (objeto, JSON o texto). */
1202
+ this.output = input(undefined, /* @ts-ignore */
1203
+ ...(ngDevMode ? [{ debugName: "output" }] : /* istanbul ignore next */ []));
1204
+ /** Desplegado. */
1205
+ this.open = model(false, /* @ts-ignore */
1206
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
1207
+ this.shownInput = computed(() => show(this.input()), /* @ts-ignore */
1208
+ ...(ngDevMode ? [{ debugName: "shownInput" }] : /* istanbul ignore next */ []));
1209
+ this.shownOutput = computed(() => show(this.output()), /* @ts-ignore */
1210
+ ...(ngDevMode ? [{ debugName: "shownOutput" }] : /* istanbul ignore next */ []));
1211
+ this.label = computed(() => {
1212
+ const name = this.title() || this.name();
1213
+ const status = this.status();
1214
+ if (status === 'running')
1215
+ return cbFormat(this.t().chatToolRunning, name);
1216
+ if (status === 'error')
1217
+ return cbFormat(this.t().chatToolError, name);
1218
+ return cbFormat(this.t().chatToolDone, name);
1219
+ }, /* @ts-ignore */
1220
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1221
+ }
1222
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatTool, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1223
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbChatTool, isStandalone: true, selector: "cb-chat-tool", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, input: { classPropertyName: "input", publicName: "input", isSignal: true, isRequired: false, transformFunction: null }, output: { classPropertyName: "output", publicName: "output", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { properties: { "class.cb-chat-tool--running": "status() === 'running'", "class.cb-chat-tool--done": "status() === 'done'", "class.cb-chat-tool--error": "status() === 'error'" }, classAttribute: "cb-chat-tool" }, ngImport: i0, template: `
1224
+ <button type="button" class="cb-chat-tool__header" [attr.aria-expanded]="open()" [attr.aria-controls]="uid" (click)="open.set(!open())">
1225
+ @switch (status()) {
1226
+ @case ('running') {
1227
+ <span class="cb-chat-tool__spinner" aria-hidden="true"></span>
1228
+ }
1229
+ @case ('error') {
1230
+ <cb-icon class="cb-chat-tool__icon" name="circle-alert" [size]="16" aria-hidden="true" />
1231
+ }
1232
+ @default {
1233
+ <cb-icon class="cb-chat-tool__icon" name="check" [size]="16" aria-hidden="true" />
1234
+ }
1235
+ }
1236
+ <span class="cb-chat-tool__label">{{ label() }}</span>
1237
+ <cb-icon class="cb-chat-tool__chevron" name="chevron-down" [size]="14" aria-hidden="true" />
1238
+ </button>
1239
+ <span class="cb-chat-tool__status" role="status">{{ status() === 'running' ? '' : label() }}</span>
1240
+ @if (open()) {
1241
+ <div class="cb-chat-tool__body" [id]="uid">
1242
+ @if (shownInput(); as i) {
1243
+ <div class="cb-chat-tool__section">{{ t().chatToolInput }}</div>
1244
+ <cb-code-block [code]="i.code" [language]="i.language" [header]="false" wrap maxHeight="14rem" [ariaLabel]="t().chatToolInput" />
1245
+ }
1246
+ @if (shownOutput(); as o) {
1247
+ <div class="cb-chat-tool__section">{{ t().chatToolOutput }}</div>
1248
+ <cb-code-block [code]="o.code" [language]="o.language" [header]="false" wrap maxHeight="14rem" [ariaLabel]="t().chatToolOutput" />
1249
+ }
1250
+ <ng-content />
1251
+ </div>
1252
+ }
1253
+ `, isInline: true, styles: [":host{display:block;max-width:100%;overflow:hidden;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md, 8px);background:var(--cb-color-surface-raised);color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm, .875rem)}.cb-chat-tool__header{display:flex;align-items:center;gap:.5rem;width:100%;padding:.5rem .75rem;border:0;background:transparent;color:inherit;font:inherit;text-align:start;cursor:pointer}.cb-chat-tool__header:hover{background:var(--cb-color-surface-hover)}.cb-chat-tool__header:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:-2px}.cb-chat-tool__label{flex:1;min-width:0;overflow:hidden;font-family:var(--cb-font-mono);font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap}:host{--cb-chat-tool-ok: var(--cb-color-success-700);--cb-chat-tool-error: var(--cb-color-danger-700)}:host-context([data-theme=\"dark\"]){--cb-chat-tool-ok: var(--cb-color-success-400);--cb-chat-tool-error: var(--cb-color-danger-400)}:host(.cb-chat-tool--done) .cb-chat-tool__icon{color:var(--cb-chat-tool-ok)}:host(.cb-chat-tool--error) .cb-chat-tool__icon{color:var(--cb-chat-tool-error)}.cb-chat-tool__spinner{flex:none;width:14px;height:14px;border:2px solid var(--cb-color-border-strong);border-top-color:var(--cb-color-primary);border-radius:50%;animation:cb-chat-spin .8s linear infinite}@keyframes cb-chat-spin{to{transform:rotate(360deg)}}.cb-chat-tool__chevron{flex:none;color:var(--cb-color-text-muted);transition:transform var(--cb-duration-fast, .15s) ease}.cb-chat-tool__header[aria-expanded=true] .cb-chat-tool__chevron{transform:rotate(180deg)}.cb-chat-tool__status{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}.cb-chat-tool__body{display:flex;flex-direction:column;gap:.375rem;padding:0 .75rem .75rem}.cb-chat-tool__section{margin-top:.25rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-semibold, 600)}@media(prefers-reduced-motion:reduce){.cb-chat-tool__spinner{animation-duration:2.4s}}\n"], dependencies: [{ kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }, { kind: "component", type: CbCodeBlock, selector: "cb-code-block", inputs: ["code", "language", "filename", "lineNumbers", "startLine", "highlightLines", "wrap", "copyable", "header", "maxHeight", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1254
+ }
1255
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbChatTool, decorators: [{
1256
+ type: Component,
1257
+ args: [{ selector: 'cb-chat-tool', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon, CbCodeBlock], host: {
1258
+ class: 'cb-chat-tool',
1259
+ '[class.cb-chat-tool--running]': "status() === 'running'",
1260
+ '[class.cb-chat-tool--done]': "status() === 'done'",
1261
+ '[class.cb-chat-tool--error]': "status() === 'error'",
1262
+ }, template: `
1263
+ <button type="button" class="cb-chat-tool__header" [attr.aria-expanded]="open()" [attr.aria-controls]="uid" (click)="open.set(!open())">
1264
+ @switch (status()) {
1265
+ @case ('running') {
1266
+ <span class="cb-chat-tool__spinner" aria-hidden="true"></span>
1267
+ }
1268
+ @case ('error') {
1269
+ <cb-icon class="cb-chat-tool__icon" name="circle-alert" [size]="16" aria-hidden="true" />
1270
+ }
1271
+ @default {
1272
+ <cb-icon class="cb-chat-tool__icon" name="check" [size]="16" aria-hidden="true" />
1273
+ }
1274
+ }
1275
+ <span class="cb-chat-tool__label">{{ label() }}</span>
1276
+ <cb-icon class="cb-chat-tool__chevron" name="chevron-down" [size]="14" aria-hidden="true" />
1277
+ </button>
1278
+ <span class="cb-chat-tool__status" role="status">{{ status() === 'running' ? '' : label() }}</span>
1279
+ @if (open()) {
1280
+ <div class="cb-chat-tool__body" [id]="uid">
1281
+ @if (shownInput(); as i) {
1282
+ <div class="cb-chat-tool__section">{{ t().chatToolInput }}</div>
1283
+ <cb-code-block [code]="i.code" [language]="i.language" [header]="false" wrap maxHeight="14rem" [ariaLabel]="t().chatToolInput" />
1284
+ }
1285
+ @if (shownOutput(); as o) {
1286
+ <div class="cb-chat-tool__section">{{ t().chatToolOutput }}</div>
1287
+ <cb-code-block [code]="o.code" [language]="o.language" [header]="false" wrap maxHeight="14rem" [ariaLabel]="t().chatToolOutput" />
1288
+ }
1289
+ <ng-content />
1290
+ </div>
1291
+ }
1292
+ `, styles: [":host{display:block;max-width:100%;overflow:hidden;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md, 8px);background:var(--cb-color-surface-raised);color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm, .875rem)}.cb-chat-tool__header{display:flex;align-items:center;gap:.5rem;width:100%;padding:.5rem .75rem;border:0;background:transparent;color:inherit;font:inherit;text-align:start;cursor:pointer}.cb-chat-tool__header:hover{background:var(--cb-color-surface-hover)}.cb-chat-tool__header:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:-2px}.cb-chat-tool__label{flex:1;min-width:0;overflow:hidden;font-family:var(--cb-font-mono);font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap}:host{--cb-chat-tool-ok: var(--cb-color-success-700);--cb-chat-tool-error: var(--cb-color-danger-700)}:host-context([data-theme=\"dark\"]){--cb-chat-tool-ok: var(--cb-color-success-400);--cb-chat-tool-error: var(--cb-color-danger-400)}:host(.cb-chat-tool--done) .cb-chat-tool__icon{color:var(--cb-chat-tool-ok)}:host(.cb-chat-tool--error) .cb-chat-tool__icon{color:var(--cb-chat-tool-error)}.cb-chat-tool__spinner{flex:none;width:14px;height:14px;border:2px solid var(--cb-color-border-strong);border-top-color:var(--cb-color-primary);border-radius:50%;animation:cb-chat-spin .8s linear infinite}@keyframes cb-chat-spin{to{transform:rotate(360deg)}}.cb-chat-tool__chevron{flex:none;color:var(--cb-color-text-muted);transition:transform var(--cb-duration-fast, .15s) ease}.cb-chat-tool__header[aria-expanded=true] .cb-chat-tool__chevron{transform:rotate(180deg)}.cb-chat-tool__status{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}.cb-chat-tool__body{display:flex;flex-direction:column;gap:.375rem;padding:0 .75rem .75rem}.cb-chat-tool__section{margin-top:.25rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-semibold, 600)}@media(prefers-reduced-motion:reduce){.cb-chat-tool__spinner{animation-duration:2.4s}}\n"] }]
1293
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], input: [{ type: i0.Input, args: [{ isSignal: true, alias: "input", required: false }] }], output: [{ type: i0.Input, args: [{ isSignal: true, alias: "output", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }] } });
1294
+
1295
+ /*
1296
+ * Public API Surface de @caribeng/ui/chat
1297
+ */
1298
+
1299
+ /**
1300
+ * Generated bundle index. Do not edit.
1301
+ */
1302
+
1303
+ export { CbChatInput, CbChatMessage, CbChatMessages, CbChatReasoning, CbChatTool, cbMarkdownToBlocks, cbMarkdownToHtml, cbSafeUrl };