@vendoai/ui 0.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 (525) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +7 -0
  3. package/dist/chrome/activity-ledger.d.ts +14 -0
  4. package/dist/chrome/activity-ledger.d.ts.map +1 -0
  5. package/dist/chrome/activity-ledger.js +40 -0
  6. package/dist/chrome/activity-ledger.js.map +1 -0
  7. package/dist/chrome/activity-panel.d.ts +7 -0
  8. package/dist/chrome/activity-panel.d.ts.map +1 -0
  9. package/dist/chrome/activity-panel.js +27 -0
  10. package/dist/chrome/activity-panel.js.map +1 -0
  11. package/dist/chrome/activity-semantics.d.ts +42 -0
  12. package/dist/chrome/activity-semantics.d.ts.map +1 -0
  13. package/dist/chrome/activity-semantics.js +119 -0
  14. package/dist/chrome/activity-semantics.js.map +1 -0
  15. package/dist/chrome/approval-card.d.ts +22 -0
  16. package/dist/chrome/approval-card.d.ts.map +1 -0
  17. package/dist/chrome/approval-card.js +95 -0
  18. package/dist/chrome/approval-card.js.map +1 -0
  19. package/dist/chrome/approval-sheet.d.ts +23 -0
  20. package/dist/chrome/approval-sheet.d.ts.map +1 -0
  21. package/dist/chrome/approval-sheet.js +72 -0
  22. package/dist/chrome/approval-sheet.js.map +1 -0
  23. package/dist/chrome/automations-panel.d.ts +3 -0
  24. package/dist/chrome/automations-panel.d.ts.map +1 -0
  25. package/dist/chrome/automations-panel.js +305 -0
  26. package/dist/chrome/automations-panel.js.map +1 -0
  27. package/dist/chrome/build-beat.d.ts +70 -0
  28. package/dist/chrome/build-beat.d.ts.map +1 -0
  29. package/dist/chrome/build-beat.js +101 -0
  30. package/dist/chrome/build-beat.js.map +1 -0
  31. package/dist/chrome/chrome-css.d.ts +7 -0
  32. package/dist/chrome/chrome-css.d.ts.map +1 -0
  33. package/dist/chrome/chrome-css.js +2042 -0
  34. package/dist/chrome/chrome-css.js.map +1 -0
  35. package/dist/chrome/chrome-root.d.ts +13 -0
  36. package/dist/chrome/chrome-root.d.ts.map +1 -0
  37. package/dist/chrome/chrome-root.js +39 -0
  38. package/dist/chrome/chrome-root.js.map +1 -0
  39. package/dist/chrome/clipboard.d.ts +4 -0
  40. package/dist/chrome/clipboard.d.ts.map +1 -0
  41. package/dist/chrome/clipboard.js +49 -0
  42. package/dist/chrome/clipboard.js.map +1 -0
  43. package/dist/chrome/connect-card.d.ts +19 -0
  44. package/dist/chrome/connect-card.d.ts.map +1 -0
  45. package/dist/chrome/connect-card.js +57 -0
  46. package/dist/chrome/connect-card.js.map +1 -0
  47. package/dist/chrome/connect-dock.d.ts +25 -0
  48. package/dist/chrome/connect-dock.d.ts.map +1 -0
  49. package/dist/chrome/connect-dock.js +192 -0
  50. package/dist/chrome/connect-dock.js.map +1 -0
  51. package/dist/chrome/connected-accounts-panel.d.ts +14 -0
  52. package/dist/chrome/connected-accounts-panel.d.ts.map +1 -0
  53. package/dist/chrome/connected-accounts-panel.js +157 -0
  54. package/dist/chrome/connected-accounts-panel.js.map +1 -0
  55. package/dist/chrome/dev-mode.d.ts +5 -0
  56. package/dist/chrome/dev-mode.d.ts.map +1 -0
  57. package/dist/chrome/dev-mode.js +7 -0
  58. package/dist/chrome/dev-mode.js.map +1 -0
  59. package/dist/chrome/discoverability.d.ts +33 -0
  60. package/dist/chrome/discoverability.d.ts.map +1 -0
  61. package/dist/chrome/discoverability.js +63 -0
  62. package/dist/chrome/discoverability.js.map +1 -0
  63. package/dist/chrome/embeds.d.ts +21 -0
  64. package/dist/chrome/embeds.d.ts.map +1 -0
  65. package/dist/chrome/embeds.js +181 -0
  66. package/dist/chrome/embeds.js.map +1 -0
  67. package/dist/chrome/fluid-thinking.d.ts +15 -0
  68. package/dist/chrome/fluid-thinking.d.ts.map +1 -0
  69. package/dist/chrome/fluid-thinking.js +37 -0
  70. package/dist/chrome/fluid-thinking.js.map +1 -0
  71. package/dist/chrome/humanize.d.ts +46 -0
  72. package/dist/chrome/humanize.d.ts.map +1 -0
  73. package/dist/chrome/humanize.js +104 -0
  74. package/dist/chrome/humanize.js.map +1 -0
  75. package/dist/chrome/index.d.ts +39 -0
  76. package/dist/chrome/index.d.ts.map +1 -0
  77. package/dist/chrome/index.js +38 -0
  78. package/dist/chrome/index.js.map +1 -0
  79. package/dist/chrome/markdown.d.ts +23 -0
  80. package/dist/chrome/markdown.d.ts.map +1 -0
  81. package/dist/chrome/markdown.js +149 -0
  82. package/dist/chrome/markdown.js.map +1 -0
  83. package/dist/chrome/morph-toast.d.ts +43 -0
  84. package/dist/chrome/morph-toast.d.ts.map +1 -0
  85. package/dist/chrome/morph-toast.js +89 -0
  86. package/dist/chrome/morph-toast.js.map +1 -0
  87. package/dist/chrome/no-policy-notice.d.ts +8 -0
  88. package/dist/chrome/no-policy-notice.d.ts.map +1 -0
  89. package/dist/chrome/no-policy-notice.js +18 -0
  90. package/dist/chrome/no-policy-notice.js.map +1 -0
  91. package/dist/chrome/onest-font.gen.d.ts +10 -0
  92. package/dist/chrome/onest-font.gen.d.ts.map +1 -0
  93. package/dist/chrome/onest-font.gen.js +10 -0
  94. package/dist/chrome/onest-font.gen.js.map +1 -0
  95. package/dist/chrome/overlay-registry.d.ts +66 -0
  96. package/dist/chrome/overlay-registry.d.ts.map +1 -0
  97. package/dist/chrome/overlay-registry.js +103 -0
  98. package/dist/chrome/overlay-registry.js.map +1 -0
  99. package/dist/chrome/palette-hotkey.d.ts +42 -0
  100. package/dist/chrome/palette-hotkey.d.ts.map +1 -0
  101. package/dist/chrome/palette-hotkey.js +91 -0
  102. package/dist/chrome/palette-hotkey.js.map +1 -0
  103. package/dist/chrome/policy-notice-body.d.ts +3 -0
  104. package/dist/chrome/policy-notice-body.d.ts.map +1 -0
  105. package/dist/chrome/policy-notice-body.js +6 -0
  106. package/dist/chrome/policy-notice-body.js.map +1 -0
  107. package/dist/chrome/takeover-portal.d.ts +16 -0
  108. package/dist/chrome/takeover-portal.d.ts.map +1 -0
  109. package/dist/chrome/takeover-portal.js +21 -0
  110. package/dist/chrome/takeover-portal.js.map +1 -0
  111. package/dist/chrome/thread/attachments.d.ts +24 -0
  112. package/dist/chrome/thread/attachments.d.ts.map +1 -0
  113. package/dist/chrome/thread/attachments.js +50 -0
  114. package/dist/chrome/thread/attachments.js.map +1 -0
  115. package/dist/chrome/thread/composer.d.ts +63 -0
  116. package/dist/chrome/thread/composer.d.ts.map +1 -0
  117. package/dist/chrome/thread/composer.js +259 -0
  118. package/dist/chrome/thread/composer.js.map +1 -0
  119. package/dist/chrome/thread/index.d.ts +41 -0
  120. package/dist/chrome/thread/index.d.ts.map +1 -0
  121. package/dist/chrome/thread/index.js +258 -0
  122. package/dist/chrome/thread/index.js.map +1 -0
  123. package/dist/chrome/thread/message-data.d.ts +35 -0
  124. package/dist/chrome/thread/message-data.d.ts.map +1 -0
  125. package/dist/chrome/thread/message-data.js +127 -0
  126. package/dist/chrome/thread/message-data.js.map +1 -0
  127. package/dist/chrome/thread/message-list.d.ts +30 -0
  128. package/dist/chrome/thread/message-list.d.ts.map +1 -0
  129. package/dist/chrome/thread/message-list.js +11 -0
  130. package/dist/chrome/thread/message-list.js.map +1 -0
  131. package/dist/chrome/thread/message.d.ts +18 -0
  132. package/dist/chrome/thread/message.d.ts.map +1 -0
  133. package/dist/chrome/thread/message.js +57 -0
  134. package/dist/chrome/thread/message.js.map +1 -0
  135. package/dist/chrome/thread/parts.d.ts +53 -0
  136. package/dist/chrome/thread/parts.d.ts.map +1 -0
  137. package/dist/chrome/thread/parts.js +184 -0
  138. package/dist/chrome/thread/parts.js.map +1 -0
  139. package/dist/chrome/thread/scrolling.d.ts +26 -0
  140. package/dist/chrome/thread/scrolling.d.ts.map +1 -0
  141. package/dist/chrome/thread/scrolling.js +214 -0
  142. package/dist/chrome/thread/scrolling.js.map +1 -0
  143. package/dist/chrome/truncate.d.ts +12 -0
  144. package/dist/chrome/truncate.d.ts.map +1 -0
  145. package/dist/chrome/truncate.js +18 -0
  146. package/dist/chrome/truncate.js.map +1 -0
  147. package/dist/chrome/vendo-activities.d.ts +24 -0
  148. package/dist/chrome/vendo-activities.d.ts.map +1 -0
  149. package/dist/chrome/vendo-activities.js +38 -0
  150. package/dist/chrome/vendo-activities.js.map +1 -0
  151. package/dist/chrome/vendo-overlay.d.ts +63 -0
  152. package/dist/chrome/vendo-overlay.d.ts.map +1 -0
  153. package/dist/chrome/vendo-overlay.js +256 -0
  154. package/dist/chrome/vendo-overlay.js.map +1 -0
  155. package/dist/chrome/vendo-page.d.ts +3 -0
  156. package/dist/chrome/vendo-page.d.ts.map +1 -0
  157. package/dist/chrome/vendo-page.js +162 -0
  158. package/dist/chrome/vendo-page.js.map +1 -0
  159. package/dist/chrome/vendo-palette.d.ts +29 -0
  160. package/dist/chrome/vendo-palette.d.ts.map +1 -0
  161. package/dist/chrome/vendo-palette.js +95 -0
  162. package/dist/chrome/vendo-palette.js.map +1 -0
  163. package/dist/chrome/vendo-slot.d.ts +79 -0
  164. package/dist/chrome/vendo-slot.d.ts.map +1 -0
  165. package/dist/chrome/vendo-slot.js +153 -0
  166. package/dist/chrome/vendo-slot.js.map +1 -0
  167. package/dist/chrome/vendo-toasts.d.ts +40 -0
  168. package/dist/chrome/vendo-toasts.d.ts.map +1 -0
  169. package/dist/chrome/vendo-toasts.js +135 -0
  170. package/dist/chrome/vendo-toasts.js.map +1 -0
  171. package/dist/chrome/vendo-trigger.d.ts +24 -0
  172. package/dist/chrome/vendo-trigger.d.ts.map +1 -0
  173. package/dist/chrome/vendo-trigger.js +30 -0
  174. package/dist/chrome/vendo-trigger.js.map +1 -0
  175. package/dist/chrome/waiting-queue.d.ts +8 -0
  176. package/dist/chrome/waiting-queue.d.ts.map +1 -0
  177. package/dist/chrome/waiting-queue.js +21 -0
  178. package/dist/chrome/waiting-queue.js.map +1 -0
  179. package/dist/client-impl.d.ts +4 -0
  180. package/dist/client-impl.d.ts.map +1 -0
  181. package/dist/client-impl.js +160 -0
  182. package/dist/client-impl.js.map +1 -0
  183. package/dist/client.d.ts +136 -0
  184. package/dist/client.d.ts.map +1 -0
  185. package/dist/client.js +2 -0
  186. package/dist/client.js.map +1 -0
  187. package/dist/context.d.ts +97 -0
  188. package/dist/context.d.ts.map +1 -0
  189. package/dist/context.js +71 -0
  190. package/dist/context.js.map +1 -0
  191. package/dist/eject-templates/activities/index.tsx +113 -0
  192. package/dist/eject-templates/templates.json +29 -0
  193. package/dist/eject-templates/thread/attachments.tsx +69 -0
  194. package/dist/eject-templates/thread/composer.tsx +409 -0
  195. package/dist/eject-templates/thread/index.tsx +422 -0
  196. package/dist/eject-templates/thread/message-data.ts +151 -0
  197. package/dist/eject-templates/thread/message-list.tsx +123 -0
  198. package/dist/eject-templates/thread/message.tsx +149 -0
  199. package/dist/eject-templates/thread/parts.tsx +293 -0
  200. package/dist/eject-templates/thread/scrolling.ts +223 -0
  201. package/dist/embeds.d.ts +28 -0
  202. package/dist/embeds.d.ts.map +1 -0
  203. package/dist/embeds.js +2 -0
  204. package/dist/embeds.js.map +1 -0
  205. package/dist/hooks/index.d.ts +18 -0
  206. package/dist/hooks/index.d.ts.map +1 -0
  207. package/dist/hooks/index.js +19 -0
  208. package/dist/hooks/index.js.map +1 -0
  209. package/dist/hooks/scripted-transport.d.ts +44 -0
  210. package/dist/hooks/scripted-transport.d.ts.map +1 -0
  211. package/dist/hooks/scripted-transport.js +58 -0
  212. package/dist/hooks/scripted-transport.js.map +1 -0
  213. package/dist/hooks/use-activity.d.ts +17 -0
  214. package/dist/hooks/use-activity.d.ts.map +1 -0
  215. package/dist/hooks/use-activity.js +50 -0
  216. package/dist/hooks/use-activity.js.map +1 -0
  217. package/dist/hooks/use-app.d.ts +19 -0
  218. package/dist/hooks/use-app.d.ts.map +1 -0
  219. package/dist/hooks/use-app.js +66 -0
  220. package/dist/hooks/use-app.js.map +1 -0
  221. package/dist/hooks/use-approvals.d.ts +13 -0
  222. package/dist/hooks/use-approvals.d.ts.map +1 -0
  223. package/dist/hooks/use-approvals.js +14 -0
  224. package/dist/hooks/use-approvals.js.map +1 -0
  225. package/dist/hooks/use-apps.d.ts +17 -0
  226. package/dist/hooks/use-apps.d.ts.map +1 -0
  227. package/dist/hooks/use-apps.js +30 -0
  228. package/dist/hooks/use-apps.js.map +1 -0
  229. package/dist/hooks/use-automations.d.ts +25 -0
  230. package/dist/hooks/use-automations.d.ts.map +1 -0
  231. package/dist/hooks/use-automations.js +30 -0
  232. package/dist/hooks/use-automations.js.map +1 -0
  233. package/dist/hooks/use-connections.d.ts +12 -0
  234. package/dist/hooks/use-connections.d.ts.map +1 -0
  235. package/dist/hooks/use-connections.js +15 -0
  236. package/dist/hooks/use-connections.js.map +1 -0
  237. package/dist/hooks/use-connector-catalog.d.ts +8 -0
  238. package/dist/hooks/use-connector-catalog.d.ts.map +1 -0
  239. package/dist/hooks/use-connector-catalog.js +52 -0
  240. package/dist/hooks/use-connector-catalog.js.map +1 -0
  241. package/dist/hooks/use-grants.d.ts +13 -0
  242. package/dist/hooks/use-grants.d.ts.map +1 -0
  243. package/dist/hooks/use-grants.js +14 -0
  244. package/dist/hooks/use-grants.js.map +1 -0
  245. package/dist/hooks/use-mobile-takeover.d.ts +22 -0
  246. package/dist/hooks/use-mobile-takeover.d.ts.map +1 -0
  247. package/dist/hooks/use-mobile-takeover.js +66 -0
  248. package/dist/hooks/use-mobile-takeover.js.map +1 -0
  249. package/dist/hooks/use-resource.d.ts +21 -0
  250. package/dist/hooks/use-resource.d.ts.map +1 -0
  251. package/dist/hooks/use-resource.js +79 -0
  252. package/dist/hooks/use-resource.js.map +1 -0
  253. package/dist/hooks/use-slot-app.d.ts +19 -0
  254. package/dist/hooks/use-slot-app.d.ts.map +1 -0
  255. package/dist/hooks/use-slot-app.js +18 -0
  256. package/dist/hooks/use-slot-app.js.map +1 -0
  257. package/dist/hooks/use-threads.d.ts +16 -0
  258. package/dist/hooks/use-threads.d.ts.map +1 -0
  259. package/dist/hooks/use-threads.js +15 -0
  260. package/dist/hooks/use-threads.js.map +1 -0
  261. package/dist/hooks/use-vendo-overlay.d.ts +31 -0
  262. package/dist/hooks/use-vendo-overlay.d.ts.map +1 -0
  263. package/dist/hooks/use-vendo-overlay.js +23 -0
  264. package/dist/hooks/use-vendo-overlay.js.map +1 -0
  265. package/dist/hooks/use-vendo-status.d.ts +6 -0
  266. package/dist/hooks/use-vendo-status.d.ts.map +1 -0
  267. package/dist/hooks/use-vendo-status.js +28 -0
  268. package/dist/hooks/use-vendo-status.js.map +1 -0
  269. package/dist/hooks/use-vendo-thread.d.ts +39 -0
  270. package/dist/hooks/use-vendo-thread.d.ts.map +1 -0
  271. package/dist/hooks/use-vendo-thread.js +185 -0
  272. package/dist/hooks/use-vendo-thread.js.map +1 -0
  273. package/dist/index.d.ts +12 -0
  274. package/dist/index.d.ts.map +1 -0
  275. package/dist/index.js +12 -0
  276. package/dist/index.js.map +1 -0
  277. package/dist/kit/charts/bar.d.ts +16 -0
  278. package/dist/kit/charts/bar.d.ts.map +1 -0
  279. package/dist/kit/charts/bar.js +21 -0
  280. package/dist/kit/charts/bar.js.map +1 -0
  281. package/dist/kit/charts/donut.d.ts +16 -0
  282. package/dist/kit/charts/donut.d.ts.map +1 -0
  283. package/dist/kit/charts/donut.js +17 -0
  284. package/dist/kit/charts/donut.js.map +1 -0
  285. package/dist/kit/charts/line.d.ts +19 -0
  286. package/dist/kit/charts/line.d.ts.map +1 -0
  287. package/dist/kit/charts/line.js +21 -0
  288. package/dist/kit/charts/line.js.map +1 -0
  289. package/dist/kit/charts/progress.d.ts +12 -0
  290. package/dist/kit/charts/progress.d.ts.map +1 -0
  291. package/dist/kit/charts/progress.js +25 -0
  292. package/dist/kit/charts/progress.js.map +1 -0
  293. package/dist/kit/charts/sanitize.d.ts +24 -0
  294. package/dist/kit/charts/sanitize.d.ts.map +1 -0
  295. package/dist/kit/charts/sanitize.js +50 -0
  296. package/dist/kit/charts/sanitize.js.map +1 -0
  297. package/dist/kit/charts/sparkline.d.ts +11 -0
  298. package/dist/kit/charts/sparkline.d.ts.map +1 -0
  299. package/dist/kit/charts/sparkline.js +17 -0
  300. package/dist/kit/charts/sparkline.js.map +1 -0
  301. package/dist/kit/data/badge.d.ts +13 -0
  302. package/dist/kit/data/badge.d.ts.map +1 -0
  303. package/dist/kit/data/badge.js +12 -0
  304. package/dist/kit/data/badge.js.map +1 -0
  305. package/dist/kit/data/card-list.d.ts +23 -0
  306. package/dist/kit/data/card-list.d.ts.map +1 -0
  307. package/dist/kit/data/card-list.js +43 -0
  308. package/dist/kit/data/card-list.js.map +1 -0
  309. package/dist/kit/data/data-table.d.ts +32 -0
  310. package/dist/kit/data/data-table.d.ts.map +1 -0
  311. package/dist/kit/data/data-table.js +153 -0
  312. package/dist/kit/data/data-table.js.map +1 -0
  313. package/dist/kit/data/stat.d.ts +15 -0
  314. package/dist/kit/data/stat.d.ts.map +1 -0
  315. package/dist/kit/data/stat.js +28 -0
  316. package/dist/kit/data/stat.js.map +1 -0
  317. package/dist/kit/feedback/accordion.d.ts +15 -0
  318. package/dist/kit/feedback/accordion.d.ts.map +1 -0
  319. package/dist/kit/feedback/accordion.js +33 -0
  320. package/dist/kit/feedback/accordion.js.map +1 -0
  321. package/dist/kit/feedback/callout.d.ts +13 -0
  322. package/dist/kit/feedback/callout.d.ts.map +1 -0
  323. package/dist/kit/feedback/callout.js +24 -0
  324. package/dist/kit/feedback/callout.js.map +1 -0
  325. package/dist/kit/feedback/tabs.d.ts +14 -0
  326. package/dist/kit/feedback/tabs.d.ts.map +1 -0
  327. package/dist/kit/feedback/tabs.js +37 -0
  328. package/dist/kit/feedback/tabs.js.map +1 -0
  329. package/dist/kit/format.d.ts +60 -0
  330. package/dist/kit/format.d.ts.map +1 -0
  331. package/dist/kit/format.js +123 -0
  332. package/dist/kit/format.js.map +1 -0
  333. package/dist/kit/forms/button.d.ts +17 -0
  334. package/dist/kit/forms/button.d.ts.map +1 -0
  335. package/dist/kit/forms/button.js +33 -0
  336. package/dist/kit/forms/button.js.map +1 -0
  337. package/dist/kit/forms/checkbox.d.ts +9 -0
  338. package/dist/kit/forms/checkbox.d.ts.map +1 -0
  339. package/dist/kit/forms/checkbox.js +9 -0
  340. package/dist/kit/forms/checkbox.js.map +1 -0
  341. package/dist/kit/forms/date-picker.d.ts +13 -0
  342. package/dist/kit/forms/date-picker.d.ts.map +1 -0
  343. package/dist/kit/forms/date-picker.js +9 -0
  344. package/dist/kit/forms/date-picker.js.map +1 -0
  345. package/dist/kit/forms/disclaimer.d.ts +8 -0
  346. package/dist/kit/forms/disclaimer.d.ts.map +1 -0
  347. package/dist/kit/forms/disclaimer.js +20 -0
  348. package/dist/kit/forms/disclaimer.js.map +1 -0
  349. package/dist/kit/forms/field.d.ts +18 -0
  350. package/dist/kit/forms/field.d.ts.map +1 -0
  351. package/dist/kit/forms/field.js +19 -0
  352. package/dist/kit/forms/field.js.map +1 -0
  353. package/dist/kit/forms/form.d.ts +10 -0
  354. package/dist/kit/forms/form.d.ts.map +1 -0
  355. package/dist/kit/forms/form.js +9 -0
  356. package/dist/kit/forms/form.js.map +1 -0
  357. package/dist/kit/forms/input.d.ts +14 -0
  358. package/dist/kit/forms/input.d.ts.map +1 -0
  359. package/dist/kit/forms/input.js +9 -0
  360. package/dist/kit/forms/input.js.map +1 -0
  361. package/dist/kit/forms/select.d.ts +21 -0
  362. package/dist/kit/forms/select.d.ts.map +1 -0
  363. package/dist/kit/forms/select.js +35 -0
  364. package/dist/kit/forms/select.js.map +1 -0
  365. package/dist/kit/forms/textarea.d.ts +12 -0
  366. package/dist/kit/forms/textarea.d.ts.map +1 -0
  367. package/dist/kit/forms/textarea.js +9 -0
  368. package/dist/kit/forms/textarea.js.map +1 -0
  369. package/dist/kit/index.d.ts +42 -0
  370. package/dist/kit/index.d.ts.map +1 -0
  371. package/dist/kit/index.js +45 -0
  372. package/dist/kit/index.js.map +1 -0
  373. package/dist/kit/kit-prompt.d.ts +7 -0
  374. package/dist/kit/kit-prompt.d.ts.map +1 -0
  375. package/dist/kit/kit-prompt.js +7 -0
  376. package/dist/kit/kit-prompt.js.map +1 -0
  377. package/dist/kit/layout.d.ts +29 -0
  378. package/dist/kit/layout.d.ts.map +1 -0
  379. package/dist/kit/layout.js +64 -0
  380. package/dist/kit/layout.js.map +1 -0
  381. package/dist/kit/registry.d.ts +11 -0
  382. package/dist/kit/registry.d.ts.map +1 -0
  383. package/dist/kit/registry.js +34 -0
  384. package/dist/kit/registry.js.map +1 -0
  385. package/dist/kit/schema.d.ts +9 -0
  386. package/dist/kit/schema.d.ts.map +1 -0
  387. package/dist/kit/schema.js +9 -0
  388. package/dist/kit/schema.js.map +1 -0
  389. package/dist/kit/tokens.d.ts +33 -0
  390. package/dist/kit/tokens.d.ts.map +1 -0
  391. package/dist/kit/tokens.js +48 -0
  392. package/dist/kit/tokens.js.map +1 -0
  393. package/dist/kit/values.d.ts +55 -0
  394. package/dist/kit/values.d.ts.map +1 -0
  395. package/dist/kit/values.js +108 -0
  396. package/dist/kit/values.js.map +1 -0
  397. package/dist/theme.d.ts +25 -0
  398. package/dist/theme.d.ts.map +1 -0
  399. package/dist/theme.js +100 -0
  400. package/dist/theme.js.map +1 -0
  401. package/dist/tree/bindings.d.ts +4 -0
  402. package/dist/tree/bindings.d.ts.map +1 -0
  403. package/dist/tree/bindings.js +20 -0
  404. package/dist/tree/bindings.js.map +1 -0
  405. package/dist/tree/branded.d.ts +124 -0
  406. package/dist/tree/branded.d.ts.map +1 -0
  407. package/dist/tree/branded.js +319 -0
  408. package/dist/tree/branded.js.map +1 -0
  409. package/dist/tree/error-boundary.d.ts +22 -0
  410. package/dist/tree/error-boundary.d.ts.map +1 -0
  411. package/dist/tree/error-boundary.js +25 -0
  412. package/dist/tree/error-boundary.js.map +1 -0
  413. package/dist/tree/fluid-reveal.d.ts +22 -0
  414. package/dist/tree/fluid-reveal.d.ts.map +1 -0
  415. package/dist/tree/fluid-reveal.js +28 -0
  416. package/dist/tree/fluid-reveal.js.map +1 -0
  417. package/dist/tree/forming-skeleton.d.ts +14 -0
  418. package/dist/tree/forming-skeleton.d.ts.map +1 -0
  419. package/dist/tree/forming-skeleton.js +36 -0
  420. package/dist/tree/forming-skeleton.js.map +1 -0
  421. package/dist/tree/frames.d.ts +52 -0
  422. package/dist/tree/frames.d.ts.map +1 -0
  423. package/dist/tree/frames.js +143 -0
  424. package/dist/tree/frames.js.map +1 -0
  425. package/dist/tree/host-mount.d.ts +24 -0
  426. package/dist/tree/host-mount.d.ts.map +1 -0
  427. package/dist/tree/host-mount.js +0 -0
  428. package/dist/tree/host-mount.js.map +1 -0
  429. package/dist/tree/index.d.ts +9 -0
  430. package/dist/tree/index.d.ts.map +1 -0
  431. package/dist/tree/index.js +9 -0
  432. package/dist/tree/index.js.map +1 -0
  433. package/dist/tree/jail/JailedComponent.d.ts +41 -0
  434. package/dist/tree/jail/JailedComponent.d.ts.map +1 -0
  435. package/dist/tree/jail/JailedComponent.js +269 -0
  436. package/dist/tree/jail/JailedComponent.js.map +1 -0
  437. package/dist/tree/jail/runtime-bundle.gen.d.ts +3 -0
  438. package/dist/tree/jail/runtime-bundle.gen.d.ts.map +1 -0
  439. package/dist/tree/jail/runtime-bundle.gen.js +3 -0
  440. package/dist/tree/jail/runtime-bundle.gen.js.map +1 -0
  441. package/dist/tree/jail/runtime-entry.d.ts +18 -0
  442. package/dist/tree/jail/runtime-entry.d.ts.map +1 -0
  443. package/dist/tree/jail/runtime-entry.js +440 -0
  444. package/dist/tree/jail/runtime-entry.js.map +1 -0
  445. package/dist/tree/jail/viewport-css.d.ts +17 -0
  446. package/dist/tree/jail/viewport-css.d.ts.map +1 -0
  447. package/dist/tree/jail/viewport-css.js +32 -0
  448. package/dist/tree/jail/viewport-css.js.map +1 -0
  449. package/dist/tree/mcp-shim/entry.d.ts +2 -0
  450. package/dist/tree/mcp-shim/entry.d.ts.map +1 -0
  451. package/dist/tree/mcp-shim/entry.js +45 -0
  452. package/dist/tree/mcp-shim/entry.js.map +1 -0
  453. package/dist/tree/mcp-shim/http-open-card.d.ts +5 -0
  454. package/dist/tree/mcp-shim/http-open-card.d.ts.map +1 -0
  455. package/dist/tree/mcp-shim/http-open-card.js +61 -0
  456. package/dist/tree/mcp-shim/http-open-card.js.map +1 -0
  457. package/dist/tree/mcp-shim/shim-core.d.ts +58 -0
  458. package/dist/tree/mcp-shim/shim-core.d.ts.map +1 -0
  459. package/dist/tree/mcp-shim/shim-core.js +176 -0
  460. package/dist/tree/mcp-shim/shim-core.js.map +1 -0
  461. package/dist/tree/mcp-shim/theme.d.ts +8 -0
  462. package/dist/tree/mcp-shim/theme.d.ts.map +1 -0
  463. package/dist/tree/mcp-shim/theme.js +36 -0
  464. package/dist/tree/mcp-shim/theme.js.map +1 -0
  465. package/dist/tree/notice.d.ts +8 -0
  466. package/dist/tree/notice.d.ts.map +1 -0
  467. package/dist/tree/notice.js +30 -0
  468. package/dist/tree/notice.js.map +1 -0
  469. package/dist/tree/primitives.d.ts +30 -0
  470. package/dist/tree/primitives.d.ts.map +1 -0
  471. package/dist/tree/primitives.js +105 -0
  472. package/dist/tree/primitives.js.map +1 -0
  473. package/dist/tree/renderer-v2.d.ts +15 -0
  474. package/dist/tree/renderer-v2.d.ts.map +1 -0
  475. package/dist/tree/renderer-v2.js +71 -0
  476. package/dist/tree/renderer-v2.js.map +1 -0
  477. package/dist/tree/renderer.d.ts +77 -0
  478. package/dist/tree/renderer.d.ts.map +1 -0
  479. package/dist/tree/renderer.js +376 -0
  480. package/dist/tree/renderer.js.map +1 -0
  481. package/dist/voice/driver.d.ts +94 -0
  482. package/dist/voice/driver.d.ts.map +1 -0
  483. package/dist/voice/driver.js +2 -0
  484. package/dist/voice/driver.js.map +1 -0
  485. package/dist/voice/index.d.ts +7 -0
  486. package/dist/voice/index.d.ts.map +1 -0
  487. package/dist/voice/index.js +5 -0
  488. package/dist/voice/index.js.map +1 -0
  489. package/dist/voice/realtime-driver.d.ts +57 -0
  490. package/dist/voice/realtime-driver.d.ts.map +1 -0
  491. package/dist/voice/realtime-driver.js +592 -0
  492. package/dist/voice/realtime-driver.js.map +1 -0
  493. package/dist/voice/stage.d.ts +12 -0
  494. package/dist/voice/stage.d.ts.map +1 -0
  495. package/dist/voice/stage.js +259 -0
  496. package/dist/voice/stage.js.map +1 -0
  497. package/dist/voice/use-voice-approvals.d.ts +16 -0
  498. package/dist/voice/use-voice-approvals.d.ts.map +1 -0
  499. package/dist/voice/use-voice-approvals.js +77 -0
  500. package/dist/voice/use-voice-approvals.js.map +1 -0
  501. package/dist/voice/use-voice.d.ts +26 -0
  502. package/dist/voice/use-voice.d.ts.map +1 -0
  503. package/dist/voice/use-voice.js +201 -0
  504. package/dist/voice/use-voice.js.map +1 -0
  505. package/dist/voice/voice-act.d.ts +13 -0
  506. package/dist/voice/voice-act.d.ts.map +1 -0
  507. package/dist/voice/voice-act.js +301 -0
  508. package/dist/voice/voice-act.js.map +1 -0
  509. package/dist/voice/voice-blob.d.ts +17 -0
  510. package/dist/voice/voice-blob.d.ts.map +1 -0
  511. package/dist/voice/voice-blob.js +67 -0
  512. package/dist/voice/voice-blob.js.map +1 -0
  513. package/dist/voice/voice-consent.d.ts +17 -0
  514. package/dist/voice/voice-consent.d.ts.map +1 -0
  515. package/dist/voice/voice-consent.js +45 -0
  516. package/dist/voice/voice-consent.js.map +1 -0
  517. package/dist/voice/voice-drawer.d.ts +10 -0
  518. package/dist/voice/voice-drawer.d.ts.map +1 -0
  519. package/dist/voice/voice-drawer.js +22 -0
  520. package/dist/voice/voice-drawer.js.map +1 -0
  521. package/dist/wire-types.d.ts +238 -0
  522. package/dist/wire-types.d.ts.map +1 -0
  523. package/dist/wire-types.js +2 -0
  524. package/dist/wire-types.js.map +1 -0
  525. package/package.json +106 -0
@@ -0,0 +1,422 @@
1
+ /**
2
+ * Ejected from @vendoai/ui v0.4.0 — yours to edit.
3
+ * Presentation only: data and wire logic still flow through @vendoai/ui
4
+ * imports, so protocol updates keep working while these pixels are yours.
5
+ */
6
+ import { isToolUIPart } from "ai";
7
+ import { useEffect, useMemo, useRef, useState } from "react";
8
+ import { useVendoDiscoverability, useVendoGreeting } from "../../context.js";
9
+ import { useVendoThread } from "../../hooks/use-vendo-thread.js";
10
+ import { StatusRibbon } from "../build-beat.js";
11
+ import { ChromeRoot } from "../chrome-root.js";
12
+ import { defaultVendoGreeting, hasSeen, markSeen, type VendoDiscoverability, type VendoGreeting } from "../discoverability.js";
13
+ import { MorphToast, type MorphToastProps } from "../morph-toast.js";
14
+ import { Composer, dragHasFiles, useComposer } from "./composer.js";
15
+ import { MessageList } from "./message-list.js";
16
+ import { useMessageWindow, useStickToBottom } from "./scrolling.js";
17
+ import { approvalByCall, riskByCall, userText } from "./message-data.js";
18
+
19
+ /** Lane pick 4B — a rich landing suggestion: two-line starter card. */
20
+ export interface VendoSuggestionCard {
21
+ /** Card headline (verb-first reads best: "Build a view"). */
22
+ title: string;
23
+ /** One concrete outcome line under the title. */
24
+ description: string;
25
+ /** Sent as the message on tap; defaults to the title. */
26
+ prompt?: string;
27
+ /** Optional host-supplied leading icon node. */
28
+ icon?: import("react").ReactNode;
29
+ }
30
+
31
+ export interface VendoThreadProps {
32
+ threadId?: string;
33
+ /** Landing headline shown above the composer while the thread is empty. */
34
+ greeting?: string;
35
+ /** Starter prompts on the empty landing; clicking sends one. Lane pick 4B —
36
+ * a plain string keeps today's pill chip; the object form renders a two-line
37
+ * starter card (title + concrete outcome, optional icon) with more scent. */
38
+ suggestions?: (string | VendoSuggestionCard)[];
39
+ /** Show a mic affordance in the composer that launches the host's voice surface. */
40
+ onVoice?: () => void;
41
+ /** ENG-222 — fires with the effective thread id once it is known, including
42
+ * the fresh `thr_` the server mints for a new conversation. Lets a host
43
+ * surface (e.g. VendoPage's sidebar) pull the new conversation into its list. */
44
+ onThreadId?: (threadId: string) => void;
45
+ /** The discoverability dial (ui-usage-dx §6), overriding the provider's:
46
+ * `"quiet"` disables the fire-once greeting-as-tutorial below. */
47
+ discoverability?: VendoDiscoverability;
48
+ /** Greeting-as-tutorial content (intro + prompt chips) overriding the
49
+ * provider's `greeting`. Distinct from `greeting` above (the returning-user
50
+ * landing headline) — this one renders once per user, ever. */
51
+ firstRunGreeting?: VendoGreeting;
52
+ /** Rendered directly above the composer in both landing and conversation
53
+ * layouts — the seam VendoOverlay uses for its command chip strip (the
54
+ * one-surface ⌘K design). Presentation-only; the thread never reads it. */
55
+ composerAccessory?: import("react").ReactNode;
56
+ }
57
+
58
+ /** 08-ui §4 — conversation chrome over the headless thread transport. */
59
+ export function VendoThread({
60
+ threadId,
61
+ greeting = "What can I help you build?",
62
+ suggestions = [],
63
+ onVoice,
64
+ onThreadId,
65
+ discoverability,
66
+ firstRunGreeting,
67
+ composerAccessory,
68
+ }: VendoThreadProps) {
69
+ const thread = useVendoThread(threadId);
70
+ // ui-usage-dx §6 — greeting-as-tutorial: the user's FIRST-ever conversation
71
+ // open (fresh thread only — an adopted thread with history is not a first
72
+ // open and does not burn the flag) renders the agent-voiced intro + starter
73
+ // chips locally. Presentation-only: nothing here touches the transport or
74
+ // the transcript; chips prefill the composer and never send.
75
+ const providerDial = useVendoDiscoverability();
76
+ const dial = discoverability ?? providerDial;
77
+ const contextGreeting = useVendoGreeting();
78
+ const tutorial = firstRunGreeting ?? contextGreeting ?? defaultVendoGreeting;
79
+ const [tutorialActive, setTutorialActive] = useState(false);
80
+ // Arming is REACTIVE, not mount-only: surfaces that don't remount their
81
+ // thread (VendoPage flips threadId props on one instance) become eligible
82
+ // later — e.g. when the page's dial gate opens on an explicit "New
83
+ // conversation". Burned on first showing (not on interaction) — a reload
84
+ // mid-look never replays it, per the once-per-user-ever rule.
85
+ const messageCount = thread.messages.length;
86
+ useEffect(() => {
87
+ if (tutorialActive || dial === "quiet" || threadId !== undefined) return;
88
+ if (messageCount > 0 || hasSeen("greeting")) return;
89
+ markSeen("greeting");
90
+ setTutorialActive(true);
91
+ }, [tutorialActive, dial, threadId, messageCount]);
92
+ // Once the landing is left (a turn exists, or the surface switches to a
93
+ // stored thread), the tutorial is done for good on this instance too — the
94
+ // burned flag keeps every later landing plain.
95
+ useEffect(() => {
96
+ if (tutorialActive && (messageCount > 0 || threadId !== undefined)) setTutorialActive(false);
97
+ }, [tutorialActive, messageCount, threadId]);
98
+ // ENG-222 — surface the effective (possibly server-minted) thread id upward.
99
+ const reportedThreadId = thread.threadId;
100
+ useEffect(() => {
101
+ if (reportedThreadId !== undefined) onThreadId?.(reportedThreadId);
102
+ }, [reportedThreadId, onThreadId]);
103
+ const busy = thread.status === "submitted" || thread.status === "streaming";
104
+ // busy is a content-revision signal for the scroll hook: turn-actions mount
105
+ // below the last turn when a stream settles, which changes the list height.
106
+ const scroll = useStickToBottom(thread.messages, threadId, busy);
107
+ const approvalCardRefs = useRef(new Map<string, HTMLDivElement | null>());
108
+ const [morph, setMorph] = useState<Omit<MorphToastProps, "onDone"> | null>(null);
109
+
110
+ // A build's approval lands below a tall generated view — off-screen — so it
111
+ // would sit unnoticed until the reader scrolls. When a NEW approval appears,
112
+ // bring it into view (and re-stick), so consent is never something you have
113
+ // to go hunting for.
114
+ const seenApprovalsRef = useRef<Set<string>>(new Set());
115
+ useEffect(() => {
116
+ const pending = thread.messages
117
+ .flatMap(message => message.parts)
118
+ .filter(part => isToolUIPart(part) && part.state === "approval-requested")
119
+ .map(part => (part as { approval?: { id?: string } }).approval?.id)
120
+ .filter((id): id is string => typeof id === "string");
121
+ const fresh = pending.find(id => !seenApprovalsRef.current.has(id));
122
+ seenApprovalsRef.current = new Set(pending);
123
+ if (fresh === undefined) return;
124
+ const timer = setTimeout(() => {
125
+ const card = approvalCardRefs.current.get(fresh)?.querySelector<HTMLElement>(".fl-approval");
126
+ // block: "end", not "center": a sibling surface sharing this pane's
127
+ // flex column (VendoStage docked below the thread, Maple's /vendo tab)
128
+ // can leave the list shorter than the card itself at a short viewport
129
+ // height — centering then crops evenly off BOTH edges, hiding the
130
+ // card's own Approve/Decline row behind whatever renders next in flow
131
+ // with no way to reach it. Bottom-aligning always leaves the action
132
+ // row — the part the reader actually needs — as the last thing in
133
+ // view, consistent with the list's own stick-to-bottom behavior.
134
+ if (card) card.scrollIntoView({ behavior: "smooth", block: "end" });
135
+ else scroll.jumpToLatest();
136
+ }, 80);
137
+ return () => clearTimeout(timer);
138
+ }, [thread.messages, scroll]);
139
+
140
+ const messageWindow = useMessageWindow(thread.messages, scroll.listRef, threadId);
141
+ // ENG-218 — entrance-animation gating on restore. The .fl-item-in rise runs
142
+ // when an article first mounts; a reopened long thread mounts them all at once
143
+ // → a stampede on first paint. We record every message id present when the
144
+ // thread is first shown (and after each switch) as "restored" and suppress
145
+ // the entrance on those; only turns that arrive AFTER restore (streamed
146
+ // replies, sends) animate. A ref, not state — read during render, no re-render.
147
+ const restoredIdsRef = useRef<{ key: string | undefined; ids: Set<string> }>({ key: undefined, ids: new Set() });
148
+ if (restoredIdsRef.current.key !== threadId) {
149
+ restoredIdsRef.current = { key: threadId, ids: new Set(thread.messages.map(message => message.id)) };
150
+ } else if (restoredIdsRef.current.ids.size === 0 && thread.messages.length > 0) {
151
+ // First non-empty render after an async history load (mount → list/get):
152
+ // that whole batch is a restore, not new arrivals.
153
+ restoredIdsRef.current.ids = new Set(thread.messages.map(message => message.id));
154
+ }
155
+ const isRestored = (id: string) => restoredIdsRef.current.ids.has(id);
156
+ const composerApi = useComposer({ busy, sendMessage: message => thread.sendMessage(message) });
157
+ const { setDraft, setQueued, textareaRef, send } = composerApi;
158
+ const risks = useMemo(() => riskByCall(thread.messages), [thread.messages]);
159
+ const guardApprovals = useMemo(() => approvalByCall(thread.messages), [thread.messages]);
160
+ const landing = thread.messages.length === 0;
161
+ const activeAssistant = thread.messages.at(-1)?.role === "assistant" ? thread.messages.at(-1) : undefined;
162
+ const assistantHasVisibleText = activeAssistant?.parts.some(
163
+ part => part.type === "text" && part.text.trim().length > 0,
164
+ ) ?? false;
165
+ // ENG-217 — the three streaming moments each get exactly ONE affordance:
166
+ // before the first chunk the generating skeleton holds the floor; a streamed
167
+ // turn whose text is still empty shows the lone caret (renderPart); once
168
+ // text flows the trailing caret rides .fl-md--streaming. FluidThinking
169
+ // covers the remaining gap (tool phases with no text yet).
170
+ const awaitingFirstChunk = busy && (activeAssistant === undefined || activeAssistant.parts.length === 0);
171
+ const lastPart = activeAssistant?.parts.at(-1);
172
+ const caretShowing = busy && lastPart?.type === "text" && lastPart.state === "streaming"
173
+ && lastPart.text.trim().length === 0;
174
+ // Once ANY build beat exists in the active turn, the checklist is the
175
+ // progress voice — the thinking indicator between beats reads as two
176
+ // indicators fighting.
177
+ const hasBeats = activeAssistant?.parts.some(part => isToolUIPart(part)) ?? false;
178
+ const working = busy && !assistantHasVisibleText && !awaitingFirstChunk && !caretShowing && !hasBeats;
179
+
180
+ // ENG-215 — edit the last user turn: drop it (and anything after) from the
181
+ // transcript and refill the composer, so re-sending amends rather than
182
+ // duplicates. Only meaningful when idle.
183
+ const lastUserIndex = (() => {
184
+ for (let index = thread.messages.length - 1; index >= 0; index -= 1) {
185
+ if (thread.messages[index]?.role === "user") return index;
186
+ }
187
+ return -1;
188
+ })();
189
+ // Turn actions attach by id, not list index: the map below renders a windowed
190
+ // slice (ENG-218), so positional indices no longer line up with thread.messages.
191
+ const lastUserId = lastUserIndex >= 0 ? thread.messages[lastUserIndex]?.id : undefined;
192
+ const editLast = () => {
193
+ if (busy || lastUserIndex < 0) return;
194
+ const message = thread.messages[lastUserIndex];
195
+ if (!message) return;
196
+ thread.setMessages(thread.messages.slice(0, lastUserIndex));
197
+ setQueued(null);
198
+ setDraft(userText(message));
199
+ requestAnimationFrame(() => textareaRef.current?.focus());
200
+ };
201
+
202
+ // ENG-215 — regenerate the last assistant turn (re-issues from the preserved
203
+ // user message; no duplication). Only when idle and an assistant turn exists.
204
+ const lastAssistantIndex = (() => {
205
+ for (let index = thread.messages.length - 1; index >= 0; index -= 1) {
206
+ if (thread.messages[index]?.role === "assistant") return index;
207
+ }
208
+ return -1;
209
+ })();
210
+ const lastAssistantId = lastAssistantIndex >= 0 ? thread.messages[lastAssistantIndex]?.id : undefined;
211
+ const regenerateLast = () => {
212
+ if (busy || lastAssistantIndex < 0) return;
213
+ void thread.regenerate();
214
+ };
215
+
216
+ // ENG-214 — a broken turn (failed send, mid-stream drop, any thread.error)
217
+ // surfaces VISIBLY in the thread, not only through the hidden status span.
218
+ // The copy stays friendly — raw transport errors are announced to assistive
219
+ // tech below but never printed to end users. Retry regenerates the failed
220
+ // turn from the preserved user message (no duplication).
221
+ const errorBanner = thread.error ? (
222
+ <div className="fl-error">
223
+ <span>Something went wrong and the response didn&rsquo;t finish.</span>
224
+ <button
225
+ type="button"
226
+ className="fl-error-retry"
227
+ onClick={() => {
228
+ // Nothing to re-issue (sends append the user turn before any request
229
+ // fires, so this is a defensive rail): degrade to dismissing the
230
+ // error instead of letting regenerate() throw on an empty thread.
231
+ if (thread.messages.length === 0) {
232
+ thread.clearError();
233
+ return;
234
+ }
235
+ void thread.regenerate();
236
+ }}
237
+ >
238
+ Retry
239
+ </button>
240
+ </div>
241
+ ) : null;
242
+
243
+ const composer = (
244
+ <Composer
245
+ composer={composerApi}
246
+ busy={busy}
247
+ status={thread.status}
248
+ errorMessage={thread.status === "error" && thread.error ? thread.error.message : undefined}
249
+ onStop={() => void thread.stop()}
250
+ onVoice={onVoice}
251
+ />
252
+ );
253
+
254
+ const approvals = thread.messages.flatMap(message => message.parts).filter(isToolUIPart).filter(part => part.state === "approval-requested");
255
+
256
+ // Lane pick C1 — the live status ribbon: while the turn works through tool
257
+ // calls (and no text has started flowing), the ACTIVE call narrates above
258
+ // the composer — label · elapsed · step N of M. The transcript stays
259
+ // beat-free (parts.tsx renders only errored calls). Once text streams the
260
+ // ribbon yields the floor to the caret choreography.
261
+ const activeToolParts = (activeAssistant?.parts ?? []).filter(isToolUIPart);
262
+ const liveToolPart = [...activeToolParts].reverse()
263
+ .find(part => part.state !== "output-available" && part.state !== "output-error");
264
+ // A turn parked on an approval is not "busy" (the stream yielded), but the
265
+ // pause still narrates: the ribbon holds "— waiting for your approval" while
266
+ // the card sits in the transcript.
267
+ const awaitingApprovalPart = activeToolParts.find(part => part.state === "approval-requested");
268
+ const activeToolPart = busy && !assistantHasVisibleText
269
+ ? liveToolPart ?? (activeToolParts.length > 0 && !caretShowing ? activeToolParts.at(-1) : undefined)
270
+ : awaitingApprovalPart;
271
+ const ribbon = activeToolPart ? (
272
+ <StatusRibbon
273
+ part={activeToolPart}
274
+ stepIndex={activeToolParts.indexOf(activeToolPart) + 1}
275
+ stepTotal={activeToolParts.length}
276
+ risk={risks.get(activeToolPart.toolCallId) ?? "read"}
277
+ />
278
+ ) : null;
279
+
280
+ // Lane pick 2E — the WHOLE thread surface is the drop target (the composer
281
+ // bar no longer owns drag): a huge, overshoot-proof zone with a centered
282
+ // card naming what will happen. Depth counter as before (child crossings).
283
+ const { dragDepth, setDragDepth, setFiles } = composerApi;
284
+ const dropProps = {
285
+ onDragEnter: (event: React.DragEvent) => {
286
+ if (!dragHasFiles(event)) return;
287
+ event.preventDefault();
288
+ setDragDepth(depth => depth + 1);
289
+ },
290
+ onDragOver: (event: React.DragEvent) => {
291
+ if (dragHasFiles(event)) event.preventDefault();
292
+ },
293
+ onDragLeave: (event: React.DragEvent) => {
294
+ if (dragHasFiles(event)) setDragDepth(depth => Math.max(0, depth - 1));
295
+ },
296
+ onDrop: (event: React.DragEvent) => {
297
+ if (!dragHasFiles(event)) return;
298
+ event.preventDefault();
299
+ setDragDepth(0);
300
+ const dropped = Array.from(event.dataTransfer.files);
301
+ if (dropped.length > 0) setFiles(current => [...current, ...dropped]);
302
+ },
303
+ };
304
+ const dropOverlay = dragDepth > 0 ? (
305
+ <div className="fl-drop fl-drop--thread">
306
+ <div className="fl-drop-card">
307
+ <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
308
+ <path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" />
309
+ </svg>
310
+ Drop files to attach to your message
311
+ </div>
312
+ </div>
313
+ ) : null;
314
+
315
+ if (landing) {
316
+ return (
317
+ <ChromeRoot>
318
+ <div className="fl-thread" role="region" aria-label="Vendo conversation" {...dropProps}>
319
+ {dropOverlay}
320
+ <div className="fl-landing">
321
+ {tutorialActive ? (
322
+ // The one-time tutorial replaces the headline (and the host's
323
+ // send-on-tap suggestion chips — two chip rows with different
324
+ // behaviors would read as one). Chips PREFILL, never send.
325
+ <div className="fl-greeting" role="group" aria-label="Getting started">
326
+ <p className="fl-greeting-intro">{tutorial.intro}</p>
327
+ <div className="fl-chips fl-greeting-chips">
328
+ {tutorial.prompts.slice(0, 3).map((text, i) => (
329
+ <button
330
+ type="button"
331
+ className="fl-chip"
332
+ key={`${i}-${text}`}
333
+ onClick={() => {
334
+ setDraft(text);
335
+ requestAnimationFrame(() => textareaRef.current?.focus());
336
+ }}
337
+ >
338
+ {text}
339
+ </button>
340
+ ))}
341
+ </div>
342
+ </div>
343
+ ) : (
344
+ <h1 className="fl-greet">{greeting}</h1>
345
+ )}
346
+ {errorBanner}
347
+ {composerAccessory}
348
+ <div className="fl-landing-composer">{composer}</div>
349
+ {!tutorialActive && suggestions.length > 0 ? (
350
+ // Lane pick 4B — object suggestions render as two-line starter
351
+ // cards (title + concrete outcome, optional host icon); plain
352
+ // strings keep the pill chip. A MIXED array renders both
353
+ // containers (cards grid, then a chips row) so string entries
354
+ // never stretch as grid cells (AI-review catch). Both send on
355
+ // tap, unchanged.
356
+ <>
357
+ {suggestions.some(s => typeof s !== "string") ? (
358
+ <div className="fl-cards">
359
+ {suggestions.flatMap((suggestion, i) => {
360
+ if (typeof suggestion === "string") return [];
361
+ const prompt = suggestion.prompt ?? suggestion.title;
362
+ return [(
363
+ <button type="button" className="fl-card" key={`${i}-${suggestion.title}`} onClick={() => send(prompt)}>
364
+ {suggestion.icon}
365
+ <b>{suggestion.title}</b>
366
+ <span>{suggestion.description}</span>
367
+ </button>
368
+ )];
369
+ })}
370
+ </div>
371
+ ) : null}
372
+ {suggestions.some(s => typeof s === "string") ? (
373
+ <div className="fl-chips">
374
+ {suggestions.flatMap((text, i) => (
375
+ typeof text === "string"
376
+ ? [<button type="button" className="fl-chip" key={`${i}-${text}`} onClick={() => send(text)}>{text}</button>]
377
+ : []
378
+ ))}
379
+ </div>
380
+ ) : null}
381
+ </>
382
+ ) : null}
383
+ </div>
384
+ </div>
385
+ </ChromeRoot>
386
+ );
387
+ }
388
+
389
+ return (
390
+ <ChromeRoot>
391
+ <div className="fl-thread" role="region" aria-label="Vendo conversation" {...dropProps}>
392
+ {dropOverlay}
393
+ <MessageList
394
+ scroll={scroll}
395
+ messageWindow={messageWindow}
396
+ busy={busy}
397
+ risks={risks}
398
+ isRestored={isRestored}
399
+ activeAssistantId={activeAssistant?.id}
400
+ lastUserId={lastUserId}
401
+ lastAssistantId={lastAssistantId}
402
+ onEditLast={editLast}
403
+ onRegenerateLast={regenerateLast}
404
+ approvals={approvals}
405
+ guardApprovals={guardApprovals}
406
+ cardRefs={approvalCardRefs}
407
+ respond={response => thread.addToolApprovalResponse(response)}
408
+ onMorph={setMorph}
409
+ messages={thread.messages}
410
+ sendMessage={message => thread.sendMessage(message)}
411
+ awaitingFirstChunk={awaitingFirstChunk}
412
+ working={working}
413
+ />
414
+ {errorBanner}
415
+ {composerAccessory}
416
+ {ribbon}
417
+ {composer}
418
+ </div>
419
+ {morph ? <MorphToast {...morph} onDone={() => setMorph(null)} /> : null}
420
+ </ChromeRoot>
421
+ );
422
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Ejected from @vendoai/ui v0.4.0 — yours to edit.
3
+ * Presentation only: data and wire logic still flow through @vendoai/ui
4
+ * imports, so protocol updates keep working while these pixels are yours.
5
+ */
6
+ import type { ApprovalRequest, RiskLabel } from "@vendoai/core";
7
+ import { isToolUIPart, type UIMessage } from "ai";
8
+ import { previewArgs } from "../humanize.js";
9
+ import { LONG_TEXT_CAP, truncateHead } from "../truncate.js";
10
+
11
+ export function partData(part: UIMessage["parts"][number]): unknown {
12
+ return "data" in part ? part.data : part;
13
+ }
14
+
15
+ // ENG-216 — a stable placeholder for the in-thread synthesized ApprovalRequest's
16
+ // required `createdAt`. The wire approval part carries no timestamp; this value
17
+ // is never displayed (the card hides the context byline in-thread) and a fixed
18
+ // constant replaces the former per-render `new Date()` that churned on every
19
+ // re-render and broke deterministic tests.
20
+ export const SYNTHESIZED_CREATED_AT = "1970-01-01T00:00:00.000Z";
21
+
22
+ export function riskByCall(messages: UIMessage[]): Map<string, RiskLabel> {
23
+ const risks = new Map<string, RiskLabel>();
24
+ for (const message of messages) {
25
+ for (const part of message.parts) {
26
+ if (part.type !== "data-vendo-approval") continue;
27
+ const data = partData(part) as { toolCallId?: unknown; risk?: unknown };
28
+ if (typeof data.toolCallId === "string" && ["read", "write", "destructive"].includes(String(data.risk))) {
29
+ risks.set(data.toolCallId, data.risk as RiskLabel);
30
+ }
31
+ }
32
+ }
33
+ return risks;
34
+ }
35
+
36
+ /** Guard approval metadata by tool call — carried in the data-vendo-approval
37
+ part beside the native ai-SDK approval (whose own id is transport-local). */
38
+ export function approvalByCall(messages: UIMessage[]): Map<string, {
39
+ approvalId?: string;
40
+ invalidatedGrant?: ApprovalRequest["invalidatedGrant"];
41
+ }> {
42
+ const approvals = new Map<string, {
43
+ approvalId?: string;
44
+ invalidatedGrant?: ApprovalRequest["invalidatedGrant"];
45
+ }>();
46
+ for (const message of messages) {
47
+ for (const part of message.parts) {
48
+ if (part.type !== "data-vendo-approval") continue;
49
+ const data = partData(part) as {
50
+ toolCallId?: unknown;
51
+ approvalId?: unknown;
52
+ invalidatedGrant?: { id?: unknown; grantedAt?: unknown };
53
+ };
54
+ if (typeof data.toolCallId !== "string") continue;
55
+ approvals.set(data.toolCallId, {
56
+ ...(typeof data.approvalId === "string" ? { approvalId: data.approvalId } : {}),
57
+ ...(typeof data.invalidatedGrant?.id === "string"
58
+ && typeof data.invalidatedGrant.grantedAt === "string"
59
+ ? { invalidatedGrant: data.invalidatedGrant as NonNullable<ApprovalRequest["invalidatedGrant"]> }
60
+ : {}),
61
+ });
62
+ }
63
+ }
64
+ return approvals;
65
+ }
66
+
67
+ export function toolName(part: Extract<UIMessage["parts"][number], { toolCallId: string }>): string {
68
+ return part.type === "dynamic-tool" && "toolName" in part ? part.toolName : part.type.replace(/^tool-/, "");
69
+ }
70
+
71
+ /** The app-boundary title: the payload's `name`, else its first heading Text node. */
72
+ export function appTitle(payload: unknown): string | undefined {
73
+ const named = (payload as { name?: unknown }).name;
74
+ if (typeof named === "string" && named.trim()) return named;
75
+ const nodes = (payload as { nodes?: Array<{ component?: string; props?: Record<string, unknown> }> }).nodes;
76
+ if (!Array.isArray(nodes)) return undefined;
77
+ for (const node of nodes) {
78
+ if (node.component === "Text" && node.props?.variant === "heading" && typeof node.props.text === "string") {
79
+ return node.props.text;
80
+ }
81
+ }
82
+ return undefined;
83
+ }
84
+
85
+ /** A stable signature for a tool part — same tool + same input = the same call. */
86
+ function toolSignature(part: Extract<UIMessage["parts"][number], { toolCallId: string }>): string {
87
+ const input = "input" in part ? part.input : undefined;
88
+ let serialized: string;
89
+ try {
90
+ serialized = JSON.stringify(input);
91
+ } catch {
92
+ serialized = String(input);
93
+ }
94
+ return `${toolName(part)}::${serialized}`;
95
+ }
96
+
97
+ /** ENG-216 — collapse runs of consecutive identical tool chips (e.g. eight
98
+ `host_listClientDocuments` calls) into one entry carrying a count. The
99
+ latest part in the run is kept so the chip icon reflects the final state. */
100
+ export function collapseToolRuns(
101
+ parts: UIMessage["parts"],
102
+ ): { part: UIMessage["parts"][number]; index: number; count: number }[] {
103
+ const items: { part: UIMessage["parts"][number]; index: number; count: number }[] = [];
104
+ parts.forEach((part, index) => {
105
+ const previous = items.at(-1);
106
+ if (
107
+ isToolUIPart(part)
108
+ && previous !== undefined
109
+ && isToolUIPart(previous.part)
110
+ && toolSignature(previous.part) === toolSignature(part)
111
+ ) {
112
+ previous.count += 1;
113
+ previous.part = part;
114
+ return;
115
+ }
116
+ items.push({ part, index, count: 1 });
117
+ });
118
+ return items;
119
+ }
120
+
121
+ /** The plain text a user turn carried, joined across its text parts — the seed
122
+ for "edit last message" (ENG-215). */
123
+ export function userText(message: UIMessage): string {
124
+ return message.parts
125
+ .filter((part): part is Extract<UIMessage["parts"][number], { type: "text" }> => part.type === "text")
126
+ .map(part => part.text)
127
+ .join("");
128
+ }
129
+
130
+ /** What "copy this turn" yields for an assistant message: its text parts (the
131
+ markdown source), blank-line separated — tool beats and views don't copy. */
132
+ export function assistantText(message: UIMessage): string {
133
+ return message.parts
134
+ .filter((part): part is Extract<UIMessage["parts"][number], { type: "text" }> => part.type === "text")
135
+ .map(part => part.text)
136
+ .join("\n\n");
137
+ }
138
+
139
+ /** ENG-216 — the in-thread approval preview is built client-side (the wire part
140
+ carries no descriptor), so format args as readable `Label: value` lines
141
+ instead of the raw JSON with literal \n escapes end users were reading. */
142
+ export function preview(input: unknown): string {
143
+ // ENG-216 — readable `Label: value` lines instead of raw JSON. ENG-218 — then
144
+ // bound the result before it reaches the DOM: a huge argument blob (dumped
145
+ // rows, base64) otherwise renders unbounded inside the approval card's <pre>,
146
+ // blowing up layout and the node count.
147
+ const formatted = previewArgs(input);
148
+ return formatted.length > LONG_TEXT_CAP
149
+ ? `${truncateHead(formatted)}\n… (${(formatted.length / 1000).toFixed(0)}k chars, truncated)`
150
+ : formatted;
151
+ }