@signalflare-ai/ui 1.0.0 → 1.2.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 (300) hide show
  1. package/CHANGELOG.md +37 -4
  2. package/README.md +1 -1
  3. package/ai/component-registry.json +1047 -183
  4. package/ai/component-registry.md +4241 -50
  5. package/ai/schemas.ts +99 -502
  6. package/dist/.build-complete +1 -1
  7. package/dist/ai/schemas.d.ts +76 -58
  8. package/dist/ai/schemas.d.ts.map +1 -1
  9. package/dist/{ai-actions-DSVeQn4e.js → ai-actions-BdUZI3Gk.js} +3 -3
  10. package/dist/{ai-actions-DSVeQn4e.js.map → ai-actions-BdUZI3Gk.js.map} +1 -1
  11. package/dist/{ai-agent-card-BXHwhWAU.js → ai-agent-card-BR2NIYhi.js} +1 -1
  12. package/dist/{ai-agent-card-BXHwhWAU.js.map → ai-agent-card-BR2NIYhi.js.map} +1 -1
  13. package/dist/{ai-approval-aa0qvjFN.js → ai-approval-Ba7mrKba.js} +2 -2
  14. package/dist/{ai-approval-aa0qvjFN.js.map → ai-approval-Ba7mrKba.js.map} +1 -1
  15. package/dist/{ai-code-block-BgtIxtZZ.js → ai-code-block-CZtoL73R.js} +3 -3
  16. package/dist/{ai-code-block-BgtIxtZZ.js.map → ai-code-block-CZtoL73R.js.map} +1 -1
  17. package/dist/ai-conversation-Cc7WlaBg.js +242 -0
  18. package/dist/ai-conversation-Cc7WlaBg.js.map +1 -0
  19. package/dist/{ai-info-banner-uFxHHwBA.js → ai-info-banner-C7EWPBj7.js} +7 -3
  20. package/dist/ai-info-banner-C7EWPBj7.js.map +1 -0
  21. package/dist/{ai-message-BjnFznXy.js → ai-message-Bp7L68U_.js} +27 -8
  22. package/dist/ai-message-Bp7L68U_.js.map +1 -0
  23. package/dist/{ai-mission-header-08__gULL.js → ai-mission-header-TiCJfTNt.js} +1 -1
  24. package/dist/{ai-mission-header-08__gULL.js.map → ai-mission-header-TiCJfTNt.js.map} +1 -1
  25. package/dist/{ai-part-group-DBtgTgAn.js → ai-part-group-DNb9I446.js} +3 -3
  26. package/dist/{ai-part-group-DBtgTgAn.js.map → ai-part-group-DNb9I446.js.map} +1 -1
  27. package/dist/{ai-prompt-input-Dy1LfxPk.js → ai-prompt-input-BVvov_KF.js} +467 -25
  28. package/dist/ai-prompt-input-BVvov_KF.js.map +1 -0
  29. package/dist/{ai-question-CHHoDJMg.js → ai-question-GPPMk7YM.js} +2 -2
  30. package/dist/{ai-question-CHHoDJMg.js.map → ai-question-GPPMk7YM.js.map} +1 -1
  31. package/dist/{ai-reasoning-CnL6ZSr5.js → ai-reasoning-_feFjk56.js} +2 -2
  32. package/dist/{ai-reasoning-CnL6ZSr5.js.map → ai-reasoning-_feFjk56.js.map} +1 -1
  33. package/dist/{ai-response-BEUg3xvd.js → ai-response-CvjV3WhV.js} +8 -3
  34. package/dist/ai-response-CvjV3WhV.js.map +1 -0
  35. package/dist/{ai-shimmer-By5_L05p.js → ai-shimmer-j6lKIrjj.js} +1 -1
  36. package/dist/{ai-shimmer-By5_L05p.js.map → ai-shimmer-j6lKIrjj.js.map} +1 -1
  37. package/dist/{ai-status-badge-BGYGWYF6.js → ai-status-badge-CSU_QOdz.js} +1 -1
  38. package/dist/{ai-status-badge-BGYGWYF6.js.map → ai-status-badge-CSU_QOdz.js.map} +1 -1
  39. package/dist/{ai-streaming-text-CMfoThV0.js → ai-streaming-text-IWW1BhvZ.js} +44 -16
  40. package/dist/ai-streaming-text-IWW1BhvZ.js.map +1 -0
  41. package/dist/{ai-subagent-DcPRqkAA.js → ai-subagent-JA4iIMW3.js} +13 -5
  42. package/dist/ai-subagent-JA4iIMW3.js.map +1 -0
  43. package/dist/{ai-suggestion-MgeCg5Ar.js → ai-suggestion-BdO6MBuH.js} +2 -2
  44. package/dist/{ai-suggestion-MgeCg5Ar.js.map → ai-suggestion-BdO6MBuH.js.map} +1 -1
  45. package/dist/{ai-task-list-Da9zIm00.js → ai-task-list-DYw4R1FA.js} +12 -5
  46. package/dist/ai-task-list-DYw4R1FA.js.map +1 -0
  47. package/dist/{ai-timeline-Cwu045IR.js → ai-timeline-C42tOUT8.js} +1 -1
  48. package/dist/{ai-timeline-Cwu045IR.js.map → ai-timeline-C42tOUT8.js.map} +1 -1
  49. package/dist/{ai-tool-Cn1O4xjP.js → ai-tool-03jOTwUI.js} +23 -10
  50. package/dist/ai-tool-03jOTwUI.js.map +1 -0
  51. package/dist/{ai-usage-bar-DjS12DMp.js → ai-usage-bar-BRf5LC_b.js} +1 -1
  52. package/dist/{ai-usage-bar-DjS12DMp.js.map → ai-usage-bar-BRf5LC_b.js.map} +1 -1
  53. package/dist/{badge-D_eaA6wv.js → badge-BheXjMc8.js} +2 -2
  54. package/dist/{badge-D_eaA6wv.js.map → badge-BheXjMc8.js.map} +1 -1
  55. package/dist/{banner-B_6oBrsu.js → banner-CcsjunJg.js} +7 -2
  56. package/dist/banner-CcsjunJg.js.map +1 -0
  57. package/dist/{breadcrumbs-BlmeYfgq.js → breadcrumbs-CouSyy3H.js} +3 -3
  58. package/dist/{breadcrumbs-BlmeYfgq.js.map → breadcrumbs-CouSyy3H.js.map} +1 -1
  59. package/dist/{button-De0267YU.js → button-CO6-qPax.js} +1 -1
  60. package/dist/{button-De0267YU.js.map → button-CO6-qPax.js.map} +1 -1
  61. package/dist/catalog.js +1 -1
  62. package/dist/{chart-BK3sVPnD.js → chart-Dg0qUeSc.js} +2 -2
  63. package/dist/{chart-BK3sVPnD.js.map → chart-Dg0qUeSc.js.map} +1 -1
  64. package/dist/{checkbox-DYhUmZNw.js → checkbox-D7p4QKsC.js} +2 -2
  65. package/dist/{checkbox-DYhUmZNw.js.map → checkbox-D7p4QKsC.js.map} +1 -1
  66. package/dist/{clipboard-text-ssybngLw.js → clipboard-text-kLaMogs3.js} +3 -3
  67. package/dist/{clipboard-text-ssybngLw.js.map → clipboard-text-kLaMogs3.js.map} +1 -1
  68. package/dist/{code-Cx-QSoOT.js → code-BN8InC0G.js} +2 -2
  69. package/dist/{code-Cx-QSoOT.js.map → code-BN8InC0G.js.map} +1 -1
  70. package/dist/{collapsible-DWsXeXmS.js → collapsible-D_ueZ0jz.js} +1 -1
  71. package/dist/{collapsible-DWsXeXmS.js.map → collapsible-D_ueZ0jz.js.map} +1 -1
  72. package/dist/{combobox-C0iW6a0r.js → combobox-B7TOK0U2.js} +3 -3
  73. package/dist/{combobox-C0iW6a0r.js.map → combobox-B7TOK0U2.js.map} +1 -1
  74. package/dist/{command-palette-DGzioeki.js → command-palette-CuNUyJca.js} +2 -2
  75. package/dist/{command-palette-DGzioeki.js.map → command-palette-CuNUyJca.js.map} +1 -1
  76. package/dist/components/ai-actions.js +1 -1
  77. package/dist/components/ai-agent-card.js +1 -1
  78. package/dist/components/ai-approval.js +1 -1
  79. package/dist/components/ai-code-block.js +1 -1
  80. package/dist/components/ai-conversation.js +2 -2
  81. package/dist/components/ai-info-banner.js +1 -1
  82. package/dist/components/ai-message.js +1 -1
  83. package/dist/components/ai-mission-header.js +1 -1
  84. package/dist/components/ai-part-group.js +1 -1
  85. package/dist/components/ai-prompt-input.js +2 -2
  86. package/dist/components/ai-question.js +1 -1
  87. package/dist/components/ai-reasoning.js +1 -1
  88. package/dist/components/ai-response.js +1 -1
  89. package/dist/components/ai-shimmer.js +1 -1
  90. package/dist/components/ai-status-badge.js +1 -1
  91. package/dist/components/ai-streaming-text.js +2 -2
  92. package/dist/components/ai-subagent.js +1 -1
  93. package/dist/components/ai-suggestion.js +1 -1
  94. package/dist/components/ai-task-list.js +1 -1
  95. package/dist/components/ai-timeline.js +1 -1
  96. package/dist/components/ai-tool.js +1 -1
  97. package/dist/components/ai-usage-bar.js +1 -1
  98. package/dist/components/badge.js +1 -1
  99. package/dist/components/banner.js +1 -1
  100. package/dist/components/breadcrumbs.js +1 -1
  101. package/dist/components/button.js +1 -1
  102. package/dist/components/chart.js +2 -2
  103. package/dist/components/checkbox.js +1 -1
  104. package/dist/components/clipboard-text.js +1 -1
  105. package/dist/components/code.js +1 -1
  106. package/dist/components/collapsible.js +1 -1
  107. package/dist/components/combobox.js +1 -1
  108. package/dist/components/command-palette.js +1 -1
  109. package/dist/components/data-grid.js +1 -1
  110. package/dist/components/date-picker.js +1 -1
  111. package/dist/components/date-range-picker.js +1 -1
  112. package/dist/components/dialog.js +1 -1
  113. package/dist/components/dropdown.js +1 -1
  114. package/dist/components/empty.js +1 -1
  115. package/dist/components/field.js +1 -1
  116. package/dist/components/filters.js +1 -1
  117. package/dist/components/flow.js +1 -1
  118. package/dist/components/grid.js +1 -1
  119. package/dist/components/input.js +2 -2
  120. package/dist/components/label.js +1 -1
  121. package/dist/components/layer-card.js +1 -1
  122. package/dist/components/loader.js +1 -1
  123. package/dist/components/menubar.js +1 -1
  124. package/dist/components/meter.js +1 -1
  125. package/dist/components/pagination.js +1 -1
  126. package/dist/components/popover.js +1 -1
  127. package/dist/components/radio.js +1 -1
  128. package/dist/components/select.js +1 -1
  129. package/dist/components/sensitive-input.js +1 -1
  130. package/dist/components/sidebar.js +1 -1
  131. package/dist/components/signalflare-ai-logo.js +1 -1
  132. package/dist/components/sparkline.js +1 -1
  133. package/dist/components/stat-card.js +1 -1
  134. package/dist/components/surface.js +1 -1
  135. package/dist/components/switch.js +1 -1
  136. package/dist/components/table.js +1 -1
  137. package/dist/components/tabs.js +1 -1
  138. package/dist/components/text-roll.js +1 -1
  139. package/dist/components/text.js +1 -1
  140. package/dist/components/theme-toggle.js +1 -1
  141. package/dist/components/toast.js +1 -1
  142. package/dist/components/tooltip.js +1 -1
  143. package/dist/components/use-agent-harness.js +1 -1
  144. package/dist/{data-grid-CG76N_hK.js → data-grid-DGHmU0w3.js} +8 -8
  145. package/dist/{data-grid-CG76N_hK.js.map → data-grid-DGHmU0w3.js.map} +1 -1
  146. package/dist/{date-picker-Dqg9L4xu.js → date-picker--ox89RBy.js} +1 -1
  147. package/dist/{date-picker-Dqg9L4xu.js.map → date-picker--ox89RBy.js.map} +1 -1
  148. package/dist/{date-range-picker-D75LLINc.js → date-range-picker-DVa7QBqE.js} +1 -1
  149. package/dist/{date-range-picker-D75LLINc.js.map → date-range-picker-DVa7QBqE.js.map} +1 -1
  150. package/dist/{dialog-CyHEQXEY.js → dialog-Bv1oSFOd.js} +2 -2
  151. package/dist/{dialog-CyHEQXEY.js.map → dialog-Bv1oSFOd.js.map} +1 -1
  152. package/dist/{dist-1-gcEL2L.js → dist-B6iWiWwp.js} +25 -25
  153. package/dist/{dist-1-gcEL2L.js.map → dist-B6iWiWwp.js.map} +1 -1
  154. package/dist/{dropdown-qnEYRFXZ.js → dropdown-B_nrGXjV.js} +2 -2
  155. package/dist/{dropdown-qnEYRFXZ.js.map → dropdown-B_nrGXjV.js.map} +1 -1
  156. package/dist/{echart-DURZEyai.js → echart-CdOUaT-r.js} +1 -1
  157. package/dist/{echart-DURZEyai.js.map → echart-CdOUaT-r.js.map} +1 -1
  158. package/dist/{empty-D2TypIId.js → empty-DZnN0zKX.js} +11 -6
  159. package/dist/empty-DZnN0zKX.js.map +1 -0
  160. package/dist/{field-Y_UK1_Cg.js → field-B_yVof52.js} +2 -2
  161. package/dist/{field-Y_UK1_Cg.js.map → field-B_yVof52.js.map} +1 -1
  162. package/dist/{filters-Bw_U6ZTx.js → filters-cpJCY21R.js} +7 -7
  163. package/dist/{filters-Bw_U6ZTx.js.map → filters-cpJCY21R.js.map} +1 -1
  164. package/dist/{flow-BRsYUCJa.js → flow-B4v198ot.js} +1 -1
  165. package/dist/{flow-BRsYUCJa.js.map → flow-B4v198ot.js.map} +1 -1
  166. package/dist/genui.js +1 -1
  167. package/dist/{grid-qUAN9hFx.js → grid-CEd64Lnh.js} +1 -1
  168. package/dist/{grid-qUAN9hFx.js.map → grid-CEd64Lnh.js.map} +1 -1
  169. package/dist/{highlight-to-react-ClEfL81q.js → highlight-to-react-D0Yav4jk.js} +1 -1
  170. package/dist/{highlight-to-react-ClEfL81q.js.map → highlight-to-react-D0Yav4jk.js.map} +1 -1
  171. package/dist/index.js +69 -69
  172. package/dist/{input-DXYUjGgD.js → input-B2bbijRh.js} +2 -2
  173. package/dist/{input-DXYUjGgD.js.map → input-B2bbijRh.js.map} +1 -1
  174. package/dist/{input-DddtBN-g.js → input-ClB_E4Lb.js} +4 -4
  175. package/dist/{input-DddtBN-g.js.map → input-ClB_E4Lb.js.map} +1 -1
  176. package/dist/{label-QtJxtJ4u.js → label-DUv_urO1.js} +2 -2
  177. package/dist/{label-QtJxtJ4u.js.map → label-DUv_urO1.js.map} +1 -1
  178. package/dist/{layer-card-BME0eljh.js → layer-card-BK7eYfwn.js} +1 -1
  179. package/dist/{layer-card-BME0eljh.js.map → layer-card-BK7eYfwn.js.map} +1 -1
  180. package/dist/layout-DJHMMap2.js +6103 -0
  181. package/dist/layout-DJHMMap2.js.map +1 -0
  182. package/dist/measured-text-BI3dTJmH.js +290 -0
  183. package/dist/measured-text-BI3dTJmH.js.map +1 -0
  184. package/dist/{menubar-C8NzAjfd.js → menubar-Cxf3xeAt.js} +2 -2
  185. package/dist/{menubar-C8NzAjfd.js.map → menubar-Cxf3xeAt.js.map} +1 -1
  186. package/dist/{meter-CpmTenEr.js → meter-BFFe9l5b.js} +1 -1
  187. package/dist/{meter-CpmTenEr.js.map → meter-BFFe9l5b.js.map} +1 -1
  188. package/dist/{pagination-BVqdlONY.js → pagination-yS372Tr4.js} +2 -2
  189. package/dist/{pagination-BVqdlONY.js.map → pagination-yS372Tr4.js.map} +1 -1
  190. package/dist/{popover-BRQZ2b6z.js → popover-SRoJaCZr.js} +1 -1
  191. package/dist/{popover-BRQZ2b6z.js.map → popover-SRoJaCZr.js.map} +1 -1
  192. package/dist/{radio-BNSwOt3B.js → radio-BcwhwYNB.js} +1 -1
  193. package/dist/{radio-BNSwOt3B.js.map → radio-BcwhwYNB.js.map} +1 -1
  194. package/dist/{select-1w2aebGQ.js → select-DMhdoHMa.js} +4 -4
  195. package/dist/{select-1w2aebGQ.js.map → select-DMhdoHMa.js.map} +1 -1
  196. package/dist/{sensitive-input-82Cez3vj.js → sensitive-input-CJUpIRal.js} +3 -3
  197. package/dist/{sensitive-input-82Cez3vj.js.map → sensitive-input-CJUpIRal.js.map} +1 -1
  198. package/dist/{sidebar-CAsCmSpM.js → sidebar-D4zrlYpn.js} +2 -2
  199. package/dist/{sidebar-CAsCmSpM.js.map → sidebar-D4zrlYpn.js.map} +1 -1
  200. package/dist/{signalflare-ai-logo-DDhxMJD6.js → signalflare-ai-logo-Bipogceq.js} +1 -1
  201. package/dist/{signalflare-ai-logo-DDhxMJD6.js.map → signalflare-ai-logo-Bipogceq.js.map} +1 -1
  202. package/dist/{skeleton-line-Do3UmGk9.js → skeleton-line-CH1-h6e2.js} +1 -1
  203. package/dist/{skeleton-line-Do3UmGk9.js.map → skeleton-line-CH1-h6e2.js.map} +1 -1
  204. package/dist/{sparkline-DdbeM4Ai.js → sparkline-DHmgj1d0.js} +2 -2
  205. package/dist/{sparkline-DdbeM4Ai.js.map → sparkline-DHmgj1d0.js.map} +1 -1
  206. package/dist/src/blocks/agent-harness/agent-harness.d.ts.map +1 -1
  207. package/dist/src/blocks/agent-harness/agent-harness.tsx +29 -5
  208. package/dist/src/components/ai-conversation/ai-conversation.d.ts +69 -37
  209. package/dist/src/components/ai-conversation/ai-conversation.d.ts.map +1 -1
  210. package/dist/src/components/ai-conversation/index.d.ts +2 -1
  211. package/dist/src/components/ai-conversation/index.d.ts.map +1 -1
  212. package/dist/src/components/ai-conversation/measurement-constants.d.ts +30 -0
  213. package/dist/src/components/ai-conversation/measurement-constants.d.ts.map +1 -0
  214. package/dist/src/components/ai-info-banner/ai-info-banner.d.ts.map +1 -1
  215. package/dist/src/components/ai-message/ai-message.d.ts +3 -0
  216. package/dist/src/components/ai-message/ai-message.d.ts.map +1 -1
  217. package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts +58 -4
  218. package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
  219. package/dist/src/components/ai-prompt-input/controller.d.ts +10 -2
  220. package/dist/src/components/ai-prompt-input/controller.d.ts.map +1 -1
  221. package/dist/src/components/ai-prompt-input/index.d.ts +2 -2
  222. package/dist/src/components/ai-prompt-input/index.d.ts.map +1 -1
  223. package/dist/src/components/ai-prompt-input/types.d.ts +16 -0
  224. package/dist/src/components/ai-prompt-input/types.d.ts.map +1 -1
  225. package/dist/src/components/ai-response/ai-response.d.ts +12 -1
  226. package/dist/src/components/ai-response/ai-response.d.ts.map +1 -1
  227. package/dist/src/components/ai-streaming-text/ai-streaming-text.d.ts +27 -0
  228. package/dist/src/components/ai-streaming-text/ai-streaming-text.d.ts.map +1 -1
  229. package/dist/src/components/ai-streaming-text/index.d.ts +1 -1
  230. package/dist/src/components/ai-streaming-text/index.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-task-list/ai-task-list.d.ts.map +1 -1
  233. package/dist/src/components/ai-tool/ai-tool.d.ts.map +1 -1
  234. package/dist/src/components/banner/banner.d.ts.map +1 -1
  235. package/dist/src/components/empty/empty.d.ts.map +1 -1
  236. package/dist/src/components/stat-card/stat-card.d.ts +5 -0
  237. package/dist/src/components/stat-card/stat-card.d.ts.map +1 -1
  238. package/dist/src/components/text/text.d.ts +35 -1
  239. package/dist/src/components/text/text.d.ts.map +1 -1
  240. package/dist/src/components/tooltip/tooltip.d.ts.map +1 -1
  241. package/dist/src/index.d.ts +3 -3
  242. package/dist/src/index.d.ts.map +1 -1
  243. package/dist/src/utils/index.d.ts +2 -0
  244. package/dist/src/utils/index.d.ts.map +1 -1
  245. package/dist/src/utils/measured-text.d.ts +40 -0
  246. package/dist/src/utils/measured-text.d.ts.map +1 -0
  247. package/dist/src/utils/use-measured-text.d.ts +59 -0
  248. package/dist/src/utils/use-measured-text.d.ts.map +1 -0
  249. package/dist/{stat-card-CEZscNh8.js → stat-card-Ew-ofzEm.js} +28 -10
  250. package/dist/stat-card-Ew-ofzEm.js.map +1 -0
  251. package/dist/styles/sf-binding.css +9 -1
  252. package/dist/styles/sf-standalone.css +2 -2
  253. package/dist/styles/shadcn.css +1 -1
  254. package/dist/styles/theme-fedramp.css +12 -3
  255. package/dist/styles/theme-minimal.css +104 -26
  256. package/dist/styles/theme-sf.css +138 -39
  257. package/dist/styles/theme-vesper.css +91 -0
  258. package/dist/{surface-BduI7Ehl.js → surface-DGwRlC0o.js} +1 -1
  259. package/dist/{surface-BduI7Ehl.js.map → surface-DGwRlC0o.js.map} +1 -1
  260. package/dist/{switch-CzZBRBL7.js → switch-BxAMfHdt.js} +2 -2
  261. package/dist/{switch-CzZBRBL7.js.map → switch-BxAMfHdt.js.map} +1 -1
  262. package/dist/{table-Rv4JMy0B.js → table-BBeAtYVZ.js} +2 -2
  263. package/dist/{table-Rv4JMy0B.js.map → table-BBeAtYVZ.js.map} +1 -1
  264. package/dist/{tabs-1cHrYoel.js → tabs-CeHu7Scn.js} +1 -1
  265. package/dist/{tabs-1cHrYoel.js.map → tabs-CeHu7Scn.js.map} +1 -1
  266. package/dist/{text-KJmGkwnf.js → text-Cqryz7rk.js} +27 -5
  267. package/dist/text-Cqryz7rk.js.map +1 -0
  268. package/dist/{text-roll-BZ3I1umc.js → text-roll-Ch52hcQj.js} +1 -1
  269. package/dist/{text-roll-BZ3I1umc.js.map → text-roll-Ch52hcQj.js.map} +1 -1
  270. package/dist/{theme-toggle-Bhu681D7.js → theme-toggle-LDfIKEqx.js} +3 -3
  271. package/dist/{theme-toggle-Bhu681D7.js.map → theme-toggle-LDfIKEqx.js.map} +1 -1
  272. package/dist/{toast-Nw28a5Cx.js → toast-CaFQNYng.js} +2 -2
  273. package/dist/{toast-Nw28a5Cx.js.map → toast-CaFQNYng.js.map} +1 -1
  274. package/dist/{tooltip-Cb7QW-7H.js → tooltip-g9lFsvcT.js} +8 -2
  275. package/dist/tooltip-g9lFsvcT.js.map +1 -0
  276. package/dist/{use-agent-harness-BMyF8pTq.js → use-agent-harness-BTcNJdw4.js} +1 -1
  277. package/dist/{use-agent-harness-BMyF8pTq.js.map → use-agent-harness-BTcNJdw4.js.map} +1 -1
  278. package/dist/utils.js +2 -1
  279. package/package.json +2 -1
  280. package/scripts/component-registry/index.ts +2 -2
  281. package/scripts/css-build.ts +1 -1
  282. package/scripts/theme-generator/config.ts +27 -141
  283. package/scripts/theme-generator/generate-css.ts +0 -1
  284. package/scripts/theme-generator/index.ts +0 -1
  285. package/dist/ai-conversation-CArP7C8K.js +0 -184
  286. package/dist/ai-conversation-CArP7C8K.js.map +0 -1
  287. package/dist/ai-info-banner-uFxHHwBA.js.map +0 -1
  288. package/dist/ai-message-BjnFznXy.js.map +0 -1
  289. package/dist/ai-prompt-input-Dy1LfxPk.js.map +0 -1
  290. package/dist/ai-response-BEUg3xvd.js.map +0 -1
  291. package/dist/ai-streaming-text-CMfoThV0.js.map +0 -1
  292. package/dist/ai-subagent-DcPRqkAA.js.map +0 -1
  293. package/dist/ai-task-list-Da9zIm00.js.map +0 -1
  294. package/dist/ai-tool-Cn1O4xjP.js.map +0 -1
  295. package/dist/banner-B_6oBrsu.js.map +0 -1
  296. package/dist/empty-D2TypIId.js.map +0 -1
  297. package/dist/stat-card-CEZscNh8.js.map +0 -1
  298. package/dist/styles/theme-blue-tint.css +0 -98
  299. package/dist/text-KJmGkwnf.js.map +0 -1
  300. package/dist/tooltip-Cb7QW-7H.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-message-BjnFznXy.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 { Tooltip } from \"../tooltip\";\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 <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 );\n}\n\n// ─── AiMessageContent ────────────────────────────────────────────────────────\n\nexport type AiMessageContentProps = HTMLAttributes<HTMLDivElement>;\n\n/**\n * Content bubble. Applies user/assistant bubble styles via group selectors.\n */\nexport function AiMessageContent({\n children,\n className,\n ...props\n}: AiMessageContentProps) {\n return (\n <div\n className={cn(\n \"flex w-fit max-w-full flex-col gap-2 overflow-hidden 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 {children}\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 +0,0 @@
1
- {"version":3,"file":"ai-prompt-input-Dy1LfxPk.js","names":[],"sources":["../../../node_modules/.bun/@tanstack+store@0.11.0/node_modules/@tanstack/store/dist/alien.js","../../../node_modules/.bun/@tanstack+store@0.11.0/node_modules/@tanstack/store/dist/atom.js","../../../node_modules/.bun/@tanstack+store@0.11.0/node_modules/@tanstack/store/dist/store.js","../../../node_modules/.bun/@tanstack+react-store@0.11.0+21ccd8898788a04d/node_modules/@tanstack/react-store/dist/useSelector.js","../../../node_modules/.bun/@tanstack+react-store@0.11.0+21ccd8898788a04d/node_modules/@tanstack/react-store/dist/useStore.js","../src/components/ai-prompt-input/controller.ts","../src/components/ai-prompt-input/ai-prompt-input.tsx"],"sourcesContent":["//#region src/alien.ts\nlet ReactiveFlags = /* @__PURE__ */ function(ReactiveFlags) {\n\tReactiveFlags[ReactiveFlags[\"None\"] = 0] = \"None\";\n\tReactiveFlags[ReactiveFlags[\"Mutable\"] = 1] = \"Mutable\";\n\tReactiveFlags[ReactiveFlags[\"Watching\"] = 2] = \"Watching\";\n\tReactiveFlags[ReactiveFlags[\"RecursedCheck\"] = 4] = \"RecursedCheck\";\n\tReactiveFlags[ReactiveFlags[\"Recursed\"] = 8] = \"Recursed\";\n\tReactiveFlags[ReactiveFlags[\"Dirty\"] = 16] = \"Dirty\";\n\tReactiveFlags[ReactiveFlags[\"Pending\"] = 32] = \"Pending\";\n\treturn ReactiveFlags;\n}({});\n/* @__NO_SIDE_EFFECTS__ */\nfunction createReactiveSystem({ update, notify, unwatched }) {\n\treturn {\n\t\tlink,\n\t\tunlink,\n\t\tpropagate,\n\t\tcheckDirty,\n\t\tshallowPropagate\n\t};\n\tfunction link(dep, sub, version) {\n\t\tconst prevDep = sub.depsTail;\n\t\tif (prevDep !== void 0 && prevDep.dep === dep) return;\n\t\tconst nextDep = prevDep !== void 0 ? prevDep.nextDep : sub.deps;\n\t\tif (nextDep !== void 0 && nextDep.dep === dep) {\n\t\t\tnextDep.version = version;\n\t\t\tsub.depsTail = nextDep;\n\t\t\treturn;\n\t\t}\n\t\tconst prevSub = dep.subsTail;\n\t\tif (prevSub !== void 0 && prevSub.version === version && prevSub.sub === sub) return;\n\t\tconst newLink = sub.depsTail = dep.subsTail = {\n\t\t\tversion,\n\t\t\tdep,\n\t\t\tsub,\n\t\t\tprevDep,\n\t\t\tnextDep,\n\t\t\tprevSub,\n\t\t\tnextSub: void 0\n\t\t};\n\t\tif (nextDep !== void 0) nextDep.prevDep = newLink;\n\t\tif (prevDep !== void 0) prevDep.nextDep = newLink;\n\t\telse sub.deps = newLink;\n\t\tif (prevSub !== void 0) prevSub.nextSub = newLink;\n\t\telse dep.subs = newLink;\n\t}\n\tfunction unlink(link, sub = link.sub) {\n\t\tconst dep = link.dep;\n\t\tconst prevDep = link.prevDep;\n\t\tconst nextDep = link.nextDep;\n\t\tconst nextSub = link.nextSub;\n\t\tconst prevSub = link.prevSub;\n\t\tif (nextDep !== void 0) nextDep.prevDep = prevDep;\n\t\telse sub.depsTail = prevDep;\n\t\tif (prevDep !== void 0) prevDep.nextDep = nextDep;\n\t\telse sub.deps = nextDep;\n\t\tif (nextSub !== void 0) nextSub.prevSub = prevSub;\n\t\telse dep.subsTail = prevSub;\n\t\tif (prevSub !== void 0) prevSub.nextSub = nextSub;\n\t\telse if ((dep.subs = nextSub) === void 0) unwatched(dep);\n\t\treturn nextDep;\n\t}\n\tfunction propagate(link) {\n\t\tlet next = link.nextSub;\n\t\tlet stack;\n\t\ttop: do {\n\t\t\tconst sub = link.sub;\n\t\t\tlet flags = sub.flags;\n\t\t\tif (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed | ReactiveFlags.Dirty | ReactiveFlags.Pending))) sub.flags = flags | ReactiveFlags.Pending;\n\t\t\telse if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed))) flags = ReactiveFlags.None;\n\t\t\telse if (!(flags & ReactiveFlags.RecursedCheck)) sub.flags = flags & ~ReactiveFlags.Recursed | ReactiveFlags.Pending;\n\t\t\telse if (!(flags & (ReactiveFlags.Dirty | ReactiveFlags.Pending)) && isValidLink(link, sub)) {\n\t\t\t\tsub.flags = flags | (ReactiveFlags.Recursed | ReactiveFlags.Pending);\n\t\t\t\tflags &= ReactiveFlags.Mutable;\n\t\t\t} else flags = ReactiveFlags.None;\n\t\t\tif (flags & ReactiveFlags.Watching) notify(sub);\n\t\t\tif (flags & ReactiveFlags.Mutable) {\n\t\t\t\tconst subSubs = sub.subs;\n\t\t\t\tif (subSubs !== void 0) {\n\t\t\t\t\tconst nextSub = (link = subSubs).nextSub;\n\t\t\t\t\tif (nextSub !== void 0) {\n\t\t\t\t\t\tstack = {\n\t\t\t\t\t\t\tvalue: next,\n\t\t\t\t\t\t\tprev: stack\n\t\t\t\t\t\t};\n\t\t\t\t\t\tnext = nextSub;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((link = next) !== void 0) {\n\t\t\t\tnext = link.nextSub;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twhile (stack !== void 0) {\n\t\t\t\tlink = stack.value;\n\t\t\t\tstack = stack.prev;\n\t\t\t\tif (link !== void 0) {\n\t\t\t\t\tnext = link.nextSub;\n\t\t\t\t\tcontinue top;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t} while (true);\n\t}\n\tfunction checkDirty(link, sub) {\n\t\tlet stack;\n\t\tlet checkDepth = 0;\n\t\tlet dirty = false;\n\t\ttop: do {\n\t\t\tconst dep = link.dep;\n\t\t\tconst flags = dep.flags;\n\t\t\tif (sub.flags & ReactiveFlags.Dirty) dirty = true;\n\t\t\telse if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) === (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) {\n\t\t\t\tif (update(dep)) {\n\t\t\t\t\tconst subs = dep.subs;\n\t\t\t\t\tif (subs.nextSub !== void 0) shallowPropagate(subs);\n\t\t\t\t\tdirty = true;\n\t\t\t\t}\n\t\t\t} else if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Pending)) === (ReactiveFlags.Mutable | ReactiveFlags.Pending)) {\n\t\t\t\tif (link.nextSub !== void 0 || link.prevSub !== void 0) stack = {\n\t\t\t\t\tvalue: link,\n\t\t\t\t\tprev: stack\n\t\t\t\t};\n\t\t\t\tlink = dep.deps;\n\t\t\t\tsub = dep;\n\t\t\t\t++checkDepth;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!dirty) {\n\t\t\t\tconst nextDep = link.nextDep;\n\t\t\t\tif (nextDep !== void 0) {\n\t\t\t\t\tlink = nextDep;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\twhile (checkDepth--) {\n\t\t\t\tconst firstSub = sub.subs;\n\t\t\t\tconst hasMultipleSubs = firstSub.nextSub !== void 0;\n\t\t\t\tif (hasMultipleSubs) {\n\t\t\t\t\tlink = stack.value;\n\t\t\t\t\tstack = stack.prev;\n\t\t\t\t} else link = firstSub;\n\t\t\t\tif (dirty) {\n\t\t\t\t\tif (update(sub)) {\n\t\t\t\t\t\tif (hasMultipleSubs) shallowPropagate(firstSub);\n\t\t\t\t\t\tsub = link.sub;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tdirty = false;\n\t\t\t\t} else sub.flags &= ~ReactiveFlags.Pending;\n\t\t\t\tsub = link.sub;\n\t\t\t\tconst nextDep = link.nextDep;\n\t\t\t\tif (nextDep !== void 0) {\n\t\t\t\t\tlink = nextDep;\n\t\t\t\t\tcontinue top;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn dirty;\n\t\t} while (true);\n\t}\n\tfunction shallowPropagate(link) {\n\t\tdo {\n\t\t\tconst sub = link.sub;\n\t\t\tconst flags = sub.flags;\n\t\t\tif ((flags & (ReactiveFlags.Pending | ReactiveFlags.Dirty)) === ReactiveFlags.Pending) {\n\t\t\t\tsub.flags = flags | ReactiveFlags.Dirty;\n\t\t\t\tif ((flags & (ReactiveFlags.Watching | ReactiveFlags.RecursedCheck)) === ReactiveFlags.Watching) notify(sub);\n\t\t\t}\n\t\t} while ((link = link.nextSub) !== void 0);\n\t}\n\tfunction isValidLink(checkLink, sub) {\n\t\tlet link = sub.depsTail;\n\t\twhile (link !== void 0) {\n\t\t\tif (link === checkLink) return true;\n\t\t\tlink = link.prevDep;\n\t\t}\n\t\treturn false;\n\t}\n}\n\n//#endregion\nexport { ReactiveFlags, createReactiveSystem };\n//# sourceMappingURL=alien.js.map","import { ReactiveFlags, createReactiveSystem } from \"./alien.js\";\n\n//#region src/atom.ts\nfunction toObserver(nextHandler, errorHandler, completionHandler) {\n\tconst isObserver = typeof nextHandler === \"object\";\n\tconst self = isObserver ? nextHandler : void 0;\n\treturn {\n\t\tnext: (isObserver ? nextHandler.next : nextHandler)?.bind(self),\n\t\terror: (isObserver ? nextHandler.error : errorHandler)?.bind(self),\n\t\tcomplete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)\n\t};\n}\nconst queuedEffects = [];\nlet cycle = 0;\nconst { link, unlink, propagate, checkDirty, shallowPropagate } = /* @__PURE__ */ createReactiveSystem({\n\tupdate(atom) {\n\t\treturn atom._update();\n\t},\n\tnotify(effect) {\n\t\tqueuedEffects[queuedEffectsLength++] = effect;\n\t\teffect.flags &= ~ReactiveFlags.Watching;\n\t},\n\tunwatched(atom) {\n\t\tif (atom.depsTail !== void 0) {\n\t\t\tatom.depsTail = void 0;\n\t\t\tatom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;\n\t\t\tpurgeDeps(atom);\n\t\t}\n\t}\n});\nlet notifyIndex = 0;\nlet queuedEffectsLength = 0;\nlet activeSub;\nlet batchDepth = 0;\nfunction batch(fn) {\n\ttry {\n\t\t++batchDepth;\n\t\tfn();\n\t} finally {\n\t\tif (!--batchDepth) flush();\n\t}\n}\nfunction purgeDeps(sub) {\n\tconst depsTail = sub.depsTail;\n\tlet dep = depsTail !== void 0 ? depsTail.nextDep : sub.deps;\n\twhile (dep !== void 0) dep = unlink(dep, sub);\n}\nfunction flush() {\n\tif (batchDepth > 0) return;\n\twhile (notifyIndex < queuedEffectsLength) {\n\t\tconst effect = queuedEffects[notifyIndex];\n\t\tqueuedEffects[notifyIndex++] = void 0;\n\t\teffect.notify();\n\t}\n\tnotifyIndex = 0;\n\tqueuedEffectsLength = 0;\n}\nfunction createAsyncAtom(getValue, options) {\n\tconst ref = {};\n\tconst atom = createAtom(() => {\n\t\tgetValue().then((data) => {\n\t\t\tconst internalAtom = ref.current;\n\t\t\tif (internalAtom._update({\n\t\t\t\tstatus: \"done\",\n\t\t\t\tdata\n\t\t\t})) {\n\t\t\t\tconst subs = internalAtom.subs;\n\t\t\t\tif (subs !== void 0) {\n\t\t\t\t\tpropagate(subs);\n\t\t\t\t\tshallowPropagate(subs);\n\t\t\t\t\tflush();\n\t\t\t\t}\n\t\t\t}\n\t\t}, (error) => {\n\t\t\tconst internalAtom = ref.current;\n\t\t\tif (internalAtom._update({\n\t\t\t\tstatus: \"error\",\n\t\t\t\terror\n\t\t\t})) {\n\t\t\t\tconst subs = internalAtom.subs;\n\t\t\t\tif (subs !== void 0) {\n\t\t\t\t\tpropagate(subs);\n\t\t\t\t\tshallowPropagate(subs);\n\t\t\t\t\tflush();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn { status: \"pending\" };\n\t}, options);\n\tref.current = atom;\n\treturn atom;\n}\nfunction createAtom(valueOrFn, options) {\n\tconst isComputed = typeof valueOrFn === \"function\";\n\tconst getter = valueOrFn;\n\tconst atom = {\n\t\t_snapshot: isComputed ? void 0 : valueOrFn,\n\t\tsubs: void 0,\n\t\tsubsTail: void 0,\n\t\tdeps: void 0,\n\t\tdepsTail: void 0,\n\t\tflags: isComputed ? ReactiveFlags.None : ReactiveFlags.Mutable,\n\t\tget() {\n\t\t\tif (activeSub !== void 0) link(atom, activeSub, cycle);\n\t\t\treturn atom._snapshot;\n\t\t},\n\t\tsubscribe(observerOrFn) {\n\t\t\tconst obs = toObserver(observerOrFn);\n\t\t\tconst observed = { current: false };\n\t\t\tconst e = effect(() => {\n\t\t\t\tatom.get();\n\t\t\t\tif (!observed.current) observed.current = true;\n\t\t\t\telse obs.next?.(atom._snapshot);\n\t\t\t});\n\t\t\treturn { unsubscribe: () => {\n\t\t\t\te.stop();\n\t\t\t} };\n\t\t},\n\t\t_update(getValue) {\n\t\t\tconst prevSub = activeSub;\n\t\t\tconst compare = options?.compare ?? Object.is;\n\t\t\tif (isComputed) {\n\t\t\t\tactiveSub = atom;\n\t\t\t\t++cycle;\n\t\t\t\tatom.depsTail = void 0;\n\t\t\t} else if (getValue === void 0) return false;\n\t\t\tif (isComputed) atom.flags = ReactiveFlags.Mutable | ReactiveFlags.RecursedCheck;\n\t\t\ttry {\n\t\t\t\tconst oldValue = atom._snapshot;\n\t\t\t\tconst newValue = typeof getValue === \"function\" ? getValue(oldValue) : getValue === void 0 && isComputed ? getter(oldValue) : getValue;\n\t\t\t\tif (oldValue === void 0 || !compare(oldValue, newValue)) {\n\t\t\t\t\tatom._snapshot = newValue;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t} finally {\n\t\t\t\tactiveSub = prevSub;\n\t\t\t\tif (isComputed) atom.flags &= ~ReactiveFlags.RecursedCheck;\n\t\t\t\tpurgeDeps(atom);\n\t\t\t}\n\t\t}\n\t};\n\tif (isComputed) {\n\t\tatom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;\n\t\tatom.get = function() {\n\t\t\tconst flags = atom.flags;\n\t\t\tif (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(atom.deps, atom)) {\n\t\t\t\tif (atom._update()) {\n\t\t\t\t\tconst subs = atom.subs;\n\t\t\t\t\tif (subs !== void 0) shallowPropagate(subs);\n\t\t\t\t}\n\t\t\t} else if (flags & ReactiveFlags.Pending) atom.flags = flags & ~ReactiveFlags.Pending;\n\t\t\tif (activeSub !== void 0) link(atom, activeSub, cycle);\n\t\t\treturn atom._snapshot;\n\t\t};\n\t} else atom.set = function(valueOrFn) {\n\t\tif (atom._update(valueOrFn)) {\n\t\t\tconst subs = atom.subs;\n\t\t\tif (subs !== void 0) {\n\t\t\t\tpropagate(subs);\n\t\t\t\tshallowPropagate(subs);\n\t\t\t\tflush();\n\t\t\t}\n\t\t}\n\t};\n\treturn atom;\n}\nfunction effect(fn) {\n\tconst run = () => {\n\t\tconst prevSub = activeSub;\n\t\tactiveSub = effectObj;\n\t\t++cycle;\n\t\teffectObj.depsTail = void 0;\n\t\teffectObj.flags = ReactiveFlags.Watching | ReactiveFlags.RecursedCheck;\n\t\ttry {\n\t\t\treturn fn();\n\t\t} finally {\n\t\t\tactiveSub = prevSub;\n\t\t\teffectObj.flags &= ~ReactiveFlags.RecursedCheck;\n\t\t\tpurgeDeps(effectObj);\n\t\t}\n\t};\n\tconst effectObj = {\n\t\tdeps: void 0,\n\t\tdepsTail: void 0,\n\t\tsubs: void 0,\n\t\tsubsTail: void 0,\n\t\tflags: ReactiveFlags.Watching | ReactiveFlags.RecursedCheck,\n\t\tnotify() {\n\t\t\tconst flags = this.flags;\n\t\t\tif (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(this.deps, this)) run();\n\t\t\telse this.flags = ReactiveFlags.Watching;\n\t\t},\n\t\tstop() {\n\t\t\tthis.flags = ReactiveFlags.None;\n\t\t\tthis.depsTail = void 0;\n\t\t\tpurgeDeps(this);\n\t\t}\n\t};\n\trun();\n\treturn effectObj;\n}\n\n//#endregion\nexport { batch, createAsyncAtom, createAtom, flush, toObserver };\n//# sourceMappingURL=atom.js.map","import { createAtom, toObserver } from \"./atom.js\";\n\n//#region src/store.ts\nvar Store = class {\n\tconstructor(valueOrFn, actionsFactory) {\n\t\tthis.atom = createAtom(valueOrFn);\n\t\tthis.get = this.get.bind(this);\n\t\tthis.setState = this.setState.bind(this);\n\t\tthis.subscribe = this.subscribe.bind(this);\n\t\tif (actionsFactory) this.actions = actionsFactory(this);\n\t}\n\tsetState(updater) {\n\t\tthis.atom.set(updater);\n\t}\n\tget state() {\n\t\treturn this.atom.get();\n\t}\n\tget() {\n\t\treturn this.state;\n\t}\n\tsubscribe(observerOrFn) {\n\t\treturn this.atom.subscribe(toObserver(observerOrFn));\n\t}\n};\nvar ReadonlyStore = class {\n\tconstructor(valueOrFn) {\n\t\tthis.atom = createAtom(valueOrFn);\n\t}\n\tget state() {\n\t\treturn this.atom.get();\n\t}\n\tget() {\n\t\treturn this.state;\n\t}\n\tsubscribe(observerOrFn) {\n\t\treturn this.atom.subscribe(toObserver(observerOrFn));\n\t}\n};\nfunction createStore(valueOrFn, actions) {\n\tif (typeof valueOrFn === \"function\") return new ReadonlyStore(valueOrFn);\n\tif (actions) return new Store(valueOrFn, actions);\n\treturn new Store(valueOrFn);\n}\n\n//#endregion\nexport { ReadonlyStore, Store, createStore };\n//# sourceMappingURL=store.js.map","import { useCallback } from \"react\";\nimport { useSyncExternalStoreWithSelector } from \"use-sync-external-store/shim/with-selector\";\n\n//#region src/useSelector.ts\nfunction defaultCompare(a, b) {\n\treturn a === b;\n}\n/**\n* Selects a slice of state from an atom or store and subscribes the component\n* to that selection.\n*\n* This is the primary React read hook for TanStack Store. It works with any\n* source that exposes `get()` and `subscribe()`, including atoms, readonly\n* atoms, stores, and readonly stores.\n*\n* Omit the selector to subscribe to the whole value.\n*\n* @example\n* ```tsx\n* const count = useSelector(counterStore, (state) => state.count)\n* ```\n*\n* @example\n* ```tsx\n* const value = useSelector(countAtom)\n* ```\n*/\nfunction useSelector(source, selector = (s) => s, options) {\n\tconst compare = options?.compare ?? defaultCompare;\n\tconst subscribe = useCallback((handleStoreChange) => {\n\t\tconst { unsubscribe } = source.subscribe(handleStoreChange);\n\t\treturn unsubscribe;\n\t}, [source]);\n\tconst getSnapshot = useCallback(() => source.get(), [source]);\n\treturn useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, selector, compare);\n}\n\n//#endregion\nexport { useSelector };\n//# sourceMappingURL=useSelector.js.map","import { useSelector } from \"./useSelector.js\";\n\n//#region src/useStore.ts\n/**\n* Deprecated alias for {@link useSelector}.\n*\n* @example\n* ```tsx\n* const count = useStore(counterStore, (state) => state.count)\n* ```\n*\n* @deprecated Use `useSelector` instead.\n*/\nconst useStore = (source, selector = (s) => s, compare) => useSelector(source, selector, { compare });\n\n//#endregion\nexport { useStore };\n//# sourceMappingURL=useStore.js.map","/**\n * PromptInputController — the single source of truth for a prompt input tree.\n *\n * Built on @tanstack/store for per-slice subscriptions (sub-components\n * re-render only when the fields they read change). The controller exposes\n * two stores:\n *\n * - `request` : outbound state (text, files, mode, model, …extensions). The\n * user edits these; `submit()` ships them to `onSubmit`.\n * - `display` : inbound state (sendStatus, tasks, pendingQuestion, pendingPlan,\n * usage). The harness writes these in; sub-components read them.\n *\n * Consumers never touch the stores directly. They use:\n *\n * const mode = useRequestField('mode'); // read\n * const setMode = useSetRequestField('mode'); // write\n * const status = useDisplayField('sendStatus'); // read\n *\n * Or, for advanced use, subscribe to the whole store with\n * `useStore(controller.request)`.\n */\n\nimport { useStore } from \"@tanstack/react-store\";\nimport { Store } from \"@tanstack/store\";\nimport { createContext, useContext, useMemo } from \"react\";\n\nimport type {\n AttachmentFile,\n PromptInputDisplayContext,\n PromptInputRequestContext,\n PromptInputSubmitPayload,\n} from \"./types\";\n\n// ─── Controller shape ─────────────────────────────────────────────────────────\n\nexport type PromptInputRequestController<\n TExt extends Record<string, unknown> = Record<string, unknown>,\n> = {\n /** Outbound stores (user-edited). */\n request: Store<PromptInputRequestContext<TExt>>;\n /** Inbound stores (harness-pushed). */\n display: Store<PromptInputDisplayContext>;\n\n /** Imperative helpers. */\n setRequestField: <K extends keyof PromptInputRequestContext<TExt>>(\n key: K,\n value: PromptInputRequestContext<TExt>[K]\n ) => void;\n setDisplayField: <K extends keyof PromptInputDisplayContext>(\n key: K,\n value: PromptInputDisplayContext[K]\n ) => void;\n\n /** Attachment helpers (common enough to warrant first-class methods). */\n addAttachments: (files: AttachmentFile[]) => void;\n removeAttachment: (id: string) => void;\n clearAttachments: () => void;\n\n /** Reset request state (clears text + files; keeps mode/model). */\n resetRequest: () => void;\n\n /** Snapshot the current request context (for onSubmit). */\n snapshot: () => PromptInputSubmitPayload<TExt>;\n};\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\nconst DEFAULT_REQUEST: PromptInputRequestContext = {\n text: \"\",\n files: [],\n};\n\nconst DEFAULT_DISPLAY: PromptInputDisplayContext = {\n sendStatus: \"idle\",\n};\n\nexport type CreatePromptInputRequestControllerOptions<\n TExt extends Record<string, unknown> = Record<string, unknown>,\n> = {\n initialRequest?: Partial<PromptInputRequestContext<TExt>>;\n initialDisplay?: Partial<PromptInputDisplayContext>;\n};\n\nexport function createPromptInputRequestController<\n TExt extends Record<string, unknown> = Record<string, unknown>,\n>(\n options: CreatePromptInputRequestControllerOptions<TExt> = {}\n): PromptInputRequestController<TExt> {\n const request = new Store<PromptInputRequestContext<TExt>>({\n ...(DEFAULT_REQUEST as PromptInputRequestContext<TExt>),\n ...(options.initialRequest as Partial<PromptInputRequestContext<TExt>>),\n });\n\n const display = new Store<PromptInputDisplayContext>({\n ...DEFAULT_DISPLAY,\n ...options.initialDisplay,\n });\n\n return {\n request,\n display,\n setRequestField(key, value) {\n request.setState((prev) => ({ ...prev, [key]: value }));\n },\n setDisplayField(key, value) {\n display.setState((prev) => ({ ...prev, [key]: value }));\n },\n addAttachments(files) {\n request.setState((prev) => ({\n ...prev,\n files: [...prev.files, ...files],\n }));\n },\n removeAttachment(id) {\n request.setState((prev) => {\n const removed = prev.files.find((f) => f.id === id);\n if (removed?.url.startsWith(\"blob:\")) URL.revokeObjectURL(removed.url);\n return { ...prev, files: prev.files.filter((f) => f.id !== id) };\n });\n },\n clearAttachments() {\n request.setState((prev) => {\n for (const f of prev.files) {\n if (f.url.startsWith(\"blob:\")) URL.revokeObjectURL(f.url);\n }\n return { ...prev, files: [] };\n });\n },\n resetRequest() {\n request.setState((prev) => {\n for (const f of prev.files) {\n if (f.url.startsWith(\"blob:\")) URL.revokeObjectURL(f.url);\n }\n return { ...prev, text: \"\", files: [] };\n });\n },\n snapshot() {\n return request.state;\n },\n };\n}\n\n// ─── React context + hooks ────────────────────────────────────────────────────\n\n// Typed as `any` in the context because each provider parameterizes `TExt`\n// differently; callers narrow via the hook generic or typed consumer.\nconst PromptInputRequestControllerContext =\n // biome-ignore lint/suspicious/noExplicitAny: controller TExt is provider-parameterized\n createContext<PromptInputRequestController<any> | null>(null);\n\nexport const PromptInputRequestControllerProvider =\n PromptInputRequestControllerContext.Provider;\n\nexport function usePromptInputRequestController<\n TExt extends Record<string, unknown> = Record<string, unknown>,\n>(): PromptInputRequestController<TExt> {\n const ctx = useContext(PromptInputRequestControllerContext);\n if (!ctx) {\n throw new Error(\n \"usePromptInputRequestController must be used inside <PromptInput> or <PromptInputRequestProvider>\"\n );\n }\n return ctx as PromptInputRequestController<TExt>;\n}\n\n/** Optional variant — returns null if no controller is in context. */\nexport function useOptionalPromptInputRequestController<\n TExt extends Record<string, unknown> = Record<string, unknown>,\n>(): PromptInputRequestController<TExt> | null {\n return useContext(\n PromptInputRequestControllerContext\n ) as PromptInputRequestController<TExt> | null;\n}\n\n// ─── Field hooks ──────────────────────────────────────────────────────────────\n\n/**\n * Subscribe to one field of the request context. Component re-renders only\n * when that field changes.\n */\nexport function useRequestField<\n K extends keyof PromptInputRequestContext<TExt>,\n TExt extends Record<string, unknown> = Record<string, unknown>,\n>(key: K): PromptInputRequestContext<TExt>[K] {\n const ctrl = usePromptInputRequestController<TExt>();\n return useStore(ctrl.request, (state) => state[key]);\n}\n\n/** Returns a stable setter for one field of the request context. */\nexport function useSetRequestField<\n K extends keyof PromptInputRequestContext<TExt>,\n TExt extends Record<string, unknown> = Record<string, unknown>,\n>(key: K): (value: PromptInputRequestContext<TExt>[K]) => void {\n const ctrl = usePromptInputRequestController<TExt>();\n return useMemo(\n () => (value: PromptInputRequestContext<TExt>[K]) => {\n ctrl.setRequestField(key, value);\n },\n [ctrl, key]\n );\n}\n\n/**\n * Subscribe to one field of the display context. Component re-renders only\n * when that field changes.\n */\nexport function useDisplayField<K extends keyof PromptInputDisplayContext>(\n key: K\n): PromptInputDisplayContext[K] {\n const ctrl = usePromptInputRequestController();\n return useStore(ctrl.display, (state) => state[key]);\n}\n","\"use client\";\n\nimport { Menu as DropdownMenuPrimitive } from \"@base-ui/react/menu\";\nimport { Select as SelectPrimitive } from \"@base-ui/react/select\";\nimport {\n ArrowUpIcon,\n ArrowsClockwiseIcon,\n CaretDownIcon,\n CheckIcon,\n FileIcon,\n FileTextIcon,\n ImageIcon,\n MicrophoneIcon,\n PaperclipIcon,\n PlusIcon,\n SpinnerGapIcon,\n SquareIcon,\n XIcon,\n} from \"@phosphor-icons/react\";\nimport { useStore } from \"@tanstack/react-store\";\nimport { Store } from \"@tanstack/store\";\nimport type {\n ChangeEvent,\n ChangeEventHandler,\n ClipboardEventHandler,\n ComponentProps,\n FormEvent,\n FormEventHandler,\n HTMLAttributes,\n KeyboardEventHandler,\n PropsWithChildren,\n ReactNode,\n RefObject,\n} from \"react\";\nimport {\n Fragment,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\nimport { InputGroup } from \"../input\";\nimport { Tooltip } from \"../tooltip\";\nimport { useOptionalPromptInputRequestController } from \"./controller\";\nimport type { ModeOption } from \"./types\";\n\n// Stable fallback store for selectors used outside a controller — keeps\n// `useStore` hook order consistent whether or not the controller is mounted.\nconst FALLBACK_REQUEST_STORE = new Store<Record<string, unknown>>({});\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_PROMPT_INPUT_VARIANTS = {\n status: {\n idle: { classes: \"\", description: \"Ready to accept input\" },\n submitted: {\n classes: \"\",\n description: \"Waiting for response (shows spinner)\",\n },\n streaming: { classes: \"\", description: \"Streaming response (shows stop)\" },\n error: { classes: \"\", description: \"Error state (shows X)\" },\n },\n} as const;\n\nexport const SF_AI_PROMPT_INPUT_DEFAULT_VARIANTS = {\n status: \"idle\",\n} as const;\n\nexport type SFAiPromptInputStatus =\n keyof typeof SF_AI_PROMPT_INPUT_VARIANTS.status;\n\n// ─── Attachment types ─────────────────────────────────────────────────────────\n\nexport type AttachmentFile = {\n id: string;\n url: string;\n mediaType: string;\n filename: string;\n};\n\nexport type AttachmentsContext = {\n files: AttachmentFile[];\n add: (files: File[] | FileList) => void;\n remove: (id: string) => void;\n clear: () => void;\n openFileDialog: () => void;\n fileInputRef: RefObject<HTMLInputElement | null>;\n};\n\n// ─── Provider Context & Types ─────────────────────────────────────────────────\n\nexport type TextInputContext = {\n value: string;\n setInput: (v: string) => void;\n clear: () => void;\n};\n\nexport type PromptInputController = {\n textInput: TextInputContext;\n attachments: AttachmentsContext;\n /** INTERNAL: Allows PromptInput to register its file input + \"open\" callback */\n __registerFileInput: (\n ref: RefObject<HTMLInputElement | null>,\n open: () => void\n ) => void;\n};\n\nconst PromptInputContext = createContext<PromptInputController | null>(null);\nconst ProviderAttachmentsContext = createContext<AttachmentsContext | null>(\n null\n);\n\nexport const usePromptInputController = () => {\n const ctx = useContext(PromptInputContext);\n if (!ctx) {\n throw new Error(\n \"Wrap your component inside <PromptInputProvider> to use usePromptInputController().\"\n );\n }\n return ctx;\n};\n\nconst useOptionalPromptInputController = () => useContext(PromptInputContext);\n\nexport const useProviderAttachments = () => {\n const ctx = useContext(ProviderAttachmentsContext);\n if (!ctx) {\n throw new Error(\n \"Wrap your component inside <PromptInputProvider> to use useProviderAttachments().\"\n );\n }\n return ctx;\n};\n\nconst useOptionalProviderAttachments = () =>\n useContext(ProviderAttachmentsContext);\n\n// ─── PromptInputProvider ──────────────────────────────────────────────────────\n\nexport type PromptInputProviderProps = PropsWithChildren<{\n initialInput?: string;\n}>;\n\n/**\n * Optional global provider that lifts PromptInput state outside of PromptInput.\n * If you don't use it, PromptInput stays fully self-managed.\n *\n * @example\n * ```tsx\n * <PromptInputProvider>\n * <PromptInput onSubmit={handleSubmit}>...</PromptInput>\n * </PromptInputProvider>\n * ```\n */\nexport function PromptInputProvider({\n initialInput = \"\",\n children,\n}: PromptInputProviderProps) {\n const [textInput, setTextInput] = useState(initialInput);\n const clearInput = useCallback(() => setTextInput(\"\"), []);\n\n const [attachmentItems, setAttachmentItems] = useState<AttachmentFile[]>([]);\n const fileInputRef = useRef<HTMLInputElement | null>(null);\n const openRef = useRef<() => void>(() => {});\n\n const add = useCallback((files: File[] | FileList) => {\n const incoming = Array.from(files);\n if (incoming.length === 0) return;\n setAttachmentItems((prev) =>\n prev.concat(\n incoming.map((file) => ({\n id: `${Date.now()}-${Math.random()}`,\n url: URL.createObjectURL(file),\n mediaType: file.type,\n filename: file.name,\n }))\n )\n );\n }, []);\n\n const remove = useCallback((id: string) => {\n setAttachmentItems((prev) => {\n const found = prev.find((f) => f.id === id);\n if (found?.url) URL.revokeObjectURL(found.url);\n return prev.filter((f) => f.id !== id);\n });\n }, []);\n\n const clear = useCallback(() => {\n setAttachmentItems((prev) => {\n for (const f of prev) if (f.url) URL.revokeObjectURL(f.url);\n return [];\n });\n }, []);\n\n const openFileDialog = useCallback(() => {\n openRef.current?.();\n }, []);\n\n const attachments = useMemo<AttachmentsContext>(\n () => ({\n files: attachmentItems,\n add,\n remove,\n clear,\n openFileDialog,\n fileInputRef,\n }),\n [attachmentItems, add, remove, clear, openFileDialog]\n );\n\n const __registerFileInput = useCallback(\n (ref: RefObject<HTMLInputElement | null>, open: () => void) => {\n fileInputRef.current = ref.current;\n openRef.current = open;\n },\n []\n );\n\n const controller = useMemo<PromptInputController>(\n () => ({\n textInput: {\n value: textInput,\n setInput: setTextInput,\n clear: clearInput,\n },\n attachments,\n __registerFileInput,\n }),\n [textInput, clearInput, attachments, __registerFileInput]\n );\n\n return (\n <PromptInputContext.Provider value={controller}>\n <ProviderAttachmentsContext.Provider value={attachments}>\n {children}\n </ProviderAttachmentsContext.Provider>\n </PromptInputContext.Provider>\n );\n}\n\n// ─── Local Attachments Context ────────────────────────────────────────────────\n\nconst LocalAttachmentsContext = createContext<AttachmentsContext | null>(null);\n\nexport const usePromptInputAttachments = () => {\n const provider = useOptionalProviderAttachments();\n const local = useContext(LocalAttachmentsContext);\n const context = provider ?? local;\n if (!context) {\n throw new Error(\n \"usePromptInputAttachments must be used within a PromptInput or PromptInputProvider\"\n );\n }\n return context;\n};\n\n// ─── Helpers (module-level) ───────────────────────────────────────────────────\n\n/** Convert a blob URL to a base64 data URL for serialization. */\nasync function convertBlobToDataUrl(url: string): Promise<string> {\n const response = await fetch(url);\n const blob = await response.blob();\n // FileReader is callback-only — no Promise-based API exists for this\n // oxlint-disable-next-line avoid-new\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.addEventListener(\"loadend\", () => resolve(reader.result as string));\n reader.addEventListener(\"error\", () =>\n reject(new Error(\"FileReader error\"))\n );\n reader.readAsDataURL(blob);\n });\n}\n\nfunction makeAttachmentId() {\n return `${Date.now()}-${Math.random()}`;\n}\n\n// ─── PromptInputMessage ───────────────────────────────────────────────────────\n\nexport type PromptInputMessage = {\n text?: string;\n files?: AttachmentFile[];\n};\n\n// ─── PromptInput ─────────────────────────────────────────────────────────────\n\nexport type PromptInputProps = Omit<\n HTMLAttributes<HTMLFormElement>,\n \"onSubmit\" | \"onError\"\n> & {\n /** Accepted file types — e.g. `\"image/*\"`. Default: any. */\n accept?: string;\n /** Allow selecting multiple files. */\n multiple?: boolean;\n /** When true, accept drops anywhere on document. Default false. */\n globalDrop?: boolean;\n /** Maximum number of attached files. */\n maxFiles?: number;\n /** Maximum file size in bytes. */\n maxFileSize?: number;\n onError?: (err: {\n code: \"max_files\" | \"max_file_size\" | \"accept\";\n message: string;\n }) => void;\n onSubmit: (\n message: PromptInputMessage,\n event: FormEvent<HTMLFormElement>\n ) => void | Promise<void>;\n /**\n * Content rendered in the collapsible back layer (e.g. HITL approvals, task\n * lists). When provided the prompt input is wrapped in a LayerCard shell with\n * a header row containing a chevron toggle.\n * Use `PromptInputBackLayer` to compose structured content for this slot.\n */\n backLayer?: ReactNode;\n /**\n * Title shown in the back layer header row. Defaults to `\"Context\"`.\n */\n backLayerTitle?: string;\n /**\n * Controls whether the back layer is visible. Pair with `onBackLayerOpenChange`\n * for a controlled pattern.\n */\n backLayerOpen?: boolean;\n /** Called when the chevron toggle changes the back layer open state. */\n onBackLayerOpenChange?: (open: boolean) => void;\n /**\n * Automatically opens the back layer when this condition is true.\n * Useful for showing pending approvals/questions without manual toggle.\n */\n autoOpenBackLayerWhen?: boolean;\n};\n\n/**\n * Prompt input form. Can be self-managed or controlled via `PromptInputProvider`.\n *\n * @example\n * ```tsx\n * <PromptInput onSubmit={({ text, files }) => send(text, files)}>\n * <PromptInputAttachments>{(f) => <PromptInputAttachment data={f} />}</PromptInputAttachments>\n * <PromptInputTextarea placeholder=\"Ask anything…\" />\n * <PromptInputToolbar>\n * <PromptInputTools>\n * <PromptInputAttachButton />\n * </PromptInputTools>\n * <PromptInputSubmit />\n * </PromptInputToolbar>\n * </PromptInput>\n * ```\n */\nexport const PromptInput = ({\n className,\n accept,\n multiple,\n globalDrop,\n maxFiles,\n maxFileSize,\n onError,\n onSubmit,\n children,\n backLayer,\n backLayerTitle = \"Context\",\n backLayerOpen,\n onBackLayerOpenChange,\n autoOpenBackLayerWhen,\n ...props\n}: PromptInputProps) => {\n const hasBackLayer = backLayer !== undefined;\n const controller = useOptionalPromptInputController();\n const usingProvider = !!controller;\n\n const inputRef = useRef<HTMLInputElement | null>(null);\n const anchorRef = useRef<HTMLSpanElement>(null);\n const formRef = useRef<HTMLFormElement | null>(null);\n\n useEffect(() => {\n const root = anchorRef.current?.closest(\"form\");\n if (root instanceof HTMLFormElement) {\n formRef.current = root;\n }\n }, []);\n\n // Auto-open back layer when condition is true\n useEffect(() => {\n if (autoOpenBackLayerWhen) {\n onBackLayerOpenChange?.(true);\n }\n }, [autoOpenBackLayerWhen, onBackLayerOpenChange]);\n\n // Local attachment state (only when no provider)\n const [items, setItems] = useState<AttachmentFile[]>([]);\n const files = usingProvider ? controller.attachments.files : items;\n\n const openFileDialogLocal = useCallback(() => {\n inputRef.current?.click();\n }, []);\n\n const matchesAccept = useCallback(\n (f: File) => {\n if (!accept || accept.trim() === \"\") return true;\n if (accept.includes(\"image/*\")) return f.type.startsWith(\"image/\");\n return true;\n },\n [accept]\n );\n\n const addLocal = useCallback(\n (fileList: File[] | FileList) => {\n const incoming = Array.from(fileList);\n const accepted = incoming.filter((f) => matchesAccept(f));\n if (incoming.length && accepted.length === 0) {\n onError?.({\n code: \"accept\",\n message: \"No files match the accepted types.\",\n });\n return;\n }\n const withinSize = (f: File) =>\n maxFileSize ? f.size <= maxFileSize : true;\n const sized = accepted.filter(withinSize);\n if (accepted.length > 0 && sized.length === 0) {\n onError?.({\n code: \"max_file_size\",\n message: \"All files exceed the maximum size.\",\n });\n return;\n }\n setItems((prev) => {\n const capacity =\n typeof maxFiles === \"number\"\n ? Math.max(0, maxFiles - prev.length)\n : undefined;\n const capped =\n typeof capacity === \"number\" ? sized.slice(0, capacity) : sized;\n if (typeof capacity === \"number\" && sized.length > capacity) {\n onError?.({\n code: \"max_files\",\n message: \"Too many files. Some were not added.\",\n });\n }\n const next: AttachmentFile[] = capped.map((file) => ({\n id: makeAttachmentId(),\n url: URL.createObjectURL(file),\n mediaType: file.type,\n filename: file.name,\n }));\n return prev.concat(next);\n });\n },\n [matchesAccept, maxFiles, maxFileSize, onError]\n );\n\n const add = usingProvider\n ? (f: File[] | FileList) => controller.attachments.add(f)\n : addLocal;\n\n const remove = usingProvider\n ? (id: string) => controller.attachments.remove(id)\n : (id: string) =>\n setItems((prev) => {\n const found = prev.find((f) => f.id === id);\n if (found?.url) URL.revokeObjectURL(found.url);\n return prev.filter((f) => f.id !== id);\n });\n\n const clear = usingProvider\n ? () => controller.attachments.clear()\n : () =>\n setItems((prev) => {\n for (const f of prev) if (f.url) URL.revokeObjectURL(f.url);\n return [];\n });\n\n const openFileDialog = usingProvider\n ? () => controller.attachments.openFileDialog()\n : openFileDialogLocal;\n\n useEffect(() => {\n if (!usingProvider) return;\n controller.__registerFileInput(inputRef, () => inputRef.current?.click());\n }, [usingProvider, controller]);\n\n // File drag-drop on the form\n useEffect(() => {\n const form = formRef.current;\n if (!form) return;\n const onDragOver = (e: DragEvent) => {\n if (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n };\n const onDrop = (e: DragEvent) => {\n if (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\n add(e.dataTransfer.files);\n }\n };\n form.addEventListener(\"dragover\", onDragOver);\n form.addEventListener(\"drop\", onDrop);\n return () => {\n form.removeEventListener(\"dragover\", onDragOver);\n form.removeEventListener(\"drop\", onDrop);\n };\n }, [add]);\n\n useEffect(() => {\n if (!globalDrop) return;\n const onDragOver = (e: DragEvent) => {\n if (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n };\n const onDrop = (e: DragEvent) => {\n if (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n if (e.dataTransfer?.files && e.dataTransfer.files.length > 0)\n add(e.dataTransfer.files);\n };\n document.addEventListener(\"dragover\", onDragOver);\n document.addEventListener(\"drop\", onDrop);\n return () => {\n document.removeEventListener(\"dragover\", onDragOver);\n document.removeEventListener(\"drop\", onDrop);\n };\n }, [add, globalDrop]);\n\n // Cleanup blob URLs on unmount (local mode only)\n useEffect(() => {\n return () => {\n if (!usingProvider) {\n for (const f of files) if (f.url) URL.revokeObjectURL(f.url);\n }\n };\n }, [usingProvider, files]);\n\n const handleFileInputChange: ChangeEventHandler<HTMLInputElement> = (\n event\n ) => {\n if (event.currentTarget.files) add(event.currentTarget.files);\n };\n\n const ctx = useMemo<AttachmentsContext>(\n () => ({\n files,\n add,\n remove,\n clear,\n openFileDialog,\n fileInputRef: inputRef,\n }),\n [files, add, remove, clear, openFileDialog]\n );\n\n const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {\n event.preventDefault();\n const form = event.currentTarget;\n const text = usingProvider\n ? controller.textInput.value\n : (() => {\n const formData = new FormData(form);\n return (formData.get(\"message\") as string) || \"\";\n })();\n\n if (!usingProvider) form.reset();\n\n const doSubmit = async () => {\n const convertedFiles = await Promise.all(\n files.map(async (item) => {\n if (item.url.startsWith(\"blob:\")) {\n return { ...item, url: await convertBlobToDataUrl(item.url) };\n }\n return item;\n })\n );\n\n try {\n await onSubmit({ text, files: convertedFiles }, event);\n clear();\n if (usingProvider) controller.textInput.clear();\n } catch {\n // Don't clear on error — user may want to retry\n }\n };\n\n doSubmit().catch(() => {});\n };\n\n const isBackLayerOpen = backLayerOpen ?? false;\n\n const inputShell = hasBackLayer ? (\n // LayerCard-style shell: elevated outer card with header + collapsible back\n // layer + primary layer housing the actual input.\n <div className=\"flex w-full flex-col rounded-xl bg-sf-elevated ring ring-sf-line/50\">\n {/* Header row — always visible, contains title + chevron toggle */}\n <button\n aria-expanded={isBackLayerOpen}\n aria-label={isBackLayerOpen ? \"Collapse context\" : \"Expand context\"}\n className=\"flex w-full items-center justify-between gap-2 px-3 py-2 text-left\"\n onClick={() => onBackLayerOpenChange?.(!isBackLayerOpen)}\n type=\"button\"\n >\n <span className=\"text-sm font-medium text-sf-subtle uppercase tracking-wide select-none\">\n {backLayerTitle}\n </span>\n <CaretDownIcon\n className={cn(\n \"size-3.5 text-sf-subtle transition-transform duration-200\",\n isBackLayerOpen && \"rotate-180\"\n )}\n />\n </button>\n {/* Collapsible back layer content */}\n <PromptInputBackLayerPanel open={isBackLayerOpen}>\n {backLayer}\n </PromptInputBackLayerPanel>\n {/* Primary layer — the input itself, visually raised off the back layer */}\n <div className=\"flex flex-col overflow-hidden rounded-xl bg-sf-base ring ring-sf-line/50 focus-within:ring-sf-ring\">\n {children}\n </div>\n </div>\n ) : (\n <InputGroup>{children}</InputGroup>\n );\n\n const inner = (\n <>\n <span aria-hidden=\"true\" className=\"hidden\" ref={anchorRef} />\n <input\n accept={accept}\n aria-label=\"Upload files\"\n className=\"hidden\"\n multiple={multiple}\n onChange={handleFileInputChange}\n ref={inputRef}\n type=\"file\"\n />\n <form\n className={cn(\"w-full\", className)}\n onSubmit={handleSubmit}\n {...props}\n >\n {inputShell}\n </form>\n </>\n );\n\n return usingProvider ? (\n inner\n ) : (\n <LocalAttachmentsContext.Provider value={ctx}>\n {inner}\n </LocalAttachmentsContext.Provider>\n );\n};\n\n// ─── PromptInputBody ──────────────────────────────────────────────────────────\n\nexport type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputBody = ({\n className,\n ...props\n}: PromptInputBodyProps) => (\n <div className={cn(\"contents\", className)} {...props} />\n);\n\n// ─── PromptInputBackLayerPanel (internal) ─────────────────────────────────────\n// Grid-rows animation: collapses to 0fr when closed, expands to 1fr when open.\n\ntype PromptInputBackLayerPanelProps = PropsWithChildren<{\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}>;\n\nfunction PromptInputBackLayerPanel({\n open,\n children,\n}: PromptInputBackLayerPanelProps) {\n const isOpen = open ?? false;\n\n return (\n <div\n aria-hidden={!isOpen}\n className=\"grid\"\n style={{\n gridTemplateRows: isOpen ? \"1fr\" : \"0fr\",\n opacity: isOpen ? 1 : 0,\n transition: \"grid-template-rows 200ms ease-out, opacity 200ms ease-out\",\n }}\n >\n {/* min-h-0 lets the grid row collapse to 0 */}\n <div className=\"min-h-0 overflow-hidden\">{children}</div>\n </div>\n );\n}\n\n// ─── PromptInputBackLayer (public) ────────────────────────────────────────────\n\nexport type PromptInputBackLayerProps = PropsWithChildren<{\n className?: string;\n}>;\n\n/**\n * Container for back-layer content (HITL approvals, task lists, agent status,\n * etc.). Pass instances of this as the `backLayer` prop on `PromptInput`.\n *\n * @example\n * ```tsx\n * <PromptInput\n * onSubmit={handleSubmit}\n * backLayer={<PromptInputBackLayer>...</PromptInputBackLayer>}\n * backLayerOpen={open}\n * onBackLayerOpenChange={setOpen}\n * >\n * <PromptInputTextarea />\n * <PromptInputToolbar>\n * <PromptInputTools />\n * <PromptInputSubmit />\n * </PromptInputToolbar>\n * </PromptInput>\n * ```\n */\nexport function PromptInputBackLayer({\n children,\n className,\n}: PromptInputBackLayerProps) {\n return (\n <div\n className={cn(\n \"flex flex-col gap-2 px-3 py-2.5 text-sm text-sf-default\",\n className\n )}\n >\n {children}\n </div>\n );\n}\n\n// ─── Textarea helpers (module-level) ──────────────────────────────────────────\n\nconst handleTextareaKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = (\n e\n) => {\n if (e.key === \"Enter\") {\n if (e.nativeEvent.isComposing) return;\n if (e.shiftKey) return;\n e.preventDefault();\n e.currentTarget.form?.requestSubmit();\n }\n};\n\n// ─── PromptInputTextarea ──────────────────────────────────────────────────────\n\nexport type PromptInputTextareaProps = ComponentProps<\"textarea\"> & {\n placeholder?: string;\n};\n\n/**\n * Auto-resizing textarea for the prompt. Submits on Enter (Shift+Enter for newline).\n * Pastes files into attachments.\n */\nexport const PromptInputTextarea = ({\n onChange,\n className,\n placeholder = \"What would you like to know?\",\n ...props\n}: PromptInputTextareaProps) => {\n const controller = useOptionalPromptInputController();\n const attachments = usePromptInputAttachments();\n\n const handlePaste: ClipboardEventHandler<HTMLTextAreaElement> = (event) => {\n const items = event.clipboardData?.items;\n if (!items) return;\n const pastedFiles: File[] = [];\n for (const item of items) {\n if (item.kind === \"file\") {\n const file = item.getAsFile();\n if (file) pastedFiles.push(file);\n }\n }\n if (pastedFiles.length > 0) {\n event.preventDefault();\n attachments.add(pastedFiles);\n }\n };\n\n const controlledProps = controller\n ? {\n value: controller.textInput.value,\n onChange: (e: ChangeEvent<HTMLTextAreaElement>) => {\n controller.textInput.setInput(e.currentTarget.value);\n onChange?.(e);\n },\n }\n : { onChange };\n\n return (\n <textarea\n className={cn(\n \"field-sizing-content max-h-48 min-h-16 w-full resize-none border-0 bg-transparent px-3 py-2 text-sm text-sf-default outline-none placeholder:text-sf-inactive\",\n className\n )}\n name=\"message\"\n onKeyDown={handleTextareaKeyDown}\n onPaste={handlePaste}\n placeholder={placeholder}\n rows={1}\n {...props}\n {...controlledProps}\n />\n );\n};\n\n// ─── PromptInputToolbar ───────────────────────────────────────────────────────\n\nexport type PromptInputToolbarProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputToolbar = ({\n className,\n ...props\n}: PromptInputToolbarProps) => (\n <div\n className={cn(\n \"flex min-w-0 items-center justify-between gap-1 px-2 py-1.5\",\n className\n )}\n {...props}\n />\n);\n\n// ─── PromptInputTools ─────────────────────────────────────────────────────────\n\nexport type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTools = ({\n className,\n ...props\n}: PromptInputToolsProps) => (\n <div\n className={cn(\"flex min-w-0 items-center gap-1\", className)}\n {...props}\n />\n);\n\n// ─── PromptInputButton ────────────────────────────────────────────────────────\n\nexport type PromptInputButtonProps = ComponentProps<typeof Button> & {\n \"aria-label\": string;\n};\n\nexport const PromptInputButton = ({\n variant = \"ghost\",\n size = \"sm\",\n className,\n ...props\n}: PromptInputButtonProps) => (\n <Button\n className={cn(\"text-sf-subtle hover:text-sf-default\", className)}\n size={size}\n type=\"button\"\n variant={variant}\n {...props}\n />\n);\n\n// ─── PromptInputSubmit ────────────────────────────────────────────────────────\n\nexport type PromptInputSubmitProps = ComponentProps<typeof Button> & {\n status?: SFAiPromptInputStatus;\n};\n\n/**\n * Submit button. Icon changes based on `status`:\n * - `idle` → send arrow\n * - `submitted` → spinner\n * - `streaming` → stop square\n * - `error` → X\n */\nexport const PromptInputSubmit = ({\n className,\n variant = \"primary\",\n size = \"sm\",\n status = \"idle\",\n children,\n ...props\n}: PromptInputSubmitProps) => {\n let Icon: ReactNode;\n if (status === \"submitted\") {\n Icon = <SpinnerGapIcon className=\"size-4 animate-spin\" />;\n } else if (status === \"streaming\") {\n Icon = <SquareIcon className=\"size-4\" />;\n } else if (status === \"error\") {\n Icon = <XIcon className=\"size-4\" />;\n } else {\n Icon = <ArrowUpIcon className=\"size-4\" />;\n }\n\n return (\n <Button\n aria-label=\"Submit\"\n className={cn(className)}\n size={size}\n type=\"submit\"\n variant={variant}\n {...props}\n >\n {children ?? Icon}\n </Button>\n );\n};\n\n// ─── PromptInputActionMenu ────────────────────────────────────────────────────\n\nconst DropdownMenu = DropdownMenuPrimitive;\n\nexport type PromptInputActionMenuProps = ComponentProps<\n typeof DropdownMenu.Root\n>;\n\nexport const PromptInputActionMenu = (props: PromptInputActionMenuProps) => (\n <DropdownMenu.Root {...props} />\n);\n\nexport type PromptInputActionMenuTriggerProps = Omit<\n PromptInputButtonProps,\n \"aria-label\"\n> & {\n \"aria-label\"?: string;\n};\n\nexport const PromptInputActionMenuTrigger = ({\n className,\n children,\n \"aria-label\": ariaLabel = \"More actions\",\n ...props\n}: PromptInputActionMenuTriggerProps) => (\n <DropdownMenu.Trigger\n render={\n <Button\n aria-label={ariaLabel}\n className={cn(\"text-sf-subtle hover:text-sf-default\", className)}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n {...props}\n >\n {children ?? <PlusIcon className=\"size-4\" />}\n </Button>\n }\n />\n);\n\nexport type PromptInputActionMenuContentProps = ComponentProps<\n typeof DropdownMenu.Positioner\n> & { className?: string; children?: ReactNode };\n\nexport const PromptInputActionMenuContent = ({\n className,\n children,\n ...props\n}: PromptInputActionMenuContentProps) => (\n <DropdownMenu.Positioner className=\"z-50\" side=\"top\" align=\"start\" {...props}>\n <DropdownMenu.Popup\n className={cn(\n \"min-w-[160px] rounded-lg bg-sf-elevated p-1 shadow-md\",\n \"data-[starting-style]:opacity-0 data-[ending-style]:opacity-0 transition-opacity duration-100\",\n className\n )}\n >\n {children}\n </DropdownMenu.Popup>\n </DropdownMenu.Positioner>\n);\n\nexport type PromptInputActionMenuItemProps = ComponentProps<\n typeof DropdownMenu.Item\n>;\n\nexport const PromptInputActionMenuItem = ({\n className,\n ...props\n}: PromptInputActionMenuItemProps) => (\n <DropdownMenu.Item\n className={cn(\n \"flex cursor-pointer select-none items-center gap-2 rounded-md px-2 py-1.5 text-sm text-sf-default outline-none\",\n \"data-highlighted:bg-sf-tint\",\n className\n )}\n {...props}\n />\n);\n\n// ─── PromptInputActionAddAttachments ─────────────────────────────────────────\n\nexport type PromptInputActionAddAttachmentsProps =\n PromptInputActionMenuItemProps & {\n label?: string;\n };\n\nexport const PromptInputActionAddAttachments = ({\n label = \"Add photos or files\",\n ...props\n}: PromptInputActionAddAttachmentsProps) => {\n const attachments = usePromptInputAttachments();\n const handleSelect = useCallback(() => {\n attachments.openFileDialog();\n }, [attachments]);\n return (\n <PromptInputActionMenuItem {...props} onSelect={handleSelect}>\n <ImageIcon className=\"size-4\" />\n {label}\n </PromptInputActionMenuItem>\n );\n};\n\n// ─── PromptInputModeSelector ───────────────────────────────────────────────────\n\n/**\n * Pill-style mode selector.\n *\n * Two wiring modes:\n * 1. **Controller-driven (preferred)** — omit `value`/`onChange`; the selector\n * reads/writes `requestContext.mode` via `usePromptInputController()`.\n * 2. **Explicit props** — pass `value` + `onChange` for controlled usage\n * outside a controller (e.g. standalone demos or legacy harnesses).\n *\n * Generic over `TValue` so consumers with a known id union get narrow types.\n *\n * @example Controller-driven\n * ```tsx\n * <PromptInput onSubmit={(ctx) => send(ctx)}>\n * <PromptInputTextarea />\n * <PromptInputToolbar>\n * <PromptInputModeSelector<'build' | 'plan' | 'research'>\n * options={[\n * { value: 'build', label: 'Build' },\n * { value: 'plan', label: 'Plan' },\n * { value: 'research', label: 'Research' },\n * ]}\n * />\n * <PromptInputSubmit />\n * </PromptInputToolbar>\n * </PromptInput>\n * ```\n *\n * @example Explicit\n * ```tsx\n * <PromptInputModeSelector options={opts} value={mode} onChange={setMode} />\n * ```\n */\nexport type PromptInputModeSelectorProps<TValue extends string = string> = {\n /** Available options. Pass an empty array to hide the selector. */\n options: ModeOption<TValue>[];\n /** Controlled value. Omit to bind to `requestContext.mode` via controller. */\n value?: TValue;\n /** Controlled handler. Omit to write `requestContext.mode` via controller. */\n onChange?: (value: TValue) => void;\n /** Optional aria-label override. */\n \"aria-label\"?: string;\n className?: string;\n};\n\nexport const PromptInputModeSelector = <TValue extends string = string>({\n options,\n value: valueProp,\n onChange: onChangeProp,\n \"aria-label\": ariaLabel = \"Select mode\",\n className,\n}: PromptInputModeSelectorProps<TValue>) => {\n const controller = useOptionalPromptInputRequestController();\n\n // Always call the store hook; default to empty store to keep hook order stable.\n const ctxMode = useStore(\n controller?.request ?? FALLBACK_REQUEST_STORE,\n (s) => (s as { mode?: string }).mode\n ) as TValue | undefined;\n\n if (options.length === 0) return null;\n\n const isControlled = valueProp !== undefined || onChangeProp !== undefined;\n const value = isControlled ? valueProp : ctxMode;\n\n const handleSelect = (next: TValue) => {\n if (onChangeProp) {\n onChangeProp(next);\n return;\n }\n if (!isControlled && controller) {\n controller.setRequestField(\"mode\", next);\n }\n };\n\n return (\n <div\n className={cn(\n \"flex items-center gap-0.5 rounded-md bg-sf-tint p-0.5\",\n className\n )}\n role=\"group\"\n aria-label={ariaLabel}\n >\n {options.map((opt) => {\n const isSelected = opt.value === value;\n return (\n <button\n key={opt.value}\n aria-pressed={isSelected}\n disabled={opt.disabled}\n className={cn(\n \"flex cursor-pointer items-center gap-1 rounded-[4px] px-2 py-0.5 text-[11px] font-medium transition-colors\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n isSelected\n ? \"bg-sf-elevated text-sf-default shadow-sm\"\n : \"text-sf-subtle hover:text-sf-default\"\n )}\n onClick={() => handleSelect(opt.value)}\n title={opt.description}\n type=\"button\"\n >\n {opt.icon}\n {opt.label}\n </button>\n );\n })}\n </div>\n );\n};\n\n// ─── PromptInputCompactSelect (shared primitive) ─────────────────────────────\n\nexport type CompactSelectOption = {\n /** Stable value identifier. */\n value: string;\n /** Display label. Falls back to `value` when omitted. */\n label?: string;\n /** Disable selection. */\n disabled?: boolean;\n};\n\nexport type PromptInputCompactSelectProps = {\n /** Available options. Pass an empty array to render nothing. */\n options: CompactSelectOption[];\n /** Controlled value. */\n value?: string;\n /** Fires when the user picks a new option. */\n onChange?: (value: string) => void;\n /** Trigger text when `value` is empty or doesn't match any option. */\n placeholder?: string;\n /** aria-label for the trigger. */\n \"aria-label\"?: string;\n /** Max width of the trigger label (with truncation). Default `160px`. */\n maxWidth?: number | string;\n className?: string;\n};\n\n/**\n * Compact Select primitive sized for the PromptInput toolbar.\n *\n * - 24px-tall trigger with `text-[11px]` label and truncating value\n * - Matching compact popup with the same text scale\n *\n * Build toolbar-specific selectors (mode, model, voice, tone, ...) by\n * wrapping this and wiring `value`/`onChange` to your state source.\n */\nexport const PromptInputCompactSelect = ({\n options,\n value,\n onChange,\n placeholder = \"Select\",\n \"aria-label\": ariaLabel = \"Select\",\n maxWidth = 160,\n className,\n}: PromptInputCompactSelectProps) => {\n if (options.length === 0) return null;\n\n const selected = options.find((o) => o.value === value);\n const triggerMaxWidth =\n typeof maxWidth === \"number\" ? `${maxWidth}px` : maxWidth;\n\n return (\n <SelectPrimitive.Root\n value={value ?? \"\"}\n onValueChange={(v) => {\n if (typeof v === \"string\") onChange?.(v);\n }}\n >\n <SelectPrimitive.Trigger\n aria-label={ariaLabel}\n style={{ maxWidth: triggerMaxWidth }}\n className={cn(\n \"flex h-6 min-w-0 shrink cursor-pointer items-center gap-1 rounded-[4px] px-2 text-[11px] leading-none font-medium text-sf-subtle transition-colors\",\n \"hover:text-sf-default focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-sf-ring\",\n className\n )}\n >\n <SelectPrimitive.Value className=\"min-w-0 truncate\">\n {selected ? (selected.label ?? selected.value) : placeholder}\n </SelectPrimitive.Value>\n <SelectPrimitive.Icon className=\"flex shrink-0 items-center\">\n <CaretDownIcon className=\"size-3\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n <SelectPrimitive.Portal>\n <SelectPrimitive.Positioner sideOffset={6}>\n <SelectPrimitive.Popup\n className={cn(\n \"overflow-hidden rounded-lg bg-sf-control p-1 text-[11px] text-sf-default shadow-lg ring ring-sf-line\",\n \"min-w-[calc(var(--anchor-width)+3px)]\"\n )}\n >\n {options.map((opt) => (\n <SelectPrimitive.Item\n key={opt.value}\n value={opt.value}\n disabled={opt.disabled}\n className={cn(\n \"flex cursor-pointer items-center justify-between gap-2 rounded px-2 py-1 text-[11px] leading-tight outline-none\",\n \"data-[highlighted]:bg-sf-tint data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50\"\n )}\n >\n <SelectPrimitive.ItemText>\n {opt.label ?? opt.value}\n </SelectPrimitive.ItemText>\n <SelectPrimitive.ItemIndicator>\n <CheckIcon className=\"size-3\" />\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n ))}\n </SelectPrimitive.Popup>\n </SelectPrimitive.Positioner>\n </SelectPrimitive.Portal>\n </SelectPrimitive.Root>\n );\n};\n\n// ─── PromptInputModelSelect ──────────────────────────────────────────────────\n\nexport type ModelOption = {\n /** Stable model identifier (e.g. `\"anthropic/claude-sonnet-4\"`). */\n value: string;\n /** Display label. Falls back to the last path segment of `value`. */\n label?: string;\n /** Optional group/provider label. */\n group?: string;\n /** Disable selection. */\n disabled?: boolean;\n};\n\nexport type PromptInputModelSelectProps = {\n /** Available model options. Pass an empty array to hide the selector. */\n options: ModelOption[];\n /** Controlled value. Omit to bind to `requestContext.model` via controller. */\n value?: string;\n /** Controlled handler. Omit to write `requestContext.model` via controller. */\n onChange?: (value: string) => void;\n /** Optional aria-label override. */\n \"aria-label\"?: string;\n className?: string;\n};\n\nfunction getModelLabel(option: ModelOption): string {\n if (option.label) return option.label;\n return option.value.split(\"/\").pop() ?? option.value;\n}\n\n/**\n * Compact model selector for the PromptInput toolbar.\n *\n * - Controlled: pass `value` + `onChange`.\n * - Controller-bound: omit both and wrap in `PromptInputRequestControllerProvider`\n * — the selector reads/writes `requestContext.model` automatically.\n */\nexport const PromptInputModelSelect = ({\n options,\n value: valueProp,\n onChange: onChangeProp,\n \"aria-label\": ariaLabel = \"Select model\",\n className,\n}: PromptInputModelSelectProps) => {\n const controller = useOptionalPromptInputRequestController();\n\n const ctxModel = useStore(\n controller?.request ?? FALLBACK_REQUEST_STORE,\n (s) => (s as { model?: string }).model\n );\n\n const isControlled = valueProp !== undefined || onChangeProp !== undefined;\n const value = isControlled ? valueProp : ctxModel;\n\n const handleChange = (next: string) => {\n if (onChangeProp) {\n onChangeProp(next);\n return;\n }\n if (!isControlled && controller) {\n controller.setRequestField(\"model\", next);\n }\n };\n\n const normalized = options.map((o) => ({\n value: o.value,\n label: getModelLabel(o),\n disabled: o.disabled,\n }));\n\n return (\n <PromptInputCompactSelect\n options={normalized}\n value={value}\n onChange={handleChange}\n placeholder=\"Select model\"\n aria-label={ariaLabel}\n className={className}\n />\n );\n};\n\n// ─── PromptInputModeSelect ───────────────────────────────────────────────────\n\nexport type PromptInputModeSelectProps = {\n /** Available modes. */\n options: ModeOption[];\n /** Controlled value. Omit to bind to `requestContext.mode` via controller. */\n value?: string;\n /** Controlled handler. Omit to write `requestContext.mode` via controller. */\n onChange?: (value: string) => void;\n /** Optional aria-label override. */\n \"aria-label\"?: string;\n className?: string;\n};\n\n/**\n * Compact mode selector for the PromptInput toolbar — matches\n * `PromptInputModelSelect` visually. For a segmented button-group look\n * (multi-mode headers, empty states), use `PromptInputModeSelector` instead.\n *\n * - Controlled: pass `value` + `onChange`.\n * - Controller-bound: omit both and wrap in `PromptInputRequestControllerProvider`\n * — the selector reads/writes `requestContext.mode` automatically.\n */\nexport const PromptInputModeSelect = ({\n options,\n value: valueProp,\n onChange: onChangeProp,\n \"aria-label\": ariaLabel = \"Select mode\",\n className,\n}: PromptInputModeSelectProps) => {\n const controller = useOptionalPromptInputRequestController();\n\n const ctxMode = useStore(\n controller?.request ?? FALLBACK_REQUEST_STORE,\n (s) => (s as { mode?: string }).mode\n );\n\n const isControlled = valueProp !== undefined || onChangeProp !== undefined;\n const value = isControlled ? valueProp : ctxMode;\n\n const handleChange = (next: string) => {\n if (onChangeProp) {\n onChangeProp(next);\n return;\n }\n if (!isControlled && controller) {\n controller.setRequestField(\"mode\", next);\n }\n };\n\n const normalized = options.map((o) => ({\n value: o.value,\n label: o.label ?? o.value,\n disabled: o.disabled,\n }));\n\n return (\n <PromptInputCompactSelect\n options={normalized}\n value={value}\n onChange={handleChange}\n placeholder=\"Select mode\"\n aria-label={ariaLabel}\n className={className}\n />\n );\n};\n\n// ─── PromptInputCompactCycle (shared primitive) ──────────────────────────────\n\nexport type PromptInputCompactCycleProps = {\n /** Available options. Cycles through them in order, wrapping at end. */\n options: CompactSelectOption[];\n /** Current value. If unset (or no match), the first option is highlighted. */\n value?: string;\n /** Fires when the user clicks (advances to next non-disabled option). */\n onChange?: (value: string) => void;\n /** aria-label for the button. */\n \"aria-label\"?: string;\n /** Show the small cycle icon next to the label. @default true */\n showIcon?: boolean;\n /** Max width of the label (truncates). @default 160px */\n maxWidth?: number | string;\n className?: string;\n};\n\n/**\n * Single compact button that cycles through `options` on click.\n *\n * - Click / Enter / Space → next option (wraps at end)\n * - Shift+Click / Shift+Enter → previous option\n * - Skips `disabled` options\n *\n * Useful for binary or 2–3 option toggles where a dropdown feels heavy.\n */\nexport const PromptInputCompactCycle = ({\n options,\n value,\n onChange,\n \"aria-label\": ariaLabel = \"Cycle option\",\n showIcon = true,\n maxWidth = 160,\n className,\n}: PromptInputCompactCycleProps) => {\n const enabledOptions = options.filter((o) => !o.disabled);\n if (enabledOptions.length === 0) return null;\n\n const currentIndex = options.findIndex((o) => o.value === value);\n const current =\n currentIndex !== -1 ? options[currentIndex] : enabledOptions[0];\n\n const advance = (direction: 1 | -1) => {\n // Find the next/prev non-disabled option, wrapping around.\n const start = currentIndex !== -1 ? currentIndex : -direction;\n const len = options.length;\n for (let step = 1; step <= len; step++) {\n const i = (start + direction * step + len * len) % len;\n const candidate = options[i];\n if (candidate && !candidate.disabled) {\n onChange?.(candidate.value);\n return;\n }\n }\n };\n\n const triggerMaxWidth =\n typeof maxWidth === \"number\" ? `${maxWidth}px` : maxWidth;\n\n return (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n title={`${current?.label ?? current?.value ?? \"\"} — click to cycle`}\n style={{ maxWidth: triggerMaxWidth }}\n onClick={(e) => advance(e.shiftKey ? -1 : 1)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n advance(e.shiftKey ? -1 : 1);\n }\n }}\n className={cn(\n \"flex h-6 min-w-0 shrink cursor-pointer items-center gap-1 rounded-[4px] px-2 text-[11px] leading-none font-medium text-sf-subtle transition-colors\",\n \"hover:text-sf-default focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-sf-ring\",\n className\n )}\n >\n <span className=\"min-w-0 truncate\">\n {current?.label ?? current?.value ?? \"\"}\n </span>\n {showIcon && (\n <ArrowsClockwiseIcon className=\"size-3 shrink-0 opacity-70\" />\n )}\n </button>\n );\n};\n\n// ─── PromptInputModeCycle ────────────────────────────────────────────────────\n\nexport type PromptInputModeCycleProps = {\n /** Available modes to cycle through. */\n options: ModeOption[];\n /** Controlled value. Omit to bind to `requestContext.mode` via controller. */\n value?: string;\n /** Controlled handler. Omit to write `requestContext.mode` via controller. */\n onChange?: (value: string) => void;\n /** Optional aria-label override. */\n \"aria-label\"?: string;\n /** Show the small cycle icon next to the label. @default true */\n showIcon?: boolean;\n className?: string;\n};\n\n/**\n * Click-to-cycle mode picker. Single compact button that advances through\n * `options` on each click. Best for 2–3 modes.\n *\n * - Controlled: pass `value` + `onChange`.\n * - Controller-bound: omit both and wrap in `PromptInputRequestControllerProvider`\n * — reads/writes `requestContext.mode` automatically.\n */\nexport const PromptInputModeCycle = ({\n options,\n value: valueProp,\n onChange: onChangeProp,\n \"aria-label\": ariaLabel = \"Cycle mode\",\n showIcon = true,\n className,\n}: PromptInputModeCycleProps) => {\n const controller = useOptionalPromptInputRequestController();\n\n const ctxMode = useStore(\n controller?.request ?? FALLBACK_REQUEST_STORE,\n (s) => (s as { mode?: string }).mode\n );\n\n const isControlled = valueProp !== undefined || onChangeProp !== undefined;\n const value = isControlled ? valueProp : ctxMode;\n\n const handleChange = (next: string) => {\n if (onChangeProp) {\n onChangeProp(next);\n return;\n }\n if (!isControlled && controller) {\n controller.setRequestField(\"mode\", next);\n }\n };\n\n const normalized = options.map((o) => ({\n value: o.value,\n label: o.label ?? o.value,\n disabled: o.disabled,\n }));\n\n return (\n <PromptInputCompactCycle\n options={normalized}\n value={value}\n onChange={handleChange}\n aria-label={ariaLabel}\n showIcon={showIcon}\n className={className}\n />\n );\n};\n\n// ─── PromptInputModelCycle ───────────────────────────────────────────────────\n\nexport type PromptInputModelCycleProps = {\n /** Available models to cycle through. */\n options: ModelOption[];\n /** Controlled value. Omit to bind to `requestContext.model` via controller. */\n value?: string;\n /** Controlled handler. Omit to write `requestContext.model` via controller. */\n onChange?: (value: string) => void;\n /** Optional aria-label override. */\n \"aria-label\"?: string;\n /** Show the small cycle icon next to the label. @default true */\n showIcon?: boolean;\n className?: string;\n};\n\n/**\n * Click-to-cycle model picker. Single compact button that advances through\n * `options` on each click. Best for 2–3 models.\n *\n * - Controlled: pass `value` + `onChange`.\n * - Controller-bound: omit both and wrap in `PromptInputRequestControllerProvider`\n * — reads/writes `requestContext.model` automatically.\n */\nexport const PromptInputModelCycle = ({\n options,\n value: valueProp,\n onChange: onChangeProp,\n \"aria-label\": ariaLabel = \"Cycle model\",\n showIcon = true,\n className,\n}: PromptInputModelCycleProps) => {\n const controller = useOptionalPromptInputRequestController();\n\n const ctxModel = useStore(\n controller?.request ?? FALLBACK_REQUEST_STORE,\n (s) => (s as { model?: string }).model\n );\n\n const isControlled = valueProp !== undefined || onChangeProp !== undefined;\n const value = isControlled ? valueProp : ctxModel;\n\n const handleChange = (next: string) => {\n if (onChangeProp) {\n onChangeProp(next);\n return;\n }\n if (!isControlled && controller) {\n controller.setRequestField(\"model\", next);\n }\n };\n\n const normalized = options.map((o) => ({\n value: o.value,\n label: getModelLabel(o),\n disabled: o.disabled,\n }));\n\n return (\n <PromptInputCompactCycle\n options={normalized}\n value={value}\n onChange={handleChange}\n aria-label={ariaLabel}\n showIcon={showIcon}\n className={className}\n />\n );\n};\n\n// ─── Attachment display helpers ───────────────────────────────────────────────\n\nconst IMAGE_EXTS = new Set([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\"]);\n\nfunction getFileIcon(filename: string): ReactNode {\n const ext = filename.split(\".\").pop()?.toLowerCase() ?? \"\";\n if (IMAGE_EXTS.has(ext)) return <ImageIcon className=\"size-3.5\" />;\n if ([\"pdf\", \"doc\", \"docx\", \"txt\", \"md\"].includes(ext))\n return <FileTextIcon className=\"size-3.5\" />;\n return <FileIcon className=\"size-3.5\" />;\n}\n\n// ─── PromptInputAttachment ────────────────────────────────────────────────────\n\nexport type PromptInputAttachmentProps = HTMLAttributes<HTMLDivElement> & {\n data: AttachmentFile;\n};\n\n/**\n * Single attachment chip for images or files. Includes a remove button.\n */\nexport function PromptInputAttachment({\n data,\n className,\n ...props\n}: PromptInputAttachmentProps) {\n const attachments = usePromptInputAttachments();\n const isImage = data.mediaType?.startsWith(\"image/\") && data.url;\n\n const handleRemove = useCallback(() => {\n attachments.remove(data.id);\n }, [attachments, data.id]);\n\n if (isImage) {\n return (\n <div\n className={cn(\n \"group relative size-14 overflow-hidden rounded-md\",\n className\n )}\n {...props}\n >\n <img\n alt={data.filename || \"attachment\"}\n className=\"size-full object-cover\"\n src={data.url}\n />\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={handleRemove}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n <XIcon className=\"size-3\" />\n </Button>\n </div>\n );\n }\n\n const ext = data.filename.split(\".\").pop()?.toUpperCase() ?? \"FILE\";\n const name = data.filename.replace(/\\.[^/.]+$/, \"\");\n\n return (\n <div\n className={cn(\n \"group relative inline-flex h-8 items-center gap-2 rounded-md bg-sf-tint/50 px-2 text-sm\",\n className\n )}\n {...props}\n >\n <div className=\"flex size-5 shrink-0 items-center justify-center text-sf-subtle\">\n {getFileIcon(data.filename)}\n </div>\n <Tooltip content={data.filename}>\n <span className=\"max-w-[160px] truncate font-medium text-sf-default\">\n {name || \"File\"}\n </span>\n </Tooltip>\n <span className=\"rounded bg-sf-tint px-1 py-0.5 font-semibold text-sf-subtle text-[10px] uppercase\">\n {ext}\n </span>\n <Button\n aria-label=\"Remove attachment\"\n className=\"ml-0.5 size-4 shrink-0 rounded-full p-0 opacity-0 transition-opacity group-hover:opacity-100\"\n onClick={handleRemove}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n <XIcon className=\"size-3\" />\n </Button>\n </div>\n );\n}\n\n// ─── PromptInputAttachments ───────────────────────────────────────────────────\n\nexport type PromptInputAttachmentsProps = Omit<\n HTMLAttributes<HTMLDivElement>,\n \"children\"\n> & {\n children: (attachment: AttachmentFile) => ReactNode;\n};\n\n/**\n * Renders all attachments using a render prop. Hidden when no files.\n * Animates height as files are added/removed.\n */\nexport function PromptInputAttachments({\n className,\n children,\n ...props\n}: PromptInputAttachmentsProps) {\n const attachments = usePromptInputAttachments();\n const [height, setHeight] = useState(0);\n const contentRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const el = contentRef.current;\n if (!el) return;\n const ro = new ResizeObserver(() => {\n setHeight(el.getBoundingClientRect().height);\n });\n ro.observe(el);\n setHeight(el.getBoundingClientRect().height);\n return () => ro.disconnect();\n }, []);\n\n // Re-measure when file count changes\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional re-measure on count change\n useLayoutEffect(() => {\n const el = contentRef.current;\n if (el) setHeight(el.getBoundingClientRect().height);\n }, [attachments.files.length]);\n\n if (attachments.files.length === 0) return null;\n\n return (\n <div\n aria-live=\"polite\"\n className={cn(\n \"overflow-hidden px-2 transition-[height] duration-200 ease-out\",\n className\n )}\n style={{ height: attachments.files.length ? height : 0 }}\n {...props}\n >\n <div className=\"flex flex-wrap gap-2 py-1\" ref={contentRef}>\n {attachments.files.map((file) => (\n <Fragment key={file.id}>{children(file)}</Fragment>\n ))}\n </div>\n </div>\n );\n}\n\n// ─── Speech recognition types ─────────────────────────────────────────────────\n\ntype SpeechRecognitionInstance = EventTarget & {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n start(): void;\n stop(): void;\n};\n\ntype SpeechRecognitionResultEvent = Event & {\n results: {\n readonly length: number;\n item(index: number): {\n isFinal: boolean;\n [i: number]: { transcript: string };\n };\n [index: number]: { isFinal: boolean; [i: number]: { transcript: string } };\n };\n};\n\ntype SpeechRecognitionConstructor = new () => SpeechRecognitionInstance;\n\ndeclare global {\n interface Window {\n SpeechRecognition?: SpeechRecognitionConstructor;\n webkitSpeechRecognition?: SpeechRecognitionConstructor;\n }\n}\n\n// ─── PromptInputSpeechButton ──────────────────────────────────────────────────\n\nexport type PromptInputSpeechButtonProps = Omit<\n PromptInputButtonProps,\n \"aria-label\"\n> & {\n \"aria-label\"?: string;\n textareaRef?: RefObject<HTMLTextAreaElement | null>;\n onTranscriptionChange?: (text: string) => void;\n};\n\n/**\n * Voice input button. Uses the Web Speech API (Chrome/Edge). Hidden on unsupported browsers.\n * Pulses while listening.\n */\nexport const PromptInputSpeechButton = ({\n className,\n textareaRef,\n onTranscriptionChange,\n \"aria-label\": ariaLabel = \"Voice input\",\n ...props\n}: PromptInputSpeechButtonProps) => {\n const [isListening, setIsListening] = useState(false);\n const [recognition, setRecognition] =\n useState<SpeechRecognitionInstance | null>(null);\n const recognitionRef = useRef<SpeechRecognitionInstance | null>(null);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const SR = window.SpeechRecognition ?? window.webkitSpeechRecognition;\n if (!SR) return;\n\n const sr = new SR() as SpeechRecognitionInstance & {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n };\n sr.continuous = true;\n sr.interimResults = true;\n sr.lang = \"en-US\";\n\n const handleStart = () => setIsListening(true);\n const handleEnd = () => setIsListening(false);\n const handleResult = (event: Event) => {\n const e = event as SpeechRecognitionResultEvent;\n let final = \"\";\n for (const result of Array.from(\n { length: e.results.length },\n (_, i) => e.results[i]\n )) {\n if (result?.isFinal && result[0]?.transcript) {\n final += result[0].transcript;\n }\n }\n if (final && textareaRef?.current) {\n const ta = textareaRef.current;\n const next = ta.value + (ta.value ? \" \" : \"\") + final;\n ta.value = next;\n ta.dispatchEvent(new Event(\"input\", { bubbles: true }));\n onTranscriptionChange?.(next);\n }\n };\n const handleError = (_event: Event) => setIsListening(false);\n\n sr.addEventListener(\"start\", handleStart);\n sr.addEventListener(\"end\", handleEnd);\n sr.addEventListener(\"result\", handleResult);\n sr.addEventListener(\"error\", handleError);\n\n recognitionRef.current = sr;\n setRecognition(sr);\n\n return () => {\n sr.removeEventListener(\"start\", handleStart);\n sr.removeEventListener(\"end\", handleEnd);\n sr.removeEventListener(\"result\", handleResult);\n sr.removeEventListener(\"error\", handleError);\n recognitionRef.current?.stop();\n };\n }, [textareaRef, onTranscriptionChange]);\n\n const handleToggleListening = useCallback(() => {\n if (!recognition) return;\n const sr = recognition as SpeechRecognitionInstance & {\n start(): void;\n stop(): void;\n };\n if (isListening) sr.stop();\n else sr.start();\n }, [recognition, isListening]);\n\n const button = (\n <Button\n aria-label={ariaLabel}\n className={cn(\n \"text-sf-subtle hover:text-sf-default transition-all\",\n isListening && \"animate-pulse text-sf-brand\",\n className\n )}\n disabled={!recognition}\n onClick={handleToggleListening}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n {...props}\n >\n <MicrophoneIcon className=\"size-4\" />\n </Button>\n );\n\n if (!recognition) return null;\n\n return (\n <Tooltip content={isListening ? \"Stop listening\" : \"Voice input\"}>\n {button}\n </Tooltip>\n );\n};\n\n// ─── PromptInputAttachButton ──────────────────────────────────────────────────\n\nexport type PromptInputAttachButtonProps = Omit<\n PromptInputButtonProps,\n \"aria-label\"\n> & {\n \"aria-label\"?: string;\n};\n\n/**\n * Convenience button that opens the file dialog directly (no dropdown needed).\n */\nexport const PromptInputAttachButton = ({\n \"aria-label\": ariaLabel = \"Attach file\",\n className,\n ...props\n}: PromptInputAttachButtonProps) => {\n const attachments = usePromptInputAttachments();\n const handleClick = useCallback(() => {\n attachments.openFileDialog();\n }, [attachments]);\n return (\n <Tooltip content={ariaLabel}>\n <Button\n aria-label={ariaLabel}\n className={cn(\"text-sf-subtle hover:text-sf-default\", className)}\n onClick={handleClick}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n {...props}\n >\n <PaperclipIcon className=\"size-4\" />\n </Button>\n </Tooltip>\n );\n};\n"],"x_google_ignoreList":[0,1,2,3,4],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-response-BEUg3xvd.js","names":[],"sources":["../src/components/ai-response/ai-response.tsx"],"sourcesContent":["\"use client\";\n\nimport { code } from \"@streamdown/code\";\nimport { memo } from \"react\";\nimport { Streamdown, type StreamdownProps } from \"streamdown\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_RESPONSE_VARIANTS = {} as const;\nexport const SF_AI_RESPONSE_DEFAULT_VARIANTS = {} as const;\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nconst plugins = { code };\n\nexport type AiResponseProps = StreamdownProps & {\n /** Additional CSS classes. */\n className?: string;\n};\n\n/**\n * Renders AI-generated markdown using Streamdown.\n * Supports streaming animation, syntax-highlighted code, tables, and more.\n *\n * Pass `isAnimating={true}` while streaming and `false` once complete to get\n * character-by-character reveal animation.\n *\n * @example\n * ```tsx\n * // Static\n * <AiResponse>{\"# Hello\\n\\nThis is **bold** and _italic_.\"}</AiResponse>\n *\n * // Streaming\n * <AiResponse isAnimating={isStreaming}>{streamedText}</AiResponse>\n * ```\n */\nexport const AiResponse = memo(\n ({ className, children, ...props }: AiResponseProps) => {\n return (\n <Streamdown\n className={cn(\n \"w-full text-sf-default text-sm\",\n \"[&>*:first-child]:mt-0 [&>*:last-child]:mb-0\",\n // Bridge shadcn tokens used by @streamdown/code to sf tokens so\n // code blocks render with visible borders/backgrounds/text.\n \"[--border:var(--color-sf-line)] [--sidebar:var(--color-sf-elevated)]\",\n \"[--background:var(--color-sf-recessed)] [--foreground:var(--text-color-sf-default)]\",\n \"[--muted-foreground:var(--text-color-sf-subtle)]\",\n className\n )}\n plugins={plugins}\n {...props}\n >\n {children}\n </Streamdown>\n );\n },\n (prev, next) =>\n prev.children === next.children && prev.isAnimating === next.isAnimating\n);\n\nAiResponse.displayName = \"AiResponse\";\n"],"mappings":";;;;;;;AAUA,IAAa,0BAA0B,EAAE;AACzC,IAAa,kCAAkC,EAAE;AAIjD,IAAM,UAAU,EAAE,MAAM;;;;;;;;;;;;;;;;;AAuBxB,IAAa,aAAa,MACvB,EAAE,WAAW,UAAU,GAAG,YAA6B;AACtD,QACE,oBAAC,YAAD;EACE,WAAW,GACT,kCACA,gDAGA,wEACA,uFACA,oDACA,UACD;EACQ;EACT,GAAI;EAEH;EACU,CAAA;IAGhB,MAAM,SACL,KAAK,aAAa,KAAK,YAAY,KAAK,gBAAgB,KAAK,YAChE;AAED,WAAW,cAAc"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-streaming-text-CMfoThV0.js","names":[],"sources":["../src/components/ai-streaming-text/ai-streaming-text.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_STREAMING_TEXT_VARIANTS = {} as const;\nexport const SF_AI_STREAMING_TEXT_DEFAULT_VARIANTS = {} as const;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface UseAiStreamingTextOptions {\n /** Whether content is actively streaming. @default false */\n isStreaming?: boolean;\n /** Characters to release per animation frame tick. @default 10 */\n charsPerTick?: number;\n /** Milliseconds between releases (~16ms = 60fps). @default 16 */\n tickInterval?: number;\n /** Immediately flush all text when streaming stops. @default true */\n flushOnStop?: boolean;\n}\n\n// ─── Hook ────────────────────────────────────────────────────────────────────\n\n/**\n * Buffers incoming text and releases it character-by-character for a smooth\n * typing effect. Pass the full (possibly incomplete) text and whether streaming\n * is active — get back the display-ready substring.\n *\n * @example\n * ```tsx\n * function StreamingMessage({ text, isStreaming }) {\n * const displayText = useAiStreamingText(text, { isStreaming });\n * return <AiResponse>{displayText}</AiResponse>;\n * }\n * ```\n */\nexport function useAiStreamingText(\n text: string,\n options: UseAiStreamingTextOptions = {}\n): string {\n const {\n isStreaming = false,\n charsPerTick = 10,\n tickInterval = 16,\n flushOnStop = true,\n } = options;\n\n const [displayText, setDisplayText] = useState(text);\n const targetRef = useRef(text);\n const posRef = useRef(text.length);\n const rafRef = useRef<number | null>(null);\n const lastTickRef = useRef(0);\n\n useEffect(() => {\n targetRef.current = text;\n }, [text]);\n\n useEffect(() => {\n if (!isStreaming) {\n if (flushOnStop) {\n setDisplayText(text);\n posRef.current = text.length;\n }\n return;\n }\n\n const animate = (timestamp: number) => {\n const target = targetRef.current;\n const pos = posRef.current;\n\n if (pos >= target.length) {\n rafRef.current = requestAnimationFrame(animate);\n return;\n }\n\n if (timestamp - lastTickRef.current < tickInterval) {\n rafRef.current = requestAnimationFrame(animate);\n return;\n }\n lastTickRef.current = timestamp;\n\n const newPos = Math.min(pos + charsPerTick, target.length);\n posRef.current = newPos;\n setDisplayText(target.slice(0, newPos));\n rafRef.current = requestAnimationFrame(animate);\n };\n\n rafRef.current = requestAnimationFrame(animate);\n return () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [isStreaming, charsPerTick, tickInterval, flushOnStop, text]);\n\n useEffect(() => {\n if (!isStreaming && flushOnStop && displayText !== text) {\n setDisplayText(text);\n posRef.current = text.length;\n }\n }, [isStreaming, flushOnStop, text, displayText]);\n\n return displayText;\n}\n"],"mappings":";;;AAMA,IAAa,gCAAgC,EAAE;AAC/C,IAAa,wCAAwC,EAAE;;;;;;;;;;;;;;AA8BvD,SAAgB,mBACd,MACA,UAAqC,EAAE,EAC/B;CACR,MAAM,EACJ,cAAc,OACd,eAAe,IACf,eAAe,IACf,cAAc,SACZ;CAEJ,MAAM,CAAC,aAAa,kBAAkB,SAAS,KAAK;CACpD,MAAM,YAAY,OAAO,KAAK;CAC9B,MAAM,SAAS,OAAO,KAAK,OAAO;CAClC,MAAM,SAAS,OAAsB,KAAK;CAC1C,MAAM,cAAc,OAAO,EAAE;AAE7B,iBAAgB;AACd,YAAU,UAAU;IACnB,CAAC,KAAK,CAAC;AAEV,iBAAgB;AACd,MAAI,CAAC,aAAa;AAChB,OAAI,aAAa;AACf,mBAAe,KAAK;AACpB,WAAO,UAAU,KAAK;;AAExB;;EAGF,MAAM,WAAW,cAAsB;GACrC,MAAM,SAAS,UAAU;GACzB,MAAM,MAAM,OAAO;AAEnB,OAAI,OAAO,OAAO,QAAQ;AACxB,WAAO,UAAU,sBAAsB,QAAQ;AAC/C;;AAGF,OAAI,YAAY,YAAY,UAAU,cAAc;AAClD,WAAO,UAAU,sBAAsB,QAAQ;AAC/C;;AAEF,eAAY,UAAU;GAEtB,MAAM,SAAS,KAAK,IAAI,MAAM,cAAc,OAAO,OAAO;AAC1D,UAAO,UAAU;AACjB,kBAAe,OAAO,MAAM,GAAG,OAAO,CAAC;AACvC,UAAO,UAAU,sBAAsB,QAAQ;;AAGjD,SAAO,UAAU,sBAAsB,QAAQ;AAC/C,eAAa;AACX,OAAI,OAAO,QAAS,sBAAqB,OAAO,QAAQ;;IAEzD;EAAC;EAAa;EAAc;EAAc;EAAa;EAAK,CAAC;AAEhE,iBAAgB;AACd,MAAI,CAAC,eAAe,eAAe,gBAAgB,MAAM;AACvD,kBAAe,KAAK;AACpB,UAAO,UAAU,KAAK;;IAEvB;EAAC;EAAa;EAAa;EAAM;EAAY,CAAC;AAEjD,QAAO"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-subagent-DcPRqkAA.js","names":[],"sources":["../src/components/ai-subagent/ai-subagent.tsx"],"sourcesContent":["\"use client\";\n\nimport { Collapsible as BaseCollapsible } from \"@base-ui/react/collapsible\";\nimport {\n RobotIcon,\n CaretDownIcon,\n SpinnerGapIcon,\n CheckCircleIcon,\n XCircleIcon,\n} from \"@phosphor-icons/react\";\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\nimport { useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_SUBAGENT_VARIANTS = {\n status: {\n running: { classes: \"\", description: \"Subagent is actively processing\" },\n completed: { classes: \"\", description: \"Subagent finished successfully\" },\n error: { classes: \"\", description: \"Subagent encountered an error\" },\n },\n} as const;\n\nexport const SF_AI_SUBAGENT_DEFAULT_VARIANTS = {\n status: \"running\",\n} as const;\n\nexport type SFAiSubagentStatus = keyof typeof SF_AI_SUBAGENT_VARIANTS.status;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiSubagentProps = HTMLAttributes<HTMLDivElement> & {\n /** Subagent type ID (e.g. `\"explore\"`, `\"execute\"`). */\n agentType?: string;\n /** Human-readable name for this subagent. */\n name?: string;\n /** Current status. */\n status?: SFAiSubagentStatus;\n /** Current model ID being used (e.g. `\"claude-haiku-3.5\"`). */\n modelId?: string;\n /** Duration in milliseconds. */\n duration?: number;\n /** Custom icon. Defaults to `RobotIcon`. */\n icon?: ElementType;\n /** Whether the subagent content is expanded by default. */\n defaultExpanded?: boolean;\n /** Controlled open state. */\n open?: boolean;\n /** Called when open state changes. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Content rendered inside the collapsible body — typically streaming text,\n * tool calls, and other nested agent output.\n */\n children?: ReactNode;\n};\n\n// ─── Status config ────────────────────────────────────────────────────────────\n\nconst STATUS_CONFIG: Record<\n SFAiSubagentStatus,\n {\n icon: ElementType;\n className: string;\n label: string;\n }\n> = {\n running: {\n icon: SpinnerGapIcon,\n className: \"text-sf-brand animate-spin\",\n label: \"Running\",\n },\n completed: {\n icon: CheckCircleIcon,\n className: \"text-sf-success\",\n label: \"Completed\",\n },\n error: {\n icon: XCircleIcon,\n className: \"text-sf-danger\",\n label: \"Error\",\n },\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const remaining = s % 60;\n return remaining > 0 ? `${m}m ${remaining}s` : `${m}m`;\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Collapsible wrapper for nested subagent activity. Shows the subagent name,\n * status, model, and duration in a header, with all subagent output (text,\n * tool calls, etc.) in a collapsible body.\n *\n * Maps to harness events: `subagent_start`, `subagent_text_delta`,\n * `subagent_tool_start`, `subagent_tool_end`, `subagent_end`,\n * `subagent_model_changed`.\n *\n * @example\n * ```tsx\n * <AiSubagent\n * name=\"Explore\"\n * agentType=\"explore\"\n * status=\"running\"\n * modelId=\"claude-haiku-3.5\"\n * defaultExpanded\n * >\n * <AiResponse isAnimating>{streamingText}</AiResponse>\n * <AiToolCallGroup tools={toolCalls} />\n * </AiSubagent>\n * ```\n */\nexport function AiSubagent({\n agentType,\n name,\n status = \"running\",\n modelId,\n duration,\n icon,\n defaultExpanded = true,\n open: controlledOpen,\n onOpenChange,\n children,\n className,\n ...props\n}: AiSubagentProps) {\n const [internalOpen, setInternalOpen] = useState(defaultExpanded);\n const isControlled = controlledOpen !== undefined;\n const isOpen = isControlled ? controlledOpen : internalOpen;\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (!isControlled) setInternalOpen(nextOpen);\n onOpenChange?.(nextOpen);\n };\n\n const IconComponent = icon ?? RobotIcon;\n const statusConfig = STATUS_CONFIG[status];\n const StatusIcon = statusConfig.icon;\n const displayName = name ?? agentType ?? \"Subagent\";\n\n return (\n <BaseCollapsible.Root\n defaultOpen={defaultExpanded}\n onOpenChange={handleOpenChange}\n open={isOpen}\n >\n <div\n className={cn(\"flex flex-col overflow-hidden rounded-lg\", className)}\n {...props}\n >\n {/* Header / trigger */}\n <BaseCollapsible.Trigger className=\"flex w-fit items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors hover:bg-sf-tint\">\n {/* Agent icon */}\n <div className=\"flex size-5 shrink-0 items-center justify-center rounded bg-sf-tint text-sf-subtle\">\n <IconComponent className=\"size-3.5\" />\n </div>\n\n {/* Name + model */}\n <div className=\"flex min-w-0 grow items-center gap-2\">\n <span className=\"text-sm font-medium text-sf-default\">\n {displayName}\n </span>\n {modelId && (\n <span className=\"truncate rounded bg-sf-tint px-1.5 py-0.5 font-mono text-[10px] text-sf-subtle\">\n {modelId}\n </span>\n )}\n </div>\n\n {/* Status + duration + caret */}\n <div className=\"flex shrink-0 items-center gap-2\">\n {duration !== undefined && (\n <span className=\"text-xs tabular-nums text-sf-subtle\">\n {formatDuration(duration)}\n </span>\n )}\n <StatusIcon className={cn(\"size-3.5\", statusConfig.className)} />\n <CaretDownIcon\n className={cn(\n \"size-3 text-sf-subtle transition-transform duration-200\",\n isOpen && \"rotate-180\"\n )}\n />\n </div>\n </BaseCollapsible.Trigger>\n\n {/* Collapsible content */}\n <BaseCollapsible.Panel className=\"overflow-hidden [&[data-ending-style]]:h-0 [&[data-starting-style]]:h-0\">\n <div className=\"flex flex-col gap-2 bg-sf-tint/50 px-3 py-2.5\">\n {children}\n </div>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\nAiSubagent.displayName = \"AiSubagent\";\n"],"mappings":";;;;;;;AAiBA,IAAa,0BAA0B,EACrC,QAAQ;CACN,SAAS;EAAE,SAAS;EAAI,aAAa;EAAmC;CACxE,WAAW;EAAE,SAAS;EAAI,aAAa;EAAkC;CACzE,OAAO;EAAE,SAAS;EAAI,aAAa;EAAiC;CACrE,EACF;AAED,IAAa,kCAAkC,EAC7C,QAAQ,WACT;AAkCD,IAAM,gBAOF;CACF,SAAS;EACP,MAAM;EACN,WAAW;EACX,OAAO;EACR;CACD,WAAW;EACT,MAAM;EACN,WAAW;EACX,OAAO;EACR;CACD,OAAO;EACL,MAAM;EACN,WAAW;EACX,OAAO;EACR;CACF;AAID,SAAS,eAAe,IAAoB;AAC1C,KAAI,KAAK,IAAM,QAAO,GAAG,GAAG;CAC5B,MAAM,IAAI,KAAK,MAAM,KAAK,IAAK;AAC/B,KAAI,IAAI,GAAI,QAAO,GAAG,EAAE;CACxB,MAAM,IAAI,KAAK,MAAM,IAAI,GAAG;CAC5B,MAAM,YAAY,IAAI;AACtB,QAAO,YAAY,IAAI,GAAG,EAAE,IAAI,UAAU,KAAK,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;AA4BtD,SAAgB,WAAW,EACzB,WACA,MACA,SAAS,WACT,SACA,UACA,MACA,kBAAkB,MAClB,MAAM,gBACN,cACA,UACA,WACA,GAAG,SACe;CAClB,MAAM,CAAC,cAAc,mBAAmB,SAAS,gBAAgB;CACjE,MAAM,eAAe,mBAAmB,KAAA;CACxC,MAAM,SAAS,eAAe,iBAAiB;CAE/C,MAAM,oBAAoB,aAAsB;AAC9C,MAAI,CAAC,aAAc,iBAAgB,SAAS;AAC5C,iBAAe,SAAS;;CAG1B,MAAM,gBAAgB,QAAQ;CAC9B,MAAM,eAAe,cAAc;CACnC,MAAM,aAAa,aAAa;CAChC,MAAM,cAAc,QAAQ,aAAa;AAEzC,QACE,oBAAC,YAAgB,MAAjB;EACE,aAAa;EACb,cAAc;EACd,MAAM;YAEN,qBAAC,OAAD;GACE,WAAW,GAAG,4CAA4C,UAAU;GACpE,GAAI;aAFN,CAKE,qBAAC,YAAgB,SAAjB;IAAyB,WAAU;cAAnC;KAEE,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,eAAD,EAAe,WAAU,YAAa,CAAA;MAClC,CAAA;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA,EACN,WACC,oBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA,CAEL;;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACG,aAAa,KAAA,KACZ,oBAAC,QAAD;QAAM,WAAU;kBACb,eAAe,SAAS;QACpB,CAAA;OAET,oBAAC,YAAD,EAAY,WAAW,GAAG,YAAY,aAAa,UAAU,EAAI,CAAA;OACjE,oBAAC,eAAD,EACE,WAAW,GACT,2DACA,UAAU,aACX,EACD,CAAA;OACE;;KACkB;OAG1B,oBAAC,YAAgB,OAAjB;IAAuB,WAAU;cAC/B,oBAAC,OAAD;KAAK,WAAU;KACZ;KACG,CAAA;IACgB,CAAA,CACpB;;EACe,CAAA;;AAI3B,WAAW,cAAc"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-task-list-Da9zIm00.js","names":[],"sources":["../src/components/ai-task-list/ai-task-list.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_TASK_LIST_VARIANTS = {} as const;\nexport const SF_AI_TASK_LIST_DEFAULT_VARIANTS = {} as const;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiTaskStatus =\n | \"pending\"\n | \"in_progress\"\n | \"completed\"\n | \"cancelled\";\n\nexport type AiTaskPriority = \"high\" | \"medium\" | \"low\";\n\nexport type AiTaskItem = {\n /** Unique task identifier. */\n id: string;\n /** Task description. */\n content: string;\n /** Current status. */\n status: AiTaskStatus;\n /** Priority level. */\n priority?: AiTaskPriority;\n};\n\nexport type AiTaskListProps = Omit<HTMLAttributes<HTMLDivElement>, \"title\"> & {\n /** List of tasks. */\n tasks: AiTaskItem[];\n /** Optional title above the task list. */\n title?: string;\n /** Custom icon for the title area. */\n icon?: ElementType;\n /** Show a progress summary (e.g. \"3 / 7 done\"). Default: `true`. */\n showProgress?: boolean;\n /** Content rendered below the task list. */\n children?: ReactNode;\n};\n\nexport type AiTaskListItemProps = HTMLAttributes<HTMLDivElement> & {\n task: AiTaskItem;\n};\n\n// ─── Status config ────────────────────────────────────────────────────────────\n\nconst STATUS_CONFIG: Record<\n AiTaskStatus,\n { dotClassName: string; textClassName: string }\n> = {\n pending: {\n dotClassName: \"bg-sf-subtle/30\",\n textClassName: \"text-sf-subtle\",\n },\n in_progress: {\n dotClassName: \"bg-sf-brand\",\n textClassName: \"text-sf-default\",\n },\n completed: {\n dotClassName: \"bg-sf-success\",\n textClassName: \"text-sf-subtle/60 line-through\",\n },\n cancelled: {\n dotClassName: \"bg-sf-subtle/30\",\n textClassName: \"text-sf-subtle/40 line-through\",\n },\n};\n\nconst PRIORITY_DOT: Record<AiTaskPriority, string> = {\n high: \"bg-sf-danger\",\n medium: \"bg-sf-warning\",\n low: \"bg-sf-subtle\",\n};\n\n// ─── AiTaskListItem ──────────────────────────────────────────────────────────\n\n/**\n * Single task item with status icon, content, and optional priority dot.\n */\nexport function AiTaskListItem({\n task,\n className,\n ...props\n}: AiTaskListItemProps) {\n const config = STATUS_CONFIG[task.status];\n\n return (\n <div className={cn(\"flex items-start gap-2 py-1\", className)} {...props}>\n <span\n aria-hidden\n className={cn(\n \"mt-[0.4375rem] size-1.5 shrink-0 rounded-full\",\n config.dotClassName\n )}\n />\n <span className={cn(\"min-w-0 grow text-sm\", config.textClassName)}>\n {task.content}\n </span>\n {task.priority && (\n <span\n className={cn(\n \"mt-2 size-1 shrink-0 rounded-full\",\n PRIORITY_DOT[task.priority]\n )}\n title={`${task.priority} priority`}\n />\n )}\n </div>\n );\n}\n\nAiTaskListItem.displayName = \"AiTaskListItem\";\n\n// ─── AiTaskList ──────────────────────────────────────────────────────────────\n\n/**\n * Task progress list. Renders structured tasks from the harness `task_write`\n * tool. Typically rendered inside the `PromptInputBackLayer` or inline in\n * conversation.\n *\n * Maps to harness event: `task_updated`.\n *\n * @example\n * ```tsx\n * <AiTaskList\n * title=\"Current tasks\"\n * tasks={[\n * { id: \"1\", content: \"Read config file\", status: \"completed\", priority: \"high\" },\n * { id: \"2\", content: \"Run migrations\", status: \"in_progress\", priority: \"high\" },\n * { id: \"3\", content: \"Seed database\", status: \"pending\", priority: \"medium\" },\n * ]}\n * />\n * ```\n */\nexport function AiTaskList({\n tasks,\n title,\n icon,\n showProgress = true,\n children,\n className,\n ...props\n}: AiTaskListProps) {\n const completed = tasks.filter((t) => t.status === \"completed\").length;\n const total = tasks.filter((t) => t.status !== \"cancelled\").length;\n\n return (\n <div className={cn(\"flex flex-col gap-1\", className)} {...props}>\n {/* Header */}\n {(title || showProgress) && (\n <div className=\"flex items-center justify-between gap-2 pb-0.5\">\n <div className=\"flex items-center gap-1.5\">\n {icon &&\n (() => {\n const Icon = icon;\n return <Icon className=\"size-3.5 text-sf-subtle\" />;\n })()}\n {title && <span className=\"text-sm text-sf-subtle\">{title}</span>}\n </div>\n {showProgress && total > 0 && (\n <span className=\"text-xs tabular-nums text-sf-subtle/60\">\n {completed}/{total}\n </span>\n )}\n </div>\n )}\n\n {/* Tasks */}\n <div className=\"flex flex-col\">\n {tasks.map((task) => (\n <AiTaskListItem key={task.id} task={task} />\n ))}\n </div>\n\n {/* Progress bar */}\n {showProgress && total > 0 && (\n <div className=\"mt-1 h-0.5 overflow-hidden rounded-full bg-sf-tint\">\n <div\n className=\"h-full rounded-full bg-sf-brand/60 transition-[width] duration-300 ease-out\"\n style={{ width: `${(completed / total) * 100}%` }}\n />\n </div>\n )}\n\n {children}\n </div>\n );\n}\n\nAiTaskList.displayName = \"AiTaskList\";\n"],"mappings":";;;;AAQA,IAAa,2BAA2B,EAAE;AAC1C,IAAa,mCAAmC,EAAE;AA0ClD,IAAM,gBAGF;CACF,SAAS;EACP,cAAc;EACd,eAAe;EAChB;CACD,aAAa;EACX,cAAc;EACd,eAAe;EAChB;CACD,WAAW;EACT,cAAc;EACd,eAAe;EAChB;CACD,WAAW;EACT,cAAc;EACd,eAAe;EAChB;CACF;AAED,IAAM,eAA+C;CACnD,MAAM;CACN,QAAQ;CACR,KAAK;CACN;;;;AAOD,SAAgB,eAAe,EAC7B,MACA,WACA,GAAG,SACmB;CACtB,MAAM,SAAS,cAAc,KAAK;AAElC,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,+BAA+B,UAAU;EAAE,GAAI;YAAlE;GACE,oBAAC,QAAD;IACE,eAAA;IACA,WAAW,GACT,iDACA,OAAO,aACR;IACD,CAAA;GACF,oBAAC,QAAD;IAAM,WAAW,GAAG,wBAAwB,OAAO,cAAc;cAC9D,KAAK;IACD,CAAA;GACN,KAAK,YACJ,oBAAC,QAAD;IACE,WAAW,GACT,qCACA,aAAa,KAAK,UACnB;IACD,OAAO,GAAG,KAAK,SAAS;IACxB,CAAA;GAEA;;;AAIV,eAAe,cAAc;;;;;;;;;;;;;;;;;;;;AAuB7B,SAAgB,WAAW,EACzB,OACA,OACA,MACA,eAAe,MACf,UACA,WACA,GAAG,SACe;CAClB,MAAM,YAAY,MAAM,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC;CAChE,MAAM,QAAQ,MAAM,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC;AAE5D,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,uBAAuB,UAAU;EAAE,GAAI;YAA1D;IAEI,SAAS,iBACT,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACG,QAGU,oBADM,MACN,EAAM,WAAU,2BAA4B,CAAA,EAEtD,SAAS,oBAAC,QAAD;MAAM,WAAU;gBAA0B;MAAa,CAAA,CAC7D;QACL,gBAAgB,QAAQ,KACvB,qBAAC,QAAD;KAAM,WAAU;eAAhB;MACG;MAAU;MAAE;MACR;OAEL;;GAIR,oBAAC,OAAD;IAAK,WAAU;cACZ,MAAM,KAAK,SACV,oBAAC,gBAAD,EAAoC,MAAQ,EAAvB,KAAK,GAAkB,CAC5C;IACE,CAAA;GAGL,gBAAgB,QAAQ,KACvB,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAE,OAAO,GAAI,YAAY,QAAS,IAAI,IAAI;KACjD,CAAA;IACE,CAAA;GAGP;GACG;;;AAIV,WAAW,cAAc"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-tool-Cn1O4xjP.js","names":[],"sources":["../src/components/ai-tool/ai-tool.tsx"],"sourcesContent":["\"use client\";\n\nimport { Collapsible as BaseCollapsible } from \"@base-ui/react/collapsible\";\nimport {\n CaretDownIcon,\n CheckIcon,\n CircleIcon,\n CodeIcon,\n DatabaseIcon,\n FileTextIcon,\n GearIcon,\n GlobeIcon,\n ImageIcon,\n LightningIcon,\n MagnifyingGlassIcon,\n ChatCircleIcon,\n SpinnerGapIcon,\n TerminalIcon,\n WarningCircleIcon,\n XCircleIcon,\n ShieldWarningIcon,\n BrainIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps, ElementType } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { AiStatusBadge, type AiStatusBadgeStatus } from \"../ai-status-badge\";\nimport { Button } from \"../button\";\nimport { Tooltip } from \"../tooltip\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_TOOL_VARIANTS = {\n variant: {\n default: {\n classes: \"\",\n description: \"Expandable card with collapsible input/output sections\",\n },\n inline: {\n classes: \"\",\n description: \"Compact single-line display with left accent border\",\n },\n minimal: {\n classes: \"\",\n description: \"Pill-shaped status badge\",\n },\n ephemeral: {\n classes: \"\",\n description:\n \"Single-status row that auto-dismisses after the call completes. Persists on error or when the user expands it.\",\n },\n },\n} as const;\n\nexport const SF_AI_TOOL_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Tool execution state — matches Vercel AI SDK ToolUIPart[\"state\"]. */\nexport type AiToolState =\n | \"input-streaming\"\n | \"input-available\"\n | \"approval-requested\"\n | \"approval-responded\"\n | \"output-available\"\n | \"output-error\"\n | \"output-denied\";\n\n/** Display variant for tool call rendering. */\nexport type AiToolCallVariant = keyof typeof SF_AI_TOOL_VARIANTS.variant;\n\n/** Approval state for tools requiring user confirmation. */\nexport type AiToolApprovalState =\n | \"awaiting\"\n | \"approved\"\n | \"declined\"\n | undefined;\n\n/**\n * Structured tool part — mirrors the shape of Vercel AI SDK's `ToolUIPart`\n * without requiring the `ai` package as a dependency.\n */\nexport interface AiToolPart {\n /** Tool call identifier. */\n toolCallId: string;\n /** Tool name or type identifier. */\n toolName: string;\n /** Current execution state. */\n state: AiToolState;\n /** Tool input parameters. */\n input?: unknown;\n /** Tool output result. */\n output?: unknown;\n /** Error text when state is \"output-error\". */\n errorText?: string;\n}\n\nexport type AiToolCallProps = Omit<ComponentProps<\"div\">, \"part\"> & {\n /** Structured tool part data. */\n part: AiToolPart;\n /** Display variant. @default \"default\" */\n variant?: AiToolCallVariant;\n /** Duration in milliseconds. */\n duration?: number;\n /** Summary text shown alongside the tool name. */\n summary?: string;\n /** Custom icon override. */\n icon?: ElementType;\n /** Approval state for tools requiring confirmation. */\n approvalState?: AiToolApprovalState;\n /** Called when user approves a tool call (only relevant when `approvalState=\"awaiting\"`). */\n onApprove?: () => void;\n /** Called when user rejects a tool call (only relevant when `approvalState=\"awaiting\"`). */\n onReject?: () => void;\n /** Default expanded state for the collapsible. @default false */\n defaultExpanded?: boolean;\n /**\n * For `variant=\"ephemeral\"`: ms to keep the completed row visible before\n * fading out. Set to `0` to disable auto-dismiss. @default 800\n */\n dismissDelay?: number;\n /**\n * For `variant=\"ephemeral\"`: when `true`, the row never auto-dismisses.\n * Errors and user-expanded rows are pinned automatically.\n */\n persist?: boolean;\n /** Called once the ephemeral row has finished its dismiss animation. */\n onDismiss?: () => void;\n};\n\nexport type AiToolCallGroupProps = Omit<ComponentProps<\"div\">, \"part\"> & {\n /** Array of tool parts. */\n parts: AiToolPart[];\n /** Group label. */\n label?: string;\n /** Duration map keyed by toolCallId. */\n durations?: Record<string, number>;\n /** Summary map keyed by toolCallId. */\n summaries?: Record<string, string>;\n /** Approval states map keyed by toolCallId. */\n approvalStates?: Record<string, AiToolApprovalState>;\n /** Called when user approves a tool call. Receives the `toolCallId`. */\n onApprove?: (toolCallId: string) => void;\n /** Called when user rejects a tool call. Receives the `toolCallId`. */\n onReject?: (toolCallId: string) => void;\n /** Default expanded state. @default false */\n defaultExpanded?: boolean;\n};\n\nexport type AiToolCallTimelineProps = ComponentProps<\"div\"> & {\n /** Array of tool parts. */\n parts: AiToolPart[];\n /** Duration map keyed by toolCallId. */\n durations?: Record<string, number>;\n};\n\n// ─── Icon Map ────────────────────────────────────────────────────────────────\n\nconst TOOL_ICONS: Record<string, ElementType> = {\n generateSuggestions: BrainIcon,\n searchDatabase: DatabaseIcon,\n searchWeb: GlobeIcon,\n search: MagnifyingGlassIcon,\n readFile: FileTextIcon,\n writeFile: FileTextIcon,\n executeQuery: LightningIcon,\n analyzeContent: BrainIcon,\n generateImage: ImageIcon,\n chat: ChatCircleIcon,\n runCode: TerminalIcon,\n settings: GearIcon,\n code: CodeIcon,\n};\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n\n/** Extract the display-friendly tool name from a tool part. */\nexport function getToolDisplayName(part: AiToolPart): string {\n return part.toolName\n .replace(\"tool-\", \"\")\n .replace(\"Tool\", \"\")\n .replace(/([A-Z])/g, \" $1\")\n .replace(/_/g, \" \")\n .replace(/^./, (s) => s.toUpperCase())\n .trim();\n}\n\n/** Get the best-matching icon for a tool part. */\nexport function getToolIcon(part: AiToolPart): ElementType {\n if (TOOL_ICONS[part.toolName]) return TOOL_ICONS[part.toolName];\n\n const lower = part.toolName.toLowerCase();\n for (const [key, icon] of Object.entries(TOOL_ICONS)) {\n if (lower.includes(key.toLowerCase())) return icon;\n }\n\n return MagnifyingGlassIcon;\n}\n\n/** Whether the tool is still loading (input phase). */\nexport function isToolLoading(part: AiToolPart): boolean {\n return part.state === \"input-streaming\" || part.state === \"input-available\";\n}\n\n/** Whether the tool has produced a result. */\nexport function hasToolResult(part: AiToolPart): boolean {\n return part.state === \"output-available\";\n}\n\n/** Whether the output itself contains an error flag. */\nexport function hasToolOutputError(part: AiToolPart): boolean {\n if (part.state !== \"output-available\") return false;\n const output = part.output as Record<string, unknown> | undefined;\n return output?.error === true;\n}\n\n/** Whether the tool is in any error state. */\nexport function hasToolError(part: AiToolPart): boolean {\n return part.state === \"output-error\" || hasToolOutputError(part);\n}\n\n/** Extract error text from either error state or output.error. */\nexport function getToolErrorText(part: AiToolPart): string | undefined {\n if (part.state === \"output-error\") return part.errorText;\n if (hasToolOutputError(part)) {\n const output = part.output as Record<string, unknown>;\n return (\n (output.message as string) ||\n (output.error as string) ||\n \"Tool returned an error\"\n );\n }\n return undefined;\n}\n\n// ─── State Config ────────────────────────────────────────────────────────────\n\ninterface StateConfig {\n colorClass: string;\n bgClass: string;\n icon: ElementType;\n spin: boolean;\n label: string;\n /** Solid dot color for the minimalist status indicator (ephemeral variant). */\n dotClass: string;\n /** Whether the dot should pulse (running / streaming states). */\n dotPulse: boolean;\n}\n\nfunction getStateConfig(\n part: AiToolPart,\n approvalState?: AiToolApprovalState\n): StateConfig {\n if (approvalState === \"awaiting\") {\n return {\n colorClass: \"text-sf-warning\",\n bgClass: \"bg-sf-warning/10\",\n icon: ShieldWarningIcon,\n spin: false,\n label: \"Awaiting Approval\",\n dotClass: \"bg-sf-warning\",\n dotPulse: true,\n };\n }\n\n if (approvalState === \"declined\") {\n return {\n colorClass: \"text-sf-danger\",\n bgClass: \"bg-sf-danger/10\",\n icon: XCircleIcon,\n spin: false,\n label: \"Declined\",\n dotClass: \"bg-sf-danger\",\n dotPulse: false,\n };\n }\n\n if (hasToolError(part)) {\n return {\n colorClass: \"text-sf-danger\",\n bgClass: \"bg-sf-danger/10\",\n icon: WarningCircleIcon,\n spin: false,\n label: \"Failed\",\n dotClass: \"bg-sf-danger\",\n dotPulse: false,\n };\n }\n\n switch (part.state) {\n case \"input-streaming\":\n return {\n colorClass: \"text-sf-brand\",\n bgClass: \"bg-sf-brand/10\",\n icon: CircleIcon,\n spin: false,\n label: \"Streaming\",\n dotClass: \"bg-sf-warning\",\n dotPulse: true,\n };\n case \"input-available\":\n return {\n colorClass: \"text-sf-brand\",\n bgClass: \"bg-sf-brand/10\",\n icon: SpinnerGapIcon,\n spin: true,\n label: \"Running\",\n dotClass: \"bg-sf-warning\",\n dotPulse: true,\n };\n case \"output-available\":\n return {\n colorClass: \"text-sf-success\",\n bgClass: \"bg-sf-tint\",\n icon: CheckIcon,\n spin: false,\n label: \"Complete\",\n dotClass: \"bg-sf-success\",\n dotPulse: false,\n };\n default:\n return {\n colorClass: \"text-sf-subtle\",\n bgClass: \"bg-sf-tint\",\n icon: CircleIcon,\n spin: false,\n label: \"Pending\",\n dotClass: \"bg-sf-subtle/40\",\n dotPulse: false,\n };\n }\n}\n\n// ─── AiToolCall ──────────────────────────────────────────────────────────────\n\n/**\n * Renders a single tool call. Supports three display variants:\n * - `\"default\"` — expandable card with collapsible input/output\n * - `\"inline\"` — compact single-line with left accent border\n * - `\"minimal\"` — pill-shaped status badge\n *\n * @example\n * ```tsx\n * <AiToolCall\n * part={{ toolCallId: \"1\", toolName: \"web_search\", state: \"output-available\", input: { q: \"ts\" }, output: \"...\" }}\n * />\n * ```\n */\nexport function AiToolCall({\n part,\n variant = \"default\",\n duration,\n summary,\n icon: CustomIcon,\n approvalState,\n onApprove,\n onReject,\n defaultExpanded = false,\n dismissDelay = 800,\n persist = false,\n onDismiss,\n className,\n ...props\n}: AiToolCallProps) {\n const [isExpanded, setIsExpanded] = useState(defaultExpanded);\n\n const IconComponent = CustomIcon ?? getToolIcon(part);\n const displayName = getToolDisplayName(part);\n const config = getStateConfig(part, approvalState);\n const StatusIconEl = config.icon;\n const errorText = getToolErrorText(part);\n\n // ── Ephemeral ──────────────────────────────────────────────────────────────\n\n if (variant === \"ephemeral\") {\n return (\n <AiToolCallEphemeral\n part={part}\n approvalState={approvalState}\n duration={duration}\n summary={summary}\n icon={IconComponent}\n displayName={displayName}\n config={config}\n errorText={errorText}\n dismissDelay={dismissDelay}\n persist={persist}\n onDismiss={onDismiss}\n onApprove={onApprove}\n onReject={onReject}\n className={className}\n {...props}\n />\n );\n }\n\n // ── Minimal ────────────────────────────────────────────────────────────────\n\n if (variant === \"minimal\") {\n const handleGetToolStatus = (): AiStatusBadgeStatus => {\n if (hasToolError(part)) return \"error\";\n if (approvalState === \"awaiting\") return \"idle\";\n if (isToolLoading(part)) return \"running\";\n if (hasToolResult(part)) return \"success\";\n return \"idle\";\n };\n\n return (\n <AiStatusBadge\n className={cn(\n \"my-1.5 py-1\",\n hasToolError(part) && \"bg-sf-danger/10\",\n className\n )}\n icon={IconComponent}\n label={displayName}\n status={handleGetToolStatus()}\n {...props}\n />\n );\n }\n\n // ── Inline ─────────────────────────────────────────────────────────────────\n\n if (variant === \"inline\") {\n return (\n <div\n className={cn(\n \"my-1 flex w-full items-center gap-2 py-1.5 pl-1\",\n \"animate-in fade-in-0 slide-in-from-bottom-1 duration-200\",\n className\n )}\n {...props}\n >\n <div className={cn(\"shrink-0 rounded-md p-1\", config.bgClass)}>\n <IconComponent className={cn(\"size-3.5\", config.colorClass)} />\n </div>\n <span\n className={cn(\n \"min-w-0 grow truncate text-sm\",\n hasToolError(part) ? \"text-sf-danger\" : \"text-sf-subtle\"\n )}\n >\n {displayName}\n </span>\n {/* Right-aligned meta group */}\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {duration && hasToolResult(part) ? (\n <span className=\"text-xs tabular-nums text-sf-subtle\">\n {duration}ms\n </span>\n ) : null}\n {summary && !hasToolError(part) ? (\n <span className=\"max-w-[160px] truncate text-xs text-sf-subtle\">\n {summary}\n </span>\n ) : null}\n {errorText ? (\n <span\n className=\"max-w-[160px] truncate text-xs text-sf-danger\"\n title={errorText}\n >\n {errorText}\n </span>\n ) : null}\n {approvalState === \"awaiting\" && (onApprove || onReject) ? (\n <>\n {onReject && (\n <Button\n onClick={onReject}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n Reject\n </Button>\n )}\n {onApprove && (\n <Button\n onClick={onApprove}\n size=\"sm\"\n type=\"button\"\n variant=\"primary\"\n >\n Approve\n </Button>\n )}\n </>\n ) : (\n <StatusIconEl\n className={cn(\n \"size-3.5\",\n config.colorClass,\n config.spin && \"animate-spin\"\n )}\n />\n )}\n </div>\n </div>\n );\n }\n\n // ── Default (compact expandable row) ─────────────────────────────────────\n\n return (\n <BaseCollapsible.Root open={isExpanded} onOpenChange={setIsExpanded}>\n <div\n className={cn(\n \"my-0.5 flex w-full max-w-sm flex-col\",\n \"animate-in fade-in-0 duration-150\",\n className\n )}\n {...props}\n >\n {/* Trigger — content-width: name + meta + caret */}\n <BaseCollapsible.Trigger\n render={\n <button\n type=\"button\"\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 className=\"min-w-0 truncate text-sm text-sf-subtle\">\n {displayName}\n </span>\n {/* Meta group — part of the content-width trigger */}\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {duration && hasToolResult(part) && !isExpanded ? (\n <span className=\"text-xs tabular-nums text-sf-subtle\">\n {duration}ms\n </span>\n ) : null}\n {summary && !isExpanded && !errorText ? (\n <span className=\"max-w-[120px] truncate text-xs text-sf-subtle\">\n {summary}\n </span>\n ) : null}\n <StatusIconEl\n className={cn(\n \"size-3.5\",\n config.colorClass,\n config.spin && \"animate-spin\"\n )}\n />\n <CaretDownIcon\n className={cn(\n \"size-3 text-sf-subtle transition-transform duration-200\",\n !isExpanded && \"-rotate-90\"\n )}\n />\n </div>\n </BaseCollapsible.Trigger>\n\n {/* Detail panel */}\n <BaseCollapsible.Panel className=\"overflow-hidden\">\n <div className=\"mt-1 space-y-2 rounded-lg bg-sf-tint/50 px-3 py-2\">\n {/* Input */}\n {part.input &&\n typeof part.input === \"object\" &&\n Object.keys(part.input as object).length > 0 ? (\n <div>\n <p className=\"mb-1 text-[10px] uppercase tracking-wider text-sf-subtle\">\n Input\n </p>\n <pre className=\"overflow-x-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle\">\n {JSON.stringify(\n part.input as Record<string, unknown>,\n null,\n 2\n )}\n </pre>\n </div>\n ) : null}\n\n {/* Output */}\n {hasToolResult(part) &&\n part.output !== undefined &&\n !hasToolError(part) ? (\n <div>\n <p className=\"mb-1 text-[10px] uppercase tracking-wider text-sf-subtle\">\n Output\n </p>\n <pre className=\"max-h-28 overflow-x-auto overflow-y-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle\">\n {typeof part.output === \"string\"\n ? part.output\n : JSON.stringify(\n part.output as Record<string, unknown>,\n null,\n 2\n )}\n </pre>\n </div>\n ) : null}\n\n {/* Error */}\n {errorText ? (\n <div>\n <p className=\"mb-1 flex items-center gap-1 text-[10px] uppercase tracking-wider text-sf-danger\">\n <WarningCircleIcon className=\"size-3\" />\n Error\n </p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded bg-sf-danger/10 p-1.5 font-mono text-xs text-sf-danger\">\n {errorText}\n </pre>\n </div>\n ) : null}\n\n {/* Approval actions */}\n {approvalState === \"awaiting\" && (onApprove || onReject) && (\n <div className=\"flex items-center justify-end gap-2 border-t border-sf-line/20 pt-2\">\n {onReject && (\n <Button\n onClick={onReject}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n Reject\n </Button>\n )}\n {onApprove && (\n <Button\n onClick={onApprove}\n size=\"sm\"\n type=\"button\"\n variant=\"primary\"\n >\n Approve\n </Button>\n )}\n </div>\n )}\n\n {/* Tool call ID */}\n <p className=\"font-mono text-[10px] text-sf-subtle/50\">\n {part.toolCallId}\n </p>\n </div>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\n// ─── AiToolCallEphemeral ─────────────────────────────────────────────────────\n\ninterface AiToolCallEphemeralInternalProps extends Omit<\n ComponentProps<\"div\">,\n \"part\"\n> {\n part: AiToolPart;\n approvalState?: AiToolApprovalState;\n duration?: number;\n summary?: string;\n icon: ElementType;\n displayName: string;\n config: StateConfig;\n errorText: string | undefined;\n dismissDelay: number;\n persist: boolean;\n onDismiss?: () => void;\n onApprove?: () => void;\n onReject?: () => void;\n}\n\n// Note: `icon` (tool icon) is accepted for API symmetry with other variants\n// but the ephemeral row leads with the status icon for at-a-glance state.\n\nconst FADE_OUT_MS = 200;\n\n/**\n * Internal renderer for `variant=\"ephemeral\"`. Shows a single-status row\n * that auto-dismisses once the tool call completes. Pins on error or when\n * the user expands the row.\n */\nfunction AiToolCallEphemeral({\n part,\n approvalState,\n duration,\n summary,\n icon: _icon,\n displayName,\n config,\n errorText,\n dismissDelay,\n persist,\n onDismiss,\n onApprove,\n onReject,\n className,\n ...props\n}: AiToolCallEphemeralInternalProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const [phase, setPhase] = useState<\"live\" | \"fading\" | \"gone\">(\"live\");\n const userExpandedRef = useRef(false);\n\n const hasError = hasToolError(part);\n const isComplete = hasToolResult(part);\n const isAwaitingApproval = approvalState === \"awaiting\";\n\n // Pin the row when: errored, user expanded, persist requested, or awaiting approval.\n const pinned =\n persist || hasError || userExpandedRef.current || isAwaitingApproval;\n\n // Auto-dismiss when complete (and not pinned).\n useEffect(() => {\n if (phase !== \"live\") return;\n if (!isComplete) return;\n if (pinned) return;\n if (dismissDelay <= 0) return;\n\n const settleTimer = setTimeout(() => {\n setPhase(\"fading\");\n }, dismissDelay);\n\n return () => clearTimeout(settleTimer);\n }, [phase, isComplete, pinned, dismissDelay]);\n\n // After fade animation, unmount.\n useEffect(() => {\n if (phase !== \"fading\") return;\n const fadeTimer = setTimeout(() => {\n setPhase(\"gone\");\n onDismiss?.();\n }, FADE_OUT_MS);\n return () => clearTimeout(fadeTimer);\n }, [phase, onDismiss]);\n\n if (phase === \"gone\") return null;\n\n const handleToggle = (next: boolean) => {\n if (next) userExpandedRef.current = true;\n setIsExpanded(next);\n };\n\n return (\n <BaseCollapsible.Root open={isExpanded} onOpenChange={handleToggle}>\n <div\n className={cn(\n \"my-0.5 flex w-full max-w-sm flex-col overflow-hidden\",\n \"transition-[opacity,max-height,margin] duration-200 ease-out\",\n phase === \"live\"\n ? \"max-h-40 opacity-100\"\n : \"pointer-events-none my-0 max-h-0 opacity-0\",\n phase === \"live\" && \"animate-in fade-in-0 duration-150\",\n className\n )}\n data-phase={phase}\n {...props}\n >\n <BaseCollapsible.Trigger\n render={\n <button\n type=\"button\"\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-label={config.label}\n className={cn(\n \"size-1.5 shrink-0 rounded-full\",\n config.dotClass,\n config.dotPulse && \"animate-pulse\"\n )}\n role=\"status\"\n />\n <span\n className={cn(\n \"min-w-0 truncate text-sm\",\n hasError ? \"text-sf-danger\" : \"text-sf-subtle\"\n )}\n >\n {displayName}\n </span>\n {duration && isComplete && !isExpanded ? (\n <span className=\"text-xs tabular-nums text-sf-subtle/60\">\n {duration}ms\n </span>\n ) : null}\n {summary && !isExpanded && !errorText ? (\n <span className=\"max-w-[120px] truncate text-xs text-sf-subtle/60\">\n {summary}\n </span>\n ) : null}\n <CaretDownIcon\n className={cn(\n \"size-3 text-sf-subtle/60 transition-transform duration-200\",\n !isExpanded && \"-rotate-90\"\n )}\n />\n </BaseCollapsible.Trigger>\n\n <BaseCollapsible.Panel className=\"overflow-hidden\">\n <div className=\"mt-1 space-y-2 rounded-lg bg-sf-tint/50 px-3 py-2\">\n {part.input &&\n typeof part.input === \"object\" &&\n Object.keys(part.input as object).length > 0 ? (\n <div>\n <p className=\"mb-1 text-[10px] uppercase tracking-wider text-sf-subtle\">\n Input\n </p>\n <pre className=\"overflow-x-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle\">\n {JSON.stringify(\n part.input as Record<string, unknown>,\n null,\n 2\n )}\n </pre>\n </div>\n ) : null}\n\n {hasToolResult(part) && part.output !== undefined && !hasError ? (\n <div>\n <p className=\"mb-1 text-[10px] uppercase tracking-wider text-sf-subtle\">\n Output\n </p>\n <pre className=\"max-h-28 overflow-x-auto overflow-y-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle\">\n {typeof part.output === \"string\"\n ? part.output\n : JSON.stringify(\n part.output as Record<string, unknown>,\n null,\n 2\n )}\n </pre>\n </div>\n ) : null}\n\n {errorText ? (\n <div>\n <p className=\"mb-1 flex items-center gap-1 text-[10px] uppercase tracking-wider text-sf-danger\">\n <WarningCircleIcon className=\"size-3\" />\n Error\n </p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded bg-sf-danger/10 p-1.5 font-mono text-xs text-sf-danger\">\n {errorText}\n </pre>\n </div>\n ) : null}\n\n {isAwaitingApproval && (onApprove || onReject) && (\n <div className=\"flex items-center justify-end gap-2 border-t border-sf-line/20 pt-2\">\n {onReject && (\n <Button\n onClick={onReject}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n Reject\n </Button>\n )}\n {onApprove && (\n <Button\n onClick={onApprove}\n size=\"sm\"\n type=\"button\"\n variant=\"primary\"\n >\n Approve\n </Button>\n )}\n </div>\n )}\n </div>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\n// ─── AiToolCallGroup ─────────────────────────────────────────────────────────\n\n/**\n * Collapsible group of tool calls. Shows a summary header with progress\n * counter and stacked tool icons when collapsed. Renders a single tool\n * directly if only one part is provided.\n *\n * @example\n * ```tsx\n * <AiToolCallGroup parts={toolParts} label=\"3 tools ran\" />\n * ```\n */\nexport function AiToolCallGroup({\n parts,\n label,\n durations = {},\n summaries = {},\n approvalStates = {},\n onApprove,\n onReject,\n defaultExpanded = false,\n className,\n ...props\n}: AiToolCallGroupProps) {\n const [isExpanded, setIsExpanded] = useState(defaultExpanded);\n\n const completedCount = parts.filter(\n (p) => p.state === \"output-available\"\n ).length;\n const errorCount = parts.filter(hasToolError).length;\n const totalCount = parts.length;\n\n // Single item — render directly\n if (parts.length === 1 && parts[0]) {\n const singlePart = parts[0];\n // Strip `part` from div props spread to avoid collision with HTML `part` attribute\n const { part: _part, ...restProps } = props as typeof props & {\n part?: unknown;\n };\n return (\n <AiToolCall\n part={singlePart}\n duration={durations[singlePart.toolCallId]}\n summary={summaries[singlePart.toolCallId]}\n approvalState={approvalStates[singlePart.toolCallId]}\n onApprove={\n onApprove ? () => onApprove(singlePart.toolCallId) : undefined\n }\n onReject={onReject ? () => onReject(singlePart.toolCallId) : undefined}\n className={className}\n {...restProps}\n />\n );\n }\n\n return (\n <BaseCollapsible.Root open={isExpanded} onOpenChange={setIsExpanded}>\n <div\n className={cn(\n \"my-1.5 w-full overflow-hidden rounded-lg bg-sf-tint/50\",\n \"animate-in fade-in-0 slide-in-from-bottom-2 duration-200\",\n className\n )}\n {...props}\n >\n <BaseCollapsible.Trigger\n render={\n <button\n type=\"button\"\n className=\"flex w-fit items-center gap-3 px-3 py-2.5 text-left transition-colors hover:bg-sf-tint\"\n />\n }\n >\n <span className=\"text-sm font-medium text-sf-subtle\">\n {label ?? `${totalCount} tool calls`}\n </span>\n <span className=\"text-xs text-sf-subtle\">\n {completedCount}/{totalCount}\n {errorCount > 0 ? (\n <span className=\"ml-1 text-sf-danger\">({errorCount} errors)</span>\n ) : null}\n </span>\n\n <div className=\"flex items-center gap-2\">\n {/* Tool icon stack when collapsed */}\n {!isExpanded ? (\n <div className=\"flex -space-x-1\">\n {parts.slice(0, 5).map((p) => {\n const Icon = getToolIcon(p);\n return (\n <div\n key={p.toolCallId}\n className={cn(\n \"flex size-6 items-center justify-center rounded-full border-2 border-sf-elevated\",\n p.state === \"output-available\" &&\n !hasToolError(p) &&\n \"bg-sf-tint\",\n isToolLoading(p) && \"bg-sf-tint\",\n hasToolError(p) && \"bg-sf-danger/20\"\n )}\n >\n <Icon className=\"size-3\" />\n </div>\n );\n })}\n {parts.length > 5 ? (\n <div className=\"flex size-6 items-center justify-center rounded-full border-2 border-sf-elevated bg-sf-tint text-[10px] text-sf-subtle\">\n +{parts.length - 5}\n </div>\n ) : null}\n </div>\n ) : null}\n <CaretDownIcon\n className={cn(\n \"size-4 text-sf-subtle transition-transform duration-200\",\n !isExpanded && \"-rotate-90\"\n )}\n />\n </div>\n </BaseCollapsible.Trigger>\n\n <BaseCollapsible.Panel className=\"overflow-hidden\">\n <div className=\"space-y-2 border-t border-sf-line/20 px-3 pt-1 pb-3\">\n {parts.map((p) => (\n <AiToolCall\n key={p.toolCallId}\n part={p}\n variant=\"inline\"\n duration={durations[p.toolCallId]}\n summary={summaries[p.toolCallId]}\n approvalState={approvalStates[p.toolCallId]}\n onApprove={\n onApprove ? () => onApprove(p.toolCallId) : undefined\n }\n onReject={onReject ? () => onReject(p.toolCallId) : undefined}\n />\n ))}\n </div>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\n// ─── AiToolCallTimeline ──────────────────────────────────────────────────────\n\n/**\n * Horizontal timeline view of tool calls. Each tool is shown as a small\n * icon pill with a tooltip showing its name, duration, and any errors.\n *\n * @example\n * ```tsx\n * <AiToolCallTimeline parts={toolParts} durations={{ id1: 230, id2: 540 }} />\n * ```\n */\nexport function AiToolCallTimeline({\n parts,\n durations = {},\n className,\n ...props\n}: AiToolCallTimelineProps) {\n return (\n <div\n className={cn(\"flex flex-wrap items-center gap-1 py-2\", className)}\n {...props}\n >\n {parts.map((part, index) => {\n const IconComponent = getToolIcon(part);\n const displayName = getToolDisplayName(part);\n const config = getStateConfig(part);\n const duration = durations[part.toolCallId];\n const errorText = getToolErrorText(part);\n\n const tooltipLines = [\n displayName,\n duration ? `${duration}ms` : null,\n errorText ?? null,\n ]\n .filter(Boolean)\n .join(\" · \");\n\n return (\n <div key={part.toolCallId} className=\"flex items-center\">\n <Tooltip content={tooltipLines}>\n <div\n className={cn(\n \"flex cursor-default items-center gap-1.5 rounded-md px-2 py-1 transition-all\",\n config.bgClass\n )}\n >\n <IconComponent className={cn(\"size-3.5\", config.colorClass)} />\n {part.state === \"input-streaming\" ? (\n <CircleIcon\n className={cn(\"size-3 animate-pulse\", config.colorClass)}\n />\n ) : null}\n {part.state === \"input-available\" ? (\n <SpinnerGapIcon\n className={cn(\"size-3 animate-spin\", config.colorClass)}\n />\n ) : null}\n {hasToolResult(part) && !hasToolError(part) ? (\n <CheckIcon className={cn(\"size-3\", config.colorClass)} />\n ) : null}\n {hasToolError(part) ? (\n <WarningCircleIcon\n className={cn(\"size-3\", config.colorClass)}\n />\n ) : null}\n </div>\n </Tooltip>\n\n {/* Connector line */}\n {index < parts.length - 1 ? (\n <div\n className={cn(\n \"mx-0.5 h-px w-3\",\n (() => {\n const nextPart = parts[index + 1];\n if (!nextPart) return \"bg-sf-line\";\n return hasToolResult(nextPart) || isToolLoading(nextPart)\n ? \"bg-sf-success/50\"\n : \"bg-sf-line\";\n })()\n )}\n />\n ) : null}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"banner-B_6oBrsu.js","names":[],"sources":["../src/components/banner/banner.tsx"],"sourcesContent":["import { type ReactNode, isValidElement } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n/** Base styles applied to all banner variants. */\nexport const SF_BANNER_BASE_STYLES =\n \"flex w-full items-center gap-2 rounded-lg border px-4 py-1.5 text-base\";\n\n/** Banner variant definitions mapping variant names to their Tailwind classes and descriptions. */\nexport const SF_BANNER_VARIANTS = {\n variant: {\n default: {\n classes:\n \"bg-sf-info/20 border-sf-info text-sf-link selection:bg-sf-info-tint\",\n description: \"Informational banner for general messages\",\n },\n alert: {\n classes:\n \"bg-sf-warning/20 border-sf-warning text-sf-warning selection:bg-sf-warning-tint\",\n description: \"Warning banner for cautionary messages\",\n },\n error: {\n classes:\n \"bg-sf-danger/20 border-sf-danger text-sf-danger selection:bg-sf-danger-tint\",\n description: \"Error banner for critical issues\",\n },\n },\n} as const;\n\nexport const SF_BANNER_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\n// Derived types from SF_BANNER_VARIANTS\nexport type SFBannerVariant = keyof typeof SF_BANNER_VARIANTS.variant;\n\nexport interface SFBannerVariantsProps {\n /**\n * Visual style of the banner.\n * - `\"default\"` — Informational banner for general messages\n * - `\"alert\"` — Warning banner for cautionary messages\n * - `\"error\"` — Error banner for critical issues\n * @default \"default\"\n */\n variant?: SFBannerVariant;\n}\n\nexport function bannerVariants({\n variant = SF_BANNER_DEFAULT_VARIANTS.variant,\n}: SFBannerVariantsProps = {}) {\n return cn(\n // Base styles (exported as SF_BANNER_BASE_STYLES for Figma plugin)\n SF_BANNER_BASE_STYLES,\n // Apply variant styles from SF_BANNER_VARIANTS\n SF_BANNER_VARIANTS.variant[variant].classes\n );\n}\n\n// Legacy enum for backwards compatibility\nexport enum BannerVariant {\n DEFAULT,\n ALERT,\n ERROR,\n}\n\n/**\n * Banner component props.\n *\n * @example\n * ```tsx\n * <Banner>This is an informational banner.</Banner>\n * <Banner variant=\"alert\">Your session will expire soon.</Banner>\n * <Banner variant=\"error\">We couldn't save your changes.</Banner>\n * ```\n */\nexport interface BannerProps {\n /** Icon element rendered before the banner text (e.g. from `@phosphor-icons/react`). */\n icon?: ReactNode;\n /** @deprecated Use `children` instead. Will be removed in a future major version. */\n text?: string;\n /** Banner message content. Accepts strings or custom React elements. */\n children?: ReactNode;\n /**\n * Visual style of the banner.\n * - `\"default\"` — Informational blue banner for general messages\n * - `\"alert\"` — Warning yellow banner for cautionary messages\n * - `\"error\"` — Error red banner for critical issues\n * @default \"default\"\n */\n variant?: SFBannerVariant;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n}\n\n/**\n * Full-width message bar for informational, warning, or error notices.\n *\n * @example\n * ```tsx\n * <Banner variant=\"alert\" icon={<WarningCircle />}>\n * Review your billing information.\n * </Banner>\n * ```\n */\nexport function Banner({\n icon,\n children,\n text,\n variant = SF_BANNER_DEFAULT_VARIANTS.variant,\n className,\n}: BannerProps) {\n // Prefer children over deprecated text prop\n const value = children ?? text;\n\n const content = isValidElement(value) ? value : <p>{value}</p>;\n\n return (\n <div className={cn(bannerVariants({ variant }), className)}>\n {icon}\n {content}\n </div>\n );\n}\n"],"mappings":";;;;;;AAKA,IAAa,wBACX;;AAGF,IAAa,qBAAqB,EAChC,SAAS;CACP,SAAS;EACP,SACE;EACF,aAAa;EACd;CACD,OAAO;EACL,SACE;EACF,aAAa;EACd;CACD,OAAO;EACL,SACE;EACF,aAAa;EACd;CACF,EACF;AAED,IAAa,6BAA6B,EACxC,SAAS,WACV;AAgBD,SAAgB,eAAe,EAC7B,UAAU,2BAA2B,YACZ,EAAE,EAAE;AAC7B,QAAO,GAEL,uBAEA,mBAAmB,QAAQ,SAAS,QACrC;;AAIH,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,cAAA,aAAA,KAAA;AACA,eAAA,cAAA,WAAA,KAAA;AACA,eAAA,cAAA,WAAA,KAAA;;KACD;;;;;;;;;;;AAyCD,SAAgB,OAAO,EACrB,MACA,UACA,MACA,UAAU,2BAA2B,SACrC,aACc;CAEd,MAAM,QAAQ,YAAY;CAE1B,MAAM,UAAU,eAAe,MAAM,GAAG,QAAQ,oBAAC,KAAD,EAAA,UAAI,OAAU,CAAA;AAE9D,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,eAAe,EAAE,SAAS,CAAC,EAAE,UAAU;YAA1D,CACG,MACA,QACG"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"empty-D2TypIId.js","names":[],"sources":["../src/components/empty/empty.tsx"],"sourcesContent":["import { CheckIcon, CopyIcon } from \"@phosphor-icons/react\";\nimport { useState } from \"react\";\n\nimport { Button } from \"../../components/button\";\nimport { cn } from \"../../utils/cn\";\n\n/** Empty state size variant definitions mapping sizes to their Tailwind classes. */\nexport const SF_EMPTY_VARIANTS = {\n size: {\n sm: {\n classes: \"px-6 py-8 gap-4\",\n description: \"Compact empty state for smaller containers\",\n },\n base: {\n classes: \"px-10 py-16 gap-6\",\n description: \"Default empty state size\",\n },\n lg: {\n classes: \"px-12 py-20 gap-8\",\n description: \"Large empty state for prominent placement\",\n },\n },\n} as const;\n\nexport const SF_EMPTY_DEFAULT_VARIANTS = {\n size: \"base\",\n} as const;\n\nexport type SFEmptySize = keyof typeof SF_EMPTY_VARIANTS.size;\n\nexport interface SFEmptyVariantsProps {\n /**\n * Size of the empty state container.\n * - `\"sm\"` — Compact empty state for smaller containers\n * - `\"base\"` — Default empty state size\n * - `\"lg\"` — Large empty state for prominent placement\n * @default \"base\"\n */\n size?: SFEmptySize;\n}\n\nexport function emptyVariants({\n size = SF_EMPTY_DEFAULT_VARIANTS.size,\n}: SFEmptyVariantsProps = {}) {\n return cn(\n \"flex w-full flex-col items-center rounded-xl border border-sf-fill bg-sf-control text-sf-default\",\n SF_EMPTY_VARIANTS.size[size].classes\n );\n}\n\n/**\n * Empty state component props.\n *\n * @example\n * ```tsx\n * <Empty\n * icon={<PackageIcon size={48} />}\n * title=\"No packages found\"\n * description=\"Get started by installing your first package.\"\n * commandLine=\"npm install @signalflare-ai/ui\"\n * />\n * ```\n */\nexport interface EmptyProps extends SFEmptyVariantsProps {\n /** Decorative icon displayed above the title (e.g. from `@phosphor-icons/react`). */\n icon?: React.ReactNode;\n /** Primary heading text for the empty state. */\n title: string;\n /** Secondary description text displayed below the title. */\n description?: string;\n /** Shell command displayed in a copyable code block. */\n commandLine?: string;\n /** Additional content (buttons, links) rendered below the description. */\n contents?: React.ReactNode;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n}\n\n/**\n * Placeholder shown when a list, table, or page has no content to display.\n *\n * @example\n * ```tsx\n * <Empty title=\"No results found\" description=\"Try adjusting your search.\" />\n * ```\n */\nexport function Empty({\n icon,\n title,\n description,\n commandLine,\n contents,\n size = \"base\",\n className,\n}: EmptyProps) {\n const [emptyStateCopied, setEmptyStateCopied] = useState<boolean>(false);\n\n return (\n <div className={cn(emptyVariants({ size }), className)}>\n {icon}\n <h2 className=\"text-2xl font-semibold\">{title}</h2>\n\n {description && (\n <p className=\"max-w-140 text-center text-sf-strong\">{description}</p>\n )}\n\n {commandLine && (\n <div\n className={cn(\n \"group/cmd relative inline-flex h-10 max-w-8/10 transform-gpu items-center gap-2 rounded-lg font-mono shadow-sm\",\n \"bg-sf-overlay pr-2 pl-3\",\n \"transition-all duration-300 hover:border-sf-interact/80 hover:shadow-md\",\n \"border border-sf-fill/60\"\n )}\n >\n <span className=\"text-xs text-sf-inactive select-none\">$</span>\n <span className=\"no-scrollbar overflow-scroll text-[14px] whitespace-nowrap text-sf-brand\">\n {commandLine}\n </span>\n <Button\n className=\"group\"\n size=\"sm\"\n variant=\"ghost\"\n shape=\"square\"\n aria-label=\"Copy command\"\n onClick={async () => {\n setEmptyStateCopied(true);\n setTimeout(() => {\n setEmptyStateCopied(false);\n }, 1000);\n await navigator.clipboard.writeText(commandLine);\n }}\n >\n {emptyStateCopied ? (\n <CheckIcon\n size={16}\n className=\"animate-bounce-in text-sf-success\"\n />\n ) : (\n <CopyIcon\n size={16}\n className=\"text-sf-inactive group-hover:text-sf-brand\"\n />\n )}\n </Button>\n </div>\n )}\n\n {contents}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,oBAAoB,EAC/B,MAAM;CACJ,IAAI;EACF,SAAS;EACT,aAAa;EACd;CACD,MAAM;EACJ,SAAS;EACT,aAAa;EACd;CACD,IAAI;EACF,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,4BAA4B,EACvC,MAAM,QACP;AAeD,SAAgB,cAAc,EAC5B,OAAO,0BAA0B,SACT,EAAE,EAAE;AAC5B,QAAO,GACL,oGACA,kBAAkB,KAAK,MAAM,QAC9B;;;;;;;;;;AAuCH,SAAgB,MAAM,EACpB,MACA,OACA,aACA,aACA,UACA,OAAO,QACP,aACa;CACb,MAAM,CAAC,kBAAkB,uBAAuB,SAAkB,MAAM;AAExE,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,cAAc,EAAE,MAAM,CAAC,EAAE,UAAU;YAAtD;GACG;GACD,oBAAC,MAAD;IAAI,WAAU;cAA0B;IAAW,CAAA;GAElD,eACC,oBAAC,KAAD;IAAG,WAAU;cAAwC;IAAgB,CAAA;GAGtE,eACC,qBAAC,OAAD;IACE,WAAW,GACT,kHACA,2BACA,2EACA,2BACD;cANH;KAQE,oBAAC,QAAD;MAAM,WAAU;gBAAuC;MAAQ,CAAA;KAC/D,oBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA;KACP,oBAAC,QAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,OAAM;MACN,cAAW;MACX,SAAS,YAAY;AACnB,2BAAoB,KAAK;AACzB,wBAAiB;AACf,4BAAoB,MAAM;UACzB,IAAK;AACR,aAAM,UAAU,UAAU,UAAU,YAAY;;gBAGjD,mBACC,oBAAC,WAAD;OACE,MAAM;OACN,WAAU;OACV,CAAA,GAEF,oBAAC,UAAD;OACE,MAAM;OACN,WAAU;OACV,CAAA;MAEG,CAAA;KACL;;GAGP;GACG"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"stat-card-CEZscNh8.js","names":[],"sources":["../src/components/stat-card/stat-card.tsx"],"sourcesContent":["import { forwardRef, type ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n/** StatCard variant definitions (indexed by variant name) */\nexport const SF_STAT_CARD_VARIANTS = {\n size: {\n sm: {\n root: \"gap-1 p-3\",\n label: \"text-xs\",\n value: \"text-xl\",\n hint: \"text-xs\",\n },\n base: {\n root: \"gap-2 p-4\",\n label: \"text-sm\",\n value: \"text-3xl\",\n hint: \"text-xs\",\n },\n },\n} as const;\n\n/** Default variants applied when none are passed explicitly. */\nexport const SF_STAT_CARD_DEFAULT_VARIANTS = {\n size: \"base\",\n} as const;\n\nexport interface SFStatCardVariantsProps {\n /** Size of the card. Defaults to `\"base\"`. */\n size?: keyof typeof SF_STAT_CARD_VARIANTS.size;\n}\n\n/** Describes a change in the metric over a comparison period. */\nexport interface StatCardDelta {\n /** Numeric delta (e.g. `0.12` for +12% or `-4` for a drop of 4). */\n value: number;\n /**\n * Override the arrow direction. By default, direction is inferred from the\n * sign of `value`.\n */\n direction?: \"up\" | \"down\" | \"flat\";\n /** Optional context label shown after the delta (e.g. `\"vs last week\"`). */\n label?: string;\n /**\n * Whether an \"up\" direction means \"good\" (green) or \"bad\" (red). For metrics\n * like error rate, set this to `\"down\"` so downward movement is coloured\n * green.\n * @default \"up\"\n */\n positiveDirection?: \"up\" | \"down\";\n}\n\n/** Props for `StatCard` */\nexport interface StatCardProps extends SFStatCardVariantsProps {\n /** Short descriptive label (e.g. `\"Active users\"`). */\n label: ReactNode;\n /** Primary value to emphasise (pre-formatted). */\n value: ReactNode;\n /** Optional secondary text displayed under the value. */\n hint?: ReactNode;\n /** Optional change indicator (arrow + number + label). */\n delta?: StatCardDelta;\n /**\n * Optional content slot rendered on the right-hand side — typically a\n * `Sparkline` showing the metric's trend.\n */\n trend?: ReactNode;\n /** When `true`, renders a skeleton placeholder in place of value + delta. */\n loading?: boolean;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n}\n\nfunction inferDirection(\n value: number,\n override?: \"up\" | \"down\" | \"flat\"\n): \"up\" | \"down\" | \"flat\" {\n if (override) return override;\n if (value > 0) return \"up\";\n if (value < 0) return \"down\";\n return \"flat\";\n}\n\nfunction deltaColor(\n direction: \"up\" | \"down\" | \"flat\",\n positiveDirection: \"up\" | \"down\"\n): string {\n if (direction === \"flat\") return \"text-sf-subtle\";\n const isPositive = direction === positiveDirection;\n return isPositive ? \"text-sf-success\" : \"text-sf-danger\";\n}\n\nfunction deltaArrow(direction: \"up\" | \"down\" | \"flat\"): string {\n if (direction === \"up\") return \"↑\";\n if (direction === \"down\") return \"↓\";\n return \"→\";\n}\n\n/**\n * StatCard — compact KPI tile displaying a label, a primary value, and an\n * optional delta + trend sparkline.\n *\n * @example\n * ```tsx\n * <StatCard\n * label=\"Active users\"\n * value=\"12,384\"\n * delta={{ value: 0.12, label: \"vs last week\" }}\n * />\n * ```\n */\nexport const StatCard = forwardRef<HTMLDivElement, StatCardProps>(\n function StatCard(\n {\n label,\n value,\n hint,\n delta,\n trend,\n loading,\n size = SF_STAT_CARD_DEFAULT_VARIANTS.size,\n className,\n },\n ref\n ) {\n const sizeClasses = SF_STAT_CARD_VARIANTS.size[size];\n const direction = delta\n ? inferDirection(delta.value, delta.direction)\n : undefined;\n const deltaClass =\n delta && direction\n ? deltaColor(direction, delta.positiveDirection ?? \"up\")\n : undefined;\n\n return (\n <div\n ref={ref}\n className={cn(\n \"flex flex-col rounded-lg bg-sf-elevated text-sf-default\",\n sizeClasses.root,\n className\n )}\n >\n <div className={cn(\"text-sf-subtle\", sizeClasses.label)}>{label}</div>\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex flex-col gap-1\">\n {loading ? (\n <div\n aria-busy=\"true\"\n className={cn(\n \"h-8 w-24 animate-pulse rounded-md bg-sf-fill\",\n size === \"sm\" && \"h-6 w-20\"\n )}\n />\n ) : (\n <div\n className={cn(\n \"font-semibold tabular-nums text-sf-strong\",\n sizeClasses.value\n )}\n >\n {value}\n </div>\n )}\n {delta && !loading ? (\n <div\n className={cn(\n \"flex items-baseline gap-1 tabular-nums\",\n sizeClasses.hint\n )}\n >\n <span className={cn(\"font-medium\", deltaClass)}>\n {deltaArrow(direction!)}{\" \"}\n {Math.abs(delta.value).toLocaleString(undefined, {\n maximumFractionDigits: 2,\n })}\n </span>\n {delta.label ? (\n <span className=\"text-sf-subtle\">{delta.label}</span>\n ) : null}\n </div>\n ) : null}\n {hint && !loading ? (\n <div className={cn(\"text-sf-subtle\", sizeClasses.hint)}>\n {hint}\n </div>\n ) : null}\n </div>\n {trend ? (\n <div className=\"pointer-events-none shrink-0\">{trend}</div>\n ) : null}\n </div>\n </div>\n );\n }\n);\n\nStatCard.displayName = \"StatCard\";\n"],"mappings":";;;;;;AAKA,IAAa,wBAAwB,EACnC,MAAM;CACJ,IAAI;EACF,MAAM;EACN,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACD,MAAM;EACJ,MAAM;EACN,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACF,EACF;;AAGD,IAAa,gCAAgC,EAC3C,MAAM,QACP;AAgDD,SAAS,eACP,OACA,UACwB;AACxB,KAAI,SAAU,QAAO;AACrB,KAAI,QAAQ,EAAG,QAAO;AACtB,KAAI,QAAQ,EAAG,QAAO;AACtB,QAAO;;AAGT,SAAS,WACP,WACA,mBACQ;AACR,KAAI,cAAc,OAAQ,QAAO;AAEjC,QADmB,cAAc,oBACb,oBAAoB;;AAG1C,SAAS,WAAW,WAA2C;AAC7D,KAAI,cAAc,KAAM,QAAO;AAC/B,KAAI,cAAc,OAAQ,QAAO;AACjC,QAAO;;;;;;;;;;;;;;;AAgBT,IAAa,WAAW,WACtB,SAAS,SACP,EACE,OACA,OACA,MACA,OACA,OACA,SACA,OAAO,8BAA8B,MACrC,aAEF,KACA;CACA,MAAM,cAAc,sBAAsB,KAAK;CAC/C,MAAM,YAAY,QACd,eAAe,MAAM,OAAO,MAAM,UAAU,GAC5C,KAAA;CACJ,MAAM,aACJ,SAAS,YACL,WAAW,WAAW,MAAM,qBAAqB,KAAK,GACtD,KAAA;AAEN,QACE,qBAAC,OAAD;EACO;EACL,WAAW,GACT,2DACA,YAAY,MACZ,UACD;YANH,CAQE,oBAAC,OAAD;GAAK,WAAW,GAAG,kBAAkB,YAAY,MAAM;aAAG;GAAY,CAAA,EACtE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,OAAD;IAAK,WAAU;cAAf;KACG,UACC,oBAAC,OAAD;MACE,aAAU;MACV,WAAW,GACT,gDACA,SAAS,QAAQ,WAClB;MACD,CAAA,GAEF,oBAAC,OAAD;MACE,WAAW,GACT,6CACA,YAAY,MACb;gBAEA;MACG,CAAA;KAEP,SAAS,CAAC,UACT,qBAAC,OAAD;MACE,WAAW,GACT,0CACA,YAAY,KACb;gBAJH,CAME,qBAAC,QAAD;OAAM,WAAW,GAAG,eAAe,WAAW;iBAA9C;QACG,WAAW,UAAW;QAAE;QACxB,KAAK,IAAI,MAAM,MAAM,CAAC,eAAe,KAAA,GAAW,EAC/C,uBAAuB,GACxB,CAAC;QACG;UACN,MAAM,QACL,oBAAC,QAAD;OAAM,WAAU;iBAAkB,MAAM;OAAa,CAAA,GACnD,KACA;UACJ;KACH,QAAQ,CAAC,UACR,oBAAC,OAAD;MAAK,WAAW,GAAG,kBAAkB,YAAY,KAAK;gBACnD;MACG,CAAA,GACJ;KACA;OACL,QACC,oBAAC,OAAD;IAAK,WAAU;cAAgC;IAAY,CAAA,GACzD,KACA;KACF;;EAGX;AAED,SAAS,cAAc"}