@signalflare-ai/ui 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/CHANGELOG.md +77 -1
  2. package/ai/component-registry.json +208 -550
  3. package/ai/component-registry.md +3042 -3115
  4. package/ai/schemas.ts +504 -96
  5. package/dist/.build-complete +1 -1
  6. package/dist/ai/schemas.d.ts.map +1 -1
  7. package/dist/{ai-actions-BdUZI3Gk.js → ai-actions-CBfz5XEf.js} +4 -4
  8. package/dist/{ai-actions-BdUZI3Gk.js.map → ai-actions-CBfz5XEf.js.map} +1 -1
  9. package/dist/{ai-agent-card-BR2NIYhi.js → ai-agent-card-CByAUe0q.js} +3 -3
  10. package/dist/ai-agent-card-CByAUe0q.js.map +1 -0
  11. package/dist/{ai-approval-Ba7mrKba.js → ai-approval-Ci8N70a7.js} +4 -3
  12. package/dist/{ai-approval-Ba7mrKba.js.map → ai-approval-Ci8N70a7.js.map} +1 -1
  13. package/dist/{ai-code-block-CZtoL73R.js → ai-code-block-P9TJHvaC.js} +37 -39
  14. package/dist/ai-code-block-P9TJHvaC.js.map +1 -0
  15. package/dist/{ai-conversation-Cc7WlaBg.js → ai-conversation-Qslfdi1t.js} +28 -42
  16. package/dist/ai-conversation-Qslfdi1t.js.map +1 -0
  17. package/dist/{ai-info-banner-C7EWPBj7.js → ai-info-banner-B_9vtGK3.js} +3 -3
  18. package/dist/{ai-info-banner-C7EWPBj7.js.map → ai-info-banner-B_9vtGK3.js.map} +1 -1
  19. package/dist/{ai-message-Bp7L68U_.js → ai-message-Ci3gwM7G.js} +6 -6
  20. package/dist/{ai-message-Bp7L68U_.js.map → ai-message-Ci3gwM7G.js.map} +1 -1
  21. package/dist/{ai-mission-header-TiCJfTNt.js → ai-mission-header-CaBc19-t.js} +2 -2
  22. package/dist/{ai-mission-header-TiCJfTNt.js.map → ai-mission-header-CaBc19-t.js.map} +1 -1
  23. package/dist/{ai-part-group-DNb9I446.js → ai-part-group-Dx1Mr92B.js} +5 -4
  24. package/dist/ai-part-group-Dx1Mr92B.js.map +1 -0
  25. package/dist/{ai-prompt-input-BVvov_KF.js → ai-prompt-input-Bm4XoSj2.js} +19 -17
  26. package/dist/ai-prompt-input-Bm4XoSj2.js.map +1 -0
  27. package/dist/{ai-question-GPPMk7YM.js → ai-question-OyJovxGe.js} +4 -3
  28. package/dist/{ai-question-GPPMk7YM.js.map → ai-question-OyJovxGe.js.map} +1 -1
  29. package/dist/{ai-reasoning-_feFjk56.js → ai-reasoning-BLfBXx3F.js} +9 -5
  30. package/dist/ai-reasoning-BLfBXx3F.js.map +1 -0
  31. package/dist/{ai-response-CvjV3WhV.js → ai-response-hbVCZJmo.js} +2 -2
  32. package/dist/{ai-response-CvjV3WhV.js.map → ai-response-hbVCZJmo.js.map} +1 -1
  33. package/dist/{ai-shimmer-j6lKIrjj.js → ai-shimmer-BamNMNK3.js} +2 -2
  34. package/dist/{ai-shimmer-j6lKIrjj.js.map → ai-shimmer-BamNMNK3.js.map} +1 -1
  35. package/dist/{ai-status-badge-CSU_QOdz.js → ai-status-badge-BZLczdkI.js} +2 -2
  36. package/dist/{ai-status-badge-CSU_QOdz.js.map → ai-status-badge-BZLczdkI.js.map} +1 -1
  37. package/dist/{ai-streaming-text-IWW1BhvZ.js → ai-streaming-text-DgYu64UH.js} +1 -1
  38. package/dist/{ai-streaming-text-IWW1BhvZ.js.map → ai-streaming-text-DgYu64UH.js.map} +1 -1
  39. package/dist/{ai-subagent-JA4iIMW3.js → ai-subagent-p97AI1h9.js} +3 -3
  40. package/dist/{ai-subagent-JA4iIMW3.js.map → ai-subagent-p97AI1h9.js.map} +1 -1
  41. package/dist/{ai-suggestion-BdO6MBuH.js → ai-suggestion-Bj6vF7CT.js} +3 -3
  42. package/dist/{ai-suggestion-BdO6MBuH.js.map → ai-suggestion-Bj6vF7CT.js.map} +1 -1
  43. package/dist/{ai-task-list-DYw4R1FA.js → ai-task-list-C_UQYpk9.js} +6 -4
  44. package/dist/{ai-task-list-DYw4R1FA.js.map → ai-task-list-C_UQYpk9.js.map} +1 -1
  45. package/dist/{ai-timeline-C42tOUT8.js → ai-timeline-CePL1LOU.js} +3 -3
  46. package/dist/ai-timeline-CePL1LOU.js.map +1 -0
  47. package/dist/{ai-tool-03jOTwUI.js → ai-tool-CfRcwmHT.js} +17 -11
  48. package/dist/ai-tool-CfRcwmHT.js.map +1 -0
  49. package/dist/{ai-usage-bar-BRf5LC_b.js → ai-usage-bar-45pVRCGA.js} +2 -2
  50. package/dist/{ai-usage-bar-BRf5LC_b.js.map → ai-usage-bar-45pVRCGA.js.map} +1 -1
  51. package/dist/{badge-BheXjMc8.js → badge-Beb-6uut.js} +5 -5
  52. package/dist/{badge-BheXjMc8.js.map → badge-Beb-6uut.js.map} +1 -1
  53. package/dist/{banner-CcsjunJg.js → banner-CCEksxPg.js} +3 -3
  54. package/dist/{banner-CcsjunJg.js.map → banner-CCEksxPg.js.map} +1 -1
  55. package/dist/{breadcrumbs-CouSyy3H.js → breadcrumbs-HiTmgaZ4.js} +5 -5
  56. package/dist/{breadcrumbs-CouSyy3H.js.map → breadcrumbs-HiTmgaZ4.js.map} +1 -1
  57. package/dist/{button-CO6-qPax.js → button-BHOgXJRU.js} +4 -4
  58. package/dist/{button-CO6-qPax.js.map → button-BHOgXJRU.js.map} +1 -1
  59. package/dist/catalog.js +1 -1
  60. package/dist/catalog.js.map +1 -1
  61. package/dist/{chart-Dg0qUeSc.js → chart-B9FfZdKs.js} +7 -7
  62. package/dist/chart-B9FfZdKs.js.map +1 -0
  63. package/dist/{checkbox-D7p4QKsC.js → checkbox-Cy_OCyay.js} +3 -3
  64. package/dist/{checkbox-D7p4QKsC.js.map → checkbox-Cy_OCyay.js.map} +1 -1
  65. package/dist/{clipboard-text-kLaMogs3.js → clipboard-text-CKSvNp9L.js} +6 -5
  66. package/dist/clipboard-text-CKSvNp9L.js.map +1 -0
  67. package/dist/{cn-YROP2_ox.js → cn-CmAOpn49.js} +2 -2
  68. package/dist/{cn-YROP2_ox.js.map → cn-CmAOpn49.js.map} +1 -1
  69. package/dist/{code-BN8InC0G.js → code-JsQz-0G_.js} +4 -4
  70. package/dist/{code-BN8InC0G.js.map → code-JsQz-0G_.js.map} +1 -1
  71. package/dist/{collapsible-D_ueZ0jz.js → collapsible-1kOZ-89L.js} +2 -2
  72. package/dist/{collapsible-D_ueZ0jz.js.map → collapsible-1kOZ-89L.js.map} +1 -1
  73. package/dist/{combobox-B7TOK0U2.js → combobox-CQwDmqgA.js} +4 -4
  74. package/dist/{combobox-B7TOK0U2.js.map → combobox-CQwDmqgA.js.map} +1 -1
  75. package/dist/command-line/cli.js +3 -3
  76. package/dist/{command-palette-CuNUyJca.js → command-palette-Bkuv3e6o.js} +20 -5
  77. package/dist/command-palette-Bkuv3e6o.js.map +1 -0
  78. package/dist/components/ai-actions.js +1 -1
  79. package/dist/components/ai-agent-card.js +1 -1
  80. package/dist/components/ai-approval.js +1 -1
  81. package/dist/components/ai-code-block.js +1 -1
  82. package/dist/components/ai-conversation.js +1 -1
  83. package/dist/components/ai-info-banner.js +1 -1
  84. package/dist/components/ai-message.js +1 -1
  85. package/dist/components/ai-mission-header.js +1 -1
  86. package/dist/components/ai-part-group.js +1 -1
  87. package/dist/components/ai-prompt-input.js +1 -1
  88. package/dist/components/ai-question.js +1 -1
  89. package/dist/components/ai-reasoning.js +1 -1
  90. package/dist/components/ai-response.js +1 -1
  91. package/dist/components/ai-shimmer.js +1 -1
  92. package/dist/components/ai-status-badge.js +1 -1
  93. package/dist/components/ai-streaming-text.js +1 -1
  94. package/dist/components/ai-subagent.js +1 -1
  95. package/dist/components/ai-suggestion.js +1 -1
  96. package/dist/components/ai-task-list.js +1 -1
  97. package/dist/components/ai-timeline.js +1 -1
  98. package/dist/components/ai-tool.js +1 -1
  99. package/dist/components/ai-usage-bar.js +1 -1
  100. package/dist/components/badge.js +1 -1
  101. package/dist/components/banner.js +1 -1
  102. package/dist/components/breadcrumbs.js +1 -1
  103. package/dist/components/button.js +1 -1
  104. package/dist/components/chart.js +2 -2
  105. package/dist/components/checkbox.js +1 -1
  106. package/dist/components/clipboard-text.js +1 -1
  107. package/dist/components/code.js +1 -1
  108. package/dist/components/collapsible.js +1 -1
  109. package/dist/components/combobox.js +1 -1
  110. package/dist/components/command-palette.js +1 -1
  111. package/dist/components/data-grid.js +1 -1
  112. package/dist/components/date-picker.js +1 -1
  113. package/dist/components/date-range-picker.js +1 -1
  114. package/dist/components/dialog.js +1 -1
  115. package/dist/components/dropdown.js +1 -1
  116. package/dist/components/empty.js +1 -1
  117. package/dist/components/field.js +1 -1
  118. package/dist/components/filters.js +1 -1
  119. package/dist/components/flow.js +1 -1
  120. package/dist/components/grid.js +1 -1
  121. package/dist/components/input.js +2 -2
  122. package/dist/components/label.js +1 -1
  123. package/dist/components/layer-card.js +1 -1
  124. package/dist/components/link.js +3 -3
  125. package/dist/components/link.js.map +1 -1
  126. package/dist/components/loader.js +2 -2
  127. package/dist/components/menubar.js +1 -1
  128. package/dist/components/meter.js +1 -1
  129. package/dist/components/pagination.js +1 -1
  130. package/dist/components/popover.js +1 -1
  131. package/dist/components/radio.js +1 -1
  132. package/dist/components/select.js +1 -1
  133. package/dist/components/sensitive-input.js +1 -1
  134. package/dist/components/sidebar.js +1 -1
  135. package/dist/components/signalflare-ai-logo.js +1 -1
  136. package/dist/components/sparkline.js +1 -1
  137. package/dist/components/stat-card.js +1 -1
  138. package/dist/components/surface.js +1 -1
  139. package/dist/components/switch.js +1 -1
  140. package/dist/components/table.js +1 -1
  141. package/dist/components/tabs.js +1 -1
  142. package/dist/components/text-roll.js +1 -1
  143. package/dist/components/text.js +1 -1
  144. package/dist/components/theme-toggle.js +1 -1
  145. package/dist/components/toast.js +1 -1
  146. package/dist/components/tooltip.js +1 -1
  147. package/dist/components/use-agent-harness.js +1 -1
  148. package/dist/{data-grid-DGHmU0w3.js → data-grid-DDSFMHud.js} +136 -53
  149. package/dist/data-grid-DDSFMHud.js.map +1 -0
  150. package/dist/{date-picker--ox89RBy.js → date-picker-O34AqG3f.js} +2 -2
  151. package/dist/{date-picker--ox89RBy.js.map → date-picker-O34AqG3f.js.map} +1 -1
  152. package/dist/{date-range-picker-DVa7QBqE.js → date-range-picker-YKYvum_r.js} +29 -39
  153. package/dist/{date-range-picker-DVa7QBqE.js.map → date-range-picker-YKYvum_r.js.map} +1 -1
  154. package/dist/{dialog-Bv1oSFOd.js → dialog-DYqu4aDO.js} +3 -3
  155. package/dist/{dialog-Bv1oSFOd.js.map → dialog-DYqu4aDO.js.map} +1 -1
  156. package/dist/{dist-B6iWiWwp.js → dist-6AtBsaJE.js} +153 -47
  157. package/dist/dist-6AtBsaJE.js.map +1 -0
  158. package/dist/{dropdown-B_nrGXjV.js → dropdown-XzbnRLYR.js} +15 -5
  159. package/dist/dropdown-XzbnRLYR.js.map +1 -0
  160. package/dist/{echart-CdOUaT-r.js → echart-DGBIVAv1.js} +23 -57
  161. package/dist/{echart-CdOUaT-r.js.map → echart-DGBIVAv1.js.map} +1 -1
  162. package/dist/{empty-DZnN0zKX.js → empty-C1tAkawe.js} +6 -6
  163. package/dist/{empty-DZnN0zKX.js.map → empty-C1tAkawe.js.map} +1 -1
  164. package/dist/{field-B_yVof52.js → field-DBpFzzBS.js} +3 -3
  165. package/dist/{field-B_yVof52.js.map → field-DBpFzzBS.js.map} +1 -1
  166. package/dist/{filters-cpJCY21R.js → filters-SmEl93za.js} +10 -10
  167. package/dist/filters-SmEl93za.js.map +1 -0
  168. package/dist/{flow-B4v198ot.js → flow-BLzgbq1T.js} +6 -6
  169. package/dist/flow-BLzgbq1T.js.map +1 -0
  170. package/dist/genui.js +2 -2
  171. package/dist/genui.js.map +1 -1
  172. package/dist/{grid-CEd64Lnh.js → grid-CifjQL-5.js} +2 -2
  173. package/dist/{grid-CEd64Lnh.js.map → grid-CifjQL-5.js.map} +1 -1
  174. package/dist/{highlight-to-react-D0Yav4jk.js → highlight-to-react-DN9dUCS2.js} +9 -15
  175. package/dist/highlight-to-react-DN9dUCS2.js.map +1 -0
  176. package/dist/index.js +71 -71
  177. package/dist/index.js.map +1 -1
  178. package/dist/{input-ClB_E4Lb.js → input-COmx2M_R.js} +5 -5
  179. package/dist/{input-ClB_E4Lb.js.map → input-COmx2M_R.js.map} +1 -1
  180. package/dist/{input-B2bbijRh.js → input-GkfMQZC_.js} +3 -3
  181. package/dist/{input-B2bbijRh.js.map → input-GkfMQZC_.js.map} +1 -1
  182. package/dist/{label-DUv_urO1.js → label-CiGZ464N.js} +3 -3
  183. package/dist/{label-DUv_urO1.js.map → label-CiGZ464N.js.map} +1 -1
  184. package/dist/{layer-card-BK7eYfwn.js → layer-card-8l8GuLQr.js} +2 -2
  185. package/dist/{layer-card-BK7eYfwn.js.map → layer-card-8l8GuLQr.js.map} +1 -1
  186. package/dist/{layout-DJHMMap2.js → layout-CWBE0qwx.js} +258 -154
  187. package/dist/layout-CWBE0qwx.js.map +1 -0
  188. package/dist/{link-provider-BUZKXaNE.js → link-provider-BSn8YJon.js} +2 -2
  189. package/dist/link-provider-BSn8YJon.js.map +1 -0
  190. package/dist/{loader-DAcc-Uag.js → loader-BEMz8pJO.js} +1 -1
  191. package/dist/{loader-DAcc-Uag.js.map → loader-BEMz8pJO.js.map} +1 -1
  192. package/dist/{measured-text-BI3dTJmH.js → measured-text-CXkdw9Yr.js} +45 -30
  193. package/dist/measured-text-CXkdw9Yr.js.map +1 -0
  194. package/dist/{menubar-Cxf3xeAt.js → menubar-CoOr4ocj.js} +3 -3
  195. package/dist/{menubar-Cxf3xeAt.js.map → menubar-CoOr4ocj.js.map} +1 -1
  196. package/dist/{meter-BFFe9l5b.js → meter-Pf_VOl59.js} +2 -2
  197. package/dist/{meter-BFFe9l5b.js.map → meter-Pf_VOl59.js.map} +1 -1
  198. package/dist/{pagination-yS372Tr4.js → pagination-DSY279Ta.js} +2 -2
  199. package/dist/{pagination-yS372Tr4.js.map → pagination-DSY279Ta.js.map} +1 -1
  200. package/dist/{popover-SRoJaCZr.js → popover-BY-e9co1.js} +2 -2
  201. package/dist/{popover-SRoJaCZr.js.map → popover-BY-e9co1.js.map} +1 -1
  202. package/dist/{radio-BcwhwYNB.js → radio-DZwL13j0.js} +2 -2
  203. package/dist/{radio-BcwhwYNB.js.map → radio-DZwL13j0.js.map} +1 -1
  204. package/dist/{select-DMhdoHMa.js → select-BFifYqHA.js} +6 -6
  205. package/dist/{select-DMhdoHMa.js.map → select-BFifYqHA.js.map} +1 -1
  206. package/dist/{sensitive-input-CJUpIRal.js → sensitive-input-DHLZcM73.js} +4 -4
  207. package/dist/{sensitive-input-CJUpIRal.js.map → sensitive-input-DHLZcM73.js.map} +1 -1
  208. package/dist/{sidebar-D4zrlYpn.js → sidebar-odGsdvG4.js} +6 -7
  209. package/dist/sidebar-odGsdvG4.js.map +1 -0
  210. package/dist/{signalflare-ai-logo-Bipogceq.js → signalflare-ai-logo-CNaDT_w8.js} +2 -2
  211. package/dist/{signalflare-ai-logo-Bipogceq.js.map → signalflare-ai-logo-CNaDT_w8.js.map} +1 -1
  212. package/dist/{skeleton-line-CH1-h6e2.js → skeleton-line-CxxYVTO2.js} +2 -2
  213. package/dist/{skeleton-line-CH1-h6e2.js.map → skeleton-line-CxxYVTO2.js.map} +1 -1
  214. package/dist/{sparkline-DHmgj1d0.js → sparkline-BQ-4j2W2.js} +2 -2
  215. package/dist/{sparkline-DHmgj1d0.js.map → sparkline-BQ-4j2W2.js.map} +1 -1
  216. package/dist/src/blocks/agent-harness/agent-harness.tsx +11 -11
  217. package/dist/src/blocks/commander/commander.tsx +15 -15
  218. package/dist/src/blocks/map-block/map-block.d.ts.map +1 -1
  219. package/dist/src/blocks/map-block/map-block.tsx +11 -7
  220. package/dist/src/components/ai-approval/ai-approval.d.ts.map +1 -1
  221. package/dist/src/components/ai-code-block/ai-code-block.d.ts +14 -13
  222. package/dist/src/components/ai-code-block/ai-code-block.d.ts.map +1 -1
  223. package/dist/src/components/ai-conversation/ai-conversation.d.ts.map +1 -1
  224. package/dist/src/components/ai-part-group/ai-part-group.d.ts.map +1 -1
  225. package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
  226. package/dist/src/components/ai-prompt-input/controller.d.ts.map +1 -1
  227. package/dist/src/components/ai-prompt-input/types.d.ts.map +1 -1
  228. package/dist/src/components/ai-question/ai-question.d.ts.map +1 -1
  229. package/dist/src/components/ai-reasoning/ai-reasoning.d.ts.map +1 -1
  230. package/dist/src/components/ai-response/ai-response.d.ts.map +1 -1
  231. package/dist/src/components/ai-subagent/ai-subagent.d.ts.map +1 -1
  232. package/dist/src/components/ai-tool/ai-tool.d.ts.map +1 -1
  233. package/dist/src/components/chart/echart.d.ts.map +1 -1
  234. package/dist/src/components/clipboard-text/clipboard-text.d.ts.map +1 -1
  235. package/dist/src/components/data-grid/data-grid.d.ts +2 -1
  236. package/dist/src/components/data-grid/data-grid.d.ts.map +1 -1
  237. package/dist/src/components/data-grid/features.d.ts +20 -0
  238. package/dist/src/components/data-grid/features.d.ts.map +1 -0
  239. package/dist/src/components/data-grid/types.d.ts +38 -7
  240. package/dist/src/components/data-grid/types.d.ts.map +1 -1
  241. package/dist/src/components/filters/filters.d.ts.map +1 -1
  242. package/dist/src/components/flow/use-children.d.ts +1 -1
  243. package/dist/src/components/link/link.d.ts.map +1 -1
  244. package/dist/src/components/sidebar/sidebar.d.ts +1 -1
  245. package/dist/src/components/signalflare-ai-logo/signalflare-ai-logo.d.ts.map +1 -1
  246. package/dist/src/components/text/text.d.ts +2 -1
  247. package/dist/src/components/text/text.d.ts.map +1 -1
  248. package/dist/src/components/text-roll/text-roll.d.ts.map +1 -1
  249. package/dist/src/components/theme-toggle/theme-toggle.d.ts.map +1 -1
  250. package/dist/src/components/toast/toast.d.ts.map +1 -1
  251. package/dist/src/utils/highlight-to-react.d.ts.map +1 -1
  252. package/dist/src/utils/measured-text.d.ts.map +1 -1
  253. package/dist/src/utils/use-measured-text.d.ts.map +1 -1
  254. package/dist/{stat-card-Ew-ofzEm.js → stat-card-Bspk4XFr.js} +4 -4
  255. package/dist/stat-card-Bspk4XFr.js.map +1 -0
  256. package/dist/styles/sf-standalone.css +1 -1
  257. package/dist/styles/theme-fedramp.css +3 -12
  258. package/dist/styles/theme-minimal.css +26 -104
  259. package/dist/styles/theme-sf.css +37 -142
  260. package/dist/{surface-DGwRlC0o.js → surface-CWdSFVUx.js} +3 -3
  261. package/dist/{surface-DGwRlC0o.js.map → surface-CWdSFVUx.js.map} +1 -1
  262. package/dist/{switch-BxAMfHdt.js → switch-TA4cByCJ.js} +5 -5
  263. package/dist/switch-TA4cByCJ.js.map +1 -0
  264. package/dist/{table-BBeAtYVZ.js → table-BM8JBGBs.js} +3 -3
  265. package/dist/{table-BBeAtYVZ.js.map → table-BM8JBGBs.js.map} +1 -1
  266. package/dist/{tabs-CeHu7Scn.js → tabs-bnH2vGLv.js} +2 -2
  267. package/dist/{tabs-CeHu7Scn.js.map → tabs-bnH2vGLv.js.map} +1 -1
  268. package/dist/{text-Cqryz7rk.js → text-iQ0YUFNg.js} +4 -5
  269. package/dist/{text-Cqryz7rk.js.map → text-iQ0YUFNg.js.map} +1 -1
  270. package/dist/{text-roll-Ch52hcQj.js → text-roll-C3U2jd2u.js} +5 -2
  271. package/dist/text-roll-C3U2jd2u.js.map +1 -0
  272. package/dist/{theme-toggle-LDfIKEqx.js → theme-toggle-BTVxD-fD.js} +10 -9
  273. package/dist/theme-toggle-BTVxD-fD.js.map +1 -0
  274. package/dist/{toast-CaFQNYng.js → toast-CgZVaAkw.js} +3 -3
  275. package/dist/{toast-CaFQNYng.js.map → toast-CgZVaAkw.js.map} +1 -1
  276. package/dist/{tooltip-g9lFsvcT.js → tooltip-uobk6Oh-.js} +3 -3
  277. package/dist/{tooltip-g9lFsvcT.js.map → tooltip-uobk6Oh-.js.map} +1 -1
  278. package/dist/{use-agent-harness-BTcNJdw4.js → use-agent-harness-Dl8w6X5O.js} +3 -3
  279. package/dist/{use-agent-harness-BTcNJdw4.js.map → use-agent-harness-Dl8w6X5O.js.map} +1 -1
  280. package/dist/utils.js +3 -3
  281. package/package.json +27 -25
  282. package/scripts/component-registry/discovery.ts +11 -10
  283. package/scripts/component-registry/example-cleanup.ts +8 -8
  284. package/scripts/component-registry/index.ts +6 -6
  285. package/scripts/component-registry/schema-generator.ts +1 -1
  286. package/scripts/component-registry/sub-components.ts +35 -23
  287. package/scripts/component-registry/utils.ts +11 -11
  288. package/scripts/component-registry/variant-parser.ts +17 -15
  289. package/scripts/convert-demos-to-stories.ts +5 -5
  290. package/scripts/theme-generator/config.ts +1 -5
  291. package/scripts/theme-generator/generate-css.ts +1 -1
  292. package/scripts/theme-generator/migrate.ts +3 -3
  293. package/dist/ai-agent-card-BR2NIYhi.js.map +0 -1
  294. package/dist/ai-code-block-CZtoL73R.js.map +0 -1
  295. package/dist/ai-conversation-Cc7WlaBg.js.map +0 -1
  296. package/dist/ai-part-group-DNb9I446.js.map +0 -1
  297. package/dist/ai-prompt-input-BVvov_KF.js.map +0 -1
  298. package/dist/ai-reasoning-_feFjk56.js.map +0 -1
  299. package/dist/ai-timeline-C42tOUT8.js.map +0 -1
  300. package/dist/ai-tool-03jOTwUI.js.map +0 -1
  301. package/dist/chart-Dg0qUeSc.js.map +0 -1
  302. package/dist/clipboard-text-kLaMogs3.js.map +0 -1
  303. package/dist/command-palette-CuNUyJca.js.map +0 -1
  304. package/dist/data-grid-DGHmU0w3.js.map +0 -1
  305. package/dist/dist-B6iWiWwp.js.map +0 -1
  306. package/dist/dropdown-B_nrGXjV.js.map +0 -1
  307. package/dist/filters-cpJCY21R.js.map +0 -1
  308. package/dist/flow-B4v198ot.js.map +0 -1
  309. package/dist/highlight-to-react-D0Yav4jk.js.map +0 -1
  310. package/dist/layout-DJHMMap2.js.map +0 -1
  311. package/dist/link-provider-BUZKXaNE.js.map +0 -1
  312. package/dist/measured-text-BI3dTJmH.js.map +0 -1
  313. package/dist/sidebar-D4zrlYpn.js.map +0 -1
  314. package/dist/stat-card-Ew-ofzEm.js.map +0 -1
  315. package/dist/switch-BxAMfHdt.js.map +0 -1
  316. package/dist/text-roll-Ch52hcQj.js.map +0 -1
  317. package/dist/theme-toggle-LDfIKEqx.js.map +0 -1
@@ -30,7 +30,7 @@ export function detectSubComponents(filePath: string): SubComponentConfig[] {
30
30
  // Pattern 1: Object.assign with sub-components
31
31
  // Find the start of Object.assign and then extract the balanced braces
32
32
  // Supports both simple names (Component) and dotted names (SomeBase.Root)
33
- const objectAssignStart = /Object\.assign\s*\(\s*[\w.]+\s*,\s*\{/g;
33
+ const objectAssignStart = /Object\.assign\s*\(\s*[\w.]+\s*,\s*\{/gu;
34
34
  let startMatch: RegExpExecArray | null;
35
35
 
36
36
  while ((startMatch = objectAssignStart.exec(content)) !== null) {
@@ -42,7 +42,7 @@ export function detectSubComponents(filePath: string): SubComponentConfig[] {
42
42
 
43
43
  // Extract sub-component assignments: SubName: Value or SubName: SomeBase.SubName
44
44
  // Handle multi-line with comments
45
- const subPattern = /^\s*(\w+)\s*[,:]/gm;
45
+ const subPattern = /^\s*(\w+)\s*[,:]/gmu;
46
46
  let subMatch: RegExpExecArray | null;
47
47
 
48
48
  while ((subMatch = subPattern.exec(assignBlock)) !== null) {
@@ -59,14 +59,15 @@ export function detectSubComponents(filePath: string): SubComponentConfig[] {
59
59
  "function",
60
60
  "description",
61
61
  ].includes(subName) ||
62
- !/^[A-Z]/.test(subName)
62
+ !/^[A-Z]/u.test(subName)
63
63
  ) {
64
64
  continue;
65
65
  }
66
66
 
67
67
  // Find the value after the colon for this sub-component
68
68
  const valuePattern = new RegExp(
69
- `\\b${subName}\\s*:\\s*(\\w+(?:\\.\\w+)?)`
69
+ `\\b${subName}\\s*:\\s*(\\w+(?:\\.\\w+)?)`,
70
+ "u"
70
71
  );
71
72
  const valueMatch = assignBlock.match(valuePattern);
72
73
  const value = valueMatch ? valueMatch[1] : subName;
@@ -81,7 +82,8 @@ export function detectSubComponents(filePath: string): SubComponentConfig[] {
81
82
  // Look for function signature or interface for this sub-component
82
83
  // Pattern: function SubName({ ... }: SubNameProps) or function SubName(props: SubNameProps)
83
84
  const funcPropsPattern = new RegExp(
84
- `function\\s+${value}\\s*(?:<[^>]*>)?\\s*\\([^)]*:\\s*(\\w+Props)`
85
+ `function\\s+${value}\\s*(?:<[^>]*>)?\\s*\\([^)]*:\\s*(\\w+Props)`,
86
+ "u"
85
87
  );
86
88
  const funcMatch = content.match(funcPropsPattern);
87
89
  if (funcMatch) {
@@ -90,7 +92,8 @@ export function detectSubComponents(filePath: string): SubComponentConfig[] {
90
92
 
91
93
  // Also check for inline type in PropsWithChildren pattern
92
94
  const propsWithChildrenPattern = new RegExp(
93
- `function\\s+${value}\\s*\\([^)]*:\\s*PropsWithChildren<\\{([^}]+)\\}>`
95
+ `function\\s+${value}\\s*\\([^)]*:\\s*PropsWithChildren<\\{([^}]+)\\}>`,
96
+ "u"
94
97
  );
95
98
  const pwcMatch = content.match(propsWithChildrenPattern);
96
99
  if (pwcMatch && !propsType) {
@@ -122,7 +125,7 @@ export function detectSubComponents(filePath: string): SubComponentConfig[] {
122
125
 
123
126
  // Pattern 2: Direct property assignment at module level (e.g., Breadcrumb.Link = Link)
124
127
  // Must be at start of line (module level) and sub-component name must be PascalCase
125
- const directAssignPattern = /^([A-Z]\w+)\.([A-Z]\w+)\s*=\s*(\w+)\s*;/gm;
128
+ const directAssignPattern = /^([A-Z]\w+)\.([A-Z]\w+)\s*=\s*(\w+)\s*;/gmu;
126
129
  let directMatch: RegExpExecArray | null;
127
130
 
128
131
  while ((directMatch = directAssignPattern.exec(content)) !== null) {
@@ -142,7 +145,8 @@ export function detectSubComponents(filePath: string): SubComponentConfig[] {
142
145
  // Try to find props type for this sub-component
143
146
  let propsType: string | null = null;
144
147
  const funcPropsPattern = new RegExp(
145
- `function\\s+${value}\\s*(?:<[^>]*>)?\\s*\\([^)]*:\\s*(\\w+Props)`
148
+ `function\\s+${value}\\s*(?:<[^>]*>)?\\s*\\([^)]*:\\s*(\\w+Props)`,
149
+ "u"
146
150
  );
147
151
  const funcMatch = content.match(funcPropsPattern);
148
152
  if (funcMatch) {
@@ -195,11 +199,13 @@ export function extractSubComponentProps(
195
199
  const inlinePropsPatterns = [
196
200
  // function Name({ destructured }: { inline props })
197
201
  new RegExp(
198
- `(?:function|const)\\s+${funcName}\\s*=?\\s*\\([^)]*:\\s*(?:PropsWithChildren<)?\\{([^}]+)\\}`
202
+ `(?:function|const)\\s+${funcName}\\s*=?\\s*\\([^)]*:\\s*(?:PropsWithChildren<)?\\{([^}]+)\\}`,
203
+ "u"
199
204
  ),
200
205
  // function Name({ destructured }: PropsWithChildren<InterfaceName>)
201
206
  new RegExp(
202
- `(?:function|const)\\s+${funcName}\\s*=?\\s*\\([^)]*:\\s*PropsWithChildren<(\\w+)>`
207
+ `(?:function|const)\\s+${funcName}\\s*=?\\s*\\([^)]*:\\s*PropsWithChildren<(\\w+)>`,
208
+ "u"
203
209
  ),
204
210
  ];
205
211
 
@@ -209,7 +215,7 @@ export function extractSubComponentProps(
209
215
  const propsBlock = match[1];
210
216
 
211
217
  // Check if it's an interface name (single word) or inline props
212
- if (/^\w+$/.test(propsBlock)) {
218
+ if (/^\w+$/u.test(propsBlock)) {
213
219
  // It's an interface name, try to find and parse it
214
220
  const interfaceProps = extractPropsFromInterface(
215
221
  content,
@@ -219,7 +225,7 @@ export function extractSubComponentProps(
219
225
  Object.assign(props, interfaceProps);
220
226
  } else {
221
227
  // Parse inline props: propName?: Type or propName: Type
222
- const propPattern = /(\w+)(\?)?:\s*([^;,\n}]+)/g;
228
+ const propPattern = /(\w+)(\?)?:\s*([^;,\n}]+)/gu;
223
229
  let propMatch: RegExpExecArray | null;
224
230
 
225
231
  while ((propMatch = propPattern.exec(propsBlock)) !== null) {
@@ -228,7 +234,7 @@ export function extractSubComponentProps(
228
234
  let propType = propMatch[3].trim();
229
235
 
230
236
  // Clean up type
231
- propType = propType.replace(/[,;]$/, "").trim();
237
+ propType = propType.replace(/[,;]$/u, "").trim();
232
238
 
233
239
  if (shouldSkipProp(propName, cliFlags)) continue;
234
240
 
@@ -263,9 +269,9 @@ export function extractSubComponentProps(
263
269
  // This handles multi-line destructuring patterns
264
270
  const funcDefPatterns = [
265
271
  // Arrow function: const Name = (...)
266
- new RegExp(`const\\s+${funcName}\\s*=\\s*\\([\\s\\S]*?\\)\\s*=>`),
272
+ new RegExp(`const\\s+${funcName}\\s*=\\s*\\([\\s\\S]*?\\)\\s*=>`, "u"),
267
273
  // Regular function: function Name(...)
268
- new RegExp(`function\\s+${funcName}\\s*\\([\\s\\S]*?\\)\\s*\\{`),
274
+ new RegExp(`function\\s+${funcName}\\s*\\([\\s\\S]*?\\)\\s*\\{`, "u"),
269
275
  ];
270
276
 
271
277
  for (const defPattern of funcDefPatterns) {
@@ -275,9 +281,9 @@ export function extractSubComponentProps(
275
281
 
276
282
  // Extract PropsWithChildren<InterfaceName> or direct interface reference
277
283
  const typePatterns = [
278
- /PropsWithChildren<(\w+)>/,
279
- /:\s*(\w+Props)\s*\)/,
280
- /:\s*(\w+ItemProps)\s*\)/,
284
+ /PropsWithChildren<(\w+)>/u,
285
+ /:\s*(\w+Props)\s*\)/u,
286
+ /:\s*(\w+ItemProps)\s*\)/u,
281
287
  ];
282
288
 
283
289
  for (const typePattern of typePatterns) {
@@ -319,15 +325,21 @@ export function extractPropsFromInterface(
319
325
  ): Record<string, PropSchema> {
320
326
  const props: Record<string, PropSchema> = {};
321
327
 
322
- // Match interface definition
328
+ // Match interface definition, or a type alias whose definition contains an
329
+ // inline object literal (e.g. `type FooProps = ComponentProps<X> & { ... }`).
323
330
  const interfacePattern = new RegExp(
324
- `interface\\s+${interfaceName}\\s*(?:extends[^{]*)?\\{([^}]+)\\}`
331
+ `interface\\s+${interfaceName}\\s*(?:extends[^{]*)?\\{([^}]+)\\}`,
332
+ "u"
325
333
  );
326
- const match = content.match(interfacePattern);
334
+ const typePattern = new RegExp(
335
+ `type\\s+${interfaceName}\\s*=[^{]*\\{([^}]+)\\}`,
336
+ "u"
337
+ );
338
+ const match = content.match(interfacePattern) ?? content.match(typePattern);
327
339
 
328
340
  if (match) {
329
341
  const propsBlock = match[1];
330
- const propPattern = /(\w+)(\?)?:\s*([^;,\n}]+)/g;
342
+ const propPattern = /(\w+)(\?)?:\s*([^;,\n}]+)/gu;
331
343
  let propMatch: RegExpExecArray | null;
332
344
 
333
345
  while ((propMatch = propPattern.exec(propsBlock)) !== null) {
@@ -335,7 +347,7 @@ export function extractPropsFromInterface(
335
347
  const isOptional = propMatch[2] === "?";
336
348
  let propType = propMatch[3].trim();
337
349
 
338
- propType = propType.replace(/[,;]$/, "").trim();
350
+ propType = propType.replace(/[,;]$/u, "").trim();
339
351
 
340
352
  if (shouldSkipProp(propName, cliFlags)) continue;
341
353
 
@@ -31,8 +31,8 @@ export function toPascalCase(str: string): string {
31
31
  */
32
32
  export function toScreamingSnakeCase(str: string): string {
33
33
  return str
34
- .replace(/([a-z])([A-Z])/g, "$1_$2")
35
- .replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2")
34
+ .replace(/([a-z])([A-Z])/gu, "$1_$2")
35
+ .replace(/([A-Z]+)([A-Z][a-z])/gu, "$1_$2")
36
36
  .toUpperCase();
37
37
  }
38
38
 
@@ -90,7 +90,7 @@ export function parseSemanticColorNames(): string[] {
90
90
  // Pattern: "--color-<name>: light-dark(" or "--text-color-<name>: light-dark("
91
91
  // This excludes raw palette colors which are defined with direct values like "oklch(...)"
92
92
  const semanticColorPattern =
93
- /--(?:text-)?color-([a-zA-Z][a-zA-Z0-9-]*)\s*:\s*light-dark\(/g;
93
+ /--(?:text-)?color-([a-zA-Z][a-zA-Z0-9-]*)\s*:\s*light-dark\(/gu;
94
94
  let match: RegExpExecArray | null;
95
95
 
96
96
  while ((match = semanticColorPattern.exec(content)) !== null) {
@@ -129,7 +129,7 @@ export function extractSemanticColors(sourceFile: string): string[] {
129
129
  // Escape hyphens for regex and create pattern
130
130
  const pattern = new RegExp(
131
131
  `(?:[\\w\\[\\]=_-]+:)*(${prefix}-${colorName})(?![a-zA-Z0-9-])`,
132
- "g"
132
+ "gu"
133
133
  );
134
134
  let match: RegExpExecArray | null;
135
135
  while ((match = pattern.exec(content)) !== null) {
@@ -159,19 +159,19 @@ export function extractStateClasses(
159
159
  const states: Record<string, string> = {};
160
160
 
161
161
  // Split by whitespace to process each class individually
162
- const classes = classString.split(/\s+/);
162
+ const classes = classString.split(/\s+/u);
163
163
 
164
164
  for (const cls of classes) {
165
165
  if (!cls) continue;
166
166
 
167
167
  // Check for hover states
168
- if (cls.startsWith("hover:") || /^\[&:hover[^\]]*\]:/.exec(cls)) {
168
+ if (cls.startsWith("hover:") || /^\[&:hover[^\]]*\]:/u.exec(cls)) {
169
169
  states.hover = states.hover ? `${states.hover} ${cls}` : cls;
170
170
  }
171
171
  // Check for focus states (focus, focus-visible, focus-within)
172
172
  else if (
173
- /^(focus|focus-visible|focus-within):/.exec(cls) ||
174
- /^\[&:focus(-visible|-within)?[^\]]*\]:/.exec(cls)
173
+ /^(focus|focus-visible|focus-within):/u.exec(cls) ||
174
+ /^\[&:focus(-visible|-within)?[^\]]*\]:/u.exec(cls)
175
175
  ) {
176
176
  states.focus = states.focus ? `${states.focus} ${cls}` : cls;
177
177
  }
@@ -190,7 +190,7 @@ export function extractStateClasses(
190
190
  : cls;
191
191
  }
192
192
  // Check for data-state
193
- else if (/^data-\[state=[^\]]+\]:/.test(cls)) {
193
+ else if (/^data-\[state=[^\]]+\]:/u.test(cls)) {
194
194
  states["data-state"] = states["data-state"]
195
195
  ? `${states["data-state"]} ${cls}`
196
196
  : cls;
@@ -218,7 +218,7 @@ export function extractBlockDependencies(sourceFile: string): string[] {
218
218
  // Pattern: import { Foo, Bar } from "../../components/something"
219
219
  // Pattern: import { Foo } from "../../blocks/something"
220
220
  const importPattern =
221
- /import\s+(?:type\s+)?\{([^}]+)\}\s+from\s+["']\.\.\/\.\.\/(?:components|blocks)\/[^"']+["']/g;
221
+ /import\s+(?:type\s+)?\{([^}]+)\}\s+from\s+["']\.\.\/\.\.\/(?:components|blocks)\/[^"']+["']/gu;
222
222
  let match: RegExpExecArray | null;
223
223
 
224
224
  while ((match = importPattern.exec(content)) !== null) {
@@ -235,7 +235,7 @@ export function extractBlockDependencies(sourceFile: string): string[] {
235
235
  continue;
236
236
  }
237
237
  // Extract name (handle "Foo as Bar" -> "Foo")
238
- const nameMatch = /^(\w+)(?:\s+as\s+\w+)?/.exec(item);
238
+ const nameMatch = /^(\w+)(?:\s+as\s+\w+)?/u.exec(item);
239
239
  if (nameMatch) {
240
240
  dependencies.add(nameMatch[1]);
241
241
  }
@@ -36,7 +36,7 @@ export function extractBaseStylesFromFile(filePath: string): string | null {
36
36
  // Match: export const SF_*_BASE_STYLES = "..." or '...' or `...`
37
37
  // Handles multi-line strings with template literals
38
38
  const baseStylesMatch =
39
- /export\s+const\s+SF_\w+_BASE_STYLES\s*=\s*["'`]([^"'`]+)["'`]/.exec(
39
+ /export\s+const\s+SF_\w+_BASE_STYLES\s*=\s*["'`]([^"'`]+)["'`]/u.exec(
40
40
  content
41
41
  );
42
42
 
@@ -67,7 +67,7 @@ export function parseVariantsObject(
67
67
 
68
68
  // Find top-level property names (e.g., shape, size, variant)
69
69
  // These are identifiers followed by `: {` at the first nesting level
70
- const topLevelPropPattern = /^\s*(\w+)\s*:\s*\{/gm;
70
+ const topLevelPropPattern = /^\s*(\w+)\s*:\s*\{/gmu;
71
71
  let propMatch: RegExpExecArray | null;
72
72
 
73
73
  while ((propMatch = topLevelPropPattern.exec(objStr)) !== null) {
@@ -86,7 +86,7 @@ export function parseVariantsObject(
86
86
  // biome-ignore lint/suspicious/noExplicitAny: Variants have varying shapes
87
87
  const variants: Record<string, any> = {};
88
88
  // Match variant names including quoted keys like "secondary-destructive"
89
- const variantPropPattern = /^\s*(?:"([^"]+)"|'([^']+)'|(\w+))\s*:\s*\{/gm;
89
+ const variantPropPattern = /^\s*(?:"([^"]+)"|'([^']+)'|(\w+))\s*:\s*\{/gmu;
90
90
  let variantMatch: RegExpExecArray | null;
91
91
 
92
92
  while ((variantMatch = variantPropPattern.exec(propBlock)) !== null) {
@@ -103,9 +103,11 @@ export function parseVariantsObject(
103
103
  if (!variantBlock) continue;
104
104
 
105
105
  // Extract description if present
106
- const descMatch = /description\s*:\s*["']([^"']*)["']/.exec(variantBlock);
106
+ const descMatch = /description\s*:\s*["']([^"']*)["']/u.exec(
107
+ variantBlock
108
+ );
107
109
  // Extract classes if present (for Figma plugin consumption)
108
- const classesMatch = /classes\s*:\s*["']([^"']*)["']/.exec(variantBlock);
110
+ const classesMatch = /classes\s*:\s*["']([^"']*)["']/u.exec(variantBlock);
109
111
 
110
112
  // Extract state classes from the classes string
111
113
  const stateClasses = classesMatch
@@ -134,7 +136,7 @@ export function parseDefaultsObject(objStr: string): Record<string, string> {
134
136
  const result: Record<string, string> = {};
135
137
 
136
138
  // Match properties like: variant: "primary", size: "base"
137
- const propPattern = /(\w+)\s*:\s*["']([^"']*)["']/g;
139
+ const propPattern = /(\w+)\s*:\s*["']([^"']*)["']/gu;
138
140
  let match: RegExpExecArray | null;
139
141
 
140
142
  while ((match = propPattern.exec(objStr)) !== null) {
@@ -159,12 +161,12 @@ export function extractVariantsFromFile(
159
161
  const content = readFileSync(filePath, "utf-8");
160
162
 
161
163
  // Find SF_*_VARIANTS export start position
162
- const variantsStartMatch = /export\s+const\s+SF_\w+_VARIANTS\s*=\s*/.exec(
164
+ const variantsStartMatch = /export\s+const\s+SF_\w+_VARIANTS\s*=\s*/u.exec(
163
165
  content
164
166
  );
165
167
  // Find SF_*_DEFAULT_VARIANTS export start position
166
168
  const defaultsStartMatch =
167
- /export\s+const\s+SF_\w+_DEFAULT_VARIANTS\s*=\s*/.exec(content);
169
+ /export\s+const\s+SF_\w+_DEFAULT_VARIANTS\s*=\s*/u.exec(content);
168
170
 
169
171
  if (!variantsStartMatch || !defaultsStartMatch) {
170
172
  return null;
@@ -212,7 +214,7 @@ export function extractVariantsFromFile(
212
214
  function parseStylingObject(objStr: string): ComponentStyling | null {
213
215
  try {
214
216
  // Remove 'as const' suffix if present
215
- const cleanedStr = objStr.replace(/\s*as\s+const\s*$/, "");
217
+ const cleanedStr = objStr.replace(/\s*as\s+const\s*$/u, "");
216
218
 
217
219
  // Security: Parse as JSON-like structure instead of executing code
218
220
  // This prevents RCE if a source file is compromised
@@ -234,14 +236,14 @@ function parseObjectLiteralSafely(
234
236
  const result: Record<string, unknown> = {};
235
237
 
236
238
  // Remove outer braces
237
- const content = objStr.trim().replace(/^\{/, "").replace(/\}$/, "").trim();
239
+ const content = objStr.trim().replace(/^\{/u, "").replace(/\}$/u, "").trim();
238
240
  if (!content) return {};
239
241
 
240
242
  // Split by top-level commas (not inside nested braces)
241
243
  const properties = splitTopLevelProperties(content);
242
244
 
243
245
  for (const prop of properties) {
244
- const match = /^\s*(\w+)\s*:\s*(.+)$/s.exec(prop);
246
+ const match = /^\s*(\w+)\s*:\s*(.+)$/su.exec(prop);
245
247
  if (!match) continue;
246
248
 
247
249
  const key = match[1];
@@ -301,20 +303,20 @@ function parseValue(valueStr: string): unknown {
301
303
  if (valueStr.startsWith("[")) {
302
304
  try {
303
305
  // Arrays are typically simple in styling configs
304
- return JSON.parse(valueStr.replace(/'/g, '"'));
306
+ return JSON.parse(valueStr.replace(/'/gu, '"'));
305
307
  } catch {
306
308
  return null;
307
309
  }
308
310
  }
309
311
 
310
312
  // Handle strings (single or double quotes)
311
- const stringMatch = /^["'](.+)["']$/s.exec(valueStr);
313
+ const stringMatch = /^["'](.+)["']$/su.exec(valueStr);
312
314
  if (stringMatch) {
313
315
  return stringMatch[1];
314
316
  }
315
317
 
316
318
  // Handle numbers
317
- if (/^-?\d+(\.\d+)?$/.test(valueStr)) {
319
+ if (/^-?\d+(\.\d+)?$/u.test(valueStr)) {
318
320
  return Number(valueStr);
319
321
  }
320
322
 
@@ -343,7 +345,7 @@ export function extractStylingFromFile(
343
345
  const content = readFileSync(filePath, "utf-8");
344
346
 
345
347
  // Find SF_*_STYLING export start position
346
- const stylingStartMatch = /export\s+const\s+SF_\w+_STYLING\s*=\s*/.exec(
348
+ const stylingStartMatch = /export\s+const\s+SF_\w+_STYLING\s*=\s*/u.exec(
347
349
  content
348
350
  );
349
351
 
@@ -113,13 +113,13 @@ function collectEntries(): Entry[] {
113
113
  function extractExportedDemoFunctions(source: string): string[] {
114
114
  // Strip template literal contents so we don't match `export function`
115
115
  // inside code strings.
116
- const cleaned = source.replace(/`(?:\\.|[^`\\])*`/g, "``");
116
+ const cleaned = source.replace(/`(?:\\.|[^`\\])*`/gu, "``");
117
117
 
118
118
  const names = new Set<string>();
119
119
 
120
120
  // Only match at start of a line (after optional whitespace) to avoid
121
121
  // matching inside expressions/strings.
122
- const fnRegex = /^\s*export\s+function\s+([A-Z][A-Za-z0-9_]*)\s*\(/gm;
122
+ const fnRegex = /^\s*export\s+function\s+([A-Z][A-Za-z0-9_]*)\s*\(/gmu;
123
123
  let m: RegExpExecArray | null = fnRegex.exec(cleaned);
124
124
  while (m) {
125
125
  names.add(m[1]);
@@ -127,7 +127,7 @@ function extractExportedDemoFunctions(source: string): string[] {
127
127
  }
128
128
 
129
129
  const constRegex =
130
- /^\s*export\s+const\s+([A-Z][A-Za-z0-9_]*)\s*(?::\s*[^=]+)?=\s*(?:\([^)]*\)\s*=>|function)/gm;
130
+ /^\s*export\s+const\s+([A-Z][A-Za-z0-9_]*)\s*(?::\s*[^=]+)?=\s*(?:\([^)]*\)\s*=>|function)/gmu;
131
131
  m = constRegex.exec(cleaned);
132
132
  while (m) {
133
133
  names.add(m[1]);
@@ -161,7 +161,7 @@ function demoImportPath(entry: Entry): string {
161
161
  const up = "../".repeat(depth);
162
162
  const relFromRegistry = path.relative(
163
163
  REGISTRY_ROOT,
164
- entry.demoFile.replace(/\.tsx$/, "")
164
+ entry.demoFile.replace(/\.tsx$/u, "")
165
165
  );
166
166
  return `${up}docs/${relFromRegistry.replace("../", "")}`;
167
167
  }
@@ -170,7 +170,7 @@ function buildDemoImportPath(entry: Entry): string {
170
170
  // Story lives at packages/registry/src/{components|blocks}/{name}/{name}.stories.tsx
171
171
  // Demo lives at packages/docs/src/components/demos/{Name}Demo.tsx
172
172
  // Relative path: ../../../../docs/src/components/demos/{Name}Demo
173
- const demoBase = path.basename(entry.demoFile).replace(/\.tsx$/, "");
173
+ const demoBase = path.basename(entry.demoFile).replace(/\.tsx$/u, "");
174
174
  return `../../../../docs/src/components/demos/${demoBase}`;
175
175
  }
176
176
 
@@ -656,11 +656,7 @@ export const THEME_CONFIG: ThemeConfig = {
656
656
  };
657
657
 
658
658
  /** List of all available themes */
659
- export const AVAILABLE_THEMES = [
660
- "sf",
661
- "fedramp",
662
- "minimal",
663
- ] as const;
659
+ export const AVAILABLE_THEMES = ["sf", "fedramp", "minimal"] as const;
664
660
  export type AvailableTheme = (typeof AVAILABLE_THEMES)[number];
665
661
 
666
662
  /**
@@ -32,7 +32,7 @@ function lightDark(light: string, dark: string): string {
32
32
  }
33
33
 
34
34
  function toThemeFileName(themeName: string): string {
35
- return themeName.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
35
+ return themeName.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase();
36
36
  }
37
37
 
38
38
  /**
@@ -116,7 +116,7 @@ function buildPatterns(renameMap: TokenRenameMap): Array<{
116
116
  patterns.push({
117
117
  pattern: new RegExp(
118
118
  `(${variants})(${prefix}-${escapeRegex(oldName)})(${opacity})(?=\\s|"|'|\`|$|\\))`,
119
- "g"
119
+ "gu"
120
120
  ),
121
121
  replacement: `$1${prefix}-${newName}$3`,
122
122
  });
@@ -129,7 +129,7 @@ function buildPatterns(renameMap: TokenRenameMap): Array<{
129
129
  patterns.push({
130
130
  pattern: new RegExp(
131
131
  `(${variants})(${prefix}-${escapeRegex(oldName)})(${opacity})(?=\\s|"|'|\`|$|\\))`,
132
- "g"
132
+ "gu"
133
133
  ),
134
134
  replacement: `$1${prefix}-${newName}$3`,
135
135
  });
@@ -140,7 +140,7 @@ function buildPatterns(renameMap: TokenRenameMap): Array<{
140
140
  }
141
141
 
142
142
  function escapeRegex(str: string): string {
143
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
143
+ return str.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
144
144
  }
145
145
 
146
146
  /**
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-agent-card-BR2NIYhi.js","names":[],"sources":["../src/components/ai-agent-card/ai-agent-card.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n BrainIcon,\n CheckCircleIcon,\n CircleIcon,\n CodeIcon,\n MagnifyingGlassIcon,\n RobotIcon,\n SpinnerGapIcon,\n WrenchIcon,\n XCircleIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps, ElementType } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_AGENT_CARD_VARIANTS = {\n status: {\n idle: { classes: \"\", description: \"Agent is idle, not yet started\" },\n running: { classes: \"\", description: \"Agent is actively working\" },\n completed: { classes: \"\", description: \"Agent finished successfully\" },\n error: { classes: \"\", description: \"Agent encountered an error\" },\n },\n size: {\n sm: { classes: \"\", description: \"Compact card for dense grids\" },\n md: { classes: \"\", description: \"Default card size\" },\n },\n} as const;\n\nexport const SF_AI_AGENT_CARD_DEFAULT_VARIANTS = {\n status: \"idle\",\n size: \"md\",\n} as const;\n\nexport type SFAiAgentCardStatus = keyof typeof SF_AI_AGENT_CARD_VARIANTS.status;\nexport type SFAiAgentCardSize = keyof typeof SF_AI_AGENT_CARD_VARIANTS.size;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AiAgentCardProps = Omit<ComponentProps<\"button\">, \"children\"> & {\n /** Human-readable agent name (e.g. \"Explore\", \"Execute\"). */\n name: string;\n /** Agent type ID — used for icon mapping. */\n agentType?: string;\n /** Current status. @default \"idle\" */\n status?: SFAiAgentCardStatus;\n /** Model ID being used (e.g. \"claude-haiku-3.5\"). */\n modelId?: string;\n /** What the agent is currently doing. */\n currentTask?: string;\n /** Total elapsed duration in ms. */\n duration?: number;\n /** Number of tool calls made. */\n toolCallCount?: number;\n /** Whether this card is currently selected/active. */\n selected?: boolean;\n /** Custom icon override. */\n icon?: ElementType;\n /** Card size. @default \"md\" */\n size?: SFAiAgentCardSize;\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nconst AGENT_TYPE_ICONS: Record<string, ElementType> = {\n explore: MagnifyingGlassIcon,\n search: MagnifyingGlassIcon,\n execute: CodeIcon,\n code: CodeIcon,\n plan: BrainIcon,\n think: BrainIcon,\n tool: WrenchIcon,\n build: WrenchIcon,\n};\n\nfunction getAgentIcon(\n agentType?: string,\n customIcon?: ElementType\n): ElementType {\n if (customIcon) return customIcon;\n if (agentType) {\n const lower = agentType.toLowerCase();\n for (const [key, icon] of Object.entries(AGENT_TYPE_ICONS)) {\n if (lower.includes(key)) return icon;\n }\n }\n return RobotIcon;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const s = Math.round(ms / 100) / 10;\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const rem = Math.round(s % 60);\n return `${m}m ${rem}s`;\n}\n\nfunction getModelShortName(modelId?: string): string {\n if (!modelId) return \"\";\n return (\n modelId\n .split(\"/\")\n .pop()\n ?.replace(/^claude-/, \"\")\n .replace(/-\\d+$/, \"\") ?? modelId\n );\n}\n\n// ─── Status decorations ───────────────────────────────────────────────────────\n\nconst STATUS_DOT: Record<SFAiAgentCardStatus, string> = {\n idle: \"bg-sf-fill\",\n running: \"bg-sf-brand animate-pulse\",\n completed: \"bg-sf-success\",\n error: \"bg-sf-danger\",\n};\n\nfunction StatusIcon({\n status,\n size = 14,\n}: {\n status: SFAiAgentCardStatus;\n size?: number;\n}) {\n switch (status) {\n case \"running\":\n return (\n <SpinnerGapIcon size={size} className=\"animate-spin text-sf-brand\" />\n );\n case \"completed\":\n return <CheckCircleIcon size={size} className=\"text-sf-success\" />;\n case \"error\":\n return <XCircleIcon size={size} className=\"text-sf-danger\" />;\n default:\n return <CircleIcon size={size} className=\"text-sf-inactive\" />;\n }\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * `AiAgentCard` — compact card showing one agent's status in a commander dashboard.\n *\n * Displays: agent icon, name, status indicator, current task, model, duration,\n * and tool call count. Clickable for selection.\n *\n * @example\n * ```tsx\n * <AiAgentCard\n * name=\"Explore\"\n * agentType=\"explore\"\n * status=\"running\"\n * modelId=\"claude-haiku-3.5\"\n * currentTask=\"Scanning auth files...\"\n * duration={4200}\n * toolCallCount={3}\n * selected\n * onClick={handleSelect}\n * />\n * ```\n */\nexport const AiAgentCard = forwardRef<HTMLButtonElement, AiAgentCardProps>(\n (\n {\n name,\n agentType,\n status = SF_AI_AGENT_CARD_DEFAULT_VARIANTS.status,\n modelId,\n currentTask,\n duration,\n toolCallCount,\n selected,\n icon,\n size = SF_AI_AGENT_CARD_DEFAULT_VARIANTS.size,\n className,\n onClick,\n ...props\n },\n ref\n ) => {\n const Icon = getAgentIcon(agentType, icon);\n const modelShort = getModelShortName(modelId);\n const isSm = size === \"sm\";\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-pressed={selected}\n onClick={onClick}\n className={cn(\n \"group flex flex-col gap-2 rounded-xl border text-left transition-all\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sf-ring focus-visible:ring-offset-1\",\n isSm ? \"p-2.5\" : \"p-3.5\",\n selected\n ? \"border-sf-line bg-sf-recessed shadow-sm\"\n : \"border-sf-line bg-sf-elevated hover:border-sf-line hover:bg-sf-tint\",\n !onClick && \"cursor-default\",\n className\n )}\n {...props}\n >\n {/* Header row */}\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-center gap-2 min-w-0\">\n {/* Agent icon */}\n <div\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg\",\n isSm ? \"size-6\" : \"size-8\",\n status === \"running\"\n ? \"bg-sf-brand/10 text-sf-brand\"\n : status === \"completed\"\n ? \"bg-sf-tint text-sf-success\"\n : status === \"error\"\n ? \"bg-sf-tint text-sf-danger\"\n : \"bg-sf-fill text-sf-subtle\"\n )}\n >\n <Icon size={isSm ? 12 : 16} />\n </div>\n\n {/* Name */}\n <span\n className={cn(\n \"truncate font-medium text-sf-default\",\n isSm ? \"text-xs\" : \"text-sm\"\n )}\n >\n {name}\n </span>\n </div>\n\n {/* Status icon */}\n <StatusIcon status={status} size={isSm ? 12 : 14} />\n </div>\n\n {/* Current task */}\n {currentTask && (\n <p\n className={cn(\n \"truncate text-sf-subtle leading-snug\",\n isSm ? \"text-[10px]\" : \"text-xs\"\n )}\n >\n {currentTask}\n </p>\n )}\n\n {/* Footer row: model + stats */}\n {!isSm && (\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex items-center gap-1.5\">\n {/* Status dot + model */}\n <span\n className={cn(\n \"size-1.5 shrink-0 rounded-full\",\n STATUS_DOT[status]\n )}\n />\n {modelShort && (\n <span className=\"font-mono text-[10px] text-sf-inactive\">\n {modelShort}\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-2 text-[10px] text-sf-subtle\">\n {typeof toolCallCount === \"number\" && toolCallCount > 0 && (\n <span className=\"flex items-center gap-0.5\">\n <WrenchIcon size={9} />\n {toolCallCount}\n </span>\n )}\n {typeof duration === \"number\" && (\n <span>{formatDuration(duration)}</span>\n )}\n </div>\n </div>\n )}\n </button>\n );\n }\n);\n\nAiAgentCard.displayName = \"AiAgentCard\";\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-code-block-CZtoL73R.js","names":[],"sources":["../src/components/ai-code-block/ai-code-block.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, HTMLAttributes, ReactNode } from \"react\";\nimport { createContext, memo, useContext, useState } from \"react\";\nimport { highlight } from \"sugar-high\";\n\nimport { cn } from \"../../utils/cn\";\nimport { highlightToLines } from \"../../utils/highlight-to-react\";\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_CODE_BLOCK_VARIANTS = {} as const;\nexport const SF_AI_CODE_BLOCK_DEFAULT_VARIANTS = {} as const;\n\n// ─── Context ─────────────────────────────────────────────────────────────────\n\ninterface AiCodeBlockContextValue {\n code: string;\n}\n\nconst AiCodeBlockContext = createContext<AiCodeBlockContextValue>({ code: \"\" });\n\n// ─── AiCodeBlock ─────────────────────────────────────────────────────────────\n\nexport type AiCodeBlockProps = HTMLAttributes<HTMLDivElement> & {\n /** The raw code string to display and make copyable. */\n code: string;\n /** Language identifier for syntax highlighting (display only, no highlighting applied). */\n language?: string;\n /** Show line numbers. @default false */\n showLineNumbers?: boolean;\n /** Extra content rendered in the top-right overlay (e.g. copy button). */\n children?: ReactNode;\n};\n\n/**\n * Displays a code block with monospace formatting and an optional copy button.\n * Does not include a syntax highlighter — avoids the heavy `react-syntax-highlighter`\n * dependency. Wrap with `AiCodeBlockCopyButton` for clipboard support.\n *\n * @example\n * ```tsx\n * <AiCodeBlock code={`const x = 1;`} language=\"ts\">\n * <AiCodeBlockCopyButton />\n * </AiCodeBlock>\n * ```\n */\nexport const AiCodeBlock = memo(\n ({\n code,\n language,\n showLineNumbers = false,\n className,\n children,\n ...props\n }: AiCodeBlockProps) => {\n const highlightedLines = highlightToLines(highlight(code));\n\n return (\n <AiCodeBlockContext.Provider value={{ code }}>\n <div\n className={cn(\n \"relative w-full overflow-hidden rounded-lg border border-sf-line bg-sf-recessed font-mono text-sm text-sf-default\",\n className\n )}\n {...props}\n >\n {language && (\n <div className=\"flex items-center justify-between border-b border-sf-line px-4 py-2\">\n <span className=\"text-sf-subtle text-xs\">{language}</span>\n {children && (\n <div className=\"flex items-center gap-2\">{children}</div>\n )}\n </div>\n )}\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse\">\n <tbody>\n {highlightedLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: line index is stable\n <tr key={i} className=\"leading-6\">\n {showLineNumbers && (\n <td className=\"select-none pr-4 pl-4 text-right text-sf-inactive\">\n {i + 1}\n </td>\n )}\n <td className={cn(\"pr-4\", !showLineNumbers && \"px-4\")}>\n <pre className=\"whitespace-pre\">{line}</pre>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n {!language && children && (\n <div className=\"absolute top-2 right-2 flex items-center gap-2\">\n {children}\n </div>\n )}\n </div>\n </AiCodeBlockContext.Provider>\n );\n },\n (prev, next) =>\n prev.code === next.code &&\n prev.language === next.language &&\n prev.showLineNumbers === next.showLineNumbers\n);\n\nAiCodeBlock.displayName = \"AiCodeBlock\";\n\n// ─── AiCodeBlockCopyButton ───────────────────────────────────────────────────\n\nexport type AiCodeBlockCopyButtonProps = ComponentProps<typeof Button> & {\n /** Called after a successful copy. */\n onCopy?: () => void;\n /** Called if the clipboard API is unavailable or errors. */\n onError?: (error: Error) => void;\n /** How long (ms) to show the \"copied\" state. @default 2000 */\n timeout?: number;\n};\n\n/**\n * Copy-to-clipboard button for use inside `AiCodeBlock`.\n * Must be a descendant of `AiCodeBlock` to access the code via context.\n *\n * @example\n * ```tsx\n * <AiCodeBlock code=\"const x = 1;\" language=\"ts\">\n * <AiCodeBlockCopyButton />\n * </AiCodeBlock>\n * ```\n */\nexport function AiCodeBlockCopyButton({\n onCopy,\n onError,\n timeout = 2000,\n children,\n className,\n size = \"sm\",\n variant = \"ghost\",\n ...props\n}: AiCodeBlockCopyButtonProps) {\n const [copied, setCopied] = useState(false);\n const { code } = useContext(AiCodeBlockContext);\n\n const handleCopy = async () => {\n if (typeof navigator === \"undefined\" || !navigator.clipboard?.writeText) {\n onError?.(new Error(\"Clipboard API not available\"));\n return;\n }\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n onCopy?.();\n setTimeout(() => setCopied(false), timeout);\n } catch (err) {\n onError?.(err as Error);\n }\n };\n\n return (\n <Button\n className={cn(\"shrink-0\", className)}\n onClick={handleCopy}\n size={size}\n variant={variant}\n {...props}\n >\n {children ?? (copied ? \"Copied\" : \"Copy\")}\n </Button>\n );\n}\n"],"mappings":";;;;;;;;AAYA,IAAa,4BAA4B,EAAE;AAC3C,IAAa,oCAAoC,EAAE;AAQnD,IAAM,qBAAqB,cAAuC,EAAE,MAAM,IAAI,CAAC;;;;;;;;;;;;;AA2B/E,IAAa,cAAc,MACxB,EACC,MACA,UACA,kBAAkB,OAClB,WACA,UACA,GAAG,YACmB;CACtB,MAAM,mBAAmB,iBAAiB,UAAU,KAAK,CAAC;AAE1D,QACE,oBAAC,mBAAmB,UAApB;EAA6B,OAAO,EAAE,MAAM;YAC1C,qBAAC,OAAD;GACE,WAAW,GACT,qHACA,UACD;GACD,GAAI;aALN;IAOG,YACC,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,QAAD;MAAM,WAAU;gBAA0B;MAAgB,CAAA,EACzD,YACC,oBAAC,OAAD;MAAK,WAAU;MAA2B;MAAe,CAAA,CAEvD;;IAER,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,SAAD;MAAO,WAAU;gBACf,oBAAC,SAAD,EAAA,UACG,iBAAiB,KAAK,MAAM,MAE3B,qBAAC,MAAD;OAAY,WAAU;iBAAtB,CACG,mBACC,oBAAC,MAAD;QAAI,WAAU;kBACX,IAAI;QACF,CAAA,EAEP,oBAAC,MAAD;QAAI,WAAW,GAAG,QAAQ,CAAC,mBAAmB,OAAO;kBACnD,oBAAC,OAAD;SAAK,WAAU;mBAAkB;SAAW,CAAA;QACzC,CAAA,CACF;SATI,EASJ,CACL,EACI,CAAA;MACF,CAAA;KACJ,CAAA;IACL,CAAC,YAAY,YACZ,oBAAC,OAAD;KAAK,WAAU;KACZ;KACG,CAAA;IAEJ;;EACsB,CAAA;IAGjC,MAAM,SACL,KAAK,SAAS,KAAK,QACnB,KAAK,aAAa,KAAK,YACvB,KAAK,oBAAoB,KAAK,gBACjC;AAED,YAAY,cAAc;;;;;;;;;;;;AAwB1B,SAAgB,sBAAsB,EACpC,QACA,SACA,UAAU,KACV,UACA,WACA,OAAO,MACP,UAAU,SACV,GAAG,SAC0B;CAC7B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,EAAE,SAAS,WAAW,mBAAmB;CAE/C,MAAM,aAAa,YAAY;AAC7B,MAAI,OAAO,cAAc,eAAe,CAAC,UAAU,WAAW,WAAW;AACvE,6BAAU,IAAI,MAAM,8BAA8B,CAAC;AACnD;;AAEF,MAAI;AACF,SAAM,UAAU,UAAU,UAAU,KAAK;AACzC,aAAU,KAAK;AACf,aAAU;AACV,oBAAiB,UAAU,MAAM,EAAE,QAAQ;WACpC,KAAK;AACZ,aAAU,IAAa;;;AAI3B,QACE,oBAAC,QAAD;EACE,WAAW,GAAG,YAAY,UAAU;EACpC,SAAS;EACH;EACG;EACT,GAAI;YAEH,aAAa,SAAS,WAAW;EAC3B,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-conversation-Cc7WlaBg.js","names":[],"sources":["../src/components/ai-conversation/ai-conversation.tsx","../src/components/ai-conversation/measurement-constants.ts"],"sourcesContent":["\"use client\";\n\nimport { ArrowDownIcon } from \"@phosphor-icons/react\";\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\nimport type { Virtualizer, VirtualItem } from \"@tanstack/react-virtual\";\nimport { layout, prepare } from \"@chenglou/pretext\";\nimport type { ComponentProps, ReactNode, RefObject } from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\nimport { Button } from \"../button\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_CONVERSATION_VARIANTS = {} as const;\nexport const SF_AI_CONVERSATION_DEFAULT_VARIANTS = {} as const;\n\n// ─── AiConversation ──────────────────────────────────────────────────────────\n\nexport type AiConversationProps = ComponentProps<\"div\">;\n\n/**\n * Outer scroll container for a conversation. Sticks to the bottom as new\n * messages are added. Pair with `AiConversationContent` and optionally\n * `AiConversationScrollButton`.\n *\n * @example\n * ```tsx\n * <AiConversation ref={scrollRef}>\n * <AiConversationContent>\n * {messages.map(m => <AiMessage key={m.id} from={m.role}>...</AiMessage>)}\n * </AiConversationContent>\n * <AiConversationScrollButton scrollRef={scrollRef} />\n * </AiConversation>\n * ```\n */\nexport const AiConversation = forwardRef<HTMLDivElement, AiConversationProps>(\n ({ className, children, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\"relative flex-1 overflow-y-auto\", className)}\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Conversation\"\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nAiConversation.displayName = \"AiConversation\";\n\n// ─── AiConversationContent ───────────────────────────────────────────────────\n\nexport type AiConversationContentProps = ComponentProps<\"div\">;\n\n/**\n * Inner content wrapper with padding. Place message components inside here.\n */\nexport function AiConversationContent({\n className,\n children,\n ...props\n}: AiConversationContentProps) {\n return (\n <div\n className={cn(\"flex flex-col gap-0 overscroll-y-none p-4\", className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\n// ─── AiConversationScrollButton ──────────────────────────────────────────────\n\nexport type AiConversationScrollButtonProps = ComponentProps<typeof Button> & {\n /**\n * Ref to the scroll container (the `AiConversation` element).\n * When omitted, the button is always visible.\n */\n scrollRef?: RefObject<HTMLElement | null>;\n /** Scroll threshold in pixels from bottom to show the button. @default 100 */\n threshold?: number;\n};\n\n/**\n * Floating \"scroll to bottom\" button. Appears when the user has scrolled up\n * more than `threshold` pixels from the bottom.\n */\nexport function AiConversationScrollButton({\n scrollRef,\n threshold = 100,\n className,\n onClick,\n ...props\n}: AiConversationScrollButtonProps) {\n const [show, setShow] = useState(false);\n\n useEffect(() => {\n const el = scrollRef?.current;\n if (!el) return;\n\n const check = () => {\n const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;\n setShow(distFromBottom > threshold);\n };\n\n check();\n el.addEventListener(\"scroll\", check, { passive: true });\n return () => el.removeEventListener(\"scroll\", check);\n }, [scrollRef, threshold]);\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n scrollRef?.current?.scrollTo({\n top: scrollRef.current.scrollHeight,\n behavior: \"smooth\",\n });\n onClick?.(e);\n },\n [scrollRef, onClick]\n );\n\n if (scrollRef && !show) return null;\n\n return (\n <Button\n aria-label=\"Scroll to bottom\"\n className={cn(\n \"absolute bottom-4 left-1/2 -translate-x-1/2 rounded-full shadow-md\",\n className\n )}\n onClick={handleClick}\n size=\"sm\"\n variant=\"secondary\"\n {...props}\n >\n <ArrowDownIcon className=\"size-4\" />\n </Button>\n );\n}\n\n// ─── Virtualized Conversation ────────────────────────────────────────────────\n\n/** Measurement spec for predictive height calculation using pretext. */\nexport interface ConversationItemMeasureSpec {\n /** Text content to measure */\n text: string;\n /** CSS font shorthand (e.g. \"14px Inter, sans-serif\"). Auto-detected if omitted. */\n font?: string;\n /** Line height in pixels. Auto-detected if omitted. */\n lineHeight?: number;\n /** Additional vertical padding (top + bottom) in pixels @default 0 */\n padding?: number;\n /**\n * Fraction of column width this item occupies (0-1). Used to compute the\n * actual content width for items that are constrained (e.g. user message\n * bubbles at max-w-[70%]). @default 1\n */\n widthFraction?: number;\n /**\n * Horizontal padding (left + right) inside the content box, subtracted from\n * the available width before line wrapping. @default 0\n */\n horizontalPadding?: number;\n /**\n * Margin below this item (gap to next item). @default 0\n */\n gapAfter?: number;\n /**\n * White-space handling — must match the rendered CSS. Use `\"pre-wrap\"` when\n * the rendered content preserves explicit newlines (e.g. plain text inside\n * a <pre> or `whitespace-pre-wrap` div). @default \"normal\"\n */\n whiteSpace?: \"normal\" | \"pre-wrap\";\n /**\n * Word-break handling — match CSS `word-break`. Use `\"keep-all\"` for CJK\n * text that should not break mid-word. @default \"normal\"\n */\n wordBreak?: \"normal\" | \"keep-all\";\n}\n\n/** A single item in the virtualized conversation list. */\nexport interface ConversationItem {\n /** Unique key for this item. */\n key: string;\n /**\n * Render the item content. The virtualizer handles measurement and\n * positioning on the wrapper — just return the content.\n */\n render: () => ReactNode;\n /**\n * Optional predictive height measurement. When provided, the virtualizer\n * uses pretext to compute exact height without DOM measurement, eliminating\n * layout shift during streaming. Falls back to estimateSize + DOM measurement\n * when omitted.\n *\n * **Attach this only when rendering plain text** (no markdown). If your\n * `render` output uses `<AiResponse>` or any rich-content component whose\n * blocks have varying typography (headings, code, lists, blockquotes), omit\n * `measure` to let the virtualizer use DOM measurement instead. Pretext\n * predicts heights from a single (font, line-height) pair and cannot\n * accurately model multi-block markdown.\n *\n * Plain-text + `measure` gives jitter-free virtualization (no estimate-then-\n * adjust feedback loop). Rich content + DOM measurement is rendering-correct\n * but slightly less smooth on first paint.\n *\n * For streaming items, update `measure.text` as text grows so the virtualizer\n * can recalculate height per tick (rebuild the `items` array with the latest\n * text on each token arrival).\n */\n measure?: ConversationItemMeasureSpec;\n}\n\nexport interface UseConversationVirtualizerOptions {\n /** Ref to the scroll container element. */\n scrollRef: RefObject<HTMLElement | null>;\n /** The conversation items to virtualize. */\n items: ConversationItem[];\n /**\n * Estimated height of a single item in pixels.\n * Used before measurement. Doesn't need to be exact.\n * @default 80\n */\n estimateSize?: number;\n /**\n * Number of items to render beyond the visible area.\n * @default 8\n */\n overscan?: number;\n /**\n * Pixel distance from bottom to consider \"at bottom\" for stick-to-bottom.\n * @default 40\n */\n stickThreshold?: number;\n}\n\nexport interface UseConversationVirtualizerReturn {\n /** The TanStack virtualizer instance. */\n virtualizer: Virtualizer<HTMLElement, Element>;\n /** The virtual items to render. */\n virtualItems: VirtualItem[];\n /** Total measured/estimated height of all items. */\n totalSize: number;\n /** Whether the user is currently at the bottom of the conversation. */\n isAtBottom: boolean;\n /** Smoothly scroll to the bottom. */\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n /**\n * Get the measurement ref for a virtual item by index.\n * @deprecated Use `getMeasureRef(index)` instead to respect predictive heights.\n */\n measureRef: (node: HTMLElement | null) => void;\n /**\n * Get the measurement ref for a specific item index.\n * Returns `null` for items with predictive height (no DOM measurement needed),\n * or the measurement callback for legacy items.\n */\n getMeasureRef: (index: number) => ((node: HTMLElement | null) => void) | null;\n}\n\n/**\n * `useConversationVirtualizer` — virtualizes a conversation list with\n * dynamic item heights and automatic stick-to-bottom behavior.\n *\n * Built on `@tanstack/react-virtual`. Items are measured dynamically via\n * `ResizeObserver`, so tool calls expanding, subagents collapsing, and\n * streaming text growing all work correctly.\n *\n * **Stick-to-bottom**: When the user is scrolled to (or near) the bottom,\n * new items and item resizes automatically keep the viewport pinned to the\n * bottom. When the user scrolls up, stick-to-bottom disengages until they\n * scroll back down.\n *\n * @example\n * ```tsx\n * const { virtualItems, totalSize, measureRef, isAtBottom, scrollToBottom } =\n * useConversationVirtualizer({ scrollRef, items });\n *\n * return (\n * <AiConversation ref={scrollRef}>\n * <div style={{ height: totalSize, position: \"relative\" }}>\n * {virtualItems.map((vi) => {\n * const item = items[vi.index];\n * return (\n * <div key={item.key} ref={measureRef} data-index={vi.index}\n * style={{ position: \"absolute\", top: vi.start, width: \"100%\" }}>\n * {item.render()}\n * </div>\n * );\n * })}\n * </div>\n * {!isAtBottom && <button onClick={() => scrollToBottom()}>↓</button>}\n * </AiConversation>\n * );\n * ```\n */\nconst isSSR = typeof window === \"undefined\";\nconst useIsomorphicLayoutEffect = isSSR ? useEffect : useLayoutEffect;\n\nexport function useConversationVirtualizer({\n scrollRef,\n items,\n estimateSize = 80,\n overscan = 8,\n stickThreshold = 40,\n}: UseConversationVirtualizerOptions): UseConversationVirtualizerReturn {\n const [isAtBottom, setIsAtBottom] = useState(true);\n const isAtBottomRef = useRef(true);\n const prevCountRef = useRef(items.length);\n\n // Track container width for predictive height calculation\n const [containerWidth, setContainerWidth] = useState(0);\n\n // Track whether webfonts are ready. Pretext measures via canvas which uses\n // the browser font engine — if Inter (or any custom font) hasn't loaded\n // yet, the canvas falls back to system-ui and predictions diverge from the\n // rendered DOM. Once fonts.ready resolves we re-trigger measurement.\n const [fontsReady, setFontsReady] = useState(() => {\n if (typeof document === \"undefined\") return true;\n // `document.fonts.status === \"loaded\"` means all current FontFaces are\n // loaded. If still \"loading\" we wait for fonts.ready below.\n return document.fonts?.status === \"loaded\";\n });\n\n useEffect(() => {\n if (fontsReady) return;\n if (typeof document === \"undefined\" || !document.fonts) {\n setFontsReady(true);\n return;\n }\n let cancelled = false;\n document.fonts.ready.then(() => {\n if (!cancelled) setFontsReady(true);\n });\n return () => {\n cancelled = true;\n };\n }, [fontsReady]);\n\n useIsomorphicLayoutEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n\n const updateWidth = () => {\n // clientWidth excludes scrollbars but includes padding.\n // Subtract horizontal padding to get the actual content width.\n const style = window.getComputedStyle(el);\n const padLeft = parseFloat(style.paddingLeft) || 0;\n const padRight = parseFloat(style.paddingRight) || 0;\n setContainerWidth(el.clientWidth - padLeft - padRight);\n };\n\n updateWidth();\n\n const observer = new ResizeObserver(updateWidth);\n observer.observe(el);\n\n return () => observer.disconnect();\n }, [scrollRef]);\n\n // Track whether user is at the bottom\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n\n const check = () => {\n const dist = el.scrollHeight - el.scrollTop - el.clientHeight;\n const atBottom = dist <= stickThreshold;\n isAtBottomRef.current = atBottom;\n setIsAtBottom(atBottom);\n };\n\n check();\n el.addEventListener(\"scroll\", check, { passive: true });\n return () => el.removeEventListener(\"scroll\", check);\n }, [scrollRef, stickThreshold]);\n\n // Track the previous totalSize so we can detect growth from item resizes\n const prevTotalSizeRef = useRef(0);\n\n const virtualizer = useVirtualizer({\n count: items.length,\n getScrollElement: () => scrollRef.current as HTMLElement | null,\n estimateSize: (index) => {\n const item = items[index];\n if (!item?.measure || containerWidth <= 0) {\n return estimateSize;\n }\n const {\n text,\n font = \"14px sans-serif\",\n lineHeight = 20,\n padding = 0,\n widthFraction = 1,\n horizontalPadding = 0,\n gapAfter = 0,\n whiteSpace = \"normal\",\n wordBreak = \"normal\",\n } = item.measure;\n\n // Apply the same width constraints as the rendered message bubble.\n const contentWidth = Math.max(\n 40,\n Math.floor(containerWidth * widthFraction) - horizontalPadding\n );\n\n try {\n const prepared = prepare(text, font, { whiteSpace, wordBreak });\n const { height } = layout(prepared, contentWidth, lineHeight);\n return height + padding + gapAfter;\n } catch {\n return estimateSize;\n }\n },\n overscan,\n getItemKey: (index) => items[index]?.key ?? index,\n });\n\n // When container width, item measurement specs, or font-readiness change,\n // force TanStack to recompute sizes through `estimateSize`. For predictive\n // rows this calls pretext again and still skips DOM measurement.\n useEffect(() => {\n if (containerWidth > 0) {\n virtualizer.measure();\n }\n }, [containerWidth, items, virtualizer, fontsReady]);\n\n // When items resize (streaming text, collapsible expand) and we're at\n // bottom, adjust scroll so the bottom stays pinned.\n virtualizer.shouldAdjustScrollPositionOnItemSizeChange = () =>\n isAtBottomRef.current;\n\n const virtualItems = virtualizer.getVirtualItems();\n const totalSize = virtualizer.getTotalSize();\n\n // When new items are added and we're at bottom, scroll to the end\n useEffect(() => {\n const count = items.length;\n if (count > prevCountRef.current && isAtBottomRef.current) {\n requestAnimationFrame(() => {\n virtualizer.scrollToIndex(count - 1, {\n align: \"end\",\n behavior: \"smooth\",\n });\n });\n }\n prevCountRef.current = count;\n }, [items.length, virtualizer]);\n\n // When totalSize grows (item content expanded, streaming text grew)\n // and we're stuck to bottom, smoothly follow by scrolling down.\n // This complements shouldAdjustScrollPositionOnItemSizeChange which\n // handles the instantaneous adjustment — this handles the smooth follow.\n useEffect(() => {\n const prev = prevTotalSizeRef.current;\n prevTotalSizeRef.current = totalSize;\n\n if (!isAtBottomRef.current || totalSize <= prev) return;\n\n const el = scrollRef.current;\n if (!el) return;\n\n // Only nudge if we're slightly off — the shouldAdjust callback handles\n // most cases, but measurement batching can leave a small gap.\n const dist = el.scrollHeight - el.scrollTop - el.clientHeight;\n if (dist > 1 && dist < 300) {\n el.scrollTop = el.scrollHeight - el.clientHeight;\n }\n }, [totalSize, scrollRef]);\n\n const scrollToBottom = useCallback(\n (behavior: ScrollBehavior = \"smooth\") => {\n if (items.length === 0) return;\n virtualizer.scrollToIndex(items.length - 1, { align: \"end\", behavior });\n },\n [virtualizer, items.length]\n );\n\n const measureRef = useCallback(\n (node: HTMLElement | null) => {\n if (node) virtualizer.measureElement(node);\n },\n [virtualizer]\n );\n\n // For items with predictive height, return null so TanStack doesn't remeasure.\n // For legacy items without measure spec, return the measurement callback.\n const getMeasureRef = useCallback(\n (index: number): ((node: HTMLElement | null) => void) | null => {\n const item = items[index];\n if (item?.measure) {\n return null; // Predicted height — no DOM measurement needed\n }\n return measureRef;\n },\n [items, measureRef]\n );\n\n return {\n virtualizer,\n virtualItems,\n totalSize,\n isAtBottom,\n scrollToBottom,\n measureRef,\n getMeasureRef,\n };\n}\n","/**\n * Shared pretext measurement constants for `useConversationVirtualizer`.\n *\n * These match the rendered styling of `<AiMessage>` + `<AiMessageContent>`\n * (text-sm = 14px / line-height 20px, user bubble `max-w-[70%]` with `px-4 py-3`).\n *\n * Anywhere that builds `ConversationItem.measure` for plain-text AI chat messages\n * should import from here so calibration stays in one place. If production CSS\n * for `<AiMessage>` ever changes (font size, padding, bubble width), update these\n * constants — pretext predictions will then automatically follow.\n */\n\n/** CSS `font` shorthand matching `text-sm` rendering. */\nexport const SF_AI_MEASURE_FONT =\n '14px \"Inter\", ui-sans-serif, system-ui, sans-serif';\n\n/** Line height in pixels, matching `text-sm`. */\nexport const SF_AI_MEASURE_LINE_HEIGHT = 20;\n\n/** User bubbles: `max-w-[70%]`. */\nexport const SF_AI_USER_WIDTH_FRACTION = 0.7;\n\n/** User bubbles: `px-4` → 16px × 2 horizontal padding. */\nexport const SF_AI_USER_HORIZONTAL_PADDING = 32;\n\n/** User bubbles: `py-3` → 12px × 2 vertical padding. */\nexport const SF_AI_USER_VERTICAL_PADDING = 24;\n\n/** Assistant text: full width column, no bubble. */\nexport const SF_AI_ASSISTANT_WIDTH_FRACTION = 1;\n\n/** Assistant text: no horizontal padding inside the bubble. */\nexport const SF_AI_ASSISTANT_HORIZONTAL_PADDING = 0;\n\n/** Assistant text: no vertical padding inside the bubble. */\nexport const SF_AI_ASSISTANT_VERTICAL_PADDING = 0;\n\n/** Default gap between conversation items. */\nexport const SF_AI_DEFAULT_MESSAGE_GAP = 12;\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-part-group-DNb9I446.js","names":[],"sources":["../src/components/ai-part-group/ai-part-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { Collapsible as BaseCollapsible } from \"@base-ui/react/collapsible\";\nimport { CaretDownIcon, SpinnerGapIcon } from \"@phosphor-icons/react\";\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\nimport { Children, isValidElement, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { type AiToolPart, hasToolError, hasToolResult } from \"../ai-tool\";\nimport { TextRoll } from \"../text-roll\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ntype FrozenStep = {\n kind: \"tool\" | \"reasoning\";\n name: string;\n ok: boolean;\n duration?: number;\n};\n\ntype FrozenSnapshot = {\n title: string;\n steps: FrozenStep[];\n errorCount: number;\n totalDuration: number;\n};\n\n// Module-level snapshot cache. Survives re-renders within the page session.\n// Keyed by `groupKey` — usually the parent message id.\nconst snapshotCache = new Map<string, FrozenSnapshot>();\n\n/** Test-only escape hatch for clearing the snapshot cache. */\nexport function _clearAiPartGroupCache() {\n snapshotCache.clear();\n}\n\n/**\n * Reads the frozen snapshot for a group, or `undefined` if it hasn't completed\n * yet. Useful for consumers driving custom layouts.\n */\nexport function getAiPartGroupSnapshot(\n groupKey: string\n): FrozenSnapshot | undefined {\n return snapshotCache.get(groupKey);\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport type AiPartGroupProps = Omit<ComponentProps<\"div\">, \"title\"> & {\n /**\n * Stable identifier — usually the parent message id. Required so the group\n * can persist its frozen snapshot across re-mounts (scroll-back into a\n * virtualized list re-renders without restarting animations).\n */\n groupKey: string;\n /**\n * Set to `true` once the parent stream has finished. Triggers the group to\n * snapshot its current children and from then on render only the static\n * collapsed summary — ephemeral children never re-mount.\n */\n complete?: boolean;\n /**\n * Max number of live ephemeral rows visible at once during streaming.\n * Older rows fall off the top. Pinned rows (errors / user-expanded) render\n * above the live window and do not count toward this cap.\n * @default 3\n */\n max?: number;\n /**\n * Title shown in the collapsed summary line. Falls back to the first\n * reasoning text snippet, then to a step count.\n */\n title?: string;\n /** Child ephemeral rows (`AiToolCall variant=\"ephemeral\"`, `AiReasoning variant=\"ephemeral\"`). */\n children?: ReactNode;\n};\n\n/**\n * Groups streaming ephemeral activity (tool calls + reasoning) into a single\n * rolling window during streaming, then collapses to a frozen summary line\n * once the parent message completes.\n *\n * Behavior:\n * - **Streaming:** shows the most recent `max` (default 3) ephemeral children.\n * Pinned rows (errors / user-expanded) sit above the live window.\n * - **Completed:** writes a snapshot keyed by `groupKey`, unmounts ephemeral\n * children entirely, and renders a static `▸ {title} · N steps` line.\n * - **Re-mount after completion:** always renders the cached snapshot — no\n * animations, no `useEffect` re-runs, no tool re-execution risk.\n *\n * @example\n * ```tsx\n * <AiPartGroup\n * groupKey={message.id}\n * complete={message.status === \"complete\"}\n * title={message.reasoningSummary}\n * max={3}\n * >\n * {message.parts.map((p) =>\n * p.type === \"reasoning\"\n * ? <AiReasoning key={p.id} variant=\"ephemeral\" part={p} />\n * : <AiToolCall key={p.toolCallId} variant=\"ephemeral\" part={p} />\n * )}\n * </AiPartGroup>\n * ```\n */\nexport function AiPartGroup({\n groupKey,\n complete = false,\n max = 3,\n title,\n children,\n className,\n ...props\n}: AiPartGroupProps) {\n const cached = snapshotCache.get(groupKey);\n\n // Cache hit: always render the frozen snapshot, never re-mount ephemerals.\n // This is the scroll-back-into-an-old-message path.\n if (cached) {\n return (\n <TitledSummary\n snapshot={cached}\n className={className}\n defaultOpen={false}\n {...props}\n />\n );\n }\n\n // First-time completion: derive a snapshot, persist it, render frozen.\n if (complete) {\n const snapshot = deriveSnapshot(children, title);\n snapshotCache.set(groupKey, snapshot);\n return (\n <TitledSummary\n snapshot={snapshot}\n className={className}\n defaultOpen={false}\n {...props}\n />\n );\n }\n\n // Streaming: persistent title row + rolling window of the latest `max` children.\n // oxlint-disable-next-line no-react-children\n const allChildren = Children.toArray(children).filter(isValidElement);\n const live = allChildren.slice(Math.max(0, allChildren.length - max));\n const liveTitle = title?.trim() || deriveLiveTitle(allChildren);\n const stepCount = allChildren.length;\n\n return (\n <LiveGroup\n className={className}\n liveRows={live}\n stepCount={stepCount}\n title={liveTitle}\n {...props}\n />\n );\n}\n\nAiPartGroup.displayName = \"AiPartGroup\";\n\n// ─── Live group (streaming) ──────────────────────────────────────────────────\n\nfunction LiveGroup({\n title,\n liveRows,\n stepCount,\n className,\n ...props\n}: {\n title: string;\n liveRows: ReactElement[];\n stepCount: number;\n} & Omit<ComponentProps<\"div\">, \"title\">) {\n // During streaming the group is always open — the caret is purely decorative\n // so the frozen/live states share a visual footprint (no layout jump on settle).\n const stepLabel = stepCount === 1 ? \"step\" : \"steps\";\n\n return (\n <div\n className={cn(\"flex flex-col\", className)}\n data-streaming=\"true\"\n {...props}\n >\n <div\n className={cn(\"flex w-fit items-center gap-1.5 rounded px-1 py-0.5\")}\n >\n <SpinnerGapIcon className=\"size-3.5 shrink-0 animate-spin text-sf-subtle\" />\n <span className=\"min-w-0 truncate text-sm text-sf-default\">\n <TextRoll\n duration={0.3}\n getEnterDelay={(i) => i * 0.03}\n getExitDelay={(i) => i * 0.03 + 0.12}\n key={title}\n >\n {title}\n </TextRoll>\n </span>\n {stepCount > 0 ? (\n <span className=\"text-xs tabular-nums text-sf-subtle/60\">\n {stepCount} {stepLabel}\n </span>\n ) : null}\n <CaretDownIcon className=\"size-3 shrink-0 text-sf-subtle/60\" />\n </div>\n {liveRows.length > 0 ? (\n <div className=\"ml-[0.3125rem] flex flex-col gap-0.5 border-l border-sf-line/40 pl-3\">\n {liveRows}\n </div>\n ) : null}\n </div>\n );\n}\n\n// ─── Frozen summary (titled) ─────────────────────────────────────────────────\n\nfunction TitledSummary({\n snapshot,\n defaultOpen,\n className,\n ...props\n}: {\n snapshot: FrozenSnapshot;\n defaultOpen: boolean;\n} & Omit<ComponentProps<\"div\">, \"title\">) {\n const [isOpen, setIsOpen] = useState(defaultOpen);\n const stepLabel = snapshot.steps.length === 1 ? \"step\" : \"steps\";\n\n return (\n <BaseCollapsible.Root open={isOpen} onOpenChange={setIsOpen}>\n <div\n className={cn(\"flex flex-col\", className)}\n data-streaming=\"false\"\n {...props}\n >\n <BaseCollapsible.Trigger\n render={\n <button\n type=\"button\"\n className={cn(\n \"flex w-fit items-center gap-1.5 rounded px-1 py-0.5 text-left\",\n \"transition-colors hover:bg-sf-tint\"\n )}\n />\n }\n >\n <span\n aria-hidden\n className={cn(\n \"size-1.5 shrink-0 rounded-full bg-current\",\n snapshot.errorCount > 0 ? \"text-sf-danger\" : \"text-sf-success\"\n )}\n />\n <span className=\"min-w-0 truncate text-sm text-sf-subtle\">\n {snapshot.title}\n </span>\n <span className=\"text-xs tabular-nums text-sf-subtle/60\">\n {snapshot.steps.length} {stepLabel}\n {snapshot.errorCount > 0 ? ` · ${snapshot.errorCount} error` : \"\"}\n </span>\n <CaretDownIcon\n className={cn(\n \"size-3 shrink-0 text-sf-subtle/60 transition-transform duration-200\",\n !isOpen && \"-rotate-90\"\n )}\n />\n </BaseCollapsible.Trigger>\n\n <BaseCollapsible.Panel className=\"overflow-hidden\">\n <ul className=\"mt-1 ml-[0.3125rem] flex flex-col gap-0.5 border-l border-sf-line/40 pl-3\">\n {snapshot.steps.map((step, i) => (\n <li\n className=\"flex items-center gap-1.5 text-xs text-sf-subtle\"\n key={`${step.kind}-${i}-${step.name}`}\n >\n <span\n aria-hidden\n className={cn(\n \"size-1.5 shrink-0 rounded-full bg-current\",\n step.ok ? \"text-sf-success\" : \"text-sf-danger\"\n )}\n />\n <span className=\"min-w-0 truncate\">{step.name}</span>\n {step.duration ? (\n <span className=\"tabular-nums text-sf-subtle/60\">\n {step.duration}ms\n </span>\n ) : null}\n </li>\n ))}\n </ul>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\nfunction deriveLiveTitle(elements: ReactElement[]): string {\n for (const el of elements as ChildLike[]) {\n const part = el.props.part as\n | { text?: string; summary?: string }\n | undefined;\n const text = part?.summary ?? part?.text;\n if (text) return truncate(text, 60);\n }\n return \"Working…\";\n}\n\n// ─── Snapshot derivation ─────────────────────────────────────────────────────\n\ntype ChildLike = ReactElement<{\n part?: AiToolPart | { text?: string; summary?: string; name?: string };\n duration?: number;\n}>;\n\nfunction deriveSnapshot(\n children: ReactNode,\n explicitTitle: string | undefined\n): FrozenSnapshot {\n // oxlint-disable-next-line no-react-children\n const elements = Children.toArray(children).filter(\n isValidElement\n ) as ChildLike[];\n\n const steps: FrozenStep[] = [];\n let errorCount = 0;\n let totalDuration = 0;\n let firstReasoningSnippet: string | undefined;\n\n for (const el of elements) {\n const kind = inferKind(el);\n const part = el.props.part as\n | (AiToolPart & { text?: string; summary?: string; name?: string })\n | undefined;\n const duration = el.props.duration;\n if (typeof duration === \"number\") totalDuration += duration;\n\n if (kind === \"tool\") {\n const toolPart = part as AiToolPart | undefined;\n const ok = toolPart ? !hasToolError(toolPart) : true;\n const settled = toolPart ? hasToolResult(toolPart) : true;\n if (!ok) errorCount += 1;\n steps.push({\n kind: \"tool\",\n name: toolPart?.toolName ?? \"tool\",\n ok: ok && settled,\n duration,\n });\n } else {\n const text =\n (part?.summary as string | undefined) ??\n (part?.text as string | undefined);\n if (!firstReasoningSnippet && text) {\n firstReasoningSnippet = text;\n }\n steps.push({\n kind: \"reasoning\",\n name: text ? truncate(text, 60) : \"Thinking\",\n ok: true,\n duration,\n });\n }\n }\n\n const title =\n explicitTitle?.trim() ||\n (firstReasoningSnippet && truncate(firstReasoningSnippet, 60)) ||\n `${steps.length} ${steps.length === 1 ? \"step\" : \"steps\"}`;\n\n return { title, steps, errorCount, totalDuration };\n}\n\nfunction inferKind(el: ChildLike): \"tool\" | \"reasoning\" {\n // Best-effort: check common indicators. Tool parts have `toolName`/`toolCallId`.\n // Reasoning parts have `text`.\n const part = el.props.part as Record<string, unknown> | undefined;\n if (part && typeof part === \"object\") {\n if (\"toolName\" in part || \"toolCallId\" in part) return \"tool\";\n if (\"text\" in part || \"summary\" in part) return \"reasoning\";\n }\n // Fallback: inspect displayName.\n const displayName =\n (el.type as { displayName?: string; name?: string } | undefined)\n ?.displayName ??\n (el.type as { displayName?: string; name?: string } | undefined)?.name ??\n \"\";\n if (displayName.includes(\"Tool\")) return \"tool\";\n return \"reasoning\";\n}\n\nfunction truncate(s: string, n: number): string {\n if (s.length <= n) return s;\n return `${s.slice(0, n - 1).trimEnd()}…`;\n}\n\n// ─── groupParts ──────────────────────────────────────────────────────\n\n/**\n * A message part that is either narrative text or an \"activity\" (reasoning or\n * tool call). Consumers pass parts shaped like this — the helper does not read\n * any field other than `type`.\n */\nexport type GroupablePart =\n | { type: \"text\"; [key: string]: unknown }\n | { type: \"reasoning\"; [key: string]: unknown }\n | { type: \"tool-call\" | \"tool-result\" | string; [key: string]: unknown };\n\n/** Emitted by {@link groupParts}. */\nexport type PartChunk<TPart = GroupablePart> =\n | { kind: \"text\"; parts: TPart[] }\n | { kind: \"activity\"; parts: TPart[] };\n\n/**\n * Split a message's parts into alternating `text` and `activity` chunks so each\n * contiguous run of reasoning/tool parts can be rendered inside its own\n * {@link AiPartGroup}, with narrative text rendered between them.\n *\n * @example\n * ```tsx\n * {groupParts(message.parts).map((chunk, i) =>\n * chunk.kind === \"text\" ? (\n * <AiMessageContent key={i}>{joinText(chunk.parts)}</AiMessageContent>\n * ) : (\n * <AiPartGroup\n * key={i}\n * groupKey={`${message.id}:activity:${i}`}\n * complete={!message.isStreaming}\n * >\n * {chunk.parts.map(renderActivityPart)}\n * </AiPartGroup>\n * )\n * )}\n * ```\n */\nexport function groupParts<TPart extends { type: string }>(\n parts: readonly TPart[],\n options: {\n /** Which `type` values count as \"activity\". Defaults to reasoning + tool parts. */\n isActivity?: (part: TPart) => boolean;\n } = {}\n): PartChunk<TPart>[] {\n const isActivity =\n options.isActivity ??\n ((p: TPart) => {\n const t = p.type;\n return (\n t === \"reasoning\" ||\n t === \"tool-call\" ||\n t === \"tool-result\" ||\n t.startsWith(\"tool-\")\n );\n });\n\n const chunks: PartChunk<TPart>[] = [];\n for (const part of parts) {\n const kind: PartChunk<TPart>[\"kind\"] = isActivity(part)\n ? \"activity\"\n : \"text\";\n const last = chunks.at(-1);\n if (last && last.kind === kind) {\n last.parts.push(part);\n } else {\n chunks.push({ kind, parts: [part] });\n }\n }\n return chunks;\n}\n"],"mappings":"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"}