@agent-native/core 0.110.2 → 0.111.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 (380) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +17 -0
  3. package/corpus/core/docs/content/components.mdx +7 -0
  4. package/corpus/core/docs/content/locales/ar-SA/components.mdx +4 -2
  5. package/corpus/core/docs/content/locales/de-DE/components.mdx +4 -2
  6. package/corpus/core/docs/content/locales/es-ES/components.mdx +4 -2
  7. package/corpus/core/docs/content/locales/fr-FR/components.mdx +4 -2
  8. package/corpus/core/docs/content/locales/hi-IN/components.mdx +3 -2
  9. package/corpus/core/docs/content/locales/ja-JP/components.mdx +3 -2
  10. package/corpus/core/docs/content/locales/ko-KR/components.mdx +3 -2
  11. package/corpus/core/docs/content/locales/pt-BR/components.mdx +4 -2
  12. package/corpus/core/docs/content/locales/zh-CN/components.mdx +3 -2
  13. package/corpus/core/docs/content/locales/zh-TW/components.mdx +3 -2
  14. package/corpus/core/docs/content/toolkit-agent-ux.mdx +8 -3
  15. package/corpus/core/migration-manifest.json +133 -269
  16. package/corpus/core/package.json +32 -33
  17. package/corpus/core/scripts/check-dist-imports.mjs +25 -1
  18. package/corpus/core/scripts/finalize-build.mjs +59 -2
  19. package/corpus/core/src/client/AgentAskPopover.tsx +1 -1
  20. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  21. package/corpus/core/src/client/AssistantChat.tsx +12 -14
  22. package/corpus/core/src/client/NewWorkspaceAppFlow.tsx +1 -1
  23. package/corpus/core/src/client/agent-chat-adapter.ts +1 -1
  24. package/corpus/core/src/client/blocks/RegistryBlockDataProvider.tsx +121 -0
  25. package/corpus/core/src/client/blocks/index.ts +6 -0
  26. package/corpus/core/src/client/chat/attachment-adapters.ts +4 -5
  27. package/corpus/core/src/client/chat/index.ts +0 -1
  28. package/corpus/core/src/client/chat/message-components.tsx +2 -2
  29. package/corpus/core/src/client/code-agent-chat-adapter.ts +2 -2
  30. package/corpus/core/src/client/collab/index.ts +0 -22
  31. package/corpus/core/src/client/composer/index.ts +5 -66
  32. package/corpus/core/src/client/composer/runtime-adapters.tsx +123 -0
  33. package/corpus/core/src/client/composer/use-mention-search.ts +2 -79
  34. package/corpus/core/src/client/composer/wired-components.tsx +53 -0
  35. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +1 -1
  36. package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +1 -1
  37. package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +1 -1
  38. package/corpus/core/src/client/index.ts +0 -156
  39. package/corpus/core/src/client/resources/ResourceEditor.tsx +29 -681
  40. package/corpus/core/src/client/resources/ResourcesPanel.tsx +1 -1
  41. package/corpus/core/src/client/settings/AutomationsSection.tsx +1 -1
  42. package/corpus/core/src/client/sharing/index.ts +0 -4
  43. package/corpus/core/src/client/tombstone/agent-presence-chip.ts +17 -0
  44. package/corpus/core/src/client/tombstone/editor.ts +265 -0
  45. package/corpus/core/src/client/tombstone/live-cursor-overlay.ts +21 -0
  46. package/corpus/core/src/client/tombstone/presence-bar.ts +17 -0
  47. package/corpus/core/src/client/tombstone/recent-edit-highlights.ts +17 -0
  48. package/corpus/core/src/client/tombstone/remote-selection-rings.ts +21 -0
  49. package/corpus/core/src/client/tombstone/rich-markdown-editor.ts +265 -0
  50. package/corpus/core/src/client/tombstone/ui-dialog.ts +49 -0
  51. package/corpus/core/src/client/tombstone/ui-dropdown-menu.ts +69 -0
  52. package/corpus/core/src/client/tombstone/ui-hover-card.ts +21 -0
  53. package/corpus/core/src/client/tombstone/ui-popover.ts +25 -0
  54. package/corpus/core/src/client/tombstone/ui-sheet.ts +49 -0
  55. package/corpus/core/src/client/tombstone/ui-tooltip.ts +29 -0
  56. package/corpus/core/src/client/tombstone/visual-style-controls.ts +61 -0
  57. package/corpus/core/src/client/uploads/index.ts +5 -0
  58. package/corpus/core/src/client/{rich-markdown-editor/uploadEditorImage.ts → uploads/upload-editor-image.ts} +11 -4
  59. package/corpus/core/src/package-lifecycle/tombstone.ts +16 -2
  60. package/corpus/core/src/package-lifecycle/upgrade-error.ts +23 -5
  61. package/corpus/core/src/testing.ts +1 -4
  62. package/corpus/core/src/vite/client.ts +62 -22
  63. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +2 -4
  64. package/corpus/templates/content/app/components/editor/extensions/registryBlocks.ts +1 -1
  65. package/corpus/templates/content/app/components/editor/registrySlashItems.ts +53 -52
  66. package/corpus/templates/plan/app/components/editor/PlanBlockNode.tsx +2 -2
  67. package/corpus/templates/plan/app/components/editor/planSlashCommands.ts +1 -1
  68. package/corpus/templates/plan/app/components/plan/DocumentArea.tsx +1 -1
  69. package/corpus/templates/plan/app/components/plan/PlanImageNode.tsx +1 -1
  70. package/corpus/templates/plan/app/components/plan/PlanMarkdownEditor.tsx +1 -1
  71. package/dist/catalog.json +1 -0
  72. package/dist/client/AgentAskPopover.js +1 -1
  73. package/dist/client/AgentAskPopover.js.map +1 -1
  74. package/dist/client/AgentPanel.js +1 -1
  75. package/dist/client/AgentPanel.js.map +1 -1
  76. package/dist/client/AssistantChat.d.ts +2 -2
  77. package/dist/client/AssistantChat.d.ts.map +1 -1
  78. package/dist/client/AssistantChat.js +5 -6
  79. package/dist/client/AssistantChat.js.map +1 -1
  80. package/dist/client/NewWorkspaceAppFlow.js +1 -1
  81. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  82. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  83. package/dist/client/agent-chat-adapter.js +1 -1
  84. package/dist/client/agent-chat-adapter.js.map +1 -1
  85. package/dist/client/blocks/RegistryBlockDataProvider.d.ts +7 -0
  86. package/dist/client/blocks/RegistryBlockDataProvider.d.ts.map +1 -0
  87. package/dist/client/blocks/RegistryBlockDataProvider.js +54 -0
  88. package/dist/client/blocks/RegistryBlockDataProvider.js.map +1 -0
  89. package/dist/client/blocks/index.d.ts +2 -0
  90. package/dist/client/blocks/index.d.ts.map +1 -1
  91. package/dist/client/blocks/index.js +1 -0
  92. package/dist/client/blocks/index.js.map +1 -1
  93. package/dist/client/chat/attachment-adapters.d.ts.map +1 -1
  94. package/dist/client/chat/attachment-adapters.js +1 -1
  95. package/dist/client/chat/attachment-adapters.js.map +1 -1
  96. package/dist/client/chat/index.d.ts +0 -1
  97. package/dist/client/chat/index.d.ts.map +1 -1
  98. package/dist/client/chat/index.js +0 -1
  99. package/dist/client/chat/index.js.map +1 -1
  100. package/dist/client/chat/message-components.d.ts.map +1 -1
  101. package/dist/client/chat/message-components.js +2 -2
  102. package/dist/client/chat/message-components.js.map +1 -1
  103. package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
  104. package/dist/client/code-agent-chat-adapter.js +1 -1
  105. package/dist/client/code-agent-chat-adapter.js.map +1 -1
  106. package/dist/client/collab/index.d.ts +0 -5
  107. package/dist/client/collab/index.d.ts.map +1 -1
  108. package/dist/client/collab/index.js +0 -5
  109. package/dist/client/collab/index.js.map +1 -1
  110. package/dist/client/composer/index.d.ts +3 -11
  111. package/dist/client/composer/index.d.ts.map +1 -1
  112. package/dist/client/composer/index.js +3 -10
  113. package/dist/client/composer/index.js.map +1 -1
  114. package/dist/client/composer/runtime-adapters.d.ts +5 -0
  115. package/dist/client/composer/runtime-adapters.d.ts.map +1 -0
  116. package/dist/client/composer/runtime-adapters.js +85 -0
  117. package/dist/client/composer/runtime-adapters.js.map +1 -0
  118. package/dist/client/composer/use-mention-search.d.ts +1 -2
  119. package/dist/client/composer/use-mention-search.d.ts.map +1 -1
  120. package/dist/client/composer/use-mention-search.js +2 -69
  121. package/dist/client/composer/use-mention-search.js.map +1 -1
  122. package/dist/client/composer/wired-components.d.ts +10 -0
  123. package/dist/client/composer/wired-components.d.ts.map +1 -0
  124. package/dist/client/composer/wired-components.js +20 -0
  125. package/dist/client/composer/wired-components.js.map +1 -0
  126. package/dist/client/extensions/ExtensionViewer.js +1 -1
  127. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  128. package/dist/client/extensions/ExtensionsListPage.js +1 -1
  129. package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
  130. package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
  131. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  132. package/dist/client/index.d.ts +0 -10
  133. package/dist/client/index.d.ts.map +1 -1
  134. package/dist/client/index.js +0 -26
  135. package/dist/client/index.js.map +1 -1
  136. package/dist/client/resources/ResourceEditor.d.ts.map +1 -1
  137. package/dist/client/resources/ResourceEditor.js +14 -532
  138. package/dist/client/resources/ResourceEditor.js.map +1 -1
  139. package/dist/client/resources/ResourcesPanel.js +1 -1
  140. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  141. package/dist/client/settings/AutomationsSection.js +1 -1
  142. package/dist/client/settings/AutomationsSection.js.map +1 -1
  143. package/dist/client/sharing/index.d.ts +0 -1
  144. package/dist/client/sharing/index.d.ts.map +1 -1
  145. package/dist/client/sharing/index.js +0 -1
  146. package/dist/client/sharing/index.js.map +1 -1
  147. package/dist/client/tombstone/agent-presence-chip.d.ts +6 -0
  148. package/dist/client/tombstone/agent-presence-chip.d.ts.map +1 -0
  149. package/dist/client/tombstone/agent-presence-chip.js +5 -0
  150. package/dist/client/tombstone/agent-presence-chip.js.map +1 -0
  151. package/dist/client/tombstone/editor.d.ts +128 -0
  152. package/dist/client/tombstone/editor.d.ts.map +1 -0
  153. package/dist/client/tombstone/editor.js +76 -0
  154. package/dist/client/tombstone/editor.js.map +1 -0
  155. package/dist/client/tombstone/live-cursor-overlay.d.ts +8 -0
  156. package/dist/client/tombstone/live-cursor-overlay.d.ts.map +1 -0
  157. package/dist/client/tombstone/live-cursor-overlay.js +5 -0
  158. package/dist/client/tombstone/live-cursor-overlay.js.map +1 -0
  159. package/dist/client/tombstone/presence-bar.d.ts +6 -0
  160. package/dist/client/tombstone/presence-bar.d.ts.map +1 -0
  161. package/dist/client/tombstone/presence-bar.js +5 -0
  162. package/dist/client/tombstone/presence-bar.js.map +1 -0
  163. package/dist/client/tombstone/recent-edit-highlights.d.ts +6 -0
  164. package/dist/client/tombstone/recent-edit-highlights.d.ts.map +1 -0
  165. package/dist/client/tombstone/recent-edit-highlights.js +5 -0
  166. package/dist/client/tombstone/recent-edit-highlights.js.map +1 -0
  167. package/dist/client/tombstone/remote-selection-rings.d.ts +8 -0
  168. package/dist/client/tombstone/remote-selection-rings.d.ts.map +1 -0
  169. package/dist/client/tombstone/remote-selection-rings.js +5 -0
  170. package/dist/client/tombstone/remote-selection-rings.js.map +1 -0
  171. package/dist/client/tombstone/rich-markdown-editor.d.ts +128 -0
  172. package/dist/client/tombstone/rich-markdown-editor.d.ts.map +1 -0
  173. package/dist/client/tombstone/rich-markdown-editor.js +76 -0
  174. package/dist/client/tombstone/rich-markdown-editor.js.map +1 -0
  175. package/dist/client/tombstone/ui-dialog.d.ts +22 -0
  176. package/dist/client/tombstone/ui-dialog.d.ts.map +1 -0
  177. package/dist/client/tombstone/ui-dialog.js +23 -0
  178. package/dist/client/tombstone/ui-dialog.js.map +1 -0
  179. package/dist/client/tombstone/ui-dropdown-menu.d.ts +32 -0
  180. package/dist/client/tombstone/ui-dropdown-menu.d.ts.map +1 -0
  181. package/dist/client/tombstone/ui-dropdown-menu.js +33 -0
  182. package/dist/client/tombstone/ui-dropdown-menu.js.map +1 -0
  183. package/dist/client/tombstone/ui-hover-card.d.ts +8 -0
  184. package/dist/client/tombstone/ui-hover-card.d.ts.map +1 -0
  185. package/dist/client/tombstone/ui-hover-card.js +9 -0
  186. package/dist/client/tombstone/ui-hover-card.js.map +1 -0
  187. package/dist/client/tombstone/ui-popover.d.ts +10 -0
  188. package/dist/client/tombstone/ui-popover.d.ts.map +1 -0
  189. package/dist/client/tombstone/ui-popover.js +11 -0
  190. package/dist/client/tombstone/ui-popover.js.map +1 -0
  191. package/dist/client/tombstone/ui-sheet.d.ts +22 -0
  192. package/dist/client/tombstone/ui-sheet.d.ts.map +1 -0
  193. package/dist/client/tombstone/ui-sheet.js +23 -0
  194. package/dist/client/tombstone/ui-sheet.js.map +1 -0
  195. package/dist/client/tombstone/ui-tooltip.d.ts +12 -0
  196. package/dist/client/tombstone/ui-tooltip.d.ts.map +1 -0
  197. package/dist/client/tombstone/ui-tooltip.js +13 -0
  198. package/dist/client/tombstone/ui-tooltip.js.map +1 -0
  199. package/dist/client/tombstone/visual-style-controls.d.ts +28 -0
  200. package/dist/client/tombstone/visual-style-controls.d.ts.map +1 -0
  201. package/dist/client/tombstone/visual-style-controls.js +23 -0
  202. package/dist/client/tombstone/visual-style-controls.js.map +1 -0
  203. package/dist/client/uploads/index.d.ts +1 -0
  204. package/dist/client/uploads/index.d.ts.map +1 -1
  205. package/dist/client/uploads/index.js +1 -0
  206. package/dist/client/uploads/index.js.map +1 -1
  207. package/dist/client/{rich-markdown-editor/uploadEditorImage.d.ts → uploads/upload-editor-image.d.ts} +8 -4
  208. package/dist/client/uploads/upload-editor-image.d.ts.map +1 -0
  209. package/dist/client/{rich-markdown-editor/uploadEditorImage.js → uploads/upload-editor-image.js} +2 -2
  210. package/dist/client/uploads/upload-editor-image.js.map +1 -0
  211. package/dist/collab/struct-routes.d.ts +1 -1
  212. package/dist/notifications/routes.d.ts +3 -3
  213. package/dist/package-lifecycle/tombstone.d.ts.map +1 -1
  214. package/dist/package-lifecycle/tombstone.js +13 -3
  215. package/dist/package-lifecycle/tombstone.js.map +1 -1
  216. package/dist/package-lifecycle/upgrade-error.d.ts +2 -2
  217. package/dist/package-lifecycle/upgrade-error.d.ts.map +1 -1
  218. package/dist/package-lifecycle/upgrade-error.js +8 -5
  219. package/dist/package-lifecycle/upgrade-error.js.map +1 -1
  220. package/dist/secrets/routes.d.ts +9 -9
  221. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  222. package/dist/server/transcribe-voice.d.ts +1 -1
  223. package/dist/testing.d.ts +1 -3
  224. package/dist/testing.d.ts.map +1 -1
  225. package/dist/testing.js +1 -3
  226. package/dist/testing.js.map +1 -1
  227. package/dist/vite/client.d.ts.map +1 -1
  228. package/dist/vite/client.js +26 -22
  229. package/dist/vite/client.js.map +1 -1
  230. package/docs/content/components.mdx +7 -0
  231. package/docs/content/locales/ar-SA/components.mdx +4 -2
  232. package/docs/content/locales/de-DE/components.mdx +4 -2
  233. package/docs/content/locales/es-ES/components.mdx +4 -2
  234. package/docs/content/locales/fr-FR/components.mdx +4 -2
  235. package/docs/content/locales/hi-IN/components.mdx +3 -2
  236. package/docs/content/locales/ja-JP/components.mdx +3 -2
  237. package/docs/content/locales/ko-KR/components.mdx +3 -2
  238. package/docs/content/locales/pt-BR/components.mdx +4 -2
  239. package/docs/content/locales/zh-CN/components.mdx +3 -2
  240. package/docs/content/locales/zh-TW/components.mdx +3 -2
  241. package/docs/content/toolkit-agent-ux.mdx +8 -3
  242. package/migration-manifest.json +133 -269
  243. package/package.json +31 -32
  244. package/corpus/core/src/client/composer/AgentComposerFrame.tsx +0 -59
  245. package/corpus/core/src/client/composer/ComposerPlusMenu.tsx +0 -988
  246. package/corpus/core/src/client/composer/MentionPopover.tsx +0 -412
  247. package/corpus/core/src/client/composer/PastedTextChip.tsx +0 -150
  248. package/corpus/core/src/client/composer/PromptComposer.tsx +0 -714
  249. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +0 -860
  250. package/corpus/core/src/client/composer/TiptapComposer.tsx +0 -2928
  251. package/corpus/core/src/client/composer/VoiceButton.tsx +0 -262
  252. package/corpus/core/src/client/composer/asset-picker-url.ts +0 -80
  253. package/corpus/core/src/client/composer/attachment-accept.ts +0 -71
  254. package/corpus/core/src/client/composer/draft-key.ts +0 -7
  255. package/corpus/core/src/client/composer/extensions/FileReference.tsx +0 -55
  256. package/corpus/core/src/client/composer/extensions/MentionReference.tsx +0 -93
  257. package/corpus/core/src/client/composer/extensions/SkillReference.tsx +0 -56
  258. package/corpus/core/src/client/composer/pasted-text.ts +0 -160
  259. package/corpus/core/src/client/composer/prompt-attachments.ts +0 -70
  260. package/corpus/core/src/client/composer/realtime-voice-audio-level.ts +0 -64
  261. package/corpus/core/src/client/composer/realtime-voice-transcript.ts +0 -134
  262. package/corpus/core/src/client/composer/types.ts +0 -66
  263. package/corpus/core/src/client/composer/use-file-search.ts +0 -63
  264. package/corpus/core/src/client/composer/use-skills.ts +0 -44
  265. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +0 -2095
  266. package/corpus/core/src/client/composer/useVoiceDictation.ts +0 -1155
  267. package/corpus/core/src/client/editor/index.ts +0 -1
  268. package/corpus/core/src/client/rich-markdown-editor/RegistryBlockNode.tsx +0 -799
  269. package/corpus/core/src/client/rich-markdown-editor/index.ts +0 -70
  270. package/corpus/core/src/client/rich-markdown-editor/registrySlashCommands.ts +0 -112
  271. package/dist/client/composer/AgentComposerFrame.d.ts +0 -19
  272. package/dist/client/composer/AgentComposerFrame.d.ts.map +0 -1
  273. package/dist/client/composer/AgentComposerFrame.js +0 -14
  274. package/dist/client/composer/AgentComposerFrame.js.map +0 -1
  275. package/dist/client/composer/ComposerPlusMenu.d.ts +0 -17
  276. package/dist/client/composer/ComposerPlusMenu.d.ts.map +0 -1
  277. package/dist/client/composer/ComposerPlusMenu.js +0 -501
  278. package/dist/client/composer/ComposerPlusMenu.js.map +0 -1
  279. package/dist/client/composer/MentionPopover.d.ts +0 -29
  280. package/dist/client/composer/MentionPopover.d.ts.map +0 -1
  281. package/dist/client/composer/MentionPopover.js +0 -180
  282. package/dist/client/composer/MentionPopover.js.map +0 -1
  283. package/dist/client/composer/PastedTextChip.d.ts +0 -9
  284. package/dist/client/composer/PastedTextChip.d.ts.map +0 -1
  285. package/dist/client/composer/PastedTextChip.js +0 -47
  286. package/dist/client/composer/PastedTextChip.js.map +0 -1
  287. package/dist/client/composer/PromptComposer.d.ts +0 -115
  288. package/dist/client/composer/PromptComposer.d.ts.map +0 -1
  289. package/dist/client/composer/PromptComposer.js +0 -299
  290. package/dist/client/composer/PromptComposer.js.map +0 -1
  291. package/dist/client/composer/RealtimeVoiceMode.d.ts +0 -104
  292. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +0 -1
  293. package/dist/client/composer/RealtimeVoiceMode.js +0 -245
  294. package/dist/client/composer/RealtimeVoiceMode.js.map +0 -1
  295. package/dist/client/composer/TiptapComposer.d.ts +0 -222
  296. package/dist/client/composer/TiptapComposer.d.ts.map +0 -1
  297. package/dist/client/composer/TiptapComposer.js +0 -1834
  298. package/dist/client/composer/TiptapComposer.js.map +0 -1
  299. package/dist/client/composer/VoiceButton.d.ts +0 -23
  300. package/dist/client/composer/VoiceButton.d.ts.map +0 -1
  301. package/dist/client/composer/VoiceButton.js +0 -100
  302. package/dist/client/composer/VoiceButton.js.map +0 -1
  303. package/dist/client/composer/asset-picker-url.d.ts +0 -16
  304. package/dist/client/composer/asset-picker-url.d.ts.map +0 -1
  305. package/dist/client/composer/asset-picker-url.js +0 -64
  306. package/dist/client/composer/asset-picker-url.js.map +0 -1
  307. package/dist/client/composer/attachment-accept.d.ts +0 -9
  308. package/dist/client/composer/attachment-accept.d.ts.map +0 -1
  309. package/dist/client/composer/attachment-accept.js +0 -65
  310. package/dist/client/composer/attachment-accept.js.map +0 -1
  311. package/dist/client/composer/draft-key.d.ts +0 -2
  312. package/dist/client/composer/draft-key.d.ts.map +0 -1
  313. package/dist/client/composer/draft-key.js +0 -8
  314. package/dist/client/composer/draft-key.js.map +0 -1
  315. package/dist/client/composer/extensions/FileReference.d.ts +0 -3
  316. package/dist/client/composer/extensions/FileReference.d.ts.map +0 -1
  317. package/dist/client/composer/extensions/FileReference.js +0 -36
  318. package/dist/client/composer/extensions/FileReference.js.map +0 -1
  319. package/dist/client/composer/extensions/MentionReference.d.ts +0 -3
  320. package/dist/client/composer/extensions/MentionReference.d.ts.map +0 -1
  321. package/dist/client/composer/extensions/MentionReference.js +0 -66
  322. package/dist/client/composer/extensions/MentionReference.js.map +0 -1
  323. package/dist/client/composer/extensions/SkillReference.d.ts +0 -3
  324. package/dist/client/composer/extensions/SkillReference.d.ts.map +0 -1
  325. package/dist/client/composer/extensions/SkillReference.js +0 -40
  326. package/dist/client/composer/extensions/SkillReference.js.map +0 -1
  327. package/dist/client/composer/pasted-text.d.ts +0 -31
  328. package/dist/client/composer/pasted-text.d.ts.map +0 -1
  329. package/dist/client/composer/pasted-text.js +0 -130
  330. package/dist/client/composer/pasted-text.js.map +0 -1
  331. package/dist/client/composer/prompt-attachments.d.ts +0 -11
  332. package/dist/client/composer/prompt-attachments.d.ts.map +0 -1
  333. package/dist/client/composer/prompt-attachments.js +0 -45
  334. package/dist/client/composer/prompt-attachments.js.map +0 -1
  335. package/dist/client/composer/realtime-voice-audio-level.d.ts +0 -14
  336. package/dist/client/composer/realtime-voice-audio-level.d.ts.map +0 -1
  337. package/dist/client/composer/realtime-voice-audio-level.js +0 -47
  338. package/dist/client/composer/realtime-voice-audio-level.js.map +0 -1
  339. package/dist/client/composer/realtime-voice-transcript.d.ts +0 -26
  340. package/dist/client/composer/realtime-voice-transcript.d.ts.map +0 -1
  341. package/dist/client/composer/realtime-voice-transcript.js +0 -103
  342. package/dist/client/composer/realtime-voice-transcript.js.map +0 -1
  343. package/dist/client/composer/types.d.ts +0 -60
  344. package/dist/client/composer/types.d.ts.map +0 -1
  345. package/dist/client/composer/types.js +0 -2
  346. package/dist/client/composer/types.js.map +0 -1
  347. package/dist/client/composer/use-file-search.d.ts +0 -6
  348. package/dist/client/composer/use-file-search.d.ts.map +0 -1
  349. package/dist/client/composer/use-file-search.js +0 -52
  350. package/dist/client/composer/use-file-search.js.map +0 -1
  351. package/dist/client/composer/use-skills.d.ts +0 -7
  352. package/dist/client/composer/use-skills.d.ts.map +0 -1
  353. package/dist/client/composer/use-skills.js +0 -39
  354. package/dist/client/composer/use-skills.js.map +0 -1
  355. package/dist/client/composer/useRealtimeVoiceMode.d.ts +0 -172
  356. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +0 -1
  357. package/dist/client/composer/useRealtimeVoiceMode.js +0 -1648
  358. package/dist/client/composer/useRealtimeVoiceMode.js.map +0 -1
  359. package/dist/client/composer/useVoiceDictation.d.ts +0 -57
  360. package/dist/client/composer/useVoiceDictation.d.ts.map +0 -1
  361. package/dist/client/composer/useVoiceDictation.js +0 -975
  362. package/dist/client/composer/useVoiceDictation.js.map +0 -1
  363. package/dist/client/editor/index.d.ts +0 -2
  364. package/dist/client/editor/index.d.ts.map +0 -1
  365. package/dist/client/editor/index.js +0 -2
  366. package/dist/client/editor/index.js.map +0 -1
  367. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +0 -130
  368. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +0 -1
  369. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +0 -426
  370. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +0 -1
  371. package/dist/client/rich-markdown-editor/index.d.ts +0 -5
  372. package/dist/client/rich-markdown-editor/index.d.ts.map +0 -1
  373. package/dist/client/rich-markdown-editor/index.js +0 -5
  374. package/dist/client/rich-markdown-editor/index.js.map +0 -1
  375. package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts +0 -60
  376. package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts.map +0 -1
  377. package/dist/client/rich-markdown-editor/registrySlashCommands.js +0 -51
  378. package/dist/client/rich-markdown-editor/registrySlashCommands.js.map +0 -1
  379. package/dist/client/rich-markdown-editor/uploadEditorImage.d.ts.map +0 -1
  380. package/dist/client/rich-markdown-editor/uploadEditorImage.js.map +0 -1
@@ -1,799 +0,0 @@
1
- import { IconPencil } from "@tabler/icons-react";
2
- import {
3
- NodeSelection,
4
- Plugin,
5
- PluginKey,
6
- type EditorState,
7
- } from "@tiptap/pm/state";
8
- import {
9
- Node,
10
- NodeViewWrapper,
11
- ReactNodeViewRenderer,
12
- mergeAttributes,
13
- type NodeViewProps,
14
- } from "@tiptap/react";
15
- import {
16
- createContext,
17
- useEffect,
18
- useContext,
19
- useMemo,
20
- useState,
21
- type ReactNode,
22
- type MouseEvent as ReactMouseEvent,
23
- } from "react";
24
-
25
- import {
26
- blockEditSurface,
27
- useOptionalBlockRegistry,
28
- type BlockDataChangeMeta,
29
- type BlockRenderContext,
30
- } from "../blocks/index.js";
31
- import { SchemaBlockEditor } from "../blocks/SchemaBlockEditor.js";
32
-
33
- /* -------------------------------------------------------------------------- */
34
- /* The generic registry-block side-map + Tiptap NodeView, lifted into core. */
35
- /* */
36
- /* This is the app-agnostic NodeView that renders registered block specs */
37
- /* inside a `SharedRichEditor` document. Hosts mount the node produced by */
38
- /* {@link createRegistryBlockNode} as an extra extension and wrap the editor */
39
- /* in a {@link RegistryBlockDataProvider}, sourcing the typed block `data` */
40
- /* from their own authoritative store (for example, PlanContent.blocks). The */
41
- /* node itself carries only lightweight identity attrs (type/id/title/summary) */
42
- /* plus an optional `__raw` verbatim-MDX attr for byte-stable source */
43
- /* round-trips; the heavy typed `data` is threaded through the side-map */
44
- /* context, keeping the doc small and the block data the single source of */
45
- /* truth. */
46
- /* -------------------------------------------------------------------------- */
47
-
48
- /* -------------------------------------------------------------------------- */
49
- /* C. Block-data side-map context */
50
- /* -------------------------------------------------------------------------- */
51
-
52
- /** The minimal block shape the NodeView renders through `<BlockView>`. */
53
- export interface RegistryBlockSideMapBlock {
54
- id: string;
55
- title?: string;
56
- summary?: string;
57
- data: unknown;
58
- }
59
-
60
- /**
61
- * The side-map an editor host supplies so the registry NodeView can resolve a
62
- * block's full typed `data` (and commit edits) by its stable id, without ever
63
- * storing that data in the ProseMirror doc.
64
- */
65
- export interface RegistryBlockDataValue<
66
- TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock,
67
- > {
68
- /** Resolve a block's full record (incl. `data`) by its stable id. */
69
- getBlock: (blockId: string) => TBlock | undefined;
70
- /** Commit a new `data` value for a block (edit-mode only). */
71
- onBlockDataChange: (
72
- blockId: string,
73
- nextData: unknown,
74
- meta?: BlockDataChangeMeta,
75
- ) => void;
76
- /** Whether the document (and thus its blocks) is editable. */
77
- editable: boolean;
78
- /**
79
- * When true, blocks whose type has no Notion (NFM) analog are badged so the
80
- * author knows they won't sync. The host decides which types are incompatible
81
- * via {@link isNotionIncompatibleType}; this flag just toggles the badge on.
82
- */
83
- notionSync?: boolean;
84
- /**
85
- * Decide whether a block type is Notion-incompatible (no NFM analog). Only
86
- * consulted when {@link notionSync} is true. Injected by the host so the
87
- * single registry-level allowlist (plan's `isNotionCompatibleBlockType`, or
88
- * content's registry-derived gate) drives the badge — core stays policy-free.
89
- */
90
- isNotionIncompatibleType?: (blockType: string) => boolean;
91
- /**
92
- * Render a block whose type is NOT in the registry through the host's own
93
- * dispatcher (plan: `PlanBlockView` for decision / legacy visual-questions /
94
- * image; omitted in hosts with no legacy types), so every block type renders
95
- * in the document instead of a bare fallback.
96
- */
97
- renderLegacyBlock?: (
98
- block: TBlock,
99
- options: { editing: boolean },
100
- ) => ReactNode;
101
- /**
102
- * Optional: render a schema-driven (or otherwise custom) editor for a legacy
103
- * block in place of the raw-JSON fallback. Receives an `onChange` that commits
104
- * the block's new `data`. Return `null`/`undefined` to keep the JSON editor for
105
- * that block type. The returned editor is expected to autosave through
106
- * `onChange` (no Save button is shown), matching registered panel blocks.
107
- */
108
- renderLegacyBlockEditor?: (
109
- block: TBlock,
110
- args: { onChange: (nextData: unknown) => void },
111
- ) => ReactNode;
112
- /**
113
- * Return `true` for legacy block types that render their OWN edit affordance
114
- * inside `renderLegacyBlock` (e.g. an image block with its own hover toolbar).
115
- * For those types the node view renders the legacy block in edit mode and adds
116
- * NO separate corner edit surface (no pencil / JSON / form popover), so the
117
- * block owns a single, self-contained control overlay.
118
- */
119
- legacyBlockSelfEdits?: (blockType: string) => boolean;
120
- }
121
-
122
- const RegistryBlockDataContext =
123
- createContext<RegistryBlockDataValue<any> | null>(null);
124
-
125
- export function RegistryBlockDataProvider<
126
- TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock,
127
- >({
128
- value,
129
- children,
130
- }: {
131
- value: RegistryBlockDataValue<TBlock>;
132
- children: ReactNode;
133
- }) {
134
- return (
135
- <RegistryBlockDataContext.Provider value={value}>
136
- {children}
137
- </RegistryBlockDataContext.Provider>
138
- );
139
- }
140
-
141
- /** Read the registry block side-map. Returns `null` outside a provider. */
142
- export function useRegistryBlockData<
143
- TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock,
144
- >(): RegistryBlockDataValue<TBlock> | null {
145
- return useContext(
146
- RegistryBlockDataContext,
147
- ) as RegistryBlockDataValue<TBlock> | null;
148
- }
149
-
150
- function clickedInteractiveChild(target: HTMLElement) {
151
- if (target.closest("button,input,textarea,select,a,[role='textbox']")) {
152
- return true;
153
- }
154
-
155
- // The block drag-handle grip is a `role="button"` div that lives in the editor
156
- // wrapper, and nested container blocks (columns/tabs) render their own inner
157
- // editors — each its own grip. A container block's `onMouseDownCapture`
158
- // selection handler runs in a SEPARATE React root (Tiptap mounts every node
159
- // view as its own root), so its `stopPropagation()` halts the NATIVE mousedown
160
- // right there, before it can reach an inner grip or inner editor. Treat the
161
- // grip and anything inside a nested editor region as interactive so the outer
162
- // container never hijacks a mousedown meant for inner machinery — this is what
163
- // makes dragging a block OUT of / BETWEEN columns (a nested grip) work at all.
164
- if (
165
- target.closest(".drag-handle") ||
166
- target.closest(".plan-nested-document-editor-region")
167
- ) {
168
- return true;
169
- }
170
-
171
- const blockNode = target.closest(".plan-block-node");
172
- const editable = target.closest("[contenteditable='true']");
173
- return !!blockNode && !!editable && blockNode.contains(editable);
174
- }
175
-
176
- /* -------------------------------------------------------------------------- */
177
- /* B. RegistryBlockNodeView (React) */
178
- /* -------------------------------------------------------------------------- */
179
-
180
- /**
181
- * Renders one registry-block atom. The block is non-editable as far as
182
- * ProseMirror is concerned (`contentEditable={false}`); all interaction happens
183
- * inside the registry-driven `<BlockView>`. Read vs edit is toggled by
184
- * `props.selected` (the node is "selected" in the editor) AND the document being
185
- * editable. `data-plan-interactive` keeps existing host click-guards from
186
- * treating clicks inside the block as document clicks.
187
- */
188
- export function RegistryBlockNodeView(props: NodeViewProps) {
189
- const blockType = String(props.node.attrs.blockType ?? "");
190
- const blockId = String(props.node.attrs.blockId ?? "");
191
- const [panelOpen, setPanelOpen] = useState(false);
192
- const [shellHovered, setShellHovered] = useState(false);
193
-
194
- const registryValue = useOptionalBlockRegistry();
195
- const sideMap = useRegistryBlockData();
196
-
197
- // Optimistic edit override. `onBlockDataChange` commits into the host's own
198
- // store (a ref the side-map context can't observe), so an edit does NOT
199
- // re-render this node — the new data only reaches the view on the next full
200
- // document reconcile, which lands after the autosave round-trip (seconds
201
- // later) and is skipped entirely when the host treats the save as its own
202
- // echo. That left quick toggles like the callout tone buttons visually frozen
203
- // until reload. Holding the just-edited data locally re-renders this one node
204
- // immediately, then releases once the authoritative block catches up to (or
205
- // moves past) the value the edit was based on.
206
- const [pendingEdit, setPendingEdit] = useState<{
207
- data: unknown;
208
- base: unknown;
209
- } | null>(null);
210
- const liveBlock = sideMap?.getBlock(blockId);
211
- const liveData = liveBlock?.data;
212
- useEffect(() => {
213
- if (pendingEdit && !Object.is(liveData, pendingEdit.base)) {
214
- setPendingEdit(null);
215
- }
216
- }, [liveData, pendingEdit]);
217
- const block =
218
- liveBlock && pendingEdit && Object.is(liveData, pendingEdit.base)
219
- ? { ...liveBlock, data: pendingEdit.data }
220
- : liveBlock;
221
- const commitBlockData = (nextData: unknown, meta?: BlockDataChangeMeta) => {
222
- setPendingEdit({ data: nextData, base: liveData });
223
- sideMap?.onBlockDataChange(blockId, nextData, meta);
224
- };
225
- const editable = sideMap?.editable ?? false;
226
- // In Notion-sync mode, flag blocks that have no Notion (NFM) analog so the
227
- // author sees what won't push. Prose blocks aren't registry-block nodes, so
228
- // this only ever covers structured blocks.
229
- const incompatibleWithNotion =
230
- (sideMap?.notionSync ?? false) &&
231
- (sideMap?.isNotionIncompatibleType?.(blockType) ?? false);
232
-
233
- // The block data isn't in the side-map yet (e.g. a freshly inserted node whose
234
- // store entry hasn't been seeded). Render a graceful placeholder.
235
- if (!block) {
236
- return (
237
- <NodeViewWrapper className="plan-block-node" data-block-id={blockId}>
238
- <div
239
- contentEditable={false}
240
- data-plan-interactive
241
- className="plan-block-node__placeholder rounded-md border border-dashed border-border px-3 py-2 text-sm text-muted-foreground"
242
- >
243
- {blockType ? `Loading ${blockType} block…` : "Loading block…"}
244
- </div>
245
- </NodeViewWrapper>
246
- );
247
- }
248
-
249
- const spec = registryValue?.registry.get(blockType);
250
- const selectNode = (event: ReactMouseEvent<HTMLElement>) => {
251
- if (!editable) return;
252
- const target = event.target;
253
- if (target instanceof HTMLElement && clickedInteractiveChild(target))
254
- return;
255
- const pos = typeof props.getPos === "function" ? props.getPos() : null;
256
- if (typeof pos !== "number") return;
257
- try {
258
- event.preventDefault();
259
- event.stopPropagation();
260
- const { view } = props.editor;
261
- view.dispatch(
262
- view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos)),
263
- );
264
- view.focus();
265
- } catch {
266
- // Ignore stale positions during React/ProseMirror reconciliation.
267
- }
268
- };
269
- const updateShellHover = (event: ReactMouseEvent<HTMLElement>) => {
270
- const target = event.target;
271
- setShellHovered(
272
- target instanceof HTMLElement &&
273
- target.closest(".plan-block-node__shell") === event.currentTarget,
274
- );
275
- };
276
-
277
- // Choose how to render the block body:
278
- // 1. Registered spec → read view by default; direct-manipulation specs
279
- // (`editSurface: "inline" | "container"`) render their editor in place,
280
- // read-only-in-edit-mode specs (`"none"`) keep their read view, while
281
- // artifact/config specs (`"panel"`) keep the read view plus a corner edit
282
- // button.
283
- // 2. No spec, but the side-map provides `renderLegacyBlock` → delegate to the
284
- // host's dispatcher (decision, legacy visual-questions, image, and any
285
- // other type rendered by a bespoke component rather than the registry), so
286
- // EVERY block type renders in the document exactly as it does in the
287
- // per-block reader — never a bare title fallback.
288
- // 3. Neither → a small non-crashing fallback.
289
- let body: ReactNode;
290
- let editSurface: ReactNode = null;
291
- if (registryValue && spec) {
292
- const blockData = (block as { data: unknown }).data;
293
- const Read = spec.Read;
294
- const readNode = (
295
- <Read
296
- data={blockData}
297
- blockId={block.id}
298
- title={block.title}
299
- summary={block.summary}
300
- ctx={registryValue.ctx}
301
- />
302
- );
303
- body = readNode;
304
- const canEditBlock =
305
- editable &&
306
- spec.placement.includes("block") &&
307
- !!sideMap?.onBlockDataChange;
308
- if (canEditBlock) {
309
- const Edit = spec.Edit;
310
- const editorNode = Edit ? (
311
- <Edit
312
- data={blockData}
313
- onChange={commitBlockData}
314
- editable
315
- blockId={block.id}
316
- title={block.title}
317
- summary={block.summary}
318
- ctx={registryValue.ctx}
319
- />
320
- ) : (
321
- <SchemaBlockEditor
322
- data={blockData}
323
- onChange={(nextData) => commitBlockData(nextData)}
324
- schema={spec.schema}
325
- editable
326
- blockId={block.id}
327
- ctx={registryValue.ctx}
328
- />
329
- );
330
- const surface = blockEditSurface(spec);
331
- if (surface === "panel" && registryValue.ctx.renderEditSurface) {
332
- editSurface = registryValue.ctx.renderEditSurface({
333
- title: spec.label,
334
- open: panelOpen,
335
- onOpenChange: setPanelOpen,
336
- blockId: block.id,
337
- blockType,
338
- blockTitle: block.title,
339
- blockSummary: block.summary,
340
- blockData,
341
- trigger: (
342
- <button
343
- type="button"
344
- data-plan-interactive
345
- aria-label={`Edit ${spec.label}`}
346
- onClick={() => setPanelOpen(true)}
347
- className="an-block-edit-trigger flex size-7 items-center justify-center rounded-md border border-border bg-background text-muted-foreground opacity-0 shadow-sm transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 data-[visible=true]:opacity-100"
348
- data-visible={panelOpen || shellHovered}
349
- >
350
- <IconPencil className="size-4" />
351
- </button>
352
- ),
353
- children: editorNode,
354
- });
355
- } else if (surface === "panel") {
356
- editSurface = props.selected ? (
357
- <div className="mt-3">{editorNode}</div>
358
- ) : null;
359
- } else if (surface !== "none") {
360
- body = editorNode;
361
- }
362
- }
363
- } else if (sideMap?.renderLegacyBlock) {
364
- // Self-editing legacy blocks (e.g. image) render their own edit affordance
365
- // inside their overlay, so render them in edit mode and add NO separate
366
- // corner edit surface — the block owns a single, self-contained overlay.
367
- const selfEdits =
368
- editable && Boolean(sideMap.legacyBlockSelfEdits?.(blockType));
369
- body = sideMap.renderLegacyBlock(block, { editing: selfEdits });
370
- if (editable && !selfEdits) {
371
- // Prefer a host-provided schema/custom editor (a real form) over the raw
372
- // JSON fallback when the host knows how to edit this legacy block type.
373
- const customEditor = sideMap.renderLegacyBlockEditor?.(block, {
374
- onChange: (nextData) => commitBlockData(nextData),
375
- });
376
- editSurface = (
377
- <LegacyJsonEditSurface
378
- block={block}
379
- open={panelOpen}
380
- onOpenChange={setPanelOpen}
381
- renderEditSurface={registryValue?.ctx.renderEditSurface}
382
- onChange={(nextBlock) => commitBlockData(nextBlock)}
383
- selected={shellHovered}
384
- customEditor={customEditor}
385
- />
386
- );
387
- }
388
- } else {
389
- body = (
390
- <div className="plan-block-node__fallback rounded-md border border-border px-3 py-2 text-sm text-muted-foreground">
391
- {block.title || blockType || "Unsupported block"}
392
- </div>
393
- );
394
- }
395
-
396
- return (
397
- <NodeViewWrapper
398
- className="plan-block-node"
399
- data-block-id={blockId}
400
- // Mirror the block type onto the wrapper so the document flow can detect a
401
- // RUN of consecutive blocks of the same type (e.g. api-endpoint) and
402
- // collapse the divider + gap between them, matching the read-only path.
403
- data-block-type={blockType || undefined}
404
- data-plan-block-selected={props.selected ? "" : undefined}
405
- data-notion-incompatible={incompatibleWithNotion ? "" : undefined}
406
- onMouseDownCapture={selectNode}
407
- >
408
- <div
409
- contentEditable={false}
410
- data-plan-interactive
411
- className="plan-block-node__shell relative"
412
- onMouseEnter={updateShellHover}
413
- onMouseMove={updateShellHover}
414
- onMouseLeave={() => setShellHovered(false)}
415
- >
416
- {incompatibleWithNotion && (
417
- <span
418
- className="plan-block-notion-badge"
419
- title="This block type has no Notion equivalent and won't sync to Notion."
420
- >
421
- Won't sync to Notion
422
- </span>
423
- )}
424
- {body}
425
- {editSurface && (
426
- <div className="plan-block-node__edit absolute right-2 top-2 z-20">
427
- {editSurface}
428
- </div>
429
- )}
430
- </div>
431
- </NodeViewWrapper>
432
- );
433
- }
434
-
435
- export function LegacyJsonEditSurface({
436
- block,
437
- open,
438
- onOpenChange,
439
- renderEditSurface,
440
- onChange,
441
- selected,
442
- customEditor,
443
- }: {
444
- block: RegistryBlockSideMapBlock;
445
- open: boolean;
446
- onOpenChange: (open: boolean) => void;
447
- renderEditSurface?: BlockRenderContext["renderEditSurface"];
448
- onChange: (nextData: unknown) => void;
449
- selected: boolean;
450
- /**
451
- * A host-provided form editor (e.g. {@link SchemaBlockEditor}). When present it
452
- * replaces the raw-JSON textarea + Save button; the form autosaves through its
453
- * own `onChange`.
454
- */
455
- customEditor?: ReactNode;
456
- }) {
457
- const serializedBlockData = useMemo(
458
- () => JSON.stringify(block.data, null, 2),
459
- [block.data],
460
- );
461
- const [draft, setDraft] = useState(serializedBlockData);
462
- const [parseError, setParseError] = useState<string | null>(null);
463
-
464
- useEffect(() => {
465
- setDraft(serializedBlockData);
466
- setParseError(null);
467
- }, [block.id, serializedBlockData]);
468
-
469
- const saveDraft = () => {
470
- try {
471
- const nextData = JSON.parse(draft) as unknown;
472
- setParseError(null);
473
- onChange(nextData);
474
- onOpenChange(false);
475
- } catch (error) {
476
- setParseError(
477
- error instanceof Error ? error.message : "Invalid JSON data.",
478
- );
479
- }
480
- };
481
-
482
- const trigger = (
483
- <button
484
- type="button"
485
- data-plan-interactive
486
- aria-label={`Edit ${block.title ?? "block"}`}
487
- onClick={() => onOpenChange(true)}
488
- className="an-block-edit-trigger flex size-7 items-center justify-center rounded-md border border-border bg-background text-muted-foreground opacity-0 shadow-sm transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 data-[visible=true]:opacity-100"
489
- data-visible={selected || open}
490
- >
491
- <IconPencil className="size-4" />
492
- </button>
493
- );
494
- const jsonEditor = (
495
- <div className="grid gap-3">
496
- <textarea
497
- data-plan-interactive
498
- className="min-h-64 w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs leading-5 text-foreground shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
499
- value={draft}
500
- aria-invalid={parseError ? true : undefined}
501
- onChange={(event) => {
502
- setDraft(event.target.value);
503
- if (parseError) setParseError(null);
504
- }}
505
- />
506
- {parseError ? (
507
- <p className="text-xs text-destructive" role="alert">
508
- Invalid JSON: {parseError}
509
- </p>
510
- ) : null}
511
- <div className="flex justify-end">
512
- <button
513
- type="button"
514
- data-plan-interactive
515
- className="inline-flex h-8 items-center justify-center rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground"
516
- onClick={saveDraft}
517
- >
518
- Save
519
- </button>
520
- </div>
521
- </div>
522
- );
523
- const editor = customEditor ?? jsonEditor;
524
- if (!renderEditSurface) return open ? editor : trigger;
525
- return renderEditSurface({
526
- title: block.title ?? "Block",
527
- open,
528
- onOpenChange,
529
- blockId: block.id,
530
- blockType:
531
- typeof (block as { type?: unknown }).type === "string"
532
- ? ((block as unknown as { type: string }).type ?? "")
533
- : "legacy",
534
- blockTitle: block.title,
535
- blockSummary: block.summary,
536
- blockData: block.data,
537
- trigger,
538
- children: editor,
539
- });
540
- }
541
-
542
- /* -------------------------------------------------------------------------- */
543
- /* A. registry-block Tiptap node factory */
544
- /* -------------------------------------------------------------------------- */
545
-
546
- /** Options for {@link createRegistryBlockNode}. */
547
- export interface CreateRegistryBlockNodeOptions {
548
- /**
549
- * The Tiptap node name (e.g. `"planBlock"`). Hosts that serialize the doc by
550
- * node name (plan's `plan-doc.ts` keys off `"planBlock"`) must pass the exact
551
- * name their serializer expects.
552
- */
553
- nodeName: string;
554
- /**
555
- * The HTML data-attribute that marks a serialized registry block on copy/paste
556
- * round-trip (e.g. `"data-plan-block"`).
557
- */
558
- dataTag: string;
559
- /**
560
- * Mint a fresh, unique block id for a given block type. Used by the dedupe
561
- * plugin to re-mint duplicate / missing ids (paste/duplicate). Plan passes
562
- * `createPlanBlockId`.
563
- */
564
- mintId: (blockType: string) => string;
565
- /** Node group (default `"block"`). */
566
- group?: string;
567
- }
568
-
569
- /**
570
- * Build the generic registry-block Tiptap atom node. Returns a Tiptap `Node`
571
- * that:
572
- * - carries identity attrs `blockType` / `blockId` / `title` / `summary`, a
573
- * `sourceBlockId` (set when a duplicate is re-minted, so the host can copy the
574
- * original block's data), and an optional `__raw` verbatim-MDX attr for
575
- * byte-stable source round-trips;
576
- * - is an atom + isolating + draggable block that renders through
577
- * {@link RegistryBlockNodeView} (via `ReactNodeViewRenderer`);
578
- * - installs a dedupe `appendTransaction` plugin that re-mints any duplicate or
579
- * empty `blockId` (the classic paste/duplicate case), preserving the original
580
- * block's id + side-map data and tagging its own transaction so it never
581
- * loops.
582
- */
583
- export function createRegistryBlockNode(
584
- options: CreateRegistryBlockNodeOptions,
585
- ) {
586
- const { nodeName, dataTag, mintId, group = "block" } = options;
587
- const dedupeKey = new PluginKey(`${nodeName}DedupeIds`);
588
- const keyboardGuardKey = new PluginKey(`${nodeName}KeyboardGuard`);
589
-
590
- /**
591
- * Collect every `blockId` currently present on this node type in a doc, with
592
- * the position of each node, so duplicate ids (from paste/duplicate) can be
593
- * detected and re-minted.
594
- */
595
- function collectEntries(state: EditorState): Array<{
596
- pos: number;
597
- blockType: string;
598
- blockId: string;
599
- sourceBlockId?: string;
600
- }> {
601
- const found: Array<{
602
- pos: number;
603
- blockType: string;
604
- blockId: string;
605
- sourceBlockId?: string;
606
- }> = [];
607
- state.doc.descendants((node, pos) => {
608
- if (node.type.name === nodeName) {
609
- found.push({
610
- pos,
611
- blockType: String(node.attrs.blockType ?? ""),
612
- blockId: String(node.attrs.blockId ?? ""),
613
- sourceBlockId:
614
- typeof node.attrs.sourceBlockId === "string"
615
- ? node.attrs.sourceBlockId
616
- : undefined,
617
- });
618
- }
619
- return true;
620
- });
621
- return found;
622
- }
623
-
624
- /**
625
- * Build a transaction that re-mints any duplicate / missing ids in `state`, or
626
- * `null` when nothing needs changing. Only the *later* duplicate (and any node
627
- * with an empty id) is re-minted, so the original keeps its id and side-map
628
- * data.
629
- */
630
- function buildDedupeTransaction(state: EditorState) {
631
- const entries = collectEntries(state);
632
- if (entries.length === 0) return null;
633
-
634
- const seen = new Set<string>();
635
- let tr = state.tr;
636
- let changed = false;
637
-
638
- for (const entry of entries) {
639
- const needsNewId = !entry.blockId || seen.has(entry.blockId);
640
- if (needsNewId) {
641
- const freshId = mintId(entry.blockType || "block");
642
- const node = state.doc.nodeAt(entry.pos);
643
- if (node) {
644
- tr = tr.setNodeMarkup(entry.pos, undefined, {
645
- ...node.attrs,
646
- blockId: freshId,
647
- sourceBlockId: entry.sourceBlockId || entry.blockId || null,
648
- });
649
- changed = true;
650
- }
651
- seen.add(freshId);
652
- } else {
653
- seen.add(entry.blockId);
654
- }
655
- }
656
-
657
- return changed ? tr.setMeta(dedupeKey, true) : null;
658
- }
659
-
660
- const selectedRegistryBlock = (state: EditorState) =>
661
- state.selection instanceof NodeSelection &&
662
- state.selection.node.type.name === nodeName;
663
-
664
- const isMutatingKey = (event: KeyboardEvent) => {
665
- if (event.altKey || event.ctrlKey || event.metaKey) return false;
666
- if (event.key === "Enter") return true;
667
- return event.key.length === 1;
668
- };
669
-
670
- return Node.create({
671
- name: nodeName,
672
- group,
673
- atom: true,
674
- draggable: true,
675
- selectable: true,
676
- isolating: true,
677
-
678
- addAttributes() {
679
- return {
680
- blockType: { default: "" },
681
- blockId: { default: "" },
682
- title: { default: null },
683
- summary: { default: null },
684
- sourceBlockId: { default: null },
685
- // Optional verbatim source for hosts that need byte-identical
686
- // source-format round-trips without React (server pull, hashing). Plan
687
- // never sets this.
688
- __raw: { default: null, rendered: false },
689
- };
690
- },
691
-
692
- parseHTML() {
693
- return [
694
- {
695
- tag: `div[${dataTag}]`,
696
- getAttrs: (element) => {
697
- const node = element as HTMLElement;
698
- return {
699
- blockType: node.getAttribute("data-block-type") || "",
700
- blockId: node.getAttribute("data-block-id") || "",
701
- title: node.getAttribute("data-title") || null,
702
- summary: node.getAttribute("data-summary") || null,
703
- sourceBlockId: node.getAttribute("data-source-block-id") || null,
704
- };
705
- },
706
- },
707
- ];
708
- },
709
-
710
- renderHTML({ HTMLAttributes }) {
711
- return [
712
- "div",
713
- mergeAttributes(HTMLAttributes, {
714
- [dataTag]: "",
715
- "data-block-type": HTMLAttributes.blockType ?? "",
716
- "data-block-id": HTMLAttributes.blockId ?? "",
717
- "data-title": HTMLAttributes.title ?? undefined,
718
- "data-summary": HTMLAttributes.summary ?? undefined,
719
- "data-source-block-id": HTMLAttributes.sourceBlockId ?? undefined,
720
- }),
721
- ];
722
- },
723
-
724
- addNodeView() {
725
- return ReactNodeViewRenderer(RegistryBlockNodeView);
726
- },
727
-
728
- addProseMirrorPlugins() {
729
- return [
730
- new Plugin({
731
- key: dedupeKey,
732
- appendTransaction(transactions, _oldState, newState) {
733
- // Ignore our own re-mint, and skip when nothing changed the doc.
734
- if (
735
- transactions.some((transaction) =>
736
- transaction.getMeta(dedupeKey),
737
- ) ||
738
- !transactions.some((transaction) => transaction.docChanged)
739
- ) {
740
- return null;
741
- }
742
- return buildDedupeTransaction(newState);
743
- },
744
- }),
745
- new Plugin({
746
- key: keyboardGuardKey,
747
- props: {
748
- handleClickOn(view, _pos, node, nodePos, event, direct) {
749
- if (node.type.name !== nodeName || !direct) return false;
750
- if (
751
- event.target instanceof HTMLElement &&
752
- clickedInteractiveChild(event.target)
753
- ) {
754
- return false;
755
- }
756
- event.preventDefault();
757
- view.dispatch(
758
- view.state.tr.setSelection(
759
- NodeSelection.create(view.state.doc, nodePos),
760
- ),
761
- );
762
- view.focus();
763
- return true;
764
- },
765
- handleKeyDown(view, event) {
766
- if (!selectedRegistryBlock(view.state) || !isMutatingKey(event))
767
- return false;
768
- event.preventDefault();
769
- return true;
770
- },
771
- handleTextInput(view) {
772
- return selectedRegistryBlock(view.state);
773
- },
774
- handlePaste(view, event) {
775
- if (!selectedRegistryBlock(view.state)) return false;
776
- event.preventDefault();
777
- return true;
778
- },
779
- handleDOMEvents: {
780
- beforeinput(view, event) {
781
- if (!selectedRegistryBlock(view.state)) return false;
782
- const inputEvent = event as InputEvent;
783
- if (
784
- !inputEvent.inputType ||
785
- (!inputEvent.inputType.startsWith("insert") &&
786
- inputEvent.inputType !== "formatSetBlockTextDirection")
787
- ) {
788
- return false;
789
- }
790
- event.preventDefault();
791
- return true;
792
- },
793
- },
794
- },
795
- }),
796
- ];
797
- },
798
- });
799
- }