@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,113 @@
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 { useId, useState } from "react";
7
+ import { useActivity } from "../hooks/use-activity.js";
8
+ import { useApprovals } from "../hooks/use-approvals.js";
9
+ import { useVendoTools } from "../context.js";
10
+ import { ActivityLedger } from "./activity-ledger.js";
11
+ import { ApprovalCard } from "./approval-card.js";
12
+ import { ChromeRoot } from "./chrome-root.js";
13
+
14
+ export interface VendoActivitiesProps {
15
+ /**
16
+ * Poll interval (ms) shared by the approvals queue and the activity feed, so
17
+ * an approval raised elsewhere (a thread turn, the MCP door, an automation)
18
+ * appears here on its own — no page remount. Default 5000; `0` disables
19
+ * polling (initial fetch only).
20
+ */
21
+ pollMs?: number;
22
+ /** Cap on recent-activity rows shown (this piece is a feed, not the full
23
+ * paginated audit — that stays `ActivityPanel`). Default 8. */
24
+ maxItems?: number;
25
+ }
26
+
27
+ /**
28
+ * The shelf's drop-in feed of what the agent did + what it is waiting on
29
+ * (ui-usage-dx §2) — placeable in any host page. Pending approvals render as
30
+ * ONE card with a "1 of N" pager (ui-lane-panels pick B): deciding slides the
31
+ * next approval into place instead of stacking a wall of cards. Recent
32
+ * activity renders as the shared icon ledger below. When nothing has happened
33
+ * yet it shows a quiet one-line empty state rather than rendering nothing —
34
+ * hosts place this in their own pages, and an invisible component would read
35
+ * as broken.
36
+ */
37
+ export function VendoActivities({ pollMs = 5000, maxItems = 8 }: VendoActivitiesProps = {}) {
38
+ const poll = pollMs > 0 ? { pollMs } : undefined;
39
+ const { pending, decide } = useApprovals(poll);
40
+ const { events, isLoading } = useActivity(poll);
41
+ const tools = useVendoTools();
42
+ const headingId = useId();
43
+ const recent = events.slice(0, Math.max(0, maxItems));
44
+
45
+ // The pager holds a POSITION, not an approval: deciding removes the current
46
+ // approval from `pending`, so the next one shifts into the same index and
47
+ // slides in (keyed by approval id). Clamp when the queue shrinks below it.
48
+ const [rawIndex, setRawIndex] = useState(0);
49
+ const index = Math.min(rawIndex, Math.max(0, pending.length - 1));
50
+ const current = pending[index];
51
+ const paged = pending.length > 1;
52
+
53
+ return (
54
+ <ChromeRoot>
55
+ <section aria-label="Vendo activity" style={{ display: "grid", gap: "14px" }}>
56
+ {current ? (
57
+ <section aria-labelledby={`${headingId}-approvals`} style={{ display: "grid", gap: "10px" }}>
58
+ <header>
59
+ <div className={paged ? "fl-approvals-pager" : undefined}>
60
+ <h2 id={`${headingId}-approvals`} className="fl-act-head-lbl" style={{ margin: 0, fontSize: "14px" }}>
61
+ Needs your approval
62
+ </h2>
63
+ {paged ? (
64
+ <>
65
+ <p className="fl-act-now" style={{ margin: 0, fontSize: "11.5px" }} aria-live="polite">
66
+ · {index + 1} of {pending.length}
67
+ </p>
68
+ <span className="fl-approvals-dots" aria-hidden="true">
69
+ {pending.map((approval, i) => (
70
+ <span key={approval.id} className={`fl-approvals-dot${i === index ? " fl-approvals-dot--on" : ""}`} />
71
+ ))}
72
+ </span>
73
+ </>
74
+ ) : null}
75
+ </div>
76
+ <p className="fl-act-now" style={{ margin: "3px 0 0", fontSize: "12.5px" }}>
77
+ An agent asked to act on your account. Review the exact request before it runs.
78
+ </p>
79
+ </header>
80
+ <div className="fl-approvals-stack">
81
+ <div key={current.id} className="fl-approvals-slide">
82
+ <ApprovalCard
83
+ approval={current}
84
+ onDecide={async decision => {
85
+ await decide(current.id, decision);
86
+ setRawIndex(value => Math.min(value, Math.max(0, pending.length - 2)));
87
+ }}
88
+ />
89
+ </div>
90
+ {index < pending.length - 1 ? <div className="fl-approvals-ghost" aria-hidden="true" /> : null}
91
+ </div>
92
+ </section>
93
+ ) : null}
94
+ <section className="fl-act" aria-labelledby={`${headingId}-recent`}>
95
+ <header className="fl-act-head" style={{ cursor: "default" }}>
96
+ <span className="fl-act-ic fl-act-tick" aria-hidden="true">✓</span>
97
+ <h2 id={`${headingId}-recent`} className="fl-act-head-lbl" style={{ margin: 0 }}>Recent activity</h2>
98
+ </header>
99
+ {recent.length === 0 ? (
100
+ <p className="fl-act-row fl-act-now">
101
+ {isLoading ? "Loading activity…" : "No recent agent activity yet."}
102
+ </p>
103
+ ) : (
104
+ <>
105
+ <p className="fl-act-cap" style={{ margin: 0 }}>Actions performed as your account</p>
106
+ <ActivityLedger events={recent} tools={tools} />
107
+ </>
108
+ )}
109
+ </section>
110
+ </section>
111
+ </ChromeRoot>
112
+ );
113
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "version": "0.4.0",
3
+ "surfaces": {
4
+ "thread": {
5
+ "description": "The conversation thread: composer, message list, parts, scrolling.",
6
+ "component": "VendoThread",
7
+ "sourceBase": "chrome/thread",
8
+ "sourceDir": "chrome/thread",
9
+ "files": [
10
+ "attachments.tsx",
11
+ "composer.tsx",
12
+ "index.tsx",
13
+ "message-data.ts",
14
+ "message-list.tsx",
15
+ "message.tsx",
16
+ "parts.tsx",
17
+ "scrolling.ts"
18
+ ]
19
+ },
20
+ "activities": {
21
+ "description": "The placeable activity piece: approvals queue + recent-runs feed.",
22
+ "component": "VendoActivities",
23
+ "sourceBase": "chrome",
24
+ "files": [
25
+ "index.tsx"
26
+ ]
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,69 @@
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 { UIMessage } from "ai";
7
+
8
+ /** A picked File → an ai-SDK FileUIPart (data URL) so it can ride the turn.
9
+ Lane pick 2F — the optional `onProgress` (0..1) feeds the chip's read ring:
10
+ attachments are now read eagerly at attach time (progress visible, failures
11
+ surfaced per-chip with retry) instead of silently at send time. */
12
+ export function fileToPart(
13
+ file: File,
14
+ onProgress?: (fraction: number) => void,
15
+ ): Promise<{ type: "file"; mediaType: string; filename: string; url: string }> {
16
+ return new Promise((resolve, reject) => {
17
+ const reader = new FileReader();
18
+ reader.onerror = () => reject(reader.error ?? new Error("file read failed"));
19
+ if (onProgress) {
20
+ reader.onprogress = event => {
21
+ if (event.lengthComputable && event.total > 0) onProgress(event.loaded / event.total);
22
+ };
23
+ }
24
+ reader.onload = () => {
25
+ onProgress?.(1);
26
+ resolve({
27
+ type: "file",
28
+ mediaType: file.type || "application/octet-stream",
29
+ filename: file.name,
30
+ url: String(reader.result),
31
+ });
32
+ };
33
+ reader.readAsDataURL(file);
34
+ });
35
+ }
36
+
37
+ /** The `.fl-att-ext` badge text — the filename's extension, bounded so a long
38
+ or missing one still reads as a badge. */
39
+ export function fileExt(name: string | undefined): string {
40
+ const ext = name?.match(/\.([a-z0-9]+)$/i)?.[1];
41
+ return (ext ?? "file").slice(0, 4).toUpperCase();
42
+ }
43
+
44
+ export function formatBytes(size: number): string {
45
+ if (size < 1024) return `${size} B`;
46
+ if (size < 1024 * 1024) return `${(size / 1024).toFixed(0)} KB`;
47
+ return `${(size / (1024 * 1024)).toFixed(1)} MB`;
48
+ }
49
+
50
+ export type FilePart = Extract<UIMessage["parts"][number], { type: "file" }>;
51
+
52
+ /** ENG-225 — a sent attachment in the transcript: images render as the designed
53
+ `.fl-msg-img` thumbnail, anything else as a `.fl-msg-file` download pill. */
54
+ export function SentAttachment({ part }: { part: FilePart }) {
55
+ const name = part.filename ?? "attachment";
56
+ if (part.mediaType?.startsWith("image/") === true) {
57
+ return (
58
+ <span className="fl-msg-img">
59
+ <img src={part.url} alt={name} />
60
+ </span>
61
+ );
62
+ }
63
+ return (
64
+ <a className="fl-msg-file" href={part.url} download={name}>
65
+ <span className="fl-att-ext" aria-hidden="true">{fileExt(part.filename)}</span>
66
+ <span className="fl-att-name">{name}</span>
67
+ </a>
68
+ );
69
+ }
@@ -0,0 +1,409 @@
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 { useContext, useEffect, useRef, useState } from "react";
7
+ import type { KeyboardEvent as ReactKeyboardEvent } from "react";
8
+ import { ConnectDockButton, ConnectTray } from "../connect-dock.js";
9
+ import { PrefillScopeContext, registerPrefillConsumer } from "../overlay-registry.js";
10
+ import { fileExt, fileToPart, formatBytes } from "./attachments.js";
11
+
12
+ /** The message shape the composer commits — mirrors useVendoThread.sendMessage. */
13
+ type OutgoingMessage = { text: string; files?: Awaited<ReturnType<typeof fileToPart>>[] };
14
+
15
+ /** Lane pick 2F — one attachment's eager-read lifecycle (drives the chip ring). */
16
+ type AttachmentRead = {
17
+ status: "reading" | "ready" | "error";
18
+ /** 0..1 read progress; meaningful while `reading`. */
19
+ progress: number;
20
+ part?: Awaited<ReturnType<typeof fileToPart>>;
21
+ };
22
+
23
+ /** ENG-225 — drag-drop attach: only reacts to drags that actually carry files
24
+ (text selections dragged across the composer must not flash the drop zone).
25
+ Exported for the thread-level drop surface (lane pick 2E). */
26
+ export const dragHasFiles = (event: React.DragEvent) =>
27
+ Array.from(event.dataTransfer?.types ?? []).includes("Files");
28
+
29
+ /** All composer state and send/queue mechanics, lifted to the thread level so
30
+ the draft (and queued slot) survive the landing ↔ transcript flip. The
31
+ Composer component below is the matching presentation. */
32
+ export function useComposer({ busy, sendMessage }: {
33
+ busy: boolean;
34
+ sendMessage: (message: OutgoingMessage) => unknown;
35
+ }) {
36
+ const [draft, setDraft] = useState("");
37
+ const fileRef = useRef<HTMLInputElement>(null);
38
+ const textareaRef = useRef<HTMLTextAreaElement>(null);
39
+ const [files, setFiles] = useState<File[]>([]);
40
+ // ENG-225 — drag-drop attach. A depth counter, not a boolean: dragging over
41
+ // the composer's children fires enter/leave pairs for every element crossed.
42
+ const [dragDepth, setDragDepth] = useState(0);
43
+ // ENG-225 — object-URL thumbnails for image attachments in the chip strip.
44
+ // Keyed by File identity; a URL is minted once per file and revoked only when
45
+ // that file leaves the set — never recreated for files still shown (which
46
+ // would briefly point a mounted <img> at a revoked URL, Devin review). The
47
+ // ref mirrors the state so the unmount cleanup revokes the final set.
48
+ const [attachmentPreviews, setAttachmentPreviews] = useState<Map<File, string>>(new Map());
49
+ const previewsRef = useRef(attachmentPreviews);
50
+ previewsRef.current = attachmentPreviews;
51
+ useEffect(() => {
52
+ if (typeof URL.createObjectURL !== "function") return;
53
+ setAttachmentPreviews(prev => {
54
+ const next = new Map<File, string>();
55
+ for (const file of files) {
56
+ if (!file.type.startsWith("image/")) continue;
57
+ next.set(file, prev.get(file) ?? URL.createObjectURL(file));
58
+ }
59
+ // Revoke only URLs for files that are no longer attached.
60
+ for (const [file, url] of prev) {
61
+ if (!next.has(file)) URL.revokeObjectURL(url);
62
+ }
63
+ return next;
64
+ });
65
+ }, [files]);
66
+ // Final cleanup on unmount: revoke whatever is still live.
67
+ useEffect(() => () => {
68
+ for (const url of previewsRef.current.values()) URL.revokeObjectURL(url);
69
+ }, []);
70
+ // Lane pick 2F — attachments read EAGERLY at attach time. Each file's read
71
+ // progress (FileReader onprogress) drives the chip ring; a failed read marks
72
+ // that chip (inline retry) instead of surfacing only as a text line at send.
73
+ // The finished part is cached so send doesn't re-read. Keyed by File identity,
74
+ // mirroring the previews map; entries leave with their file.
75
+ const [attachmentReads, setAttachmentReads] = useState<Map<File, AttachmentRead>>(new Map());
76
+ const readsRef = useRef(attachmentReads);
77
+ readsRef.current = attachmentReads;
78
+ const startRead = (file: File) => {
79
+ setAttachmentReads(prev => {
80
+ const next = new Map(prev);
81
+ next.set(file, { status: "reading", progress: 0 });
82
+ return next;
83
+ });
84
+ fileToPart(file, fraction => {
85
+ setAttachmentReads(prev => {
86
+ const current = prev.get(file);
87
+ if (!current || current.status !== "reading") return prev;
88
+ const next = new Map(prev);
89
+ next.set(file, { ...current, progress: fraction });
90
+ return next;
91
+ });
92
+ }).then(
93
+ part => setAttachmentReads(prev => {
94
+ if (!prev.has(file)) return prev;
95
+ const next = new Map(prev);
96
+ next.set(file, { status: "ready", progress: 1, part });
97
+ return next;
98
+ }),
99
+ () => setAttachmentReads(prev => {
100
+ if (!prev.has(file)) return prev;
101
+ const next = new Map(prev);
102
+ next.set(file, { status: "error", progress: 0 });
103
+ return next;
104
+ }),
105
+ );
106
+ };
107
+ useEffect(() => {
108
+ setAttachmentReads(prev => {
109
+ const next = new Map<File, AttachmentRead>();
110
+ for (const file of files) {
111
+ const existing = prev.get(file);
112
+ if (existing) next.set(file, existing);
113
+ }
114
+ return next.size === prev.size && files.every(f => prev.has(f)) ? prev : next;
115
+ });
116
+ for (const file of files) {
117
+ if (!readsRef.current.has(file)) startRead(file);
118
+ }
119
+ // startRead closes over stable setters only; files is the real trigger.
120
+ // eslint-disable-next-line react-hooks/exhaustive-deps
121
+ }, [files]);
122
+ // ENG-225 — the connect dock's liquid tray, anchored over the composer.
123
+ const [dockOpen, setDockOpen] = useState(false);
124
+ const dockButtonRef = useRef<HTMLButtonElement>(null);
125
+ // ENG-215 — a message the user sent DURING a turn: it parks here (visible as a
126
+ // pill) and auto-sends the instant the turn finishes. A single slot — a second
127
+ // send while one is parked replaces it — because there is only ever one "next"
128
+ // turn. Stop stays the explicit interrupt; queueing never cancels the stream.
129
+ const [queued, setQueued] = useState<{ text: string; files: File[] } | null>(null);
130
+ const [attachError, setAttachError] = useState<string>();
131
+
132
+ // ENG-215 — commit a turn to the transport (attachment parts come from the
133
+ // eager-read cache when ready, else a fresh read). Used both by an immediate
134
+ // send and by the deferred flush of a queued message.
135
+ const dispatch = (text: string, pending: File[]) => {
136
+ void (async () => {
137
+ let parts: Awaited<ReturnType<typeof fileToPart>>[];
138
+ try {
139
+ parts = await Promise.all(pending.map(file => {
140
+ const cached = readsRef.current.get(file);
141
+ return cached?.status === "ready" && cached.part ? Promise.resolve(cached.part) : fileToPart(file);
142
+ }));
143
+ } catch (reason) {
144
+ // A file read failed — surface it and restore the message so it never
145
+ // vanishes silently.
146
+ setAttachError(reason instanceof Error ? reason.message : "Couldn't read an attachment.");
147
+ setDraft(current => current || text);
148
+ setFiles(current => (current.length > 0 ? current : pending));
149
+ return;
150
+ }
151
+ setAttachError(undefined);
152
+ void sendMessage(parts.length > 0 ? { text, files: parts } : { text });
153
+ })();
154
+ };
155
+
156
+ const send = (override?: string) => {
157
+ const text = (override ?? draft).trim();
158
+ const pending = files;
159
+ if (!text && pending.length === 0) return;
160
+ // The message leaves the input immediately (whether it sends now or parks).
161
+ setDraft("");
162
+ setFiles([]);
163
+ if (fileRef.current) fileRef.current.value = "";
164
+ if (busy) {
165
+ setQueued({ text, files: pending });
166
+ return;
167
+ }
168
+ dispatch(text, pending);
169
+ };
170
+
171
+ // The enclosing overlay's prefill scope (null for embedded threads/pages):
172
+ // registry-delivered prompts are directed at one overlay's composer.
173
+ const prefillScope = useContext(PrefillScopeContext);
174
+ // The listeners below register once but must send with CURRENT composer
175
+ // state: a first-render `send` closure sees busy=false forever, so a remix
176
+ // fired mid-stream would dispatch concurrently instead of parking in the
177
+ // queued slot (the single-in-flight contract).
178
+ const sendRef = useRef(send);
179
+ sendRef.current = send;
180
+ // Remix bridge: a host affordance (slot remix, a trigger button, the legacy
181
+ // `vendo:prefill` event) opens this surface and hands it the request to
182
+ // type + send, so the whole build happens here — the one conversational
183
+ // place (08-ui §4). The registry consumer also drains a prompt parked while
184
+ // this composer was still mounting (overlay first open / fresh conversation).
185
+ useEffect(() => {
186
+ const prefill = (prompt: string, sendNow: boolean) => {
187
+ setDraft(prompt);
188
+ if (sendNow) queueMicrotask(() => sendRef.current(prompt));
189
+ };
190
+ const onPrefill = (event: Event) => {
191
+ const detail = (event as CustomEvent<{ prompt?: string; send?: boolean }>).detail;
192
+ if (typeof detail?.prompt !== "string") return;
193
+ prefill(detail.prompt, detail.send === true);
194
+ };
195
+ window.addEventListener("vendo:prefill", onPrefill);
196
+ const unregister = registerPrefillConsumer(parked => prefill(parked.prompt, parked.send), prefillScope);
197
+ return () => {
198
+ window.removeEventListener("vendo:prefill", onPrefill);
199
+ unregister();
200
+ };
201
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- sendRef tracks the latest send; scope is mount-stable
202
+ }, []);
203
+
204
+ // Flush the queued message the moment the active turn finishes. A ref-tracked
205
+ // busy edge keeps this from firing on unrelated re-renders.
206
+ const wasBusyRef = useRef(busy);
207
+ useEffect(() => {
208
+ if (wasBusyRef.current && !busy && queued) {
209
+ const pending = queued;
210
+ setQueued(null);
211
+ dispatch(pending.text, pending.files);
212
+ }
213
+ wasBusyRef.current = busy;
214
+ // dispatch is recreated each render but closes only over stable setters and
215
+ // thread.sendMessage; the busy edge + queued slot are the real triggers.
216
+ // eslint-disable-next-line react-hooks/exhaustive-deps
217
+ }, [busy, queued]);
218
+
219
+ // ENG-215 — autogrow: the textarea tracks its content height (CSS caps it at
220
+ // max-height and scrolls past that). Runs on every draft change, including the
221
+ // programmatic reset on send and the refill on edit.
222
+ useEffect(() => {
223
+ const node = textareaRef.current;
224
+ if (!node) return;
225
+ node.style.height = "auto";
226
+ node.style.height = `${node.scrollHeight}px`;
227
+ }, [draft]);
228
+
229
+ return {
230
+ draft, setDraft, files, setFiles, dragDepth, setDragDepth,
231
+ attachmentPreviews, attachmentReads, retryRead: startRead,
232
+ dockOpen, setDockOpen, dockButtonRef,
233
+ queued, setQueued, attachError, fileRef, textareaRef, send,
234
+ };
235
+ }
236
+
237
+ export type ComposerApi = ReturnType<typeof useComposer>;
238
+
239
+ export interface ComposerProps {
240
+ composer: ComposerApi;
241
+ busy: boolean;
242
+ /** The live transport status + error for the sr-only announcement span. */
243
+ status: string;
244
+ errorMessage?: string;
245
+ onStop: () => void;
246
+ onVoice?: (() => void) | undefined;
247
+ }
248
+
249
+ /** The message composer (08-ui §4): attachments, drag-drop, queueing, dock. */
250
+ export function Composer({ composer, busy, status, errorMessage, onStop, onVoice }: ComposerProps) {
251
+ const {
252
+ draft, setDraft, files, setFiles,
253
+ attachmentPreviews, attachmentReads, retryRead,
254
+ dockOpen, setDockOpen, dockButtonRef,
255
+ queued, setQueued, attachError, fileRef, textareaRef, send,
256
+ } = composer;
257
+ return (
258
+ <div className="fl-dock-anchor">
259
+ {dockOpen ? (
260
+ <ConnectTray
261
+ anchorRef={dockButtonRef}
262
+ onClose={() => {
263
+ setDockOpen(false);
264
+ queueMicrotask(() => dockButtonRef.current?.focus());
265
+ }}
266
+ />
267
+ ) : null}
268
+ {/* Lane pick 2E — drag-drop moved UP to the whole thread surface (see
269
+ VendoThread): the bar itself no longer owns enter/leave/drop. */}
270
+ <form
271
+ className="fl-composer"
272
+ aria-label="Message composer"
273
+ onSubmit={event => { event.preventDefault(); send(); }}
274
+ >
275
+ {attachError ? <div className="fl-att-error" role="alert">{attachError}</div> : null}
276
+ {queued ? (
277
+ <div className="fl-queued" role="status" aria-live="polite">
278
+ <span className="fl-queued-tag">Queued</span>
279
+ <span className="fl-queued-text">{queued.text || `${queued.files.length} attachment(s)`}</span>
280
+ <span className="fl-queued-hint">sends when the reply finishes</span>
281
+ {/* Lane pick 2B — Send now: stop the stream; the ENG-215 busy-edge
282
+ flush then dispatches this queued slot immediately. One code
283
+ path for both the polite wait and the deliberate interrupt. */}
284
+ <button type="button" className="fl-queued-now" onClick={onStop}>Send now</button>
285
+ <button type="button" className="fl-att-rm fl-queued-rm" aria-label="Cancel queued message" onClick={() => setQueued(null)}>×</button>
286
+ </div>
287
+ ) : null}
288
+ {files.length > 0 ? (
289
+ <div className="fl-att-chips">
290
+ {files.map((file, i) => {
291
+ const preview = attachmentPreviews.get(file);
292
+ const read = attachmentReads.get(file);
293
+ // An errored image renders as the file-style error chip, so its
294
+ // remove button needs the file-chip placement too.
295
+ const asFileChip = preview === undefined || read?.status === "error";
296
+ const remove = (
297
+ <button type="button" className={`fl-att-rm${asFileChip ? " fl-att-rm-file" : ""}`} aria-label={`Remove ${file.name}`}
298
+ onClick={() => setFiles(current => current.filter((_, j) => j !== i))}>×</button>
299
+ );
300
+ // ENG-225 — images preview as the designed thumbnail chip; other
301
+ // files carry an extension badge plus name and size. An image whose
302
+ // READ failed falls through to the error file-chip below (retry in
303
+ // place) instead of silently posing as attachable — the object-URL
304
+ // thumbnail says nothing about whether FileReader could read it
305
+ // (AI-review catch).
306
+ if (preview !== undefined && read?.status !== "error") {
307
+ return (
308
+ <span className="fl-att-img" key={`${file.name}-${i}`}>
309
+ <img src={preview} alt={file.name} />
310
+ {remove}
311
+ </span>
312
+ );
313
+ }
314
+ // Lane pick 2F — the chip narrates its read: progress ring while
315
+ // reading, error + inline retry on failure, quiet size when ready.
316
+ const failed = read?.status === "error";
317
+ const reading = read?.status === "reading";
318
+ const ring = 2 * Math.PI * 7;
319
+ return (
320
+ <span className={`fl-att-file${failed ? " fl-att-file--error" : ""}`} key={`${file.name}-${i}`}>
321
+ {reading ? (
322
+ <span className="fl-att-ring" aria-hidden="true">
323
+ <svg width="18" height="18" viewBox="0 0 18 18">
324
+ <circle className="fl-att-ring-bg" cx="9" cy="9" r="7" />
325
+ <circle className="fl-att-ring-fg" cx="9" cy="9" r="7"
326
+ strokeDasharray={ring} strokeDashoffset={ring * (1 - (read?.progress ?? 0))} />
327
+ </svg>
328
+ </span>
329
+ ) : (
330
+ <span className="fl-att-ext" aria-hidden="true">{fileExt(file.name)}</span>
331
+ )}
332
+ <span className="fl-att-meta">
333
+ <span className="fl-att-name">{file.name}</span>
334
+ {failed ? (
335
+ <small className="fl-att-fail" role="alert">
336
+ couldn&rsquo;t read — <button type="button" className="fl-att-retry" onClick={() => retryRead(file)}>retry</button>
337
+ </small>
338
+ ) : (
339
+ <small>{reading ? "reading…" : formatBytes(file.size)}</small>
340
+ )}
341
+ </span>
342
+ {remove}
343
+ </span>
344
+ );
345
+ })}
346
+ </div>
347
+ ) : null}
348
+ <div className="fl-composer-row">
349
+ <input ref={fileRef} type="file" multiple hidden aria-hidden="true"
350
+ onChange={event => { if (event.target.files) setFiles(current => [...current, ...Array.from(event.target.files!)]); }} />
351
+ <ConnectDockButton ref={dockButtonRef} open={dockOpen} onToggle={() => setDockOpen(value => !value)} />
352
+ <button type="button" className="fl-icon-btn fl-attach" aria-label="Attach files" onClick={() => fileRef.current?.click()}>
353
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
354
+ <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" />
355
+ </svg>
356
+ </button>
357
+ <label style={{ display: "contents" }}>
358
+ <span className="fl-sr-only">Message</span>
359
+ <textarea
360
+ ref={textareaRef}
361
+ aria-label="Message"
362
+ placeholder="Ask anything"
363
+ rows={1}
364
+ value={draft}
365
+ // ENG-215 — never disabled: typing (and queueing) stays live through
366
+ // the whole turn, and the composer never dumps focus to <body>.
367
+ onChange={event => setDraft(event.currentTarget.value)}
368
+ onKeyDown={(event: ReactKeyboardEvent<HTMLTextAreaElement>) => {
369
+ if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); send(); }
370
+ }}
371
+ />
372
+ </label>
373
+ {onVoice ? (
374
+ <button type="button" className="fl-icon-btn" aria-label="Start voice" onClick={onVoice}>
375
+ <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
376
+ <rect x="9" y="2" width="6" height="12" rx="3" /><path d="M5 10a7 7 0 0 0 14 0M12 19v3" />
377
+ </svg>
378
+ </button>
379
+ ) : null}
380
+ {/* ENG-215 — Stop is the explicit interrupt (only mid-turn); Send is
381
+ always available and, during a turn, queues the message instead. */}
382
+ {busy ? (
383
+ <button className="fl-icon-btn fl-stop" type="button" aria-label="Stop" onClick={onStop}>
384
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2.5" /></svg>
385
+ <span className="fl-sr-only">Stop</span>
386
+ </button>
387
+ ) : null}
388
+ <button className="fl-icon-btn fl-send" type="submit" aria-label="Send" disabled={!draft.trim() && files.length === 0}>
389
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
390
+ <path d="M12 19V5" /><path d="m5 12 7-7 7 7" />
391
+ </svg>
392
+ <span className="fl-sr-only">Send</span>
393
+ </button>
394
+ </div>
395
+ {/* Lane pick 2C — focus bloom: a one-line hint row that exists only
396
+ while the composer holds focus (pure CSS via :focus-within). Teaches
397
+ the hidden powers exactly when attention is on the bar. */}
398
+ <div className="fl-hintrow" aria-hidden="true">
399
+ <span><kbd className="fl-kbd">⇧↵</kbd> new line</span>
400
+ <span><kbd className="fl-kbd">⌘K</kbd> commands</span>
401
+ <span>drop files anywhere</span>
402
+ </div>
403
+ <span role="status" aria-live="polite" className="fl-sr-only">
404
+ {errorMessage !== undefined ? `error: ${errorMessage}` : status}
405
+ </span>
406
+ </form>
407
+ </div>
408
+ );
409
+ }