@signalflare-ai/ui 0.5.0 → 1.0.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 (316) hide show
  1. package/CHANGELOG.md +117 -1
  2. package/ai/USAGE.md +64 -0
  3. package/ai/component-registry.json +492 -618
  4. package/ai/component-registry.md +167 -85
  5. package/ai/schemas.ts +545 -102
  6. package/bin/sf.js +2 -3
  7. package/dist/.build-complete +1 -1
  8. package/dist/ai/schemas.d.ts +1659 -5532
  9. package/dist/ai/schemas.d.ts.map +1 -1
  10. package/dist/{ai-actions-DG1dhDMP.js → ai-actions-DSVeQn4e.js} +1 -1
  11. package/dist/{ai-actions-DG1dhDMP.js.map → ai-actions-DSVeQn4e.js.map} +1 -1
  12. package/dist/{ai-agent-card-BbtL4NII.js → ai-agent-card-BXHwhWAU.js} +1 -1
  13. package/dist/{ai-agent-card-BbtL4NII.js.map → ai-agent-card-BXHwhWAU.js.map} +1 -1
  14. package/dist/{ai-approval-Mb7-BY6i.js → ai-approval-aa0qvjFN.js} +1 -1
  15. package/dist/{ai-approval-Mb7-BY6i.js.map → ai-approval-aa0qvjFN.js.map} +1 -1
  16. package/dist/{ai-code-block-BI_z0UVR.js → ai-code-block-BgtIxtZZ.js} +1 -1
  17. package/dist/{ai-code-block-BI_z0UVR.js.map → ai-code-block-BgtIxtZZ.js.map} +1 -1
  18. package/dist/{ai-conversation-DYtExcrw.js → ai-conversation-CArP7C8K.js} +1 -1
  19. package/dist/{ai-conversation-DYtExcrw.js.map → ai-conversation-CArP7C8K.js.map} +1 -1
  20. package/dist/{ai-info-banner-BpzauUAY.js → ai-info-banner-uFxHHwBA.js} +1 -1
  21. package/dist/{ai-info-banner-BpzauUAY.js.map → ai-info-banner-uFxHHwBA.js.map} +1 -1
  22. package/dist/{ai-message-CV8SBoHM.js → ai-message-BjnFznXy.js} +1 -1
  23. package/dist/{ai-message-CV8SBoHM.js.map → ai-message-BjnFznXy.js.map} +1 -1
  24. package/dist/{ai-mission-header-ByYkJ6YP.js → ai-mission-header-08__gULL.js} +1 -1
  25. package/dist/{ai-mission-header-ByYkJ6YP.js.map → ai-mission-header-08__gULL.js.map} +1 -1
  26. package/dist/ai-part-group-DBtgTgAn.js +277 -0
  27. package/dist/ai-part-group-DBtgTgAn.js.map +1 -0
  28. package/dist/ai-prompt-input-Dy1LfxPk.js +1541 -0
  29. package/dist/ai-prompt-input-Dy1LfxPk.js.map +1 -0
  30. package/dist/{ai-question-Dp1g9k2o.js → ai-question-CHHoDJMg.js} +1 -1
  31. package/dist/{ai-question-Dp1g9k2o.js.map → ai-question-CHHoDJMg.js.map} +1 -1
  32. package/dist/{ai-reasoning-UAmNx_LD.js → ai-reasoning-CnL6ZSr5.js} +84 -5
  33. package/dist/ai-reasoning-CnL6ZSr5.js.map +1 -0
  34. package/dist/{ai-response-BWoVsNQG.js → ai-response-BEUg3xvd.js} +13 -16
  35. package/dist/ai-response-BEUg3xvd.js.map +1 -0
  36. package/dist/{ai-shimmer-BpOmfonu.js → ai-shimmer-By5_L05p.js} +1 -1
  37. package/dist/{ai-shimmer-BpOmfonu.js.map → ai-shimmer-By5_L05p.js.map} +1 -1
  38. package/dist/{ai-status-badge-WhbKVeqn.js → ai-status-badge-BGYGWYF6.js} +1 -1
  39. package/dist/{ai-status-badge-WhbKVeqn.js.map → ai-status-badge-BGYGWYF6.js.map} +1 -1
  40. package/dist/{ai-streaming-text-ClL7FwvD.js → ai-streaming-text-CMfoThV0.js} +1 -1
  41. package/dist/{ai-streaming-text-ClL7FwvD.js.map → ai-streaming-text-CMfoThV0.js.map} +1 -1
  42. package/dist/{ai-subagent-BruGN1UE.js → ai-subagent-DcPRqkAA.js} +1 -1
  43. package/dist/{ai-subagent-BruGN1UE.js.map → ai-subagent-DcPRqkAA.js.map} +1 -1
  44. package/dist/{ai-suggestion-CNsCZj5P.js → ai-suggestion-MgeCg5Ar.js} +1 -1
  45. package/dist/{ai-suggestion-CNsCZj5P.js.map → ai-suggestion-MgeCg5Ar.js.map} +1 -1
  46. package/dist/{ai-task-list-B9CpMDYN.js → ai-task-list-Da9zIm00.js} +9 -12
  47. package/dist/ai-task-list-Da9zIm00.js.map +1 -0
  48. package/dist/{ai-timeline-Bb5ntsr3.js → ai-timeline-Cwu045IR.js} +1 -1
  49. package/dist/ai-timeline-Cwu045IR.js.map +1 -0
  50. package/dist/{ai-tool-BGH8nQ_D.js → ai-tool-Cn1O4xjP.js} +168 -11
  51. package/dist/ai-tool-Cn1O4xjP.js.map +1 -0
  52. package/dist/{ai-usage-bar-BI-p-JBk.js → ai-usage-bar-DjS12DMp.js} +1 -1
  53. package/dist/{ai-usage-bar-BI-p-JBk.js.map → ai-usage-bar-DjS12DMp.js.map} +1 -1
  54. package/dist/catalog.js +3 -3
  55. package/dist/catalog.js.map +1 -1
  56. package/dist/{chart-Bes4MN3C.js → chart-BK3sVPnD.js} +442 -248
  57. package/dist/chart-BK3sVPnD.js.map +1 -0
  58. package/dist/{checkbox-CPX7lBaU.js → checkbox-DYhUmZNw.js} +4 -4
  59. package/dist/checkbox-DYhUmZNw.js.map +1 -0
  60. package/dist/{clipboard-text-92YeCybc.js → clipboard-text-ssybngLw.js} +2 -2
  61. package/dist/{clipboard-text-92YeCybc.js.map → clipboard-text-ssybngLw.js.map} +1 -1
  62. package/dist/{code-DE1Yy1Cu.js → code-Cx-QSoOT.js} +2 -2
  63. package/dist/{code-DE1Yy1Cu.js.map → code-Cx-QSoOT.js.map} +1 -1
  64. package/dist/{combobox-B0bLdsX8.js → combobox-C0iW6a0r.js} +2 -2
  65. package/dist/{combobox-B0bLdsX8.js.map → combobox-C0iW6a0r.js.map} +1 -1
  66. package/dist/command-line/cli.js +22 -27
  67. package/dist/{command-palette-CBTY8EiF.js → command-palette-DGzioeki.js} +2 -2
  68. package/dist/command-palette-DGzioeki.js.map +1 -0
  69. package/dist/components/ai-actions.js +1 -1
  70. package/dist/components/ai-agent-card.js +1 -1
  71. package/dist/components/ai-approval.js +1 -1
  72. package/dist/components/ai-code-block.js +1 -1
  73. package/dist/components/ai-conversation.js +1 -1
  74. package/dist/components/ai-info-banner.js +1 -1
  75. package/dist/components/ai-message.js +1 -1
  76. package/dist/components/ai-mission-header.js +1 -1
  77. package/dist/components/ai-part-group.js +3 -0
  78. package/dist/components/ai-prompt-input.js +2 -2
  79. package/dist/components/ai-question.js +1 -1
  80. package/dist/components/ai-reasoning.js +1 -1
  81. package/dist/components/ai-response.js +1 -1
  82. package/dist/components/ai-shimmer.js +1 -1
  83. package/dist/components/ai-status-badge.js +1 -1
  84. package/dist/components/ai-streaming-text.js +1 -1
  85. package/dist/components/ai-subagent.js +1 -1
  86. package/dist/components/ai-suggestion.js +1 -1
  87. package/dist/components/ai-task-list.js +1 -1
  88. package/dist/components/ai-timeline.js +1 -1
  89. package/dist/components/ai-tool.js +1 -1
  90. package/dist/components/ai-usage-bar.js +1 -1
  91. package/dist/components/chart.js +3 -2
  92. package/dist/components/checkbox.js +1 -1
  93. package/dist/components/clipboard-text.js +1 -1
  94. package/dist/components/code.js +1 -1
  95. package/dist/components/combobox.js +1 -1
  96. package/dist/components/command-palette.js +1 -1
  97. package/dist/components/data-grid.js +1 -1
  98. package/dist/components/date-picker.js +1 -1
  99. package/dist/components/dropdown.js +1 -1
  100. package/dist/components/filters.js +1 -1
  101. package/dist/components/input.js +2 -2
  102. package/dist/components/link.js +2 -2
  103. package/dist/components/link.js.map +1 -1
  104. package/dist/components/pagination.js +1 -1
  105. package/dist/components/radio.js +1 -1
  106. package/dist/components/select.js +1 -1
  107. package/dist/components/sensitive-input.js +1 -1
  108. package/dist/components/sidebar.js +1 -1
  109. package/dist/components/sparkline.js +3 -0
  110. package/dist/components/stat-card.js +3 -0
  111. package/dist/components/table.js +1 -1
  112. package/dist/components/text-roll.js +3 -0
  113. package/dist/components/theme-toggle.js +1 -1
  114. package/dist/components/use-agent-harness.js +1 -1
  115. package/dist/{data-grid-UJ9ja5cu.js → data-grid-CG76N_hK.js} +6 -6
  116. package/dist/data-grid-CG76N_hK.js.map +1 -0
  117. package/dist/{date-picker-ebekkC3R.js → date-picker-Dqg9L4xu.js} +2 -2
  118. package/dist/{date-picker-ebekkC3R.js.map → date-picker-Dqg9L4xu.js.map} +1 -1
  119. package/dist/{dist-BNlyONdD.js → dist-1-gcEL2L.js} +224 -135
  120. package/dist/dist-1-gcEL2L.js.map +1 -0
  121. package/dist/{dropdown-J5T4pHaR.js → dropdown-qnEYRFXZ.js} +2 -2
  122. package/dist/{dropdown-J5T4pHaR.js.map → dropdown-qnEYRFXZ.js.map} +1 -1
  123. package/dist/echart-DURZEyai.js +314 -0
  124. package/dist/echart-DURZEyai.js.map +1 -0
  125. package/dist/{filters-BdBogf7D.js → filters-Bw_U6ZTx.js} +5 -5
  126. package/dist/filters-Bw_U6ZTx.js.map +1 -0
  127. package/dist/flow-BRsYUCJa.js.map +1 -1
  128. package/dist/genui.js +2 -2
  129. package/dist/genui.js.map +1 -1
  130. package/dist/index.js +45 -42
  131. package/dist/index.js.map +1 -1
  132. package/dist/{input-BxQAnXki.js → input-DXYUjGgD.js} +2 -2
  133. package/dist/{input-BxQAnXki.js.map → input-DXYUjGgD.js.map} +1 -1
  134. package/dist/{input-Cn25I4o5.js → input-DddtBN-g.js} +2 -2
  135. package/dist/{input-Cn25I4o5.js.map → input-DddtBN-g.js.map} +1 -1
  136. package/dist/layer-card-BME0eljh.js.map +1 -1
  137. package/dist/{pagination-C_YqCy8l.js → pagination-BVqdlONY.js} +2 -2
  138. package/dist/{pagination-C_YqCy8l.js.map → pagination-BVqdlONY.js.map} +1 -1
  139. package/dist/primitives/otp-field.js +2 -0
  140. package/dist/primitives.js +1 -0
  141. package/dist/{radio-B7zg1wUI.js → radio-BNSwOt3B.js} +3 -3
  142. package/dist/radio-BNSwOt3B.js.map +1 -0
  143. package/dist/{select-9p721G00.js → select-1w2aebGQ.js} +2 -2
  144. package/dist/select-1w2aebGQ.js.map +1 -0
  145. package/dist/{sensitive-input-D5je2NLl.js → sensitive-input-82Cez3vj.js} +2 -2
  146. package/dist/{sensitive-input-D5je2NLl.js.map → sensitive-input-82Cez3vj.js.map} +1 -1
  147. package/dist/{sidebar-DOwBrq57.js → sidebar-CAsCmSpM.js} +3 -3
  148. package/dist/sidebar-CAsCmSpM.js.map +1 -0
  149. package/dist/sparkline-DdbeM4Ai.js +108 -0
  150. package/dist/sparkline-DdbeM4Ai.js.map +1 -0
  151. package/dist/src/blocks/agent-harness/agent-harness.d.ts +30 -9
  152. package/dist/src/blocks/agent-harness/agent-harness.d.ts.map +1 -1
  153. package/dist/src/blocks/agent-harness/agent-harness.stories.tsx +114 -0
  154. package/dist/src/blocks/agent-harness/agent-harness.tsx +144 -63
  155. package/dist/src/blocks/commander/commander.stories.tsx +31 -0
  156. package/dist/src/blocks/dashboard-grid/dashboard-grid.d.ts +48 -0
  157. package/dist/src/blocks/dashboard-grid/dashboard-grid.d.ts.map +1 -0
  158. package/dist/src/blocks/dashboard-grid/dashboard-grid.stories.tsx +19 -0
  159. package/dist/src/blocks/dashboard-grid/dashboard-grid.tsx +110 -0
  160. package/dist/src/blocks/dashboard-grid/index.d.ts +2 -0
  161. package/dist/src/blocks/dashboard-grid/index.d.ts.map +1 -0
  162. package/dist/src/blocks/delete-resource/delete-resource.stories.tsx +31 -0
  163. package/dist/src/blocks/map-block/map-block.stories.tsx +41 -0
  164. package/dist/src/blocks/metrics-overview/index.d.ts +2 -0
  165. package/dist/src/blocks/metrics-overview/index.d.ts.map +1 -0
  166. package/dist/src/blocks/metrics-overview/metrics-overview.d.ts +67 -0
  167. package/dist/src/blocks/metrics-overview/metrics-overview.d.ts.map +1 -0
  168. package/dist/src/blocks/metrics-overview/metrics-overview.stories.tsx +26 -0
  169. package/dist/src/blocks/metrics-overview/metrics-overview.tsx +139 -0
  170. package/dist/src/blocks/page-header/page-header.stories.tsx +56 -0
  171. package/dist/src/blocks/resource-list/resource-list.stories.tsx +31 -0
  172. package/dist/src/catalog/catalog.d.ts +1 -1
  173. package/dist/src/catalog/index.d.ts +1 -1
  174. package/dist/src/catalog/types.d.ts +1 -1
  175. package/dist/src/command-line/build-cli.d.ts +1 -1
  176. package/dist/src/command-line/cli.d.ts +1 -1
  177. package/dist/src/command-line/commands/add.d.ts +1 -1
  178. package/dist/src/command-line/commands/add.d.ts.map +1 -1
  179. package/dist/src/command-line/commands/ai.d.ts.map +1 -1
  180. package/dist/src/command-line/commands/blocks.d.ts +1 -1
  181. package/dist/src/command-line/commands/blocks.d.ts.map +1 -1
  182. package/dist/src/command-line/commands/doc.d.ts +1 -1
  183. package/dist/src/command-line/commands/doc.d.ts.map +1 -1
  184. package/dist/src/command-line/commands/ls.d.ts +1 -1
  185. package/dist/src/command-line/commands/ls.d.ts.map +1 -1
  186. package/dist/src/command-line/commands/migrate.d.ts +3 -3
  187. package/dist/src/command-line/commands/migrate.d.ts.map +1 -1
  188. package/dist/src/command-line/utils/config.d.ts +1 -1
  189. package/dist/src/command-line/utils/transformer.d.ts +1 -1
  190. package/dist/src/components/ai-part-group/ai-part-group.d.ts +134 -0
  191. package/dist/src/components/ai-part-group/ai-part-group.d.ts.map +1 -0
  192. package/dist/src/components/ai-part-group/index.d.ts +2 -0
  193. package/dist/src/components/ai-part-group/index.d.ts.map +1 -0
  194. package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts +196 -7
  195. package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
  196. package/dist/src/components/ai-prompt-input/controller.d.ts +41 -0
  197. package/dist/src/components/ai-prompt-input/controller.d.ts.map +1 -0
  198. package/dist/src/components/ai-prompt-input/index.d.ts +3 -1
  199. package/dist/src/components/ai-prompt-input/index.d.ts.map +1 -1
  200. package/dist/src/components/ai-prompt-input/types.d.ts +86 -0
  201. package/dist/src/components/ai-prompt-input/types.d.ts.map +1 -0
  202. package/dist/src/components/ai-reasoning/ai-reasoning.d.ts +17 -1
  203. package/dist/src/components/ai-reasoning/ai-reasoning.d.ts.map +1 -1
  204. package/dist/src/components/ai-response/ai-response.d.ts +1 -2
  205. package/dist/src/components/ai-response/ai-response.d.ts.map +1 -1
  206. package/dist/src/components/ai-task-list/ai-task-list.d.ts.map +1 -1
  207. package/dist/src/components/ai-tool/ai-tool.d.ts +17 -1
  208. package/dist/src/components/ai-tool/ai-tool.d.ts.map +1 -1
  209. package/dist/src/components/chart/area-chart.d.ts +69 -0
  210. package/dist/src/components/chart/area-chart.d.ts.map +1 -0
  211. package/dist/src/components/chart/bar-chart.d.ts +60 -0
  212. package/dist/src/components/chart/bar-chart.d.ts.map +1 -0
  213. package/dist/src/components/chart/color.d.ts +56 -0
  214. package/dist/src/components/chart/color.d.ts.map +1 -1
  215. package/dist/src/components/chart/echart.d.ts.map +1 -1
  216. package/dist/src/components/chart/index.d.ts +5 -0
  217. package/dist/src/components/chart/index.d.ts.map +1 -1
  218. package/dist/src/components/chart/pie-chart.d.ts +64 -0
  219. package/dist/src/components/chart/pie-chart.d.ts.map +1 -0
  220. package/dist/src/components/chart/scatter-chart.d.ts +57 -0
  221. package/dist/src/components/chart/scatter-chart.d.ts.map +1 -0
  222. package/dist/src/components/chart/stacked-bar-chart.d.ts +59 -0
  223. package/dist/src/components/chart/stacked-bar-chart.d.ts.map +1 -0
  224. package/dist/src/components/chart/timeseries-chart.d.ts.map +1 -1
  225. package/dist/src/components/code/code.d.ts +1 -1
  226. package/dist/src/components/command-palette/command-palette.d.ts +1 -1
  227. package/dist/src/components/data-grid/data-grid.d.ts.map +1 -1
  228. package/dist/src/components/data-grid/index.d.ts +1 -1
  229. package/dist/src/components/data-grid/index.d.ts.map +1 -1
  230. package/dist/src/components/data-grid/types.d.ts.map +1 -1
  231. package/dist/src/components/date-picker/date-picker.d.ts +2 -2
  232. package/dist/src/components/filters/filters.d.ts.map +1 -1
  233. package/dist/src/components/filters/helpers.d.ts.map +1 -1
  234. package/dist/src/components/filters/types.d.ts.map +1 -1
  235. package/dist/src/components/layer-card/layer-card.d.ts +1 -1
  236. package/dist/src/components/link/link.d.ts +2 -2
  237. package/dist/src/components/radio/radio.d.ts +1 -1
  238. package/dist/src/components/select/select.d.ts +1 -1
  239. package/dist/src/components/sidebar/sidebar.d.ts +2 -2
  240. package/dist/src/components/sparkline/index.d.ts +2 -0
  241. package/dist/src/components/sparkline/index.d.ts.map +1 -0
  242. package/dist/src/components/sparkline/sparkline.d.ts +52 -0
  243. package/dist/src/components/sparkline/sparkline.d.ts.map +1 -0
  244. package/dist/src/components/stat-card/index.d.ts +2 -0
  245. package/dist/src/components/{ai-loader → stat-card}/index.d.ts.map +1 -1
  246. package/dist/src/components/stat-card/stat-card.d.ts +80 -0
  247. package/dist/src/components/stat-card/stat-card.d.ts.map +1 -0
  248. package/dist/src/components/text-roll/index.d.ts +2 -0
  249. package/dist/src/components/text-roll/index.d.ts.map +1 -0
  250. package/dist/src/components/text-roll/text-roll.d.ts +49 -0
  251. package/dist/src/components/text-roll/text-roll.d.ts.map +1 -0
  252. package/dist/src/components/toast/toast.d.ts.map +1 -1
  253. package/dist/src/components/use-agent-harness/use-agent-harness.d.ts +2 -2
  254. package/dist/src/genui/genui.d.ts.map +1 -1
  255. package/dist/src/index.d.ts +5 -2
  256. package/dist/src/index.d.ts.map +1 -1
  257. package/dist/src/primitives/index.d.ts +1 -0
  258. package/dist/src/primitives/index.d.ts.map +1 -1
  259. package/dist/src/primitives/otp-field.d.ts +13 -0
  260. package/dist/src/primitives/otp-field.d.ts.map +1 -0
  261. package/dist/src/registry/index.d.ts +1 -1
  262. package/dist/src/registry/types.d.ts +1 -1
  263. package/dist/stat-card-CEZscNh8.js +103 -0
  264. package/dist/stat-card-CEZscNh8.js.map +1 -0
  265. package/dist/styles/sf-binding.css +20 -62
  266. package/dist/styles/sf-standalone.css +2 -2
  267. package/dist/styles/shadcn.css +120 -0
  268. package/dist/styles/theme-blue-tint.css +98 -0
  269. package/dist/styles/theme-fedramp.css +3 -12
  270. package/dist/styles/theme-minimal.css +26 -104
  271. package/dist/styles/theme-sf.css +96 -114
  272. package/dist/{table-CIMx0Oq0.js → table-Rv4JMy0B.js} +2 -2
  273. package/dist/{table-CIMx0Oq0.js.map → table-Rv4JMy0B.js.map} +1 -1
  274. package/dist/text-roll-BZ3I1umc.js +79 -0
  275. package/dist/text-roll-BZ3I1umc.js.map +1 -0
  276. package/dist/{theme-toggle-Dpgnoj_Q.js → theme-toggle-Bhu681D7.js} +1 -1
  277. package/dist/{theme-toggle-Dpgnoj_Q.js.map → theme-toggle-Bhu681D7.js.map} +1 -1
  278. package/dist/{use-agent-harness-DZzcn96L.js → use-agent-harness-BMyF8pTq.js} +5 -5
  279. package/dist/use-agent-harness-BMyF8pTq.js.map +1 -0
  280. package/package.json +48 -19
  281. package/scripts/component-registry/discovery.ts +2 -2
  282. package/scripts/component-registry/index.ts +1 -1
  283. package/scripts/component-registry/props-filter.ts +1 -1
  284. package/scripts/component-registry/utils.ts +5 -5
  285. package/scripts/component-registry/variant-parser.ts +124 -19
  286. package/scripts/convert-demos-to-stories.ts +253 -0
  287. package/scripts/css-build.ts +4 -3
  288. package/scripts/generate-primitives.ts +11 -3
  289. package/scripts/theme-generator/config.ts +339 -71
  290. package/scripts/theme-generator/generate-css.ts +17 -3
  291. package/scripts/theme-generator/index.ts +3 -3
  292. package/scripts/theme-generator/migrate.ts +1 -1
  293. package/dist/ai-loader-Cr3eQkNS.js +0 -134
  294. package/dist/ai-loader-Cr3eQkNS.js.map +0 -1
  295. package/dist/ai-prompt-input-Bo1YuJly.js +0 -769
  296. package/dist/ai-prompt-input-Bo1YuJly.js.map +0 -1
  297. package/dist/ai-reasoning-UAmNx_LD.js.map +0 -1
  298. package/dist/ai-response-BWoVsNQG.js.map +0 -1
  299. package/dist/ai-task-list-B9CpMDYN.js.map +0 -1
  300. package/dist/ai-timeline-Bb5ntsr3.js.map +0 -1
  301. package/dist/ai-tool-BGH8nQ_D.js.map +0 -1
  302. package/dist/chart-Bes4MN3C.js.map +0 -1
  303. package/dist/checkbox-CPX7lBaU.js.map +0 -1
  304. package/dist/command-palette-CBTY8EiF.js.map +0 -1
  305. package/dist/components/ai-loader.js +0 -3
  306. package/dist/data-grid-UJ9ja5cu.js.map +0 -1
  307. package/dist/dist-BNlyONdD.js.map +0 -1
  308. package/dist/filters-BdBogf7D.js.map +0 -1
  309. package/dist/radio-B7zg1wUI.js.map +0 -1
  310. package/dist/select-9p721G00.js.map +0 -1
  311. package/dist/sidebar-DOwBrq57.js.map +0 -1
  312. package/dist/src/components/ai-loader/ai-loader.d.ts +0 -44
  313. package/dist/src/components/ai-loader/ai-loader.d.ts.map +0 -1
  314. package/dist/src/components/ai-loader/index.d.ts +0 -2
  315. package/dist/styles/theme-navigator.css +0 -137
  316. package/dist/use-agent-harness-DZzcn96L.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../ai/schemas.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiExB,CAAC;AAMX;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQ9B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAE5D;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGvB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAMlD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAMjF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAErE;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,g0BAAi0B,CAAC"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../ai/schemas.ts"],"names":[],"mappings":"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"}
@@ -54,4 +54,4 @@ function AiAction({ tooltip, label, children, className, variant = "ghost", size
54
54
  //#endregion
55
55
  export { SF_AI_ACTIONS_VARIANTS as i, AiActions as n, SF_AI_ACTIONS_DEFAULT_VARIANTS as r, AiAction as t };
56
56
 
57
- //# sourceMappingURL=ai-actions-DG1dhDMP.js.map
57
+ //# sourceMappingURL=ai-actions-DSVeQn4e.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-actions-DG1dhDMP.js","names":[],"sources":["../src/components/ai-actions/ai-actions.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\nimport { Tooltip } from \"../tooltip\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_ACTIONS_VARIANTS = {} as const;\nexport const SF_AI_ACTIONS_DEFAULT_VARIANTS = {} as const;\n\n// ─── AiActions ───────────────────────────────────────────────────────────────\n\nexport type AiActionsProps = ComponentProps<\"div\">;\n\n/**\n * Container for a row of AI message action buttons.\n *\n * @example\n * ```tsx\n * <AiActions>\n * <AiAction tooltip=\"Copy\" onClick={handleCopy}><CopyIcon /></AiAction>\n * <AiAction tooltip=\"Retry\" onClick={handleRetry}><ArrowCounterClockwiseIcon /></AiAction>\n * </AiActions>\n * ```\n */\nexport function AiActions({ className, children, ...props }: AiActionsProps) {\n return (\n <div className={cn(\"flex items-center gap-1\", className)} {...props}>\n {children}\n </div>\n );\n}\n\n// ─── AiAction ────────────────────────────────────────────────────────────────\n\nexport type AiActionProps = ComponentProps<typeof Button> & {\n /** Tooltip text shown on hover. Also used as the accessible label. */\n tooltip?: string;\n /** Accessible label (overrides tooltip for sr-only text). */\n label?: string;\n};\n\n/**\n * A single icon button action for AI message toolbars, with optional tooltip.\n *\n * @example\n * ```tsx\n * <AiAction tooltip=\"Copy message\" onClick={handleCopy}>\n * <CopyIcon />\n * </AiAction>\n * ```\n */\nexport function AiAction({\n tooltip,\n label,\n children,\n className,\n variant = \"ghost\",\n size = \"sm\",\n ...props\n}: AiActionProps) {\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\n return button;\n}\n"],"mappings":";;;;;;AAUA,IAAa,yBAAyB,EAAE;AACxC,IAAa,iCAAiC,EAAE;;;;;;;;;;;;AAiBhD,SAAgB,UAAU,EAAE,WAAW,UAAU,GAAG,SAAyB;AAC3E,QACE,oBAAC,OAAD;EAAK,WAAW,GAAG,2BAA2B,UAAU;EAAE,GAAI;EAC3D;EACG,CAAA;;;;;;;;;;;;AAuBV,SAAgB,SAAS,EACvB,SACA,OACA,UACA,WACA,UAAU,SACV,OAAO,MACP,GAAG,SACa;CAChB,MAAM,SACJ,qBAAC,QAAD;EACE,WAAW,GAAG,wCAAwC,UAAU;EAC1D;EACG;EACT,GAAI;YAJN,CAMG,WACC,SAAS,YACT,oBAAC,QAAD;GAAM,WAAU;aAAW,SAAS;GAAe,CAAA,CAE9C;;AAGX,KAAI,QACF,QAAO,oBAAC,SAAD;EAAS,SAAS;YAAU;EAAiB,CAAA;AAGtD,QAAO"}
1
+ {"version":3,"file":"ai-actions-DSVeQn4e.js","names":[],"sources":["../src/components/ai-actions/ai-actions.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\nimport { Tooltip } from \"../tooltip\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_ACTIONS_VARIANTS = {} as const;\nexport const SF_AI_ACTIONS_DEFAULT_VARIANTS = {} as const;\n\n// ─── AiActions ───────────────────────────────────────────────────────────────\n\nexport type AiActionsProps = ComponentProps<\"div\">;\n\n/**\n * Container for a row of AI message action buttons.\n *\n * @example\n * ```tsx\n * <AiActions>\n * <AiAction tooltip=\"Copy\" onClick={handleCopy}><CopyIcon /></AiAction>\n * <AiAction tooltip=\"Retry\" onClick={handleRetry}><ArrowCounterClockwiseIcon /></AiAction>\n * </AiActions>\n * ```\n */\nexport function AiActions({ className, children, ...props }: AiActionsProps) {\n return (\n <div className={cn(\"flex items-center gap-1\", className)} {...props}>\n {children}\n </div>\n );\n}\n\n// ─── AiAction ────────────────────────────────────────────────────────────────\n\nexport type AiActionProps = ComponentProps<typeof Button> & {\n /** Tooltip text shown on hover. Also used as the accessible label. */\n tooltip?: string;\n /** Accessible label (overrides tooltip for sr-only text). */\n label?: string;\n};\n\n/**\n * A single icon button action for AI message toolbars, with optional tooltip.\n *\n * @example\n * ```tsx\n * <AiAction tooltip=\"Copy message\" onClick={handleCopy}>\n * <CopyIcon />\n * </AiAction>\n * ```\n */\nexport function AiAction({\n tooltip,\n label,\n children,\n className,\n variant = \"ghost\",\n size = \"sm\",\n ...props\n}: AiActionProps) {\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\n return button;\n}\n"],"mappings":";;;;;;AAUA,IAAa,yBAAyB,EAAE;AACxC,IAAa,iCAAiC,EAAE;;;;;;;;;;;;AAiBhD,SAAgB,UAAU,EAAE,WAAW,UAAU,GAAG,SAAyB;AAC3E,QACE,oBAAC,OAAD;EAAK,WAAW,GAAG,2BAA2B,UAAU;EAAE,GAAI;EAC3D;EACG,CAAA;;;;;;;;;;;;AAuBV,SAAgB,SAAS,EACvB,SACA,OACA,UACA,WACA,UAAU,SACV,OAAO,MACP,GAAG,SACa;CAChB,MAAM,SACJ,qBAAC,QAAD;EACE,WAAW,GAAG,wCAAwC,UAAU;EAC1D;EACG;EACT,GAAI;YAJN,CAMG,WACC,SAAS,YACT,oBAAC,QAAD;GAAM,WAAU;aAAW,SAAS;GAAe,CAAA,CAE9C;;AAGX,KAAI,QACF,QAAO,oBAAC,SAAD;EAAS,SAAS;YAAU;EAAiB,CAAA;AAGtD,QAAO"}
@@ -168,4 +168,4 @@ AiAgentCard.displayName = "AiAgentCard";
168
168
  //#endregion
169
169
  export { SF_AI_AGENT_CARD_DEFAULT_VARIANTS as n, SF_AI_AGENT_CARD_VARIANTS as r, AiAgentCard as t };
170
170
 
171
- //# sourceMappingURL=ai-agent-card-BbtL4NII.js.map
171
+ //# sourceMappingURL=ai-agent-card-BXHwhWAU.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-agent-card-BbtL4NII.js","names":[],"sources":["../src/components/ai-agent-card/ai-agent-card.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n BrainIcon,\n CheckCircleIcon,\n CircleIcon,\n CodeIcon,\n MagnifyingGlassIcon,\n RobotIcon,\n SpinnerGapIcon,\n WrenchIcon,\n XCircleIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps, ElementType } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_AGENT_CARD_VARIANTS = {\n status: {\n idle: { classes: \"\", description: \"Agent is idle, not yet started\" },\n running: { classes: \"\", description: \"Agent is actively working\" },\n completed: { classes: \"\", description: \"Agent finished successfully\" },\n error: { classes: \"\", description: \"Agent encountered an error\" },\n },\n size: {\n sm: { classes: \"\", description: \"Compact card for dense grids\" },\n md: { classes: \"\", description: \"Default card size\" },\n },\n} as const;\n\nexport const SF_AI_AGENT_CARD_DEFAULT_VARIANTS = {\n status: \"idle\",\n size: \"md\",\n} as const;\n\nexport type SFAiAgentCardStatus = keyof typeof SF_AI_AGENT_CARD_VARIANTS.status;\nexport type SFAiAgentCardSize = keyof typeof SF_AI_AGENT_CARD_VARIANTS.size;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiAgentCardProps = Omit<ComponentProps<\"button\">, \"children\"> & {\n /** Human-readable agent name (e.g. \"Explore\", \"Execute\"). */\n name: string;\n /** Agent type ID — used for icon mapping. */\n agentType?: string;\n /** Current status. @default \"idle\" */\n status?: SFAiAgentCardStatus;\n /** Model ID being used (e.g. \"claude-haiku-3.5\"). */\n modelId?: string;\n /** What the agent is currently doing. */\n currentTask?: string;\n /** Total elapsed duration in ms. */\n duration?: number;\n /** Number of tool calls made. */\n toolCallCount?: number;\n /** Whether this card is currently selected/active. */\n selected?: boolean;\n /** Custom icon override. */\n icon?: ElementType;\n /** Card size. @default \"md\" */\n size?: SFAiAgentCardSize;\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nconst AGENT_TYPE_ICONS: Record<string, ElementType> = {\n explore: MagnifyingGlassIcon,\n search: MagnifyingGlassIcon,\n execute: CodeIcon,\n code: CodeIcon,\n plan: BrainIcon,\n think: BrainIcon,\n tool: WrenchIcon,\n build: WrenchIcon,\n};\n\nfunction getAgentIcon(\n agentType?: string,\n customIcon?: ElementType\n): ElementType {\n if (customIcon) return customIcon;\n if (agentType) {\n const lower = agentType.toLowerCase();\n for (const [key, icon] of Object.entries(AGENT_TYPE_ICONS)) {\n if (lower.includes(key)) return icon;\n }\n }\n return RobotIcon;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const s = Math.round(ms / 100) / 10;\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const rem = Math.round(s % 60);\n return `${m}m ${rem}s`;\n}\n\nfunction getModelShortName(modelId?: string): string {\n if (!modelId) return \"\";\n return (\n modelId\n .split(\"/\")\n .pop()\n ?.replace(/^claude-/, \"\")\n .replace(/-\\d+$/, \"\") ?? modelId\n );\n}\n\n// ─── Status decorations ───────────────────────────────────────────────────────\n\nconst STATUS_DOT: Record<SFAiAgentCardStatus, string> = {\n idle: \"bg-sf-fill\",\n running: \"bg-sf-brand animate-pulse\",\n completed: \"bg-sf-success\",\n error: \"bg-sf-danger\",\n};\n\nfunction StatusIcon({\n status,\n size = 14,\n}: {\n status: SFAiAgentCardStatus;\n size?: number;\n}) {\n switch (status) {\n case \"running\":\n return (\n <SpinnerGapIcon size={size} className=\"animate-spin text-sf-brand\" />\n );\n case \"completed\":\n return <CheckCircleIcon size={size} className=\"text-sf-success\" />;\n case \"error\":\n return <XCircleIcon size={size} className=\"text-sf-danger\" />;\n default:\n return <CircleIcon size={size} className=\"text-sf-inactive\" />;\n }\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * `AiAgentCard` — compact card showing one agent's status in a commander dashboard.\n *\n * Displays: agent icon, name, status indicator, current task, model, duration,\n * and tool call count. Clickable for selection.\n *\n * @example\n * ```tsx\n * <AiAgentCard\n * name=\"Explore\"\n * agentType=\"explore\"\n * status=\"running\"\n * modelId=\"claude-haiku-3.5\"\n * currentTask=\"Scanning auth files...\"\n * duration={4200}\n * toolCallCount={3}\n * selected\n * onClick={handleSelect}\n * />\n * ```\n */\nexport const AiAgentCard = forwardRef<HTMLButtonElement, AiAgentCardProps>(\n (\n {\n name,\n agentType,\n status = SF_AI_AGENT_CARD_DEFAULT_VARIANTS.status,\n modelId,\n currentTask,\n duration,\n toolCallCount,\n selected,\n icon,\n size = SF_AI_AGENT_CARD_DEFAULT_VARIANTS.size,\n className,\n onClick,\n ...props\n },\n ref\n ) => {\n const Icon = getAgentIcon(agentType, icon);\n const modelShort = getModelShortName(modelId);\n const isSm = size === \"sm\";\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-pressed={selected}\n onClick={onClick}\n className={cn(\n \"group flex flex-col gap-2 rounded-xl border text-left transition-all\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sf-ring focus-visible:ring-offset-1\",\n isSm ? \"p-2.5\" : \"p-3.5\",\n selected\n ? \"border-sf-line bg-sf-recessed shadow-sm\"\n : \"border-sf-line bg-sf-elevated hover:border-sf-line hover:bg-sf-tint\",\n !onClick && \"cursor-default\",\n className\n )}\n {...props}\n >\n {/* Header row */}\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-center gap-2 min-w-0\">\n {/* Agent icon */}\n <div\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg\",\n isSm ? \"size-6\" : \"size-8\",\n status === \"running\"\n ? \"bg-sf-brand/10 text-sf-brand\"\n : status === \"completed\"\n ? \"bg-sf-tint text-sf-success\"\n : status === \"error\"\n ? \"bg-sf-tint text-sf-danger\"\n : \"bg-sf-fill text-sf-subtle\"\n )}\n >\n <Icon size={isSm ? 12 : 16} />\n </div>\n\n {/* Name */}\n <span\n className={cn(\n \"truncate font-medium text-sf-default\",\n isSm ? \"text-xs\" : \"text-sm\"\n )}\n >\n {name}\n </span>\n </div>\n\n {/* Status icon */}\n <StatusIcon status={status} size={isSm ? 12 : 14} />\n </div>\n\n {/* Current task */}\n {currentTask && (\n <p\n className={cn(\n \"truncate text-sf-subtle leading-snug\",\n isSm ? \"text-[10px]\" : \"text-xs\"\n )}\n >\n {currentTask}\n </p>\n )}\n\n {/* Footer row: model + stats */}\n {!isSm && (\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex items-center gap-1.5\">\n {/* Status dot + model */}\n <span\n className={cn(\n \"size-1.5 shrink-0 rounded-full\",\n STATUS_DOT[status]\n )}\n />\n {modelShort && (\n <span className=\"font-mono text-[10px] text-sf-inactive\">\n {modelShort}\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-2 text-[10px] text-sf-subtle\">\n {typeof toolCallCount === \"number\" && toolCallCount > 0 && (\n <span className=\"flex items-center gap-0.5\">\n <WrenchIcon size={9} />\n {toolCallCount}\n </span>\n )}\n {typeof duration === \"number\" && (\n <span>{formatDuration(duration)}</span>\n )}\n </div>\n </div>\n )}\n </button>\n );\n }\n);\n\nAiAgentCard.displayName = \"AiAgentCard\";\n"],"mappings":";;;;;;AAoBA,IAAa,4BAA4B;CACvC,QAAQ;EACN,MAAM;GAAE,SAAS;GAAI,aAAa;GAAkC;EACpE,SAAS;GAAE,SAAS;GAAI,aAAa;GAA6B;EAClE,WAAW;GAAE,SAAS;GAAI,aAAa;GAA+B;EACtE,OAAO;GAAE,SAAS;GAAI,aAAa;GAA8B;EAClE;CACD,MAAM;EACJ,IAAI;GAAE,SAAS;GAAI,aAAa;GAAgC;EAChE,IAAI;GAAE,SAAS;GAAI,aAAa;GAAqB;EACtD;CACF;AAED,IAAa,oCAAoC;CAC/C,QAAQ;CACR,MAAM;CACP;AAgCD,IAAM,mBAAgD;CACpD,SAAS;CACT,QAAQ;CACR,SAAS;CACT,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACN,OAAO;CACR;AAED,SAAS,aACP,WACA,YACa;AACb,KAAI,WAAY,QAAO;AACvB,KAAI,WAAW;EACb,MAAM,QAAQ,UAAU,aAAa;AACrC,OAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,iBAAiB,CACxD,KAAI,MAAM,SAAS,IAAI,CAAE,QAAO;;AAGpC,QAAO;;AAGT,SAAS,eAAe,IAAoB;AAC1C,KAAI,KAAK,IAAM,QAAO,GAAG,GAAG;CAC5B,MAAM,IAAI,KAAK,MAAM,KAAK,IAAI,GAAG;AACjC,KAAI,IAAI,GAAI,QAAO,GAAG,EAAE;AAGxB,QAAO,GAFG,KAAK,MAAM,IAAI,GAAG,CAEhB,IADA,KAAK,MAAM,IAAI,GAAG,CACV;;AAGtB,SAAS,kBAAkB,SAA0B;AACnD,KAAI,CAAC,QAAS,QAAO;AACrB,QACE,QACG,MAAM,IAAI,CACV,KAAK,EACJ,QAAQ,YAAY,GAAG,CACxB,QAAQ,SAAS,GAAG,IAAI;;AAM/B,IAAM,aAAkD;CACtD,MAAM;CACN,SAAS;CACT,WAAW;CACX,OAAO;CACR;AAED,SAAS,WAAW,EAClB,QACA,OAAO,MAIN;AACD,SAAQ,QAAR;EACE,KAAK,UACH,QACE,oBAAC,gBAAD;GAAsB;GAAM,WAAU;GAA+B,CAAA;EAEzE,KAAK,YACH,QAAO,oBAAC,iBAAD;GAAuB;GAAM,WAAU;GAAoB,CAAA;EACpE,KAAK,QACH,QAAO,oBAAC,aAAD;GAAmB;GAAM,WAAU;GAAmB,CAAA;EAC/D,QACE,QAAO,oBAAC,YAAD;GAAkB;GAAM,WAAU;GAAqB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;AA2BpE,IAAa,cAAc,YAEvB,EACE,MACA,WACA,SAAS,kCAAkC,QAC3C,SACA,aACA,UACA,eACA,UACA,MACA,OAAO,kCAAkC,MACzC,WACA,SACA,GAAG,SAEL,QACG;CACH,MAAM,OAAO,aAAa,WAAW,KAAK;CAC1C,MAAM,aAAa,kBAAkB,QAAQ;CAC7C,MAAM,OAAO,SAAS;AAEtB,QACE,qBAAC,UAAD;EACO;EACL,MAAK;EACL,gBAAc;EACL;EACT,WAAW,GACT,wEACA,0GACA,OAAO,UAAU,SACjB,WACI,4CACA,uEACJ,CAAC,WAAW,kBACZ,UACD;EACD,GAAI;YAfN;GAkBE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,oBAAC,OAAD;MACE,WAAW,GACT,wDACA,OAAO,WAAW,UAClB,WAAW,YACP,iCACA,WAAW,cACT,+BACA,WAAW,UACT,8BACA,4BACT;gBAED,oBAAC,MAAD,EAAM,MAAM,OAAO,KAAK,IAAM,CAAA;MAC1B,CAAA,EAGN,oBAAC,QAAD;MACE,WAAW,GACT,wCACA,OAAO,YAAY,UACpB;gBAEA;MACI,CAAA,CACH;QAGN,oBAAC,YAAD;KAAoB;KAAQ,MAAM,OAAO,KAAK;KAAM,CAAA,CAChD;;GAGL,eACC,oBAAC,KAAD;IACE,WAAW,GACT,wCACA,OAAO,gBAAgB,UACxB;cAEA;IACC,CAAA;GAIL,CAAC,QACA,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,oBAAC,QAAD,EACE,WAAW,GACT,kCACA,WAAW,QACZ,EACD,CAAA,EACD,cACC,oBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA,CAEL;QAEN,qBAAC,OAAD;KAAK,WAAU;eAAf,CACG,OAAO,kBAAkB,YAAY,gBAAgB,KACpD,qBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,oBAAC,YAAD,EAAY,MAAM,GAAK,CAAA,EACtB,cACI;SAER,OAAO,aAAa,YACnB,oBAAC,QAAD,EAAA,UAAO,eAAe,SAAS,EAAQ,CAAA,CAErC;OACF;;GAED;;EAGd;AAED,YAAY,cAAc"}
1
+ {"version":3,"file":"ai-agent-card-BXHwhWAU.js","names":[],"sources":["../src/components/ai-agent-card/ai-agent-card.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n BrainIcon,\n CheckCircleIcon,\n CircleIcon,\n CodeIcon,\n MagnifyingGlassIcon,\n RobotIcon,\n SpinnerGapIcon,\n WrenchIcon,\n XCircleIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps, ElementType } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_AGENT_CARD_VARIANTS = {\n status: {\n idle: { classes: \"\", description: \"Agent is idle, not yet started\" },\n running: { classes: \"\", description: \"Agent is actively working\" },\n completed: { classes: \"\", description: \"Agent finished successfully\" },\n error: { classes: \"\", description: \"Agent encountered an error\" },\n },\n size: {\n sm: { classes: \"\", description: \"Compact card for dense grids\" },\n md: { classes: \"\", description: \"Default card size\" },\n },\n} as const;\n\nexport const SF_AI_AGENT_CARD_DEFAULT_VARIANTS = {\n status: \"idle\",\n size: \"md\",\n} as const;\n\nexport type SFAiAgentCardStatus = keyof typeof SF_AI_AGENT_CARD_VARIANTS.status;\nexport type SFAiAgentCardSize = keyof typeof SF_AI_AGENT_CARD_VARIANTS.size;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiAgentCardProps = Omit<ComponentProps<\"button\">, \"children\"> & {\n /** Human-readable agent name (e.g. \"Explore\", \"Execute\"). */\n name: string;\n /** Agent type ID — used for icon mapping. */\n agentType?: string;\n /** Current status. @default \"idle\" */\n status?: SFAiAgentCardStatus;\n /** Model ID being used (e.g. \"claude-haiku-3.5\"). */\n modelId?: string;\n /** What the agent is currently doing. */\n currentTask?: string;\n /** Total elapsed duration in ms. */\n duration?: number;\n /** Number of tool calls made. */\n toolCallCount?: number;\n /** Whether this card is currently selected/active. */\n selected?: boolean;\n /** Custom icon override. */\n icon?: ElementType;\n /** Card size. @default \"md\" */\n size?: SFAiAgentCardSize;\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nconst AGENT_TYPE_ICONS: Record<string, ElementType> = {\n explore: MagnifyingGlassIcon,\n search: MagnifyingGlassIcon,\n execute: CodeIcon,\n code: CodeIcon,\n plan: BrainIcon,\n think: BrainIcon,\n tool: WrenchIcon,\n build: WrenchIcon,\n};\n\nfunction getAgentIcon(\n agentType?: string,\n customIcon?: ElementType\n): ElementType {\n if (customIcon) return customIcon;\n if (agentType) {\n const lower = agentType.toLowerCase();\n for (const [key, icon] of Object.entries(AGENT_TYPE_ICONS)) {\n if (lower.includes(key)) return icon;\n }\n }\n return RobotIcon;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const s = Math.round(ms / 100) / 10;\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const rem = Math.round(s % 60);\n return `${m}m ${rem}s`;\n}\n\nfunction getModelShortName(modelId?: string): string {\n if (!modelId) return \"\";\n return (\n modelId\n .split(\"/\")\n .pop()\n ?.replace(/^claude-/, \"\")\n .replace(/-\\d+$/, \"\") ?? modelId\n );\n}\n\n// ─── Status decorations ───────────────────────────────────────────────────────\n\nconst STATUS_DOT: Record<SFAiAgentCardStatus, string> = {\n idle: \"bg-sf-fill\",\n running: \"bg-sf-brand animate-pulse\",\n completed: \"bg-sf-success\",\n error: \"bg-sf-danger\",\n};\n\nfunction StatusIcon({\n status,\n size = 14,\n}: {\n status: SFAiAgentCardStatus;\n size?: number;\n}) {\n switch (status) {\n case \"running\":\n return (\n <SpinnerGapIcon size={size} className=\"animate-spin text-sf-brand\" />\n );\n case \"completed\":\n return <CheckCircleIcon size={size} className=\"text-sf-success\" />;\n case \"error\":\n return <XCircleIcon size={size} className=\"text-sf-danger\" />;\n default:\n return <CircleIcon size={size} className=\"text-sf-inactive\" />;\n }\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * `AiAgentCard` — compact card showing one agent's status in a commander dashboard.\n *\n * Displays: agent icon, name, status indicator, current task, model, duration,\n * and tool call count. Clickable for selection.\n *\n * @example\n * ```tsx\n * <AiAgentCard\n * name=\"Explore\"\n * agentType=\"explore\"\n * status=\"running\"\n * modelId=\"claude-haiku-3.5\"\n * currentTask=\"Scanning auth files...\"\n * duration={4200}\n * toolCallCount={3}\n * selected\n * onClick={handleSelect}\n * />\n * ```\n */\nexport const AiAgentCard = forwardRef<HTMLButtonElement, AiAgentCardProps>(\n (\n {\n name,\n agentType,\n status = SF_AI_AGENT_CARD_DEFAULT_VARIANTS.status,\n modelId,\n currentTask,\n duration,\n toolCallCount,\n selected,\n icon,\n size = SF_AI_AGENT_CARD_DEFAULT_VARIANTS.size,\n className,\n onClick,\n ...props\n },\n ref\n ) => {\n const Icon = getAgentIcon(agentType, icon);\n const modelShort = getModelShortName(modelId);\n const isSm = size === \"sm\";\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-pressed={selected}\n onClick={onClick}\n className={cn(\n \"group flex flex-col gap-2 rounded-xl border text-left transition-all\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sf-ring focus-visible:ring-offset-1\",\n isSm ? \"p-2.5\" : \"p-3.5\",\n selected\n ? \"border-sf-line bg-sf-recessed shadow-sm\"\n : \"border-sf-line bg-sf-elevated hover:border-sf-line hover:bg-sf-tint\",\n !onClick && \"cursor-default\",\n className\n )}\n {...props}\n >\n {/* Header row */}\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-center gap-2 min-w-0\">\n {/* Agent icon */}\n <div\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg\",\n isSm ? \"size-6\" : \"size-8\",\n status === \"running\"\n ? \"bg-sf-brand/10 text-sf-brand\"\n : status === \"completed\"\n ? \"bg-sf-tint text-sf-success\"\n : status === \"error\"\n ? \"bg-sf-tint text-sf-danger\"\n : \"bg-sf-fill text-sf-subtle\"\n )}\n >\n <Icon size={isSm ? 12 : 16} />\n </div>\n\n {/* Name */}\n <span\n className={cn(\n \"truncate font-medium text-sf-default\",\n isSm ? \"text-xs\" : \"text-sm\"\n )}\n >\n {name}\n </span>\n </div>\n\n {/* Status icon */}\n <StatusIcon status={status} size={isSm ? 12 : 14} />\n </div>\n\n {/* Current task */}\n {currentTask && (\n <p\n className={cn(\n \"truncate text-sf-subtle leading-snug\",\n isSm ? \"text-[10px]\" : \"text-xs\"\n )}\n >\n {currentTask}\n </p>\n )}\n\n {/* Footer row: model + stats */}\n {!isSm && (\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex items-center gap-1.5\">\n {/* Status dot + model */}\n <span\n className={cn(\n \"size-1.5 shrink-0 rounded-full\",\n STATUS_DOT[status]\n )}\n />\n {modelShort && (\n <span className=\"font-mono text-[10px] text-sf-inactive\">\n {modelShort}\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-2 text-[10px] text-sf-subtle\">\n {typeof toolCallCount === \"number\" && toolCallCount > 0 && (\n <span className=\"flex items-center gap-0.5\">\n <WrenchIcon size={9} />\n {toolCallCount}\n </span>\n )}\n {typeof duration === \"number\" && (\n <span>{formatDuration(duration)}</span>\n )}\n </div>\n </div>\n )}\n </button>\n );\n }\n);\n\nAiAgentCard.displayName = \"AiAgentCard\";\n"],"mappings":";;;;;;AAoBA,IAAa,4BAA4B;CACvC,QAAQ;EACN,MAAM;GAAE,SAAS;GAAI,aAAa;GAAkC;EACpE,SAAS;GAAE,SAAS;GAAI,aAAa;GAA6B;EAClE,WAAW;GAAE,SAAS;GAAI,aAAa;GAA+B;EACtE,OAAO;GAAE,SAAS;GAAI,aAAa;GAA8B;EAClE;CACD,MAAM;EACJ,IAAI;GAAE,SAAS;GAAI,aAAa;GAAgC;EAChE,IAAI;GAAE,SAAS;GAAI,aAAa;GAAqB;EACtD;CACF;AAED,IAAa,oCAAoC;CAC/C,QAAQ;CACR,MAAM;CACP;AAgCD,IAAM,mBAAgD;CACpD,SAAS;CACT,QAAQ;CACR,SAAS;CACT,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACN,OAAO;CACR;AAED,SAAS,aACP,WACA,YACa;AACb,KAAI,WAAY,QAAO;AACvB,KAAI,WAAW;EACb,MAAM,QAAQ,UAAU,aAAa;AACrC,OAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,iBAAiB,CACxD,KAAI,MAAM,SAAS,IAAI,CAAE,QAAO;;AAGpC,QAAO;;AAGT,SAAS,eAAe,IAAoB;AAC1C,KAAI,KAAK,IAAM,QAAO,GAAG,GAAG;CAC5B,MAAM,IAAI,KAAK,MAAM,KAAK,IAAI,GAAG;AACjC,KAAI,IAAI,GAAI,QAAO,GAAG,EAAE;AAGxB,QAAO,GAFG,KAAK,MAAM,IAAI,GAAG,CAEhB,IADA,KAAK,MAAM,IAAI,GAAG,CACV;;AAGtB,SAAS,kBAAkB,SAA0B;AACnD,KAAI,CAAC,QAAS,QAAO;AACrB,QACE,QACG,MAAM,IAAI,CACV,KAAK,EACJ,QAAQ,YAAY,GAAG,CACxB,QAAQ,SAAS,GAAG,IAAI;;AAM/B,IAAM,aAAkD;CACtD,MAAM;CACN,SAAS;CACT,WAAW;CACX,OAAO;CACR;AAED,SAAS,WAAW,EAClB,QACA,OAAO,MAIN;AACD,SAAQ,QAAR;EACE,KAAK,UACH,QACE,oBAAC,gBAAD;GAAsB;GAAM,WAAU;GAA+B,CAAA;EAEzE,KAAK,YACH,QAAO,oBAAC,iBAAD;GAAuB;GAAM,WAAU;GAAoB,CAAA;EACpE,KAAK,QACH,QAAO,oBAAC,aAAD;GAAmB;GAAM,WAAU;GAAmB,CAAA;EAC/D,QACE,QAAO,oBAAC,YAAD;GAAkB;GAAM,WAAU;GAAqB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;AA2BpE,IAAa,cAAc,YAEvB,EACE,MACA,WACA,SAAS,kCAAkC,QAC3C,SACA,aACA,UACA,eACA,UACA,MACA,OAAO,kCAAkC,MACzC,WACA,SACA,GAAG,SAEL,QACG;CACH,MAAM,OAAO,aAAa,WAAW,KAAK;CAC1C,MAAM,aAAa,kBAAkB,QAAQ;CAC7C,MAAM,OAAO,SAAS;AAEtB,QACE,qBAAC,UAAD;EACO;EACL,MAAK;EACL,gBAAc;EACL;EACT,WAAW,GACT,wEACA,0GACA,OAAO,UAAU,SACjB,WACI,4CACA,uEACJ,CAAC,WAAW,kBACZ,UACD;EACD,GAAI;YAfN;GAkBE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,oBAAC,OAAD;MACE,WAAW,GACT,wDACA,OAAO,WAAW,UAClB,WAAW,YACP,iCACA,WAAW,cACT,+BACA,WAAW,UACT,8BACA,4BACT;gBAED,oBAAC,MAAD,EAAM,MAAM,OAAO,KAAK,IAAM,CAAA;MAC1B,CAAA,EAGN,oBAAC,QAAD;MACE,WAAW,GACT,wCACA,OAAO,YAAY,UACpB;gBAEA;MACI,CAAA,CACH;QAGN,oBAAC,YAAD;KAAoB;KAAQ,MAAM,OAAO,KAAK;KAAM,CAAA,CAChD;;GAGL,eACC,oBAAC,KAAD;IACE,WAAW,GACT,wCACA,OAAO,gBAAgB,UACxB;cAEA;IACC,CAAA;GAIL,CAAC,QACA,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,oBAAC,QAAD,EACE,WAAW,GACT,kCACA,WAAW,QACZ,EACD,CAAA,EACD,cACC,oBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA,CAEL;QAEN,qBAAC,OAAD;KAAK,WAAU;eAAf,CACG,OAAO,kBAAkB,YAAY,gBAAgB,KACpD,qBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,oBAAC,YAAD,EAAY,MAAM,GAAK,CAAA,EACtB,cACI;SAER,OAAO,aAAa,YACnB,oBAAC,QAAD,EAAA,UAAO,eAAe,SAAS,EAAQ,CAAA,CAErC;OACF;;GAED;;EAGd;AAED,YAAY,cAAc"}
@@ -181,4 +181,4 @@ AiApproval.displayName = "AiApproval";
181
181
  //#endregion
182
182
  export { SF_AI_APPROVAL_DEFAULT_VARIANTS as n, SF_AI_APPROVAL_VARIANTS as r, AiApproval as t };
183
183
 
184
- //# sourceMappingURL=ai-approval-Mb7-BY6i.js.map
184
+ //# sourceMappingURL=ai-approval-aa0qvjFN.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-approval-Mb7-BY6i.js","names":[],"sources":["../src/components/ai-approval/ai-approval.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n CheckCircleIcon,\n ProhibitIcon,\n ShieldCheckIcon,\n ListChecksIcon,\n} from \"@phosphor-icons/react\";\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\nimport { useCallback, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_APPROVAL_VARIANTS = {\n kind: {\n tool: { classes: \"\", description: \"Tool call approval\" },\n plan: { classes: \"\", description: \"Plan approval\" },\n },\n status: {\n pending: { classes: \"\", description: \"Awaiting user decision\" },\n approved: { classes: \"\", description: \"Approved by user\" },\n rejected: { classes: \"\", description: \"Rejected by user\" },\n },\n} as const;\n\nexport const SF_AI_APPROVAL_DEFAULT_VARIANTS = {\n kind: \"tool\",\n status: \"pending\",\n} as const;\n\nexport type SFAiApprovalKind = keyof typeof SF_AI_APPROVAL_VARIANTS.kind;\nexport type SFAiApprovalStatus = keyof typeof SF_AI_APPROVAL_VARIANTS.status;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiApprovalItem = {\n id: string;\n label: string;\n description?: string;\n};\n\nexport type AiApprovalProps = Omit<HTMLAttributes<HTMLDivElement>, \"title\"> & {\n /** Approval kind — `\"tool\"` for individual tool calls, `\"plan\"` for multi-step plans. */\n kind?: SFAiApprovalKind;\n /** Current approval status. */\n status?: SFAiApprovalStatus;\n /** Title text shown in the header. */\n title: string;\n /** Optional description below the title. */\n description?: string;\n /** Custom icon. Defaults to shield (tool) or list (plan). */\n icon?: ElementType;\n /**\n * For plan approvals: list of steps/items in the plan.\n * For tool approvals: can show tool name, parameters, etc.\n */\n items?: AiApprovalItem[];\n /** Called when user approves. */\n onApprove?: () => void;\n /** Called when user rejects. Receives optional feedback string. */\n onReject?: (feedback?: string) => void;\n /** Label for the approve button. Default: `\"Approve\"`. */\n approveLabel?: string;\n /** Label for the reject button. Default: `\"Reject\"`. */\n rejectLabel?: string;\n /** Show a text input for rejection feedback. Default: `false`. */\n showFeedback?: boolean;\n /** Content rendered below items and above the action buttons. */\n children?: ReactNode;\n};\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nconst KIND_ICONS: Record<SFAiApprovalKind, ElementType> = {\n tool: ShieldCheckIcon,\n plan: ListChecksIcon,\n};\n\nconst STATUS_CONFIG: Record<\n SFAiApprovalStatus,\n { icon: ElementType; label: string; className: string }\n> = {\n pending: {\n icon: ShieldCheckIcon,\n label: \"Awaiting approval\",\n className: \"text-sf-subtle\",\n },\n approved: {\n icon: CheckCircleIcon,\n label: \"Approved\",\n className: \"text-sf-success\",\n },\n rejected: {\n icon: ProhibitIcon,\n label: \"Rejected\",\n className: \"text-sf-danger\",\n },\n};\n\n/**\n * Approval card for tool calls and plan submissions.\n *\n * Maps to harness events: `tool_approval_required`, `plan_approval_required`,\n * `plan_approved`.\n *\n * @example\n * ```tsx\n * <AiApproval\n * kind=\"tool\"\n * title=\"Execute shell command\"\n * description=\"rm -rf /tmp/cache\"\n * onApprove={() => harness.respondToToolApproval({ decision: 'approve' })}\n * onReject={() => harness.respondToToolApproval({ decision: 'decline' })}\n * />\n *\n * <AiApproval\n * kind=\"plan\"\n * title=\"Deployment plan\"\n * items={[\n * { id: \"1\", label: \"Run tests\" },\n * { id: \"2\", label: \"Build production bundle\" },\n * { id: \"3\", label: \"Deploy to staging\" },\n * ]}\n * showFeedback\n * onApprove={() => harness.respondToPlanApproval({ planId, response: { action: 'approved' } })}\n * onReject={(feedback) => harness.respondToPlanApproval({ planId, response: { action: 'rejected', feedback } })}\n * />\n * ```\n */\nexport function AiApproval({\n kind = \"tool\",\n status = \"pending\",\n title,\n description,\n icon,\n items,\n onApprove,\n onReject,\n approveLabel = \"Approve\",\n rejectLabel = \"Reject\",\n showFeedback = false,\n children,\n className,\n ...props\n}: AiApprovalProps) {\n const [feedback, setFeedback] = useState(\"\");\n const isPending = status === \"pending\";\n\n const IconComponent = icon ?? KIND_ICONS[kind];\n const statusConfig = STATUS_CONFIG[status];\n const StatusIcon = statusConfig.icon;\n\n const handleReject = useCallback(() => {\n onReject?.(showFeedback ? feedback : undefined);\n }, [onReject, showFeedback, feedback]);\n\n return (\n <div\n className={cn(\n \"flex flex-col gap-2.5 rounded-lg border border-sf-line bg-sf-elevated p-3\",\n !isPending && \"opacity-75\",\n className\n )}\n {...props}\n >\n {/* Header */}\n <div className=\"flex items-start gap-2.5\">\n <div\n className={cn(\n \"mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-md\",\n isPending ? \"bg-sf-brand/10 text-sf-brand\" : statusConfig.className\n )}\n >\n {isPending ? (\n <IconComponent className=\"size-4\" weight=\"bold\" />\n ) : (\n <StatusIcon className=\"size-4\" weight=\"bold\" />\n )}\n </div>\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm font-medium text-sf-default\">{title}</span>\n {description && (\n <span className=\"text-xs text-sf-subtle\">{description}</span>\n )}\n </div>\n {!isPending && (\n <span\n className={cn(\n \"ml-auto shrink-0 text-xs font-medium\",\n statusConfig.className\n )}\n >\n {statusConfig.label}\n </span>\n )}\n </div>\n\n {/* Items (plan steps / tool details) */}\n {items && items.length > 0 && (\n <div className=\"flex flex-col gap-1 pl-8\">\n {items.map((item, index) => (\n <div key={item.id} className=\"flex items-start gap-2 text-xs\">\n <span className=\"mt-px shrink-0 font-mono text-sf-subtle\">\n {index + 1}.\n </span>\n <div className=\"flex min-w-0 flex-col\">\n <span className=\"text-sf-default\">{item.label}</span>\n {item.description && (\n <span className=\"text-sf-subtle\">{item.description}</span>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {children}\n\n {/* Feedback input */}\n {isPending && showFeedback && (\n <textarea\n className=\"mx-0 min-h-[60px] resize-none rounded-md border border-sf-line bg-sf-base px-2.5 py-1.5 text-xs text-sf-default outline-none placeholder:text-sf-inactive focus:border-sf-ring\"\n onChange={(e) => setFeedback(e.target.value)}\n placeholder=\"Optional feedback…\"\n value={feedback}\n />\n )}\n\n {/* Action buttons */}\n {isPending && (\n <div className=\"flex items-center justify-end gap-2\">\n <Button\n onClick={handleReject}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n {rejectLabel}\n </Button>\n <Button onClick={onApprove} size=\"sm\" type=\"button\" variant=\"primary\">\n {approveLabel}\n </Button>\n </div>\n )}\n </div>\n );\n}\n\nAiApproval.displayName = \"AiApproval\";\n"],"mappings":";;;;;;;AAgBA,IAAa,0BAA0B;CACrC,MAAM;EACJ,MAAM;GAAE,SAAS;GAAI,aAAa;GAAsB;EACxD,MAAM;GAAE,SAAS;GAAI,aAAa;GAAiB;EACpD;CACD,QAAQ;EACN,SAAS;GAAE,SAAS;GAAI,aAAa;GAA0B;EAC/D,UAAU;GAAE,SAAS;GAAI,aAAa;GAAoB;EAC1D,UAAU;GAAE,SAAS;GAAI,aAAa;GAAoB;EAC3D;CACF;AAED,IAAa,kCAAkC;CAC7C,MAAM;CACN,QAAQ;CACT;AA6CD,IAAM,aAAoD;CACxD,MAAM;CACN,MAAM;CACP;AAED,IAAM,gBAGF;CACF,SAAS;EACP,MAAM;EACN,OAAO;EACP,WAAW;EACZ;CACD,UAAU;EACR,MAAM;EACN,OAAO;EACP,WAAW;EACZ;CACD,UAAU;EACR,MAAM;EACN,OAAO;EACP,WAAW;EACZ;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCD,SAAgB,WAAW,EACzB,OAAO,QACP,SAAS,WACT,OACA,aACA,MACA,OACA,WACA,UACA,eAAe,WACf,cAAc,UACd,eAAe,OACf,UACA,WACA,GAAG,SACe;CAClB,MAAM,CAAC,UAAU,eAAe,SAAS,GAAG;CAC5C,MAAM,YAAY,WAAW;CAE7B,MAAM,gBAAgB,QAAQ,WAAW;CACzC,MAAM,eAAe,cAAc;CACnC,MAAM,aAAa,aAAa;CAEhC,MAAM,eAAe,kBAAkB;AACrC,aAAW,eAAe,WAAW,KAAA,EAAU;IAC9C;EAAC;EAAU;EAAc;EAAS,CAAC;AAEtC,QACE,qBAAC,OAAD;EACE,WAAW,GACT,6EACA,CAAC,aAAa,cACd,UACD;EACD,GAAI;YANN;GASE,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,oBAAC,OAAD;MACE,WAAW,GACT,sEACA,YAAY,iCAAiC,aAAa,UAC3D;gBAEA,YACC,oBAAC,eAAD;OAAe,WAAU;OAAS,QAAO;OAAS,CAAA,GAElD,oBAAC,YAAD;OAAY,WAAU;OAAS,QAAO;OAAS,CAAA;MAE7C,CAAA;KACN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBAAuC;OAAa,CAAA,EACnE,eACC,oBAAC,QAAD;OAAM,WAAU;iBAA0B;OAAmB,CAAA,CAE3D;;KACL,CAAC,aACA,oBAAC,QAAD;MACE,WAAW,GACT,wCACA,aAAa,UACd;gBAEA,aAAa;MACT,CAAA;KAEL;;GAGL,SAAS,MAAM,SAAS,KACvB,oBAAC,OAAD;IAAK,WAAU;cACZ,MAAM,KAAK,MAAM,UAChB,qBAAC,OAAD;KAAmB,WAAU;eAA7B,CACE,qBAAC,QAAD;MAAM,WAAU;gBAAhB,CACG,QAAQ,GAAE,IACN;SACP,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBAAmB,KAAK;OAAa,CAAA,EACpD,KAAK,eACJ,oBAAC,QAAD;OAAM,WAAU;iBAAkB,KAAK;OAAmB,CAAA,CAExD;QACF;OAVI,KAAK,GAUT,CACN;IACE,CAAA;GAGP;GAGA,aAAa,gBACZ,oBAAC,YAAD;IACE,WAAU;IACV,WAAW,MAAM,YAAY,EAAE,OAAO,MAAM;IAC5C,aAAY;IACZ,OAAO;IACP,CAAA;GAIH,aACC,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,QAAD;KACE,SAAS;KACT,MAAK;KACL,MAAK;KACL,SAAQ;eAEP;KACM,CAAA,EACT,oBAAC,QAAD;KAAQ,SAAS;KAAW,MAAK;KAAK,MAAK;KAAS,SAAQ;eACzD;KACM,CAAA,CACL;;GAEJ;;;AAIV,WAAW,cAAc"}
1
+ {"version":3,"file":"ai-approval-aa0qvjFN.js","names":[],"sources":["../src/components/ai-approval/ai-approval.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n CheckCircleIcon,\n ProhibitIcon,\n ShieldCheckIcon,\n ListChecksIcon,\n} from \"@phosphor-icons/react\";\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\nimport { useCallback, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_APPROVAL_VARIANTS = {\n kind: {\n tool: { classes: \"\", description: \"Tool call approval\" },\n plan: { classes: \"\", description: \"Plan approval\" },\n },\n status: {\n pending: { classes: \"\", description: \"Awaiting user decision\" },\n approved: { classes: \"\", description: \"Approved by user\" },\n rejected: { classes: \"\", description: \"Rejected by user\" },\n },\n} as const;\n\nexport const SF_AI_APPROVAL_DEFAULT_VARIANTS = {\n kind: \"tool\",\n status: \"pending\",\n} as const;\n\nexport type SFAiApprovalKind = keyof typeof SF_AI_APPROVAL_VARIANTS.kind;\nexport type SFAiApprovalStatus = keyof typeof SF_AI_APPROVAL_VARIANTS.status;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiApprovalItem = {\n id: string;\n label: string;\n description?: string;\n};\n\nexport type AiApprovalProps = Omit<HTMLAttributes<HTMLDivElement>, \"title\"> & {\n /** Approval kind — `\"tool\"` for individual tool calls, `\"plan\"` for multi-step plans. */\n kind?: SFAiApprovalKind;\n /** Current approval status. */\n status?: SFAiApprovalStatus;\n /** Title text shown in the header. */\n title: string;\n /** Optional description below the title. */\n description?: string;\n /** Custom icon. Defaults to shield (tool) or list (plan). */\n icon?: ElementType;\n /**\n * For plan approvals: list of steps/items in the plan.\n * For tool approvals: can show tool name, parameters, etc.\n */\n items?: AiApprovalItem[];\n /** Called when user approves. */\n onApprove?: () => void;\n /** Called when user rejects. Receives optional feedback string. */\n onReject?: (feedback?: string) => void;\n /** Label for the approve button. Default: `\"Approve\"`. */\n approveLabel?: string;\n /** Label for the reject button. Default: `\"Reject\"`. */\n rejectLabel?: string;\n /** Show a text input for rejection feedback. Default: `false`. */\n showFeedback?: boolean;\n /** Content rendered below items and above the action buttons. */\n children?: ReactNode;\n};\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nconst KIND_ICONS: Record<SFAiApprovalKind, ElementType> = {\n tool: ShieldCheckIcon,\n plan: ListChecksIcon,\n};\n\nconst STATUS_CONFIG: Record<\n SFAiApprovalStatus,\n { icon: ElementType; label: string; className: string }\n> = {\n pending: {\n icon: ShieldCheckIcon,\n label: \"Awaiting approval\",\n className: \"text-sf-subtle\",\n },\n approved: {\n icon: CheckCircleIcon,\n label: \"Approved\",\n className: \"text-sf-success\",\n },\n rejected: {\n icon: ProhibitIcon,\n label: \"Rejected\",\n className: \"text-sf-danger\",\n },\n};\n\n/**\n * Approval card for tool calls and plan submissions.\n *\n * Maps to harness events: `tool_approval_required`, `plan_approval_required`,\n * `plan_approved`.\n *\n * @example\n * ```tsx\n * <AiApproval\n * kind=\"tool\"\n * title=\"Execute shell command\"\n * description=\"rm -rf /tmp/cache\"\n * onApprove={() => harness.respondToToolApproval({ decision: 'approve' })}\n * onReject={() => harness.respondToToolApproval({ decision: 'decline' })}\n * />\n *\n * <AiApproval\n * kind=\"plan\"\n * title=\"Deployment plan\"\n * items={[\n * { id: \"1\", label: \"Run tests\" },\n * { id: \"2\", label: \"Build production bundle\" },\n * { id: \"3\", label: \"Deploy to staging\" },\n * ]}\n * showFeedback\n * onApprove={() => harness.respondToPlanApproval({ planId, response: { action: 'approved' } })}\n * onReject={(feedback) => harness.respondToPlanApproval({ planId, response: { action: 'rejected', feedback } })}\n * />\n * ```\n */\nexport function AiApproval({\n kind = \"tool\",\n status = \"pending\",\n title,\n description,\n icon,\n items,\n onApprove,\n onReject,\n approveLabel = \"Approve\",\n rejectLabel = \"Reject\",\n showFeedback = false,\n children,\n className,\n ...props\n}: AiApprovalProps) {\n const [feedback, setFeedback] = useState(\"\");\n const isPending = status === \"pending\";\n\n const IconComponent = icon ?? KIND_ICONS[kind];\n const statusConfig = STATUS_CONFIG[status];\n const StatusIcon = statusConfig.icon;\n\n const handleReject = useCallback(() => {\n onReject?.(showFeedback ? feedback : undefined);\n }, [onReject, showFeedback, feedback]);\n\n return (\n <div\n className={cn(\n \"flex flex-col gap-2.5 rounded-lg border border-sf-line bg-sf-elevated p-3\",\n !isPending && \"opacity-75\",\n className\n )}\n {...props}\n >\n {/* Header */}\n <div className=\"flex items-start gap-2.5\">\n <div\n className={cn(\n \"mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-md\",\n isPending ? \"bg-sf-brand/10 text-sf-brand\" : statusConfig.className\n )}\n >\n {isPending ? (\n <IconComponent className=\"size-4\" weight=\"bold\" />\n ) : (\n <StatusIcon className=\"size-4\" weight=\"bold\" />\n )}\n </div>\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm font-medium text-sf-default\">{title}</span>\n {description && (\n <span className=\"text-xs text-sf-subtle\">{description}</span>\n )}\n </div>\n {!isPending && (\n <span\n className={cn(\n \"ml-auto shrink-0 text-xs font-medium\",\n statusConfig.className\n )}\n >\n {statusConfig.label}\n </span>\n )}\n </div>\n\n {/* Items (plan steps / tool details) */}\n {items && items.length > 0 && (\n <div className=\"flex flex-col gap-1 pl-8\">\n {items.map((item, index) => (\n <div key={item.id} className=\"flex items-start gap-2 text-xs\">\n <span className=\"mt-px shrink-0 font-mono text-sf-subtle\">\n {index + 1}.\n </span>\n <div className=\"flex min-w-0 flex-col\">\n <span className=\"text-sf-default\">{item.label}</span>\n {item.description && (\n <span className=\"text-sf-subtle\">{item.description}</span>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {children}\n\n {/* Feedback input */}\n {isPending && showFeedback && (\n <textarea\n className=\"mx-0 min-h-[60px] resize-none rounded-md border border-sf-line bg-sf-base px-2.5 py-1.5 text-xs text-sf-default outline-none placeholder:text-sf-inactive focus:border-sf-ring\"\n onChange={(e) => setFeedback(e.target.value)}\n placeholder=\"Optional feedback…\"\n value={feedback}\n />\n )}\n\n {/* Action buttons */}\n {isPending && (\n <div className=\"flex items-center justify-end gap-2\">\n <Button\n onClick={handleReject}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n {rejectLabel}\n </Button>\n <Button onClick={onApprove} size=\"sm\" type=\"button\" variant=\"primary\">\n {approveLabel}\n </Button>\n </div>\n )}\n </div>\n );\n}\n\nAiApproval.displayName = \"AiApproval\";\n"],"mappings":";;;;;;;AAgBA,IAAa,0BAA0B;CACrC,MAAM;EACJ,MAAM;GAAE,SAAS;GAAI,aAAa;GAAsB;EACxD,MAAM;GAAE,SAAS;GAAI,aAAa;GAAiB;EACpD;CACD,QAAQ;EACN,SAAS;GAAE,SAAS;GAAI,aAAa;GAA0B;EAC/D,UAAU;GAAE,SAAS;GAAI,aAAa;GAAoB;EAC1D,UAAU;GAAE,SAAS;GAAI,aAAa;GAAoB;EAC3D;CACF;AAED,IAAa,kCAAkC;CAC7C,MAAM;CACN,QAAQ;CACT;AA6CD,IAAM,aAAoD;CACxD,MAAM;CACN,MAAM;CACP;AAED,IAAM,gBAGF;CACF,SAAS;EACP,MAAM;EACN,OAAO;EACP,WAAW;EACZ;CACD,UAAU;EACR,MAAM;EACN,OAAO;EACP,WAAW;EACZ;CACD,UAAU;EACR,MAAM;EACN,OAAO;EACP,WAAW;EACZ;CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCD,SAAgB,WAAW,EACzB,OAAO,QACP,SAAS,WACT,OACA,aACA,MACA,OACA,WACA,UACA,eAAe,WACf,cAAc,UACd,eAAe,OACf,UACA,WACA,GAAG,SACe;CAClB,MAAM,CAAC,UAAU,eAAe,SAAS,GAAG;CAC5C,MAAM,YAAY,WAAW;CAE7B,MAAM,gBAAgB,QAAQ,WAAW;CACzC,MAAM,eAAe,cAAc;CACnC,MAAM,aAAa,aAAa;CAEhC,MAAM,eAAe,kBAAkB;AACrC,aAAW,eAAe,WAAW,KAAA,EAAU;IAC9C;EAAC;EAAU;EAAc;EAAS,CAAC;AAEtC,QACE,qBAAC,OAAD;EACE,WAAW,GACT,6EACA,CAAC,aAAa,cACd,UACD;EACD,GAAI;YANN;GASE,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,oBAAC,OAAD;MACE,WAAW,GACT,sEACA,YAAY,iCAAiC,aAAa,UAC3D;gBAEA,YACC,oBAAC,eAAD;OAAe,WAAU;OAAS,QAAO;OAAS,CAAA,GAElD,oBAAC,YAAD;OAAY,WAAU;OAAS,QAAO;OAAS,CAAA;MAE7C,CAAA;KACN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBAAuC;OAAa,CAAA,EACnE,eACC,oBAAC,QAAD;OAAM,WAAU;iBAA0B;OAAmB,CAAA,CAE3D;;KACL,CAAC,aACA,oBAAC,QAAD;MACE,WAAW,GACT,wCACA,aAAa,UACd;gBAEA,aAAa;MACT,CAAA;KAEL;;GAGL,SAAS,MAAM,SAAS,KACvB,oBAAC,OAAD;IAAK,WAAU;cACZ,MAAM,KAAK,MAAM,UAChB,qBAAC,OAAD;KAAmB,WAAU;eAA7B,CACE,qBAAC,QAAD;MAAM,WAAU;gBAAhB,CACG,QAAQ,GAAE,IACN;SACP,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBAAmB,KAAK;OAAa,CAAA,EACpD,KAAK,eACJ,oBAAC,QAAD;OAAM,WAAU;iBAAkB,KAAK;OAAmB,CAAA,CAExD;QACF;OAVI,KAAK,GAUT,CACN;IACE,CAAA;GAGP;GAGA,aAAa,gBACZ,oBAAC,YAAD;IACE,WAAU;IACV,WAAW,MAAM,YAAY,EAAE,OAAO,MAAM;IAC5C,aAAY;IACZ,OAAO;IACP,CAAA;GAIH,aACC,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,QAAD;KACE,SAAS;KACT,MAAK;KACL,MAAK;KACL,SAAQ;eAEP;KACM,CAAA,EACT,oBAAC,QAAD;KAAQ,SAAS;KAAW,MAAK;KAAK,MAAK;KAAS,SAAQ;eACzD;KACM,CAAA,CACL;;GAEJ;;;AAIV,WAAW,cAAc"}
@@ -107,4 +107,4 @@ function AiCodeBlockCopyButton({ onCopy, onError, timeout = 2e3, children, class
107
107
  //#endregion
108
108
  export { SF_AI_CODE_BLOCK_VARIANTS as i, AiCodeBlockCopyButton as n, SF_AI_CODE_BLOCK_DEFAULT_VARIANTS as r, AiCodeBlock as t };
109
109
 
110
- //# sourceMappingURL=ai-code-block-BI_z0UVR.js.map
110
+ //# sourceMappingURL=ai-code-block-BgtIxtZZ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-code-block-BI_z0UVR.js","names":[],"sources":["../src/components/ai-code-block/ai-code-block.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, HTMLAttributes, ReactNode } from \"react\";\nimport { createContext, memo, useContext, useState } from \"react\";\nimport { highlight } from \"sugar-high\";\n\nimport { cn } from \"../../utils/cn\";\nimport { highlightToLines } from \"../../utils/highlight-to-react\";\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_CODE_BLOCK_VARIANTS = {} as const;\nexport const SF_AI_CODE_BLOCK_DEFAULT_VARIANTS = {} as const;\n\n// ─── Context ─────────────────────────────────────────────────────────────────\n\ninterface AiCodeBlockContextValue {\n code: string;\n}\n\nconst AiCodeBlockContext = createContext<AiCodeBlockContextValue>({ code: \"\" });\n\n// ─── AiCodeBlock ─────────────────────────────────────────────────────────────\n\nexport type AiCodeBlockProps = HTMLAttributes<HTMLDivElement> & {\n /** The raw code string to display and make copyable. */\n code: string;\n /** Language identifier for syntax highlighting (display only, no highlighting applied). */\n language?: string;\n /** Show line numbers. @default false */\n showLineNumbers?: boolean;\n /** Extra content rendered in the top-right overlay (e.g. copy button). */\n children?: ReactNode;\n};\n\n/**\n * Displays a code block with monospace formatting and an optional copy button.\n * Does not include a syntax highlighter — avoids the heavy `react-syntax-highlighter`\n * dependency. Wrap with `AiCodeBlockCopyButton` for clipboard support.\n *\n * @example\n * ```tsx\n * <AiCodeBlock code={`const x = 1;`} language=\"ts\">\n * <AiCodeBlockCopyButton />\n * </AiCodeBlock>\n * ```\n */\nexport const AiCodeBlock = memo(\n ({\n code,\n language,\n showLineNumbers = false,\n className,\n children,\n ...props\n }: AiCodeBlockProps) => {\n const highlightedLines = highlightToLines(highlight(code));\n\n return (\n <AiCodeBlockContext.Provider value={{ code }}>\n <div\n className={cn(\n \"relative w-full overflow-hidden rounded-lg border border-sf-line bg-sf-recessed font-mono text-sm text-sf-default\",\n className\n )}\n {...props}\n >\n {language && (\n <div className=\"flex items-center justify-between border-b border-sf-line px-4 py-2\">\n <span className=\"text-sf-subtle text-xs\">{language}</span>\n {children && (\n <div className=\"flex items-center gap-2\">{children}</div>\n )}\n </div>\n )}\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse\">\n <tbody>\n {highlightedLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: line index is stable\n <tr key={i} className=\"leading-6\">\n {showLineNumbers && (\n <td className=\"select-none pr-4 pl-4 text-right text-sf-inactive\">\n {i + 1}\n </td>\n )}\n <td className={cn(\"pr-4\", !showLineNumbers && \"px-4\")}>\n <pre className=\"whitespace-pre\">{line}</pre>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n {!language && children && (\n <div className=\"absolute top-2 right-2 flex items-center gap-2\">\n {children}\n </div>\n )}\n </div>\n </AiCodeBlockContext.Provider>\n );\n },\n (prev, next) =>\n prev.code === next.code &&\n prev.language === next.language &&\n prev.showLineNumbers === next.showLineNumbers\n);\n\nAiCodeBlock.displayName = \"AiCodeBlock\";\n\n// ─── AiCodeBlockCopyButton ───────────────────────────────────────────────────\n\nexport type AiCodeBlockCopyButtonProps = ComponentProps<typeof Button> & {\n /** Called after a successful copy. */\n onCopy?: () => void;\n /** Called if the clipboard API is unavailable or errors. */\n onError?: (error: Error) => void;\n /** How long (ms) to show the \"copied\" state. @default 2000 */\n timeout?: number;\n};\n\n/**\n * Copy-to-clipboard button for use inside `AiCodeBlock`.\n * Must be a descendant of `AiCodeBlock` to access the code via context.\n *\n * @example\n * ```tsx\n * <AiCodeBlock code=\"const x = 1;\" language=\"ts\">\n * <AiCodeBlockCopyButton />\n * </AiCodeBlock>\n * ```\n */\nexport function AiCodeBlockCopyButton({\n onCopy,\n onError,\n timeout = 2000,\n children,\n className,\n size = \"sm\",\n variant = \"ghost\",\n ...props\n}: AiCodeBlockCopyButtonProps) {\n const [copied, setCopied] = useState(false);\n const { code } = useContext(AiCodeBlockContext);\n\n const handleCopy = async () => {\n if (typeof navigator === \"undefined\" || !navigator.clipboard?.writeText) {\n onError?.(new Error(\"Clipboard API not available\"));\n return;\n }\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n onCopy?.();\n setTimeout(() => setCopied(false), timeout);\n } catch (err) {\n onError?.(err as Error);\n }\n };\n\n return (\n <Button\n className={cn(\"shrink-0\", className)}\n onClick={handleCopy}\n size={size}\n variant={variant}\n {...props}\n >\n {children ?? (copied ? \"Copied\" : \"Copy\")}\n </Button>\n );\n}\n"],"mappings":";;;;;;;;AAYA,IAAa,4BAA4B,EAAE;AAC3C,IAAa,oCAAoC,EAAE;AAQnD,IAAM,qBAAqB,cAAuC,EAAE,MAAM,IAAI,CAAC;;;;;;;;;;;;;AA2B/E,IAAa,cAAc,MACxB,EACC,MACA,UACA,kBAAkB,OAClB,WACA,UACA,GAAG,YACmB;CACtB,MAAM,mBAAmB,iBAAiB,UAAU,KAAK,CAAC;AAE1D,QACE,oBAAC,mBAAmB,UAApB;EAA6B,OAAO,EAAE,MAAM;YAC1C,qBAAC,OAAD;GACE,WAAW,GACT,qHACA,UACD;GACD,GAAI;aALN;IAOG,YACC,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,QAAD;MAAM,WAAU;gBAA0B;MAAgB,CAAA,EACzD,YACC,oBAAC,OAAD;MAAK,WAAU;MAA2B;MAAe,CAAA,CAEvD;;IAER,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,SAAD;MAAO,WAAU;gBACf,oBAAC,SAAD,EAAA,UACG,iBAAiB,KAAK,MAAM,MAE3B,qBAAC,MAAD;OAAY,WAAU;iBAAtB,CACG,mBACC,oBAAC,MAAD;QAAI,WAAU;kBACX,IAAI;QACF,CAAA,EAEP,oBAAC,MAAD;QAAI,WAAW,GAAG,QAAQ,CAAC,mBAAmB,OAAO;kBACnD,oBAAC,OAAD;SAAK,WAAU;mBAAkB;SAAW,CAAA;QACzC,CAAA,CACF;SATI,EASJ,CACL,EACI,CAAA;MACF,CAAA;KACJ,CAAA;IACL,CAAC,YAAY,YACZ,oBAAC,OAAD;KAAK,WAAU;KACZ;KACG,CAAA;IAEJ;;EACsB,CAAA;IAGjC,MAAM,SACL,KAAK,SAAS,KAAK,QACnB,KAAK,aAAa,KAAK,YACvB,KAAK,oBAAoB,KAAK,gBACjC;AAED,YAAY,cAAc;;;;;;;;;;;;AAwB1B,SAAgB,sBAAsB,EACpC,QACA,SACA,UAAU,KACV,UACA,WACA,OAAO,MACP,UAAU,SACV,GAAG,SAC0B;CAC7B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,EAAE,SAAS,WAAW,mBAAmB;CAE/C,MAAM,aAAa,YAAY;AAC7B,MAAI,OAAO,cAAc,eAAe,CAAC,UAAU,WAAW,WAAW;AACvE,6BAAU,IAAI,MAAM,8BAA8B,CAAC;AACnD;;AAEF,MAAI;AACF,SAAM,UAAU,UAAU,UAAU,KAAK;AACzC,aAAU,KAAK;AACf,aAAU;AACV,oBAAiB,UAAU,MAAM,EAAE,QAAQ;WACpC,KAAK;AACZ,aAAU,IAAa;;;AAI3B,QACE,oBAAC,QAAD;EACE,WAAW,GAAG,YAAY,UAAU;EACpC,SAAS;EACH;EACG;EACT,GAAI;YAEH,aAAa,SAAS,WAAW;EAC3B,CAAA"}
1
+ {"version":3,"file":"ai-code-block-BgtIxtZZ.js","names":[],"sources":["../src/components/ai-code-block/ai-code-block.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, HTMLAttributes, ReactNode } from \"react\";\nimport { createContext, memo, useContext, useState } from \"react\";\nimport { highlight } from \"sugar-high\";\n\nimport { cn } from \"../../utils/cn\";\nimport { highlightToLines } from \"../../utils/highlight-to-react\";\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_CODE_BLOCK_VARIANTS = {} as const;\nexport const SF_AI_CODE_BLOCK_DEFAULT_VARIANTS = {} as const;\n\n// ─── Context ─────────────────────────────────────────────────────────────────\n\ninterface AiCodeBlockContextValue {\n code: string;\n}\n\nconst AiCodeBlockContext = createContext<AiCodeBlockContextValue>({ code: \"\" });\n\n// ─── AiCodeBlock ─────────────────────────────────────────────────────────────\n\nexport type AiCodeBlockProps = HTMLAttributes<HTMLDivElement> & {\n /** The raw code string to display and make copyable. */\n code: string;\n /** Language identifier for syntax highlighting (display only, no highlighting applied). */\n language?: string;\n /** Show line numbers. @default false */\n showLineNumbers?: boolean;\n /** Extra content rendered in the top-right overlay (e.g. copy button). */\n children?: ReactNode;\n};\n\n/**\n * Displays a code block with monospace formatting and an optional copy button.\n * Does not include a syntax highlighter — avoids the heavy `react-syntax-highlighter`\n * dependency. Wrap with `AiCodeBlockCopyButton` for clipboard support.\n *\n * @example\n * ```tsx\n * <AiCodeBlock code={`const x = 1;`} language=\"ts\">\n * <AiCodeBlockCopyButton />\n * </AiCodeBlock>\n * ```\n */\nexport const AiCodeBlock = memo(\n ({\n code,\n language,\n showLineNumbers = false,\n className,\n children,\n ...props\n }: AiCodeBlockProps) => {\n const highlightedLines = highlightToLines(highlight(code));\n\n return (\n <AiCodeBlockContext.Provider value={{ code }}>\n <div\n className={cn(\n \"relative w-full overflow-hidden rounded-lg border border-sf-line bg-sf-recessed font-mono text-sm text-sf-default\",\n className\n )}\n {...props}\n >\n {language && (\n <div className=\"flex items-center justify-between border-b border-sf-line px-4 py-2\">\n <span className=\"text-sf-subtle text-xs\">{language}</span>\n {children && (\n <div className=\"flex items-center gap-2\">{children}</div>\n )}\n </div>\n )}\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse\">\n <tbody>\n {highlightedLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: line index is stable\n <tr key={i} className=\"leading-6\">\n {showLineNumbers && (\n <td className=\"select-none pr-4 pl-4 text-right text-sf-inactive\">\n {i + 1}\n </td>\n )}\n <td className={cn(\"pr-4\", !showLineNumbers && \"px-4\")}>\n <pre className=\"whitespace-pre\">{line}</pre>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n {!language && children && (\n <div className=\"absolute top-2 right-2 flex items-center gap-2\">\n {children}\n </div>\n )}\n </div>\n </AiCodeBlockContext.Provider>\n );\n },\n (prev, next) =>\n prev.code === next.code &&\n prev.language === next.language &&\n prev.showLineNumbers === next.showLineNumbers\n);\n\nAiCodeBlock.displayName = \"AiCodeBlock\";\n\n// ─── AiCodeBlockCopyButton ───────────────────────────────────────────────────\n\nexport type AiCodeBlockCopyButtonProps = ComponentProps<typeof Button> & {\n /** Called after a successful copy. */\n onCopy?: () => void;\n /** Called if the clipboard API is unavailable or errors. */\n onError?: (error: Error) => void;\n /** How long (ms) to show the \"copied\" state. @default 2000 */\n timeout?: number;\n};\n\n/**\n * Copy-to-clipboard button for use inside `AiCodeBlock`.\n * Must be a descendant of `AiCodeBlock` to access the code via context.\n *\n * @example\n * ```tsx\n * <AiCodeBlock code=\"const x = 1;\" language=\"ts\">\n * <AiCodeBlockCopyButton />\n * </AiCodeBlock>\n * ```\n */\nexport function AiCodeBlockCopyButton({\n onCopy,\n onError,\n timeout = 2000,\n children,\n className,\n size = \"sm\",\n variant = \"ghost\",\n ...props\n}: AiCodeBlockCopyButtonProps) {\n const [copied, setCopied] = useState(false);\n const { code } = useContext(AiCodeBlockContext);\n\n const handleCopy = async () => {\n if (typeof navigator === \"undefined\" || !navigator.clipboard?.writeText) {\n onError?.(new Error(\"Clipboard API not available\"));\n return;\n }\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n onCopy?.();\n setTimeout(() => setCopied(false), timeout);\n } catch (err) {\n onError?.(err as Error);\n }\n };\n\n return (\n <Button\n className={cn(\"shrink-0\", className)}\n onClick={handleCopy}\n size={size}\n variant={variant}\n {...props}\n >\n {children ?? (copied ? \"Copied\" : \"Copy\")}\n </Button>\n );\n}\n"],"mappings":";;;;;;;;AAYA,IAAa,4BAA4B,EAAE;AAC3C,IAAa,oCAAoC,EAAE;AAQnD,IAAM,qBAAqB,cAAuC,EAAE,MAAM,IAAI,CAAC;;;;;;;;;;;;;AA2B/E,IAAa,cAAc,MACxB,EACC,MACA,UACA,kBAAkB,OAClB,WACA,UACA,GAAG,YACmB;CACtB,MAAM,mBAAmB,iBAAiB,UAAU,KAAK,CAAC;AAE1D,QACE,oBAAC,mBAAmB,UAApB;EAA6B,OAAO,EAAE,MAAM;YAC1C,qBAAC,OAAD;GACE,WAAW,GACT,qHACA,UACD;GACD,GAAI;aALN;IAOG,YACC,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,QAAD;MAAM,WAAU;gBAA0B;MAAgB,CAAA,EACzD,YACC,oBAAC,OAAD;MAAK,WAAU;MAA2B;MAAe,CAAA,CAEvD;;IAER,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,SAAD;MAAO,WAAU;gBACf,oBAAC,SAAD,EAAA,UACG,iBAAiB,KAAK,MAAM,MAE3B,qBAAC,MAAD;OAAY,WAAU;iBAAtB,CACG,mBACC,oBAAC,MAAD;QAAI,WAAU;kBACX,IAAI;QACF,CAAA,EAEP,oBAAC,MAAD;QAAI,WAAW,GAAG,QAAQ,CAAC,mBAAmB,OAAO;kBACnD,oBAAC,OAAD;SAAK,WAAU;mBAAkB;SAAW,CAAA;QACzC,CAAA,CACF;SATI,EASJ,CACL,EACI,CAAA;MACF,CAAA;KACJ,CAAA;IACL,CAAC,YAAY,YACZ,oBAAC,OAAD;KAAK,WAAU;KACZ;KACG,CAAA;IAEJ;;EACsB,CAAA;IAGjC,MAAM,SACL,KAAK,SAAS,KAAK,QACnB,KAAK,aAAa,KAAK,YACvB,KAAK,oBAAoB,KAAK,gBACjC;AAED,YAAY,cAAc;;;;;;;;;;;;AAwB1B,SAAgB,sBAAsB,EACpC,QACA,SACA,UAAU,KACV,UACA,WACA,OAAO,MACP,UAAU,SACV,GAAG,SAC0B;CAC7B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,EAAE,SAAS,WAAW,mBAAmB;CAE/C,MAAM,aAAa,YAAY;AAC7B,MAAI,OAAO,cAAc,eAAe,CAAC,UAAU,WAAW,WAAW;AACvE,6BAAU,IAAI,MAAM,8BAA8B,CAAC;AACnD;;AAEF,MAAI;AACF,SAAM,UAAU,UAAU,UAAU,KAAK;AACzC,aAAU,KAAK;AACf,aAAU;AACV,oBAAiB,UAAU,MAAM,EAAE,QAAQ;WACpC,KAAK;AACZ,aAAU,IAAa;;;AAI3B,QACE,oBAAC,QAAD;EACE,WAAW,GAAG,YAAY,UAAU;EACpC,SAAS;EACH;EACG;EACT,GAAI;YAEH,aAAa,SAAS,WAAW;EAC3B,CAAA"}
@@ -181,4 +181,4 @@ function useConversationVirtualizer({ scrollRef, items, estimateSize = 80, overs
181
181
  //#endregion
182
182
  export { SF_AI_CONVERSATION_VARIANTS as a, SF_AI_CONVERSATION_DEFAULT_VARIANTS as i, AiConversationContent as n, useConversationVirtualizer as o, AiConversationScrollButton as r, AiConversation as t };
183
183
 
184
- //# sourceMappingURL=ai-conversation-DYtExcrw.js.map
184
+ //# sourceMappingURL=ai-conversation-CArP7C8K.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-conversation-DYtExcrw.js","names":[],"sources":["../src/components/ai-conversation/ai-conversation.tsx"],"sourcesContent":["\"use client\";\n\nimport { ArrowDownIcon } from \"@phosphor-icons/react\";\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\nimport type { Virtualizer, VirtualItem } from \"@tanstack/react-virtual\";\nimport type { ComponentProps, ReactNode, RefObject } from \"react\";\nimport { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_CONVERSATION_VARIANTS = {} as const;\nexport const SF_AI_CONVERSATION_DEFAULT_VARIANTS = {} as const;\n\n// ─── AiConversation ──────────────────────────────────────────────────────────\n\nexport type AiConversationProps = ComponentProps<\"div\">;\n\n/**\n * Outer scroll container for a conversation. Sticks to the bottom as new\n * messages are added. Pair with `AiConversationContent` and optionally\n * `AiConversationScrollButton`.\n *\n * @example\n * ```tsx\n * <AiConversation ref={scrollRef}>\n * <AiConversationContent>\n * {messages.map(m => <AiMessage key={m.id} from={m.role}>...</AiMessage>)}\n * </AiConversationContent>\n * <AiConversationScrollButton scrollRef={scrollRef} />\n * </AiConversation>\n * ```\n */\nexport const AiConversation = forwardRef<HTMLDivElement, AiConversationProps>(\n ({ className, children, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\"relative flex-1 overflow-y-auto\", className)}\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Conversation\"\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nAiConversation.displayName = \"AiConversation\";\n\n// ─── AiConversationContent ───────────────────────────────────────────────────\n\nexport type AiConversationContentProps = ComponentProps<\"div\">;\n\n/**\n * Inner content wrapper with padding. Place message components inside here.\n */\nexport function AiConversationContent({\n className,\n children,\n ...props\n}: AiConversationContentProps) {\n return (\n <div\n className={cn(\"flex flex-col gap-0 overscroll-y-none p-4\", className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\n// ─── AiConversationScrollButton ──────────────────────────────────────────────\n\nexport type AiConversationScrollButtonProps = ComponentProps<typeof Button> & {\n /**\n * Ref to the scroll container (the `AiConversation` element).\n * When omitted, the button is always visible.\n */\n scrollRef?: RefObject<HTMLElement | null>;\n /** Scroll threshold in pixels from bottom to show the button. @default 100 */\n threshold?: number;\n};\n\n/**\n * Floating \"scroll to bottom\" button. Appears when the user has scrolled up\n * more than `threshold` pixels from the bottom.\n */\nexport function AiConversationScrollButton({\n scrollRef,\n threshold = 100,\n className,\n onClick,\n ...props\n}: AiConversationScrollButtonProps) {\n const [show, setShow] = useState(false);\n\n useEffect(() => {\n const el = scrollRef?.current;\n if (!el) return;\n\n const check = () => {\n const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;\n setShow(distFromBottom > threshold);\n };\n\n check();\n el.addEventListener(\"scroll\", check, { passive: true });\n return () => el.removeEventListener(\"scroll\", check);\n }, [scrollRef, threshold]);\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n scrollRef?.current?.scrollTo({\n top: scrollRef.current.scrollHeight,\n behavior: \"smooth\",\n });\n onClick?.(e);\n },\n [scrollRef, onClick]\n );\n\n if (scrollRef && !show) return null;\n\n return (\n <Button\n aria-label=\"Scroll to bottom\"\n className={cn(\n \"absolute bottom-4 left-1/2 -translate-x-1/2 rounded-full shadow-md\",\n className\n )}\n onClick={handleClick}\n size=\"sm\"\n variant=\"secondary\"\n {...props}\n >\n <ArrowDownIcon className=\"size-4\" />\n </Button>\n );\n}\n\n// ─── Virtualized Conversation ────────────────────────────────────────────────\n\n/** A single item in the virtualized conversation list. */\nexport interface ConversationItem {\n /** Unique key for this item. */\n key: string;\n /**\n * Render the item content. The virtualizer handles measurement and\n * positioning on the wrapper — just return the content.\n */\n render: () => ReactNode;\n}\n\nexport interface UseConversationVirtualizerOptions {\n /** Ref to the scroll container element. */\n scrollRef: RefObject<HTMLElement | null>;\n /** The conversation items to virtualize. */\n items: ConversationItem[];\n /**\n * Estimated height of a single item in pixels.\n * Used before measurement. Doesn't need to be exact.\n * @default 80\n */\n estimateSize?: number;\n /**\n * Number of items to render beyond the visible area.\n * @default 8\n */\n overscan?: number;\n /**\n * Pixel distance from bottom to consider \"at bottom\" for stick-to-bottom.\n * @default 40\n */\n stickThreshold?: number;\n}\n\nexport interface UseConversationVirtualizerReturn {\n /** The TanStack virtualizer instance. */\n virtualizer: Virtualizer<HTMLElement, Element>;\n /** The virtual items to render. */\n virtualItems: VirtualItem[];\n /** Total measured/estimated height of all items. */\n totalSize: number;\n /** Whether the user is currently at the bottom of the conversation. */\n isAtBottom: boolean;\n /** Smoothly scroll to the bottom. */\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n /** Get the measurement ref for a virtual item by index. */\n measureRef: (node: HTMLElement | null) => void;\n}\n\n/**\n * `useConversationVirtualizer` — virtualizes a conversation list with\n * dynamic item heights and automatic stick-to-bottom behavior.\n *\n * Built on `@tanstack/react-virtual`. Items are measured dynamically via\n * `ResizeObserver`, so tool calls expanding, subagents collapsing, and\n * streaming text growing all work correctly.\n *\n * **Stick-to-bottom**: When the user is scrolled to (or near) the bottom,\n * new items and item resizes automatically keep the viewport pinned to the\n * bottom. When the user scrolls up, stick-to-bottom disengages until they\n * scroll back down.\n *\n * @example\n * ```tsx\n * const { virtualItems, totalSize, measureRef, isAtBottom, scrollToBottom } =\n * useConversationVirtualizer({ scrollRef, items });\n *\n * return (\n * <AiConversation ref={scrollRef}>\n * <div style={{ height: totalSize, position: \"relative\" }}>\n * {virtualItems.map((vi) => {\n * const item = items[vi.index];\n * return (\n * <div key={item.key} ref={measureRef} data-index={vi.index}\n * style={{ position: \"absolute\", top: vi.start, width: \"100%\" }}>\n * {item.render()}\n * </div>\n * );\n * })}\n * </div>\n * {!isAtBottom && <button onClick={() => scrollToBottom()}>↓</button>}\n * </AiConversation>\n * );\n * ```\n */\nexport function useConversationVirtualizer({\n scrollRef,\n items,\n estimateSize = 80,\n overscan = 8,\n stickThreshold = 40,\n}: UseConversationVirtualizerOptions): UseConversationVirtualizerReturn {\n const [isAtBottom, setIsAtBottom] = useState(true);\n const isAtBottomRef = useRef(true);\n const prevCountRef = useRef(items.length);\n\n // Track whether user is at the bottom\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n\n const check = () => {\n const dist = el.scrollHeight - el.scrollTop - el.clientHeight;\n const atBottom = dist <= stickThreshold;\n isAtBottomRef.current = atBottom;\n setIsAtBottom(atBottom);\n };\n\n check();\n el.addEventListener(\"scroll\", check, { passive: true });\n return () => el.removeEventListener(\"scroll\", check);\n }, [scrollRef, stickThreshold]);\n\n // Track the previous totalSize so we can detect growth from item resizes\n const prevTotalSizeRef = useRef(0);\n\n const virtualizer = useVirtualizer({\n count: items.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => estimateSize,\n overscan,\n getItemKey: (index) => items[index]?.key ?? index,\n });\n\n // When items resize (streaming text, collapsible expand) and we're at\n // bottom, adjust scroll so the bottom stays pinned.\n virtualizer.shouldAdjustScrollPositionOnItemSizeChange = () =>\n isAtBottomRef.current;\n\n const virtualItems = virtualizer.getVirtualItems();\n const totalSize = virtualizer.getTotalSize();\n\n // When new items are added and we're at bottom, scroll to the end\n useEffect(() => {\n const count = items.length;\n if (count > prevCountRef.current && isAtBottomRef.current) {\n requestAnimationFrame(() => {\n virtualizer.scrollToIndex(count - 1, {\n align: \"end\",\n behavior: \"smooth\",\n });\n });\n }\n prevCountRef.current = count;\n }, [items.length, virtualizer]);\n\n // When totalSize grows (item content expanded, streaming text grew)\n // and we're stuck to bottom, smoothly follow by scrolling down.\n // This complements shouldAdjustScrollPositionOnItemSizeChange which\n // handles the instantaneous adjustment — this handles the smooth follow.\n useEffect(() => {\n const prev = prevTotalSizeRef.current;\n prevTotalSizeRef.current = totalSize;\n\n if (!isAtBottomRef.current || totalSize <= prev) return;\n\n const el = scrollRef.current;\n if (!el) return;\n\n // Only nudge if we're slightly off — the shouldAdjust callback handles\n // most cases, but measurement batching can leave a small gap.\n const dist = el.scrollHeight - el.scrollTop - el.clientHeight;\n if (dist > 1 && dist < 300) {\n el.scrollTop = el.scrollHeight - el.clientHeight;\n }\n }, [totalSize, scrollRef]);\n\n const scrollToBottom = useCallback(\n (behavior: ScrollBehavior = \"smooth\") => {\n if (items.length === 0) return;\n virtualizer.scrollToIndex(items.length - 1, { align: \"end\", behavior });\n },\n [virtualizer, items.length]\n );\n\n const measureRef = useCallback(\n (node: HTMLElement | null) => {\n if (node) virtualizer.measureElement(node);\n },\n [virtualizer]\n );\n\n return {\n virtualizer,\n virtualItems,\n totalSize,\n isAtBottom,\n scrollToBottom,\n measureRef,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai-conversation-CArP7C8K.js","names":[],"sources":["../src/components/ai-conversation/ai-conversation.tsx"],"sourcesContent":["\"use client\";\n\nimport { ArrowDownIcon } from \"@phosphor-icons/react\";\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\nimport type { Virtualizer, VirtualItem } from \"@tanstack/react-virtual\";\nimport type { ComponentProps, ReactNode, RefObject } from \"react\";\nimport { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_CONVERSATION_VARIANTS = {} as const;\nexport const SF_AI_CONVERSATION_DEFAULT_VARIANTS = {} as const;\n\n// ─── AiConversation ──────────────────────────────────────────────────────────\n\nexport type AiConversationProps = ComponentProps<\"div\">;\n\n/**\n * Outer scroll container for a conversation. Sticks to the bottom as new\n * messages are added. Pair with `AiConversationContent` and optionally\n * `AiConversationScrollButton`.\n *\n * @example\n * ```tsx\n * <AiConversation ref={scrollRef}>\n * <AiConversationContent>\n * {messages.map(m => <AiMessage key={m.id} from={m.role}>...</AiMessage>)}\n * </AiConversationContent>\n * <AiConversationScrollButton scrollRef={scrollRef} />\n * </AiConversation>\n * ```\n */\nexport const AiConversation = forwardRef<HTMLDivElement, AiConversationProps>(\n ({ className, children, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\"relative flex-1 overflow-y-auto\", className)}\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Conversation\"\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nAiConversation.displayName = \"AiConversation\";\n\n// ─── AiConversationContent ───────────────────────────────────────────────────\n\nexport type AiConversationContentProps = ComponentProps<\"div\">;\n\n/**\n * Inner content wrapper with padding. Place message components inside here.\n */\nexport function AiConversationContent({\n className,\n children,\n ...props\n}: AiConversationContentProps) {\n return (\n <div\n className={cn(\"flex flex-col gap-0 overscroll-y-none p-4\", className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\n// ─── AiConversationScrollButton ──────────────────────────────────────────────\n\nexport type AiConversationScrollButtonProps = ComponentProps<typeof Button> & {\n /**\n * Ref to the scroll container (the `AiConversation` element).\n * When omitted, the button is always visible.\n */\n scrollRef?: RefObject<HTMLElement | null>;\n /** Scroll threshold in pixels from bottom to show the button. @default 100 */\n threshold?: number;\n};\n\n/**\n * Floating \"scroll to bottom\" button. Appears when the user has scrolled up\n * more than `threshold` pixels from the bottom.\n */\nexport function AiConversationScrollButton({\n scrollRef,\n threshold = 100,\n className,\n onClick,\n ...props\n}: AiConversationScrollButtonProps) {\n const [show, setShow] = useState(false);\n\n useEffect(() => {\n const el = scrollRef?.current;\n if (!el) return;\n\n const check = () => {\n const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;\n setShow(distFromBottom > threshold);\n };\n\n check();\n el.addEventListener(\"scroll\", check, { passive: true });\n return () => el.removeEventListener(\"scroll\", check);\n }, [scrollRef, threshold]);\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n scrollRef?.current?.scrollTo({\n top: scrollRef.current.scrollHeight,\n behavior: \"smooth\",\n });\n onClick?.(e);\n },\n [scrollRef, onClick]\n );\n\n if (scrollRef && !show) return null;\n\n return (\n <Button\n aria-label=\"Scroll to bottom\"\n className={cn(\n \"absolute bottom-4 left-1/2 -translate-x-1/2 rounded-full shadow-md\",\n className\n )}\n onClick={handleClick}\n size=\"sm\"\n variant=\"secondary\"\n {...props}\n >\n <ArrowDownIcon className=\"size-4\" />\n </Button>\n );\n}\n\n// ─── Virtualized Conversation ────────────────────────────────────────────────\n\n/** A single item in the virtualized conversation list. */\nexport interface ConversationItem {\n /** Unique key for this item. */\n key: string;\n /**\n * Render the item content. The virtualizer handles measurement and\n * positioning on the wrapper — just return the content.\n */\n render: () => ReactNode;\n}\n\nexport interface UseConversationVirtualizerOptions {\n /** Ref to the scroll container element. */\n scrollRef: RefObject<HTMLElement | null>;\n /** The conversation items to virtualize. */\n items: ConversationItem[];\n /**\n * Estimated height of a single item in pixels.\n * Used before measurement. Doesn't need to be exact.\n * @default 80\n */\n estimateSize?: number;\n /**\n * Number of items to render beyond the visible area.\n * @default 8\n */\n overscan?: number;\n /**\n * Pixel distance from bottom to consider \"at bottom\" for stick-to-bottom.\n * @default 40\n */\n stickThreshold?: number;\n}\n\nexport interface UseConversationVirtualizerReturn {\n /** The TanStack virtualizer instance. */\n virtualizer: Virtualizer<HTMLElement, Element>;\n /** The virtual items to render. */\n virtualItems: VirtualItem[];\n /** Total measured/estimated height of all items. */\n totalSize: number;\n /** Whether the user is currently at the bottom of the conversation. */\n isAtBottom: boolean;\n /** Smoothly scroll to the bottom. */\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n /** Get the measurement ref for a virtual item by index. */\n measureRef: (node: HTMLElement | null) => void;\n}\n\n/**\n * `useConversationVirtualizer` — virtualizes a conversation list with\n * dynamic item heights and automatic stick-to-bottom behavior.\n *\n * Built on `@tanstack/react-virtual`. Items are measured dynamically via\n * `ResizeObserver`, so tool calls expanding, subagents collapsing, and\n * streaming text growing all work correctly.\n *\n * **Stick-to-bottom**: When the user is scrolled to (or near) the bottom,\n * new items and item resizes automatically keep the viewport pinned to the\n * bottom. When the user scrolls up, stick-to-bottom disengages until they\n * scroll back down.\n *\n * @example\n * ```tsx\n * const { virtualItems, totalSize, measureRef, isAtBottom, scrollToBottom } =\n * useConversationVirtualizer({ scrollRef, items });\n *\n * return (\n * <AiConversation ref={scrollRef}>\n * <div style={{ height: totalSize, position: \"relative\" }}>\n * {virtualItems.map((vi) => {\n * const item = items[vi.index];\n * return (\n * <div key={item.key} ref={measureRef} data-index={vi.index}\n * style={{ position: \"absolute\", top: vi.start, width: \"100%\" }}>\n * {item.render()}\n * </div>\n * );\n * })}\n * </div>\n * {!isAtBottom && <button onClick={() => scrollToBottom()}>↓</button>}\n * </AiConversation>\n * );\n * ```\n */\nexport function useConversationVirtualizer({\n scrollRef,\n items,\n estimateSize = 80,\n overscan = 8,\n stickThreshold = 40,\n}: UseConversationVirtualizerOptions): UseConversationVirtualizerReturn {\n const [isAtBottom, setIsAtBottom] = useState(true);\n const isAtBottomRef = useRef(true);\n const prevCountRef = useRef(items.length);\n\n // Track whether user is at the bottom\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n\n const check = () => {\n const dist = el.scrollHeight - el.scrollTop - el.clientHeight;\n const atBottom = dist <= stickThreshold;\n isAtBottomRef.current = atBottom;\n setIsAtBottom(atBottom);\n };\n\n check();\n el.addEventListener(\"scroll\", check, { passive: true });\n return () => el.removeEventListener(\"scroll\", check);\n }, [scrollRef, stickThreshold]);\n\n // Track the previous totalSize so we can detect growth from item resizes\n const prevTotalSizeRef = useRef(0);\n\n const virtualizer = useVirtualizer({\n count: items.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => estimateSize,\n overscan,\n getItemKey: (index) => items[index]?.key ?? index,\n });\n\n // When items resize (streaming text, collapsible expand) and we're at\n // bottom, adjust scroll so the bottom stays pinned.\n virtualizer.shouldAdjustScrollPositionOnItemSizeChange = () =>\n isAtBottomRef.current;\n\n const virtualItems = virtualizer.getVirtualItems();\n const totalSize = virtualizer.getTotalSize();\n\n // When new items are added and we're at bottom, scroll to the end\n useEffect(() => {\n const count = items.length;\n if (count > prevCountRef.current && isAtBottomRef.current) {\n requestAnimationFrame(() => {\n virtualizer.scrollToIndex(count - 1, {\n align: \"end\",\n behavior: \"smooth\",\n });\n });\n }\n prevCountRef.current = count;\n }, [items.length, virtualizer]);\n\n // When totalSize grows (item content expanded, streaming text grew)\n // and we're stuck to bottom, smoothly follow by scrolling down.\n // This complements shouldAdjustScrollPositionOnItemSizeChange which\n // handles the instantaneous adjustment — this handles the smooth follow.\n useEffect(() => {\n const prev = prevTotalSizeRef.current;\n prevTotalSizeRef.current = totalSize;\n\n if (!isAtBottomRef.current || totalSize <= prev) return;\n\n const el = scrollRef.current;\n if (!el) return;\n\n // Only nudge if we're slightly off — the shouldAdjust callback handles\n // most cases, but measurement batching can leave a small gap.\n const dist = el.scrollHeight - el.scrollTop - el.clientHeight;\n if (dist > 1 && dist < 300) {\n el.scrollTop = el.scrollHeight - el.clientHeight;\n }\n }, [totalSize, scrollRef]);\n\n const scrollToBottom = useCallback(\n (behavior: ScrollBehavior = \"smooth\") => {\n if (items.length === 0) return;\n virtualizer.scrollToIndex(items.length - 1, { align: \"end\", behavior });\n },\n [virtualizer, items.length]\n );\n\n const measureRef = useCallback(\n (node: HTMLElement | null) => {\n if (node) virtualizer.measureElement(node);\n },\n [virtualizer]\n );\n\n return {\n virtualizer,\n virtualItems,\n totalSize,\n isAtBottom,\n scrollToBottom,\n measureRef,\n };\n}\n"],"mappings":"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"}
@@ -73,4 +73,4 @@ AiInfoBanner.displayName = "AiInfoBanner";
73
73
  //#endregion
74
74
  export { SF_AI_INFO_BANNER_DEFAULT_VARIANTS as n, SF_AI_INFO_BANNER_VARIANTS as r, AiInfoBanner as t };
75
75
 
76
- //# sourceMappingURL=ai-info-banner-BpzauUAY.js.map
76
+ //# sourceMappingURL=ai-info-banner-uFxHHwBA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-info-banner-BpzauUAY.js","names":[],"sources":["../src/components/ai-info-banner/ai-info-banner.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n InfoIcon,\n WarningCircleIcon,\n ArrowsClockwiseIcon,\n} from \"@phosphor-icons/react\";\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_INFO_BANNER_VARIANTS = {\n level: {\n info: { classes: \"\", description: \"Informational notice\" },\n error: { classes: \"\", description: \"Error notice\" },\n change: {\n classes: \"\",\n description: \"State change notice (model/mode switch)\",\n },\n },\n} as const;\n\nexport const SF_AI_INFO_BANNER_DEFAULT_VARIANTS = {\n level: \"info\",\n} as const;\n\nexport type SFAiInfoBannerLevel = keyof typeof SF_AI_INFO_BANNER_VARIANTS.level;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiInfoBannerProps = HTMLAttributes<HTMLDivElement> & {\n /** Banner level controls icon and color treatment. */\n level?: SFAiInfoBannerLevel;\n /** Custom icon override. */\n icon?: ElementType;\n /** Content to display. */\n children: ReactNode;\n};\n\n// ─── Level config ─────────────────────────────────────────────────────────────\n\nconst LEVEL_CONFIG: Record<\n SFAiInfoBannerLevel,\n { icon: ElementType; iconClassName: string; borderClassName: string }\n> = {\n info: {\n icon: InfoIcon,\n iconClassName: \"text-sf-info\",\n borderClassName: \"border-sf-info/20\",\n },\n error: {\n icon: WarningCircleIcon,\n iconClassName: \"text-sf-danger\",\n borderClassName: \"border-sf-danger/20\",\n },\n change: {\n icon: ArrowsClockwiseIcon,\n iconClassName: \"text-sf-subtle\",\n borderClassName: \"border-sf-line\",\n },\n};\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Inline conversation banner for system notices. Renders as a compact,\n * horizontally centered divider-like notice rather than a chat bubble.\n *\n * Maps to harness events: `error`, `info`, `mode_changed`, `model_changed`.\n *\n * @example\n * ```tsx\n * <AiInfoBanner level=\"error\">\n * Connection lost. Retrying in 3s…\n * </AiInfoBanner>\n *\n * <AiInfoBanner level=\"change\">\n * Switched to claude-opus-4-6\n * </AiInfoBanner>\n *\n * <AiInfoBanner level=\"info\">\n * Follow-up queued — will send after current response\n * </AiInfoBanner>\n * ```\n */\nexport function AiInfoBanner({\n level = \"info\",\n icon,\n children,\n className,\n ...props\n}: AiInfoBannerProps) {\n const config = LEVEL_CONFIG[level];\n const IconComponent = icon ?? config.icon;\n\n return (\n <div\n className={cn(\n \"flex items-center justify-center gap-2 border-y px-4 py-1.5\",\n config.borderClassName,\n className\n )}\n role={level === \"error\" ? \"alert\" : \"status\"}\n {...props}\n >\n <IconComponent\n className={cn(\"size-3.5 shrink-0\", config.iconClassName)}\n />\n <span className=\"text-xs text-sf-subtle\">{children}</span>\n </div>\n );\n}\n\nAiInfoBanner.displayName = \"AiInfoBanner\";\n"],"mappings":";;;;;AAaA,IAAa,6BAA6B,EACxC,OAAO;CACL,MAAM;EAAE,SAAS;EAAI,aAAa;EAAwB;CAC1D,OAAO;EAAE,SAAS;EAAI,aAAa;EAAgB;CACnD,QAAQ;EACN,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,qCAAqC,EAChD,OAAO,QACR;AAiBD,IAAM,eAGF;CACF,MAAM;EACJ,MAAM;EACN,eAAe;EACf,iBAAiB;EAClB;CACD,OAAO;EACL,MAAM;EACN,eAAe;EACf,iBAAiB;EAClB;CACD,QAAQ;EACN,MAAM;EACN,eAAe;EACf,iBAAiB;EAClB;CACF;;;;;;;;;;;;;;;;;;;;;;AAyBD,SAAgB,aAAa,EAC3B,QAAQ,QACR,MACA,UACA,WACA,GAAG,SACiB;CACpB,MAAM,SAAS,aAAa;CAC5B,MAAM,gBAAgB,QAAQ,OAAO;AAErC,QACE,qBAAC,OAAD;EACE,WAAW,GACT,+DACA,OAAO,iBACP,UACD;EACD,MAAM,UAAU,UAAU,UAAU;EACpC,GAAI;YAPN,CASE,oBAAC,eAAD,EACE,WAAW,GAAG,qBAAqB,OAAO,cAAc,EACxD,CAAA,EACF,oBAAC,QAAD;GAAM,WAAU;GAA0B;GAAgB,CAAA,CACtD;;;AAIV,aAAa,cAAc"}
1
+ {"version":3,"file":"ai-info-banner-uFxHHwBA.js","names":[],"sources":["../src/components/ai-info-banner/ai-info-banner.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n InfoIcon,\n WarningCircleIcon,\n ArrowsClockwiseIcon,\n} from \"@phosphor-icons/react\";\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_INFO_BANNER_VARIANTS = {\n level: {\n info: { classes: \"\", description: \"Informational notice\" },\n error: { classes: \"\", description: \"Error notice\" },\n change: {\n classes: \"\",\n description: \"State change notice (model/mode switch)\",\n },\n },\n} as const;\n\nexport const SF_AI_INFO_BANNER_DEFAULT_VARIANTS = {\n level: \"info\",\n} as const;\n\nexport type SFAiInfoBannerLevel = keyof typeof SF_AI_INFO_BANNER_VARIANTS.level;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiInfoBannerProps = HTMLAttributes<HTMLDivElement> & {\n /** Banner level controls icon and color treatment. */\n level?: SFAiInfoBannerLevel;\n /** Custom icon override. */\n icon?: ElementType;\n /** Content to display. */\n children: ReactNode;\n};\n\n// ─── Level config ─────────────────────────────────────────────────────────────\n\nconst LEVEL_CONFIG: Record<\n SFAiInfoBannerLevel,\n { icon: ElementType; iconClassName: string; borderClassName: string }\n> = {\n info: {\n icon: InfoIcon,\n iconClassName: \"text-sf-info\",\n borderClassName: \"border-sf-info/20\",\n },\n error: {\n icon: WarningCircleIcon,\n iconClassName: \"text-sf-danger\",\n borderClassName: \"border-sf-danger/20\",\n },\n change: {\n icon: ArrowsClockwiseIcon,\n iconClassName: \"text-sf-subtle\",\n borderClassName: \"border-sf-line\",\n },\n};\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Inline conversation banner for system notices. Renders as a compact,\n * horizontally centered divider-like notice rather than a chat bubble.\n *\n * Maps to harness events: `error`, `info`, `mode_changed`, `model_changed`.\n *\n * @example\n * ```tsx\n * <AiInfoBanner level=\"error\">\n * Connection lost. Retrying in 3s…\n * </AiInfoBanner>\n *\n * <AiInfoBanner level=\"change\">\n * Switched to claude-opus-4-6\n * </AiInfoBanner>\n *\n * <AiInfoBanner level=\"info\">\n * Follow-up queued — will send after current response\n * </AiInfoBanner>\n * ```\n */\nexport function AiInfoBanner({\n level = \"info\",\n icon,\n children,\n className,\n ...props\n}: AiInfoBannerProps) {\n const config = LEVEL_CONFIG[level];\n const IconComponent = icon ?? config.icon;\n\n return (\n <div\n className={cn(\n \"flex items-center justify-center gap-2 border-y px-4 py-1.5\",\n config.borderClassName,\n className\n )}\n role={level === \"error\" ? \"alert\" : \"status\"}\n {...props}\n >\n <IconComponent\n className={cn(\"size-3.5 shrink-0\", config.iconClassName)}\n />\n <span className=\"text-xs text-sf-subtle\">{children}</span>\n </div>\n );\n}\n\nAiInfoBanner.displayName = \"AiInfoBanner\";\n"],"mappings":";;;;;AAaA,IAAa,6BAA6B,EACxC,OAAO;CACL,MAAM;EAAE,SAAS;EAAI,aAAa;EAAwB;CAC1D,OAAO;EAAE,SAAS;EAAI,aAAa;EAAgB;CACnD,QAAQ;EACN,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,qCAAqC,EAChD,OAAO,QACR;AAiBD,IAAM,eAGF;CACF,MAAM;EACJ,MAAM;EACN,eAAe;EACf,iBAAiB;EAClB;CACD,OAAO;EACL,MAAM;EACN,eAAe;EACf,iBAAiB;EAClB;CACD,QAAQ;EACN,MAAM;EACN,eAAe;EACf,iBAAiB;EAClB;CACF;;;;;;;;;;;;;;;;;;;;;;AAyBD,SAAgB,aAAa,EAC3B,QAAQ,QACR,MACA,UACA,WACA,GAAG,SACiB;CACpB,MAAM,SAAS,aAAa;CAC5B,MAAM,gBAAgB,QAAQ,OAAO;AAErC,QACE,qBAAC,OAAD;EACE,WAAW,GACT,+DACA,OAAO,iBACP,UACD;EACD,MAAM,UAAU,UAAU,UAAU;EACpC,GAAI;YAPN,CASE,oBAAC,eAAD,EACE,WAAW,GAAG,qBAAqB,OAAO,cAAc,EACxD,CAAA,EACF,oBAAC,QAAD;GAAM,WAAU;GAA0B;GAAgB,CAAA,CACtD;;;AAIV,aAAa,cAAc"}
@@ -283,4 +283,4 @@ AiMessageAttachment.displayName = "AiMessageAttachment";
283
283
  //#endregion
284
284
  export { AiMessageAttachments as a, AiMessageBranchSelector as c, SF_AI_MESSAGE_DEFAULT_VARIANTS as d, SF_AI_MESSAGE_VARIANTS as f, AiMessageAttachment as i, AiMessageContent as l, AiMessageAction as n, AiMessageBranch as o, AiMessageActions as r, AiMessageBranchContent as s, AiMessage as t, AiMessageToolbar as u };
285
285
 
286
- //# sourceMappingURL=ai-message-CV8SBoHM.js.map
286
+ //# sourceMappingURL=ai-message-BjnFznXy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-message-CV8SBoHM.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
+ {"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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"}
@@ -168,4 +168,4 @@ AiMissionHeader.displayName = "AiMissionHeader";
168
168
  //#endregion
169
169
  export { SF_AI_MISSION_HEADER_DEFAULT_VARIANTS as n, SF_AI_MISSION_HEADER_VARIANTS as r, AiMissionHeader as t };
170
170
 
171
- //# sourceMappingURL=ai-mission-header-ByYkJ6YP.js.map
171
+ //# sourceMappingURL=ai-mission-header-08__gULL.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-mission-header-ByYkJ6YP.js","names":[],"sources":["../src/components/ai-mission-header/ai-mission-header.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n CheckCircleIcon,\n ClockIcon,\n CoinsIcon,\n CpuIcon,\n SpinnerGapIcon,\n StopCircleIcon,\n UsersIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps } from \"react\";\nimport { forwardRef, useEffect, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport type { AgentTaskList, AgentUsage } from \"../use-agent-harness\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_MISSION_HEADER_VARIANTS = {\n variant: {\n default: { classes: \"\", description: \"Full header with all stats\" },\n compact: { classes: \"\", description: \"Single-row compact header\" },\n },\n} as const;\n\nexport const SF_AI_MISSION_HEADER_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\nexport type SFAiMissionHeaderVariant =\n keyof typeof SF_AI_MISSION_HEADER_VARIANTS.variant;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiMissionHeaderProps = Omit<ComponentProps<\"div\">, \"title\"> & {\n /** The mission goal or title. */\n title?: string;\n /** Task list for progress calculation. */\n taskList?: AgentTaskList | null;\n /** Token/cost usage summary. */\n usage?: AgentUsage | null;\n /** Number of agents currently active. */\n activeAgentCount?: number;\n /** Total number of agents that have run or are running. */\n totalAgentCount?: number;\n /** Wall-clock ms when the mission started. */\n startedAt?: number;\n /** Wall-clock ms when the mission ended. Undefined while still running. */\n endedAt?: number;\n /** Whether the mission is currently running. */\n isRunning?: boolean;\n /** Called when the user clicks the abort/stop button. */\n onAbort?: () => void;\n /** Display variant. @default \"default\" */\n variant?: SFAiMissionHeaderVariant;\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction formatElapsed(ms: number): string {\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const rem = s % 60;\n return `${m}m ${rem.toString().padStart(2, \"0\")}s`;\n}\n\nfunction formatCost(cost: number): string {\n if (cost < 0.01) return `${(cost * 100).toFixed(2)}¢`;\n return `$${cost.toFixed(4)}`;\n}\n\nfunction formatTokens(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;\n return String(n);\n}\n\n/** Live elapsed time that ticks every ~500ms while running. */\nfunction useElapsed(startedAt?: number, endedAt?: number, isRunning = false) {\n const [elapsed, setElapsed] = useState<number>(\n startedAt ? (endedAt ?? Date.now()) - startedAt : 0\n );\n\n useEffect(() => {\n if (!startedAt || !isRunning) {\n if (startedAt) setElapsed((endedAt ?? Date.now()) - startedAt);\n return;\n }\n\n // Tick at 2Hz — efficient for a human-readable seconds display\n const id = setInterval(() => {\n setElapsed(Date.now() - startedAt);\n }, 500);\n return () => clearInterval(id);\n }, [startedAt, endedAt, isRunning]);\n\n return elapsed;\n}\n\n// ─── Progress bar ─────────────────────────────────────────────────────────────\n\nfunction MissionProgress({ taskList }: { taskList?: AgentTaskList | null }) {\n if (!taskList?.tasks.length) return null;\n\n const total = taskList.tasks.length;\n const done = taskList.tasks.filter(\n (t) => t.status === \"completed\" || t.status === \"cancelled\"\n ).length;\n const inProgress = taskList.tasks.filter(\n (t) => t.status === \"in_progress\"\n ).length;\n const pct = total > 0 ? Math.round((done / total) * 100) : 0;\n const activePct = total > 0 ? Math.round((inProgress / total) * 100) : 0;\n\n return (\n <div className=\"flex items-center gap-3\">\n {/* Bar */}\n <div className=\"relative h-1.5 flex-1 overflow-hidden rounded-full bg-sf-fill\">\n {/* Active (in-progress) ghost — slightly ahead of done */}\n {activePct > 0 && (\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-sf-brand/30 transition-[width] duration-500\"\n style={{ width: `${pct + activePct}%` }}\n />\n )}\n {/* Done */}\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-sf-brand transition-[width] duration-500\"\n style={{ width: `${pct}%` }}\n />\n </div>\n {/* Fraction */}\n <span className=\"shrink-0 tabular-nums text-[11px] text-sf-subtle\">\n {done}/{total}\n </span>\n </div>\n );\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * `AiMissionHeader` — top bar for the Commander dashboard.\n *\n * Shows: mission title, progress bar (from task list), agent count,\n * live elapsed time, token usage, and an abort button while running.\n *\n * @example\n * ```tsx\n * <AiMissionHeader\n * title=\"Refactor auth module\"\n * taskList={state.taskList}\n * usage={state.usage}\n * activeAgentCount={activeAgents.length}\n * totalAgentCount={allAgents.length}\n * startedAt={state.missionStartedAt}\n * isRunning={state.isRunning}\n * onAbort={abort}\n * />\n * ```\n */\nexport const AiMissionHeader = forwardRef<HTMLDivElement, AiMissionHeaderProps>(\n (\n {\n title,\n taskList,\n usage,\n activeAgentCount,\n totalAgentCount,\n startedAt,\n endedAt,\n isRunning = false,\n onAbort,\n variant = SF_AI_MISSION_HEADER_DEFAULT_VARIANTS.variant,\n className,\n ...props\n },\n ref\n ) => {\n const elapsed = useElapsed(startedAt, endedAt, isRunning);\n const isCompact = variant === \"compact\";\n\n const statusLabel = isRunning\n ? \"Running\"\n : startedAt && endedAt\n ? \"Completed\"\n : startedAt\n ? \"Paused\"\n : \"Idle\";\n\n return (\n <div\n ref={ref}\n className={cn(\n \"flex flex-col gap-2 border-b border-sf-line bg-sf-base px-4 py-3\",\n isCompact && \"flex-row items-center gap-4 py-2\",\n className\n )}\n {...props}\n >\n {/* Top row: title + status + abort */}\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n {/* Status icon */}\n {isRunning ? (\n <SpinnerGapIcon\n size={14}\n className=\"shrink-0 animate-spin text-sf-brand\"\n />\n ) : startedAt && endedAt ? (\n <CheckCircleIcon size={14} className=\"shrink-0 text-sf-success\" />\n ) : (\n <CpuIcon size={14} className=\"shrink-0 text-sf-subtle\" />\n )}\n\n {/* Title */}\n {title ? (\n <h2 className=\"truncate text-sm font-semibold text-sf-default\">\n {title}\n </h2>\n ) : (\n <span className=\"text-sm font-medium text-sf-subtle\">\n {statusLabel}\n </span>\n )}\n </div>\n\n {/* Right: stats + abort */}\n <div className=\"flex shrink-0 items-center gap-3\">\n {/* Agent count */}\n {(activeAgentCount !== undefined ||\n totalAgentCount !== undefined) && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <UsersIcon size={11} />\n <span className=\"tabular-nums\">\n {activeAgentCount !== undefined &&\n totalAgentCount !== undefined\n ? `${activeAgentCount}/${totalAgentCount}`\n : (activeAgentCount ?? totalAgentCount)}\n </span>\n </div>\n )}\n\n {/* Elapsed time */}\n {startedAt && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <ClockIcon size={11} />\n <span className=\"tabular-nums\">{formatElapsed(elapsed)}</span>\n </div>\n )}\n\n {/* Token cost */}\n {usage?.cost !== undefined && usage.cost > 0 && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <CoinsIcon size={11} />\n <span className=\"tabular-nums\">{formatCost(usage.cost)}</span>\n </div>\n )}\n\n {/* Tokens */}\n {usage?.totalTokens !== undefined && (\n <span className=\"tabular-nums text-[11px] text-sf-subtle\">\n {formatTokens(usage.totalTokens)} tok\n </span>\n )}\n\n {/* Abort */}\n {isRunning && onAbort && (\n <button\n type=\"button\"\n aria-label=\"Abort mission\"\n onClick={onAbort}\n className={cn(\n \"flex items-center gap-1 rounded-md px-2 py-1 text-[11px] font-medium\",\n \"text-sf-danger transition-colors hover:bg-sf-danger/10\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sf-ring\"\n )}\n >\n <StopCircleIcon size={11} />\n Stop\n </button>\n )}\n </div>\n </div>\n\n {/* Progress bar (only in default variant) */}\n {!isCompact && <MissionProgress taskList={taskList} />}\n </div>\n );\n }\n);\n\nAiMissionHeader.displayName = \"AiMissionHeader\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai-mission-header-08__gULL.js","names":[],"sources":["../src/components/ai-mission-header/ai-mission-header.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n CheckCircleIcon,\n ClockIcon,\n CoinsIcon,\n CpuIcon,\n SpinnerGapIcon,\n StopCircleIcon,\n UsersIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps } from \"react\";\nimport { forwardRef, useEffect, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport type { AgentTaskList, AgentUsage } from \"../use-agent-harness\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_MISSION_HEADER_VARIANTS = {\n variant: {\n default: { classes: \"\", description: \"Full header with all stats\" },\n compact: { classes: \"\", description: \"Single-row compact header\" },\n },\n} as const;\n\nexport const SF_AI_MISSION_HEADER_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\nexport type SFAiMissionHeaderVariant =\n keyof typeof SF_AI_MISSION_HEADER_VARIANTS.variant;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiMissionHeaderProps = Omit<ComponentProps<\"div\">, \"title\"> & {\n /** The mission goal or title. */\n title?: string;\n /** Task list for progress calculation. */\n taskList?: AgentTaskList | null;\n /** Token/cost usage summary. */\n usage?: AgentUsage | null;\n /** Number of agents currently active. */\n activeAgentCount?: number;\n /** Total number of agents that have run or are running. */\n totalAgentCount?: number;\n /** Wall-clock ms when the mission started. */\n startedAt?: number;\n /** Wall-clock ms when the mission ended. Undefined while still running. */\n endedAt?: number;\n /** Whether the mission is currently running. */\n isRunning?: boolean;\n /** Called when the user clicks the abort/stop button. */\n onAbort?: () => void;\n /** Display variant. @default \"default\" */\n variant?: SFAiMissionHeaderVariant;\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction formatElapsed(ms: number): string {\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const rem = s % 60;\n return `${m}m ${rem.toString().padStart(2, \"0\")}s`;\n}\n\nfunction formatCost(cost: number): string {\n if (cost < 0.01) return `${(cost * 100).toFixed(2)}¢`;\n return `$${cost.toFixed(4)}`;\n}\n\nfunction formatTokens(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;\n return String(n);\n}\n\n/** Live elapsed time that ticks every ~500ms while running. */\nfunction useElapsed(startedAt?: number, endedAt?: number, isRunning = false) {\n const [elapsed, setElapsed] = useState<number>(\n startedAt ? (endedAt ?? Date.now()) - startedAt : 0\n );\n\n useEffect(() => {\n if (!startedAt || !isRunning) {\n if (startedAt) setElapsed((endedAt ?? Date.now()) - startedAt);\n return;\n }\n\n // Tick at 2Hz — efficient for a human-readable seconds display\n const id = setInterval(() => {\n setElapsed(Date.now() - startedAt);\n }, 500);\n return () => clearInterval(id);\n }, [startedAt, endedAt, isRunning]);\n\n return elapsed;\n}\n\n// ─── Progress bar ─────────────────────────────────────────────────────────────\n\nfunction MissionProgress({ taskList }: { taskList?: AgentTaskList | null }) {\n if (!taskList?.tasks.length) return null;\n\n const total = taskList.tasks.length;\n const done = taskList.tasks.filter(\n (t) => t.status === \"completed\" || t.status === \"cancelled\"\n ).length;\n const inProgress = taskList.tasks.filter(\n (t) => t.status === \"in_progress\"\n ).length;\n const pct = total > 0 ? Math.round((done / total) * 100) : 0;\n const activePct = total > 0 ? Math.round((inProgress / total) * 100) : 0;\n\n return (\n <div className=\"flex items-center gap-3\">\n {/* Bar */}\n <div className=\"relative h-1.5 flex-1 overflow-hidden rounded-full bg-sf-fill\">\n {/* Active (in-progress) ghost — slightly ahead of done */}\n {activePct > 0 && (\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-sf-brand/30 transition-[width] duration-500\"\n style={{ width: `${pct + activePct}%` }}\n />\n )}\n {/* Done */}\n <div\n className=\"absolute inset-y-0 left-0 rounded-full bg-sf-brand transition-[width] duration-500\"\n style={{ width: `${pct}%` }}\n />\n </div>\n {/* Fraction */}\n <span className=\"shrink-0 tabular-nums text-[11px] text-sf-subtle\">\n {done}/{total}\n </span>\n </div>\n );\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * `AiMissionHeader` — top bar for the Commander dashboard.\n *\n * Shows: mission title, progress bar (from task list), agent count,\n * live elapsed time, token usage, and an abort button while running.\n *\n * @example\n * ```tsx\n * <AiMissionHeader\n * title=\"Refactor auth module\"\n * taskList={state.taskList}\n * usage={state.usage}\n * activeAgentCount={activeAgents.length}\n * totalAgentCount={allAgents.length}\n * startedAt={state.missionStartedAt}\n * isRunning={state.isRunning}\n * onAbort={abort}\n * />\n * ```\n */\nexport const AiMissionHeader = forwardRef<HTMLDivElement, AiMissionHeaderProps>(\n (\n {\n title,\n taskList,\n usage,\n activeAgentCount,\n totalAgentCount,\n startedAt,\n endedAt,\n isRunning = false,\n onAbort,\n variant = SF_AI_MISSION_HEADER_DEFAULT_VARIANTS.variant,\n className,\n ...props\n },\n ref\n ) => {\n const elapsed = useElapsed(startedAt, endedAt, isRunning);\n const isCompact = variant === \"compact\";\n\n const statusLabel = isRunning\n ? \"Running\"\n : startedAt && endedAt\n ? \"Completed\"\n : startedAt\n ? \"Paused\"\n : \"Idle\";\n\n return (\n <div\n ref={ref}\n className={cn(\n \"flex flex-col gap-2 border-b border-sf-line bg-sf-base px-4 py-3\",\n isCompact && \"flex-row items-center gap-4 py-2\",\n className\n )}\n {...props}\n >\n {/* Top row: title + status + abort */}\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n {/* Status icon */}\n {isRunning ? (\n <SpinnerGapIcon\n size={14}\n className=\"shrink-0 animate-spin text-sf-brand\"\n />\n ) : startedAt && endedAt ? (\n <CheckCircleIcon size={14} className=\"shrink-0 text-sf-success\" />\n ) : (\n <CpuIcon size={14} className=\"shrink-0 text-sf-subtle\" />\n )}\n\n {/* Title */}\n {title ? (\n <h2 className=\"truncate text-sm font-semibold text-sf-default\">\n {title}\n </h2>\n ) : (\n <span className=\"text-sm font-medium text-sf-subtle\">\n {statusLabel}\n </span>\n )}\n </div>\n\n {/* Right: stats + abort */}\n <div className=\"flex shrink-0 items-center gap-3\">\n {/* Agent count */}\n {(activeAgentCount !== undefined ||\n totalAgentCount !== undefined) && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <UsersIcon size={11} />\n <span className=\"tabular-nums\">\n {activeAgentCount !== undefined &&\n totalAgentCount !== undefined\n ? `${activeAgentCount}/${totalAgentCount}`\n : (activeAgentCount ?? totalAgentCount)}\n </span>\n </div>\n )}\n\n {/* Elapsed time */}\n {startedAt && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <ClockIcon size={11} />\n <span className=\"tabular-nums\">{formatElapsed(elapsed)}</span>\n </div>\n )}\n\n {/* Token cost */}\n {usage?.cost !== undefined && usage.cost > 0 && (\n <div className=\"flex items-center gap-1 text-[11px] text-sf-subtle\">\n <CoinsIcon size={11} />\n <span className=\"tabular-nums\">{formatCost(usage.cost)}</span>\n </div>\n )}\n\n {/* Tokens */}\n {usage?.totalTokens !== undefined && (\n <span className=\"tabular-nums text-[11px] text-sf-subtle\">\n {formatTokens(usage.totalTokens)} tok\n </span>\n )}\n\n {/* Abort */}\n {isRunning && onAbort && (\n <button\n type=\"button\"\n aria-label=\"Abort mission\"\n onClick={onAbort}\n className={cn(\n \"flex items-center gap-1 rounded-md px-2 py-1 text-[11px] font-medium\",\n \"text-sf-danger transition-colors hover:bg-sf-danger/10\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sf-ring\"\n )}\n >\n <StopCircleIcon size={11} />\n Stop\n </button>\n )}\n </div>\n </div>\n\n {/* Progress bar (only in default variant) */}\n {!isCompact && <MissionProgress taskList={taskList} />}\n </div>\n );\n }\n);\n\nAiMissionHeader.displayName = \"AiMissionHeader\";\n"],"mappings":"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"}