@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
@@ -40,4 +40,4 @@ AiShimmer.displayName = "AiShimmer";
40
40
  //#endregion
41
41
  export { SF_AI_SHIMMER_DEFAULT_VARIANTS as n, SF_AI_SHIMMER_VARIANTS as r, AiShimmer as t };
42
42
 
43
- //# sourceMappingURL=ai-shimmer-BpOmfonu.js.map
43
+ //# sourceMappingURL=ai-shimmer-By5_L05p.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-shimmer-BpOmfonu.js","names":[],"sources":["../src/components/ai-shimmer/ai-shimmer.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion } from \"motion/react\";\nimport {\n type CSSProperties,\n type ElementType,\n type JSX,\n memo,\n useMemo,\n} from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_SHIMMER_VARIANTS = {} as const;\nexport const SF_AI_SHIMMER_DEFAULT_VARIANTS = {} as const;\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport interface AiShimmerProps {\n /** Text content to animate. */\n children: string;\n /** HTML element or component to render as. @default \"p\" */\n as?: ElementType;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n /** Animation duration in seconds. @default 2 */\n duration?: number;\n /** Shimmer width multiplier relative to text length. @default 2 */\n spread?: number;\n}\n\n/**\n * Animated text shimmer effect for AI streaming states.\n * Renders a sweep of light across the text to indicate activity.\n *\n * @example\n * ```tsx\n * <AiShimmer>Thinking...</AiShimmer>\n * <AiShimmer as=\"span\" duration={1.5}>Generating response</AiShimmer>\n * ```\n */\nfunction AiShimmerBase({\n children,\n as: Component = \"p\",\n className,\n duration = 2,\n spread = 2,\n}: AiShimmerProps) {\n const MotionComponent = motion.create(\n Component as keyof JSX.IntrinsicElements\n );\n const dynamicSpread = useMemo(\n () => (children?.length ?? 0) * spread,\n [children, spread]\n );\n\n return (\n <MotionComponent\n animate={{ backgroundPosition: \"0% center\" }}\n className={cn(\n \"relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent\",\n \"[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-sf-base),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]\",\n className\n )}\n initial={{ backgroundPosition: \"100% center\" }}\n style={\n {\n \"--spread\": `${dynamicSpread}px`,\n backgroundImage:\n \"var(--bg), linear-gradient(var(--color-sf-subtle), var(--color-sf-subtle))\",\n } as CSSProperties\n }\n transition={{\n repeat: Number.POSITIVE_INFINITY,\n duration,\n ease: \"linear\",\n }}\n >\n {children}\n </MotionComponent>\n );\n}\n\nexport const AiShimmer = memo(AiShimmerBase);\nAiShimmer.displayName = \"AiShimmer\";\n"],"mappings":";;;;;;AAeA,IAAa,yBAAyB,EAAE;AACxC,IAAa,iCAAiC,EAAE;;;;;;;;;;;AA2BhD,SAAS,cAAc,EACrB,UACA,IAAI,YAAY,KAChB,WACA,WAAW,GACX,SAAS,KACQ;CACjB,MAAM,kBAAkB,OAAO,OAC7B,UACD;CACD,MAAM,gBAAgB,eACb,UAAU,UAAU,KAAK,QAChC,CAAC,UAAU,OAAO,CACnB;AAED,QACE,oBAAC,iBAAD;EACE,SAAS,EAAE,oBAAoB,aAAa;EAC5C,WAAW,GACT,kFACA,4JACA,UACD;EACD,SAAS,EAAE,oBAAoB,eAAe;EAC9C,OACE;GACE,YAAY,GAAG,cAAc;GAC7B,iBACE;GACH;EAEH,YAAY;GACV,QAAQ,OAAO;GACf;GACA,MAAM;GACP;EAEA;EACe,CAAA;;AAItB,IAAa,YAAY,KAAK,cAAc;AAC5C,UAAU,cAAc"}
1
+ {"version":3,"file":"ai-shimmer-By5_L05p.js","names":[],"sources":["../src/components/ai-shimmer/ai-shimmer.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion } from \"motion/react\";\nimport {\n type CSSProperties,\n type ElementType,\n type JSX,\n memo,\n useMemo,\n} from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_SHIMMER_VARIANTS = {} as const;\nexport const SF_AI_SHIMMER_DEFAULT_VARIANTS = {} as const;\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport interface AiShimmerProps {\n /** Text content to animate. */\n children: string;\n /** HTML element or component to render as. @default \"p\" */\n as?: ElementType;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n /** Animation duration in seconds. @default 2 */\n duration?: number;\n /** Shimmer width multiplier relative to text length. @default 2 */\n spread?: number;\n}\n\n/**\n * Animated text shimmer effect for AI streaming states.\n * Renders a sweep of light across the text to indicate activity.\n *\n * @example\n * ```tsx\n * <AiShimmer>Thinking...</AiShimmer>\n * <AiShimmer as=\"span\" duration={1.5}>Generating response</AiShimmer>\n * ```\n */\nfunction AiShimmerBase({\n children,\n as: Component = \"p\",\n className,\n duration = 2,\n spread = 2,\n}: AiShimmerProps) {\n const MotionComponent = motion.create(\n Component as keyof JSX.IntrinsicElements\n );\n const dynamicSpread = useMemo(\n () => (children?.length ?? 0) * spread,\n [children, spread]\n );\n\n return (\n <MotionComponent\n animate={{ backgroundPosition: \"0% center\" }}\n className={cn(\n \"relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent\",\n \"[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-sf-base),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]\",\n className\n )}\n initial={{ backgroundPosition: \"100% center\" }}\n style={\n {\n \"--spread\": `${dynamicSpread}px`,\n backgroundImage:\n \"var(--bg), linear-gradient(var(--color-sf-subtle), var(--color-sf-subtle))\",\n } as CSSProperties\n }\n transition={{\n repeat: Number.POSITIVE_INFINITY,\n duration,\n ease: \"linear\",\n }}\n >\n {children}\n </MotionComponent>\n );\n}\n\nexport const AiShimmer = memo(AiShimmerBase);\nAiShimmer.displayName = \"AiShimmer\";\n"],"mappings":";;;;;;AAeA,IAAa,yBAAyB,EAAE;AACxC,IAAa,iCAAiC,EAAE;;;;;;;;;;;AA2BhD,SAAS,cAAc,EACrB,UACA,IAAI,YAAY,KAChB,WACA,WAAW,GACX,SAAS,KACQ;CACjB,MAAM,kBAAkB,OAAO,OAC7B,UACD;CACD,MAAM,gBAAgB,eACb,UAAU,UAAU,KAAK,QAChC,CAAC,UAAU,OAAO,CACnB;AAED,QACE,oBAAC,iBAAD;EACE,SAAS,EAAE,oBAAoB,aAAa;EAC5C,WAAW,GACT,kFACA,4JACA,UACD;EACD,SAAS,EAAE,oBAAoB,eAAe;EAC9C,OACE;GACE,YAAY,GAAG,cAAc;GAC7B,iBACE;GACH;EAEH,YAAY;GACV,QAAQ,OAAO;GACf;GACA,MAAM;GACP;EAEA;EACe,CAAA;;AAItB,IAAa,YAAY,KAAK,cAAc;AAC5C,UAAU,cAAc"}
@@ -60,4 +60,4 @@ function AiStatusBadge({ icon: Icon, label, status = "idle", info, className, ..
60
60
  //#endregion
61
61
  export { SF_AI_STATUS_BADGE_DEFAULT_VARIANTS as n, SF_AI_STATUS_BADGE_VARIANTS as r, AiStatusBadge as t };
62
62
 
63
- //# sourceMappingURL=ai-status-badge-WhbKVeqn.js.map
63
+ //# sourceMappingURL=ai-status-badge-BGYGWYF6.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-status-badge-WhbKVeqn.js","names":[],"sources":["../src/components/ai-status-badge/ai-status-badge.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckIcon, SpinnerGapIcon, XIcon } from \"@phosphor-icons/react\";\nimport type { ComponentProps, ElementType } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_STATUS_BADGE_VARIANTS = {\n status: {\n idle: {\n classes: \"\",\n description: \"Default idle state — no status icon shown\",\n },\n running: {\n classes: \"\",\n description: \"Actively running — spinner icon shown\",\n },\n success: {\n classes: \"\",\n description: \"Completed successfully — check icon shown\",\n },\n error: {\n classes: \"\",\n description: \"Failed with an error — X icon shown\",\n },\n },\n} as const;\n\nexport const SF_AI_STATUS_BADGE_DEFAULT_VARIANTS = {\n status: \"idle\",\n} as const;\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type AiStatusBadgeStatus =\n keyof typeof SF_AI_STATUS_BADGE_VARIANTS.status;\n\nexport type AiStatusBadgeProps = ComponentProps<\"div\"> & {\n /** Icon component rendered on the left. */\n icon?: ElementType;\n /** Label text displayed in the badge. */\n label: string;\n /** Current status — determines the status icon on the right. @default \"idle\" */\n status?: AiStatusBadgeStatus;\n /** Additional info text (e.g. \"2/3\") shown after the label. */\n info?: string;\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction StatusIcon({ status }: { status: AiStatusBadgeStatus }) {\n switch (status) {\n case \"running\":\n return <SpinnerGapIcon className=\"size-3 animate-spin text-sf-brand\" />;\n case \"success\":\n return <CheckIcon className=\"size-3 text-sf-success\" />;\n case \"error\":\n return <XIcon className=\"size-3 text-sf-danger\" />;\n default:\n return null;\n }\n}\n\n// ─── AiStatusBadge ───────────────────────────────────────────────────────────\n\n/**\n * Compact pill-shaped badge showing an icon, label, and a status indicator.\n * Used by minimal variants of AiToolCall and AiReasoning.\n *\n * @example\n * ```tsx\n * <AiStatusBadge icon={MagnifyingGlassIcon} label=\"web_search\" status=\"running\" />\n * ```\n */\nexport function AiStatusBadge({\n icon: Icon,\n label,\n status = \"idle\",\n info,\n className,\n ...props\n}: AiStatusBadgeProps) {\n return (\n <div\n className={cn(\n \"mx-2 inline-flex cursor-default items-center gap-1.5 rounded-full bg-sf-tint/50 px-2 py-0.5 text-xs\",\n \"animate-in fade-in-0 slide-in-from-bottom-1 duration-200\",\n className\n )}\n {...props}\n >\n {Icon ? <Icon className=\"size-3 text-sf-subtle\" /> : null}\n <span className=\"text-sf-subtle\">{label}</span>\n {info ? <span className=\"text-sf-subtle/60\">{info}</span> : null}\n <StatusIcon status={status} />\n </div>\n );\n}\n"],"mappings":";;;;;AASA,IAAa,8BAA8B,EACzC,QAAQ;CACN,MAAM;EACJ,SAAS;EACT,aAAa;EACd;CACD,SAAS;EACP,SAAS;EACT,aAAa;EACd;CACD,SAAS;EACP,SAAS;EACT,aAAa;EACd;CACD,OAAO;EACL,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,sCAAsC,EACjD,QAAQ,QACT;AAoBD,SAAS,WAAW,EAAE,UAA2C;AAC/D,SAAQ,QAAR;EACE,KAAK,UACH,QAAO,oBAAC,gBAAD,EAAgB,WAAU,qCAAsC,CAAA;EACzE,KAAK,UACH,QAAO,oBAAC,WAAD,EAAW,WAAU,0BAA2B,CAAA;EACzD,KAAK,QACH,QAAO,oBAAC,OAAD,EAAO,WAAU,yBAA0B,CAAA;EACpD,QACE,QAAO;;;;;;;;;;;;AAeb,SAAgB,cAAc,EAC5B,MAAM,MACN,OACA,SAAS,QACT,MACA,WACA,GAAG,SACkB;AACrB,QACE,qBAAC,OAAD;EACE,WAAW,GACT,uGACA,4DACA,UACD;EACD,GAAI;YANN;GAQG,OAAO,oBAAC,MAAD,EAAM,WAAU,yBAA0B,CAAA,GAAG;GACrD,oBAAC,QAAD;IAAM,WAAU;cAAkB;IAAa,CAAA;GAC9C,OAAO,oBAAC,QAAD;IAAM,WAAU;cAAqB;IAAY,CAAA,GAAG;GAC5D,oBAAC,YAAD,EAAoB,QAAU,CAAA;GAC1B"}
1
+ {"version":3,"file":"ai-status-badge-BGYGWYF6.js","names":[],"sources":["../src/components/ai-status-badge/ai-status-badge.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckIcon, SpinnerGapIcon, XIcon } from \"@phosphor-icons/react\";\nimport type { ComponentProps, ElementType } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_STATUS_BADGE_VARIANTS = {\n status: {\n idle: {\n classes: \"\",\n description: \"Default idle state — no status icon shown\",\n },\n running: {\n classes: \"\",\n description: \"Actively running — spinner icon shown\",\n },\n success: {\n classes: \"\",\n description: \"Completed successfully — check icon shown\",\n },\n error: {\n classes: \"\",\n description: \"Failed with an error — X icon shown\",\n },\n },\n} as const;\n\nexport const SF_AI_STATUS_BADGE_DEFAULT_VARIANTS = {\n status: \"idle\",\n} as const;\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type AiStatusBadgeStatus =\n keyof typeof SF_AI_STATUS_BADGE_VARIANTS.status;\n\nexport type AiStatusBadgeProps = ComponentProps<\"div\"> & {\n /** Icon component rendered on the left. */\n icon?: ElementType;\n /** Label text displayed in the badge. */\n label: string;\n /** Current status — determines the status icon on the right. @default \"idle\" */\n status?: AiStatusBadgeStatus;\n /** Additional info text (e.g. \"2/3\") shown after the label. */\n info?: string;\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction StatusIcon({ status }: { status: AiStatusBadgeStatus }) {\n switch (status) {\n case \"running\":\n return <SpinnerGapIcon className=\"size-3 animate-spin text-sf-brand\" />;\n case \"success\":\n return <CheckIcon className=\"size-3 text-sf-success\" />;\n case \"error\":\n return <XIcon className=\"size-3 text-sf-danger\" />;\n default:\n return null;\n }\n}\n\n// ─── AiStatusBadge ───────────────────────────────────────────────────────────\n\n/**\n * Compact pill-shaped badge showing an icon, label, and a status indicator.\n * Used by minimal variants of AiToolCall and AiReasoning.\n *\n * @example\n * ```tsx\n * <AiStatusBadge icon={MagnifyingGlassIcon} label=\"web_search\" status=\"running\" />\n * ```\n */\nexport function AiStatusBadge({\n icon: Icon,\n label,\n status = \"idle\",\n info,\n className,\n ...props\n}: AiStatusBadgeProps) {\n return (\n <div\n className={cn(\n \"mx-2 inline-flex cursor-default items-center gap-1.5 rounded-full bg-sf-tint/50 px-2 py-0.5 text-xs\",\n \"animate-in fade-in-0 slide-in-from-bottom-1 duration-200\",\n className\n )}\n {...props}\n >\n {Icon ? <Icon className=\"size-3 text-sf-subtle\" /> : null}\n <span className=\"text-sf-subtle\">{label}</span>\n {info ? <span className=\"text-sf-subtle/60\">{info}</span> : null}\n <StatusIcon status={status} />\n </div>\n );\n}\n"],"mappings":";;;;;AASA,IAAa,8BAA8B,EACzC,QAAQ;CACN,MAAM;EACJ,SAAS;EACT,aAAa;EACd;CACD,SAAS;EACP,SAAS;EACT,aAAa;EACd;CACD,SAAS;EACP,SAAS;EACT,aAAa;EACd;CACD,OAAO;EACL,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,sCAAsC,EACjD,QAAQ,QACT;AAoBD,SAAS,WAAW,EAAE,UAA2C;AAC/D,SAAQ,QAAR;EACE,KAAK,UACH,QAAO,oBAAC,gBAAD,EAAgB,WAAU,qCAAsC,CAAA;EACzE,KAAK,UACH,QAAO,oBAAC,WAAD,EAAW,WAAU,0BAA2B,CAAA;EACzD,KAAK,QACH,QAAO,oBAAC,OAAD,EAAO,WAAU,yBAA0B,CAAA;EACpD,QACE,QAAO;;;;;;;;;;;;AAeb,SAAgB,cAAc,EAC5B,MAAM,MACN,OACA,SAAS,QACT,MACA,WACA,GAAG,SACkB;AACrB,QACE,qBAAC,OAAD;EACE,WAAW,GACT,uGACA,4DACA,UACD;EACD,GAAI;YANN;GAQG,OAAO,oBAAC,MAAD,EAAM,WAAU,yBAA0B,CAAA,GAAG;GACrD,oBAAC,QAAD;IAAM,WAAU;cAAkB;IAAa,CAAA;GAC9C,OAAO,oBAAC,QAAD;IAAM,WAAU;cAAqB;IAAY,CAAA,GAAG;GAC5D,oBAAC,YAAD,EAAoB,QAAU,CAAA;GAC1B"}
@@ -78,4 +78,4 @@ function useAiStreamingText(text, options = {}) {
78
78
  //#endregion
79
79
  export { SF_AI_STREAMING_TEXT_VARIANTS as n, useAiStreamingText as r, SF_AI_STREAMING_TEXT_DEFAULT_VARIANTS as t };
80
80
 
81
- //# sourceMappingURL=ai-streaming-text-ClL7FwvD.js.map
81
+ //# sourceMappingURL=ai-streaming-text-CMfoThV0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-streaming-text-ClL7FwvD.js","names":[],"sources":["../src/components/ai-streaming-text/ai-streaming-text.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_STREAMING_TEXT_VARIANTS = {} as const;\nexport const SF_AI_STREAMING_TEXT_DEFAULT_VARIANTS = {} as const;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface UseAiStreamingTextOptions {\n /** Whether content is actively streaming. @default false */\n isStreaming?: boolean;\n /** Characters to release per animation frame tick. @default 10 */\n charsPerTick?: number;\n /** Milliseconds between releases (~16ms = 60fps). @default 16 */\n tickInterval?: number;\n /** Immediately flush all text when streaming stops. @default true */\n flushOnStop?: boolean;\n}\n\n// ─── Hook ────────────────────────────────────────────────────────────────────\n\n/**\n * Buffers incoming text and releases it character-by-character for a smooth\n * typing effect. Pass the full (possibly incomplete) text and whether streaming\n * is active — get back the display-ready substring.\n *\n * @example\n * ```tsx\n * function StreamingMessage({ text, isStreaming }) {\n * const displayText = useAiStreamingText(text, { isStreaming });\n * return <AiResponse>{displayText}</AiResponse>;\n * }\n * ```\n */\nexport function useAiStreamingText(\n text: string,\n options: UseAiStreamingTextOptions = {}\n): string {\n const {\n isStreaming = false,\n charsPerTick = 10,\n tickInterval = 16,\n flushOnStop = true,\n } = options;\n\n const [displayText, setDisplayText] = useState(text);\n const targetRef = useRef(text);\n const posRef = useRef(text.length);\n const rafRef = useRef<number | null>(null);\n const lastTickRef = useRef(0);\n\n useEffect(() => {\n targetRef.current = text;\n }, [text]);\n\n useEffect(() => {\n if (!isStreaming) {\n if (flushOnStop) {\n setDisplayText(text);\n posRef.current = text.length;\n }\n return;\n }\n\n const animate = (timestamp: number) => {\n const target = targetRef.current;\n const pos = posRef.current;\n\n if (pos >= target.length) {\n rafRef.current = requestAnimationFrame(animate);\n return;\n }\n\n if (timestamp - lastTickRef.current < tickInterval) {\n rafRef.current = requestAnimationFrame(animate);\n return;\n }\n lastTickRef.current = timestamp;\n\n const newPos = Math.min(pos + charsPerTick, target.length);\n posRef.current = newPos;\n setDisplayText(target.slice(0, newPos));\n rafRef.current = requestAnimationFrame(animate);\n };\n\n rafRef.current = requestAnimationFrame(animate);\n return () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [isStreaming, charsPerTick, tickInterval, flushOnStop, text]);\n\n useEffect(() => {\n if (!isStreaming && flushOnStop && displayText !== text) {\n setDisplayText(text);\n posRef.current = text.length;\n }\n }, [isStreaming, flushOnStop, text, displayText]);\n\n return displayText;\n}\n"],"mappings":";;;AAMA,IAAa,gCAAgC,EAAE;AAC/C,IAAa,wCAAwC,EAAE;;;;;;;;;;;;;;AA8BvD,SAAgB,mBACd,MACA,UAAqC,EAAE,EAC/B;CACR,MAAM,EACJ,cAAc,OACd,eAAe,IACf,eAAe,IACf,cAAc,SACZ;CAEJ,MAAM,CAAC,aAAa,kBAAkB,SAAS,KAAK;CACpD,MAAM,YAAY,OAAO,KAAK;CAC9B,MAAM,SAAS,OAAO,KAAK,OAAO;CAClC,MAAM,SAAS,OAAsB,KAAK;CAC1C,MAAM,cAAc,OAAO,EAAE;AAE7B,iBAAgB;AACd,YAAU,UAAU;IACnB,CAAC,KAAK,CAAC;AAEV,iBAAgB;AACd,MAAI,CAAC,aAAa;AAChB,OAAI,aAAa;AACf,mBAAe,KAAK;AACpB,WAAO,UAAU,KAAK;;AAExB;;EAGF,MAAM,WAAW,cAAsB;GACrC,MAAM,SAAS,UAAU;GACzB,MAAM,MAAM,OAAO;AAEnB,OAAI,OAAO,OAAO,QAAQ;AACxB,WAAO,UAAU,sBAAsB,QAAQ;AAC/C;;AAGF,OAAI,YAAY,YAAY,UAAU,cAAc;AAClD,WAAO,UAAU,sBAAsB,QAAQ;AAC/C;;AAEF,eAAY,UAAU;GAEtB,MAAM,SAAS,KAAK,IAAI,MAAM,cAAc,OAAO,OAAO;AAC1D,UAAO,UAAU;AACjB,kBAAe,OAAO,MAAM,GAAG,OAAO,CAAC;AACvC,UAAO,UAAU,sBAAsB,QAAQ;;AAGjD,SAAO,UAAU,sBAAsB,QAAQ;AAC/C,eAAa;AACX,OAAI,OAAO,QAAS,sBAAqB,OAAO,QAAQ;;IAEzD;EAAC;EAAa;EAAc;EAAc;EAAa;EAAK,CAAC;AAEhE,iBAAgB;AACd,MAAI,CAAC,eAAe,eAAe,gBAAgB,MAAM;AACvD,kBAAe,KAAK;AACpB,UAAO,UAAU,KAAK;;IAEvB;EAAC;EAAa;EAAa;EAAM;EAAY,CAAC;AAEjD,QAAO"}
1
+ {"version":3,"file":"ai-streaming-text-CMfoThV0.js","names":[],"sources":["../src/components/ai-streaming-text/ai-streaming-text.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_STREAMING_TEXT_VARIANTS = {} as const;\nexport const SF_AI_STREAMING_TEXT_DEFAULT_VARIANTS = {} as const;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface UseAiStreamingTextOptions {\n /** Whether content is actively streaming. @default false */\n isStreaming?: boolean;\n /** Characters to release per animation frame tick. @default 10 */\n charsPerTick?: number;\n /** Milliseconds between releases (~16ms = 60fps). @default 16 */\n tickInterval?: number;\n /** Immediately flush all text when streaming stops. @default true */\n flushOnStop?: boolean;\n}\n\n// ─── Hook ────────────────────────────────────────────────────────────────────\n\n/**\n * Buffers incoming text and releases it character-by-character for a smooth\n * typing effect. Pass the full (possibly incomplete) text and whether streaming\n * is active — get back the display-ready substring.\n *\n * @example\n * ```tsx\n * function StreamingMessage({ text, isStreaming }) {\n * const displayText = useAiStreamingText(text, { isStreaming });\n * return <AiResponse>{displayText}</AiResponse>;\n * }\n * ```\n */\nexport function useAiStreamingText(\n text: string,\n options: UseAiStreamingTextOptions = {}\n): string {\n const {\n isStreaming = false,\n charsPerTick = 10,\n tickInterval = 16,\n flushOnStop = true,\n } = options;\n\n const [displayText, setDisplayText] = useState(text);\n const targetRef = useRef(text);\n const posRef = useRef(text.length);\n const rafRef = useRef<number | null>(null);\n const lastTickRef = useRef(0);\n\n useEffect(() => {\n targetRef.current = text;\n }, [text]);\n\n useEffect(() => {\n if (!isStreaming) {\n if (flushOnStop) {\n setDisplayText(text);\n posRef.current = text.length;\n }\n return;\n }\n\n const animate = (timestamp: number) => {\n const target = targetRef.current;\n const pos = posRef.current;\n\n if (pos >= target.length) {\n rafRef.current = requestAnimationFrame(animate);\n return;\n }\n\n if (timestamp - lastTickRef.current < tickInterval) {\n rafRef.current = requestAnimationFrame(animate);\n return;\n }\n lastTickRef.current = timestamp;\n\n const newPos = Math.min(pos + charsPerTick, target.length);\n posRef.current = newPos;\n setDisplayText(target.slice(0, newPos));\n rafRef.current = requestAnimationFrame(animate);\n };\n\n rafRef.current = requestAnimationFrame(animate);\n return () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [isStreaming, charsPerTick, tickInterval, flushOnStop, text]);\n\n useEffect(() => {\n if (!isStreaming && flushOnStop && displayText !== text) {\n setDisplayText(text);\n posRef.current = text.length;\n }\n }, [isStreaming, flushOnStop, text, displayText]);\n\n return displayText;\n}\n"],"mappings":";;;AAMA,IAAa,gCAAgC,EAAE;AAC/C,IAAa,wCAAwC,EAAE;;;;;;;;;;;;;;AA8BvD,SAAgB,mBACd,MACA,UAAqC,EAAE,EAC/B;CACR,MAAM,EACJ,cAAc,OACd,eAAe,IACf,eAAe,IACf,cAAc,SACZ;CAEJ,MAAM,CAAC,aAAa,kBAAkB,SAAS,KAAK;CACpD,MAAM,YAAY,OAAO,KAAK;CAC9B,MAAM,SAAS,OAAO,KAAK,OAAO;CAClC,MAAM,SAAS,OAAsB,KAAK;CAC1C,MAAM,cAAc,OAAO,EAAE;AAE7B,iBAAgB;AACd,YAAU,UAAU;IACnB,CAAC,KAAK,CAAC;AAEV,iBAAgB;AACd,MAAI,CAAC,aAAa;AAChB,OAAI,aAAa;AACf,mBAAe,KAAK;AACpB,WAAO,UAAU,KAAK;;AAExB;;EAGF,MAAM,WAAW,cAAsB;GACrC,MAAM,SAAS,UAAU;GACzB,MAAM,MAAM,OAAO;AAEnB,OAAI,OAAO,OAAO,QAAQ;AACxB,WAAO,UAAU,sBAAsB,QAAQ;AAC/C;;AAGF,OAAI,YAAY,YAAY,UAAU,cAAc;AAClD,WAAO,UAAU,sBAAsB,QAAQ;AAC/C;;AAEF,eAAY,UAAU;GAEtB,MAAM,SAAS,KAAK,IAAI,MAAM,cAAc,OAAO,OAAO;AAC1D,UAAO,UAAU;AACjB,kBAAe,OAAO,MAAM,GAAG,OAAO,CAAC;AACvC,UAAO,UAAU,sBAAsB,QAAQ;;AAGjD,SAAO,UAAU,sBAAsB,QAAQ;AAC/C,eAAa;AACX,OAAI,OAAO,QAAS,sBAAqB,OAAO,QAAQ;;IAEzD;EAAC;EAAa;EAAc;EAAc;EAAa;EAAK,CAAC;AAEhE,iBAAgB;AACd,MAAI,CAAC,eAAe,eAAe,gBAAgB,MAAM;AACvD,kBAAe,KAAK;AACpB,UAAO,UAAU,KAAK;;IAEvB;EAAC;EAAa;EAAa;EAAM;EAAY,CAAC;AAEjD,QAAO"}
@@ -130,4 +130,4 @@ AiSubagent.displayName = "AiSubagent";
130
130
  //#endregion
131
131
  export { SF_AI_SUBAGENT_DEFAULT_VARIANTS as n, SF_AI_SUBAGENT_VARIANTS as r, AiSubagent as t };
132
132
 
133
- //# sourceMappingURL=ai-subagent-BruGN1UE.js.map
133
+ //# sourceMappingURL=ai-subagent-DcPRqkAA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-subagent-BruGN1UE.js","names":[],"sources":["../src/components/ai-subagent/ai-subagent.tsx"],"sourcesContent":["\"use client\";\n\nimport { Collapsible as BaseCollapsible } from \"@base-ui/react/collapsible\";\nimport {\n RobotIcon,\n CaretDownIcon,\n SpinnerGapIcon,\n CheckCircleIcon,\n XCircleIcon,\n} from \"@phosphor-icons/react\";\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\nimport { useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_SUBAGENT_VARIANTS = {\n status: {\n running: { classes: \"\", description: \"Subagent is actively processing\" },\n completed: { classes: \"\", description: \"Subagent finished successfully\" },\n error: { classes: \"\", description: \"Subagent encountered an error\" },\n },\n} as const;\n\nexport const SF_AI_SUBAGENT_DEFAULT_VARIANTS = {\n status: \"running\",\n} as const;\n\nexport type SFAiSubagentStatus = keyof typeof SF_AI_SUBAGENT_VARIANTS.status;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiSubagentProps = HTMLAttributes<HTMLDivElement> & {\n /** Subagent type ID (e.g. `\"explore\"`, `\"execute\"`). */\n agentType?: string;\n /** Human-readable name for this subagent. */\n name?: string;\n /** Current status. */\n status?: SFAiSubagentStatus;\n /** Current model ID being used (e.g. `\"claude-haiku-3.5\"`). */\n modelId?: string;\n /** Duration in milliseconds. */\n duration?: number;\n /** Custom icon. Defaults to `RobotIcon`. */\n icon?: ElementType;\n /** Whether the subagent content is expanded by default. */\n defaultExpanded?: boolean;\n /** Controlled open state. */\n open?: boolean;\n /** Called when open state changes. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Content rendered inside the collapsible body — typically streaming text,\n * tool calls, and other nested agent output.\n */\n children?: ReactNode;\n};\n\n// ─── Status config ────────────────────────────────────────────────────────────\n\nconst STATUS_CONFIG: Record<\n SFAiSubagentStatus,\n {\n icon: ElementType;\n className: string;\n label: string;\n }\n> = {\n running: {\n icon: SpinnerGapIcon,\n className: \"text-sf-brand animate-spin\",\n label: \"Running\",\n },\n completed: {\n icon: CheckCircleIcon,\n className: \"text-sf-success\",\n label: \"Completed\",\n },\n error: {\n icon: XCircleIcon,\n className: \"text-sf-danger\",\n label: \"Error\",\n },\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const remaining = s % 60;\n return remaining > 0 ? `${m}m ${remaining}s` : `${m}m`;\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Collapsible wrapper for nested subagent activity. Shows the subagent name,\n * status, model, and duration in a header, with all subagent output (text,\n * tool calls, etc.) in a collapsible body.\n *\n * Maps to harness events: `subagent_start`, `subagent_text_delta`,\n * `subagent_tool_start`, `subagent_tool_end`, `subagent_end`,\n * `subagent_model_changed`.\n *\n * @example\n * ```tsx\n * <AiSubagent\n * name=\"Explore\"\n * agentType=\"explore\"\n * status=\"running\"\n * modelId=\"claude-haiku-3.5\"\n * defaultExpanded\n * >\n * <AiResponse isAnimating>{streamingText}</AiResponse>\n * <AiToolCallGroup tools={toolCalls} />\n * </AiSubagent>\n * ```\n */\nexport function AiSubagent({\n agentType,\n name,\n status = \"running\",\n modelId,\n duration,\n icon,\n defaultExpanded = true,\n open: controlledOpen,\n onOpenChange,\n children,\n className,\n ...props\n}: AiSubagentProps) {\n const [internalOpen, setInternalOpen] = useState(defaultExpanded);\n const isControlled = controlledOpen !== undefined;\n const isOpen = isControlled ? controlledOpen : internalOpen;\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (!isControlled) setInternalOpen(nextOpen);\n onOpenChange?.(nextOpen);\n };\n\n const IconComponent = icon ?? RobotIcon;\n const statusConfig = STATUS_CONFIG[status];\n const StatusIcon = statusConfig.icon;\n const displayName = name ?? agentType ?? \"Subagent\";\n\n return (\n <BaseCollapsible.Root\n defaultOpen={defaultExpanded}\n onOpenChange={handleOpenChange}\n open={isOpen}\n >\n <div\n className={cn(\"flex flex-col overflow-hidden rounded-lg\", className)}\n {...props}\n >\n {/* Header / trigger */}\n <BaseCollapsible.Trigger className=\"flex w-fit items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors hover:bg-sf-tint\">\n {/* Agent icon */}\n <div className=\"flex size-5 shrink-0 items-center justify-center rounded bg-sf-tint text-sf-subtle\">\n <IconComponent className=\"size-3.5\" />\n </div>\n\n {/* Name + model */}\n <div className=\"flex min-w-0 grow items-center gap-2\">\n <span className=\"text-sm font-medium text-sf-default\">\n {displayName}\n </span>\n {modelId && (\n <span className=\"truncate rounded bg-sf-tint px-1.5 py-0.5 font-mono text-[10px] text-sf-subtle\">\n {modelId}\n </span>\n )}\n </div>\n\n {/* Status + duration + caret */}\n <div className=\"flex shrink-0 items-center gap-2\">\n {duration !== undefined && (\n <span className=\"text-xs tabular-nums text-sf-subtle\">\n {formatDuration(duration)}\n </span>\n )}\n <StatusIcon className={cn(\"size-3.5\", statusConfig.className)} />\n <CaretDownIcon\n className={cn(\n \"size-3 text-sf-subtle transition-transform duration-200\",\n isOpen && \"rotate-180\"\n )}\n />\n </div>\n </BaseCollapsible.Trigger>\n\n {/* Collapsible content */}\n <BaseCollapsible.Panel className=\"overflow-hidden [&[data-ending-style]]:h-0 [&[data-starting-style]]:h-0\">\n <div className=\"flex flex-col gap-2 bg-sf-tint/50 px-3 py-2.5\">\n {children}\n </div>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\nAiSubagent.displayName = \"AiSubagent\";\n"],"mappings":";;;;;;;AAiBA,IAAa,0BAA0B,EACrC,QAAQ;CACN,SAAS;EAAE,SAAS;EAAI,aAAa;EAAmC;CACxE,WAAW;EAAE,SAAS;EAAI,aAAa;EAAkC;CACzE,OAAO;EAAE,SAAS;EAAI,aAAa;EAAiC;CACrE,EACF;AAED,IAAa,kCAAkC,EAC7C,QAAQ,WACT;AAkCD,IAAM,gBAOF;CACF,SAAS;EACP,MAAM;EACN,WAAW;EACX,OAAO;EACR;CACD,WAAW;EACT,MAAM;EACN,WAAW;EACX,OAAO;EACR;CACD,OAAO;EACL,MAAM;EACN,WAAW;EACX,OAAO;EACR;CACF;AAID,SAAS,eAAe,IAAoB;AAC1C,KAAI,KAAK,IAAM,QAAO,GAAG,GAAG;CAC5B,MAAM,IAAI,KAAK,MAAM,KAAK,IAAK;AAC/B,KAAI,IAAI,GAAI,QAAO,GAAG,EAAE;CACxB,MAAM,IAAI,KAAK,MAAM,IAAI,GAAG;CAC5B,MAAM,YAAY,IAAI;AACtB,QAAO,YAAY,IAAI,GAAG,EAAE,IAAI,UAAU,KAAK,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;AA4BtD,SAAgB,WAAW,EACzB,WACA,MACA,SAAS,WACT,SACA,UACA,MACA,kBAAkB,MAClB,MAAM,gBACN,cACA,UACA,WACA,GAAG,SACe;CAClB,MAAM,CAAC,cAAc,mBAAmB,SAAS,gBAAgB;CACjE,MAAM,eAAe,mBAAmB,KAAA;CACxC,MAAM,SAAS,eAAe,iBAAiB;CAE/C,MAAM,oBAAoB,aAAsB;AAC9C,MAAI,CAAC,aAAc,iBAAgB,SAAS;AAC5C,iBAAe,SAAS;;CAG1B,MAAM,gBAAgB,QAAQ;CAC9B,MAAM,eAAe,cAAc;CACnC,MAAM,aAAa,aAAa;CAChC,MAAM,cAAc,QAAQ,aAAa;AAEzC,QACE,oBAAC,YAAgB,MAAjB;EACE,aAAa;EACb,cAAc;EACd,MAAM;YAEN,qBAAC,OAAD;GACE,WAAW,GAAG,4CAA4C,UAAU;GACpE,GAAI;aAFN,CAKE,qBAAC,YAAgB,SAAjB;IAAyB,WAAU;cAAnC;KAEE,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,eAAD,EAAe,WAAU,YAAa,CAAA;MAClC,CAAA;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA,EACN,WACC,oBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA,CAEL;;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACG,aAAa,KAAA,KACZ,oBAAC,QAAD;QAAM,WAAU;kBACb,eAAe,SAAS;QACpB,CAAA;OAET,oBAAC,YAAD,EAAY,WAAW,GAAG,YAAY,aAAa,UAAU,EAAI,CAAA;OACjE,oBAAC,eAAD,EACE,WAAW,GACT,2DACA,UAAU,aACX,EACD,CAAA;OACE;;KACkB;OAG1B,oBAAC,YAAgB,OAAjB;IAAuB,WAAU;cAC/B,oBAAC,OAAD;KAAK,WAAU;KACZ;KACG,CAAA;IACgB,CAAA,CACpB;;EACe,CAAA;;AAI3B,WAAW,cAAc"}
1
+ {"version":3,"file":"ai-subagent-DcPRqkAA.js","names":[],"sources":["../src/components/ai-subagent/ai-subagent.tsx"],"sourcesContent":["\"use client\";\n\nimport { Collapsible as BaseCollapsible } from \"@base-ui/react/collapsible\";\nimport {\n RobotIcon,\n CaretDownIcon,\n SpinnerGapIcon,\n CheckCircleIcon,\n XCircleIcon,\n} from \"@phosphor-icons/react\";\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\nimport { useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_SUBAGENT_VARIANTS = {\n status: {\n running: { classes: \"\", description: \"Subagent is actively processing\" },\n completed: { classes: \"\", description: \"Subagent finished successfully\" },\n error: { classes: \"\", description: \"Subagent encountered an error\" },\n },\n} as const;\n\nexport const SF_AI_SUBAGENT_DEFAULT_VARIANTS = {\n status: \"running\",\n} as const;\n\nexport type SFAiSubagentStatus = keyof typeof SF_AI_SUBAGENT_VARIANTS.status;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiSubagentProps = HTMLAttributes<HTMLDivElement> & {\n /** Subagent type ID (e.g. `\"explore\"`, `\"execute\"`). */\n agentType?: string;\n /** Human-readable name for this subagent. */\n name?: string;\n /** Current status. */\n status?: SFAiSubagentStatus;\n /** Current model ID being used (e.g. `\"claude-haiku-3.5\"`). */\n modelId?: string;\n /** Duration in milliseconds. */\n duration?: number;\n /** Custom icon. Defaults to `RobotIcon`. */\n icon?: ElementType;\n /** Whether the subagent content is expanded by default. */\n defaultExpanded?: boolean;\n /** Controlled open state. */\n open?: boolean;\n /** Called when open state changes. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Content rendered inside the collapsible body — typically streaming text,\n * tool calls, and other nested agent output.\n */\n children?: ReactNode;\n};\n\n// ─── Status config ────────────────────────────────────────────────────────────\n\nconst STATUS_CONFIG: Record<\n SFAiSubagentStatus,\n {\n icon: ElementType;\n className: string;\n label: string;\n }\n> = {\n running: {\n icon: SpinnerGapIcon,\n className: \"text-sf-brand animate-spin\",\n label: \"Running\",\n },\n completed: {\n icon: CheckCircleIcon,\n className: \"text-sf-success\",\n label: \"Completed\",\n },\n error: {\n icon: XCircleIcon,\n className: \"text-sf-danger\",\n label: \"Error\",\n },\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const remaining = s % 60;\n return remaining > 0 ? `${m}m ${remaining}s` : `${m}m`;\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Collapsible wrapper for nested subagent activity. Shows the subagent name,\n * status, model, and duration in a header, with all subagent output (text,\n * tool calls, etc.) in a collapsible body.\n *\n * Maps to harness events: `subagent_start`, `subagent_text_delta`,\n * `subagent_tool_start`, `subagent_tool_end`, `subagent_end`,\n * `subagent_model_changed`.\n *\n * @example\n * ```tsx\n * <AiSubagent\n * name=\"Explore\"\n * agentType=\"explore\"\n * status=\"running\"\n * modelId=\"claude-haiku-3.5\"\n * defaultExpanded\n * >\n * <AiResponse isAnimating>{streamingText}</AiResponse>\n * <AiToolCallGroup tools={toolCalls} />\n * </AiSubagent>\n * ```\n */\nexport function AiSubagent({\n agentType,\n name,\n status = \"running\",\n modelId,\n duration,\n icon,\n defaultExpanded = true,\n open: controlledOpen,\n onOpenChange,\n children,\n className,\n ...props\n}: AiSubagentProps) {\n const [internalOpen, setInternalOpen] = useState(defaultExpanded);\n const isControlled = controlledOpen !== undefined;\n const isOpen = isControlled ? controlledOpen : internalOpen;\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (!isControlled) setInternalOpen(nextOpen);\n onOpenChange?.(nextOpen);\n };\n\n const IconComponent = icon ?? RobotIcon;\n const statusConfig = STATUS_CONFIG[status];\n const StatusIcon = statusConfig.icon;\n const displayName = name ?? agentType ?? \"Subagent\";\n\n return (\n <BaseCollapsible.Root\n defaultOpen={defaultExpanded}\n onOpenChange={handleOpenChange}\n open={isOpen}\n >\n <div\n className={cn(\"flex flex-col overflow-hidden rounded-lg\", className)}\n {...props}\n >\n {/* Header / trigger */}\n <BaseCollapsible.Trigger className=\"flex w-fit items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors hover:bg-sf-tint\">\n {/* Agent icon */}\n <div className=\"flex size-5 shrink-0 items-center justify-center rounded bg-sf-tint text-sf-subtle\">\n <IconComponent className=\"size-3.5\" />\n </div>\n\n {/* Name + model */}\n <div className=\"flex min-w-0 grow items-center gap-2\">\n <span className=\"text-sm font-medium text-sf-default\">\n {displayName}\n </span>\n {modelId && (\n <span className=\"truncate rounded bg-sf-tint px-1.5 py-0.5 font-mono text-[10px] text-sf-subtle\">\n {modelId}\n </span>\n )}\n </div>\n\n {/* Status + duration + caret */}\n <div className=\"flex shrink-0 items-center gap-2\">\n {duration !== undefined && (\n <span className=\"text-xs tabular-nums text-sf-subtle\">\n {formatDuration(duration)}\n </span>\n )}\n <StatusIcon className={cn(\"size-3.5\", statusConfig.className)} />\n <CaretDownIcon\n className={cn(\n \"size-3 text-sf-subtle transition-transform duration-200\",\n isOpen && \"rotate-180\"\n )}\n />\n </div>\n </BaseCollapsible.Trigger>\n\n {/* Collapsible content */}\n <BaseCollapsible.Panel className=\"overflow-hidden [&[data-ending-style]]:h-0 [&[data-starting-style]]:h-0\">\n <div className=\"flex flex-col gap-2 bg-sf-tint/50 px-3 py-2.5\">\n {children}\n </div>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\nAiSubagent.displayName = \"AiSubagent\";\n"],"mappings":";;;;;;;AAiBA,IAAa,0BAA0B,EACrC,QAAQ;CACN,SAAS;EAAE,SAAS;EAAI,aAAa;EAAmC;CACxE,WAAW;EAAE,SAAS;EAAI,aAAa;EAAkC;CACzE,OAAO;EAAE,SAAS;EAAI,aAAa;EAAiC;CACrE,EACF;AAED,IAAa,kCAAkC,EAC7C,QAAQ,WACT;AAkCD,IAAM,gBAOF;CACF,SAAS;EACP,MAAM;EACN,WAAW;EACX,OAAO;EACR;CACD,WAAW;EACT,MAAM;EACN,WAAW;EACX,OAAO;EACR;CACD,OAAO;EACL,MAAM;EACN,WAAW;EACX,OAAO;EACR;CACF;AAID,SAAS,eAAe,IAAoB;AAC1C,KAAI,KAAK,IAAM,QAAO,GAAG,GAAG;CAC5B,MAAM,IAAI,KAAK,MAAM,KAAK,IAAK;AAC/B,KAAI,IAAI,GAAI,QAAO,GAAG,EAAE;CACxB,MAAM,IAAI,KAAK,MAAM,IAAI,GAAG;CAC5B,MAAM,YAAY,IAAI;AACtB,QAAO,YAAY,IAAI,GAAG,EAAE,IAAI,UAAU,KAAK,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;AA4BtD,SAAgB,WAAW,EACzB,WACA,MACA,SAAS,WACT,SACA,UACA,MACA,kBAAkB,MAClB,MAAM,gBACN,cACA,UACA,WACA,GAAG,SACe;CAClB,MAAM,CAAC,cAAc,mBAAmB,SAAS,gBAAgB;CACjE,MAAM,eAAe,mBAAmB,KAAA;CACxC,MAAM,SAAS,eAAe,iBAAiB;CAE/C,MAAM,oBAAoB,aAAsB;AAC9C,MAAI,CAAC,aAAc,iBAAgB,SAAS;AAC5C,iBAAe,SAAS;;CAG1B,MAAM,gBAAgB,QAAQ;CAC9B,MAAM,eAAe,cAAc;CACnC,MAAM,aAAa,aAAa;CAChC,MAAM,cAAc,QAAQ,aAAa;AAEzC,QACE,oBAAC,YAAgB,MAAjB;EACE,aAAa;EACb,cAAc;EACd,MAAM;YAEN,qBAAC,OAAD;GACE,WAAW,GAAG,4CAA4C,UAAU;GACpE,GAAI;aAFN,CAKE,qBAAC,YAAgB,SAAjB;IAAyB,WAAU;cAAnC;KAEE,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,eAAD,EAAe,WAAU,YAAa,CAAA;MAClC,CAAA;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA,EACN,WACC,oBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA,CAEL;;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACG,aAAa,KAAA,KACZ,oBAAC,QAAD;QAAM,WAAU;kBACb,eAAe,SAAS;QACpB,CAAA;OAET,oBAAC,YAAD,EAAY,WAAW,GAAG,YAAY,aAAa,UAAU,EAAI,CAAA;OACjE,oBAAC,eAAD,EACE,WAAW,GACT,2DACA,UAAU,aACX,EACD,CAAA;OACE;;KACkB;OAG1B,oBAAC,YAAgB,OAAjB;IAAuB,WAAU;cAC/B,oBAAC,OAAD;KAAK,WAAU;KACZ;KACG,CAAA;IACgB,CAAA,CACpB;;EACe,CAAA;;AAI3B,WAAW,cAAc"}
@@ -52,4 +52,4 @@ function AiSuggestion({ suggestion, onClick, className, variant = "secondary", s
52
52
  //#endregion
53
53
  export { SF_AI_SUGGESTION_VARIANTS as i, AiSuggestions as n, SF_AI_SUGGESTION_DEFAULT_VARIANTS as r, AiSuggestion as t };
54
54
 
55
- //# sourceMappingURL=ai-suggestion-CNsCZj5P.js.map
55
+ //# sourceMappingURL=ai-suggestion-MgeCg5Ar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-suggestion-CNsCZj5P.js","names":[],"sources":["../src/components/ai-suggestion/ai-suggestion.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_SUGGESTION_VARIANTS = {} as const;\nexport const SF_AI_SUGGESTION_DEFAULT_VARIANTS = {} as const;\n\n// ─── AiSuggestions ───────────────────────────────────────────────────────────\n\nexport type AiSuggestionsProps = ComponentProps<\"div\">;\n\n/**\n * Horizontally scrollable container for suggestion pills.\n * Position it relative to your prompt input (e.g. `bottom-full`).\n *\n * @example\n * ```tsx\n * <div className=\"relative\">\n * <AiSuggestions>\n * <AiSuggestion suggestion=\"Summarize this\" onClick={handleSuggestion} />\n * <AiSuggestion suggestion=\"Explain in simple terms\" onClick={handleSuggestion} />\n * </AiSuggestions>\n * <AiPromptInput ... />\n * </div>\n * ```\n */\nexport function AiSuggestions({\n className,\n children,\n ...props\n}: AiSuggestionsProps) {\n return (\n <div\n className={cn(\n \"flex w-full flex-nowrap items-center gap-2 overflow-x-auto px-3 py-2\",\n // hide scrollbar cross-browser\n \"[scrollbar-width:none] [&::-webkit-scrollbar]:hidden\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n}\n\n// ─── AiSuggestion ────────────────────────────────────────────────────────────\n\nexport type AiSuggestionProps = Omit<\n ComponentProps<typeof Button>,\n \"onClick\" | \"shape\"\n> & {\n /** The suggestion text — used as button label if no children provided. */\n suggestion: string;\n /** Called with the suggestion string when clicked. */\n onClick?: (suggestion: string) => void;\n};\n\n/**\n * A single suggestion pill button.\n *\n * @example\n * ```tsx\n * <AiSuggestion\n * suggestion=\"What can you help me with?\"\n * onClick={(s) => setInput(s)}\n * />\n * ```\n */\nexport function AiSuggestion({\n suggestion,\n onClick,\n className,\n variant = \"secondary\",\n size = \"sm\",\n children,\n ...props\n}: AiSuggestionProps) {\n return (\n <Button\n className={cn(\"shrink-0 rounded-full px-4\", className)}\n onClick={() => onClick?.(suggestion)}\n size={size}\n type=\"button\"\n variant={variant}\n {...props}\n >\n {children ?? suggestion}\n </Button>\n );\n}\n"],"mappings":";;;;;AASA,IAAa,4BAA4B,EAAE;AAC3C,IAAa,oCAAoC,EAAE;;;;;;;;;;;;;;;;AAqBnD,SAAgB,cAAc,EAC5B,WACA,UACA,GAAG,SACkB;AACrB,QACE,oBAAC,OAAD;EACE,WAAW,GACT,wEAEA,wDACA,UACD;EACD,GAAI;EAEH;EACG,CAAA;;;;;;;;;;;;;AA2BV,SAAgB,aAAa,EAC3B,YACA,SACA,WACA,UAAU,aACV,OAAO,MACP,UACA,GAAG,SACiB;AACpB,QACE,oBAAC,QAAD;EACE,WAAW,GAAG,8BAA8B,UAAU;EACtD,eAAe,UAAU,WAAW;EAC9B;EACN,MAAK;EACI;EACT,GAAI;YAEH,YAAY;EACN,CAAA"}
1
+ {"version":3,"file":"ai-suggestion-MgeCg5Ar.js","names":[],"sources":["../src/components/ai-suggestion/ai-suggestion.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_SUGGESTION_VARIANTS = {} as const;\nexport const SF_AI_SUGGESTION_DEFAULT_VARIANTS = {} as const;\n\n// ─── AiSuggestions ───────────────────────────────────────────────────────────\n\nexport type AiSuggestionsProps = ComponentProps<\"div\">;\n\n/**\n * Horizontally scrollable container for suggestion pills.\n * Position it relative to your prompt input (e.g. `bottom-full`).\n *\n * @example\n * ```tsx\n * <div className=\"relative\">\n * <AiSuggestions>\n * <AiSuggestion suggestion=\"Summarize this\" onClick={handleSuggestion} />\n * <AiSuggestion suggestion=\"Explain in simple terms\" onClick={handleSuggestion} />\n * </AiSuggestions>\n * <AiPromptInput ... />\n * </div>\n * ```\n */\nexport function AiSuggestions({\n className,\n children,\n ...props\n}: AiSuggestionsProps) {\n return (\n <div\n className={cn(\n \"flex w-full flex-nowrap items-center gap-2 overflow-x-auto px-3 py-2\",\n // hide scrollbar cross-browser\n \"[scrollbar-width:none] [&::-webkit-scrollbar]:hidden\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n}\n\n// ─── AiSuggestion ────────────────────────────────────────────────────────────\n\nexport type AiSuggestionProps = Omit<\n ComponentProps<typeof Button>,\n \"onClick\" | \"shape\"\n> & {\n /** The suggestion text — used as button label if no children provided. */\n suggestion: string;\n /** Called with the suggestion string when clicked. */\n onClick?: (suggestion: string) => void;\n};\n\n/**\n * A single suggestion pill button.\n *\n * @example\n * ```tsx\n * <AiSuggestion\n * suggestion=\"What can you help me with?\"\n * onClick={(s) => setInput(s)}\n * />\n * ```\n */\nexport function AiSuggestion({\n suggestion,\n onClick,\n className,\n variant = \"secondary\",\n size = \"sm\",\n children,\n ...props\n}: AiSuggestionProps) {\n return (\n <Button\n className={cn(\"shrink-0 rounded-full px-4\", className)}\n onClick={() => onClick?.(suggestion)}\n size={size}\n type=\"button\"\n variant={variant}\n {...props}\n >\n {children ?? suggestion}\n </Button>\n );\n}\n"],"mappings":";;;;;AASA,IAAa,4BAA4B,EAAE;AAC3C,IAAa,oCAAoC,EAAE;;;;;;;;;;;;;;;;AAqBnD,SAAgB,cAAc,EAC5B,WACA,UACA,GAAG,SACkB;AACrB,QACE,oBAAC,OAAD;EACE,WAAW,GACT,wEAEA,wDACA,UACD;EACD,GAAI;EAEH;EACG,CAAA;;;;;;;;;;;;;AA2BV,SAAgB,aAAa,EAC3B,YACA,SACA,WACA,UAAU,aACV,OAAO,MACP,UACA,GAAG,SACiB;AACpB,QACE,oBAAC,QAAD;EACE,WAAW,GAAG,8BAA8B,UAAU;EACtD,eAAe,UAAU,WAAW;EAC9B;EACN,MAAK;EACI;EACT,GAAI;YAEH,YAAY;EACN,CAAA"}
@@ -1,29 +1,24 @@
1
1
  "use client";
2
2
  import { t as cn } from "./cn-YROP2_ox.js";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
- import { CheckCircleIcon, CircleDashedIcon, CircleNotchIcon, XCircleIcon } from "@phosphor-icons/react";
5
4
  //#region src/components/ai-task-list/ai-task-list.tsx
6
5
  var SF_AI_TASK_LIST_VARIANTS = {};
7
6
  var SF_AI_TASK_LIST_DEFAULT_VARIANTS = {};
8
7
  var STATUS_CONFIG = {
9
8
  pending: {
10
- icon: CircleDashedIcon,
11
- className: "text-sf-subtle/60",
9
+ dotClassName: "bg-sf-subtle/30",
12
10
  textClassName: "text-sf-subtle"
13
11
  },
14
12
  in_progress: {
15
- icon: CircleNotchIcon,
16
- className: "text-sf-brand animate-spin",
13
+ dotClassName: "bg-sf-brand",
17
14
  textClassName: "text-sf-default"
18
15
  },
19
16
  completed: {
20
- icon: CheckCircleIcon,
21
- className: "text-sf-success/60",
17
+ dotClassName: "bg-sf-success",
22
18
  textClassName: "text-sf-subtle/60 line-through"
23
19
  },
24
20
  cancelled: {
25
- icon: XCircleIcon,
26
- className: "text-sf-subtle/40",
21
+ dotClassName: "bg-sf-subtle/30",
27
22
  textClassName: "text-sf-subtle/40 line-through"
28
23
  }
29
24
  };
@@ -37,12 +32,14 @@ var PRIORITY_DOT = {
37
32
  */
38
33
  function AiTaskListItem({ task, className, ...props }) {
39
34
  const config = STATUS_CONFIG[task.status];
40
- const StatusIcon = config.icon;
41
35
  return /* @__PURE__ */ jsxs("div", {
42
36
  className: cn("flex items-start gap-2 py-1", className),
43
37
  ...props,
44
38
  children: [
45
- /* @__PURE__ */ jsx(StatusIcon, { className: cn("mt-0.5 size-3.5 shrink-0", config.className) }),
39
+ /* @__PURE__ */ jsx("span", {
40
+ "aria-hidden": true,
41
+ className: cn("mt-[0.4375rem] size-1.5 shrink-0 rounded-full", config.dotClassName)
42
+ }),
46
43
  /* @__PURE__ */ jsx("span", {
47
44
  className: cn("min-w-0 grow text-sm", config.textClassName),
48
45
  children: task.content
@@ -117,4 +114,4 @@ AiTaskList.displayName = "AiTaskList";
117
114
  //#endregion
118
115
  export { SF_AI_TASK_LIST_VARIANTS as i, AiTaskListItem as n, SF_AI_TASK_LIST_DEFAULT_VARIANTS as r, AiTaskList as t };
119
116
 
120
- //# sourceMappingURL=ai-task-list-B9CpMDYN.js.map
117
+ //# sourceMappingURL=ai-task-list-Da9zIm00.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-task-list-Da9zIm00.js","names":[],"sources":["../src/components/ai-task-list/ai-task-list.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_TASK_LIST_VARIANTS = {} as const;\nexport const SF_AI_TASK_LIST_DEFAULT_VARIANTS = {} as const;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiTaskStatus =\n | \"pending\"\n | \"in_progress\"\n | \"completed\"\n | \"cancelled\";\n\nexport type AiTaskPriority = \"high\" | \"medium\" | \"low\";\n\nexport type AiTaskItem = {\n /** Unique task identifier. */\n id: string;\n /** Task description. */\n content: string;\n /** Current status. */\n status: AiTaskStatus;\n /** Priority level. */\n priority?: AiTaskPriority;\n};\n\nexport type AiTaskListProps = Omit<HTMLAttributes<HTMLDivElement>, \"title\"> & {\n /** List of tasks. */\n tasks: AiTaskItem[];\n /** Optional title above the task list. */\n title?: string;\n /** Custom icon for the title area. */\n icon?: ElementType;\n /** Show a progress summary (e.g. \"3 / 7 done\"). Default: `true`. */\n showProgress?: boolean;\n /** Content rendered below the task list. */\n children?: ReactNode;\n};\n\nexport type AiTaskListItemProps = HTMLAttributes<HTMLDivElement> & {\n task: AiTaskItem;\n};\n\n// ─── Status config ────────────────────────────────────────────────────────────\n\nconst STATUS_CONFIG: Record<\n AiTaskStatus,\n { dotClassName: string; textClassName: string }\n> = {\n pending: {\n dotClassName: \"bg-sf-subtle/30\",\n textClassName: \"text-sf-subtle\",\n },\n in_progress: {\n dotClassName: \"bg-sf-brand\",\n textClassName: \"text-sf-default\",\n },\n completed: {\n dotClassName: \"bg-sf-success\",\n textClassName: \"text-sf-subtle/60 line-through\",\n },\n cancelled: {\n dotClassName: \"bg-sf-subtle/30\",\n textClassName: \"text-sf-subtle/40 line-through\",\n },\n};\n\nconst PRIORITY_DOT: Record<AiTaskPriority, string> = {\n high: \"bg-sf-danger\",\n medium: \"bg-sf-warning\",\n low: \"bg-sf-subtle\",\n};\n\n// ─── AiTaskListItem ──────────────────────────────────────────────────────────\n\n/**\n * Single task item with status icon, content, and optional priority dot.\n */\nexport function AiTaskListItem({\n task,\n className,\n ...props\n}: AiTaskListItemProps) {\n const config = STATUS_CONFIG[task.status];\n\n return (\n <div className={cn(\"flex items-start gap-2 py-1\", className)} {...props}>\n <span\n aria-hidden\n className={cn(\n \"mt-[0.4375rem] size-1.5 shrink-0 rounded-full\",\n config.dotClassName\n )}\n />\n <span className={cn(\"min-w-0 grow text-sm\", config.textClassName)}>\n {task.content}\n </span>\n {task.priority && (\n <span\n className={cn(\n \"mt-2 size-1 shrink-0 rounded-full\",\n PRIORITY_DOT[task.priority]\n )}\n title={`${task.priority} priority`}\n />\n )}\n </div>\n );\n}\n\nAiTaskListItem.displayName = \"AiTaskListItem\";\n\n// ─── AiTaskList ──────────────────────────────────────────────────────────────\n\n/**\n * Task progress list. Renders structured tasks from the harness `task_write`\n * tool. Typically rendered inside the `PromptInputBackLayer` or inline in\n * conversation.\n *\n * Maps to harness event: `task_updated`.\n *\n * @example\n * ```tsx\n * <AiTaskList\n * title=\"Current tasks\"\n * tasks={[\n * { id: \"1\", content: \"Read config file\", status: \"completed\", priority: \"high\" },\n * { id: \"2\", content: \"Run migrations\", status: \"in_progress\", priority: \"high\" },\n * { id: \"3\", content: \"Seed database\", status: \"pending\", priority: \"medium\" },\n * ]}\n * />\n * ```\n */\nexport function AiTaskList({\n tasks,\n title,\n icon,\n showProgress = true,\n children,\n className,\n ...props\n}: AiTaskListProps) {\n const completed = tasks.filter((t) => t.status === \"completed\").length;\n const total = tasks.filter((t) => t.status !== \"cancelled\").length;\n\n return (\n <div className={cn(\"flex flex-col gap-1\", className)} {...props}>\n {/* Header */}\n {(title || showProgress) && (\n <div className=\"flex items-center justify-between gap-2 pb-0.5\">\n <div className=\"flex items-center gap-1.5\">\n {icon &&\n (() => {\n const Icon = icon;\n return <Icon className=\"size-3.5 text-sf-subtle\" />;\n })()}\n {title && <span className=\"text-sm text-sf-subtle\">{title}</span>}\n </div>\n {showProgress && total > 0 && (\n <span className=\"text-xs tabular-nums text-sf-subtle/60\">\n {completed}/{total}\n </span>\n )}\n </div>\n )}\n\n {/* Tasks */}\n <div className=\"flex flex-col\">\n {tasks.map((task) => (\n <AiTaskListItem key={task.id} task={task} />\n ))}\n </div>\n\n {/* Progress bar */}\n {showProgress && total > 0 && (\n <div className=\"mt-1 h-0.5 overflow-hidden rounded-full bg-sf-tint\">\n <div\n className=\"h-full rounded-full bg-sf-brand/60 transition-[width] duration-300 ease-out\"\n style={{ width: `${(completed / total) * 100}%` }}\n />\n </div>\n )}\n\n {children}\n </div>\n );\n}\n\nAiTaskList.displayName = \"AiTaskList\";\n"],"mappings":";;;;AAQA,IAAa,2BAA2B,EAAE;AAC1C,IAAa,mCAAmC,EAAE;AA0ClD,IAAM,gBAGF;CACF,SAAS;EACP,cAAc;EACd,eAAe;EAChB;CACD,aAAa;EACX,cAAc;EACd,eAAe;EAChB;CACD,WAAW;EACT,cAAc;EACd,eAAe;EAChB;CACD,WAAW;EACT,cAAc;EACd,eAAe;EAChB;CACF;AAED,IAAM,eAA+C;CACnD,MAAM;CACN,QAAQ;CACR,KAAK;CACN;;;;AAOD,SAAgB,eAAe,EAC7B,MACA,WACA,GAAG,SACmB;CACtB,MAAM,SAAS,cAAc,KAAK;AAElC,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,+BAA+B,UAAU;EAAE,GAAI;YAAlE;GACE,oBAAC,QAAD;IACE,eAAA;IACA,WAAW,GACT,iDACA,OAAO,aACR;IACD,CAAA;GACF,oBAAC,QAAD;IAAM,WAAW,GAAG,wBAAwB,OAAO,cAAc;cAC9D,KAAK;IACD,CAAA;GACN,KAAK,YACJ,oBAAC,QAAD;IACE,WAAW,GACT,qCACA,aAAa,KAAK,UACnB;IACD,OAAO,GAAG,KAAK,SAAS;IACxB,CAAA;GAEA;;;AAIV,eAAe,cAAc;;;;;;;;;;;;;;;;;;;;AAuB7B,SAAgB,WAAW,EACzB,OACA,OACA,MACA,eAAe,MACf,UACA,WACA,GAAG,SACe;CAClB,MAAM,YAAY,MAAM,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC;CAChE,MAAM,QAAQ,MAAM,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC;AAE5D,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,uBAAuB,UAAU;EAAE,GAAI;YAA1D;IAEI,SAAS,iBACT,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACG,QAGU,oBADM,MACN,EAAM,WAAU,2BAA4B,CAAA,EAEtD,SAAS,oBAAC,QAAD;MAAM,WAAU;gBAA0B;MAAa,CAAA,CAC7D;QACL,gBAAgB,QAAQ,KACvB,qBAAC,QAAD;KAAM,WAAU;eAAhB;MACG;MAAU;MAAE;MACR;OAEL;;GAIR,oBAAC,OAAD;IAAK,WAAU;cACZ,MAAM,KAAK,SACV,oBAAC,gBAAD,EAAoC,MAAQ,EAAvB,KAAK,GAAkB,CAC5C;IACE,CAAA;GAGL,gBAAgB,QAAQ,KACvB,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAE,OAAO,GAAI,YAAY,QAAS,IAAI,IAAI;KACjD,CAAA;IACE,CAAA;GAGP;GACG;;;AAIV,WAAW,cAAc"}
@@ -370,4 +370,4 @@ AiTimeline.displayName = "AiTimeline";
370
370
  //#endregion
371
371
  export { SF_AI_TIMELINE_VARIANTS as i, AiTimelineLane as n, SF_AI_TIMELINE_DEFAULT_VARIANTS as r, AiTimeline as t };
372
372
 
373
- //# sourceMappingURL=ai-timeline-Bb5ntsr3.js.map
373
+ //# sourceMappingURL=ai-timeline-Cwu045IR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-timeline-Cwu045IR.js","names":[],"sources":["../src/components/ai-timeline/ai-timeline.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n BrainIcon,\n CaretDownIcon,\n CheckCircleIcon,\n CircleDashedIcon,\n CodeIcon,\n LightningIcon,\n MagnifyingGlassIcon,\n RobotIcon,\n ShieldWarningIcon,\n SpinnerGapIcon,\n WrenchIcon,\n XCircleIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps, ElementType, ReactNode } from \"react\";\nimport { forwardRef, useCallback, useMemo, useRef, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_TIMELINE_VARIANTS = {\n density: {\n comfortable: { classes: \"\", description: \"Taller lanes with more padding\" },\n compact: { classes: \"\", description: \"Tight lanes for dense views\" },\n },\n} as const;\n\nexport const SF_AI_TIMELINE_DEFAULT_VARIANTS = {\n density: \"comfortable\",\n} as const;\n\nexport type SFAiTimelineDensity = keyof typeof SF_AI_TIMELINE_VARIANTS.density;\n\n// ─── Block types ─────────────────────────────────────────────────────────────\n\n/** Visual type of a timeline block — controls color and icon. */\nexport type AiTimelineBlockType =\n | \"running\" // brand blue — agent actively generating\n | \"tool_call\" // amber — executing a tool\n | \"waiting\" // gray — idle/blocked\n | \"approval\" // purple — waiting for human approval\n | \"completed\" // green — successfully finished\n | \"error\"; // red — failed\n\n/** A colored segment on a lane representing a period of activity. */\nexport interface AiTimelineBlock {\n /** Unique ID for this block. */\n id: string;\n /** Visual type (controls color). */\n type: AiTimelineBlockType;\n /** Wall-clock ms when this activity started. */\n startMs: number;\n /**\n * Wall-clock ms when this activity ended.\n * Undefined means still active — the block extends to \"now\".\n */\n endMs?: number;\n /** Short label shown inside the block when space allows. */\n label?: string;\n /** Longer description shown in the expanded detail panel. */\n description?: string;\n /** Extra detail content shown when the block is expanded. */\n detail?: ReactNode;\n}\n\n// ─── Lane props ───────────────────────────────────────────────────────────────\n\nexport type AiTimelineLaneProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n /** Lane label (agent name). */\n label: string;\n /** Agent type string for icon selection. */\n agentType?: string;\n /** Model ID shown as a subtle badge. */\n modelId?: string;\n /** Current lane status — controls the label icon. */\n status?: \"idle\" | \"running\" | \"completed\" | \"error\";\n /** Ordered activity blocks for this lane. */\n blocks: AiTimelineBlock[];\n /** Timeline origin (ms) — same as AiTimeline timeOrigin. */\n timeOrigin: number;\n /** Current \"now\" timestamp (ms) for live blocks. */\n nowMs: number;\n /** Pixels per millisecond (zoom level). */\n pxPerMs: number;\n /** Whether the lane is expanded to show block details. */\n defaultExpanded?: boolean;\n /** Callback when a block is clicked. */\n onBlockClick?: (block: AiTimelineBlock) => void;\n /** Custom icon override. */\n icon?: ElementType;\n /** Lane height variant. */\n density?: SFAiTimelineDensity;\n};\n\n// ─── Timeline props ───────────────────────────────────────────────────────────\n\nexport type AiTimelineProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n /**\n * Wall-clock ms for the timeline origin (t=0).\n * Defaults to the startMs of the first block across all lanes.\n */\n timeOrigin?: number;\n /**\n * Pixels per second of elapsed time.\n * Controls horizontal zoom level.\n * @default 30\n */\n pixelsPerSecond?: number;\n /**\n * Whether to show a pulsing \"now\" marker at the live edge.\n * @default true\n */\n showNowMarker?: boolean;\n /**\n * Current timestamp in ms. Used to render the \"now\" marker and open-ended\n * block widths. Pass a stable `useState` value to prevent hydration shifts.\n * When omitted, `Date.now()` is called at render time (fine for live views\n * driven by a ticking parent, not suitable for SSR).\n */\n nowMs?: number;\n /** Lane density variant. @default \"comfortable\" */\n density?: SFAiTimelineDensity;\n /** Children should be `AiTimelineLane` components. */\n children?: ReactNode;\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nconst AGENT_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 main: LightningIcon,\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_ICONS)) {\n if (lower.includes(key)) return icon;\n }\n }\n return RobotIcon;\n}\n\nfunction getModelShort(modelId?: string) {\n if (!modelId) return \"\";\n return (\n modelId\n .split(\"/\")\n .pop()\n ?.replace(/^claude-/, \"\")\n .replace(/-\\d+$/, \"\") ?? \"\"\n );\n}\n\nfunction formatMs(ms: number) {\n const s = Math.round(ms / 100) / 10;\n if (s < 60) return `${s}s`;\n return `${Math.floor(s / 60)}m ${Math.round(s % 60)}s`;\n}\n\n// Block colors — all semantic SignalFlare tokens, no raw Tailwind colors\nconst BLOCK_COLORS: Record<AiTimelineBlockType, string> = {\n running: \"bg-sf-brand text-sf-inverse\",\n tool_call: \"bg-sf-warning text-sf-inverse\",\n waiting: \"bg-sf-fill text-sf-subtle\",\n approval: \"bg-sf-info text-sf-inverse\",\n completed: \"bg-sf-brand/60 text-sf-inverse\",\n error: \"bg-sf-danger text-sf-inverse\",\n};\n\n// Hover is a uniform subtle brightness lift — no per-type color shifting\nconst BLOCK_HOVER = \"hover:brightness-110 hover:shadow-sm\";\n\nfunction BlockIcon({ type }: { type: AiTimelineBlockType }) {\n switch (type) {\n case \"running\":\n return <SpinnerGapIcon size={8} className=\"animate-spin\" />;\n case \"tool_call\":\n return <WrenchIcon size={8} />;\n case \"approval\":\n return <ShieldWarningIcon size={8} />;\n case \"completed\":\n return <CheckCircleIcon size={8} />;\n case \"error\":\n return <XCircleIcon size={8} />;\n default:\n return <CircleDashedIcon size={8} />;\n }\n}\n\n// ─── AiTimelineBlock (rendered) ───────────────────────────────────────────────\n\nfunction TimelineBlockSegment({\n block,\n timeOrigin,\n nowMs,\n pxPerMs,\n laneHeight,\n onBlockClick,\n rowIndex = 0,\n rowCount = 1,\n}: {\n block: AiTimelineBlock;\n timeOrigin: number;\n nowMs: number;\n pxPerMs: number;\n laneHeight: number;\n onBlockClick?: (block: AiTimelineBlock) => void;\n rowIndex?: number;\n rowCount?: number;\n}) {\n const left = Math.max(0, (block.startMs - timeOrigin) * pxPerMs);\n const end = block.endMs ?? nowMs;\n const width = Math.max(4, (end - block.startMs) * pxPerMs);\n const durationMs = end - block.startMs;\n\n const MIN_LABEL_PX = 40;\n // Short blocks get sharp corners to avoid weird rounded look when overlaid\n const isShort = width < 20;\n const roundedClass = isShort\n ? \"rounded-none\"\n : width < 40\n ? \"rounded-sm\"\n : \"rounded-md\";\n\n // Calculate vertical position for overlapping blocks\n // Blocks that overlap in time stack vertically, centered within the lane\n const blockHeight = laneHeight - 8;\n const innerPadding = 2;\n const availableHeight = blockHeight - innerPadding * 2;\n const rowHeight = availableHeight / rowCount;\n const topOffset = innerPadding + rowIndex * rowHeight;\n\n return (\n <button\n type=\"button\"\n aria-label={block.label ?? block.type}\n title={block.description ?? block.label}\n onClick={onBlockClick ? () => onBlockClick(block) : undefined}\n className={cn(\n \"group absolute transition-[width] duration-150\",\n roundedClass,\n BLOCK_COLORS[block.type],\n onBlockClick ? BLOCK_HOVER : \"cursor-default\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sf-ring\",\n \"flex items-center gap-1 overflow-hidden px-1.5\"\n )}\n style={{\n left,\n width,\n height: rowHeight - 1,\n top: topOffset,\n }}\n >\n <BlockIcon type={block.type} />\n {width >= MIN_LABEL_PX && block.label && (\n <span className=\"truncate text-[9px] font-medium leading-none\">\n {block.label}\n </span>\n )}\n {width >= MIN_LABEL_PX && (\n <span className=\"ml-auto shrink-0 text-[8px] opacity-70\">\n {formatMs(durationMs)}\n </span>\n )}\n </button>\n );\n}\n\n/**\n * Calculate row assignments for blocks to avoid overlap.\n * Returns arrays of blocks grouped by row, where no blocks in a row overlap in time.\n */\nfunction computeBlockRows(blocks: AiTimelineBlock[]): AiTimelineBlock[][] {\n if (blocks.length === 0) return [];\n\n // Sort blocks by start time\n const sorted = [...blocks].sort((a, b) => a.startMs - b.startMs);\n const rows: AiTimelineBlock[][] = [];\n\n for (const block of sorted) {\n // Find the first row where this block doesn't overlap with any existing block\n let placed = false;\n for (const row of rows) {\n const overlaps = row.some(\n (existing) =>\n block.startMs < (existing.endMs ?? Infinity) &&\n (block.endMs ?? Infinity) > existing.startMs\n );\n if (!overlaps) {\n row.push(block);\n placed = true;\n break;\n }\n }\n if (!placed) {\n // Start a new row\n rows.push([block]);\n }\n }\n\n return rows;\n}\n\n// ─── AiTimelineLane ───────────────────────────────────────────────────────────\n\nconst LANE_HEIGHT_COMFORTABLE = 40;\nconst LANE_HEIGHT_COMPACT = 28;\nconst LABEL_WIDTH = 128;\n\n/**\n * `AiTimelineLane` — a single horizontal swim lane within an `AiTimeline`.\n *\n * Shows one agent's activity as colored blocks on a time axis. Blocks\n * represent periods of activity: running, tool calls, waiting, etc.\n *\n * Consumed by `AiTimeline` — pass lanes as children.\n */\nexport const AiTimelineLane = forwardRef<HTMLDivElement, AiTimelineLaneProps>(\n (\n {\n label,\n agentType,\n modelId,\n status = \"idle\",\n blocks,\n timeOrigin,\n nowMs,\n pxPerMs,\n defaultExpanded = false,\n onBlockClick,\n icon,\n density = \"comfortable\",\n className,\n ...props\n },\n ref\n ) => {\n const [expanded, setExpanded] = useState(defaultExpanded);\n const Icon = getAgentIcon(agentType, icon);\n const modelShort = getModelShort(modelId);\n\n // Compute block rows to handle overlapping blocks\n const blockRows = useMemo(() => computeBlockRows(blocks), [blocks]);\n const rowCount = blockRows.length;\n\n // Lane height scales with number of rows for overlapping blocks\n const baseLaneH =\n density === \"compact\" ? LANE_HEIGHT_COMPACT : LANE_HEIGHT_COMFORTABLE;\n const laneH = rowCount > 1 ? baseLaneH * rowCount : baseLaneH;\n\n // Total timeline width needed for this lane\n const maxEnd = useMemo(() => {\n if (!blocks.length) return timeOrigin;\n return Math.max(...blocks.map((b) => b.endMs ?? nowMs));\n }, [blocks, timeOrigin, nowMs]);\n\n const totalWidth = Math.max(0, (maxEnd - timeOrigin) * pxPerMs);\n\n return (\n <div\n ref={ref}\n className={cn(\"border-b border-sf-line last:border-b-0\", className)}\n {...props}\n >\n {/* Lane row */}\n <div className=\"flex\" style={{ height: laneH }}>\n {/* Label sidebar */}\n <div\n className=\"flex shrink-0 items-center gap-2 border-r border-sf-line bg-sf-base px-3\"\n style={{ width: LABEL_WIDTH }}\n >\n {/* Expand toggle (only when there are expandable blocks) */}\n {blocks.some((b) => b.detail) && (\n <button\n type=\"button\"\n aria-label={expanded ? \"Collapse lane\" : \"Expand lane\"}\n onClick={() => setExpanded((v) => !v)}\n className=\"shrink-0 text-sf-subtle hover:text-sf-default focus-visible:outline-none\"\n >\n <CaretDownIcon\n size={10}\n className={cn(\n \"transition-transform\",\n expanded && \"rotate-180\"\n )}\n />\n </button>\n )}\n\n {/* Agent icon */}\n <div\n className={cn(\n \"flex shrink-0 items-center justify-center rounded\",\n density === \"compact\" ? \"size-5\" : \"size-6\",\n status === \"running\"\n ? \"text-sf-brand\"\n : status === \"completed\"\n ? \"text-sf-success\"\n : status === \"error\"\n ? \"text-sf-danger\"\n : \"text-sf-subtle\"\n )}\n >\n {status === \"running\" ? (\n <SpinnerGapIcon\n size={density === \"compact\" ? 12 : 14}\n className=\"animate-spin\"\n />\n ) : (\n <Icon size={density === \"compact\" ? 12 : 14} />\n )}\n </div>\n\n {/* Name + model */}\n <div className=\"min-w-0 flex-1\">\n <div\n className={cn(\n \"truncate font-medium text-sf-default\",\n density === \"compact\" ? \"text-[10px]\" : \"text-xs\"\n )}\n >\n {label}\n </div>\n {modelShort && density === \"comfortable\" && (\n <div className=\"truncate font-mono text-[9px] text-sf-inactive\">\n {modelShort}\n </div>\n )}\n </div>\n </div>\n\n {/* Blocks area */}\n <div\n className=\"relative flex-1 overflow-hidden\"\n style={{ minWidth: totalWidth }}\n >\n {blockRows.map((row, rowIndex) =>\n row.map((block) => (\n <TimelineBlockSegment\n key={block.id}\n block={block}\n timeOrigin={timeOrigin}\n nowMs={nowMs}\n pxPerMs={pxPerMs}\n laneHeight={laneH}\n onBlockClick={onBlockClick}\n rowIndex={rowIndex}\n rowCount={rowCount}\n />\n ))\n )}\n </div>\n </div>\n\n {/* Expanded detail rows */}\n {expanded &&\n blocks\n .filter((b) => b.detail)\n .map((block) => (\n <div\n key={`detail-${block.id}`}\n className=\"ml-[128px] border-t border-sf-line/50 bg-sf-recessed px-3 py-2\"\n >\n <div className=\"mb-1 flex items-center gap-1.5\">\n <span\n className={cn(\n \"size-1.5 rounded-full\",\n BLOCK_COLORS[block.type].split(\" \")[0]\n )}\n />\n <span className=\"text-[10px] font-medium text-sf-strong\">\n {block.label ?? block.type}\n </span>\n {block.endMs && (\n <span className=\"text-[10px] text-sf-subtle\">\n {formatMs(block.endMs - block.startMs)}\n </span>\n )}\n </div>\n <div className=\"text-xs text-sf-subtle\">{block.detail}</div>\n </div>\n ))}\n </div>\n );\n }\n);\n\nAiTimelineLane.displayName = \"AiTimelineLane\";\n\n// ─── AiTimeline ───────────────────────────────────────────────────────────────\n\nconst TIME_AXIS_HEIGHT = 24;\n\n/** Format elapsed time for tick labels — shows seconds with 1 decimal when under 1 minute */\nfunction formatElapsed(ms: number): string {\n if (ms < 60_000) {\n const s = Math.round(ms / 100) / 10;\n return `${s.toFixed(s % 1 === 0 ? 0 : 1)}s`;\n }\n const m = Math.floor(ms / 60_000);\n const s = Math.round((ms % 60_000) / 1000);\n return s > 0 ? `${m}m ${s}s` : `${m}m`;\n}\n\n/** Generate time axis tick marks spaced appropriately for the zoom level. */\nfunction generateTicks(\n originMs: number,\n nowMs: number,\n pxPerMs: number,\n totalWidth: number\n): Array<{ label: string; sublabel?: string; x: number }> {\n const durationMs = nowMs - originMs;\n const tickIntervalMs = (() => {\n const pxPerSec = pxPerMs * 1000;\n if (pxPerSec >= 100) return 1000; // 1s ticks\n if (pxPerSec >= 30) return 5000; // 5s ticks\n if (pxPerSec >= 10) return 10_000; // 10s ticks\n if (pxPerSec >= 5) return 30_000; // 30s ticks\n return 60_000; // 1min ticks\n })();\n\n const ticks: Array<{ label: string; sublabel?: string; x: number }> = [];\n const firstTick = Math.ceil(originMs / tickIntervalMs) * tickIntervalMs;\n\n for (let t = firstTick; t <= originMs + durationMs; t += tickIntervalMs) {\n const elapsed = t - originMs;\n const x = elapsed * pxPerMs;\n if (x > totalWidth) break;\n const label = formatElapsed(elapsed);\n ticks.push({ label, x });\n }\n return ticks;\n}\n\n/**\n * `AiTimeline` — horizontal swim-lane timeline for commander-level orchestration.\n *\n * Displays multiple `AiTimelineLane` children on a shared time axis. Supports\n * live updating (a \"now\" marker follows the live edge), panning, and zoom via\n * `pixelsPerSecond`.\n *\n * @example\n * ```tsx\n * <AiTimeline\n * timeOrigin={state.missionStartedAt}\n * pixelsPerSecond={30}\n * showNowMarker\n * >\n * <AiTimelineLane\n * label=\"Main\"\n * blocks={mainLaneBlocks}\n * timeOrigin={timeOrigin}\n * nowMs={Date.now()}\n * pxPerMs={pxPerMs}\n * />\n * <AiTimelineLane label=\"Explore\" blocks={exploreBlocks} ... />\n * </AiTimeline>\n * ```\n */\nexport const AiTimeline = forwardRef<HTMLDivElement, AiTimelineProps>(\n (\n {\n timeOrigin,\n pixelsPerSecond = 30,\n showNowMarker = true,\n nowMs: nowMsProp,\n density: _density = SF_AI_TIMELINE_DEFAULT_VARIANTS.density,\n className,\n children,\n ...props\n },\n ref\n ) => {\n // Use the provided stable nowMs, or fall back to Date.now().\n // For SSR-safe demos, always pass a stable useState value as nowMs.\n const nowMs = nowMsProp ?? Date.now();\n const origin = timeOrigin ?? nowMs;\n const pxPerMs = pixelsPerSecond / 1000;\n const totalDurationMs = nowMs - origin;\n const totalWidth = Math.max(400, totalDurationMs * pxPerMs + 120);\n const nowX = totalDurationMs * pxPerMs;\n\n const ticks = useMemo(\n () => generateTicks(origin, nowMs, pxPerMs, totalWidth),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [origin, Math.floor(nowMs / 2000), pxPerMs, totalWidth]\n );\n\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Scroll to keep the now-marker in view\n const scrollToNow = useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n const targetScroll = LABEL_WIDTH + nowX - el.clientWidth * 0.85;\n el.scrollLeft = Math.max(0, targetScroll);\n }, [nowX]);\n\n return (\n <div\n ref={ref}\n className={cn(\n \"relative overflow-hidden rounded-lg border border-sf-line bg-sf-base\",\n className\n )}\n {...props}\n >\n {/* Scrollable horizontal container */}\n <div ref={scrollRef} className=\"overflow-x-auto\">\n <div style={{ minWidth: LABEL_WIDTH + totalWidth }}>\n {/* Time axis */}\n <div\n className=\"flex border-b border-sf-line\"\n style={{ height: TIME_AXIS_HEIGHT }}\n >\n {/* Label column spacer */}\n <div\n className=\"shrink-0 border-r border-sf-line bg-sf-elevated px-3\"\n style={{ width: LABEL_WIDTH }}\n >\n <span className=\"text-[10px] font-medium uppercase tracking-wider text-sf-subtle\">\n Time\n </span>\n </div>\n {/* Axis ticks */}\n <div\n className=\"relative flex-1 bg-sf-recessed\"\n style={{ minWidth: totalWidth }}\n >\n {ticks.map((tick) => (\n <div\n key={tick.x}\n className=\"absolute top-0 flex flex-col items-center\"\n style={{ left: tick.x }}\n >\n <div className=\"h-2 w-px bg-sf-line\" />\n <span className=\"mt-0.5 font-mono text-[9px] text-sf-inactive\">\n +{tick.label}\n </span>\n </div>\n ))}\n\n {/* Now marker line */}\n {showNowMarker && (\n <div\n className=\"absolute top-0 flex flex-col items-center\"\n style={{ left: nowX }}\n >\n <div className=\"h-2 w-px bg-sf-brand\" />\n <span className=\"mt-0.5 font-mono text-[9px] font-medium text-sf-brand\">\n now\n </span>\n </div>\n )}\n </div>\n </div>\n\n {/* Lanes — pass timeOrigin/nowMs/pxPerMs via context-like clone or expect them as props */}\n <div className=\"relative\">\n {/* Vertical grid lines at tick positions */}\n <div className=\"pointer-events-none absolute inset-0 ml-[128px]\">\n {ticks.map((tick) => (\n <div\n key={`grid-${tick.x}`}\n className=\"absolute top-0 h-full w-px bg-sf-line/50\"\n style={{ left: tick.x }}\n />\n ))}\n {/* Now marker vertical line */}\n {showNowMarker && (\n <div\n className=\"absolute top-0 h-full w-px bg-sf-brand/30\"\n style={{ left: nowX }}\n />\n )}\n </div>\n\n {children}\n </div>\n </div>\n </div>\n\n {/* Scroll-to-now button */}\n {showNowMarker && (\n <button\n type=\"button\"\n aria-label=\"Scroll to now\"\n onClick={scrollToNow}\n className={cn(\n \"absolute right-2 top-1 rounded px-1.5 py-0.5 text-[9px] font-medium\",\n \"bg-sf-brand/10 text-sf-brand hover:bg-sf-brand/20 transition-colors\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sf-ring\"\n )}\n >\n → now\n </button>\n )}\n </div>\n );\n }\n);\n\nAiTimeline.displayName = \"AiTimeline\";\n"],"mappings":"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"}
@@ -2,8 +2,8 @@
2
2
  import { t as cn } from "./cn-YROP2_ox.js";
3
3
  import { t as Tooltip } from "./tooltip-Cb7QW-7H.js";
4
4
  import { t as Button } from "./button-De0267YU.js";
5
- import { t as AiStatusBadge } from "./ai-status-badge-WhbKVeqn.js";
6
- import { useState } from "react";
5
+ import { t as AiStatusBadge } from "./ai-status-badge-BGYGWYF6.js";
6
+ import { useEffect, useRef, useState } from "react";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
  import { BrainIcon, CaretDownIcon, ChatCircleIcon, CheckIcon, CircleIcon, CodeIcon, DatabaseIcon, FileTextIcon, GearIcon, GlobeIcon, ImageIcon, LightningIcon, MagnifyingGlassIcon, ShieldWarningIcon, SpinnerGapIcon, TerminalIcon, WarningCircleIcon, XCircleIcon } from "@phosphor-icons/react";
9
9
  import { Collapsible } from "@base-ui/react/collapsible";
@@ -20,6 +20,10 @@ var SF_AI_TOOL_VARIANTS = { variant: {
20
20
  minimal: {
21
21
  classes: "",
22
22
  description: "Pill-shaped status badge"
23
+ },
24
+ ephemeral: {
25
+ classes: "",
26
+ description: "Single-status row that auto-dismisses after the call completes. Persists on error or when the user expands it."
23
27
  }
24
28
  } };
25
29
  var SF_AI_TOOL_DEFAULT_VARIANTS = { variant: "default" };
@@ -80,21 +84,27 @@ function getStateConfig(part, approvalState) {
80
84
  bgClass: "bg-sf-warning/10",
81
85
  icon: ShieldWarningIcon,
82
86
  spin: false,
83
- label: "Awaiting Approval"
87
+ label: "Awaiting Approval",
88
+ dotClass: "bg-sf-warning",
89
+ dotPulse: true
84
90
  };
85
91
  if (approvalState === "declined") return {
86
92
  colorClass: "text-sf-danger",
87
93
  bgClass: "bg-sf-danger/10",
88
94
  icon: XCircleIcon,
89
95
  spin: false,
90
- label: "Declined"
96
+ label: "Declined",
97
+ dotClass: "bg-sf-danger",
98
+ dotPulse: false
91
99
  };
92
100
  if (hasToolError(part)) return {
93
101
  colorClass: "text-sf-danger",
94
102
  bgClass: "bg-sf-danger/10",
95
103
  icon: WarningCircleIcon,
96
104
  spin: false,
97
- label: "Failed"
105
+ label: "Failed",
106
+ dotClass: "bg-sf-danger",
107
+ dotPulse: false
98
108
  };
99
109
  switch (part.state) {
100
110
  case "input-streaming": return {
@@ -102,28 +112,36 @@ function getStateConfig(part, approvalState) {
102
112
  bgClass: "bg-sf-brand/10",
103
113
  icon: CircleIcon,
104
114
  spin: false,
105
- label: "Streaming"
115
+ label: "Streaming",
116
+ dotClass: "bg-sf-warning",
117
+ dotPulse: true
106
118
  };
107
119
  case "input-available": return {
108
120
  colorClass: "text-sf-brand",
109
121
  bgClass: "bg-sf-brand/10",
110
122
  icon: SpinnerGapIcon,
111
123
  spin: true,
112
- label: "Running"
124
+ label: "Running",
125
+ dotClass: "bg-sf-warning",
126
+ dotPulse: true
113
127
  };
114
128
  case "output-available": return {
115
129
  colorClass: "text-sf-success",
116
130
  bgClass: "bg-sf-tint",
117
131
  icon: CheckIcon,
118
132
  spin: false,
119
- label: "Complete"
133
+ label: "Complete",
134
+ dotClass: "bg-sf-success",
135
+ dotPulse: false
120
136
  };
121
137
  default: return {
122
138
  colorClass: "text-sf-subtle",
123
139
  bgClass: "bg-sf-tint",
124
140
  icon: CircleIcon,
125
141
  spin: false,
126
- label: "Pending"
142
+ label: "Pending",
143
+ dotClass: "bg-sf-subtle/40",
144
+ dotPulse: false
127
145
  };
128
146
  }
129
147
  }
@@ -140,13 +158,30 @@ function getStateConfig(part, approvalState) {
140
158
  * />
141
159
  * ```
142
160
  */
143
- function AiToolCall({ part, variant = "default", duration, summary, icon: CustomIcon, approvalState, onApprove, onReject, defaultExpanded = false, className, ...props }) {
161
+ function AiToolCall({ part, variant = "default", duration, summary, icon: CustomIcon, approvalState, onApprove, onReject, defaultExpanded = false, dismissDelay = 800, persist = false, onDismiss, className, ...props }) {
144
162
  const [isExpanded, setIsExpanded] = useState(defaultExpanded);
145
163
  const IconComponent = CustomIcon ?? getToolIcon(part);
146
164
  const displayName = getToolDisplayName(part);
147
165
  const config = getStateConfig(part, approvalState);
148
166
  const StatusIconEl = config.icon;
149
167
  const errorText = getToolErrorText(part);
168
+ if (variant === "ephemeral") return /* @__PURE__ */ jsx(AiToolCallEphemeral, {
169
+ part,
170
+ approvalState,
171
+ duration,
172
+ summary,
173
+ icon: IconComponent,
174
+ displayName,
175
+ config,
176
+ errorText,
177
+ dismissDelay,
178
+ persist,
179
+ onDismiss,
180
+ onApprove,
181
+ onReject,
182
+ className,
183
+ ...props
184
+ });
150
185
  if (variant === "minimal") {
151
186
  const handleGetToolStatus = () => {
152
187
  if (hasToolError(part)) return "error";
@@ -289,6 +324,128 @@ function AiToolCall({ part, variant = "default", duration, summary, icon: Custom
289
324
  })
290
325
  });
291
326
  }
327
+ var FADE_OUT_MS = 200;
328
+ /**
329
+ * Internal renderer for `variant="ephemeral"`. Shows a single-status row
330
+ * that auto-dismisses once the tool call completes. Pins on error or when
331
+ * the user expands the row.
332
+ */
333
+ function AiToolCallEphemeral({ part, approvalState, duration, summary, icon: _icon, displayName, config, errorText, dismissDelay, persist, onDismiss, onApprove, onReject, className, ...props }) {
334
+ const [isExpanded, setIsExpanded] = useState(false);
335
+ const [phase, setPhase] = useState("live");
336
+ const userExpandedRef = useRef(false);
337
+ const hasError = hasToolError(part);
338
+ const isComplete = hasToolResult(part);
339
+ const isAwaitingApproval = approvalState === "awaiting";
340
+ const pinned = persist || hasError || userExpandedRef.current || isAwaitingApproval;
341
+ useEffect(() => {
342
+ if (phase !== "live") return;
343
+ if (!isComplete) return;
344
+ if (pinned) return;
345
+ if (dismissDelay <= 0) return;
346
+ const settleTimer = setTimeout(() => {
347
+ setPhase("fading");
348
+ }, dismissDelay);
349
+ return () => clearTimeout(settleTimer);
350
+ }, [
351
+ phase,
352
+ isComplete,
353
+ pinned,
354
+ dismissDelay
355
+ ]);
356
+ useEffect(() => {
357
+ if (phase !== "fading") return;
358
+ const fadeTimer = setTimeout(() => {
359
+ setPhase("gone");
360
+ onDismiss?.();
361
+ }, FADE_OUT_MS);
362
+ return () => clearTimeout(fadeTimer);
363
+ }, [phase, onDismiss]);
364
+ if (phase === "gone") return null;
365
+ const handleToggle = (next) => {
366
+ if (next) userExpandedRef.current = true;
367
+ setIsExpanded(next);
368
+ };
369
+ return /* @__PURE__ */ jsx(Collapsible.Root, {
370
+ open: isExpanded,
371
+ onOpenChange: handleToggle,
372
+ children: /* @__PURE__ */ jsxs("div", {
373
+ className: cn("my-0.5 flex w-full max-w-sm flex-col overflow-hidden", "transition-[opacity,max-height,margin] duration-200 ease-out", phase === "live" ? "max-h-40 opacity-100" : "pointer-events-none my-0 max-h-0 opacity-0", phase === "live" && "animate-in fade-in-0 duration-150", className),
374
+ "data-phase": phase,
375
+ ...props,
376
+ children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, {
377
+ render: /* @__PURE__ */ jsx("button", {
378
+ type: "button",
379
+ className: cn("flex w-fit items-center gap-1.5 rounded px-1 py-0.5 text-left", "transition-colors hover:bg-sf-tint")
380
+ }),
381
+ children: [
382
+ /* @__PURE__ */ jsx("span", {
383
+ "aria-label": config.label,
384
+ className: cn("size-1.5 shrink-0 rounded-full", config.dotClass, config.dotPulse && "animate-pulse"),
385
+ role: "status"
386
+ }),
387
+ /* @__PURE__ */ jsx("span", {
388
+ className: cn("min-w-0 truncate text-sm", hasError ? "text-sf-danger" : "text-sf-subtle"),
389
+ children: displayName
390
+ }),
391
+ duration && isComplete && !isExpanded ? /* @__PURE__ */ jsxs("span", {
392
+ className: "text-xs tabular-nums text-sf-subtle/60",
393
+ children: [duration, "ms"]
394
+ }) : null,
395
+ summary && !isExpanded && !errorText ? /* @__PURE__ */ jsx("span", {
396
+ className: "max-w-[120px] truncate text-xs text-sf-subtle/60",
397
+ children: summary
398
+ }) : null,
399
+ /* @__PURE__ */ jsx(CaretDownIcon, { className: cn("size-3 text-sf-subtle/60 transition-transform duration-200", !isExpanded && "-rotate-90") })
400
+ ]
401
+ }), /* @__PURE__ */ jsx(Collapsible.Panel, {
402
+ className: "overflow-hidden",
403
+ children: /* @__PURE__ */ jsxs("div", {
404
+ className: "mt-1 space-y-2 rounded-lg bg-sf-tint/50 px-3 py-2",
405
+ children: [
406
+ part.input && typeof part.input === "object" && Object.keys(part.input).length > 0 ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
407
+ className: "mb-1 text-[10px] uppercase tracking-wider text-sf-subtle",
408
+ children: "Input"
409
+ }), /* @__PURE__ */ jsx("pre", {
410
+ className: "overflow-x-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle",
411
+ children: JSON.stringify(part.input, null, 2)
412
+ })] }) : null,
413
+ hasToolResult(part) && part.output !== void 0 && !hasError ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
414
+ className: "mb-1 text-[10px] uppercase tracking-wider text-sf-subtle",
415
+ children: "Output"
416
+ }), /* @__PURE__ */ jsx("pre", {
417
+ className: "max-h-28 overflow-x-auto overflow-y-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle",
418
+ children: typeof part.output === "string" ? part.output : JSON.stringify(part.output, null, 2)
419
+ })] }) : null,
420
+ errorText ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("p", {
421
+ className: "mb-1 flex items-center gap-1 text-[10px] uppercase tracking-wider text-sf-danger",
422
+ children: [/* @__PURE__ */ jsx(WarningCircleIcon, { className: "size-3" }), "Error"]
423
+ }), /* @__PURE__ */ jsx("pre", {
424
+ className: "overflow-x-auto whitespace-pre-wrap rounded bg-sf-danger/10 p-1.5 font-mono text-xs text-sf-danger",
425
+ children: errorText
426
+ })] }) : null,
427
+ isAwaitingApproval && (onApprove || onReject) && /* @__PURE__ */ jsxs("div", {
428
+ className: "flex items-center justify-end gap-2 border-t border-sf-line/20 pt-2",
429
+ children: [onReject && /* @__PURE__ */ jsx(Button, {
430
+ onClick: onReject,
431
+ size: "sm",
432
+ type: "button",
433
+ variant: "ghost",
434
+ children: "Reject"
435
+ }), onApprove && /* @__PURE__ */ jsx(Button, {
436
+ onClick: onApprove,
437
+ size: "sm",
438
+ type: "button",
439
+ variant: "primary",
440
+ children: "Approve"
441
+ })]
442
+ })
443
+ ]
444
+ })
445
+ })]
446
+ })
447
+ });
448
+ }
292
449
  /**
293
450
  * Collapsible group of tool calls. Shows a summary header with progress
294
451
  * counter and stacked tool icons when collapsed. Renders a single tool
@@ -434,4 +591,4 @@ function AiToolCallTimeline({ parts, durations = {}, className, ...props }) {
434
591
  //#endregion
435
592
  export { SF_AI_TOOL_VARIANTS as a, getToolIcon as c, hasToolResult as d, isToolLoading as f, SF_AI_TOOL_DEFAULT_VARIANTS as i, hasToolError as l, AiToolCallGroup as n, getToolDisplayName as o, AiToolCallTimeline as r, getToolErrorText as s, AiToolCall as t, hasToolOutputError as u };
436
593
 
437
- //# sourceMappingURL=ai-tool-BGH8nQ_D.js.map
594
+ //# sourceMappingURL=ai-tool-Cn1O4xjP.js.map