@signalflare-ai/ui 1.2.0 → 1.3.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 (317) hide show
  1. package/CHANGELOG.md +77 -1
  2. package/ai/component-registry.json +208 -550
  3. package/ai/component-registry.md +3042 -3115
  4. package/ai/schemas.ts +504 -96
  5. package/dist/.build-complete +1 -1
  6. package/dist/ai/schemas.d.ts.map +1 -1
  7. package/dist/{ai-actions-BdUZI3Gk.js → ai-actions-CBfz5XEf.js} +4 -4
  8. package/dist/{ai-actions-BdUZI3Gk.js.map → ai-actions-CBfz5XEf.js.map} +1 -1
  9. package/dist/{ai-agent-card-BR2NIYhi.js → ai-agent-card-CByAUe0q.js} +3 -3
  10. package/dist/ai-agent-card-CByAUe0q.js.map +1 -0
  11. package/dist/{ai-approval-Ba7mrKba.js → ai-approval-Ci8N70a7.js} +4 -3
  12. package/dist/{ai-approval-Ba7mrKba.js.map → ai-approval-Ci8N70a7.js.map} +1 -1
  13. package/dist/{ai-code-block-CZtoL73R.js → ai-code-block-P9TJHvaC.js} +37 -39
  14. package/dist/ai-code-block-P9TJHvaC.js.map +1 -0
  15. package/dist/{ai-conversation-Cc7WlaBg.js → ai-conversation-Qslfdi1t.js} +28 -42
  16. package/dist/ai-conversation-Qslfdi1t.js.map +1 -0
  17. package/dist/{ai-info-banner-C7EWPBj7.js → ai-info-banner-B_9vtGK3.js} +3 -3
  18. package/dist/{ai-info-banner-C7EWPBj7.js.map → ai-info-banner-B_9vtGK3.js.map} +1 -1
  19. package/dist/{ai-message-Bp7L68U_.js → ai-message-Ci3gwM7G.js} +6 -6
  20. package/dist/{ai-message-Bp7L68U_.js.map → ai-message-Ci3gwM7G.js.map} +1 -1
  21. package/dist/{ai-mission-header-TiCJfTNt.js → ai-mission-header-CaBc19-t.js} +2 -2
  22. package/dist/{ai-mission-header-TiCJfTNt.js.map → ai-mission-header-CaBc19-t.js.map} +1 -1
  23. package/dist/{ai-part-group-DNb9I446.js → ai-part-group-Dx1Mr92B.js} +5 -4
  24. package/dist/ai-part-group-Dx1Mr92B.js.map +1 -0
  25. package/dist/{ai-prompt-input-BVvov_KF.js → ai-prompt-input-Bm4XoSj2.js} +19 -17
  26. package/dist/ai-prompt-input-Bm4XoSj2.js.map +1 -0
  27. package/dist/{ai-question-GPPMk7YM.js → ai-question-OyJovxGe.js} +4 -3
  28. package/dist/{ai-question-GPPMk7YM.js.map → ai-question-OyJovxGe.js.map} +1 -1
  29. package/dist/{ai-reasoning-_feFjk56.js → ai-reasoning-BLfBXx3F.js} +9 -5
  30. package/dist/ai-reasoning-BLfBXx3F.js.map +1 -0
  31. package/dist/{ai-response-CvjV3WhV.js → ai-response-hbVCZJmo.js} +2 -2
  32. package/dist/{ai-response-CvjV3WhV.js.map → ai-response-hbVCZJmo.js.map} +1 -1
  33. package/dist/{ai-shimmer-j6lKIrjj.js → ai-shimmer-BamNMNK3.js} +2 -2
  34. package/dist/{ai-shimmer-j6lKIrjj.js.map → ai-shimmer-BamNMNK3.js.map} +1 -1
  35. package/dist/{ai-status-badge-CSU_QOdz.js → ai-status-badge-BZLczdkI.js} +2 -2
  36. package/dist/{ai-status-badge-CSU_QOdz.js.map → ai-status-badge-BZLczdkI.js.map} +1 -1
  37. package/dist/{ai-streaming-text-IWW1BhvZ.js → ai-streaming-text-DgYu64UH.js} +1 -1
  38. package/dist/{ai-streaming-text-IWW1BhvZ.js.map → ai-streaming-text-DgYu64UH.js.map} +1 -1
  39. package/dist/{ai-subagent-JA4iIMW3.js → ai-subagent-p97AI1h9.js} +3 -3
  40. package/dist/{ai-subagent-JA4iIMW3.js.map → ai-subagent-p97AI1h9.js.map} +1 -1
  41. package/dist/{ai-suggestion-BdO6MBuH.js → ai-suggestion-Bj6vF7CT.js} +3 -3
  42. package/dist/{ai-suggestion-BdO6MBuH.js.map → ai-suggestion-Bj6vF7CT.js.map} +1 -1
  43. package/dist/{ai-task-list-DYw4R1FA.js → ai-task-list-C_UQYpk9.js} +6 -4
  44. package/dist/{ai-task-list-DYw4R1FA.js.map → ai-task-list-C_UQYpk9.js.map} +1 -1
  45. package/dist/{ai-timeline-C42tOUT8.js → ai-timeline-CePL1LOU.js} +3 -3
  46. package/dist/ai-timeline-CePL1LOU.js.map +1 -0
  47. package/dist/{ai-tool-03jOTwUI.js → ai-tool-CfRcwmHT.js} +17 -11
  48. package/dist/ai-tool-CfRcwmHT.js.map +1 -0
  49. package/dist/{ai-usage-bar-BRf5LC_b.js → ai-usage-bar-45pVRCGA.js} +2 -2
  50. package/dist/{ai-usage-bar-BRf5LC_b.js.map → ai-usage-bar-45pVRCGA.js.map} +1 -1
  51. package/dist/{badge-BheXjMc8.js → badge-Beb-6uut.js} +5 -5
  52. package/dist/{badge-BheXjMc8.js.map → badge-Beb-6uut.js.map} +1 -1
  53. package/dist/{banner-CcsjunJg.js → banner-CCEksxPg.js} +3 -3
  54. package/dist/{banner-CcsjunJg.js.map → banner-CCEksxPg.js.map} +1 -1
  55. package/dist/{breadcrumbs-CouSyy3H.js → breadcrumbs-HiTmgaZ4.js} +5 -5
  56. package/dist/{breadcrumbs-CouSyy3H.js.map → breadcrumbs-HiTmgaZ4.js.map} +1 -1
  57. package/dist/{button-CO6-qPax.js → button-BHOgXJRU.js} +4 -4
  58. package/dist/{button-CO6-qPax.js.map → button-BHOgXJRU.js.map} +1 -1
  59. package/dist/catalog.js +1 -1
  60. package/dist/catalog.js.map +1 -1
  61. package/dist/{chart-Dg0qUeSc.js → chart-B9FfZdKs.js} +7 -7
  62. package/dist/chart-B9FfZdKs.js.map +1 -0
  63. package/dist/{checkbox-D7p4QKsC.js → checkbox-Cy_OCyay.js} +3 -3
  64. package/dist/{checkbox-D7p4QKsC.js.map → checkbox-Cy_OCyay.js.map} +1 -1
  65. package/dist/{clipboard-text-kLaMogs3.js → clipboard-text-CKSvNp9L.js} +6 -5
  66. package/dist/clipboard-text-CKSvNp9L.js.map +1 -0
  67. package/dist/{cn-YROP2_ox.js → cn-CmAOpn49.js} +2 -2
  68. package/dist/{cn-YROP2_ox.js.map → cn-CmAOpn49.js.map} +1 -1
  69. package/dist/{code-BN8InC0G.js → code-JsQz-0G_.js} +4 -4
  70. package/dist/{code-BN8InC0G.js.map → code-JsQz-0G_.js.map} +1 -1
  71. package/dist/{collapsible-D_ueZ0jz.js → collapsible-1kOZ-89L.js} +2 -2
  72. package/dist/{collapsible-D_ueZ0jz.js.map → collapsible-1kOZ-89L.js.map} +1 -1
  73. package/dist/{combobox-B7TOK0U2.js → combobox-CQwDmqgA.js} +4 -4
  74. package/dist/{combobox-B7TOK0U2.js.map → combobox-CQwDmqgA.js.map} +1 -1
  75. package/dist/command-line/cli.js +3 -3
  76. package/dist/{command-palette-CuNUyJca.js → command-palette-Bkuv3e6o.js} +20 -5
  77. package/dist/command-palette-Bkuv3e6o.js.map +1 -0
  78. package/dist/components/ai-actions.js +1 -1
  79. package/dist/components/ai-agent-card.js +1 -1
  80. package/dist/components/ai-approval.js +1 -1
  81. package/dist/components/ai-code-block.js +1 -1
  82. package/dist/components/ai-conversation.js +1 -1
  83. package/dist/components/ai-info-banner.js +1 -1
  84. package/dist/components/ai-message.js +1 -1
  85. package/dist/components/ai-mission-header.js +1 -1
  86. package/dist/components/ai-part-group.js +1 -1
  87. package/dist/components/ai-prompt-input.js +1 -1
  88. package/dist/components/ai-question.js +1 -1
  89. package/dist/components/ai-reasoning.js +1 -1
  90. package/dist/components/ai-response.js +1 -1
  91. package/dist/components/ai-shimmer.js +1 -1
  92. package/dist/components/ai-status-badge.js +1 -1
  93. package/dist/components/ai-streaming-text.js +1 -1
  94. package/dist/components/ai-subagent.js +1 -1
  95. package/dist/components/ai-suggestion.js +1 -1
  96. package/dist/components/ai-task-list.js +1 -1
  97. package/dist/components/ai-timeline.js +1 -1
  98. package/dist/components/ai-tool.js +1 -1
  99. package/dist/components/ai-usage-bar.js +1 -1
  100. package/dist/components/badge.js +1 -1
  101. package/dist/components/banner.js +1 -1
  102. package/dist/components/breadcrumbs.js +1 -1
  103. package/dist/components/button.js +1 -1
  104. package/dist/components/chart.js +2 -2
  105. package/dist/components/checkbox.js +1 -1
  106. package/dist/components/clipboard-text.js +1 -1
  107. package/dist/components/code.js +1 -1
  108. package/dist/components/collapsible.js +1 -1
  109. package/dist/components/combobox.js +1 -1
  110. package/dist/components/command-palette.js +1 -1
  111. package/dist/components/data-grid.js +1 -1
  112. package/dist/components/date-picker.js +1 -1
  113. package/dist/components/date-range-picker.js +1 -1
  114. package/dist/components/dialog.js +1 -1
  115. package/dist/components/dropdown.js +1 -1
  116. package/dist/components/empty.js +1 -1
  117. package/dist/components/field.js +1 -1
  118. package/dist/components/filters.js +1 -1
  119. package/dist/components/flow.js +1 -1
  120. package/dist/components/grid.js +1 -1
  121. package/dist/components/input.js +2 -2
  122. package/dist/components/label.js +1 -1
  123. package/dist/components/layer-card.js +1 -1
  124. package/dist/components/link.js +3 -3
  125. package/dist/components/link.js.map +1 -1
  126. package/dist/components/loader.js +2 -2
  127. package/dist/components/menubar.js +1 -1
  128. package/dist/components/meter.js +1 -1
  129. package/dist/components/pagination.js +1 -1
  130. package/dist/components/popover.js +1 -1
  131. package/dist/components/radio.js +1 -1
  132. package/dist/components/select.js +1 -1
  133. package/dist/components/sensitive-input.js +1 -1
  134. package/dist/components/sidebar.js +1 -1
  135. package/dist/components/signalflare-ai-logo.js +1 -1
  136. package/dist/components/sparkline.js +1 -1
  137. package/dist/components/stat-card.js +1 -1
  138. package/dist/components/surface.js +1 -1
  139. package/dist/components/switch.js +1 -1
  140. package/dist/components/table.js +1 -1
  141. package/dist/components/tabs.js +1 -1
  142. package/dist/components/text-roll.js +1 -1
  143. package/dist/components/text.js +1 -1
  144. package/dist/components/theme-toggle.js +1 -1
  145. package/dist/components/toast.js +1 -1
  146. package/dist/components/tooltip.js +1 -1
  147. package/dist/components/use-agent-harness.js +1 -1
  148. package/dist/{data-grid-DGHmU0w3.js → data-grid-DDSFMHud.js} +136 -53
  149. package/dist/data-grid-DDSFMHud.js.map +1 -0
  150. package/dist/{date-picker--ox89RBy.js → date-picker-O34AqG3f.js} +2 -2
  151. package/dist/{date-picker--ox89RBy.js.map → date-picker-O34AqG3f.js.map} +1 -1
  152. package/dist/{date-range-picker-DVa7QBqE.js → date-range-picker-YKYvum_r.js} +29 -39
  153. package/dist/{date-range-picker-DVa7QBqE.js.map → date-range-picker-YKYvum_r.js.map} +1 -1
  154. package/dist/{dialog-Bv1oSFOd.js → dialog-DYqu4aDO.js} +3 -3
  155. package/dist/{dialog-Bv1oSFOd.js.map → dialog-DYqu4aDO.js.map} +1 -1
  156. package/dist/{dist-B6iWiWwp.js → dist-6AtBsaJE.js} +153 -47
  157. package/dist/dist-6AtBsaJE.js.map +1 -0
  158. package/dist/{dropdown-B_nrGXjV.js → dropdown-XzbnRLYR.js} +15 -5
  159. package/dist/dropdown-XzbnRLYR.js.map +1 -0
  160. package/dist/{echart-CdOUaT-r.js → echart-DGBIVAv1.js} +23 -57
  161. package/dist/{echart-CdOUaT-r.js.map → echart-DGBIVAv1.js.map} +1 -1
  162. package/dist/{empty-DZnN0zKX.js → empty-C1tAkawe.js} +6 -6
  163. package/dist/{empty-DZnN0zKX.js.map → empty-C1tAkawe.js.map} +1 -1
  164. package/dist/{field-B_yVof52.js → field-DBpFzzBS.js} +3 -3
  165. package/dist/{field-B_yVof52.js.map → field-DBpFzzBS.js.map} +1 -1
  166. package/dist/{filters-cpJCY21R.js → filters-SmEl93za.js} +10 -10
  167. package/dist/filters-SmEl93za.js.map +1 -0
  168. package/dist/{flow-B4v198ot.js → flow-BLzgbq1T.js} +6 -6
  169. package/dist/flow-BLzgbq1T.js.map +1 -0
  170. package/dist/genui.js +2 -2
  171. package/dist/genui.js.map +1 -1
  172. package/dist/{grid-CEd64Lnh.js → grid-CifjQL-5.js} +2 -2
  173. package/dist/{grid-CEd64Lnh.js.map → grid-CifjQL-5.js.map} +1 -1
  174. package/dist/{highlight-to-react-D0Yav4jk.js → highlight-to-react-DN9dUCS2.js} +9 -15
  175. package/dist/highlight-to-react-DN9dUCS2.js.map +1 -0
  176. package/dist/index.js +71 -71
  177. package/dist/index.js.map +1 -1
  178. package/dist/{input-ClB_E4Lb.js → input-COmx2M_R.js} +5 -5
  179. package/dist/{input-ClB_E4Lb.js.map → input-COmx2M_R.js.map} +1 -1
  180. package/dist/{input-B2bbijRh.js → input-GkfMQZC_.js} +3 -3
  181. package/dist/{input-B2bbijRh.js.map → input-GkfMQZC_.js.map} +1 -1
  182. package/dist/{label-DUv_urO1.js → label-CiGZ464N.js} +3 -3
  183. package/dist/{label-DUv_urO1.js.map → label-CiGZ464N.js.map} +1 -1
  184. package/dist/{layer-card-BK7eYfwn.js → layer-card-8l8GuLQr.js} +2 -2
  185. package/dist/{layer-card-BK7eYfwn.js.map → layer-card-8l8GuLQr.js.map} +1 -1
  186. package/dist/{layout-DJHMMap2.js → layout-CWBE0qwx.js} +258 -154
  187. package/dist/layout-CWBE0qwx.js.map +1 -0
  188. package/dist/{link-provider-BUZKXaNE.js → link-provider-BSn8YJon.js} +2 -2
  189. package/dist/link-provider-BSn8YJon.js.map +1 -0
  190. package/dist/{loader-DAcc-Uag.js → loader-BEMz8pJO.js} +1 -1
  191. package/dist/{loader-DAcc-Uag.js.map → loader-BEMz8pJO.js.map} +1 -1
  192. package/dist/{measured-text-BI3dTJmH.js → measured-text-CXkdw9Yr.js} +45 -30
  193. package/dist/measured-text-CXkdw9Yr.js.map +1 -0
  194. package/dist/{menubar-Cxf3xeAt.js → menubar-CoOr4ocj.js} +3 -3
  195. package/dist/{menubar-Cxf3xeAt.js.map → menubar-CoOr4ocj.js.map} +1 -1
  196. package/dist/{meter-BFFe9l5b.js → meter-Pf_VOl59.js} +2 -2
  197. package/dist/{meter-BFFe9l5b.js.map → meter-Pf_VOl59.js.map} +1 -1
  198. package/dist/{pagination-yS372Tr4.js → pagination-DSY279Ta.js} +2 -2
  199. package/dist/{pagination-yS372Tr4.js.map → pagination-DSY279Ta.js.map} +1 -1
  200. package/dist/{popover-SRoJaCZr.js → popover-BY-e9co1.js} +2 -2
  201. package/dist/{popover-SRoJaCZr.js.map → popover-BY-e9co1.js.map} +1 -1
  202. package/dist/{radio-BcwhwYNB.js → radio-DZwL13j0.js} +2 -2
  203. package/dist/{radio-BcwhwYNB.js.map → radio-DZwL13j0.js.map} +1 -1
  204. package/dist/{select-DMhdoHMa.js → select-BFifYqHA.js} +6 -6
  205. package/dist/{select-DMhdoHMa.js.map → select-BFifYqHA.js.map} +1 -1
  206. package/dist/{sensitive-input-CJUpIRal.js → sensitive-input-DHLZcM73.js} +4 -4
  207. package/dist/{sensitive-input-CJUpIRal.js.map → sensitive-input-DHLZcM73.js.map} +1 -1
  208. package/dist/{sidebar-D4zrlYpn.js → sidebar-odGsdvG4.js} +6 -7
  209. package/dist/sidebar-odGsdvG4.js.map +1 -0
  210. package/dist/{signalflare-ai-logo-Bipogceq.js → signalflare-ai-logo-CNaDT_w8.js} +2 -2
  211. package/dist/{signalflare-ai-logo-Bipogceq.js.map → signalflare-ai-logo-CNaDT_w8.js.map} +1 -1
  212. package/dist/{skeleton-line-CH1-h6e2.js → skeleton-line-CxxYVTO2.js} +2 -2
  213. package/dist/{skeleton-line-CH1-h6e2.js.map → skeleton-line-CxxYVTO2.js.map} +1 -1
  214. package/dist/{sparkline-DHmgj1d0.js → sparkline-BQ-4j2W2.js} +2 -2
  215. package/dist/{sparkline-DHmgj1d0.js.map → sparkline-BQ-4j2W2.js.map} +1 -1
  216. package/dist/src/blocks/agent-harness/agent-harness.tsx +11 -11
  217. package/dist/src/blocks/commander/commander.tsx +15 -15
  218. package/dist/src/blocks/map-block/map-block.d.ts.map +1 -1
  219. package/dist/src/blocks/map-block/map-block.tsx +11 -7
  220. package/dist/src/components/ai-approval/ai-approval.d.ts.map +1 -1
  221. package/dist/src/components/ai-code-block/ai-code-block.d.ts +14 -13
  222. package/dist/src/components/ai-code-block/ai-code-block.d.ts.map +1 -1
  223. package/dist/src/components/ai-conversation/ai-conversation.d.ts.map +1 -1
  224. package/dist/src/components/ai-part-group/ai-part-group.d.ts.map +1 -1
  225. package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
  226. package/dist/src/components/ai-prompt-input/controller.d.ts.map +1 -1
  227. package/dist/src/components/ai-prompt-input/types.d.ts.map +1 -1
  228. package/dist/src/components/ai-question/ai-question.d.ts.map +1 -1
  229. package/dist/src/components/ai-reasoning/ai-reasoning.d.ts.map +1 -1
  230. package/dist/src/components/ai-response/ai-response.d.ts.map +1 -1
  231. package/dist/src/components/ai-subagent/ai-subagent.d.ts.map +1 -1
  232. package/dist/src/components/ai-tool/ai-tool.d.ts.map +1 -1
  233. package/dist/src/components/chart/echart.d.ts.map +1 -1
  234. package/dist/src/components/clipboard-text/clipboard-text.d.ts.map +1 -1
  235. package/dist/src/components/data-grid/data-grid.d.ts +2 -1
  236. package/dist/src/components/data-grid/data-grid.d.ts.map +1 -1
  237. package/dist/src/components/data-grid/features.d.ts +20 -0
  238. package/dist/src/components/data-grid/features.d.ts.map +1 -0
  239. package/dist/src/components/data-grid/types.d.ts +38 -7
  240. package/dist/src/components/data-grid/types.d.ts.map +1 -1
  241. package/dist/src/components/filters/filters.d.ts.map +1 -1
  242. package/dist/src/components/flow/use-children.d.ts +1 -1
  243. package/dist/src/components/link/link.d.ts.map +1 -1
  244. package/dist/src/components/sidebar/sidebar.d.ts +1 -1
  245. package/dist/src/components/signalflare-ai-logo/signalflare-ai-logo.d.ts.map +1 -1
  246. package/dist/src/components/text/text.d.ts +2 -1
  247. package/dist/src/components/text/text.d.ts.map +1 -1
  248. package/dist/src/components/text-roll/text-roll.d.ts.map +1 -1
  249. package/dist/src/components/theme-toggle/theme-toggle.d.ts.map +1 -1
  250. package/dist/src/components/toast/toast.d.ts.map +1 -1
  251. package/dist/src/utils/highlight-to-react.d.ts.map +1 -1
  252. package/dist/src/utils/measured-text.d.ts.map +1 -1
  253. package/dist/src/utils/use-measured-text.d.ts.map +1 -1
  254. package/dist/{stat-card-Ew-ofzEm.js → stat-card-Bspk4XFr.js} +4 -4
  255. package/dist/stat-card-Bspk4XFr.js.map +1 -0
  256. package/dist/styles/sf-standalone.css +1 -1
  257. package/dist/styles/theme-fedramp.css +3 -12
  258. package/dist/styles/theme-minimal.css +26 -104
  259. package/dist/styles/theme-sf.css +37 -142
  260. package/dist/{surface-DGwRlC0o.js → surface-CWdSFVUx.js} +3 -3
  261. package/dist/{surface-DGwRlC0o.js.map → surface-CWdSFVUx.js.map} +1 -1
  262. package/dist/{switch-BxAMfHdt.js → switch-TA4cByCJ.js} +5 -5
  263. package/dist/switch-TA4cByCJ.js.map +1 -0
  264. package/dist/{table-BBeAtYVZ.js → table-BM8JBGBs.js} +3 -3
  265. package/dist/{table-BBeAtYVZ.js.map → table-BM8JBGBs.js.map} +1 -1
  266. package/dist/{tabs-CeHu7Scn.js → tabs-bnH2vGLv.js} +2 -2
  267. package/dist/{tabs-CeHu7Scn.js.map → tabs-bnH2vGLv.js.map} +1 -1
  268. package/dist/{text-Cqryz7rk.js → text-iQ0YUFNg.js} +4 -5
  269. package/dist/{text-Cqryz7rk.js.map → text-iQ0YUFNg.js.map} +1 -1
  270. package/dist/{text-roll-Ch52hcQj.js → text-roll-C3U2jd2u.js} +5 -2
  271. package/dist/text-roll-C3U2jd2u.js.map +1 -0
  272. package/dist/{theme-toggle-LDfIKEqx.js → theme-toggle-BTVxD-fD.js} +10 -9
  273. package/dist/theme-toggle-BTVxD-fD.js.map +1 -0
  274. package/dist/{toast-CaFQNYng.js → toast-CgZVaAkw.js} +3 -3
  275. package/dist/{toast-CaFQNYng.js.map → toast-CgZVaAkw.js.map} +1 -1
  276. package/dist/{tooltip-g9lFsvcT.js → tooltip-uobk6Oh-.js} +3 -3
  277. package/dist/{tooltip-g9lFsvcT.js.map → tooltip-uobk6Oh-.js.map} +1 -1
  278. package/dist/{use-agent-harness-BTcNJdw4.js → use-agent-harness-Dl8w6X5O.js} +3 -3
  279. package/dist/{use-agent-harness-BTcNJdw4.js.map → use-agent-harness-Dl8w6X5O.js.map} +1 -1
  280. package/dist/utils.js +3 -3
  281. package/package.json +27 -25
  282. package/scripts/component-registry/discovery.ts +11 -10
  283. package/scripts/component-registry/example-cleanup.ts +8 -8
  284. package/scripts/component-registry/index.ts +6 -6
  285. package/scripts/component-registry/schema-generator.ts +1 -1
  286. package/scripts/component-registry/sub-components.ts +35 -23
  287. package/scripts/component-registry/utils.ts +11 -11
  288. package/scripts/component-registry/variant-parser.ts +17 -15
  289. package/scripts/convert-demos-to-stories.ts +5 -5
  290. package/scripts/theme-generator/config.ts +1 -5
  291. package/scripts/theme-generator/generate-css.ts +1 -1
  292. package/scripts/theme-generator/migrate.ts +3 -3
  293. package/dist/ai-agent-card-BR2NIYhi.js.map +0 -1
  294. package/dist/ai-code-block-CZtoL73R.js.map +0 -1
  295. package/dist/ai-conversation-Cc7WlaBg.js.map +0 -1
  296. package/dist/ai-part-group-DNb9I446.js.map +0 -1
  297. package/dist/ai-prompt-input-BVvov_KF.js.map +0 -1
  298. package/dist/ai-reasoning-_feFjk56.js.map +0 -1
  299. package/dist/ai-timeline-C42tOUT8.js.map +0 -1
  300. package/dist/ai-tool-03jOTwUI.js.map +0 -1
  301. package/dist/chart-Dg0qUeSc.js.map +0 -1
  302. package/dist/clipboard-text-kLaMogs3.js.map +0 -1
  303. package/dist/command-palette-CuNUyJca.js.map +0 -1
  304. package/dist/data-grid-DGHmU0w3.js.map +0 -1
  305. package/dist/dist-B6iWiWwp.js.map +0 -1
  306. package/dist/dropdown-B_nrGXjV.js.map +0 -1
  307. package/dist/filters-cpJCY21R.js.map +0 -1
  308. package/dist/flow-B4v198ot.js.map +0 -1
  309. package/dist/highlight-to-react-D0Yav4jk.js.map +0 -1
  310. package/dist/layout-DJHMMap2.js.map +0 -1
  311. package/dist/link-provider-BUZKXaNE.js.map +0 -1
  312. package/dist/measured-text-BI3dTJmH.js.map +0 -1
  313. package/dist/sidebar-D4zrlYpn.js.map +0 -1
  314. package/dist/stat-card-Ew-ofzEm.js.map +0 -1
  315. package/dist/switch-BxAMfHdt.js.map +0 -1
  316. package/dist/text-roll-Ch52hcQj.js.map +0 -1
  317. package/dist/theme-toggle-LDfIKEqx.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ai-message-Bp7L68U_.js","names":[],"sources":["../src/components/ai-message/ai-message.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n FileIcon,\n FileTextIcon,\n ImageIcon,\n CaretLeftIcon,\n CaretRightIcon,\n XIcon,\n} from \"@phosphor-icons/react\";\nimport type {\n ComponentProps,\n HTMLAttributes,\n ReactElement,\n ReactNode,\n} from \"react\";\nimport {\n createContext,\n memo,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\nimport { Text } from \"../text\";\nimport { Tooltip } from \"../tooltip\";\n\n// ─── Context ─────────────────────────────────────────────────────────────────\n\ninterface AiMessageContextValue {\n from: SFAiMessageFrom;\n}\n\nconst AiMessageContext = createContext<AiMessageContextValue | null>(null);\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_MESSAGE_VARIANTS = {\n from: {\n user: {\n classes: \"ml-auto w-full max-w-[70%] justify-end is-user\",\n description: \"User message — right-aligned with max width\",\n },\n assistant: {\n classes: \"w-full is-assistant\",\n description: \"Assistant message — full width left-aligned\",\n },\n system: {\n classes: \"w-full is-system\",\n description: \"System message — full width\",\n },\n },\n} as const;\n\nexport const SF_AI_MESSAGE_DEFAULT_VARIANTS = {\n from: \"assistant\",\n} as const;\n\nexport type SFAiMessageFrom = keyof typeof SF_AI_MESSAGE_VARIANTS.from;\n\n// ─── AiMessage ───────────────────────────────────────────────────────────────\n\nexport type AiMessageProps = HTMLAttributes<HTMLDivElement> & {\n /** Message sender role — controls layout and group class. */\n from: SFAiMessageFrom;\n};\n\n/**\n * Root message container. Sets layout, group class, and role context.\n *\n * @example\n * ```tsx\n * <AiMessage from=\"user\">\n * <AiMessageContent>Hello!</AiMessageContent>\n * </AiMessage>\n * <AiMessage from=\"assistant\">\n * <AiMessageContent><AiResponse>{text}</AiResponse></AiMessageContent>\n * <AiMessageToolbar>\n * <AiActions><AiAction tooltip=\"Copy\">...</AiAction></AiActions>\n * </AiMessageToolbar>\n * </AiMessage>\n * ```\n */\nexport function AiMessage({ className, from, ...props }: AiMessageProps) {\n return (\n <AiMessageContext.Provider value={{ from }}>\n <div\n className={cn(\n \"group flex flex-col gap-2 p-4\",\n SF_AI_MESSAGE_VARIANTS.from[from].classes,\n className\n )}\n {...props}\n />\n </AiMessageContext.Provider>\n );\n}\n\n// ─── AiMessageContent ────────────────────────────────────────────────────────\n\nexport type AiMessageContentProps = HTMLAttributes<HTMLDivElement>;\n\n/**\n * Detects if children is plain text (string or number) suitable for shrink wrapping.\n */\nfunction isPlainText(children: ReactNode): children is string | number {\n return typeof children === \"string\" || typeof children === \"number\";\n}\n\n/**\n * Content bubble. Applies user/assistant bubble styles via group selectors.\n *\n * For user messages with plain text content, uses `Text wrap=\"shrink\"` so short\n * messages don't stretch to the full 70% container width.\n */\nexport function AiMessageContent({\n children,\n className,\n ...props\n}: AiMessageContentProps) {\n const isUserBubble = useContext(AiMessageContext)?.from === \"user\";\n const shouldShrink = isUserBubble && isPlainText(children);\n\n return (\n <div\n className={cn(\n \"flex w-fit max-w-full flex-col gap-2 overflow-hidden whitespace-pre-wrap text-sm\",\n // User bubble\n \"group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-sf-control group-[.is-user]:px-4 group-[.is-user]:py-3\",\n // Assistant text\n \"group-[.is-assistant]:text-sf-default\",\n className\n )}\n {...props}\n >\n {shouldShrink ? (\n <Text wrap=\"shrink\" as=\"span\">\n {children}\n </Text>\n ) : (\n children\n )}\n </div>\n );\n}\n\n// ─── AiMessageToolbar ────────────────────────────────────────────────────────\n\nexport type AiMessageToolbarProps = ComponentProps<\"div\">;\n\n/**\n * Bottom toolbar row for action buttons.\n */\nexport function AiMessageToolbar({\n className,\n children,\n ...props\n}: AiMessageToolbarProps) {\n return (\n <div\n className={cn(\n \"flex w-full items-center justify-between gap-4\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n}\n\n// ─── AiMessageActions ────────────────────────────────────────────────────────\n\nexport type AiMessageActionsProps = ComponentProps<\"div\">;\n\n/** Flex row of message action buttons. */\nexport function AiMessageActions({\n className,\n children,\n ...props\n}: AiMessageActionsProps) {\n return (\n <div className={cn(\"flex items-center gap-1\", className)} {...props}>\n {children}\n </div>\n );\n}\n\n// ─── AiMessageAction ─────────────────────────────────────────────────────────\n\nexport type AiMessageActionProps = ComponentProps<typeof Button> & {\n /** Tooltip text and accessible label. */\n tooltip?: string;\n /** Override accessible label separately from tooltip. */\n label?: string;\n};\n\n/** Single icon action button with optional tooltip. */\nexport function AiMessageAction({\n tooltip,\n label,\n children,\n className,\n variant = \"ghost\",\n size = \"sm\",\n ...props\n}: AiMessageActionProps) {\n const button = (\n <Button\n className={cn(\"text-sf-subtle hover:text-sf-default\", className)}\n size={size}\n variant={variant}\n {...props}\n >\n {children}\n {(label ?? tooltip) && (\n <span className=\"sr-only\">{label ?? tooltip}</span>\n )}\n </Button>\n );\n\n if (tooltip) {\n return <Tooltip content={tooltip}>{button}</Tooltip>;\n }\n return button;\n}\n\n// ─── Branch context ───────────────────────────────────────────────────────────\n\ninterface AiMessageBranchContextValue {\n currentBranch: number;\n totalBranches: number;\n goToPrevious: () => void;\n goToNext: () => void;\n branches: ReactElement[];\n setBranches: (branches: ReactElement[]) => void;\n}\n\nconst AiMessageBranchContext =\n createContext<AiMessageBranchContextValue | null>(null);\n\nfunction useAiMessageBranch() {\n const ctx = useContext(AiMessageBranchContext);\n if (!ctx)\n throw new Error(\n \"AiMessageBranch sub-components must be used within <AiMessageBranch>\"\n );\n return ctx;\n}\n\n// ─── AiMessageBranch ─────────────────────────────────────────────────────────\n\nexport type AiMessageBranchProps = HTMLAttributes<HTMLDivElement> & {\n defaultBranch?: number;\n onBranchChange?: (index: number) => void;\n};\n\n/**\n * Multi-branch message container. Wraps multiple alternative responses and\n * provides prev/next navigation.\n */\nexport function AiMessageBranch({\n defaultBranch = 0,\n onBranchChange,\n className,\n ...props\n}: AiMessageBranchProps) {\n const [currentBranch, setCurrentBranch] = useState(defaultBranch);\n const [branches, setBranches] = useState<ReactElement[]>([]);\n\n const handleChange = useCallback(\n (next: number) => {\n setCurrentBranch(next);\n onBranchChange?.(next);\n },\n [onBranchChange]\n );\n\n const goToPrevious = useCallback(() => {\n handleChange(currentBranch > 0 ? currentBranch - 1 : branches.length - 1);\n }, [currentBranch, branches.length, handleChange]);\n\n const goToNext = useCallback(() => {\n handleChange(currentBranch < branches.length - 1 ? currentBranch + 1 : 0);\n }, [currentBranch, branches.length, handleChange]);\n\n return (\n <AiMessageBranchContext.Provider\n value={{\n currentBranch,\n totalBranches: branches.length,\n goToPrevious,\n goToNext,\n branches,\n setBranches,\n }}\n >\n <div className={cn(\"grid w-full gap-2\", className)} {...props} />\n </AiMessageBranchContext.Provider>\n );\n}\n\n// ─── AiMessageBranchContent ──────────────────────────────────────────────────\n\nexport type AiMessageBranchContentProps = HTMLAttributes<HTMLDivElement>;\n\n/** Renders only the active branch child. */\nexport function AiMessageBranchContent({\n children,\n ...props\n}: AiMessageBranchContentProps) {\n const { currentBranch, setBranches, branches } = useAiMessageBranch();\n const arr = Array.isArray(children) ? children : [children];\n\n useEffect(() => {\n if (branches.length !== arr.length) setBranches(arr as ReactElement[]);\n }, [arr, branches, setBranches]);\n\n return arr.map((branch, i) => (\n <div\n className={cn(\n \"grid gap-2 overflow-hidden\",\n i !== currentBranch && \"hidden\"\n )}\n key={(branch as ReactElement)?.key ?? i}\n {...props}\n >\n {branch}\n </div>\n ));\n}\n\n// ─── AiMessageBranchSelector ─────────────────────────────────────────────────\n\nexport type AiMessageBranchSelectorProps = ComponentProps<\"div\">;\n\n/** Prev/next navigator for branches. Hidden when there's only one branch. */\nexport function AiMessageBranchSelector({\n className,\n ...props\n}: AiMessageBranchSelectorProps) {\n const { totalBranches, currentBranch, goToPrevious, goToNext } =\n useAiMessageBranch();\n\n if (totalBranches <= 1) return null;\n\n return (\n <div className={cn(\"flex items-center gap-0.5\", className)} {...props}>\n <Button\n aria-label=\"Previous branch\"\n disabled={totalBranches <= 1}\n onClick={goToPrevious}\n size=\"sm\"\n variant=\"ghost\"\n >\n <CaretLeftIcon className=\"size-3\" />\n </Button>\n <span className=\"tabular-nums text-sf-subtle text-xs\">\n {currentBranch + 1} / {totalBranches}\n </span>\n <Button\n aria-label=\"Next branch\"\n disabled={totalBranches <= 1}\n onClick={goToNext}\n size=\"sm\"\n variant=\"ghost\"\n >\n <CaretRightIcon className=\"size-3\" />\n </Button>\n </div>\n );\n}\n\n// ─── Attachment helpers ───────────────────────────────────────────────────────\n\nconst IMAGE_EXTS = new Set([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\", \"ico\"]);\nconst DOC_EXTS = new Set([\"pdf\", \"doc\", \"docx\", \"txt\", \"md\", \"rtf\"]);\nconst EXT_RE = /\\.[^/.]+$/;\n\nfunction fileIcon(filename: string): ReactNode {\n const ext = filename.split(\".\").pop()?.toLowerCase() ?? \"\";\n if (IMAGE_EXTS.has(ext)) return <ImageIcon className=\"size-3.5\" />;\n if (DOC_EXTS.has(ext)) return <FileTextIcon className=\"size-3.5\" />;\n return <FileIcon className=\"size-3.5\" />;\n}\n\n// ─── AiMessageAttachments ────────────────────────────────────────────────────\n\nexport type AiMessageAttachmentsProps = ComponentProps<\"div\">;\n\n/** Wrapping flex container for attachment chips. */\nexport function AiMessageAttachments({\n children,\n className,\n ...props\n}: AiMessageAttachmentsProps) {\n if (!children) return null;\n return (\n <div\n className={cn(\n \"ml-auto flex w-fit flex-wrap items-start gap-2\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n}\n\n// ─── AiMessageAttachment ─────────────────────────────────────────────────────\n\nexport type AiMessageAttachmentProps = HTMLAttributes<HTMLDivElement> & {\n /** Filename for display and icon selection. */\n filename: string;\n /** Object URL or data URL for images. */\n url?: string;\n /** MIME type — used to detect images. */\n mediaType?: string;\n /** Called when the remove button is clicked. */\n onRemove?: () => void;\n};\n\n/**\n * Single file attachment chip. Shows image thumbnail or file badge.\n */\nexport const AiMessageAttachment = memo(\n ({\n filename,\n url,\n mediaType,\n onRemove,\n className,\n ...props\n }: AiMessageAttachmentProps) => {\n const isImage = mediaType?.startsWith(\"image/\") && url;\n\n if (isImage) {\n return (\n <div\n className={cn(\n \"group relative size-24 overflow-hidden rounded-lg\",\n \"animate-in fade-in-0 slide-in-from-bottom-2 duration-200\",\n className\n )}\n {...props}\n >\n <img\n alt={filename || \"attachment\"}\n className=\"size-full object-cover\"\n src={url}\n />\n {onRemove && (\n <Button\n aria-label=\"Remove attachment\"\n className=\"absolute top-2 right-2 size-6 rounded-full bg-sf-overlay/80 p-0 opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n <XIcon className=\"size-3\" />\n </Button>\n )}\n </div>\n );\n }\n\n const ext = filename.split(\".\").pop()?.toUpperCase() ?? \"FILE\";\n const name = filename.replace(EXT_RE, \"\");\n\n return (\n <div\n className={cn(\n \"group relative inline-flex items-center gap-2 rounded-lg bg-sf-tint/50 px-3 py-2 transition-colors hover:bg-sf-tint\",\n \"animate-in fade-in-0 slide-in-from-bottom-2 duration-200\",\n className\n )}\n {...props}\n >\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-md bg-sf-control\">\n {fileIcon(filename)}\n </div>\n <div className=\"flex flex-col\">\n <span className=\"max-w-[200px] truncate font-medium text-sf-default text-sm\">\n {name || \"File\"}\n </span>\n <span className=\"w-fit rounded bg-sf-tint px-1.5 py-0.5 font-semibold text-sf-subtle text-[10px] uppercase\">\n {ext}\n </span>\n </div>\n {onRemove && (\n <Button\n aria-label=\"Remove attachment\"\n className=\"absolute top-1 right-1 size-5 rounded-full bg-sf-overlay/80 p-0 opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n <XIcon className=\"size-3\" />\n </Button>\n )}\n </div>\n );\n }\n);\n\nAiMessageAttachment.displayName = \"AiMessageAttachment\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai-message-Ci3gwM7G.js","names":[],"sources":["../src/components/ai-message/ai-message.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n FileIcon,\n FileTextIcon,\n ImageIcon,\n CaretLeftIcon,\n CaretRightIcon,\n XIcon,\n} from \"@phosphor-icons/react\";\nimport type {\n ComponentProps,\n HTMLAttributes,\n ReactElement,\n ReactNode,\n} from \"react\";\nimport {\n createContext,\n memo,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\nimport { Text } from \"../text\";\nimport { Tooltip } from \"../tooltip\";\n\n// ─── Context ─────────────────────────────────────────────────────────────────\n\ninterface AiMessageContextValue {\n from: SFAiMessageFrom;\n}\n\nconst AiMessageContext = createContext<AiMessageContextValue | null>(null);\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_MESSAGE_VARIANTS = {\n from: {\n user: {\n classes: \"ml-auto w-full max-w-[70%] justify-end is-user\",\n description: \"User message — right-aligned with max width\",\n },\n assistant: {\n classes: \"w-full is-assistant\",\n description: \"Assistant message — full width left-aligned\",\n },\n system: {\n classes: \"w-full is-system\",\n description: \"System message — full width\",\n },\n },\n} as const;\n\nexport const SF_AI_MESSAGE_DEFAULT_VARIANTS = {\n from: \"assistant\",\n} as const;\n\nexport type SFAiMessageFrom = keyof typeof SF_AI_MESSAGE_VARIANTS.from;\n\n// ─── AiMessage ───────────────────────────────────────────────────────────────\n\nexport type AiMessageProps = HTMLAttributes<HTMLDivElement> & {\n /** Message sender role — controls layout and group class. */\n from: SFAiMessageFrom;\n};\n\n/**\n * Root message container. Sets layout, group class, and role context.\n *\n * @example\n * ```tsx\n * <AiMessage from=\"user\">\n * <AiMessageContent>Hello!</AiMessageContent>\n * </AiMessage>\n * <AiMessage from=\"assistant\">\n * <AiMessageContent><AiResponse>{text}</AiResponse></AiMessageContent>\n * <AiMessageToolbar>\n * <AiActions><AiAction tooltip=\"Copy\">...</AiAction></AiActions>\n * </AiMessageToolbar>\n * </AiMessage>\n * ```\n */\nexport function AiMessage({ className, from, ...props }: AiMessageProps) {\n return (\n <AiMessageContext.Provider value={{ from }}>\n <div\n className={cn(\n \"group flex flex-col gap-2 p-4\",\n SF_AI_MESSAGE_VARIANTS.from[from].classes,\n className\n )}\n {...props}\n />\n </AiMessageContext.Provider>\n );\n}\n\n// ─── AiMessageContent ────────────────────────────────────────────────────────\n\nexport type AiMessageContentProps = HTMLAttributes<HTMLDivElement>;\n\n/**\n * Detects if children is plain text (string or number) suitable for shrink wrapping.\n */\nfunction isPlainText(children: ReactNode): children is string | number {\n return typeof children === \"string\" || typeof children === \"number\";\n}\n\n/**\n * Content bubble. Applies user/assistant bubble styles via group selectors.\n *\n * For user messages with plain text content, uses `Text wrap=\"shrink\"` so short\n * messages don't stretch to the full 70% container width.\n */\nexport function AiMessageContent({\n children,\n className,\n ...props\n}: AiMessageContentProps) {\n const isUserBubble = useContext(AiMessageContext)?.from === \"user\";\n const shouldShrink = isUserBubble && isPlainText(children);\n\n return (\n <div\n className={cn(\n \"flex w-fit max-w-full flex-col gap-2 overflow-hidden whitespace-pre-wrap text-sm\",\n // User bubble\n \"group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-sf-control group-[.is-user]:px-4 group-[.is-user]:py-3\",\n // Assistant text\n \"group-[.is-assistant]:text-sf-default\",\n className\n )}\n {...props}\n >\n {shouldShrink ? (\n <Text wrap=\"shrink\" as=\"span\">\n {children}\n </Text>\n ) : (\n children\n )}\n </div>\n );\n}\n\n// ─── AiMessageToolbar ────────────────────────────────────────────────────────\n\nexport type AiMessageToolbarProps = ComponentProps<\"div\">;\n\n/**\n * Bottom toolbar row for action buttons.\n */\nexport function AiMessageToolbar({\n className,\n children,\n ...props\n}: AiMessageToolbarProps) {\n return (\n <div\n className={cn(\n \"flex w-full items-center justify-between gap-4\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n}\n\n// ─── AiMessageActions ────────────────────────────────────────────────────────\n\nexport type AiMessageActionsProps = ComponentProps<\"div\">;\n\n/** Flex row of message action buttons. */\nexport function AiMessageActions({\n className,\n children,\n ...props\n}: AiMessageActionsProps) {\n return (\n <div className={cn(\"flex items-center gap-1\", className)} {...props}>\n {children}\n </div>\n );\n}\n\n// ─── AiMessageAction ─────────────────────────────────────────────────────────\n\nexport type AiMessageActionProps = ComponentProps<typeof Button> & {\n /** Tooltip text and accessible label. */\n tooltip?: string;\n /** Override accessible label separately from tooltip. */\n label?: string;\n};\n\n/** Single icon action button with optional tooltip. */\nexport function AiMessageAction({\n tooltip,\n label,\n children,\n className,\n variant = \"ghost\",\n size = \"sm\",\n ...props\n}: AiMessageActionProps) {\n const button = (\n <Button\n className={cn(\"text-sf-subtle hover:text-sf-default\", className)}\n size={size}\n variant={variant}\n {...props}\n >\n {children}\n {(label ?? tooltip) && (\n <span className=\"sr-only\">{label ?? tooltip}</span>\n )}\n </Button>\n );\n\n if (tooltip) {\n return <Tooltip content={tooltip}>{button}</Tooltip>;\n }\n return button;\n}\n\n// ─── Branch context ───────────────────────────────────────────────────────────\n\ninterface AiMessageBranchContextValue {\n currentBranch: number;\n totalBranches: number;\n goToPrevious: () => void;\n goToNext: () => void;\n branches: ReactElement[];\n setBranches: (branches: ReactElement[]) => void;\n}\n\nconst AiMessageBranchContext =\n createContext<AiMessageBranchContextValue | null>(null);\n\nfunction useAiMessageBranch() {\n const ctx = useContext(AiMessageBranchContext);\n if (!ctx)\n throw new Error(\n \"AiMessageBranch sub-components must be used within <AiMessageBranch>\"\n );\n return ctx;\n}\n\n// ─── AiMessageBranch ─────────────────────────────────────────────────────────\n\nexport type AiMessageBranchProps = HTMLAttributes<HTMLDivElement> & {\n defaultBranch?: number;\n onBranchChange?: (index: number) => void;\n};\n\n/**\n * Multi-branch message container. Wraps multiple alternative responses and\n * provides prev/next navigation.\n */\nexport function AiMessageBranch({\n defaultBranch = 0,\n onBranchChange,\n className,\n ...props\n}: AiMessageBranchProps) {\n const [currentBranch, setCurrentBranch] = useState(defaultBranch);\n const [branches, setBranches] = useState<ReactElement[]>([]);\n\n const handleChange = useCallback(\n (next: number) => {\n setCurrentBranch(next);\n onBranchChange?.(next);\n },\n [onBranchChange]\n );\n\n const goToPrevious = useCallback(() => {\n handleChange(currentBranch > 0 ? currentBranch - 1 : branches.length - 1);\n }, [currentBranch, branches.length, handleChange]);\n\n const goToNext = useCallback(() => {\n handleChange(currentBranch < branches.length - 1 ? currentBranch + 1 : 0);\n }, [currentBranch, branches.length, handleChange]);\n\n return (\n <AiMessageBranchContext.Provider\n value={{\n currentBranch,\n totalBranches: branches.length,\n goToPrevious,\n goToNext,\n branches,\n setBranches,\n }}\n >\n <div className={cn(\"grid w-full gap-2\", className)} {...props} />\n </AiMessageBranchContext.Provider>\n );\n}\n\n// ─── AiMessageBranchContent ──────────────────────────────────────────────────\n\nexport type AiMessageBranchContentProps = HTMLAttributes<HTMLDivElement>;\n\n/** Renders only the active branch child. */\nexport function AiMessageBranchContent({\n children,\n ...props\n}: AiMessageBranchContentProps) {\n const { currentBranch, setBranches, branches } = useAiMessageBranch();\n const arr = Array.isArray(children) ? children : [children];\n\n useEffect(() => {\n if (branches.length !== arr.length) setBranches(arr as ReactElement[]);\n }, [arr, branches, setBranches]);\n\n return arr.map((branch, i) => (\n <div\n className={cn(\n \"grid gap-2 overflow-hidden\",\n i !== currentBranch && \"hidden\"\n )}\n key={(branch as ReactElement)?.key ?? i}\n {...props}\n >\n {branch}\n </div>\n ));\n}\n\n// ─── AiMessageBranchSelector ─────────────────────────────────────────────────\n\nexport type AiMessageBranchSelectorProps = ComponentProps<\"div\">;\n\n/** Prev/next navigator for branches. Hidden when there's only one branch. */\nexport function AiMessageBranchSelector({\n className,\n ...props\n}: AiMessageBranchSelectorProps) {\n const { totalBranches, currentBranch, goToPrevious, goToNext } =\n useAiMessageBranch();\n\n if (totalBranches <= 1) return null;\n\n return (\n <div className={cn(\"flex items-center gap-0.5\", className)} {...props}>\n <Button\n aria-label=\"Previous branch\"\n disabled={totalBranches <= 1}\n onClick={goToPrevious}\n size=\"sm\"\n variant=\"ghost\"\n >\n <CaretLeftIcon className=\"size-3\" />\n </Button>\n <span className=\"tabular-nums text-sf-subtle text-xs\">\n {currentBranch + 1} / {totalBranches}\n </span>\n <Button\n aria-label=\"Next branch\"\n disabled={totalBranches <= 1}\n onClick={goToNext}\n size=\"sm\"\n variant=\"ghost\"\n >\n <CaretRightIcon className=\"size-3\" />\n </Button>\n </div>\n );\n}\n\n// ─── Attachment helpers ───────────────────────────────────────────────────────\n\nconst IMAGE_EXTS = new Set([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\", \"ico\"]);\nconst DOC_EXTS = new Set([\"pdf\", \"doc\", \"docx\", \"txt\", \"md\", \"rtf\"]);\nconst EXT_RE = /\\.[^/.]+$/u;\n\nfunction fileIcon(filename: string): ReactNode {\n const ext = filename.split(\".\").pop()?.toLowerCase() ?? \"\";\n if (IMAGE_EXTS.has(ext)) return <ImageIcon className=\"size-3.5\" />;\n if (DOC_EXTS.has(ext)) return <FileTextIcon className=\"size-3.5\" />;\n return <FileIcon className=\"size-3.5\" />;\n}\n\n// ─── AiMessageAttachments ────────────────────────────────────────────────────\n\nexport type AiMessageAttachmentsProps = ComponentProps<\"div\">;\n\n/** Wrapping flex container for attachment chips. */\nexport function AiMessageAttachments({\n children,\n className,\n ...props\n}: AiMessageAttachmentsProps) {\n if (!children) return null;\n return (\n <div\n className={cn(\n \"ml-auto flex w-fit flex-wrap items-start gap-2\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n}\n\n// ─── AiMessageAttachment ─────────────────────────────────────────────────────\n\nexport type AiMessageAttachmentProps = HTMLAttributes<HTMLDivElement> & {\n /** Filename for display and icon selection. */\n filename: string;\n /** Object URL or data URL for images. */\n url?: string;\n /** MIME type — used to detect images. */\n mediaType?: string;\n /** Called when the remove button is clicked. */\n onRemove?: () => void;\n};\n\n/**\n * Single file attachment chip. Shows image thumbnail or file badge.\n */\nexport const AiMessageAttachment = memo(\n ({\n filename,\n url,\n mediaType,\n onRemove,\n className,\n ...props\n }: AiMessageAttachmentProps) => {\n const isImage = mediaType?.startsWith(\"image/\") && url;\n\n if (isImage) {\n return (\n <div\n className={cn(\n \"group relative size-24 overflow-hidden rounded-lg\",\n \"animate-in fade-in-0 slide-in-from-bottom-2 duration-200\",\n className\n )}\n {...props}\n >\n <img\n alt={filename || \"attachment\"}\n className=\"size-full object-cover\"\n src={url}\n />\n {onRemove && (\n <Button\n aria-label=\"Remove attachment\"\n className=\"absolute top-2 right-2 size-6 rounded-full bg-sf-overlay/80 p-0 opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n <XIcon className=\"size-3\" />\n </Button>\n )}\n </div>\n );\n }\n\n const ext = filename.split(\".\").pop()?.toUpperCase() ?? \"FILE\";\n const name = filename.replace(EXT_RE, \"\");\n\n return (\n <div\n className={cn(\n \"group relative inline-flex items-center gap-2 rounded-lg bg-sf-tint/50 px-3 py-2 transition-colors hover:bg-sf-tint\",\n \"animate-in fade-in-0 slide-in-from-bottom-2 duration-200\",\n className\n )}\n {...props}\n >\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-md bg-sf-control\">\n {fileIcon(filename)}\n </div>\n <div className=\"flex flex-col\">\n <span className=\"max-w-[200px] truncate font-medium text-sf-default text-sm\">\n {name || \"File\"}\n </span>\n <span className=\"w-fit rounded bg-sf-tint px-1.5 py-0.5 font-semibold text-sf-subtle text-[10px] uppercase\">\n {ext}\n </span>\n </div>\n {onRemove && (\n <Button\n aria-label=\"Remove attachment\"\n className=\"absolute top-1 right-1 size-5 rounded-full bg-sf-overlay/80 p-0 opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n <XIcon className=\"size-3\" />\n </Button>\n )}\n </div>\n );\n }\n);\n\nAiMessageAttachment.displayName = \"AiMessageAttachment\";\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
3
  import { forwardRef, useEffect, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { CheckCircleIcon, ClockIcon, CoinsIcon, CpuIcon, SpinnerGapIcon, StopCircleIcon, UsersIcon } from "@phosphor-icons/react";
@@ -168,4 +168,4 @@ AiMissionHeader.displayName = "AiMissionHeader";
168
168
  //#endregion
169
169
  export { SF_AI_MISSION_HEADER_DEFAULT_VARIANTS as n, SF_AI_MISSION_HEADER_VARIANTS as r, AiMissionHeader as t };
170
170
 
171
- //# sourceMappingURL=ai-mission-header-TiCJfTNt.js.map
171
+ //# sourceMappingURL=ai-mission-header-CaBc19-t.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-mission-header-TiCJfTNt.js","names":[],"sources":["../src/components/ai-mission-header/ai-mission-header.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n CheckCircleIcon,\n ClockIcon,\n CoinsIcon,\n CpuIcon,\n SpinnerGapIcon,\n StopCircleIcon,\n UsersIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps } from \"react\";\nimport { forwardRef, useEffect, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport type { AgentTaskList, AgentUsage } from \"../use-agent-harness\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_MISSION_HEADER_VARIANTS = {\n variant: {\n default: { classes: \"\", description: \"Full header with all stats\" },\n compact: { classes: \"\", description: \"Single-row compact header\" },\n },\n} as const;\n\nexport const SF_AI_MISSION_HEADER_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\nexport type SFAiMissionHeaderVariant =\n keyof typeof SF_AI_MISSION_HEADER_VARIANTS.variant;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiMissionHeaderProps = Omit<ComponentProps<\"div\">, \"title\"> & {\n /** The mission goal or title. */\n title?: string;\n /** Task list for progress calculation. */\n taskList?: AgentTaskList | null;\n /** Token/cost usage summary. */\n usage?: AgentUsage | null;\n /** Number of agents currently active. */\n activeAgentCount?: number;\n /** Total number of agents that have run or are running. */\n totalAgentCount?: number;\n /** Wall-clock ms when the mission started. */\n startedAt?: number;\n /** Wall-clock ms when the mission ended. Undefined while still running. */\n endedAt?: number;\n /** Whether the mission is currently running. */\n isRunning?: boolean;\n /** Called when the user clicks the abort/stop button. */\n onAbort?: () => void;\n /** Display variant. @default \"default\" */\n variant?: SFAiMissionHeaderVariant;\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction formatElapsed(ms: number): string {\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const rem = s % 60;\n return `${m}m ${rem.toString().padStart(2, \"0\")}s`;\n}\n\nfunction formatCost(cost: number): string {\n if (cost < 0.01) return `${(cost * 100).toFixed(2)}¢`;\n return `$${cost.toFixed(4)}`;\n}\n\nfunction formatTokens(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;\n return String(n);\n}\n\n/** Live elapsed time that ticks every ~500ms while running. */\nfunction useElapsed(startedAt?: number, endedAt?: number, isRunning = false) {\n const [elapsed, setElapsed] = useState<number>(\n startedAt ? (endedAt ?? Date.now()) - startedAt : 0\n );\n\n useEffect(() => {\n if (!startedAt || !isRunning) {\n if (startedAt) setElapsed((endedAt ?? Date.now()) - startedAt);\n return;\n }\n\n // Tick at 2Hz — efficient for a human-readable seconds display\n const id = setInterval(() => {\n setElapsed(Date.now() - startedAt);\n }, 500);\n return () => clearInterval(id);\n }, [startedAt, endedAt, isRunning]);\n\n return elapsed;\n}\n\n// ─── Progress bar ─────────────────────────────────────────────────────────────\n\nfunction MissionProgress({ taskList }: { taskList?: AgentTaskList | null }) {\n if (!taskList?.tasks.length) return null;\n\n const total = taskList.tasks.length;\n const done = taskList.tasks.filter(\n (t) => t.status === \"completed\" || t.status === \"cancelled\"\n ).length;\n const inProgress = taskList.tasks.filter(\n (t) => t.status === \"in_progress\"\n ).length;\n const pct = total > 0 ? Math.round((done / total) * 100) : 0;\n const activePct = total > 0 ? Math.round((inProgress / total) * 100) : 0;\n\n return (\n <div className=\"flex items-center gap-3\">\n {/* Bar */}\n <div className=\"relative h-1.5 flex-1 overflow-hidden rounded-full bg-sf-fill\">\n {/* Active (in-progress) ghost — slightly ahead of done */}\n {activePct > 0 && (\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-sf-brand/30 transition-[width] duration-500\"\n style={{ width: `${pct + activePct}%` }}\n />\n )}\n {/* Done */}\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-sf-brand transition-[width] duration-500\"\n style={{ width: `${pct}%` }}\n />\n </div>\n {/* Fraction */}\n <span className=\"shrink-0 tabular-nums text-[11px] text-sf-subtle\">\n {done}/{total}\n </span>\n </div>\n );\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * `AiMissionHeader` — top bar for the Commander dashboard.\n *\n * Shows: mission title, progress bar (from task list), agent count,\n * live elapsed time, token usage, and an abort button while running.\n *\n * @example\n * ```tsx\n * <AiMissionHeader\n * title=\"Refactor auth module\"\n * taskList={state.taskList}\n * usage={state.usage}\n * activeAgentCount={activeAgents.length}\n * totalAgentCount={allAgents.length}\n * startedAt={state.missionStartedAt}\n * isRunning={state.isRunning}\n * onAbort={abort}\n * />\n * ```\n */\nexport const AiMissionHeader = forwardRef<HTMLDivElement, AiMissionHeaderProps>(\n (\n {\n title,\n taskList,\n usage,\n activeAgentCount,\n totalAgentCount,\n startedAt,\n endedAt,\n isRunning = false,\n onAbort,\n variant = SF_AI_MISSION_HEADER_DEFAULT_VARIANTS.variant,\n className,\n ...props\n },\n ref\n ) => {\n const elapsed = useElapsed(startedAt, endedAt, isRunning);\n const isCompact = variant === \"compact\";\n\n const statusLabel = isRunning\n ? \"Running\"\n : startedAt && endedAt\n ? \"Completed\"\n : startedAt\n ? \"Paused\"\n : \"Idle\";\n\n return (\n <div\n ref={ref}\n className={cn(\n \"flex flex-col gap-2 border-b border-sf-line bg-sf-base px-4 py-3\",\n isCompact && \"flex-row items-center gap-4 py-2\",\n className\n )}\n {...props}\n >\n {/* Top row: title + status + abort */}\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n {/* Status icon */}\n {isRunning ? (\n <SpinnerGapIcon\n size={14}\n className=\"shrink-0 animate-spin text-sf-brand\"\n />\n ) : startedAt && endedAt ? (\n <CheckCircleIcon size={14} className=\"shrink-0 text-sf-success\" />\n ) : (\n <CpuIcon size={14} className=\"shrink-0 text-sf-subtle\" />\n )}\n\n {/* Title */}\n {title ? (\n <h2 className=\"truncate text-sm font-semibold text-sf-default\">\n {title}\n </h2>\n ) : (\n <span className=\"text-sm font-medium text-sf-subtle\">\n {statusLabel}\n </span>\n )}\n </div>\n\n {/* Right: stats + abort */}\n <div className=\"flex shrink-0 items-center gap-3\">\n {/* Agent count */}\n {(activeAgentCount !== undefined ||\n totalAgentCount !== undefined) && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <UsersIcon size={11} />\n <span className=\"tabular-nums\">\n {activeAgentCount !== undefined &&\n totalAgentCount !== undefined\n ? `${activeAgentCount}/${totalAgentCount}`\n : (activeAgentCount ?? totalAgentCount)}\n </span>\n </div>\n )}\n\n {/* Elapsed time */}\n {startedAt && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <ClockIcon size={11} />\n <span className=\"tabular-nums\">{formatElapsed(elapsed)}</span>\n </div>\n )}\n\n {/* Token cost */}\n {usage?.cost !== undefined && usage.cost > 0 && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <CoinsIcon size={11} />\n <span className=\"tabular-nums\">{formatCost(usage.cost)}</span>\n </div>\n )}\n\n {/* Tokens */}\n {usage?.totalTokens !== undefined && (\n <span className=\"tabular-nums text-[11px] text-sf-subtle\">\n {formatTokens(usage.totalTokens)} tok\n </span>\n )}\n\n {/* Abort */}\n {isRunning && onAbort && (\n <button\n type=\"button\"\n aria-label=\"Abort mission\"\n onClick={onAbort}\n className={cn(\n \"flex items-center gap-1 rounded-md px-2 py-1 text-[11px] font-medium\",\n \"text-sf-danger transition-colors hover:bg-sf-danger/10\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sf-ring\"\n )}\n >\n <StopCircleIcon size={11} />\n Stop\n </button>\n )}\n </div>\n </div>\n\n {/* Progress bar (only in default variant) */}\n {!isCompact && <MissionProgress taskList={taskList} />}\n </div>\n );\n }\n);\n\nAiMissionHeader.displayName = \"AiMissionHeader\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai-mission-header-CaBc19-t.js","names":[],"sources":["../src/components/ai-mission-header/ai-mission-header.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n CheckCircleIcon,\n ClockIcon,\n CoinsIcon,\n CpuIcon,\n SpinnerGapIcon,\n StopCircleIcon,\n UsersIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps } from \"react\";\nimport { forwardRef, useEffect, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport type { AgentTaskList, AgentUsage } from \"../use-agent-harness\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_MISSION_HEADER_VARIANTS = {\n variant: {\n default: { classes: \"\", description: \"Full header with all stats\" },\n compact: { classes: \"\", description: \"Single-row compact header\" },\n },\n} as const;\n\nexport const SF_AI_MISSION_HEADER_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\nexport type SFAiMissionHeaderVariant =\n keyof typeof SF_AI_MISSION_HEADER_VARIANTS.variant;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiMissionHeaderProps = Omit<ComponentProps<\"div\">, \"title\"> & {\n /** The mission goal or title. */\n title?: string;\n /** Task list for progress calculation. */\n taskList?: AgentTaskList | null;\n /** Token/cost usage summary. */\n usage?: AgentUsage | null;\n /** Number of agents currently active. */\n activeAgentCount?: number;\n /** Total number of agents that have run or are running. */\n totalAgentCount?: number;\n /** Wall-clock ms when the mission started. */\n startedAt?: number;\n /** Wall-clock ms when the mission ended. Undefined while still running. */\n endedAt?: number;\n /** Whether the mission is currently running. */\n isRunning?: boolean;\n /** Called when the user clicks the abort/stop button. */\n onAbort?: () => void;\n /** Display variant. @default \"default\" */\n variant?: SFAiMissionHeaderVariant;\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction formatElapsed(ms: number): string {\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const rem = s % 60;\n return `${m}m ${rem.toString().padStart(2, \"0\")}s`;\n}\n\nfunction formatCost(cost: number): string {\n if (cost < 0.01) return `${(cost * 100).toFixed(2)}¢`;\n return `$${cost.toFixed(4)}`;\n}\n\nfunction formatTokens(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;\n return String(n);\n}\n\n/** Live elapsed time that ticks every ~500ms while running. */\nfunction useElapsed(startedAt?: number, endedAt?: number, isRunning = false) {\n const [elapsed, setElapsed] = useState<number>(\n startedAt ? (endedAt ?? Date.now()) - startedAt : 0\n );\n\n useEffect(() => {\n if (!startedAt || !isRunning) {\n if (startedAt) setElapsed((endedAt ?? Date.now()) - startedAt);\n return;\n }\n\n // Tick at 2Hz — efficient for a human-readable seconds display\n const id = setInterval(() => {\n setElapsed(Date.now() - startedAt);\n }, 500);\n return () => clearInterval(id);\n }, [startedAt, endedAt, isRunning]);\n\n return elapsed;\n}\n\n// ─── Progress bar ─────────────────────────────────────────────────────────────\n\nfunction MissionProgress({ taskList }: { taskList?: AgentTaskList | null }) {\n if (!taskList?.tasks.length) return null;\n\n const total = taskList.tasks.length;\n const done = taskList.tasks.filter(\n (t) => t.status === \"completed\" || t.status === \"cancelled\"\n ).length;\n const inProgress = taskList.tasks.filter(\n (t) => t.status === \"in_progress\"\n ).length;\n const pct = total > 0 ? Math.round((done / total) * 100) : 0;\n const activePct = total > 0 ? Math.round((inProgress / total) * 100) : 0;\n\n return (\n <div className=\"flex items-center gap-3\">\n {/* Bar */}\n <div className=\"relative h-1.5 flex-1 overflow-hidden rounded-full bg-sf-fill\">\n {/* Active (in-progress) ghost — slightly ahead of done */}\n {activePct > 0 && (\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-sf-brand/30 transition-[width] duration-500\"\n style={{ width: `${pct + activePct}%` }}\n />\n )}\n {/* Done */}\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-sf-brand transition-[width] duration-500\"\n style={{ width: `${pct}%` }}\n />\n </div>\n {/* Fraction */}\n <span className=\"shrink-0 tabular-nums text-[11px] text-sf-subtle\">\n {done}/{total}\n </span>\n </div>\n );\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * `AiMissionHeader` — top bar for the Commander dashboard.\n *\n * Shows: mission title, progress bar (from task list), agent count,\n * live elapsed time, token usage, and an abort button while running.\n *\n * @example\n * ```tsx\n * <AiMissionHeader\n * title=\"Refactor auth module\"\n * taskList={state.taskList}\n * usage={state.usage}\n * activeAgentCount={activeAgents.length}\n * totalAgentCount={allAgents.length}\n * startedAt={state.missionStartedAt}\n * isRunning={state.isRunning}\n * onAbort={abort}\n * />\n * ```\n */\nexport const AiMissionHeader = forwardRef<HTMLDivElement, AiMissionHeaderProps>(\n (\n {\n title,\n taskList,\n usage,\n activeAgentCount,\n totalAgentCount,\n startedAt,\n endedAt,\n isRunning = false,\n onAbort,\n variant = SF_AI_MISSION_HEADER_DEFAULT_VARIANTS.variant,\n className,\n ...props\n },\n ref\n ) => {\n const elapsed = useElapsed(startedAt, endedAt, isRunning);\n const isCompact = variant === \"compact\";\n\n const statusLabel = isRunning\n ? \"Running\"\n : startedAt && endedAt\n ? \"Completed\"\n : startedAt\n ? \"Paused\"\n : \"Idle\";\n\n return (\n <div\n ref={ref}\n className={cn(\n \"flex flex-col gap-2 border-b border-sf-line bg-sf-base px-4 py-3\",\n isCompact && \"flex-row items-center gap-4 py-2\",\n className\n )}\n {...props}\n >\n {/* Top row: title + status + abort */}\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n {/* Status icon */}\n {isRunning ? (\n <SpinnerGapIcon\n size={14}\n className=\"shrink-0 animate-spin text-sf-brand\"\n />\n ) : startedAt && endedAt ? (\n <CheckCircleIcon size={14} className=\"shrink-0 text-sf-success\" />\n ) : (\n <CpuIcon size={14} className=\"shrink-0 text-sf-subtle\" />\n )}\n\n {/* Title */}\n {title ? (\n <h2 className=\"truncate text-sm font-semibold text-sf-default\">\n {title}\n </h2>\n ) : (\n <span className=\"text-sm font-medium text-sf-subtle\">\n {statusLabel}\n </span>\n )}\n </div>\n\n {/* Right: stats + abort */}\n <div className=\"flex shrink-0 items-center gap-3\">\n {/* Agent count */}\n {(activeAgentCount !== undefined ||\n totalAgentCount !== undefined) && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <UsersIcon size={11} />\n <span className=\"tabular-nums\">\n {activeAgentCount !== undefined &&\n totalAgentCount !== undefined\n ? `${activeAgentCount}/${totalAgentCount}`\n : (activeAgentCount ?? totalAgentCount)}\n </span>\n </div>\n )}\n\n {/* Elapsed time */}\n {startedAt && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <ClockIcon size={11} />\n <span className=\"tabular-nums\">{formatElapsed(elapsed)}</span>\n </div>\n )}\n\n {/* Token cost */}\n {usage?.cost !== undefined && usage.cost > 0 && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <CoinsIcon size={11} />\n <span className=\"tabular-nums\">{formatCost(usage.cost)}</span>\n </div>\n )}\n\n {/* Tokens */}\n {usage?.totalTokens !== undefined && (\n <span className=\"tabular-nums text-[11px] text-sf-subtle\">\n {formatTokens(usage.totalTokens)} tok\n </span>\n )}\n\n {/* Abort */}\n {isRunning && onAbort && (\n <button\n type=\"button\"\n aria-label=\"Abort mission\"\n onClick={onAbort}\n className={cn(\n \"flex items-center gap-1 rounded-md px-2 py-1 text-[11px] font-medium\",\n \"text-sf-danger transition-colors hover:bg-sf-danger/10\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sf-ring\"\n )}\n >\n <StopCircleIcon size={11} />\n Stop\n </button>\n )}\n </div>\n </div>\n\n {/* Progress bar (only in default variant) */}\n {!isCompact && <MissionProgress taskList={taskList} />}\n </div>\n );\n }\n);\n\nAiMissionHeader.displayName = \"AiMissionHeader\";\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
3
- import { d as hasToolResult, l as hasToolError } from "./ai-tool-03jOTwUI.js";
4
- import { t as TextRoll } from "./text-roll-Ch52hcQj.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
+ import { d as hasToolResult, l as hasToolError } from "./ai-tool-CfRcwmHT.js";
4
+ import { t as TextRoll } from "./text-roll-C3U2jd2u.js";
5
5
  import { Children, isValidElement, useState } from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import { CaretDownIcon, SpinnerGapIcon } from "@phosphor-icons/react";
@@ -123,6 +123,7 @@ function TitledSummary({ snapshot, defaultOpen, className, ...props }) {
123
123
  children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, {
124
124
  render: /* @__PURE__ */ jsx("button", {
125
125
  type: "button",
126
+ "aria-label": snapshot.title,
126
127
  className: cn("flex w-fit items-center gap-1.5 rounded px-1 py-0.5 text-left", "transition-colors hover:bg-sf-tint")
127
128
  }),
128
129
  children: [
@@ -274,4 +275,4 @@ function groupParts(parts, options = {}) {
274
275
  //#endregion
275
276
  export { getAiPartGroupSnapshot as n, groupParts as r, AiPartGroup as t };
276
277
 
277
- //# sourceMappingURL=ai-part-group-DNb9I446.js.map
278
+ //# sourceMappingURL=ai-part-group-Dx1Mr92B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-part-group-Dx1Mr92B.js","names":[],"sources":["../src/components/ai-part-group/ai-part-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { Collapsible as BaseCollapsible } from \"@base-ui/react/collapsible\";\nimport { CaretDownIcon, SpinnerGapIcon } from \"@phosphor-icons/react\";\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\nimport { Children, isValidElement, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { type AiToolPart, hasToolError, hasToolResult } from \"../ai-tool\";\nimport { TextRoll } from \"../text-roll\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ntype FrozenStep = {\n kind: \"tool\" | \"reasoning\";\n name: string;\n ok: boolean;\n duration?: number;\n};\n\ntype FrozenSnapshot = {\n title: string;\n steps: FrozenStep[];\n errorCount: number;\n totalDuration: number;\n};\n\n// Module-level snapshot cache. Survives re-renders within the page session.\n// Keyed by `groupKey` — usually the parent message id.\nconst snapshotCache = new Map<string, FrozenSnapshot>();\n\n/** Test-only escape hatch for clearing the snapshot cache. */\nexport function _clearAiPartGroupCache() {\n snapshotCache.clear();\n}\n\n/**\n * Reads the frozen snapshot for a group, or `undefined` if it hasn't completed\n * yet. Useful for consumers driving custom layouts.\n */\nexport function getAiPartGroupSnapshot(\n groupKey: string\n): FrozenSnapshot | undefined {\n return snapshotCache.get(groupKey);\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport type AiPartGroupProps = Omit<ComponentProps<\"div\">, \"title\"> & {\n /**\n * Stable identifier — usually the parent message id. Required so the group\n * can persist its frozen snapshot across re-mounts (scroll-back into a\n * virtualized list re-renders without restarting animations).\n */\n groupKey: string;\n /**\n * Set to `true` once the parent stream has finished. Triggers the group to\n * snapshot its current children and from then on render only the static\n * collapsed summary — ephemeral children never re-mount.\n */\n complete?: boolean;\n /**\n * Max number of live ephemeral rows visible at once during streaming.\n * Older rows fall off the top. Pinned rows (errors / user-expanded) render\n * above the live window and do not count toward this cap.\n * @default 3\n */\n max?: number;\n /**\n * Title shown in the collapsed summary line. Falls back to the first\n * reasoning text snippet, then to a step count.\n */\n title?: string;\n /** Child ephemeral rows (`AiToolCall variant=\"ephemeral\"`, `AiReasoning variant=\"ephemeral\"`). */\n children?: ReactNode;\n};\n\n/**\n * Groups streaming ephemeral activity (tool calls + reasoning) into a single\n * rolling window during streaming, then collapses to a frozen summary line\n * once the parent message completes.\n *\n * Behavior:\n * - **Streaming:** shows the most recent `max` (default 3) ephemeral children.\n * Pinned rows (errors / user-expanded) sit above the live window.\n * - **Completed:** writes a snapshot keyed by `groupKey`, unmounts ephemeral\n * children entirely, and renders a static `▸ {title} · N steps` line.\n * - **Re-mount after completion:** always renders the cached snapshot — no\n * animations, no `useEffect` re-runs, no tool re-execution risk.\n *\n * @example\n * ```tsx\n * <AiPartGroup\n * groupKey={message.id}\n * complete={message.status === \"complete\"}\n * title={message.reasoningSummary}\n * max={3}\n * >\n * {message.parts.map((p) =>\n * p.type === \"reasoning\"\n * ? <AiReasoning key={p.id} variant=\"ephemeral\" part={p} />\n * : <AiToolCall key={p.toolCallId} variant=\"ephemeral\" part={p} />\n * )}\n * </AiPartGroup>\n * ```\n */\nexport function AiPartGroup({\n groupKey,\n complete = false,\n max = 3,\n title,\n children,\n className,\n ...props\n}: AiPartGroupProps) {\n const cached = snapshotCache.get(groupKey);\n\n // Cache hit: always render the frozen snapshot, never re-mount ephemerals.\n // This is the scroll-back-into-an-old-message path.\n if (cached) {\n return (\n <TitledSummary\n snapshot={cached}\n className={className}\n defaultOpen={false}\n {...props}\n />\n );\n }\n\n // First-time completion: derive a snapshot, persist it, render frozen.\n if (complete) {\n const snapshot = deriveSnapshot(children, title);\n snapshotCache.set(groupKey, snapshot);\n return (\n <TitledSummary\n snapshot={snapshot}\n className={className}\n defaultOpen={false}\n {...props}\n />\n );\n }\n\n // Streaming: persistent title row + rolling window of the latest `max` children.\n // oxlint-disable-next-line no-react-children\n const allChildren = Children.toArray(children).filter(isValidElement);\n const live = allChildren.slice(Math.max(0, allChildren.length - max));\n const liveTitle = title?.trim() || deriveLiveTitle(allChildren);\n const stepCount = allChildren.length;\n\n return (\n <LiveGroup\n className={className}\n liveRows={live}\n stepCount={stepCount}\n title={liveTitle}\n {...props}\n />\n );\n}\n\nAiPartGroup.displayName = \"AiPartGroup\";\n\n// ─── Live group (streaming) ──────────────────────────────────────────────────\n\nfunction LiveGroup({\n title,\n liveRows,\n stepCount,\n className,\n ...props\n}: {\n title: string;\n liveRows: ReactElement[];\n stepCount: number;\n} & Omit<ComponentProps<\"div\">, \"title\">) {\n // During streaming the group is always open — the caret is purely decorative\n // so the frozen/live states share a visual footprint (no layout jump on settle).\n const stepLabel = stepCount === 1 ? \"step\" : \"steps\";\n\n return (\n <div\n className={cn(\"flex flex-col\", className)}\n data-streaming=\"true\"\n {...props}\n >\n <div\n className={cn(\"flex w-fit items-center gap-1.5 rounded px-1 py-0.5\")}\n >\n <SpinnerGapIcon className=\"size-3.5 shrink-0 animate-spin text-sf-subtle\" />\n <span className=\"min-w-0 truncate text-sm text-sf-default\">\n <TextRoll\n duration={0.3}\n getEnterDelay={(i) => i * 0.03}\n getExitDelay={(i) => i * 0.03 + 0.12}\n key={title}\n >\n {title}\n </TextRoll>\n </span>\n {stepCount > 0 ? (\n <span className=\"text-xs tabular-nums text-sf-subtle/60\">\n {stepCount} {stepLabel}\n </span>\n ) : null}\n <CaretDownIcon className=\"size-3 shrink-0 text-sf-subtle/60\" />\n </div>\n {liveRows.length > 0 ? (\n <div className=\"ml-[0.3125rem] flex flex-col gap-0.5 border-l border-sf-line/40 pl-3\">\n {liveRows}\n </div>\n ) : null}\n </div>\n );\n}\n\n// ─── Frozen summary (titled) ─────────────────────────────────────────────────\n\nfunction TitledSummary({\n snapshot,\n defaultOpen,\n className,\n ...props\n}: {\n snapshot: FrozenSnapshot;\n defaultOpen: boolean;\n} & Omit<ComponentProps<\"div\">, \"title\">) {\n const [isOpen, setIsOpen] = useState(defaultOpen);\n const stepLabel = snapshot.steps.length === 1 ? \"step\" : \"steps\";\n\n return (\n <BaseCollapsible.Root open={isOpen} onOpenChange={setIsOpen}>\n <div\n className={cn(\"flex flex-col\", className)}\n data-streaming=\"false\"\n {...props}\n >\n <BaseCollapsible.Trigger\n render={\n <button\n type=\"button\"\n aria-label={snapshot.title}\n className={cn(\n \"flex w-fit items-center gap-1.5 rounded px-1 py-0.5 text-left\",\n \"transition-colors hover:bg-sf-tint\"\n )}\n />\n }\n >\n <span\n aria-hidden\n className={cn(\n \"size-1.5 shrink-0 rounded-full bg-current\",\n snapshot.errorCount > 0 ? \"text-sf-danger\" : \"text-sf-success\"\n )}\n />\n <span className=\"min-w-0 truncate text-sm text-sf-subtle\">\n {snapshot.title}\n </span>\n <span className=\"text-xs tabular-nums text-sf-subtle/60\">\n {snapshot.steps.length} {stepLabel}\n {snapshot.errorCount > 0 ? ` · ${snapshot.errorCount} error` : \"\"}\n </span>\n <CaretDownIcon\n className={cn(\n \"size-3 shrink-0 text-sf-subtle/60 transition-transform duration-200\",\n !isOpen && \"-rotate-90\"\n )}\n />\n </BaseCollapsible.Trigger>\n\n <BaseCollapsible.Panel className=\"overflow-hidden\">\n <ul className=\"mt-1 ml-[0.3125rem] flex flex-col gap-0.5 border-l border-sf-line/40 pl-3\">\n {snapshot.steps.map((step, i) => (\n <li\n className=\"flex items-center gap-1.5 text-xs text-sf-subtle\"\n key={`${step.kind}-${i}-${step.name}`}\n >\n <span\n aria-hidden\n className={cn(\n \"size-1.5 shrink-0 rounded-full bg-current\",\n step.ok ? \"text-sf-success\" : \"text-sf-danger\"\n )}\n />\n <span className=\"min-w-0 truncate\">{step.name}</span>\n {step.duration ? (\n <span className=\"tabular-nums text-sf-subtle/60\">\n {step.duration}ms\n </span>\n ) : null}\n </li>\n ))}\n </ul>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\nfunction deriveLiveTitle(elements: ReactElement[]): string {\n for (const el of elements as ChildLike[]) {\n const part = el.props.part as\n | { text?: string; summary?: string }\n | undefined;\n const text = part?.summary ?? part?.text;\n if (text) return truncate(text, 60);\n }\n return \"Working…\";\n}\n\n// ─── Snapshot derivation ─────────────────────────────────────────────────────\n\ntype ChildLike = ReactElement<{\n part?: AiToolPart | { text?: string; summary?: string; name?: string };\n duration?: number;\n}>;\n\nfunction deriveSnapshot(\n children: ReactNode,\n explicitTitle: string | undefined\n): FrozenSnapshot {\n // oxlint-disable-next-line no-react-children\n const elements = Children.toArray(children).filter(\n isValidElement\n ) as ChildLike[];\n\n const steps: FrozenStep[] = [];\n let errorCount = 0;\n let totalDuration = 0;\n let firstReasoningSnippet: string | undefined;\n\n for (const el of elements) {\n const kind = inferKind(el);\n const part = el.props.part as\n | (AiToolPart & { text?: string; summary?: string; name?: string })\n | undefined;\n const duration = el.props.duration;\n if (typeof duration === \"number\") totalDuration += duration;\n\n if (kind === \"tool\") {\n const toolPart = part as AiToolPart | undefined;\n const ok = toolPart ? !hasToolError(toolPart) : true;\n const settled = toolPart ? hasToolResult(toolPart) : true;\n if (!ok) errorCount += 1;\n steps.push({\n kind: \"tool\",\n name: toolPart?.toolName ?? \"tool\",\n ok: ok && settled,\n duration,\n });\n } else {\n const text =\n (part?.summary as string | undefined) ??\n (part?.text as string | undefined);\n if (!firstReasoningSnippet && text) {\n firstReasoningSnippet = text;\n }\n steps.push({\n kind: \"reasoning\",\n name: text ? truncate(text, 60) : \"Thinking\",\n ok: true,\n duration,\n });\n }\n }\n\n const title =\n explicitTitle?.trim() ||\n (firstReasoningSnippet && truncate(firstReasoningSnippet, 60)) ||\n `${steps.length} ${steps.length === 1 ? \"step\" : \"steps\"}`;\n\n return { title, steps, errorCount, totalDuration };\n}\n\nfunction inferKind(el: ChildLike): \"tool\" | \"reasoning\" {\n // Best-effort: check common indicators. Tool parts have `toolName`/`toolCallId`.\n // Reasoning parts have `text`.\n const part = el.props.part as Record<string, unknown> | undefined;\n if (part && typeof part === \"object\") {\n if (\"toolName\" in part || \"toolCallId\" in part) return \"tool\";\n if (\"text\" in part || \"summary\" in part) return \"reasoning\";\n }\n // Fallback: inspect displayName.\n const displayName =\n (el.type as { displayName?: string; name?: string } | undefined)\n ?.displayName ??\n (el.type as { displayName?: string; name?: string } | undefined)?.name ??\n \"\";\n if (displayName.includes(\"Tool\")) return \"tool\";\n return \"reasoning\";\n}\n\nfunction truncate(s: string, n: number): string {\n if (s.length <= n) return s;\n return `${s.slice(0, n - 1).trimEnd()}…`;\n}\n\n// ─── groupParts ──────────────────────────────────────────────────────\n\n/**\n * A message part that is either narrative text or an \"activity\" (reasoning or\n * tool call). Consumers pass parts shaped like this — the helper does not read\n * any field other than `type`.\n */\nexport type GroupablePart =\n | { type: \"text\"; [key: string]: unknown }\n | { type: \"reasoning\"; [key: string]: unknown }\n | { type: \"tool-call\" | \"tool-result\" | string; [key: string]: unknown };\n\n/** Emitted by {@link groupParts}. */\nexport type PartChunk<TPart = GroupablePart> =\n | { kind: \"text\"; parts: TPart[] }\n | { kind: \"activity\"; parts: TPart[] };\n\n/**\n * Split a message's parts into alternating `text` and `activity` chunks so each\n * contiguous run of reasoning/tool parts can be rendered inside its own\n * {@link AiPartGroup}, with narrative text rendered between them.\n *\n * @example\n * ```tsx\n * {groupParts(message.parts).map((chunk, i) =>\n * chunk.kind === \"text\" ? (\n * <AiMessageContent key={i}>{joinText(chunk.parts)}</AiMessageContent>\n * ) : (\n * <AiPartGroup\n * key={i}\n * groupKey={`${message.id}:activity:${i}`}\n * complete={!message.isStreaming}\n * >\n * {chunk.parts.map(renderActivityPart)}\n * </AiPartGroup>\n * )\n * )}\n * ```\n */\nexport function groupParts<TPart extends { type: string }>(\n parts: readonly TPart[],\n options: {\n /** Which `type` values count as \"activity\". Defaults to reasoning + tool parts. */\n isActivity?: (part: TPart) => boolean;\n } = {}\n): PartChunk<TPart>[] {\n const isActivity =\n options.isActivity ??\n ((p: TPart) => {\n const t = p.type;\n return (\n t === \"reasoning\" ||\n t === \"tool-call\" ||\n t === \"tool-result\" ||\n t.startsWith(\"tool-\")\n );\n });\n\n const chunks: PartChunk<TPart>[] = [];\n for (const part of parts) {\n const kind: PartChunk<TPart>[\"kind\"] = isActivity(part)\n ? \"activity\"\n : \"text\";\n const last = chunks.at(-1);\n if (last && last.kind === kind) {\n last.parts.push(part);\n } else {\n chunks.push({ kind, parts: [part] });\n }\n }\n return chunks;\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
3
- import { n as layoutWithLines, o as prepareWithSegments } from "./layout-DJHMMap2.js";
4
- import { t as Tooltip } from "./tooltip-g9lFsvcT.js";
5
- import { t as Button } from "./button-CO6-qPax.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
+ import { n as layoutWithLines, o as prepareWithSegments } from "./layout-CWBE0qwx.js";
4
+ import { t as Tooltip } from "./tooltip-uobk6Oh-.js";
5
+ import { t as Button } from "./button-BHOgXJRU.js";
6
6
  import { Fragment, createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
  import { ArrowUpIcon, ArrowsClockwiseIcon, CaretDownIcon, CheckIcon, FileIcon, FileTextIcon, ImageIcon, MicrophoneIcon, PaperclipIcon, PlusIcon, SpinnerGapIcon, SquareIcon, XIcon } from "@phosphor-icons/react";
@@ -371,7 +371,7 @@ var Store = class {
371
371
  }
372
372
  };
373
373
  //#endregion
374
- //#region ../../node_modules/.bun/@tanstack+react-store@0.11.0+21ccd8898788a04d/node_modules/@tanstack/react-store/dist/useSelector.js
374
+ //#region ../../node_modules/.bun/@tanstack+react-store@0.11.0+7492c01c6988791b/node_modules/@tanstack/react-store/dist/useSelector.js
375
375
  function defaultCompare(a, b) {
376
376
  return a === b;
377
377
  }
@@ -405,7 +405,7 @@ function useSelector(source, selector = (s) => s, options) {
405
405
  return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, selector, compare);
406
406
  }
407
407
  //#endregion
408
- //#region ../../node_modules/.bun/@tanstack+react-store@0.11.0+21ccd8898788a04d/node_modules/@tanstack/react-store/dist/useStore.js
408
+ //#region ../../node_modules/.bun/@tanstack+react-store@0.11.0+7492c01c6988791b/node_modules/@tanstack/react-store/dist/useStore.js
409
409
  /**
410
410
  * Deprecated alias for {@link useSelector}.
411
411
  *
@@ -507,14 +507,14 @@ function createPromptInputRequestController(options = {}) {
507
507
  },
508
508
  insertTag(tag, range) {
509
509
  request.setState((prev) => {
510
- const mention = tag.mention ?? `@${tag.label.replace(/\s+/g, "-").toLowerCase()}`;
510
+ const mention = tag.mention ?? `@${tag.label.replace(/\s+/gu, "-").toLowerCase()}`;
511
511
  const text = prev.text ?? "";
512
512
  const start = range?.start ?? text.length;
513
513
  const end = range?.end ?? text.length;
514
514
  const prefix = text.slice(0, start);
515
515
  const suffix = text.slice(end);
516
- const needsPrefixSpace = prefix.length > 0 && !/\s$/.test(prefix);
517
- const needsSuffixSpace = suffix.length > 0 && !/^\s/.test(suffix);
516
+ const needsPrefixSpace = prefix.length > 0 && !/\s$/u.test(prefix);
517
+ const needsSuffixSpace = suffix.length > 0 && !/^\s/u.test(suffix);
518
518
  const nextText = `${prefix}${needsPrefixSpace ? " " : ""}${mention}${needsSuffixSpace ? " " : ""}${suffix}`;
519
519
  const nextTag = {
520
520
  ...tag,
@@ -719,7 +719,7 @@ var usePromptInputAttachments = () => {
719
719
  /** Convert a blob URL to a base64 data URL for serialization. */
720
720
  async function convertBlobToDataUrl(url) {
721
721
  const blob = await (await fetch(url)).blob();
722
- return new Promise((resolve, reject) => {
722
+ return await new Promise((resolve, reject) => {
723
723
  const reader = new FileReader();
724
724
  reader.addEventListener("loadend", () => resolve(reader.result));
725
725
  reader.addEventListener("error", () => reject(/* @__PURE__ */ new Error("FileReader error")));
@@ -887,7 +887,9 @@ function PromptInput({ className, accept, multiple, globalDrop, maxFiles, maxFil
887
887
  const handleSubmit = (event) => {
888
888
  event.preventDefault();
889
889
  const form = event.currentTarget;
890
- const text = requestCtrl ? requestCtrl.request.state.text : usingProvider ? controller.textInput.value : new FormData(form).get("message") || "";
890
+ const text = requestCtrl ? requestCtrl.request.state.text : usingProvider ? controller.textInput.value : (() => {
891
+ return new FormData(form).get("message") || "";
892
+ })();
891
893
  if (!usingProvider) form.reset();
892
894
  const doSubmit = async () => {
893
895
  const convertedFiles = await Promise.all(files.map(async (item) => {
@@ -1077,10 +1079,10 @@ var PromptInputTextarea = ({ onChange, className, placeholder = "What would you
1077
1079
  ...controlledProps
1078
1080
  });
1079
1081
  };
1080
- var getTagMention = (tag) => tag.mention ?? `@${tag.label.replace(/\s+/g, "-").toLowerCase()}`;
1082
+ var getTagMention = (tag) => tag.mention ?? `@${tag.label.replace(/\s+/gu, "-").toLowerCase()}`;
1081
1083
  var findMentionAtCursor = (text, cursor) => {
1082
1084
  const beforeCursor = text.slice(0, cursor);
1083
- const match = /(^|\s)@([\w-]*)$/.exec(beforeCursor);
1085
+ const match = /(^|\s)@([\w-]*)$/u.exec(beforeCursor);
1084
1086
  if (!match) return null;
1085
1087
  return {
1086
1088
  start: cursor - match[0].trimStart().length,
@@ -1572,9 +1574,9 @@ var PromptInputCompactCycle = ({ options, value, onChange, "aria-label": ariaLab
1572
1574
  const enabledOptions = options.filter((o) => !o.disabled);
1573
1575
  if (enabledOptions.length === 0) return null;
1574
1576
  const currentIndex = options.findIndex((o) => o.value === value);
1575
- const current = currentIndex !== -1 ? options[currentIndex] : enabledOptions[0];
1577
+ const current = currentIndex === -1 ? enabledOptions[0] : options[currentIndex];
1576
1578
  const advance = (direction) => {
1577
- const start = currentIndex !== -1 ? currentIndex : -direction;
1579
+ const start = currentIndex === -1 ? -direction : currentIndex;
1578
1580
  const len = options.length;
1579
1581
  for (let step = 1; step <= len; step++) {
1580
1582
  const candidate = options[(start + direction * step + len * len) % len];
@@ -1717,7 +1719,7 @@ function PromptInputAttachment({ data, className, ...props }) {
1717
1719
  })]
1718
1720
  });
1719
1721
  const ext = data.filename.split(".").pop()?.toUpperCase() ?? "FILE";
1720
- const name = data.filename.replace(/\.[^/.]+$/, "");
1722
+ const name = data.filename.replace(/\.[^/.]+$/u, "");
1721
1723
  return /* @__PURE__ */ jsxs("div", {
1722
1724
  className: cn("group relative inline-flex h-8 items-center gap-2 rounded-md bg-sf-tint/50 px-2 text-sm", className),
1723
1725
  ...props,
@@ -1980,4 +1982,4 @@ PromptInput.Attachment = PromptInputAttachment;
1980
1982
  //#endregion
1981
1983
  export { SF_AI_PROMPT_INPUT_DEFAULT_VARIANTS as A, useRequestField as B, PromptInputSpeechButton as C, PromptInputTextarea as D, PromptInputTags as E, PromptInputRequestControllerProvider as F, createPromptInputRequestController as I, useDisplayField as L, usePromptInputAttachments as M, usePromptInputController as N, PromptInputToolbar as O, useProviderAttachments as P, useOptionalPromptInputRequestController as R, PromptInputProvider as S, PromptInputTag as T, useSetRequestField as V, PromptInputModeCycle as _, PromptInputActionMenuItem as a, PromptInputModelCycle as b, PromptInputAttachButton as c, PromptInputBackLayer as d, PromptInputBody as f, PromptInputEditor as g, PromptInputCompactSelect as h, PromptInputActionMenuContent as i, SF_AI_PROMPT_INPUT_VARIANTS as j, PromptInputTools as k, PromptInputAttachment as l, PromptInputCompactCycle as m, PromptInputActionAddAttachments as n, PromptInputActionMenuTrigger as o, PromptInputButton as p, PromptInputActionMenu as r, PromptInputAddTagButton as s, PromptInput as t, PromptInputAttachments as u, PromptInputModeSelect as v, PromptInputSubmit as w, PromptInputModelSelect as x, PromptInputModeSelector as y, usePromptInputRequestController as z };
1982
1984
 
1983
- //# sourceMappingURL=ai-prompt-input-BVvov_KF.js.map
1985
+ //# sourceMappingURL=ai-prompt-input-Bm4XoSj2.js.map