@agent-native/toolkit 0.5.1 → 0.7.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 (349) hide show
  1. package/README.md +17 -1
  2. package/dist/chat-history/ChatHistoryList.d.ts +103 -0
  3. package/dist/chat-history/ChatHistoryList.d.ts.map +1 -0
  4. package/dist/chat-history/ChatHistoryList.js +109 -0
  5. package/dist/chat-history/ChatHistoryList.js.map +1 -0
  6. package/dist/chat-history/ChatHistoryList.spec.d.ts +2 -0
  7. package/dist/chat-history/ChatHistoryList.spec.d.ts.map +1 -0
  8. package/dist/chat-history/ChatHistoryList.spec.js +283 -0
  9. package/dist/chat-history/ChatHistoryList.spec.js.map +1 -0
  10. package/dist/chat-history/index.d.ts +2 -0
  11. package/dist/chat-history/index.d.ts.map +1 -0
  12. package/dist/chat-history/index.js +2 -0
  13. package/dist/chat-history/index.js.map +1 -0
  14. package/dist/chat-history.css +314 -0
  15. package/dist/composer/AgentComposerFrame.d.ts +19 -0
  16. package/dist/composer/AgentComposerFrame.d.ts.map +1 -0
  17. package/dist/composer/AgentComposerFrame.js +14 -0
  18. package/dist/composer/AgentComposerFrame.js.map +1 -0
  19. package/dist/composer/ComposerPlusMenu.d.ts +17 -0
  20. package/dist/composer/ComposerPlusMenu.d.ts.map +1 -0
  21. package/dist/composer/ComposerPlusMenu.js +501 -0
  22. package/dist/composer/ComposerPlusMenu.js.map +1 -0
  23. package/dist/composer/MentionPopover.d.ts +29 -0
  24. package/dist/composer/MentionPopover.d.ts.map +1 -0
  25. package/dist/composer/MentionPopover.js +180 -0
  26. package/dist/composer/MentionPopover.js.map +1 -0
  27. package/dist/composer/PastedTextChip.d.ts +9 -0
  28. package/dist/composer/PastedTextChip.d.ts.map +1 -0
  29. package/dist/composer/PastedTextChip.js +47 -0
  30. package/dist/composer/PastedTextChip.js.map +1 -0
  31. package/dist/composer/PromptComposer.d.ts +114 -0
  32. package/dist/composer/PromptComposer.d.ts.map +1 -0
  33. package/dist/composer/PromptComposer.js +302 -0
  34. package/dist/composer/PromptComposer.js.map +1 -0
  35. package/dist/composer/PromptComposer.spec.d.ts +2 -0
  36. package/dist/composer/PromptComposer.spec.d.ts.map +1 -0
  37. package/dist/composer/PromptComposer.spec.js +64 -0
  38. package/dist/composer/PromptComposer.spec.js.map +1 -0
  39. package/dist/composer/RealtimeVoiceMode.d.ts +104 -0
  40. package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -0
  41. package/dist/composer/RealtimeVoiceMode.js +245 -0
  42. package/dist/composer/RealtimeVoiceMode.js.map +1 -0
  43. package/dist/composer/RealtimeVoiceMode.spec.d.ts +2 -0
  44. package/dist/composer/RealtimeVoiceMode.spec.d.ts.map +1 -0
  45. package/dist/composer/RealtimeVoiceMode.spec.js +439 -0
  46. package/dist/composer/RealtimeVoiceMode.spec.js.map +1 -0
  47. package/dist/composer/TiptapComposer.d.ts +221 -0
  48. package/dist/composer/TiptapComposer.d.ts.map +1 -0
  49. package/dist/composer/TiptapComposer.js +1838 -0
  50. package/dist/composer/TiptapComposer.js.map +1 -0
  51. package/dist/composer/TiptapComposer.spec.d.ts +2 -0
  52. package/dist/composer/TiptapComposer.spec.d.ts.map +1 -0
  53. package/dist/composer/TiptapComposer.spec.js +277 -0
  54. package/dist/composer/TiptapComposer.spec.js.map +1 -0
  55. package/dist/composer/VoiceButton.d.ts +25 -0
  56. package/dist/composer/VoiceButton.d.ts.map +1 -0
  57. package/dist/composer/VoiceButton.js +100 -0
  58. package/dist/composer/VoiceButton.js.map +1 -0
  59. package/dist/composer/VoiceButton.spec.d.ts +2 -0
  60. package/dist/composer/VoiceButton.spec.d.ts.map +1 -0
  61. package/dist/composer/VoiceButton.spec.js +19 -0
  62. package/dist/composer/VoiceButton.spec.js.map +1 -0
  63. package/dist/composer/asset-picker-url.d.ts +16 -0
  64. package/dist/composer/asset-picker-url.d.ts.map +1 -0
  65. package/dist/composer/asset-picker-url.js +64 -0
  66. package/dist/composer/asset-picker-url.js.map +1 -0
  67. package/dist/composer/asset-picker-url.spec.d.ts +2 -0
  68. package/dist/composer/asset-picker-url.spec.d.ts.map +1 -0
  69. package/dist/composer/asset-picker-url.spec.js +23 -0
  70. package/dist/composer/asset-picker-url.spec.js.map +1 -0
  71. package/dist/composer/attachment-accept.d.ts +9 -0
  72. package/dist/composer/attachment-accept.d.ts.map +1 -0
  73. package/dist/composer/attachment-accept.js +65 -0
  74. package/dist/composer/attachment-accept.js.map +1 -0
  75. package/dist/composer/attachment-accept.spec.d.ts +2 -0
  76. package/dist/composer/attachment-accept.spec.d.ts.map +1 -0
  77. package/dist/composer/attachment-accept.spec.js +15 -0
  78. package/dist/composer/attachment-accept.spec.js.map +1 -0
  79. package/dist/composer/draft-key.d.ts +2 -0
  80. package/dist/composer/draft-key.d.ts.map +1 -0
  81. package/dist/composer/draft-key.js +8 -0
  82. package/dist/composer/draft-key.js.map +1 -0
  83. package/dist/composer/draft-key.spec.d.ts +2 -0
  84. package/dist/composer/draft-key.spec.d.ts.map +1 -0
  85. package/dist/composer/draft-key.spec.js +13 -0
  86. package/dist/composer/draft-key.spec.js.map +1 -0
  87. package/dist/composer/extensions/FileReference.d.ts +3 -0
  88. package/dist/composer/extensions/FileReference.d.ts.map +1 -0
  89. package/dist/composer/extensions/FileReference.js +36 -0
  90. package/dist/composer/extensions/FileReference.js.map +1 -0
  91. package/dist/composer/extensions/MentionReference.d.ts +3 -0
  92. package/dist/composer/extensions/MentionReference.d.ts.map +1 -0
  93. package/dist/composer/extensions/MentionReference.js +66 -0
  94. package/dist/composer/extensions/MentionReference.js.map +1 -0
  95. package/dist/composer/extensions/SkillReference.d.ts +3 -0
  96. package/dist/composer/extensions/SkillReference.d.ts.map +1 -0
  97. package/dist/composer/extensions/SkillReference.js +40 -0
  98. package/dist/composer/extensions/SkillReference.js.map +1 -0
  99. package/dist/composer/index.d.ts +14 -0
  100. package/dist/composer/index.d.ts.map +1 -0
  101. package/dist/composer/index.js +13 -0
  102. package/dist/composer/index.js.map +1 -0
  103. package/dist/composer/pasted-text.d.ts +31 -0
  104. package/dist/composer/pasted-text.d.ts.map +1 -0
  105. package/dist/composer/pasted-text.js +130 -0
  106. package/dist/composer/pasted-text.js.map +1 -0
  107. package/dist/composer/pasted-text.spec.d.ts +2 -0
  108. package/dist/composer/pasted-text.spec.d.ts.map +1 -0
  109. package/dist/composer/pasted-text.spec.js +107 -0
  110. package/dist/composer/pasted-text.spec.js.map +1 -0
  111. package/dist/composer/prompt-attachments.d.ts +18 -0
  112. package/dist/composer/prompt-attachments.d.ts.map +1 -0
  113. package/dist/composer/prompt-attachments.js +65 -0
  114. package/dist/composer/prompt-attachments.js.map +1 -0
  115. package/dist/composer/prompt-attachments.spec.d.ts +2 -0
  116. package/dist/composer/prompt-attachments.spec.d.ts.map +1 -0
  117. package/dist/composer/prompt-attachments.spec.js +63 -0
  118. package/dist/composer/prompt-attachments.spec.js.map +1 -0
  119. package/dist/composer/realtime-voice-audio-level.d.ts +14 -0
  120. package/dist/composer/realtime-voice-audio-level.d.ts.map +1 -0
  121. package/dist/composer/realtime-voice-audio-level.js +47 -0
  122. package/dist/composer/realtime-voice-audio-level.js.map +1 -0
  123. package/dist/composer/realtime-voice-audio-level.spec.d.ts +2 -0
  124. package/dist/composer/realtime-voice-audio-level.spec.d.ts.map +1 -0
  125. package/dist/composer/realtime-voice-audio-level.spec.js +25 -0
  126. package/dist/composer/realtime-voice-audio-level.spec.js.map +1 -0
  127. package/dist/composer/realtime-voice-transcript.d.ts +26 -0
  128. package/dist/composer/realtime-voice-transcript.d.ts.map +1 -0
  129. package/dist/composer/realtime-voice-transcript.js +103 -0
  130. package/dist/composer/realtime-voice-transcript.js.map +1 -0
  131. package/dist/composer/realtime-voice-transcript.spec.d.ts +2 -0
  132. package/dist/composer/realtime-voice-transcript.spec.d.ts.map +1 -0
  133. package/dist/composer/realtime-voice-transcript.spec.js +76 -0
  134. package/dist/composer/realtime-voice-transcript.spec.js.map +1 -0
  135. package/dist/composer/runtime-adapters.d.ts +163 -0
  136. package/dist/composer/runtime-adapters.d.ts.map +1 -0
  137. package/dist/composer/runtime-adapters.js +130 -0
  138. package/dist/composer/runtime-adapters.js.map +1 -0
  139. package/dist/composer/types.d.ts +60 -0
  140. package/dist/composer/types.d.ts.map +1 -0
  141. package/dist/composer/types.js +2 -0
  142. package/dist/composer/types.js.map +1 -0
  143. package/dist/composer/use-file-search.d.ts +6 -0
  144. package/dist/composer/use-file-search.d.ts.map +1 -0
  145. package/dist/composer/use-file-search.js +53 -0
  146. package/dist/composer/use-file-search.js.map +1 -0
  147. package/dist/composer/use-mention-search.d.ts +6 -0
  148. package/dist/composer/use-mention-search.d.ts.map +1 -0
  149. package/dist/composer/use-mention-search.js +75 -0
  150. package/dist/composer/use-mention-search.js.map +1 -0
  151. package/dist/composer/use-skills.d.ts +7 -0
  152. package/dist/composer/use-skills.d.ts.map +1 -0
  153. package/dist/composer/use-skills.js +40 -0
  154. package/dist/composer/use-skills.js.map +1 -0
  155. package/dist/composer/useRealtimeVoiceMode.d.ts +179 -0
  156. package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -0
  157. package/dist/composer/useRealtimeVoiceMode.js +1657 -0
  158. package/dist/composer/useRealtimeVoiceMode.js.map +1 -0
  159. package/dist/composer/useRealtimeVoiceMode.spec.d.ts +2 -0
  160. package/dist/composer/useRealtimeVoiceMode.spec.d.ts.map +1 -0
  161. package/dist/composer/useRealtimeVoiceMode.spec.js +618 -0
  162. package/dist/composer/useRealtimeVoiceMode.spec.js.map +1 -0
  163. package/dist/composer/useVoiceDictation.d.ts +57 -0
  164. package/dist/composer/useVoiceDictation.d.ts.map +1 -0
  165. package/dist/composer/useVoiceDictation.js +1004 -0
  166. package/dist/composer/useVoiceDictation.js.map +1 -0
  167. package/dist/composer/useVoiceDictation.spec.d.ts +2 -0
  168. package/dist/composer/useVoiceDictation.spec.d.ts.map +1 -0
  169. package/dist/composer/useVoiceDictation.spec.js +32 -0
  170. package/dist/composer/useVoiceDictation.spec.js.map +1 -0
  171. package/dist/editor/RegistryBlockContext.d.ts +68 -0
  172. package/dist/editor/RegistryBlockContext.d.ts.map +1 -0
  173. package/dist/editor/RegistryBlockContext.js +10 -0
  174. package/dist/editor/RegistryBlockContext.js.map +1 -0
  175. package/dist/editor/RegistryBlockNode.d.ts +66 -0
  176. package/dist/editor/RegistryBlockNode.d.ts.map +1 -0
  177. package/dist/editor/RegistryBlockNode.js +406 -0
  178. package/dist/editor/RegistryBlockNode.js.map +1 -0
  179. package/dist/editor/RegistryBlockNode.spec.d.ts +2 -0
  180. package/dist/editor/RegistryBlockNode.spec.d.ts.map +1 -0
  181. package/dist/editor/RegistryBlockNode.spec.js +215 -0
  182. package/dist/editor/RegistryBlockNode.spec.js.map +1 -0
  183. package/dist/editor/index.d.ts +2 -0
  184. package/dist/editor/index.d.ts.map +1 -1
  185. package/dist/editor/index.js +2 -0
  186. package/dist/editor/index.js.map +1 -1
  187. package/dist/editor/registrySlashCommands.d.ts +68 -0
  188. package/dist/editor/registrySlashCommands.d.ts.map +1 -0
  189. package/dist/editor/registrySlashCommands.js +51 -0
  190. package/dist/editor/registrySlashCommands.js.map +1 -0
  191. package/package.json +32 -1
  192. package/src/app-shell/header-actions.tsx +84 -0
  193. package/src/app-shell/index.ts +1 -0
  194. package/src/chat-history/ChatHistoryList.spec.tsx +453 -0
  195. package/src/chat-history/ChatHistoryList.tsx +469 -0
  196. package/src/chat-history/index.ts +7 -0
  197. package/src/chat-history.css +314 -0
  198. package/src/collab-ui/AgentPresenceChip.tsx +69 -0
  199. package/src/collab-ui/LiveCursorOverlay.tsx +241 -0
  200. package/src/collab-ui/PresenceBar.tsx +346 -0
  201. package/src/collab-ui/RecentEditHighlights.tsx +225 -0
  202. package/src/collab-ui/RemoteSelectionRings.tsx +227 -0
  203. package/src/collab-ui/agent-identity.ts +16 -0
  204. package/src/collab-ui/index.ts +39 -0
  205. package/src/collab-ui/lead-client.ts +32 -0
  206. package/src/collab-ui/types.spec.ts +18 -0
  207. package/src/collab-ui/types.ts +96 -0
  208. package/src/composer/AgentComposerFrame.tsx +59 -0
  209. package/src/composer/ComposerPlusMenu.tsx +983 -0
  210. package/src/composer/MentionPopover.tsx +412 -0
  211. package/src/composer/PastedTextChip.tsx +150 -0
  212. package/src/composer/PromptComposer.spec.ts +73 -0
  213. package/src/composer/PromptComposer.tsx +718 -0
  214. package/src/composer/RealtimeVoiceMode.spec.tsx +771 -0
  215. package/src/composer/RealtimeVoiceMode.tsx +856 -0
  216. package/src/composer/TiptapComposer.spec.ts +390 -0
  217. package/src/composer/TiptapComposer.tsx +2936 -0
  218. package/src/composer/VoiceButton.spec.ts +33 -0
  219. package/src/composer/VoiceButton.tsx +255 -0
  220. package/src/composer/asset-picker-url.spec.ts +56 -0
  221. package/src/composer/asset-picker-url.ts +80 -0
  222. package/src/composer/attachment-accept.spec.ts +22 -0
  223. package/src/composer/attachment-accept.ts +71 -0
  224. package/src/composer/draft-key.spec.ts +19 -0
  225. package/src/composer/draft-key.ts +7 -0
  226. package/src/composer/extensions/FileReference.tsx +55 -0
  227. package/src/composer/extensions/MentionReference.tsx +93 -0
  228. package/src/composer/extensions/SkillReference.tsx +56 -0
  229. package/src/composer/index.ts +79 -0
  230. package/src/composer/pasted-text.spec.ts +162 -0
  231. package/src/composer/pasted-text.ts +160 -0
  232. package/src/composer/prompt-attachments.spec.ts +80 -0
  233. package/src/composer/prompt-attachments.ts +93 -0
  234. package/src/composer/realtime-voice-audio-level.spec.ts +32 -0
  235. package/src/composer/realtime-voice-audio-level.ts +64 -0
  236. package/src/composer/realtime-voice-transcript.spec.ts +95 -0
  237. package/src/composer/realtime-voice-transcript.ts +134 -0
  238. package/src/composer/runtime-adapters.tsx +343 -0
  239. package/src/composer/types.ts +66 -0
  240. package/src/composer/use-file-search.ts +64 -0
  241. package/src/composer/use-mention-search.ts +90 -0
  242. package/src/composer/use-skills.ts +45 -0
  243. package/src/composer/useRealtimeVoiceMode.spec.ts +818 -0
  244. package/src/composer/useRealtimeVoiceMode.tsx +2117 -0
  245. package/src/composer/useVoiceDictation.spec.ts +45 -0
  246. package/src/composer/useVoiceDictation.ts +1191 -0
  247. package/src/context-ui/ContextMeter.tsx +101 -0
  248. package/src/context-ui/ContextSegmentRow.tsx +184 -0
  249. package/src/context-ui/ContextTreemap.tsx +125 -0
  250. package/src/context-ui/ContextXRayPanel.tsx +377 -0
  251. package/src/context-ui/format.ts +42 -0
  252. package/src/context-ui/index.ts +15 -0
  253. package/src/context-ui/types.ts +43 -0
  254. package/src/design-tweaks/index.ts +1 -0
  255. package/src/design-tweaks/visual-style-controls.tsx +1084 -0
  256. package/src/editor/BubbleToolbar.tsx +253 -0
  257. package/src/editor/CodeBlockNode.tsx +254 -0
  258. package/src/editor/DragHandle.spec.ts +588 -0
  259. package/src/editor/DragHandle.ts +1437 -0
  260. package/src/editor/ImageExtension.roundtrip.spec.ts +117 -0
  261. package/src/editor/ImageExtension.ts +306 -0
  262. package/src/editor/RegistryBlockContext.tsx +107 -0
  263. package/src/editor/RegistryBlockNode.spec.ts +279 -0
  264. package/src/editor/RegistryBlockNode.tsx +652 -0
  265. package/src/editor/RichMarkdownEditor.collab.spec.ts +79 -0
  266. package/src/editor/RichMarkdownEditor.roundtrip.spec.ts +226 -0
  267. package/src/editor/RichMarkdownEditor.tsx +81 -0
  268. package/src/editor/RunId.ts +61 -0
  269. package/src/editor/SharedRichEditor.tsx +321 -0
  270. package/src/editor/SlashCommandMenu.tsx +310 -0
  271. package/src/editor/disableHistory.spec.ts +85 -0
  272. package/src/editor/extensions.ts +402 -0
  273. package/src/editor/gfmDoc.spec.ts +230 -0
  274. package/src/editor/gfmDoc.ts +92 -0
  275. package/src/editor/index.ts +93 -0
  276. package/src/editor/registrySlashCommands.ts +131 -0
  277. package/src/editor/structuralReorderUndo.spec.ts +144 -0
  278. package/src/editor/surgical-apply.spec.ts +279 -0
  279. package/src/editor/surgical-apply.ts +192 -0
  280. package/src/editor/useCollabReconcile.concurrent.spec.ts +422 -0
  281. package/src/editor/useCollabReconcile.idempotent.spec.ts +324 -0
  282. package/src/editor/useCollabReconcile.ts +589 -0
  283. package/src/editor.css +510 -0
  284. package/src/hooks/index.ts +2 -0
  285. package/src/hooks/use-mobile.tsx +21 -0
  286. package/src/hooks/use-toast.ts +188 -0
  287. package/src/index.ts +8 -0
  288. package/src/onboarding/index.ts +1 -0
  289. package/src/provider.tsx +47 -0
  290. package/src/sharing/VisibilityBadge.spec.ts +96 -0
  291. package/src/sharing/VisibilityBadge.tsx +37 -0
  292. package/src/sharing/index.ts +4 -0
  293. package/src/styles.css +21 -0
  294. package/src/ui/accordion.tsx +56 -0
  295. package/src/ui/alert-dialog.tsx +141 -0
  296. package/src/ui/alert.tsx +59 -0
  297. package/src/ui/aspect-ratio.tsx +5 -0
  298. package/src/ui/avatar.tsx +48 -0
  299. package/src/ui/badge.tsx +37 -0
  300. package/src/ui/breadcrumb.tsx +115 -0
  301. package/src/ui/button.tsx +78 -0
  302. package/src/ui/calendar.tsx +214 -0
  303. package/src/ui/card.tsx +86 -0
  304. package/src/ui/carousel.tsx +260 -0
  305. package/src/ui/chart.tsx +375 -0
  306. package/src/ui/checkbox.tsx +28 -0
  307. package/src/ui/collapsible.tsx +9 -0
  308. package/src/ui/command.spec.tsx +51 -0
  309. package/src/ui/command.tsx +174 -0
  310. package/src/ui/context-menu.tsx +202 -0
  311. package/src/ui/date-picker.tsx +64 -0
  312. package/src/ui/dialog.spec.tsx +43 -0
  313. package/src/ui/dialog.tsx +146 -0
  314. package/src/ui/drawer.tsx +126 -0
  315. package/src/ui/dropdown-menu.tsx +218 -0
  316. package/src/ui/form.tsx +177 -0
  317. package/src/ui/hover-card.tsx +27 -0
  318. package/src/ui/index.ts +52 -0
  319. package/src/ui/input-otp.tsx +69 -0
  320. package/src/ui/input.tsx +22 -0
  321. package/src/ui/label.tsx +24 -0
  322. package/src/ui/logo.tsx +36 -0
  323. package/src/ui/menubar.tsx +235 -0
  324. package/src/ui/navigation-menu.tsx +128 -0
  325. package/src/ui/pagination.tsx +121 -0
  326. package/src/ui/popover.tsx +44 -0
  327. package/src/ui/progress.tsx +26 -0
  328. package/src/ui/radio-group.tsx +42 -0
  329. package/src/ui/resizable.tsx +43 -0
  330. package/src/ui/scroll-area.tsx +46 -0
  331. package/src/ui/select.tsx +158 -0
  332. package/src/ui/separator.tsx +29 -0
  333. package/src/ui/sheet.tsx +160 -0
  334. package/src/ui/sidebar.tsx +777 -0
  335. package/src/ui/skeleton.tsx +18 -0
  336. package/src/ui/slider.tsx +26 -0
  337. package/src/ui/sonner.tsx +58 -0
  338. package/src/ui/spinner.tsx +17 -0
  339. package/src/ui/switch.tsx +27 -0
  340. package/src/ui/table.tsx +117 -0
  341. package/src/ui/tabs.tsx +53 -0
  342. package/src/ui/textarea.tsx +23 -0
  343. package/src/ui/toast.tsx +127 -0
  344. package/src/ui/toaster.tsx +33 -0
  345. package/src/ui/toggle-group.tsx +59 -0
  346. package/src/ui/toggle.tsx +43 -0
  347. package/src/ui/tooltip.tsx +53 -0
  348. package/src/ui/use-toast.ts +3 -0
  349. package/src/utils.ts +8 -0
@@ -0,0 +1,227 @@
1
+ /**
2
+ * RemoteSelectionRings — renders colored outline rings + name tags over
3
+ * elements selected by remote participants.
4
+ *
5
+ * Each participant's presence payload may contain a `selection` key with an
6
+ * opaque descriptor (e.g. a CSS selector). The `resolveRect` callback maps
7
+ * a descriptor to a DOMRect (or null when the element isn't found). Rings
8
+ * are rendered as absolutely-positioned outlines anchored to the container.
9
+ *
10
+ * Usage:
11
+ * <div style={{ position: "relative" }}>
12
+ * {content}
13
+ * <RemoteSelectionRings
14
+ * others={others}
15
+ * resolveRect={(selector) => document.querySelector(selector)?.getBoundingClientRect() ?? null}
16
+ * containerRef={containerRef}
17
+ * />
18
+ * </div>
19
+ */
20
+
21
+ import {
22
+ useState,
23
+ useEffect,
24
+ useRef,
25
+ useLayoutEffect,
26
+ memo,
27
+ type RefObject,
28
+ } from "react";
29
+
30
+ import type { OtherPresence } from "./types.js";
31
+
32
+ /**
33
+ * Selection descriptors may be a plain string (treated as the resolver
34
+ * input) or an object carrying a resolver input plus a human label
35
+ * ("Editing hero section").
36
+ */
37
+ export type SelectionDescriptor = string | { selector: string; label?: string };
38
+
39
+ export interface RemoteSelectionRingsProps {
40
+ /** Remote participants. */
41
+ others: OtherPresence[];
42
+ /**
43
+ * Key inside presence payload that carries the selection descriptor.
44
+ * Default: "selection"
45
+ */
46
+ selectionKey?: string;
47
+ /**
48
+ * Resolver: maps a selection descriptor to a DOMRect relative to the
49
+ * viewport. Return null when the element is not found.
50
+ */
51
+ resolveRect: (descriptor: string) => DOMRect | null;
52
+ /**
53
+ * Container element ref. Rings are positioned relative to this element's
54
+ * bounding box.
55
+ */
56
+ containerRef: RefObject<HTMLElement | null>;
57
+ /** Additional CSS class for the overlay div. */
58
+ className?: string;
59
+ }
60
+
61
+ interface Ring {
62
+ clientId: number;
63
+ color: string;
64
+ label: string;
65
+ avatarUrl?: string;
66
+ isAgent: boolean;
67
+ rect: { top: number; left: number; width: number; height: number };
68
+ }
69
+
70
+ const RingItem = memo(function RingItem({ ring }: { ring: Ring }) {
71
+ return (
72
+ <div
73
+ aria-label={`${ring.label} selection`}
74
+ style={{
75
+ position: "absolute",
76
+ top: ring.rect.top,
77
+ left: ring.rect.left,
78
+ width: ring.rect.width,
79
+ height: ring.rect.height,
80
+ outline: `2px solid ${ring.color}`,
81
+ outlineOffset: 2,
82
+ borderRadius: 3,
83
+ pointerEvents: "none",
84
+ boxShadow: `0 0 0 1px ${ring.color}40`,
85
+ zIndex: 9998,
86
+ }}
87
+ >
88
+ {/* Name tag in top-left corner of the ring */}
89
+ <div
90
+ style={{
91
+ position: "absolute",
92
+ top: -20,
93
+ left: 0,
94
+ display: "flex",
95
+ alignItems: "center",
96
+ gap: 4,
97
+ backgroundColor: ring.color,
98
+ color: "#fff",
99
+ fontSize: 10,
100
+ fontWeight: 600,
101
+ padding: "1px 5px",
102
+ borderRadius: 3,
103
+ whiteSpace: "nowrap",
104
+ maxWidth: 160,
105
+ overflow: "hidden",
106
+ textOverflow: "ellipsis",
107
+ }}
108
+ >
109
+ {ring.avatarUrl ? (
110
+ <img
111
+ src={ring.avatarUrl}
112
+ alt=""
113
+ style={{
114
+ width: 12,
115
+ height: 12,
116
+ borderRadius: "50%",
117
+ flexShrink: 0,
118
+ }}
119
+ />
120
+ ) : null}
121
+ {ring.label}
122
+ </div>
123
+ </div>
124
+ );
125
+ });
126
+
127
+ export function RemoteSelectionRings({
128
+ others,
129
+ selectionKey = "selection",
130
+ resolveRect,
131
+ containerRef,
132
+ className,
133
+ }: RemoteSelectionRingsProps) {
134
+ const overlayRef = useRef<HTMLDivElement>(null);
135
+ const [rings, setRings] = useState<Ring[]>([]);
136
+
137
+ // Recompute rings whenever others change or on animation frame.
138
+ const recompute = () => {
139
+ const container = containerRef.current;
140
+ if (!container) {
141
+ setRings([]);
142
+ return;
143
+ }
144
+ const containerRect = container.getBoundingClientRect();
145
+ const next: Ring[] = [];
146
+
147
+ for (const other of others) {
148
+ const raw = other.presence[selectionKey] as
149
+ | SelectionDescriptor
150
+ | null
151
+ | undefined;
152
+ const selector =
153
+ typeof raw === "string" ? raw : raw ? raw.selector : undefined;
154
+ if (!selector || typeof selector !== "string") continue;
155
+ const selectionLabel =
156
+ raw && typeof raw === "object" ? raw.label : undefined;
157
+
158
+ const domRect = resolveRect(selector);
159
+ if (!domRect) continue;
160
+
161
+ // Convert viewport-relative rect to container-relative.
162
+ const top = domRect.top - containerRect.top;
163
+ const left = domRect.left - containerRect.left;
164
+ if (
165
+ left + domRect.width < 0 ||
166
+ top + domRect.height < 0 ||
167
+ left > containerRect.width ||
168
+ top > containerRect.height
169
+ ) {
170
+ continue; // Out of container bounds — skip.
171
+ }
172
+
173
+ const baseName = other.isAgent
174
+ ? "AI"
175
+ : other.user.name || other.user.email;
176
+ next.push({
177
+ clientId: other.clientId,
178
+ color: other.user.color || "#94a3b8",
179
+ label: selectionLabel ? `${baseName} — ${selectionLabel}` : baseName,
180
+ avatarUrl: (other.user as { avatarUrl?: string }).avatarUrl,
181
+ isAgent: other.isAgent,
182
+ rect: { top, left, width: domRect.width, height: domRect.height },
183
+ });
184
+ }
185
+
186
+ setRings(next);
187
+ };
188
+
189
+ // Recompute on scroll/resize of the container to keep rings in sync.
190
+ useLayoutEffect(() => {
191
+ recompute();
192
+ // eslint-disable-next-line react-hooks/exhaustive-deps
193
+ }, [others, selectionKey]);
194
+
195
+ useEffect(() => {
196
+ const container = containerRef.current;
197
+ if (!container) return;
198
+ const observer = new ResizeObserver(() => recompute());
199
+ observer.observe(container);
200
+ container.addEventListener("scroll", recompute, { passive: true });
201
+ window.addEventListener("scroll", recompute, { passive: true });
202
+ return () => {
203
+ observer.disconnect();
204
+ container.removeEventListener("scroll", recompute);
205
+ window.removeEventListener("scroll", recompute);
206
+ };
207
+ // eslint-disable-next-line react-hooks/exhaustive-deps
208
+ }, [containerRef]);
209
+
210
+ return (
211
+ <div
212
+ ref={overlayRef}
213
+ aria-hidden
214
+ style={{
215
+ position: "absolute",
216
+ inset: 0,
217
+ pointerEvents: "none",
218
+ overflow: "hidden",
219
+ }}
220
+ className={className}
221
+ >
222
+ {rings.map((ring) => (
223
+ <RingItem key={ring.clientId} ring={ring} />
224
+ ))}
225
+ </div>
226
+ );
227
+ }
@@ -0,0 +1,16 @@
1
+ // Reserve the maximum signed 32-bit id so the agent never wins client leader election.
2
+ export const AGENT_CLIENT_ID = 2147483647;
3
+
4
+ export interface AgentIdentity {
5
+ clientId: number;
6
+ name: string;
7
+ email: string;
8
+ color: string;
9
+ }
10
+
11
+ export const DEFAULT_AGENT_IDENTITY: AgentIdentity = {
12
+ clientId: AGENT_CLIENT_ID,
13
+ name: "AI Assistant",
14
+ email: "agent@system",
15
+ color: "#00B5FF",
16
+ };
@@ -0,0 +1,39 @@
1
+ export {
2
+ AgentPresenceChip,
3
+ type AgentPresenceChipProps,
4
+ } from "./AgentPresenceChip.js";
5
+ export {
6
+ AGENT_CLIENT_ID,
7
+ DEFAULT_AGENT_IDENTITY,
8
+ type AgentIdentity,
9
+ } from "./agent-identity.js";
10
+ export {
11
+ LiveCursorOverlay,
12
+ type CursorMapFn,
13
+ type LiveCursorOverlayProps,
14
+ } from "./LiveCursorOverlay.js";
15
+ export { PresenceBar, type PresenceBarProps } from "./PresenceBar.js";
16
+ export { isReconcileLeadClient } from "./lead-client.js";
17
+ export {
18
+ RecentEditHighlights,
19
+ type RecentEditHighlightsProps,
20
+ } from "./RecentEditHighlights.js";
21
+ export {
22
+ RemoteSelectionRings,
23
+ type RemoteSelectionRingsProps,
24
+ type SelectionDescriptor,
25
+ } from "./RemoteSelectionRings.js";
26
+ export {
27
+ RECENT_EDITS_MAX,
28
+ RECENT_EDIT_TTL_MS,
29
+ dedupeCollabUsersByEmail,
30
+ emailToColor,
31
+ emailToName,
32
+ type AttributedRecentEdit,
33
+ type CollabUser,
34
+ type NormalizedPoint,
35
+ type OtherPresence,
36
+ type PresencePayload,
37
+ type RecentEdit,
38
+ type RecentEditDescriptor,
39
+ } from "./types.js";
@@ -0,0 +1,32 @@
1
+ import type { Awareness } from "y-protocols/awareness";
2
+
3
+ import { AGENT_CLIENT_ID } from "./agent-identity.js";
4
+
5
+ // Exactly one visible non-agent client may apply an external snapshot; allowing
6
+ // every client to reconcile it duplicates concurrent CRDT inserts.
7
+ export function isReconcileLeadClient(
8
+ awareness: Awareness | null | undefined,
9
+ localClientId: number | null | undefined,
10
+ ): boolean {
11
+ if (localClientId == null) return false;
12
+ if (!awareness) return true;
13
+
14
+ let hasPeer = false;
15
+ let minVisible = localClientId;
16
+ awareness.getStates().forEach((state, clientId) => {
17
+ if (clientId === AGENT_CLIENT_ID || clientId === localClientId) return;
18
+ const candidate = state as { user?: unknown; visible?: boolean };
19
+ if (!candidate?.user) return;
20
+ hasPeer = true;
21
+ if (candidate.visible !== false && clientId < minVisible) {
22
+ minVisible = clientId;
23
+ }
24
+ });
25
+
26
+ if (!hasPeer) return true;
27
+
28
+ const localHidden =
29
+ typeof document !== "undefined" && document.visibilityState === "hidden";
30
+ if (localHidden) return false;
31
+ return localClientId <= minVisible;
32
+ }
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { dedupeCollabUsersByEmail } from "./types.js";
4
+
5
+ describe("dedupeCollabUsersByEmail", () => {
6
+ it("ignores malformed awareness user payloads", () => {
7
+ expect(
8
+ dedupeCollabUsersByEmail([
9
+ {
10
+ name: "Broken",
11
+ email: undefined as unknown as string,
12
+ color: "#fff",
13
+ },
14
+ { name: "Real", email: "real@example.com", color: "#000" },
15
+ ]),
16
+ ).toEqual([{ name: "Real", email: "real@example.com", color: "#000" }]);
17
+ });
18
+ });
@@ -0,0 +1,96 @@
1
+ export interface CollabUser {
2
+ name: string;
3
+ email: string;
4
+ color: string;
5
+ avatarUrl?: string;
6
+ }
7
+
8
+ export type PresencePayload = Record<string, unknown>;
9
+
10
+ export interface OtherPresence {
11
+ clientId: number;
12
+ user: CollabUser;
13
+ presence: PresencePayload;
14
+ isAgent: boolean;
15
+ }
16
+
17
+ export interface NormalizedPoint {
18
+ x: number;
19
+ y: number;
20
+ }
21
+
22
+ export type RecentEditDescriptor =
23
+ | { kind: "text"; quote: string }
24
+ | { kind: "selector"; selector: string }
25
+ | { kind: "paths"; paths: string[] }
26
+ | { kind: "doc" }
27
+ | { kind: string; [key: string]: unknown };
28
+
29
+ export interface RecentEdit {
30
+ descriptor: RecentEditDescriptor;
31
+ label?: string;
32
+ at: number;
33
+ }
34
+
35
+ export interface AttributedRecentEdit extends RecentEdit {
36
+ clientId: number;
37
+ user: OtherPresence["user"];
38
+ isAgent: boolean;
39
+ }
40
+
41
+ export const RECENT_EDITS_MAX = 5;
42
+ export const RECENT_EDIT_TTL_MS = 10_000;
43
+
44
+ const CURSOR_COLORS = [
45
+ "#f87171",
46
+ "#fb923c",
47
+ "#fbbf24",
48
+ "#a3e635",
49
+ "#34d399",
50
+ "#22d3ee",
51
+ "#60a5fa",
52
+ "#14b8a6",
53
+ "#f472b6",
54
+ "#e879f9",
55
+ ];
56
+
57
+ export function emailToColor(email: string): string {
58
+ let hash = 0;
59
+ for (let i = 0; i < email.length; i++) {
60
+ hash = ((hash << 5) - hash + email.charCodeAt(i)) | 0;
61
+ }
62
+ return CURSOR_COLORS[Math.abs(hash) % CURSOR_COLORS.length];
63
+ }
64
+
65
+ export function emailToName(email: string): string {
66
+ const local = email.split("@")[0] || email;
67
+ return local.charAt(0).toUpperCase() + local.slice(1);
68
+ }
69
+
70
+ function normalizeCollabEmail(email: string): string {
71
+ return email.trim().toLowerCase();
72
+ }
73
+
74
+ export function dedupeCollabUsersByEmail(users: CollabUser[]): CollabUser[] {
75
+ const byEmail = new Map<string, CollabUser>();
76
+ for (const user of users) {
77
+ // Awareness is a network boundary. Older clients or partially-written
78
+ // states can contain a malformed user payload; ignore it instead of
79
+ // letting a missing email reach normalizeCollabEmail().
80
+ if (!user || typeof user.email !== "string") continue;
81
+ const email = normalizeCollabEmail(user.email);
82
+ if (!email || byEmail.has(email)) continue;
83
+ byEmail.set(email, {
84
+ name:
85
+ typeof user.name === "string" && user.name.trim()
86
+ ? user.name
87
+ : emailToName(email),
88
+ email,
89
+ color:
90
+ typeof user.color === "string" && user.color.trim()
91
+ ? user.color
92
+ : emailToColor(email),
93
+ });
94
+ }
95
+ return Array.from(byEmail.values());
96
+ }
@@ -0,0 +1,59 @@
1
+ import { ComposerPrimitive } from "@assistant-ui/react";
2
+ import type React from "react";
3
+
4
+ import { cn } from "../utils.js";
5
+ import type { AgentComposerLayoutVariant } from "./types.js";
6
+
7
+ export interface AgentComposerFrameProps {
8
+ children: React.ReactNode;
9
+ className?: string;
10
+ rootClassName?: string;
11
+ style?: React.CSSProperties;
12
+ rootStyle?: React.CSSProperties;
13
+ layoutVariant?: AgentComposerLayoutVariant;
14
+ onClick?: React.MouseEventHandler<HTMLDivElement>;
15
+ }
16
+
17
+ /**
18
+ * The single visual shell for agent chat composition.
19
+ *
20
+ * AssistantChat, PromptComposer, and host surfaces such as Agent-Native Code
21
+ * all render this same frame so the composer does not drift across products.
22
+ */
23
+ export function AgentComposerFrame({
24
+ children,
25
+ className,
26
+ rootClassName,
27
+ style,
28
+ rootStyle,
29
+ layoutVariant = "default",
30
+ onClick,
31
+ }: AgentComposerFrameProps) {
32
+ return (
33
+ <div
34
+ data-agent-composer-variant={layoutVariant}
35
+ data-agent-composer-slot="area"
36
+ className={cn(
37
+ "agent-composer-area shrink-0 px-3 py-2",
38
+ layoutVariant !== "default" && `agent-composer-area--${layoutVariant}`,
39
+ className,
40
+ )}
41
+ style={style}
42
+ onClick={onClick}
43
+ >
44
+ <ComposerPrimitive.Root
45
+ data-agent-composer-variant={layoutVariant}
46
+ data-agent-composer-slot="root"
47
+ className={cn(
48
+ "agent-composer-root flex flex-col rounded-lg border border-input bg-muted/45 transition-colors focus-within:border-ring",
49
+ layoutVariant !== "default" &&
50
+ `agent-composer-root--${layoutVariant}`,
51
+ rootClassName,
52
+ )}
53
+ style={rootStyle}
54
+ >
55
+ {children}
56
+ </ComposerPrimitive.Root>
57
+ </div>
58
+ );
59
+ }