@signalflare-ai/ui 1.2.0 → 1.4.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 (318) hide show
  1. package/CHANGELOG.md +154 -1
  2. package/ai/component-registry.json +208 -550
  3. package/ai/component-registry.md +3042 -3115
  4. package/ai/schemas.ts +504 -96
  5. package/dist/.build-complete +1 -1
  6. package/dist/ai/schemas.d.ts.map +1 -1
  7. package/dist/{ai-actions-BdUZI3Gk.js → ai-actions-CBfz5XEf.js} +4 -4
  8. package/dist/{ai-actions-BdUZI3Gk.js.map → ai-actions-CBfz5XEf.js.map} +1 -1
  9. package/dist/{ai-agent-card-BR2NIYhi.js → ai-agent-card-CByAUe0q.js} +3 -3
  10. package/dist/ai-agent-card-CByAUe0q.js.map +1 -0
  11. package/dist/{ai-approval-Ba7mrKba.js → ai-approval-Ci8N70a7.js} +4 -3
  12. package/dist/{ai-approval-Ba7mrKba.js.map → ai-approval-Ci8N70a7.js.map} +1 -1
  13. package/dist/{ai-code-block-CZtoL73R.js → ai-code-block-P9TJHvaC.js} +37 -39
  14. package/dist/ai-code-block-P9TJHvaC.js.map +1 -0
  15. package/dist/{ai-conversation-Cc7WlaBg.js → ai-conversation-Qslfdi1t.js} +28 -42
  16. package/dist/ai-conversation-Qslfdi1t.js.map +1 -0
  17. package/dist/{ai-info-banner-C7EWPBj7.js → ai-info-banner-B_9vtGK3.js} +3 -3
  18. package/dist/{ai-info-banner-C7EWPBj7.js.map → ai-info-banner-B_9vtGK3.js.map} +1 -1
  19. package/dist/{ai-message-Bp7L68U_.js → ai-message-Ci3gwM7G.js} +6 -6
  20. package/dist/{ai-message-Bp7L68U_.js.map → ai-message-Ci3gwM7G.js.map} +1 -1
  21. package/dist/{ai-mission-header-TiCJfTNt.js → ai-mission-header-CaBc19-t.js} +2 -2
  22. package/dist/{ai-mission-header-TiCJfTNt.js.map → ai-mission-header-CaBc19-t.js.map} +1 -1
  23. package/dist/{ai-part-group-DNb9I446.js → ai-part-group-Dx1Mr92B.js} +5 -4
  24. package/dist/ai-part-group-Dx1Mr92B.js.map +1 -0
  25. package/dist/{ai-prompt-input-BVvov_KF.js → ai-prompt-input-Bm4XoSj2.js} +19 -17
  26. package/dist/ai-prompt-input-Bm4XoSj2.js.map +1 -0
  27. package/dist/{ai-question-GPPMk7YM.js → ai-question-OyJovxGe.js} +4 -3
  28. package/dist/{ai-question-GPPMk7YM.js.map → ai-question-OyJovxGe.js.map} +1 -1
  29. package/dist/{ai-reasoning-_feFjk56.js → ai-reasoning-BLfBXx3F.js} +9 -5
  30. package/dist/ai-reasoning-BLfBXx3F.js.map +1 -0
  31. package/dist/{ai-response-CvjV3WhV.js → ai-response-hbVCZJmo.js} +2 -2
  32. package/dist/{ai-response-CvjV3WhV.js.map → ai-response-hbVCZJmo.js.map} +1 -1
  33. package/dist/{ai-shimmer-j6lKIrjj.js → ai-shimmer-BamNMNK3.js} +2 -2
  34. package/dist/{ai-shimmer-j6lKIrjj.js.map → ai-shimmer-BamNMNK3.js.map} +1 -1
  35. package/dist/{ai-status-badge-CSU_QOdz.js → ai-status-badge-BZLczdkI.js} +2 -2
  36. package/dist/{ai-status-badge-CSU_QOdz.js.map → ai-status-badge-BZLczdkI.js.map} +1 -1
  37. package/dist/{ai-streaming-text-IWW1BhvZ.js → ai-streaming-text-DgYu64UH.js} +1 -1
  38. package/dist/{ai-streaming-text-IWW1BhvZ.js.map → ai-streaming-text-DgYu64UH.js.map} +1 -1
  39. package/dist/{ai-subagent-JA4iIMW3.js → ai-subagent-p97AI1h9.js} +3 -3
  40. package/dist/{ai-subagent-JA4iIMW3.js.map → ai-subagent-p97AI1h9.js.map} +1 -1
  41. package/dist/{ai-suggestion-BdO6MBuH.js → ai-suggestion-Bj6vF7CT.js} +3 -3
  42. package/dist/{ai-suggestion-BdO6MBuH.js.map → ai-suggestion-Bj6vF7CT.js.map} +1 -1
  43. package/dist/{ai-task-list-DYw4R1FA.js → ai-task-list-C_UQYpk9.js} +6 -4
  44. package/dist/{ai-task-list-DYw4R1FA.js.map → ai-task-list-C_UQYpk9.js.map} +1 -1
  45. package/dist/{ai-timeline-C42tOUT8.js → ai-timeline-CePL1LOU.js} +3 -3
  46. package/dist/ai-timeline-CePL1LOU.js.map +1 -0
  47. package/dist/{ai-tool-03jOTwUI.js → ai-tool-CfRcwmHT.js} +17 -11
  48. package/dist/ai-tool-CfRcwmHT.js.map +1 -0
  49. package/dist/{ai-usage-bar-BRf5LC_b.js → ai-usage-bar-45pVRCGA.js} +2 -2
  50. package/dist/{ai-usage-bar-BRf5LC_b.js.map → ai-usage-bar-45pVRCGA.js.map} +1 -1
  51. package/dist/{badge-BheXjMc8.js → badge-Beb-6uut.js} +5 -5
  52. package/dist/{badge-BheXjMc8.js.map → badge-Beb-6uut.js.map} +1 -1
  53. package/dist/{banner-CcsjunJg.js → banner-CCEksxPg.js} +3 -3
  54. package/dist/{banner-CcsjunJg.js.map → banner-CCEksxPg.js.map} +1 -1
  55. package/dist/{breadcrumbs-CouSyy3H.js → breadcrumbs-HiTmgaZ4.js} +5 -5
  56. package/dist/{breadcrumbs-CouSyy3H.js.map → breadcrumbs-HiTmgaZ4.js.map} +1 -1
  57. package/dist/{button-CO6-qPax.js → button-BHOgXJRU.js} +4 -4
  58. package/dist/{button-CO6-qPax.js.map → button-BHOgXJRU.js.map} +1 -1
  59. package/dist/catalog.js +1 -1
  60. package/dist/catalog.js.map +1 -1
  61. package/dist/{chart-Dg0qUeSc.js → chart-B9FfZdKs.js} +7 -7
  62. package/dist/chart-B9FfZdKs.js.map +1 -0
  63. package/dist/{checkbox-D7p4QKsC.js → checkbox-Cy_OCyay.js} +3 -3
  64. package/dist/{checkbox-D7p4QKsC.js.map → checkbox-Cy_OCyay.js.map} +1 -1
  65. package/dist/{clipboard-text-kLaMogs3.js → clipboard-text-CKSvNp9L.js} +6 -5
  66. package/dist/clipboard-text-CKSvNp9L.js.map +1 -0
  67. package/dist/{cn-YROP2_ox.js → cn-CmAOpn49.js} +2 -2
  68. package/dist/{cn-YROP2_ox.js.map → cn-CmAOpn49.js.map} +1 -1
  69. package/dist/{code-BN8InC0G.js → code-JsQz-0G_.js} +4 -4
  70. package/dist/{code-BN8InC0G.js.map → code-JsQz-0G_.js.map} +1 -1
  71. package/dist/{collapsible-D_ueZ0jz.js → collapsible-1kOZ-89L.js} +2 -2
  72. package/dist/{collapsible-D_ueZ0jz.js.map → collapsible-1kOZ-89L.js.map} +1 -1
  73. package/dist/{combobox-B7TOK0U2.js → combobox-CQwDmqgA.js} +4 -4
  74. package/dist/{combobox-B7TOK0U2.js.map → combobox-CQwDmqgA.js.map} +1 -1
  75. package/dist/command-line/cli.js +3 -3
  76. package/dist/{command-palette-CuNUyJca.js → command-palette-Bkuv3e6o.js} +20 -5
  77. package/dist/command-palette-Bkuv3e6o.js.map +1 -0
  78. package/dist/components/ai-actions.js +1 -1
  79. package/dist/components/ai-agent-card.js +1 -1
  80. package/dist/components/ai-approval.js +1 -1
  81. package/dist/components/ai-code-block.js +1 -1
  82. package/dist/components/ai-conversation.js +1 -1
  83. package/dist/components/ai-info-banner.js +1 -1
  84. package/dist/components/ai-message.js +1 -1
  85. package/dist/components/ai-mission-header.js +1 -1
  86. package/dist/components/ai-part-group.js +1 -1
  87. package/dist/components/ai-prompt-input.js +1 -1
  88. package/dist/components/ai-question.js +1 -1
  89. package/dist/components/ai-reasoning.js +1 -1
  90. package/dist/components/ai-response.js +1 -1
  91. package/dist/components/ai-shimmer.js +1 -1
  92. package/dist/components/ai-status-badge.js +1 -1
  93. package/dist/components/ai-streaming-text.js +1 -1
  94. package/dist/components/ai-subagent.js +1 -1
  95. package/dist/components/ai-suggestion.js +1 -1
  96. package/dist/components/ai-task-list.js +1 -1
  97. package/dist/components/ai-timeline.js +1 -1
  98. package/dist/components/ai-tool.js +1 -1
  99. package/dist/components/ai-usage-bar.js +1 -1
  100. package/dist/components/badge.js +1 -1
  101. package/dist/components/banner.js +1 -1
  102. package/dist/components/breadcrumbs.js +1 -1
  103. package/dist/components/button.js +1 -1
  104. package/dist/components/chart.js +2 -2
  105. package/dist/components/checkbox.js +1 -1
  106. package/dist/components/clipboard-text.js +1 -1
  107. package/dist/components/code.js +1 -1
  108. package/dist/components/collapsible.js +1 -1
  109. package/dist/components/combobox.js +1 -1
  110. package/dist/components/command-palette.js +1 -1
  111. package/dist/components/data-grid.js +1 -1
  112. package/dist/components/date-picker.js +1 -1
  113. package/dist/components/date-range-picker.js +1 -1
  114. package/dist/components/dialog.js +1 -1
  115. package/dist/components/dropdown.js +1 -1
  116. package/dist/components/empty.js +1 -1
  117. package/dist/components/field.js +1 -1
  118. package/dist/components/filters.js +1 -1
  119. package/dist/components/flow.js +1 -1
  120. package/dist/components/grid.js +1 -1
  121. package/dist/components/input.js +2 -2
  122. package/dist/components/label.js +1 -1
  123. package/dist/components/layer-card.js +1 -1
  124. package/dist/components/link.js +3 -3
  125. package/dist/components/link.js.map +1 -1
  126. package/dist/components/loader.js +2 -2
  127. package/dist/components/menubar.js +1 -1
  128. package/dist/components/meter.js +1 -1
  129. package/dist/components/pagination.js +1 -1
  130. package/dist/components/popover.js +1 -1
  131. package/dist/components/radio.js +1 -1
  132. package/dist/components/select.js +1 -1
  133. package/dist/components/sensitive-input.js +1 -1
  134. package/dist/components/sidebar.js +1 -1
  135. package/dist/components/signalflare-ai-logo.js +1 -1
  136. package/dist/components/sparkline.js +1 -1
  137. package/dist/components/stat-card.js +1 -1
  138. package/dist/components/surface.js +1 -1
  139. package/dist/components/switch.js +1 -1
  140. package/dist/components/table.js +1 -1
  141. package/dist/components/tabs.js +1 -1
  142. package/dist/components/text-roll.js +1 -1
  143. package/dist/components/text.js +1 -1
  144. package/dist/components/theme-toggle.js +1 -1
  145. package/dist/components/toast.js +1 -1
  146. package/dist/components/tooltip.js +1 -1
  147. package/dist/components/use-agent-harness.js +1 -1
  148. package/dist/data-grid-DrguJq_H.js +395 -0
  149. package/dist/data-grid-DrguJq_H.js.map +1 -0
  150. package/dist/{date-picker--ox89RBy.js → date-picker-O34AqG3f.js} +2 -2
  151. package/dist/{date-picker--ox89RBy.js.map → date-picker-O34AqG3f.js.map} +1 -1
  152. package/dist/{date-range-picker-DVa7QBqE.js → date-range-picker-YKYvum_r.js} +29 -39
  153. package/dist/{date-range-picker-DVa7QBqE.js.map → date-range-picker-YKYvum_r.js.map} +1 -1
  154. package/dist/{dialog-Bv1oSFOd.js → dialog-DYqu4aDO.js} +3 -3
  155. package/dist/{dialog-Bv1oSFOd.js.map → dialog-DYqu4aDO.js.map} +1 -1
  156. package/dist/{dist-B6iWiWwp.js → dist-6AtBsaJE.js} +153 -47
  157. package/dist/dist-6AtBsaJE.js.map +1 -0
  158. package/dist/{dropdown-B_nrGXjV.js → dropdown-XzbnRLYR.js} +15 -5
  159. package/dist/dropdown-XzbnRLYR.js.map +1 -0
  160. package/dist/{echart-CdOUaT-r.js → echart-DGBIVAv1.js} +23 -57
  161. package/dist/{echart-CdOUaT-r.js.map → echart-DGBIVAv1.js.map} +1 -1
  162. package/dist/{empty-DZnN0zKX.js → empty-C1tAkawe.js} +6 -6
  163. package/dist/{empty-DZnN0zKX.js.map → empty-C1tAkawe.js.map} +1 -1
  164. package/dist/{field-B_yVof52.js → field-DBpFzzBS.js} +3 -3
  165. package/dist/{field-B_yVof52.js.map → field-DBpFzzBS.js.map} +1 -1
  166. package/dist/{filters-cpJCY21R.js → filters-SmEl93za.js} +10 -10
  167. package/dist/filters-SmEl93za.js.map +1 -0
  168. package/dist/{flow-B4v198ot.js → flow-BLzgbq1T.js} +6 -6
  169. package/dist/flow-BLzgbq1T.js.map +1 -0
  170. package/dist/genui.js +2 -2
  171. package/dist/genui.js.map +1 -1
  172. package/dist/{grid-CEd64Lnh.js → grid-CifjQL-5.js} +2 -2
  173. package/dist/{grid-CEd64Lnh.js.map → grid-CifjQL-5.js.map} +1 -1
  174. package/dist/{highlight-to-react-D0Yav4jk.js → highlight-to-react-DN9dUCS2.js} +9 -15
  175. package/dist/highlight-to-react-DN9dUCS2.js.map +1 -0
  176. package/dist/index.js +71 -71
  177. package/dist/index.js.map +1 -1
  178. package/dist/{input-ClB_E4Lb.js → input-COmx2M_R.js} +5 -5
  179. package/dist/{input-ClB_E4Lb.js.map → input-COmx2M_R.js.map} +1 -1
  180. package/dist/{input-B2bbijRh.js → input-GkfMQZC_.js} +3 -3
  181. package/dist/{input-B2bbijRh.js.map → input-GkfMQZC_.js.map} +1 -1
  182. package/dist/{label-DUv_urO1.js → label-CiGZ464N.js} +3 -3
  183. package/dist/{label-DUv_urO1.js.map → label-CiGZ464N.js.map} +1 -1
  184. package/dist/{layer-card-BK7eYfwn.js → layer-card-8l8GuLQr.js} +2 -2
  185. package/dist/{layer-card-BK7eYfwn.js.map → layer-card-8l8GuLQr.js.map} +1 -1
  186. package/dist/{layout-DJHMMap2.js → layout-CWBE0qwx.js} +258 -154
  187. package/dist/layout-CWBE0qwx.js.map +1 -0
  188. package/dist/{link-provider-BUZKXaNE.js → link-provider-BSn8YJon.js} +2 -2
  189. package/dist/link-provider-BSn8YJon.js.map +1 -0
  190. package/dist/{loader-DAcc-Uag.js → loader-BEMz8pJO.js} +1 -1
  191. package/dist/{loader-DAcc-Uag.js.map → loader-BEMz8pJO.js.map} +1 -1
  192. package/dist/{measured-text-BI3dTJmH.js → measured-text-CXkdw9Yr.js} +45 -30
  193. package/dist/measured-text-CXkdw9Yr.js.map +1 -0
  194. package/dist/{menubar-Cxf3xeAt.js → menubar-CoOr4ocj.js} +3 -3
  195. package/dist/{menubar-Cxf3xeAt.js.map → menubar-CoOr4ocj.js.map} +1 -1
  196. package/dist/{meter-BFFe9l5b.js → meter-Pf_VOl59.js} +2 -2
  197. package/dist/{meter-BFFe9l5b.js.map → meter-Pf_VOl59.js.map} +1 -1
  198. package/dist/{pagination-yS372Tr4.js → pagination-DSY279Ta.js} +2 -2
  199. package/dist/{pagination-yS372Tr4.js.map → pagination-DSY279Ta.js.map} +1 -1
  200. package/dist/{popover-SRoJaCZr.js → popover-BY-e9co1.js} +2 -2
  201. package/dist/{popover-SRoJaCZr.js.map → popover-BY-e9co1.js.map} +1 -1
  202. package/dist/{radio-BcwhwYNB.js → radio-DZwL13j0.js} +2 -2
  203. package/dist/{radio-BcwhwYNB.js.map → radio-DZwL13j0.js.map} +1 -1
  204. package/dist/{select-DMhdoHMa.js → select-BFifYqHA.js} +6 -6
  205. package/dist/{select-DMhdoHMa.js.map → select-BFifYqHA.js.map} +1 -1
  206. package/dist/{sensitive-input-CJUpIRal.js → sensitive-input-DHLZcM73.js} +4 -4
  207. package/dist/{sensitive-input-CJUpIRal.js.map → sensitive-input-DHLZcM73.js.map} +1 -1
  208. package/dist/{sidebar-D4zrlYpn.js → sidebar-odGsdvG4.js} +6 -7
  209. package/dist/sidebar-odGsdvG4.js.map +1 -0
  210. package/dist/{signalflare-ai-logo-Bipogceq.js → signalflare-ai-logo-CNaDT_w8.js} +2 -2
  211. package/dist/{signalflare-ai-logo-Bipogceq.js.map → signalflare-ai-logo-CNaDT_w8.js.map} +1 -1
  212. package/dist/{skeleton-line-CH1-h6e2.js → skeleton-line-CxxYVTO2.js} +2 -2
  213. package/dist/{skeleton-line-CH1-h6e2.js.map → skeleton-line-CxxYVTO2.js.map} +1 -1
  214. package/dist/{sparkline-DHmgj1d0.js → sparkline-BQ-4j2W2.js} +2 -2
  215. package/dist/{sparkline-DHmgj1d0.js.map → sparkline-BQ-4j2W2.js.map} +1 -1
  216. package/dist/src/blocks/agent-harness/agent-harness.tsx +11 -11
  217. package/dist/src/blocks/commander/commander.tsx +15 -15
  218. package/dist/src/blocks/map-block/map-block.d.ts.map +1 -1
  219. package/dist/src/blocks/map-block/map-block.tsx +11 -7
  220. package/dist/src/components/ai-approval/ai-approval.d.ts.map +1 -1
  221. package/dist/src/components/ai-code-block/ai-code-block.d.ts +14 -13
  222. package/dist/src/components/ai-code-block/ai-code-block.d.ts.map +1 -1
  223. package/dist/src/components/ai-conversation/ai-conversation.d.ts.map +1 -1
  224. package/dist/src/components/ai-part-group/ai-part-group.d.ts.map +1 -1
  225. package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
  226. package/dist/src/components/ai-prompt-input/controller.d.ts.map +1 -1
  227. package/dist/src/components/ai-prompt-input/types.d.ts.map +1 -1
  228. package/dist/src/components/ai-question/ai-question.d.ts.map +1 -1
  229. package/dist/src/components/ai-reasoning/ai-reasoning.d.ts.map +1 -1
  230. package/dist/src/components/ai-response/ai-response.d.ts.map +1 -1
  231. package/dist/src/components/ai-subagent/ai-subagent.d.ts.map +1 -1
  232. package/dist/src/components/ai-tool/ai-tool.d.ts.map +1 -1
  233. package/dist/src/components/chart/echart.d.ts.map +1 -1
  234. package/dist/src/components/clipboard-text/clipboard-text.d.ts.map +1 -1
  235. package/dist/src/components/data-grid/data-grid.d.ts +2 -1
  236. package/dist/src/components/data-grid/data-grid.d.ts.map +1 -1
  237. package/dist/src/components/data-grid/features.d.ts +20 -0
  238. package/dist/src/components/data-grid/features.d.ts.map +1 -0
  239. package/dist/src/components/data-grid/types.d.ts +38 -7
  240. package/dist/src/components/data-grid/types.d.ts.map +1 -1
  241. package/dist/src/components/filters/filters.d.ts.map +1 -1
  242. package/dist/src/components/flow/use-children.d.ts +1 -1
  243. package/dist/src/components/link/link.d.ts.map +1 -1
  244. package/dist/src/components/sidebar/sidebar.d.ts +1 -1
  245. package/dist/src/components/signalflare-ai-logo/signalflare-ai-logo.d.ts.map +1 -1
  246. package/dist/src/components/text/text.d.ts +2 -1
  247. package/dist/src/components/text/text.d.ts.map +1 -1
  248. package/dist/src/components/text-roll/text-roll.d.ts.map +1 -1
  249. package/dist/src/components/theme-toggle/theme-toggle.d.ts.map +1 -1
  250. package/dist/src/components/toast/toast.d.ts.map +1 -1
  251. package/dist/src/utils/highlight-to-react.d.ts.map +1 -1
  252. package/dist/src/utils/measured-text.d.ts.map +1 -1
  253. package/dist/src/utils/use-measured-text.d.ts.map +1 -1
  254. package/dist/{stat-card-Ew-ofzEm.js → stat-card-Bspk4XFr.js} +4 -4
  255. package/dist/stat-card-Bspk4XFr.js.map +1 -0
  256. package/dist/styles/sf-standalone.css +1 -1
  257. package/dist/styles/theme-fedramp.css +3 -12
  258. package/dist/styles/theme-minimal.css +26 -104
  259. package/dist/styles/theme-sf.css +37 -142
  260. package/dist/{surface-DGwRlC0o.js → surface-CWdSFVUx.js} +3 -3
  261. package/dist/{surface-DGwRlC0o.js.map → surface-CWdSFVUx.js.map} +1 -1
  262. package/dist/{switch-BxAMfHdt.js → switch-TA4cByCJ.js} +5 -5
  263. package/dist/switch-TA4cByCJ.js.map +1 -0
  264. package/dist/{table-BBeAtYVZ.js → table-BM8JBGBs.js} +3 -3
  265. package/dist/{table-BBeAtYVZ.js.map → table-BM8JBGBs.js.map} +1 -1
  266. package/dist/{tabs-CeHu7Scn.js → tabs-bnH2vGLv.js} +2 -2
  267. package/dist/{tabs-CeHu7Scn.js.map → tabs-bnH2vGLv.js.map} +1 -1
  268. package/dist/{text-Cqryz7rk.js → text-iQ0YUFNg.js} +4 -5
  269. package/dist/{text-Cqryz7rk.js.map → text-iQ0YUFNg.js.map} +1 -1
  270. package/dist/{text-roll-Ch52hcQj.js → text-roll-C3U2jd2u.js} +5 -2
  271. package/dist/text-roll-C3U2jd2u.js.map +1 -0
  272. package/dist/{theme-toggle-LDfIKEqx.js → theme-toggle-BTVxD-fD.js} +10 -9
  273. package/dist/theme-toggle-BTVxD-fD.js.map +1 -0
  274. package/dist/{toast-CaFQNYng.js → toast-CgZVaAkw.js} +3 -3
  275. package/dist/{toast-CaFQNYng.js.map → toast-CgZVaAkw.js.map} +1 -1
  276. package/dist/{tooltip-g9lFsvcT.js → tooltip-uobk6Oh-.js} +3 -3
  277. package/dist/{tooltip-g9lFsvcT.js.map → tooltip-uobk6Oh-.js.map} +1 -1
  278. package/dist/{use-agent-harness-BTcNJdw4.js → use-agent-harness-Dl8w6X5O.js} +3 -3
  279. package/dist/{use-agent-harness-BTcNJdw4.js.map → use-agent-harness-Dl8w6X5O.js.map} +1 -1
  280. package/dist/utils.js +3 -3
  281. package/package.json +27 -25
  282. package/scripts/component-registry/discovery.ts +11 -10
  283. package/scripts/component-registry/example-cleanup.ts +8 -8
  284. package/scripts/component-registry/index.ts +6 -6
  285. package/scripts/component-registry/schema-generator.ts +1 -1
  286. package/scripts/component-registry/sub-components.ts +35 -23
  287. package/scripts/component-registry/utils.ts +11 -11
  288. package/scripts/component-registry/variant-parser.ts +17 -15
  289. package/scripts/convert-demos-to-stories.ts +5 -5
  290. package/scripts/theme-generator/config.ts +1 -5
  291. package/scripts/theme-generator/generate-css.ts +1 -1
  292. package/scripts/theme-generator/migrate.ts +3 -3
  293. package/dist/ai-agent-card-BR2NIYhi.js.map +0 -1
  294. package/dist/ai-code-block-CZtoL73R.js.map +0 -1
  295. package/dist/ai-conversation-Cc7WlaBg.js.map +0 -1
  296. package/dist/ai-part-group-DNb9I446.js.map +0 -1
  297. package/dist/ai-prompt-input-BVvov_KF.js.map +0 -1
  298. package/dist/ai-reasoning-_feFjk56.js.map +0 -1
  299. package/dist/ai-timeline-C42tOUT8.js.map +0 -1
  300. package/dist/ai-tool-03jOTwUI.js.map +0 -1
  301. package/dist/chart-Dg0qUeSc.js.map +0 -1
  302. package/dist/clipboard-text-kLaMogs3.js.map +0 -1
  303. package/dist/command-palette-CuNUyJca.js.map +0 -1
  304. package/dist/data-grid-DGHmU0w3.js +0 -305
  305. package/dist/data-grid-DGHmU0w3.js.map +0 -1
  306. package/dist/dist-B6iWiWwp.js.map +0 -1
  307. package/dist/dropdown-B_nrGXjV.js.map +0 -1
  308. package/dist/filters-cpJCY21R.js.map +0 -1
  309. package/dist/flow-B4v198ot.js.map +0 -1
  310. package/dist/highlight-to-react-D0Yav4jk.js.map +0 -1
  311. package/dist/layout-DJHMMap2.js.map +0 -1
  312. package/dist/link-provider-BUZKXaNE.js.map +0 -1
  313. package/dist/measured-text-BI3dTJmH.js.map +0 -1
  314. package/dist/sidebar-D4zrlYpn.js.map +0 -1
  315. package/dist/stat-card-Ew-ofzEm.js.map +0 -1
  316. package/dist/switch-BxAMfHdt.js.map +0 -1
  317. package/dist/text-roll-Ch52hcQj.js.map +0 -1
  318. package/dist/theme-toggle-LDfIKEqx.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ai-response-CvjV3WhV.js","names":[],"sources":["../src/components/ai-response/ai-response.tsx"],"sourcesContent":["\"use client\";\n\nimport { code } from \"@streamdown/code\";\nimport { memo } from \"react\";\nimport { Streamdown, type StreamdownProps } from \"streamdown\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_RESPONSE_VARIANTS = {} as const;\nexport const SF_AI_RESPONSE_DEFAULT_VARIANTS = {} as const;\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nconst plugins = { code };\n\nexport type AiResponseProps = StreamdownProps & {\n /** Additional CSS classes. */\n className?: string;\n /**\n * For streaming: reserve height based on the full (target) text to prevent\n * layout shift as content streams in. Pass the complete expected text here\n * while streaming; the actual displayed children can be the partial stream.\n */\n reserveHeightFor?: string;\n};\n\n/**\n * Renders AI-generated markdown using Streamdown.\n * Supports streaming animation, syntax-highlighted code, tables, and more.\n *\n * Pass `isAnimating={true}` while streaming and `false` once complete to get\n * character-by-character reveal animation.\n *\n * @example\n * ```tsx\n * // Static\n * <AiResponse>{\"# Hello\\n\\nThis is **bold** and _italic_.\"}</AiResponse>\n *\n * // Streaming\n * <AiResponse isAnimating={isStreaming}>{streamedText}</AiResponse>\n *\n * // Streaming with height reserve (prevents layout shift)\n * <AiResponse isAnimating={isStreaming} reserveHeightFor={fullTargetText}>\n * {streamedText}\n * </AiResponse>\n * ```\n */\nexport const AiResponse = memo(\n ({ className, children, reserveHeightFor: _reserveHeightFor, ...props }: AiResponseProps) => {\n return (\n <Streamdown\n className={cn(\n \"w-full text-sf-default text-sm\",\n \"[&>*:first-child]:mt-0 [&>*:last-child]:mb-0\",\n // Bridge shadcn tokens used by @streamdown/code to sf tokens so\n // code blocks render with visible borders/backgrounds/text.\n \"[--border:var(--color-sf-line)] [--sidebar:var(--color-sf-elevated)]\",\n \"[--background:var(--color-sf-recessed)] [--foreground:var(--text-color-sf-default)]\",\n \"[--muted-foreground:var(--text-color-sf-subtle)]\",\n className\n )}\n plugins={plugins}\n {...props}\n >\n {children}\n </Streamdown>\n );\n },\n (prev, next) =>\n prev.children === next.children &&\n prev.isAnimating === next.isAnimating &&\n prev.reserveHeightFor === next.reserveHeightFor\n);\n\nAiResponse.displayName = \"AiResponse\";\n"],"mappings":";;;;;;;AAUA,IAAa,0BAA0B,EAAE;AACzC,IAAa,kCAAkC,EAAE;AAIjD,IAAM,UAAU,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;AAkCxB,IAAa,aAAa,MACvB,EAAE,WAAW,UAAU,kBAAkB,mBAAmB,GAAG,YAA6B;AAC3F,QACE,oBAAC,YAAD;EACE,WAAW,GACT,kCACA,gDAGA,wEACA,uFACA,oDACA,UACD;EACQ;EACT,GAAI;EAEH;EACU,CAAA;IAGhB,MAAM,SACL,KAAK,aAAa,KAAK,YACvB,KAAK,gBAAgB,KAAK,eAC1B,KAAK,qBAAqB,KAAK,iBAClC;AAED,WAAW,cAAc"}
1
+ {"version":3,"file":"ai-response-hbVCZJmo.js","names":[],"sources":["../src/components/ai-response/ai-response.tsx"],"sourcesContent":["\"use client\";\n\nimport { code } from \"@streamdown/code\";\nimport { memo } from \"react\";\nimport { Streamdown, type StreamdownProps } from \"streamdown\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_RESPONSE_VARIANTS = {} as const;\nexport const SF_AI_RESPONSE_DEFAULT_VARIANTS = {} as const;\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nconst plugins = { code };\n\nexport type AiResponseProps = StreamdownProps & {\n /** Additional CSS classes. */\n className?: string;\n /**\n * For streaming: reserve height based on the full (target) text to prevent\n * layout shift as content streams in. Pass the complete expected text here\n * while streaming; the actual displayed children can be the partial stream.\n */\n reserveHeightFor?: string;\n};\n\n/**\n * Renders AI-generated markdown using Streamdown.\n * Supports streaming animation, syntax-highlighted code, tables, and more.\n *\n * Pass `isAnimating={true}` while streaming and `false` once complete to get\n * character-by-character reveal animation.\n *\n * @example\n * ```tsx\n * // Static\n * <AiResponse>{\"# Hello\\n\\nThis is **bold** and _italic_.\"}</AiResponse>\n *\n * // Streaming\n * <AiResponse isAnimating={isStreaming}>{streamedText}</AiResponse>\n *\n * // Streaming with height reserve (prevents layout shift)\n * <AiResponse isAnimating={isStreaming} reserveHeightFor={fullTargetText}>\n * {streamedText}\n * </AiResponse>\n * ```\n */\nexport const AiResponse = memo(\n ({\n className,\n children,\n reserveHeightFor: _reserveHeightFor,\n ...props\n }: AiResponseProps) => {\n return (\n <Streamdown\n className={cn(\n \"w-full text-sf-default text-sm\",\n \"[&>*:first-child]:mt-0 [&>*:last-child]:mb-0\",\n // Bridge shadcn tokens used by @streamdown/code to sf tokens so\n // code blocks render with visible borders/backgrounds/text.\n \"[--border:var(--color-sf-line)] [--sidebar:var(--color-sf-elevated)]\",\n \"[--background:var(--color-sf-recessed)] [--foreground:var(--text-color-sf-default)]\",\n \"[--muted-foreground:var(--text-color-sf-subtle)]\",\n className\n )}\n plugins={plugins}\n {...props}\n >\n {children}\n </Streamdown>\n );\n },\n (prev, next) =>\n prev.children === next.children &&\n prev.isAnimating === next.isAnimating &&\n prev.reserveHeightFor === next.reserveHeightFor\n);\n\nAiResponse.displayName = \"AiResponse\";\n"],"mappings":";;;;;;;AAUA,IAAa,0BAA0B,CAAC;AACxC,IAAa,kCAAkC,CAAC;AAIhD,IAAM,UAAU,EAAE,KAAK;;;;;;;;;;;;;;;;;;;;;;AAkCvB,IAAa,aAAa,MACvB,EACC,WACA,UACA,kBAAkB,mBAClB,GAAG,YACkB;CACrB,OACE,oBAAC,YAAD;EACE,WAAW,GACT,kCACA,gDAGA,wEACA,uFACA,oDACA,SACF;EACS;EACT,GAAI;EAEH;CACS,CAAA;AAEhB,IACC,MAAM,SACL,KAAK,aAAa,KAAK,YACvB,KAAK,gBAAgB,KAAK,eAC1B,KAAK,qBAAqB,KAAK,gBACnC;AAEA,WAAW,cAAc"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
3
  import { memo, useMemo } from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import { motion } from "motion/react";
@@ -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-j6lKIrjj.js.map
43
+ //# sourceMappingURL=ai-shimmer-BamNMNK3.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-shimmer-j6lKIrjj.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-BamNMNK3.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,CAAC;AACvC,IAAa,iCAAiC,CAAC;;;;;;;;;;;AA2B/C,SAAS,cAAc,EACrB,UACA,IAAI,YAAY,KAChB,WACA,WAAW,GACX,SAAS,KACQ;CACjB,MAAM,kBAAkB,OAAO,OAC7B,SACF;CACA,MAAM,gBAAgB,eACb,UAAU,UAAU,KAAK,QAChC,CAAC,UAAU,MAAM,CACnB;CAEA,OACE,oBAAC,iBAAD;EACE,SAAS,EAAE,oBAAoB,YAAY;EAC3C,WAAW,GACT,kFACA,4JACA,SACF;EACA,SAAS,EAAE,oBAAoB,cAAc;EAC7C,OACE;GACE,YAAY,GAAG,cAAc;GAC7B,iBACE;EACJ;EAEF,YAAY;GACV,QAAQ,OAAO;GACf;GACA,MAAM;EACR;EAEC;CACc,CAAA;AAErB;AAEA,IAAa,YAAY,KAAK,aAAa;AAC3C,UAAU,cAAc"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  import { CheckIcon, SpinnerGapIcon, XIcon } from "@phosphor-icons/react";
5
5
  //#region src/components/ai-status-badge/ai-status-badge.tsx
@@ -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-CSU_QOdz.js.map
63
+ //# sourceMappingURL=ai-status-badge-BZLczdkI.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-status-badge-CSU_QOdz.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-BZLczdkI.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;CACf;CACA,SAAS;EACP,SAAS;EACT,aAAa;CACf;CACA,SAAS;EACP,SAAS;EACT,aAAa;CACf;CACA,OAAO;EACL,SAAS;EACT,aAAa;CACf;AACF,EACF;AAEA,IAAa,sCAAsC,EACjD,QAAQ,OACV;AAoBA,SAAS,WAAW,EAAE,UAA2C;CAC/D,QAAQ,QAAR;EACE,KAAK,WACH,OAAO,oBAAC,gBAAD,EAAgB,WAAU,oCAAqC,CAAA;EACxE,KAAK,WACH,OAAO,oBAAC,WAAD,EAAW,WAAU,yBAA0B,CAAA;EACxD,KAAK,SACH,OAAO,oBAAC,OAAD,EAAO,WAAU,wBAAyB,CAAA;EACnD,SACE,OAAO;CACX;AACF;;;;;;;;;;AAaA,SAAgB,cAAc,EAC5B,MAAM,MACN,OACA,SAAS,QACT,MACA,WACA,GAAG,SACkB;CACrB,OACE,qBAAC,OAAD;EACE,WAAW,GACT,uGACA,4DACA,SACF;EACA,GAAI;YANN;GAQG,OAAO,oBAAC,MAAD,EAAM,WAAU,wBAAyB,CAAA,IAAI;GACrD,oBAAC,QAAD;IAAM,WAAU;cAAkB;GAAY,CAAA;GAC7C,OAAO,oBAAC,QAAD;IAAM,WAAU;cAAqB;GAAW,CAAA,IAAI;GAC5D,oBAAC,YAAD,EAAoB,OAAS,CAAA;EAC1B;;AAET"}
@@ -106,4 +106,4 @@ function useAiStreamingTextWithMeta(text, options = {}) {
106
106
  //#endregion
107
107
  export { useAiStreamingTextWithMeta as i, SF_AI_STREAMING_TEXT_VARIANTS as n, useAiStreamingText as r, SF_AI_STREAMING_TEXT_DEFAULT_VARIANTS as t };
108
108
 
109
- //# sourceMappingURL=ai-streaming-text-IWW1BhvZ.js.map
109
+ //# sourceMappingURL=ai-streaming-text-DgYu64UH.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-streaming-text-IWW1BhvZ.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\nexport interface UseAiStreamingTextMeta {\n /** The display-ready substring for character-by-character reveal. */\n displayText: string;\n /** The full target text (useful for reserve height calculations). */\n targetText: string;\n /** Whether streaming is currently active. */\n isStreaming: boolean;\n}\n\n// ─── Internal core hook ──────────────────────────────────────────────────────\n\nfunction useStreamingTextCore(\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\n// ─── Hooks ───────────────────────────────────────────────────────────────────\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 return useStreamingTextCore(text, options);\n}\n\n/**\n * Same as `useAiStreamingText` but returns an object with both the display\n * substring and the full target text. Useful for reserve-height layout where\n * you want surrounding UI to allocate space for the full target while content\n * streams in.\n *\n * @example\n * ```tsx\n * function StreamingMessage({ text, isStreaming }) {\n * const { displayText, targetText } = useAiStreamingTextWithMeta(text, { isStreaming });\n * return (\n * <AiResponse reserveHeightFor={targetText}>\n * {displayText}\n * </AiResponse>\n * );\n * }\n * ```\n */\nexport function useAiStreamingTextWithMeta(\n text: string,\n options: UseAiStreamingTextOptions = {}\n): UseAiStreamingTextMeta {\n const displayText = useStreamingTextCore(text, options);\n return {\n displayText,\n targetText: text,\n isStreaming: options.isStreaming ?? false,\n };\n}\n"],"mappings":";;;AAMA,IAAa,gCAAgC,EAAE;AAC/C,IAAa,wCAAwC,EAAE;AA0BvD,SAAS,qBACP,MACA,SACQ;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;;;;;;;;;;;;;;;AAkBT,SAAgB,mBACd,MACA,UAAqC,EAAE,EAC/B;AACR,QAAO,qBAAqB,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;AAqB5C,SAAgB,2BACd,MACA,UAAqC,EAAE,EACf;AAExB,QAAO;EACL,aAFkB,qBAAqB,MAAM,QAAQ;EAGrD,YAAY;EACZ,aAAa,QAAQ,eAAe;EACrC"}
1
+ {"version":3,"file":"ai-streaming-text-DgYu64UH.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\nexport interface UseAiStreamingTextMeta {\n /** The display-ready substring for character-by-character reveal. */\n displayText: string;\n /** The full target text (useful for reserve height calculations). */\n targetText: string;\n /** Whether streaming is currently active. */\n isStreaming: boolean;\n}\n\n// ─── Internal core hook ──────────────────────────────────────────────────────\n\nfunction useStreamingTextCore(\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\n// ─── Hooks ───────────────────────────────────────────────────────────────────\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 return useStreamingTextCore(text, options);\n}\n\n/**\n * Same as `useAiStreamingText` but returns an object with both the display\n * substring and the full target text. Useful for reserve-height layout where\n * you want surrounding UI to allocate space for the full target while content\n * streams in.\n *\n * @example\n * ```tsx\n * function StreamingMessage({ text, isStreaming }) {\n * const { displayText, targetText } = useAiStreamingTextWithMeta(text, { isStreaming });\n * return (\n * <AiResponse reserveHeightFor={targetText}>\n * {displayText}\n * </AiResponse>\n * );\n * }\n * ```\n */\nexport function useAiStreamingTextWithMeta(\n text: string,\n options: UseAiStreamingTextOptions = {}\n): UseAiStreamingTextMeta {\n const displayText = useStreamingTextCore(text, options);\n return {\n displayText,\n targetText: text,\n isStreaming: options.isStreaming ?? false,\n };\n}\n"],"mappings":";;;AAMA,IAAa,gCAAgC,CAAC;AAC9C,IAAa,wCAAwC,CAAC;AA0BtD,SAAS,qBACP,MACA,SACQ;CACR,MAAM,EACJ,cAAc,OACd,eAAe,IACf,eAAe,IACf,cAAc,SACZ;CAEJ,MAAM,CAAC,aAAa,kBAAkB,SAAS,IAAI;CACnD,MAAM,YAAY,OAAO,IAAI;CAC7B,MAAM,SAAS,OAAO,KAAK,MAAM;CACjC,MAAM,SAAS,OAAsB,IAAI;CACzC,MAAM,cAAc,OAAO,CAAC;CAE5B,gBAAgB;EACd,UAAU,UAAU;CACtB,GAAG,CAAC,IAAI,CAAC;CAET,gBAAgB;EACd,IAAI,CAAC,aAAa;GAChB,IAAI,aAAa;IACf,eAAe,IAAI;IACnB,OAAO,UAAU,KAAK;GACxB;GACA;EACF;EAEA,MAAM,WAAW,cAAsB;GACrC,MAAM,SAAS,UAAU;GACzB,MAAM,MAAM,OAAO;GAEnB,IAAI,OAAO,OAAO,QAAQ;IACxB,OAAO,UAAU,sBAAsB,OAAO;IAC9C;GACF;GAEA,IAAI,YAAY,YAAY,UAAU,cAAc;IAClD,OAAO,UAAU,sBAAsB,OAAO;IAC9C;GACF;GACA,YAAY,UAAU;GAEtB,MAAM,SAAS,KAAK,IAAI,MAAM,cAAc,OAAO,MAAM;GACzD,OAAO,UAAU;GACjB,eAAe,OAAO,MAAM,GAAG,MAAM,CAAC;GACtC,OAAO,UAAU,sBAAsB,OAAO;EAChD;EAEA,OAAO,UAAU,sBAAsB,OAAO;EAC9C,aAAa;GACX,IAAI,OAAO,SAAS,qBAAqB,OAAO,OAAO;EACzD;CACF,GAAG;EAAC;EAAa;EAAc;EAAc;EAAa;CAAI,CAAC;CAE/D,gBAAgB;EACd,IAAI,CAAC,eAAe,eAAe,gBAAgB,MAAM;GACvD,eAAe,IAAI;GACnB,OAAO,UAAU,KAAK;EACxB;CACF,GAAG;EAAC;EAAa;EAAa;EAAM;CAAW,CAAC;CAEhD,OAAO;AACT;;;;;;;;;;;;;;AAiBA,SAAgB,mBACd,MACA,UAAqC,CAAC,GAC9B;CACR,OAAO,qBAAqB,MAAM,OAAO;AAC3C;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,2BACd,MACA,UAAqC,CAAC,GACd;CAExB,OAAO;EACL,aAFkB,qBAAqB,MAAM,OAE7C;EACA,YAAY;EACZ,aAAa,QAAQ,eAAe;CACtC;AACF"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
3
- import { t as Text } from "./text-Cqryz7rk.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
+ import { t as Text } from "./text-iQ0YUFNg.js";
4
4
  import { useState } from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { CaretDownIcon, CheckCircleIcon, RobotIcon, SpinnerGapIcon, XCircleIcon } from "@phosphor-icons/react";
@@ -138,4 +138,4 @@ AiSubagent.displayName = "AiSubagent";
138
138
  //#endregion
139
139
  export { SF_AI_SUBAGENT_DEFAULT_VARIANTS as n, SF_AI_SUBAGENT_VARIANTS as r, AiSubagent as t };
140
140
 
141
- //# sourceMappingURL=ai-subagent-JA4iIMW3.js.map
141
+ //# sourceMappingURL=ai-subagent-p97AI1h9.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-subagent-JA4iIMW3.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\";\nimport { Text } from \"../text\";\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 <Text size=\"sm\" wrap=\"shrink\" as=\"span\" DANGEROUS_className=\"font-medium\">\n {displayName}\n </Text>\n {modelId && (\n <Text\n size=\"xs\"\n variant=\"secondary\"\n as=\"span\"\n wrap=\"shrink\"\n DANGEROUS_className=\"truncate rounded bg-sf-tint px-1.5 py-0.5 font-mono\"\n >\n {modelId}\n </Text>\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":";;;;;;;;AAkBA,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,MAAD;OAAM,MAAK;OAAK,MAAK;OAAS,IAAG;OAAO,qBAAoB;iBACzD;OACI,CAAA,EACN,WACC,oBAAC,MAAD;OACE,MAAK;OACL,SAAQ;OACR,IAAG;OACH,MAAK;OACL,qBAAoB;iBAEnB;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-p97AI1h9.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\";\nimport { Text } from \"../text\";\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 <Text\n size=\"sm\"\n wrap=\"shrink\"\n as=\"span\"\n DANGEROUS_className=\"font-medium\"\n >\n {displayName}\n </Text>\n {modelId && (\n <Text\n size=\"xs\"\n variant=\"secondary\"\n as=\"span\"\n wrap=\"shrink\"\n DANGEROUS_className=\"truncate rounded bg-sf-tint px-1.5 py-0.5 font-mono\"\n >\n {modelId}\n </Text>\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":";;;;;;;;AAkBA,IAAa,0BAA0B,EACrC,QAAQ;CACN,SAAS;EAAE,SAAS;EAAI,aAAa;CAAkC;CACvE,WAAW;EAAE,SAAS;EAAI,aAAa;CAAiC;CACxE,OAAO;EAAE,SAAS;EAAI,aAAa;CAAgC;AACrE,EACF;AAEA,IAAa,kCAAkC,EAC7C,QAAQ,UACV;AAkCA,IAAM,gBAOF;CACF,SAAS;EACP,MAAM;EACN,WAAW;EACX,OAAO;CACT;CACA,WAAW;EACT,MAAM;EACN,WAAW;EACX,OAAO;CACT;CACA,OAAO;EACL,MAAM;EACN,WAAW;EACX,OAAO;CACT;AACF;AAIA,SAAS,eAAe,IAAoB;CAC1C,IAAI,KAAK,KAAM,OAAO,GAAG,GAAG;CAC5B,MAAM,IAAI,KAAK,MAAM,KAAK,GAAI;CAC9B,IAAI,IAAI,IAAI,OAAO,GAAG,EAAE;CACxB,MAAM,IAAI,KAAK,MAAM,IAAI,EAAE;CAC3B,MAAM,YAAY,IAAI;CACtB,OAAO,YAAY,IAAI,GAAG,EAAE,IAAI,UAAU,KAAK,GAAG,EAAE;AACtD;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,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,eAAe;CAChE,MAAM,eAAe,mBAAmB,KAAA;CACxC,MAAM,SAAS,eAAe,iBAAiB;CAE/C,MAAM,oBAAoB,aAAsB;EAC9C,IAAI,CAAC,cAAc,gBAAgB,QAAQ;EAC3C,eAAe,QAAQ;CACzB;CAEA,MAAM,gBAAgB,QAAQ;CAC9B,MAAM,eAAe,cAAc;CACnC,MAAM,aAAa,aAAa;CAChC,MAAM,cAAc,QAAQ,aAAa;CAEzC,OACE,oBAAC,YAAgB,MAAjB;EACE,aAAa;EACb,cAAc;EACd,MAAM;YAEN,qBAAC,OAAD;GACE,WAAW,GAAG,4CAA4C,SAAS;GACnE,GAAI;aAFN,CAKE,qBAAC,YAAgB,SAAjB;IAAyB,WAAU;cAAnC;KAEE,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,eAAD,EAAe,WAAU,WAAY,CAAA;KAClC,CAAA;KAGL,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,MAAD;OACE,MAAK;OACL,MAAK;OACL,IAAG;OACH,qBAAoB;iBAEnB;MACG,CAAA,GACL,WACC,oBAAC,MAAD;OACE,MAAK;OACL,SAAQ;OACR,IAAG;OACH,MAAK;OACL,qBAAoB;iBAEnB;MACG,CAAA,CAEL;;KAGL,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACG,aAAa,KAAA,KACZ,oBAAC,QAAD;QAAM,WAAU;kBACb,eAAe,QAAQ;OACpB,CAAA;OAER,oBAAC,YAAD,EAAY,WAAW,GAAG,YAAY,aAAa,SAAS,EAAI,CAAA;OAChE,oBAAC,eAAD,EACE,WAAW,GACT,2DACA,UAAU,YACZ,EACD,CAAA;MACE;;IACkB;OAGzB,oBAAC,YAAgB,OAAjB;IAAuB,WAAU;cAC/B,oBAAC,OAAD;KAAK,WAAU;KACZ;IACE,CAAA;GACgB,CAAA,CACpB;;CACe,CAAA;AAE1B;AAEA,WAAW,cAAc"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
3
- import { t as Button } from "./button-CO6-qPax.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
+ import { t as Button } from "./button-BHOgXJRU.js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/components/ai-suggestion/ai-suggestion.tsx
6
6
  var SF_AI_SUGGESTION_VARIANTS = {};
@@ -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-BdO6MBuH.js.map
55
+ //# sourceMappingURL=ai-suggestion-Bj6vF7CT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-suggestion-BdO6MBuH.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-Bj6vF7CT.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,CAAC;AAC1C,IAAa,oCAAoC,CAAC;;;;;;;;;;;;;;;;AAqBlD,SAAgB,cAAc,EAC5B,WACA,UACA,GAAG,SACkB;CACrB,OACE,oBAAC,OAAD;EACE,WAAW,GACT,wEAEA,wDACA,SACF;EACA,GAAI;EAEH;CACE,CAAA;AAET;;;;;;;;;;;;AAyBA,SAAgB,aAAa,EAC3B,YACA,SACA,WACA,UAAU,aACV,OAAO,MACP,UACA,GAAG,SACiB;CACpB,OACE,oBAAC,QAAD;EACE,WAAW,GAAG,8BAA8B,SAAS;EACrD,eAAe,UAAU,UAAU;EAC7B;EACN,MAAK;EACI;EACT,GAAI;YAEH,YAAY;CACP,CAAA;AAEZ"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
3
- import { t as Text } from "./text-Cqryz7rk.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
+ import { t as Text } from "./text-iQ0YUFNg.js";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/components/ai-task-list/ai-task-list.tsx
6
6
  var SF_AI_TASK_LIST_VARIANTS = {};
@@ -86,7 +86,9 @@ function AiTaskList({ tasks, title, icon, showProgress = true, children, classNa
86
86
  className: "flex items-center justify-between gap-2 pb-0.5",
87
87
  children: [/* @__PURE__ */ jsxs("div", {
88
88
  className: "flex items-center gap-1.5",
89
- children: [icon && /* @__PURE__ */ jsx(icon, { className: "size-3.5 text-sf-subtle" }), title && /* @__PURE__ */ jsx(Text, {
89
+ children: [icon && (() => {
90
+ return /* @__PURE__ */ jsx(icon, { className: "size-3.5 text-sf-subtle" });
91
+ })(), title && /* @__PURE__ */ jsx(Text, {
90
92
  size: "sm",
91
93
  variant: "secondary",
92
94
  wrap: "shrink",
@@ -121,4 +123,4 @@ AiTaskList.displayName = "AiTaskList";
121
123
  //#endregion
122
124
  export { SF_AI_TASK_LIST_VARIANTS as i, AiTaskListItem as n, SF_AI_TASK_LIST_DEFAULT_VARIANTS as r, AiTaskList as t };
123
125
 
124
- //# sourceMappingURL=ai-task-list-DYw4R1FA.js.map
126
+ //# sourceMappingURL=ai-task-list-C_UQYpk9.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-task-list-DYw4R1FA.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\";\nimport { Text } from \"../text\";\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 <Text\n size=\"sm\"\n wrap=\"shrink\"\n as=\"span\"\n DANGEROUS_className={cn(\"min-w-0 grow\", config.textClassName)}\n >\n {task.content}\n </Text>\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 && (\n <Text size=\"sm\" variant=\"secondary\" wrap=\"shrink\" as=\"span\">\n {title}\n </Text>\n )}\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":";;;;;AASA,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,MAAD;IACE,MAAK;IACL,MAAK;IACL,IAAG;IACH,qBAAqB,GAAG,gBAAgB,OAAO,cAAc;cAE5D,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,SACC,oBAAC,MAAD;MAAM,MAAK;MAAK,SAAQ;MAAY,MAAK;MAAS,IAAG;gBAClD;MACI,CAAA,CAEL;QACL,gBAAgB,QAAQ,KACvB,qBAAC,QAAD;KAAM,WAAU;eAAhB;MACG;MAAU;MAAE;MACR;OAEL;;GAIR,oBAAC,OAAD;IAAK,WAAU;cACZ,MAAM,KAAK,SACV,oBAAC,gBAAD,EAAoC,MAAQ,EAAvB,KAAK,GAAkB,CAC5C;IACE,CAAA;GAGL,gBAAgB,QAAQ,KACvB,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAE,OAAO,GAAI,YAAY,QAAS,IAAI,IAAI;KACjD,CAAA;IACE,CAAA;GAGP;GACG;;;AAIV,WAAW,cAAc"}
1
+ {"version":3,"file":"ai-task-list-C_UQYpk9.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\";\nimport { Text } from \"../text\";\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 <Text\n size=\"sm\"\n wrap=\"shrink\"\n as=\"span\"\n DANGEROUS_className={cn(\"min-w-0 grow\", config.textClassName)}\n >\n {task.content}\n </Text>\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 && (\n <Text size=\"sm\" variant=\"secondary\" wrap=\"shrink\" as=\"span\">\n {title}\n </Text>\n )}\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":";;;;;AASA,IAAa,2BAA2B,CAAC;AACzC,IAAa,mCAAmC,CAAC;AA0CjD,IAAM,gBAGF;CACF,SAAS;EACP,cAAc;EACd,eAAe;CACjB;CACA,aAAa;EACX,cAAc;EACd,eAAe;CACjB;CACA,WAAW;EACT,cAAc;EACd,eAAe;CACjB;CACA,WAAW;EACT,cAAc;EACd,eAAe;CACjB;AACF;AAEA,IAAM,eAA+C;CACnD,MAAM;CACN,QAAQ;CACR,KAAK;AACP;;;;AAOA,SAAgB,eAAe,EAC7B,MACA,WACA,GAAG,SACmB;CACtB,MAAM,SAAS,cAAc,KAAK;CAElC,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,+BAA+B,SAAS;EAAG,GAAI;YAAlE;GACE,oBAAC,QAAD;IACE,eAAA;IACA,WAAW,GACT,iDACA,OAAO,YACT;GACD,CAAA;GACD,oBAAC,MAAD;IACE,MAAK;IACL,MAAK;IACL,IAAG;IACH,qBAAqB,GAAG,gBAAgB,OAAO,aAAa;cAE3D,KAAK;GACF,CAAA;GACL,KAAK,YACJ,oBAAC,QAAD;IACE,WAAW,GACT,qCACA,aAAa,KAAK,SACpB;IACA,OAAO,GAAG,KAAK,SAAS;GACzB,CAAA;EAEA;;AAET;AAEA,eAAe,cAAc;;;;;;;;;;;;;;;;;;;;AAuB7B,SAAgB,WAAW,EACzB,OACA,OACA,MACA,eAAe,MACf,UACA,WACA,GAAG,SACe;CAClB,MAAM,YAAY,MAAM,QAAQ,MAAM,EAAE,WAAW,WAAW,EAAE;CAChE,MAAM,QAAQ,MAAM,QAAQ,MAAM,EAAE,WAAW,WAAW,EAAE;CAE5D,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,uBAAuB,SAAS;EAAG,GAAI;YAA1D;IAEI,SAAS,iBACT,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACG,eACQ;MAEL,OAAO,oBAAC,MAAD,EAAM,WAAU,0BAA2B,CAAA;KACpD,GAAG,GACJ,SACC,oBAAC,MAAD;MAAM,MAAK;MAAK,SAAQ;MAAY,MAAK;MAAS,IAAG;gBAClD;KACG,CAAA,CAEL;QACJ,gBAAgB,QAAQ,KACvB,qBAAC,QAAD;KAAM,WAAU;eAAhB;MACG;MAAU;MAAE;KACT;MAEL;;GAIP,oBAAC,OAAD;IAAK,WAAU;cACZ,MAAM,KAAK,SACV,oBAAC,gBAAD,EAAoC,KAAO,GAAtB,KAAK,EAAiB,CAC5C;GACE,CAAA;GAGJ,gBAAgB,QAAQ,KACvB,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAE,OAAO,GAAI,YAAY,QAAS,IAAI,GAAG;IACjD,CAAA;GACE,CAAA;GAGN;EACE;;AAET;AAEA,WAAW,cAAc"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
3
  import { forwardRef, useCallback, useMemo, useRef, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { BrainIcon, CaretDownIcon, CheckCircleIcon, CircleDashedIcon, CodeIcon, LightningIcon, MagnifyingGlassIcon, RobotIcon, ShieldWarningIcon, SpinnerGapIcon, WrenchIcon, XCircleIcon } from "@phosphor-icons/react";
@@ -36,7 +36,7 @@ function getAgentIcon(agentType, customIcon) {
36
36
  }
37
37
  function getModelShort(modelId) {
38
38
  if (!modelId) return "";
39
- return modelId.split("/").pop()?.replace(/^claude-/, "").replace(/-\d+$/, "") ?? "";
39
+ return modelId.split("/").pop()?.replace(/^claude-/u, "").replace(/-\d+$/u, "") ?? "";
40
40
  }
41
41
  function formatMs(ms) {
42
42
  const s = Math.round(ms / 100) / 10;
@@ -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-C42tOUT8.js.map
373
+ //# sourceMappingURL=ai-timeline-CePL1LOU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-timeline-CePL1LOU.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-/u, \"\")\n .replace(/-\\d+$/u, \"\") ?? \"\"\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":";;;;;;AAuBA,IAAa,0BAA0B,EACrC,SAAS;CACP,aAAa;EAAE,SAAS;EAAI,aAAa;CAAiC;CAC1E,SAAS;EAAE,SAAS;EAAI,aAAa;CAA8B;AACrE,EACF;AAEA,IAAa,kCAAkC,EAC7C,SAAS,cACX;AAmGA,IAAM,cAA2C;CAC/C,SAAS;CACT,QAAQ;CACR,SAAS;CACT,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACN,OAAO;CACP,MAAM;AACR;AAEA,SAAS,aACP,WACA,YACa;CACb,IAAI,YAAY,OAAO;CACvB,IAAI,WAAW;EACb,MAAM,QAAQ,UAAU,YAAY;EACpC,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,WAAW,GAClD,IAAI,MAAM,SAAS,GAAG,GAAG,OAAO;CAEpC;CACA,OAAO;AACT;AAEA,SAAS,cAAc,SAAkB;CACvC,IAAI,CAAC,SAAS,OAAO;CACrB,OACE,QACG,MAAM,GAAG,EACT,IAAI,GACH,QAAQ,aAAa,EAAE,EACxB,QAAQ,UAAU,EAAE,KAAK;AAEhC;AAEA,SAAS,SAAS,IAAY;CAC5B,MAAM,IAAI,KAAK,MAAM,KAAK,GAAG,IAAI;CACjC,IAAI,IAAI,IAAI,OAAO,GAAG,EAAE;CACxB,OAAO,GAAG,KAAK,MAAM,IAAI,EAAE,EAAE,IAAI,KAAK,MAAM,IAAI,EAAE,EAAE;AACtD;AAGA,IAAM,eAAoD;CACxD,SAAS;CACT,WAAW;CACX,SAAS;CACT,UAAU;CACV,WAAW;CACX,OAAO;AACT;AAGA,IAAM,cAAc;AAEpB,SAAS,UAAU,EAAE,QAAuC;CAC1D,QAAQ,MAAR;EACE,KAAK,WACH,OAAO,oBAAC,gBAAD;GAAgB,MAAM;GAAG,WAAU;EAAgB,CAAA;EAC5D,KAAK,aACH,OAAO,oBAAC,YAAD,EAAY,MAAM,EAAI,CAAA;EAC/B,KAAK,YACH,OAAO,oBAAC,mBAAD,EAAmB,MAAM,EAAI,CAAA;EACtC,KAAK,aACH,OAAO,oBAAC,iBAAD,EAAiB,MAAM,EAAI,CAAA;EACpC,KAAK,SACH,OAAO,oBAAC,aAAD,EAAa,MAAM,EAAI,CAAA;EAChC,SACE,OAAO,oBAAC,kBAAD,EAAkB,MAAM,EAAI,CAAA;CACvC;AACF;AAIA,SAAS,qBAAqB,EAC5B,OACA,YACA,OACA,SACA,YACA,cACA,WAAW,GACX,WAAW,KAUV;CACD,MAAM,OAAO,KAAK,IAAI,IAAI,MAAM,UAAU,cAAc,OAAO;CAC/D,MAAM,MAAM,MAAM,SAAS;CAC3B,MAAM,QAAQ,KAAK,IAAI,IAAI,MAAM,MAAM,WAAW,OAAO;CACzD,MAAM,aAAa,MAAM,MAAM;CAE/B,MAAM,eAAe;CAGrB,MAAM,eADU,QAAQ,KAEpB,iBACA,QAAQ,KACN,eACA;CAIN,MAAM,cAAc,aAAa;CACjC,MAAM,eAAe;CAErB,MAAM,aADkB,cAAc,eAAe,KACjB;CACpC,MAAM,YAAY,eAAe,WAAW;CAE5C,OACE,qBAAC,UAAD;EACE,MAAK;EACL,cAAY,MAAM,SAAS,MAAM;EACjC,OAAO,MAAM,eAAe,MAAM;EAClC,SAAS,qBAAqB,aAAa,KAAK,IAAI,KAAA;EACpD,WAAW,GACT,kDACA,cACA,aAAa,MAAM,OACnB,eAAe,cAAc,kBAC7B,8EACA,gDACF;EACA,OAAO;GACL;GACA;GACA,QAAQ,YAAY;GACpB,KAAK;EACP;YAlBF;GAoBE,oBAAC,WAAD,EAAW,MAAM,MAAM,KAAO,CAAA;GAC7B,SAAS,gBAAgB,MAAM,SAC9B,oBAAC,QAAD;IAAM,WAAU;cACb,MAAM;GACH,CAAA;GAEP,SAAS,gBACR,oBAAC,QAAD;IAAM,WAAU;cACb,SAAS,UAAU;GAChB,CAAA;EAEF;;AAEZ;;;;;AAMA,SAAS,iBAAiB,QAAgD;CACxE,IAAI,OAAO,WAAW,GAAG,OAAO,CAAC;CAGjC,MAAM,SAAS,CAAC,GAAG,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;CAC/D,MAAM,OAA4B,CAAC;CAEnC,KAAK,MAAM,SAAS,QAAQ;EAE1B,IAAI,SAAS;EACb,KAAK,MAAM,OAAO,MAMhB,IAAI,CALa,IAAI,MAClB,aACC,MAAM,WAAW,SAAS,SAAS,cAClC,MAAM,SAAS,YAAY,SAAS,OAEpC,GAAU;GACb,IAAI,KAAK,KAAK;GACd,SAAS;GACT;EACF;EAEF,IAAI,CAAC,QAEH,KAAK,KAAK,CAAC,KAAK,CAAC;CAErB;CAEA,OAAO;AACT;AAIA,IAAM,0BAA0B;AAChC,IAAM,sBAAsB;AAC5B,IAAM,cAAc;;;;;;;;;AAUpB,IAAa,iBAAiB,YAE1B,EACE,OACA,WACA,SACA,SAAS,QACT,QACA,YACA,OACA,SACA,kBAAkB,OAClB,cACA,MACA,UAAU,eACV,WACA,GAAG,SAEL,QACG;CACH,MAAM,CAAC,UAAU,eAAe,SAAS,eAAe;CACxD,MAAM,OAAO,aAAa,WAAW,IAAI;CACzC,MAAM,aAAa,cAAc,OAAO;CAGxC,MAAM,YAAY,cAAc,iBAAiB,MAAM,GAAG,CAAC,MAAM,CAAC;CAClE,MAAM,WAAW,UAAU;CAG3B,MAAM,YACJ,YAAY,YAAY,sBAAsB;CAChD,MAAM,QAAQ,WAAW,IAAI,YAAY,WAAW;CAGpD,MAAM,SAAS,cAAc;EAC3B,IAAI,CAAC,OAAO,QAAQ,OAAO;EAC3B,OAAO,KAAK,IAAI,GAAG,OAAO,KAAK,MAAM,EAAE,SAAS,KAAK,CAAC;CACxD,GAAG;EAAC;EAAQ;EAAY;CAAK,CAAC;CAE9B,MAAM,aAAa,KAAK,IAAI,IAAI,SAAS,cAAc,OAAO;CAE9D,OACE,qBAAC,OAAD;EACO;EACL,WAAW,GAAG,2CAA2C,SAAS;EAClE,GAAI;YAHN,CAME,qBAAC,OAAD;GAAK,WAAU;GAAO,OAAO,EAAE,QAAQ,MAAM;aAA7C,CAEE,qBAAC,OAAD;IACE,WAAU;IACV,OAAO,EAAE,OAAO,YAAY;cAF9B;KAKG,OAAO,MAAM,MAAM,EAAE,MAAM,KAC1B,oBAAC,UAAD;MACE,MAAK;MACL,cAAY,WAAW,kBAAkB;MACzC,eAAe,aAAa,MAAM,CAAC,CAAC;MACpC,WAAU;gBAEV,oBAAC,eAAD;OACE,MAAM;OACN,WAAW,GACT,wBACA,YAAY,YACd;MACD,CAAA;KACK,CAAA;KAIV,oBAAC,OAAD;MACE,WAAW,GACT,qDACA,YAAY,YAAY,WAAW,UACnC,WAAW,YACP,kBACA,WAAW,cACT,oBACA,WAAW,UACT,mBACA,gBACV;gBAEC,WAAW,YACV,oBAAC,gBAAD;OACE,MAAM,YAAY,YAAY,KAAK;OACnC,WAAU;MACX,CAAA,IAED,oBAAC,MAAD,EAAM,MAAM,YAAY,YAAY,KAAK,GAAK,CAAA;KAE7C,CAAA;KAGL,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,OAAD;OACE,WAAW,GACT,wCACA,YAAY,YAAY,gBAAgB,SAC1C;iBAEC;MACE,CAAA,GACJ,cAAc,YAAY,iBACzB,oBAAC,OAAD;OAAK,WAAU;iBACZ;MACE,CAAA,CAEJ;;IACF;OAGL,oBAAC,OAAD;IACE,WAAU;IACV,OAAO,EAAE,UAAU,WAAW;cAE7B,UAAU,KAAK,KAAK,aACnB,IAAI,KAAK,UACP,oBAAC,sBAAD;KAES;KACK;KACL;KACE;KACT,YAAY;KACE;KACJ;KACA;IACX,GATM,MAAM,EASZ,CACF,CACH;GACG,CAAA,CACF;MAGJ,YACC,OACG,QAAQ,MAAM,EAAE,MAAM,EACtB,KAAK,UACJ,qBAAC,OAAD;GAEE,WAAU;aAFZ,CAIE,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,oBAAC,QAAD,EACE,WAAW,GACT,yBACA,aAAa,MAAM,MAAM,MAAM,GAAG,EAAE,EACtC,EACD,CAAA;KACD,oBAAC,QAAD;MAAM,WAAU;gBACb,MAAM,SAAS,MAAM;KAClB,CAAA;KACL,MAAM,SACL,oBAAC,QAAD;MAAM,WAAU;gBACb,SAAS,MAAM,QAAQ,MAAM,OAAO;KACjC,CAAA;IAEL;OACL,oBAAC,OAAD;IAAK,WAAU;cAA0B,MAAM;GAAY,CAAA,CACxD;KApBE,UAAU,MAAM,IAoBlB,CACN,CACF;;AAET,CACF;AAEA,eAAe,cAAc;AAI7B,IAAM,mBAAmB;;AAGzB,SAAS,cAAc,IAAoB;CACzC,IAAI,KAAK,KAAQ;EACf,MAAM,IAAI,KAAK,MAAM,KAAK,GAAG,IAAI;EACjC,OAAO,GAAG,EAAE,QAAQ,IAAI,MAAM,IAAI,IAAI,CAAC,EAAE;CAC3C;CACA,MAAM,IAAI,KAAK,MAAM,KAAK,GAAM;CAChC,MAAM,IAAI,KAAK,MAAO,KAAK,MAAU,GAAI;CACzC,OAAO,IAAI,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,GAAG,EAAE;AACtC;;AAGA,SAAS,cACP,UACA,OACA,SACA,YACwD;CACxD,MAAM,aAAa,QAAQ;CAC3B,MAAM,wBAAwB;EAC5B,MAAM,WAAW,UAAU;EAC3B,IAAI,YAAY,KAAK,OAAO;EAC5B,IAAI,YAAY,IAAI,OAAO;EAC3B,IAAI,YAAY,IAAI,OAAO;EAC3B,IAAI,YAAY,GAAG,OAAO;EAC1B,OAAO;CACT,GAAG;CAEH,MAAM,QAAgE,CAAC;CACvE,MAAM,YAAY,KAAK,KAAK,WAAW,cAAc,IAAI;CAEzD,KAAK,IAAI,IAAI,WAAW,KAAK,WAAW,YAAY,KAAK,gBAAgB;EACvE,MAAM,UAAU,IAAI;EACpB,MAAM,IAAI,UAAU;EACpB,IAAI,IAAI,YAAY;EACpB,MAAM,QAAQ,cAAc,OAAO;EACnC,MAAM,KAAK;GAAE;GAAO;EAAE,CAAC;CACzB;CACA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,IAAa,aAAa,YAEtB,EACE,YACA,kBAAkB,IAClB,gBAAgB,MAChB,OAAO,WACP,SAAS,WAAW,gCAAgC,SACpD,WACA,UACA,GAAG,SAEL,QACG;CAGH,MAAM,QAAQ,aAAa,KAAK,IAAI;CACpC,MAAM,SAAS,cAAc;CAC7B,MAAM,UAAU,kBAAkB;CAClC,MAAM,kBAAkB,QAAQ;CAChC,MAAM,aAAa,KAAK,IAAI,KAAK,kBAAkB,UAAU,GAAG;CAChE,MAAM,OAAO,kBAAkB;CAE/B,MAAM,QAAQ,cACN,cAAc,QAAQ,OAAO,SAAS,UAAU,GAEtD;EAAC;EAAQ,KAAK,MAAM,QAAQ,GAAI;EAAG;EAAS;CAAU,CACxD;CAEA,MAAM,YAAY,OAAuB,IAAI;CAG7C,MAAM,cAAc,kBAAkB;EACpC,MAAM,KAAK,UAAU;EACrB,IAAI,CAAC,IAAI;EACT,MAAM,eAAe,cAAc,OAAO,GAAG,cAAc;EAC3D,GAAG,aAAa,KAAK,IAAI,GAAG,YAAY;CAC1C,GAAG,CAAC,IAAI,CAAC;CAET,OACE,qBAAC,OAAD;EACO;EACL,WAAW,GACT,wEACA,SACF;EACA,GAAI;YANN,CASE,oBAAC,OAAD;GAAK,KAAK;GAAW,WAAU;aAC7B,qBAAC,OAAD;IAAK,OAAO,EAAE,UAAU,cAAc,WAAW;cAAjD,CAEE,qBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAE,QAAQ,iBAAiB;eAFpC,CAKE,oBAAC,OAAD;MACE,WAAU;MACV,OAAO,EAAE,OAAO,YAAY;gBAE5B,oBAAC,QAAD;OAAM,WAAU;iBAAkE;MAE5E,CAAA;KACH,CAAA,GAEL,qBAAC,OAAD;MACE,WAAU;MACV,OAAO,EAAE,UAAU,WAAW;gBAFhC,CAIG,MAAM,KAAK,SACV,qBAAC,OAAD;OAEE,WAAU;OACV,OAAO,EAAE,MAAM,KAAK,EAAE;iBAHxB,CAKE,oBAAC,OAAD,EAAK,WAAU,sBAAuB,CAAA,GACtC,qBAAC,QAAD;QAAM,WAAU;kBAAhB,CAA+D,KAC3D,KAAK,KACH;SACH;SARE,KAAK,CAQP,CACN,GAGA,iBACC,qBAAC,OAAD;OACE,WAAU;OACV,OAAO,EAAE,MAAM,KAAK;iBAFtB,CAIE,oBAAC,OAAD,EAAK,WAAU,uBAAwB,CAAA,GACvC,oBAAC,QAAD;QAAM,WAAU;kBAAwD;OAElE,CAAA,CACH;QAEJ;OACF;QAGL,qBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,MAAM,KAAK,SACV,oBAAC,OAAD;OAEE,WAAU;OACV,OAAO,EAAE,MAAM,KAAK,EAAE;MACvB,GAHM,QAAQ,KAAK,GAGnB,CACF,GAEA,iBACC,oBAAC,OAAD;OACE,WAAU;OACV,OAAO,EAAE,MAAM,KAAK;MACrB,CAAA,CAEA;SAEJ,QACE;MACF;;EACF,CAAA,GAGJ,iBACC,oBAAC,UAAD;GACE,MAAK;GACL,cAAW;GACX,SAAS;GACT,WAAW,GACT,uEACA,uEACA,4EACF;aACD;EAEO,CAAA,CAEP;;AAET,CACF;AAEA,WAAW,cAAc"}
@@ -1,9 +1,9 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-YROP2_ox.js";
3
- import { t as Text } from "./text-Cqryz7rk.js";
4
- import { t as Tooltip } from "./tooltip-g9lFsvcT.js";
5
- import { t as Button } from "./button-CO6-qPax.js";
6
- import { t as AiStatusBadge } from "./ai-status-badge-CSU_QOdz.js";
2
+ import { t as cn } from "./cn-CmAOpn49.js";
3
+ import { t as Text } from "./text-iQ0YUFNg.js";
4
+ import { t as Tooltip } from "./tooltip-uobk6Oh-.js";
5
+ import { t as Button } from "./button-BHOgXJRU.js";
6
+ import { t as AiStatusBadge } from "./ai-status-badge-BZLczdkI.js";
7
7
  import { useEffect, useRef, useState } from "react";
8
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { BrainIcon, CaretDownIcon, ChatCircleIcon, CheckIcon, CircleIcon, CodeIcon, DatabaseIcon, FileTextIcon, GearIcon, GlobeIcon, ImageIcon, LightningIcon, MagnifyingGlassIcon, ShieldWarningIcon, SpinnerGapIcon, TerminalIcon, WarningCircleIcon, XCircleIcon } from "@phosphor-icons/react";
@@ -43,9 +43,12 @@ var TOOL_ICONS = {
43
43
  settings: GearIcon,
44
44
  code: CodeIcon
45
45
  };
46
+ var EMPTY_DURATIONS = {};
47
+ var EMPTY_SUMMARIES = {};
48
+ var EMPTY_APPROVAL_STATES = {};
46
49
  /** Extract the display-friendly tool name from a tool part. */
47
50
  function getToolDisplayName(part) {
48
- return part.toolName.replace("tool-", "").replace("Tool", "").replace(/([A-Z])/g, " $1").replace(/_/g, " ").replace(/^./, (s) => s.toUpperCase()).trim();
51
+ return part.toolName.replace("tool-", "").replace("Tool", "").replace(/([A-Z])/gu, " $1").replace(/_/gu, " ").replace(/^./u, (s) => s.toUpperCase()).trim();
49
52
  }
50
53
  /** Get the best-matching icon for a tool part. */
51
54
  function getToolIcon(part) {
@@ -265,6 +268,7 @@ function AiToolCall({ part, variant = "default", duration, summary, icon: Custom
265
268
  children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, {
266
269
  render: /* @__PURE__ */ jsx("button", {
267
270
  type: "button",
271
+ "aria-label": displayName,
268
272
  className: cn("flex w-fit items-center gap-1.5 rounded px-1 py-0.5 text-left", "transition-colors hover:bg-sf-tint")
269
273
  }),
270
274
  children: [/* @__PURE__ */ jsx("span", {
@@ -389,6 +393,7 @@ function AiToolCallEphemeral({ part, approvalState, duration, summary, icon: _ic
389
393
  children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, {
390
394
  render: /* @__PURE__ */ jsx("button", {
391
395
  type: "button",
396
+ "aria-label": displayName,
392
397
  className: cn("flex w-fit items-center gap-1.5 rounded px-1 py-0.5 text-left", "transition-colors hover:bg-sf-tint")
393
398
  }),
394
399
  children: [
@@ -469,7 +474,7 @@ function AiToolCallEphemeral({ part, approvalState, duration, summary, icon: _ic
469
474
  * <AiToolCallGroup parts={toolParts} label="3 tools ran" />
470
475
  * ```
471
476
  */
472
- function AiToolCallGroup({ parts, label, durations = {}, summaries = {}, approvalStates = {}, onApprove, onReject, defaultExpanded = false, className, ...props }) {
477
+ function AiToolCallGroup({ parts, label, durations = EMPTY_DURATIONS, summaries = EMPTY_SUMMARIES, approvalStates = EMPTY_APPROVAL_STATES, onApprove, onReject, defaultExpanded = false, className, ...props }) {
473
478
  const [isExpanded, setIsExpanded] = useState(defaultExpanded);
474
479
  const completedCount = parts.filter((p) => p.state === "output-available").length;
475
480
  const errorCount = parts.filter(hasToolError).length;
@@ -497,6 +502,7 @@ function AiToolCallGroup({ parts, label, durations = {}, summaries = {}, approva
497
502
  children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, {
498
503
  render: /* @__PURE__ */ jsx("button", {
499
504
  type: "button",
505
+ "aria-label": label ?? `${totalCount} tool calls`,
500
506
  className: "flex w-fit items-center gap-3 px-3 py-2.5 text-left transition-colors hover:bg-sf-tint"
501
507
  }),
502
508
  children: [
@@ -522,7 +528,7 @@ function AiToolCallGroup({ parts, label, durations = {}, summaries = {}, approva
522
528
  }),
523
529
  /* @__PURE__ */ jsxs("div", {
524
530
  className: "flex items-center gap-2",
525
- children: [!isExpanded ? /* @__PURE__ */ jsxs("div", {
531
+ children: [isExpanded ? null : /* @__PURE__ */ jsxs("div", {
526
532
  className: "flex -space-x-1",
527
533
  children: [parts.slice(0, 5).map((p) => {
528
534
  const Icon = getToolIcon(p);
@@ -534,7 +540,7 @@ function AiToolCallGroup({ parts, label, durations = {}, summaries = {}, approva
534
540
  className: "flex size-6 items-center justify-center rounded-full border-2 border-sf-elevated bg-sf-tint text-[10px] text-sf-subtle",
535
541
  children: ["+", parts.length - 5]
536
542
  }) : null]
537
- }) : null, /* @__PURE__ */ jsx(CaretDownIcon, { className: cn("size-4 text-sf-subtle transition-transform duration-200", !isExpanded && "-rotate-90") })]
543
+ }), /* @__PURE__ */ jsx(CaretDownIcon, { className: cn("size-4 text-sf-subtle transition-transform duration-200", !isExpanded && "-rotate-90") })]
538
544
  })
539
545
  ]
540
546
  }), /* @__PURE__ */ jsx(Collapsible.Panel, {
@@ -564,7 +570,7 @@ function AiToolCallGroup({ parts, label, durations = {}, summaries = {}, approva
564
570
  * <AiToolCallTimeline parts={toolParts} durations={{ id1: 230, id2: 540 }} />
565
571
  * ```
566
572
  */
567
- function AiToolCallTimeline({ parts, durations = {}, className, ...props }) {
573
+ function AiToolCallTimeline({ parts, durations = EMPTY_DURATIONS, className, ...props }) {
568
574
  return /* @__PURE__ */ jsx("div", {
569
575
  className: cn("flex flex-wrap items-center gap-1 py-2", className),
570
576
  ...props,
@@ -604,4 +610,4 @@ function AiToolCallTimeline({ parts, durations = {}, className, ...props }) {
604
610
  //#endregion
605
611
  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 };
606
612
 
607
- //# sourceMappingURL=ai-tool-03jOTwUI.js.map
613
+ //# sourceMappingURL=ai-tool-CfRcwmHT.js.map