@agent-native/core 0.109.2 → 0.109.4

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 (334) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +16 -0
  3. package/corpus/core/docs/content/agent-native-toolkit.mdx +46 -13
  4. package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +79 -55
  5. package/corpus/core/docs/content/locales/ar-SA/toolkit-capability-packages.mdx +21 -0
  6. package/corpus/core/docs/content/locales/ar-SA/toolkit-context-knowledge.mdx +12 -0
  7. package/corpus/core/docs/content/locales/ar-SA/toolkit-editors-canvases.mdx +17 -0
  8. package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +82 -54
  9. package/corpus/core/docs/content/locales/de-DE/toolkit-capability-packages.mdx +21 -0
  10. package/corpus/core/docs/content/locales/de-DE/toolkit-context-knowledge.mdx +12 -0
  11. package/corpus/core/docs/content/locales/de-DE/toolkit-editors-canvases.mdx +17 -0
  12. package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +81 -52
  13. package/corpus/core/docs/content/locales/es-ES/toolkit-capability-packages.mdx +21 -0
  14. package/corpus/core/docs/content/locales/es-ES/toolkit-context-knowledge.mdx +12 -0
  15. package/corpus/core/docs/content/locales/es-ES/toolkit-editors-canvases.mdx +21 -0
  16. package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +84 -54
  17. package/corpus/core/docs/content/locales/fr-FR/toolkit-capability-packages.mdx +21 -0
  18. package/corpus/core/docs/content/locales/fr-FR/toolkit-context-knowledge.mdx +12 -0
  19. package/corpus/core/docs/content/locales/fr-FR/toolkit-editors-canvases.mdx +21 -0
  20. package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +69 -62
  21. package/corpus/core/docs/content/locales/hi-IN/toolkit-capability-packages.mdx +21 -0
  22. package/corpus/core/docs/content/locales/hi-IN/toolkit-context-knowledge.mdx +12 -0
  23. package/corpus/core/docs/content/locales/hi-IN/toolkit-editors-canvases.mdx +17 -0
  24. package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +69 -62
  25. package/corpus/core/docs/content/locales/ja-JP/toolkit-capability-packages.mdx +21 -0
  26. package/corpus/core/docs/content/locales/ja-JP/toolkit-context-knowledge.mdx +12 -0
  27. package/corpus/core/docs/content/locales/ja-JP/toolkit-editors-canvases.mdx +17 -0
  28. package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +69 -62
  29. package/corpus/core/docs/content/locales/ko-KR/toolkit-capability-packages.mdx +21 -0
  30. package/corpus/core/docs/content/locales/ko-KR/toolkit-context-knowledge.mdx +12 -0
  31. package/corpus/core/docs/content/locales/ko-KR/toolkit-editors-canvases.mdx +17 -0
  32. package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +82 -54
  33. package/corpus/core/docs/content/locales/pt-BR/toolkit-capability-packages.mdx +21 -0
  34. package/corpus/core/docs/content/locales/pt-BR/toolkit-context-knowledge.mdx +12 -0
  35. package/corpus/core/docs/content/locales/pt-BR/toolkit-editors-canvases.mdx +17 -0
  36. package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +69 -62
  37. package/corpus/core/docs/content/locales/zh-CN/toolkit-capability-packages.mdx +21 -0
  38. package/corpus/core/docs/content/locales/zh-CN/toolkit-context-knowledge.mdx +12 -0
  39. package/corpus/core/docs/content/locales/zh-CN/toolkit-editors-canvases.mdx +17 -0
  40. package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +69 -62
  41. package/corpus/core/docs/content/locales/zh-TW/toolkit-capability-packages.mdx +21 -0
  42. package/corpus/core/docs/content/locales/zh-TW/toolkit-context-knowledge.mdx +12 -0
  43. package/corpus/core/docs/content/locales/zh-TW/toolkit-editors-canvases.mdx +17 -0
  44. package/corpus/core/docs/content/toolkit-capability-packages.mdx +52 -0
  45. package/corpus/core/docs/content/toolkit-context-knowledge.mdx +46 -0
  46. package/corpus/core/docs/content/toolkit-editors-canvases.mdx +55 -0
  47. package/corpus/core/package.json +35 -34
  48. package/corpus/core/src/cli/create.ts +57 -15
  49. package/corpus/core/src/cli/package-lifecycle.ts +2 -0
  50. package/corpus/core/src/cli/skills-content/visual-plan-skill.ts +2 -0
  51. package/corpus/core/src/client/context-xray/ContextMeter.tsx +31 -111
  52. package/corpus/core/src/client/context-xray/ContextSegmentRow.tsx +8 -111
  53. package/corpus/core/src/client/context-xray/ContextTreemap.tsx +6 -111
  54. package/corpus/core/src/client/context-xray/ContextXRayPanel.tsx +31 -391
  55. package/corpus/core/src/client/context-xray/SegmentProvenancePopover.tsx +4 -52
  56. package/corpus/core/src/client/context-xray/format.ts +0 -33
  57. package/corpus/core/src/client/require-session.tsx +19 -3
  58. package/corpus/core/src/client/rich-markdown-editor/index.ts +42 -57
  59. package/corpus/core/src/client/rich-markdown-editor/uploadEditorImage.ts +2 -1
  60. package/corpus/core/src/client/use-action.ts +145 -2
  61. package/corpus/core/src/client/use-session.ts +79 -48
  62. package/corpus/core/src/client/visual-style-controls.tsx +1 -1088
  63. package/corpus/core/src/collab/agent-identity.ts +5 -22
  64. package/corpus/core/src/collab/client.ts +1 -53
  65. package/corpus/core/src/db/client.ts +13 -7
  66. package/corpus/core/src/db/create-get-db.ts +20 -13
  67. package/corpus/core/src/db/request-telemetry.ts +154 -0
  68. package/corpus/core/src/integrations/index.ts +1 -0
  69. package/corpus/core/src/integrations/plugin.ts +9 -3
  70. package/corpus/core/src/integrations/remote-push-delivery-job.ts +48 -0
  71. package/corpus/core/src/integrations/remote-push-delivery.ts +370 -0
  72. package/corpus/core/src/integrations/remote-push-store.ts +275 -26
  73. package/corpus/core/src/server/better-auth-instance.ts +11 -3
  74. package/corpus/core/src/server/core-routes-plugin.ts +36 -14
  75. package/corpus/core/src/server/framework-request-handler.ts +11 -1
  76. package/corpus/core/src/server/http-response-telemetry.ts +233 -31
  77. package/corpus/core/src/styles/rich-markdown-editor.css +1 -510
  78. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +19 -0
  79. package/corpus/core/src/testing.ts +1 -1
  80. package/corpus/core/src/vite/client.ts +3 -0
  81. package/corpus/templates/clips/actions/finalize-recording.ts +3 -1
  82. package/corpus/templates/clips/actions/set-mobile-capture-state.ts +4 -1
  83. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -6
  84. package/corpus/templates/clips/changelog/2026-07-18-new-recording-is-now-emphasized-as-the-primary-action-in-the.md +6 -0
  85. package/corpus/templates/clips/changelog/2026-07-18-recordings-now-remain-safely-recoverable-until-the-stored-me.md +6 -0
  86. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +5 -3
  87. package/corpus/templates/content/app/components/editor/extensions/DragHandle.tsx +4 -4
  88. package/corpus/templates/content/package.json +25 -25
  89. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +2 -1
  90. package/corpus/templates/design/package.json +1 -1
  91. package/corpus/templates/mail/package.json +9 -9
  92. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +2 -0
  93. package/corpus/templates/plan/app/components/editor/PlanDocumentEditor.tsx +8 -8
  94. package/corpus/templates/plan/app/components/editor/planSlashCommands.ts +6 -6
  95. package/corpus/templates/plan/app/components/plan/PlanImageNode.tsx +4 -7
  96. package/corpus/templates/plan/app/components/plan/PlanMarkdownEditor.tsx +6 -4
  97. package/corpus/templates/plan/app/components/plan/PlanVisualSurface.tsx +1 -1
  98. package/corpus/templates/plan/changelog/2026-07-18-visual-plans-now-open-on-wireframes-by-default-when-a-protot.md +6 -0
  99. package/corpus/templates/plan/package.json +3 -3
  100. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +2 -2
  101. package/corpus/templates/slides/app/components/editor/TweaksPanel.tsx +2 -2
  102. package/corpus/templates/slides/package.json +13 -13
  103. package/corpus/templates/tasks/AGENTS.md +3 -0
  104. package/corpus/templates/tasks/actions/render-task-list-inline.ts +202 -0
  105. package/corpus/templates/tasks/changelog/2026-07-18-task-lists-can-now-appear-as-interactive-widgets-inline-in-c.md +6 -0
  106. package/corpus/templates/tasks/docs/features/f8-chat.mdx +2 -0
  107. package/corpus/templates/tasks/server/plugins/agent-chat.ts +2 -0
  108. package/dist/catalog.json +39 -1
  109. package/dist/cli/create.d.ts +10 -1
  110. package/dist/cli/create.d.ts.map +1 -1
  111. package/dist/cli/create.js +49 -16
  112. package/dist/cli/create.js.map +1 -1
  113. package/dist/cli/package-lifecycle.d.ts.map +1 -1
  114. package/dist/cli/package-lifecycle.js +2 -0
  115. package/dist/cli/package-lifecycle.js.map +1 -1
  116. package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
  117. package/dist/cli/skills-content/visual-plan-skill.d.ts.map +1 -1
  118. package/dist/cli/skills-content/visual-plan-skill.js +2 -0
  119. package/dist/cli/skills-content/visual-plan-skill.js.map +1 -1
  120. package/dist/client/context-xray/ContextMeter.d.ts.map +1 -1
  121. package/dist/client/context-xray/ContextMeter.js +8 -23
  122. package/dist/client/context-xray/ContextMeter.js.map +1 -1
  123. package/dist/client/context-xray/ContextSegmentRow.d.ts.map +1 -1
  124. package/dist/client/context-xray/ContextSegmentRow.js +3 -19
  125. package/dist/client/context-xray/ContextSegmentRow.js.map +1 -1
  126. package/dist/client/context-xray/ContextTreemap.d.ts.map +1 -1
  127. package/dist/client/context-xray/ContextTreemap.js +3 -54
  128. package/dist/client/context-xray/ContextTreemap.js.map +1 -1
  129. package/dist/client/context-xray/ContextXRayPanel.d.ts.map +1 -1
  130. package/dist/client/context-xray/ContextXRayPanel.js +21 -163
  131. package/dist/client/context-xray/ContextXRayPanel.js.map +1 -1
  132. package/dist/client/context-xray/SegmentProvenancePopover.d.ts.map +1 -1
  133. package/dist/client/context-xray/SegmentProvenancePopover.js +3 -4
  134. package/dist/client/context-xray/SegmentProvenancePopover.js.map +1 -1
  135. package/dist/client/context-xray/format.d.ts +0 -4
  136. package/dist/client/context-xray/format.d.ts.map +1 -1
  137. package/dist/client/context-xray/format.js +0 -49
  138. package/dist/client/context-xray/format.js.map +1 -1
  139. package/dist/client/require-session.d.ts.map +1 -1
  140. package/dist/client/require-session.js +6 -3
  141. package/dist/client/require-session.js.map +1 -1
  142. package/dist/client/rich-markdown-editor/index.d.ts +1 -12
  143. package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
  144. package/dist/client/rich-markdown-editor/index.js +1 -12
  145. package/dist/client/rich-markdown-editor/index.js.map +1 -1
  146. package/dist/client/rich-markdown-editor/uploadEditorImage.d.ts +1 -1
  147. package/dist/client/rich-markdown-editor/uploadEditorImage.d.ts.map +1 -1
  148. package/dist/client/rich-markdown-editor/uploadEditorImage.js.map +1 -1
  149. package/dist/client/use-action.d.ts.map +1 -1
  150. package/dist/client/use-action.js +117 -1
  151. package/dist/client/use-action.js.map +1 -1
  152. package/dist/client/use-session.d.ts.map +1 -1
  153. package/dist/client/use-session.js +72 -50
  154. package/dist/client/use-session.js.map +1 -1
  155. package/dist/client/visual-style-controls.d.ts +1 -89
  156. package/dist/client/visual-style-controls.d.ts.map +1 -1
  157. package/dist/client/visual-style-controls.js +1 -498
  158. package/dist/client/visual-style-controls.js.map +1 -1
  159. package/dist/collab/agent-identity.d.ts +1 -14
  160. package/dist/collab/agent-identity.d.ts.map +1 -1
  161. package/dist/collab/agent-identity.js +1 -13
  162. package/dist/collab/agent-identity.js.map +1 -1
  163. package/dist/collab/awareness.d.ts +2 -2
  164. package/dist/collab/awareness.d.ts.map +1 -1
  165. package/dist/collab/client.d.ts +1 -19
  166. package/dist/collab/client.d.ts.map +1 -1
  167. package/dist/collab/client.js +1 -53
  168. package/dist/collab/client.js.map +1 -1
  169. package/dist/collab/struct-routes.d.ts +1 -1
  170. package/dist/db/client.d.ts.map +1 -1
  171. package/dist/db/client.js +7 -1
  172. package/dist/db/client.js.map +1 -1
  173. package/dist/db/create-get-db.d.ts +3 -6
  174. package/dist/db/create-get-db.d.ts.map +1 -1
  175. package/dist/db/create-get-db.js +9 -2
  176. package/dist/db/create-get-db.js.map +1 -1
  177. package/dist/db/request-telemetry.d.ts +23 -0
  178. package/dist/db/request-telemetry.d.ts.map +1 -0
  179. package/dist/db/request-telemetry.js +95 -0
  180. package/dist/db/request-telemetry.js.map +1 -0
  181. package/dist/integrations/index.d.ts +1 -0
  182. package/dist/integrations/index.d.ts.map +1 -1
  183. package/dist/integrations/index.js +1 -0
  184. package/dist/integrations/index.js.map +1 -1
  185. package/dist/integrations/plugin.d.ts.map +1 -1
  186. package/dist/integrations/plugin.js +8 -3
  187. package/dist/integrations/plugin.js.map +1 -1
  188. package/dist/integrations/remote-push-delivery-job.d.ts +4 -0
  189. package/dist/integrations/remote-push-delivery-job.d.ts.map +1 -0
  190. package/dist/integrations/remote-push-delivery-job.js +44 -0
  191. package/dist/integrations/remote-push-delivery-job.js.map +1 -0
  192. package/dist/integrations/remote-push-delivery.d.ts +11 -0
  193. package/dist/integrations/remote-push-delivery.d.ts.map +1 -0
  194. package/dist/integrations/remote-push-delivery.js +265 -0
  195. package/dist/integrations/remote-push-delivery.js.map +1 -0
  196. package/dist/integrations/remote-push-store.d.ts +33 -0
  197. package/dist/integrations/remote-push-store.d.ts.map +1 -1
  198. package/dist/integrations/remote-push-store.js +195 -26
  199. package/dist/integrations/remote-push-store.js.map +1 -1
  200. package/dist/notifications/routes.d.ts +3 -3
  201. package/dist/progress/routes.d.ts +1 -1
  202. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  203. package/dist/resources/handlers.d.ts +3 -3
  204. package/dist/secrets/routes.d.ts +9 -9
  205. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  206. package/dist/server/better-auth-instance.js +7 -3
  207. package/dist/server/better-auth-instance.js.map +1 -1
  208. package/dist/server/core-routes-plugin.d.ts +8 -0
  209. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  210. package/dist/server/core-routes-plugin.js +23 -8
  211. package/dist/server/core-routes-plugin.js.map +1 -1
  212. package/dist/server/framework-request-handler.d.ts.map +1 -1
  213. package/dist/server/framework-request-handler.js +9 -1
  214. package/dist/server/framework-request-handler.js.map +1 -1
  215. package/dist/server/http-response-telemetry.d.ts +3 -2
  216. package/dist/server/http-response-telemetry.d.ts.map +1 -1
  217. package/dist/server/http-response-telemetry.js +160 -27
  218. package/dist/server/http-response-telemetry.js.map +1 -1
  219. package/dist/server/transcribe-voice.d.ts +1 -1
  220. package/dist/styles/rich-markdown-editor.css +1 -510
  221. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +19 -0
  222. package/dist/testing.d.ts +1 -1
  223. package/dist/testing.d.ts.map +1 -1
  224. package/dist/testing.js +1 -1
  225. package/dist/testing.js.map +1 -1
  226. package/dist/vite/client.d.ts.map +1 -1
  227. package/dist/vite/client.js +3 -0
  228. package/dist/vite/client.js.map +1 -1
  229. package/docs/content/agent-native-toolkit.mdx +46 -13
  230. package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +79 -55
  231. package/docs/content/locales/ar-SA/toolkit-capability-packages.mdx +21 -0
  232. package/docs/content/locales/ar-SA/toolkit-context-knowledge.mdx +12 -0
  233. package/docs/content/locales/ar-SA/toolkit-editors-canvases.mdx +17 -0
  234. package/docs/content/locales/de-DE/agent-native-toolkit.mdx +82 -54
  235. package/docs/content/locales/de-DE/toolkit-capability-packages.mdx +21 -0
  236. package/docs/content/locales/de-DE/toolkit-context-knowledge.mdx +12 -0
  237. package/docs/content/locales/de-DE/toolkit-editors-canvases.mdx +17 -0
  238. package/docs/content/locales/es-ES/agent-native-toolkit.mdx +81 -52
  239. package/docs/content/locales/es-ES/toolkit-capability-packages.mdx +21 -0
  240. package/docs/content/locales/es-ES/toolkit-context-knowledge.mdx +12 -0
  241. package/docs/content/locales/es-ES/toolkit-editors-canvases.mdx +21 -0
  242. package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +84 -54
  243. package/docs/content/locales/fr-FR/toolkit-capability-packages.mdx +21 -0
  244. package/docs/content/locales/fr-FR/toolkit-context-knowledge.mdx +12 -0
  245. package/docs/content/locales/fr-FR/toolkit-editors-canvases.mdx +21 -0
  246. package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +69 -62
  247. package/docs/content/locales/hi-IN/toolkit-capability-packages.mdx +21 -0
  248. package/docs/content/locales/hi-IN/toolkit-context-knowledge.mdx +12 -0
  249. package/docs/content/locales/hi-IN/toolkit-editors-canvases.mdx +17 -0
  250. package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +69 -62
  251. package/docs/content/locales/ja-JP/toolkit-capability-packages.mdx +21 -0
  252. package/docs/content/locales/ja-JP/toolkit-context-knowledge.mdx +12 -0
  253. package/docs/content/locales/ja-JP/toolkit-editors-canvases.mdx +17 -0
  254. package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +69 -62
  255. package/docs/content/locales/ko-KR/toolkit-capability-packages.mdx +21 -0
  256. package/docs/content/locales/ko-KR/toolkit-context-knowledge.mdx +12 -0
  257. package/docs/content/locales/ko-KR/toolkit-editors-canvases.mdx +17 -0
  258. package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +82 -54
  259. package/docs/content/locales/pt-BR/toolkit-capability-packages.mdx +21 -0
  260. package/docs/content/locales/pt-BR/toolkit-context-knowledge.mdx +12 -0
  261. package/docs/content/locales/pt-BR/toolkit-editors-canvases.mdx +17 -0
  262. package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +69 -62
  263. package/docs/content/locales/zh-CN/toolkit-capability-packages.mdx +21 -0
  264. package/docs/content/locales/zh-CN/toolkit-context-knowledge.mdx +12 -0
  265. package/docs/content/locales/zh-CN/toolkit-editors-canvases.mdx +17 -0
  266. package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +69 -62
  267. package/docs/content/locales/zh-TW/toolkit-capability-packages.mdx +21 -0
  268. package/docs/content/locales/zh-TW/toolkit-context-knowledge.mdx +12 -0
  269. package/docs/content/locales/zh-TW/toolkit-editors-canvases.mdx +17 -0
  270. package/docs/content/toolkit-capability-packages.mdx +52 -0
  271. package/docs/content/toolkit-context-knowledge.mdx +46 -0
  272. package/docs/content/toolkit-editors-canvases.mdx +55 -0
  273. package/package.json +27 -26
  274. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +19 -0
  275. package/corpus/core/src/client/rich-markdown-editor/BubbleToolbar.tsx +0 -253
  276. package/corpus/core/src/client/rich-markdown-editor/CodeBlockNode.tsx +0 -258
  277. package/corpus/core/src/client/rich-markdown-editor/DragHandle.ts +0 -1437
  278. package/corpus/core/src/client/rich-markdown-editor/ImageExtension.ts +0 -306
  279. package/corpus/core/src/client/rich-markdown-editor/RichMarkdownEditor.tsx +0 -80
  280. package/corpus/core/src/client/rich-markdown-editor/RunId.ts +0 -61
  281. package/corpus/core/src/client/rich-markdown-editor/SharedRichEditor.tsx +0 -321
  282. package/corpus/core/src/client/rich-markdown-editor/SlashCommandMenu.tsx +0 -310
  283. package/corpus/core/src/client/rich-markdown-editor/extensions.ts +0 -402
  284. package/corpus/core/src/client/rich-markdown-editor/gfmDoc.ts +0 -92
  285. package/corpus/core/src/client/rich-markdown-editor/surgical-apply.ts +0 -192
  286. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +0 -590
  287. package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts +0 -37
  288. package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts.map +0 -1
  289. package/dist/client/rich-markdown-editor/BubbleToolbar.js +0 -171
  290. package/dist/client/rich-markdown-editor/BubbleToolbar.js.map +0 -1
  291. package/dist/client/rich-markdown-editor/CodeBlockNode.d.ts +0 -49
  292. package/dist/client/rich-markdown-editor/CodeBlockNode.d.ts.map +0 -1
  293. package/dist/client/rich-markdown-editor/CodeBlockNode.js +0 -127
  294. package/dist/client/rich-markdown-editor/CodeBlockNode.js.map +0 -1
  295. package/dist/client/rich-markdown-editor/DragHandle.d.ts +0 -97
  296. package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +0 -1
  297. package/dist/client/rich-markdown-editor/DragHandle.js +0 -1066
  298. package/dist/client/rich-markdown-editor/DragHandle.js.map +0 -1
  299. package/dist/client/rich-markdown-editor/ImageExtension.d.ts +0 -63
  300. package/dist/client/rich-markdown-editor/ImageExtension.d.ts.map +0 -1
  301. package/dist/client/rich-markdown-editor/ImageExtension.js +0 -243
  302. package/dist/client/rich-markdown-editor/ImageExtension.js.map +0 -1
  303. package/dist/client/rich-markdown-editor/RichMarkdownEditor.d.ts +0 -51
  304. package/dist/client/rich-markdown-editor/RichMarkdownEditor.d.ts.map +0 -1
  305. package/dist/client/rich-markdown-editor/RichMarkdownEditor.js +0 -37
  306. package/dist/client/rich-markdown-editor/RichMarkdownEditor.js.map +0 -1
  307. package/dist/client/rich-markdown-editor/RunId.d.ts +0 -28
  308. package/dist/client/rich-markdown-editor/RunId.d.ts.map +0 -1
  309. package/dist/client/rich-markdown-editor/RunId.js +0 -60
  310. package/dist/client/rich-markdown-editor/RunId.js.map +0 -1
  311. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +0 -110
  312. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +0 -1
  313. package/dist/client/rich-markdown-editor/SharedRichEditor.js +0 -144
  314. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +0 -1
  315. package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts +0 -41
  316. package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts.map +0 -1
  317. package/dist/client/rich-markdown-editor/SlashCommandMenu.js +0 -221
  318. package/dist/client/rich-markdown-editor/SlashCommandMenu.js.map +0 -1
  319. package/dist/client/rich-markdown-editor/extensions.d.ts +0 -178
  320. package/dist/client/rich-markdown-editor/extensions.d.ts.map +0 -1
  321. package/dist/client/rich-markdown-editor/extensions.js +0 -224
  322. package/dist/client/rich-markdown-editor/extensions.js.map +0 -1
  323. package/dist/client/rich-markdown-editor/gfmDoc.d.ts +0 -24
  324. package/dist/client/rich-markdown-editor/gfmDoc.d.ts.map +0 -1
  325. package/dist/client/rich-markdown-editor/gfmDoc.js +0 -83
  326. package/dist/client/rich-markdown-editor/gfmDoc.js.map +0 -1
  327. package/dist/client/rich-markdown-editor/surgical-apply.d.ts +0 -66
  328. package/dist/client/rich-markdown-editor/surgical-apply.d.ts.map +0 -1
  329. package/dist/client/rich-markdown-editor/surgical-apply.js +0 -147
  330. package/dist/client/rich-markdown-editor/surgical-apply.js.map +0 -1
  331. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +0 -110
  332. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +0 -1
  333. package/dist/client/rich-markdown-editor/useCollabReconcile.js +0 -441
  334. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"surgical-apply.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/surgical-apply.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Surgical reconcile — apply an authoritative external document into the live\n * editor by replacing only the changed top-level node run, instead of a\n * whole-document `setContent`.\n *\n * Why: under the Collaboration extension, `setContent` routes through\n * y-prosemirror and rewrites the ENTIRE `Y.XmlFragment`. Every block-level\n * NodeView is torn down and recreated (each `ReactRenderer` constructor calls\n * `flushSync`, firing inside a React lifecycle), remote carets jump, and the\n * CRDT sees a delete-all + insert-all instead of a small edit. Diffing the\n * top-level children and dispatching one `tr.replaceWith(from, to, changed)`\n * leaves unchanged NodeViews untouched and produces minimal Yjs ops.\n *\n * This is the core mechanism behind re-enabling single-doc collab in the plan\n * editor (see templates/plan/shared/plan-doc.collab-stability.spec.ts, \"Option\n * B\") and removing agent-edit NodeView churn in content.\n */\n\nimport { createNodeFromContent } from \"@tiptap/core\";\nimport type { Fragment, Node as ProseMirrorNode } from \"@tiptap/pm/model\";\nimport type { Editor } from \"@tiptap/react\";\n\n/**\n * Transaction meta marking programmatic (non-user) rich-markdown transactions.\n * Declared here (not in useCollabReconcile) so the module dependency stays\n * one-directional; useCollabReconcile re-exports it for consumers.\n */\nexport const RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION =\n \"an-rich-md-programmatic-transaction\";\n\nexport interface TopLevelDiff {\n /** Index of the first differing top-level child. */\n fromIndex: number;\n /** Exclusive end index of the differing run in the OLD doc. */\n oldToIndex: number;\n /** Exclusive end index of the differing run in the NEW doc. */\n newToIndex: number;\n /** Document position where the differing run starts. */\n fromPos: number;\n /** Document position where the differing run ends in the OLD doc. */\n toPos: number;\n}\n\n/**\n * Diff two documents at top-level-node granularity: trim the common prefix and\n * suffix (node equality via ProseMirror's `Node.eq`, which is deep) and return\n * the remaining changed run. Returns null when the documents are equal.\n */\nexport function diffTopLevel(\n oldDoc: ProseMirrorNode,\n newDoc: ProseMirrorNode,\n): TopLevelDiff | null {\n const oldCount = oldDoc.childCount;\n const newCount = newDoc.childCount;\n\n let prefix = 0;\n const maxPrefix = Math.min(oldCount, newCount);\n while (prefix < maxPrefix && oldDoc.child(prefix).eq(newDoc.child(prefix))) {\n prefix++;\n }\n\n if (prefix === oldCount && prefix === newCount) return null; // identical\n\n let suffix = 0;\n const maxSuffix = Math.min(oldCount, newCount) - prefix;\n while (\n suffix < maxSuffix &&\n oldDoc.child(oldCount - 1 - suffix).eq(newDoc.child(newCount - 1 - suffix))\n ) {\n suffix++;\n }\n\n const fromIndex = prefix;\n const oldToIndex = oldCount - suffix;\n const newToIndex = newCount - suffix;\n\n // Positions: sum nodeSize of the preserved prefix, then of the changed run.\n let fromPos = 0;\n for (let i = 0; i < fromIndex; i++) fromPos += oldDoc.child(i).nodeSize;\n let toPos = fromPos;\n for (let i = fromIndex; i < oldToIndex; i++) {\n toPos += oldDoc.child(i).nodeSize;\n }\n\n return { fromIndex, oldToIndex, newToIndex, fromPos, toPos };\n}\n\nfunction changedFragment(\n newDoc: ProseMirrorNode,\n diff: TopLevelDiff,\n): Fragment {\n return newDoc.content.cut(\n positionOfChild(newDoc, diff.fromIndex),\n positionOfChild(newDoc, diff.newToIndex),\n );\n}\n\nfunction positionOfChild(doc: ProseMirrorNode, index: number): number {\n let pos = 0;\n for (let i = 0; i < index; i++) pos += doc.child(i).nodeSize;\n return pos;\n}\n\nfunction isEmptyParagraph(node: ProseMirrorNode | null): boolean {\n return !!node && node.type.name === \"paragraph\" && node.content.size === 0;\n}\n\n/**\n * Markdown can't represent a trailing empty paragraph (the cursor line users\n * keep below a list/code block), so a parsed authoritative value never has\n * one — but the live doc often does, and the legacy `setContent` path\n * preserves it. Without this, every surgical reconcile whose old doc ends\n * with an empty paragraph would DELETE the user's trailing cursor line.\n */\nfunction withPreservedTrailingParagraph(\n oldDoc: ProseMirrorNode,\n newDoc: ProseMirrorNode,\n): ProseMirrorNode {\n const oldLast =\n oldDoc.childCount > 0 ? oldDoc.child(oldDoc.childCount - 1) : null;\n if (!isEmptyParagraph(oldLast)) return newDoc;\n const newLast =\n newDoc.childCount > 0 ? newDoc.child(newDoc.childCount - 1) : null;\n if (isEmptyParagraph(newLast)) return newDoc;\n return newDoc.copy(newDoc.content.addToEnd(oldLast!.type.create()));\n}\n\n/**\n * Replace only the changed top-level run of the live document with the\n * corresponding run from `newDoc`, in one programmatic, history-free\n * transaction. Returns:\n * - \"applied\" — a targeted replacement was dispatched\n * - \"noop\" — the documents were already equal (nothing dispatched)\n * - \"failed\" — the diff/transaction could not be applied (schema mismatch,\n * invalid content, …); caller should fall back to setContent.\n */\nexport function applyDocSurgically(\n editor: Editor,\n newDoc: ProseMirrorNode,\n): \"applied\" | \"noop\" | \"failed\" {\n try {\n const oldDoc = editor.state.doc;\n // The parsed doc MUST come from this editor's schema — NodeType identity\n // is per-Schema-instance, so a foreign-schema doc can never diff or apply.\n if (newDoc.type.schema !== editor.schema || newDoc.type !== oldDoc.type) {\n return \"failed\";\n }\n\n const target = withPreservedTrailingParagraph(oldDoc, newDoc);\n const diff = diffTopLevel(oldDoc, target);\n if (!diff) return \"noop\";\n\n const fragment = changedFragment(target, diff);\n const tr = editor.state.tr;\n tr.replaceWith(diff.fromPos, diff.toPos, fragment);\n tr.setMeta(\"addToHistory\", false);\n tr.setMeta(RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION, true);\n editor.view.dispatch(tr);\n return \"applied\";\n } catch {\n return \"failed\";\n }\n}\n\n/**\n * Best-effort default parser for the surgical path: use the tiptap-markdown\n * storage parser (present when the shared editor's `features.markdown` is on)\n * to turn the authoritative markdown into a ProseMirror document. Returns null\n * when unavailable or when parsing fails — the caller falls back to the full\n * `setContent` path.\n *\n * Apps with their own serializers (Content's NFM, Plan's blocks[] doc) should\n * pass an explicit `parseValue` producing the exact doc their `setContent`\n * would have written.\n */\nexport function defaultParseValue(\n editor: Editor,\n value: string,\n): ProseMirrorNode | null {\n try {\n const storage = (editor.storage as Record<string, any>).markdown;\n const parsed = storage?.parser?.parse?.(value, { inline: false });\n if (typeof parsed !== \"string\" || !parsed) return null;\n const node = createNodeFromContent(parsed, editor.schema, {\n slice: false,\n });\n // createNodeFromContent with slice:false returns a Node (the doc).\n return (node as ProseMirrorNode) ?? null;\n } catch {\n return null;\n }\n}\n"]}
@@ -1,110 +0,0 @@
1
- import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
2
- import type { Transaction } from "@tiptap/pm/state";
3
- import type { Editor } from "@tiptap/react";
4
- import { type MutableRefObject } from "react";
5
- import type { Awareness } from "y-protocols/awareness";
6
- import type { Doc as YDoc } from "yjs";
7
- import { RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION } from "./surgical-apply.js";
8
- export { RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION };
9
- /** Reads the current markdown out of the tiptap-markdown storage. */
10
- export declare function getEditorMarkdown(editor: Editor): string;
11
- export interface UseCollabReconcileOptions {
12
- /** The live editor, or null until it mounts. */
13
- editor: Editor | null;
14
- /** Shared Y.Doc when collaborating; null disables all collab paths. */
15
- ydoc?: YDoc | null;
16
- /** True after the collab provider has loaded the persisted initial Y.Doc state. */
17
- collabSynced?: boolean;
18
- /** Shared awareness; null keeps the sole-client lead path. */
19
- awareness?: Awareness | null;
20
- /** Authoritative markdown value (SQL source of truth). */
21
- value: string;
22
- /** Timestamp of the authoritative value; gates newer-than reconcile. */
23
- contentUpdatedAt?: string | null;
24
- /** Whether the editor accepts edits. Reconcile/seed only run for the live editor. */
25
- editable: boolean;
26
- /**
27
- * Reads the current markdown from the editor. Injected so a dialect could
28
- * swap serializers; defaults to the tiptap-markdown storage reader. For an app
29
- * with a custom serializer (e.g. Content's `docToNfm(editor.getJSON())`), pass
30
- * it here so the seed/reconcile equality checks compare like-for-like.
31
- */
32
- getMarkdown?: (editor: Editor) => string;
33
- /**
34
- * Applies the authoritative `value` into the editor. Defaults to passing the
35
- * raw markdown string to `editor.commands.setContent`. Apps whose serializer
36
- * is NOT tiptap-markdown (Content parses `nfmToDoc(value)` into a PM doc)
37
- * override this so seed + reconcile write the correct content shape. The
38
- * supplied `options` carry the history/whitespace flags the default path uses;
39
- * a custom implementation should forward them when relevant.
40
- */
41
- setContent?: (editor: Editor, value: string, options: {
42
- emitUpdate?: boolean;
43
- addToHistory?: boolean;
44
- }) => void;
45
- /**
46
- * Parses the authoritative `value` into a full ProseMirror document for the
47
- * SURGICAL reconcile path: instead of a whole-document `setContent` (which
48
- * under Collaboration rewrites the entire Y.XmlFragment and tears down every
49
- * block NodeView), the reconcile diffs the parsed doc against the live doc
50
- * and replaces only the changed top-level run. Return null to skip the
51
- * surgical path for a given value (falls back to `setContent`).
52
- *
53
- * Defaults to a best-effort tiptap-markdown parse; apps with their own
54
- * serializers (Content's NFM, Plan's blocks[] doc) should supply the exact
55
- * doc their `setContent` would write. Pass `false` to disable surgical
56
- * application entirely.
57
- */
58
- parseValue?: ((editor: Editor, value: string) => ProseMirrorNode | null) | false;
59
- /**
60
- * Normalizes the authoritative `value` to the canonical markdown the editor
61
- * would emit, so the "already in sync / our own echo" equality checks match a
62
- * serializer that re-canonicalizes (Content's `canonicalizeNfm`). Defaults to
63
- * identity (GFM already round-trips byte-stably).
64
- */
65
- normalizeValue?: (value: string) => string;
66
- /**
67
- * Decides whether the empty-doc seed should run for the current shared
68
- * fragment. Defaults to "fragment has no nodes, or the editor holds no
69
- * semantic markdown". Apps with sentinel-empty content (Content's
70
- * `<empty-block/>` filler) override this. Receives the live fragment length
71
- * and the editor's current markdown.
72
- */
73
- shouldSeed?: (info: {
74
- value: string;
75
- currentMarkdown: string;
76
- fragmentLength: number;
77
- }) => boolean;
78
- /**
79
- * The initial "applied" watermark. Default mirrors `contentUpdatedAt`, so a
80
- * fresh mount whose Y.Doc already matches SQL doesn't re-apply. Pass `null`
81
- * to force the first reconcile pass to adopt authoritative SQL even at the
82
- * same timestamp — Content does this so a stale persisted Y.Doc (an agent that
83
- * edited the CLOSED doc) is corrected on open. The editor is keyed per
84
- * document upstream, so this only affects the first mount of each doc.
85
- */
86
- initialAppliedUpdatedAt?: string | null;
87
- }
88
- export interface UseCollabReconcileResult {
89
- /** True when a Y.Doc is bound (collaborative editing active). */
90
- collab: boolean;
91
- /**
92
- * Set true around any programmatic `setContent` so the editor's `onUpdate`
93
- * can ignore the resulting transaction (it isn't a user edit).
94
- */
95
- isSettingContentRef: MutableRefObject<boolean>;
96
- /**
97
- * Call from `onUpdate` BEFORE serializing. Returns true when the update must
98
- * be ignored: editor not editable, mid-programmatic-setContent, or (in collab
99
- * mode) a remote-origin transaction. Also records the local typing time.
100
- */
101
- shouldIgnoreUpdate: (transaction: Transaction) => boolean;
102
- /**
103
- * Call from `onUpdate` AFTER computing the markdown to emit. Returns false
104
- * when the value must NOT be persisted yet (an empty collab doc before the
105
- * seed has run); records it as the last-emitted value otherwise.
106
- */
107
- registerEmitted: (markdown: string) => boolean;
108
- }
109
- export declare function useCollabReconcile({ editor, ydoc, collabSynced, awareness, value, contentUpdatedAt, editable, getMarkdown, setContent, parseValue, normalizeValue, shouldSeed, initialAppliedUpdatedAt, }: UseCollabReconcileOptions): UseCollabReconcileResult;
110
- //# sourceMappingURL=useCollabReconcile.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useCollabReconcile.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/useCollabReconcile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAA+B,KAAK,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAC;AAIvC,OAAO,EACL,sCAAsC,EAGvC,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,sCAAsC,EAAE,CAAC;AAElD,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKxD;AAqBD,MAAM,WAAW,yBAAyB;IACxC,gDAAgD;IAChD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uEAAuE;IACvE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,mFAAmF;IACnF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,qFAAqF;IACrF,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CACX,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,KACtD,IAAI,CAAC;IACV;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EACP,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,eAAe,GAAG,IAAI,CAAC,GAC3D,KAAK,CAAC;IACV;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;KACxB,KAAK,OAAO,CAAC;IACd;;;;;;;OAOG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,wBAAwB;IACvC,iEAAiE;IACjE,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,mBAAmB,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC/C;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC;IAC1D;;;;OAIG;IACH,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CAChD;AAoED,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,IAAW,EACX,YAAmB,EACnB,SAAgB,EAChB,KAAK,EACL,gBAAgB,EAChB,QAAQ,EACR,WAA+B,EAC/B,UAA8B,EAC9B,UAAU,EACV,cAAyB,EACzB,UAA8B,EAC9B,uBAAuB,GACxB,EAAE,yBAAyB,GAAG,wBAAwB,CAyWtD"}
@@ -1,441 +0,0 @@
1
- import { isChangeOrigin } from "@tiptap/extension-collaboration";
2
- import { useEffect, useRef, useState } from "react";
3
- import { AGENT_CLIENT_ID } from "../../collab/agent-identity.js";
4
- import { isReconcileLeadClient } from "../../collab/client.js";
5
- import { RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION, applyDocSurgically, defaultParseValue, } from "./surgical-apply.js";
6
- export { RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION };
7
- /** Reads the current markdown out of the tiptap-markdown storage. */
8
- export function getEditorMarkdown(editor) {
9
- const markdownStorage = editor.storage;
10
- return markdownStorage.markdown?.getMarkdown?.() ?? "";
11
- }
12
- /**
13
- * Push a value onto the bounded ring of recently-emitted markdown (most recent
14
- * last, deduped, capped). The reconcile uses this to recognize a stale-but-recent
15
- * echo of OUR OWN edits: a debounced autosave can persist a PARTIAL burst, and
16
- * the next poll re-supplies that partial value with a newer timestamp — applying
17
- * it would clobber the freshly-typed tail. An external change (agent/peer) never
18
- * byte-matches one of our own recent emissions, and if it somehow did the content
19
- * is identical, so skipping it is safe by construction.
20
- */
21
- const EMITTED_RING_MAX = 16;
22
- function pushEmittedRing(ring, value) {
23
- if (!value)
24
- return;
25
- if (ring[ring.length - 1] === value)
26
- return;
27
- const dupe = ring.indexOf(value);
28
- if (dupe !== -1)
29
- ring.splice(dupe, 1);
30
- ring.push(value);
31
- if (ring.length > EMITTED_RING_MAX)
32
- ring.shift();
33
- }
34
- /**
35
- * The subtle seed / reconcile / lead-client logic for the shared markdown
36
- * editor, extracted once so it can never be duplicated across embedders.
37
- *
38
- * Responsibilities (reproducing the Plan editor's behavior exactly):
39
- * - Track whether THIS client is the reconcile lead (sole client always leads;
40
- * otherwise elected via {@link isReconcileLeadClient}) and how many other
41
- * visible human peers are present.
42
- * - Seed an empty shared Y.Doc once from `value` — lead client only — so two
43
- * clients opening a brand-new block don't both insert the content.
44
- * - Reconcile authoritative external markdown (agent edit, source patch, peer
45
- * edit mirrored to SQL) into the editor: in collab mode only the lead client
46
- * applies it through `setContent` and Yjs propagates; in non-collab mode this
47
- * is the original controlled-value reconcile.
48
- * - Provide the `onUpdate` guards (`shouldIgnoreUpdate`, `registerEmitted`) so
49
- * the component never persists remote-origin or pre-seed empty content.
50
- */
51
- /** Default seed predicate: seed only when the shared doc is genuinely empty. */
52
- function defaultShouldSeed({ currentMarkdown, fragmentLength, }) {
53
- return fragmentLength === 0 || !currentMarkdown.trim();
54
- }
55
- /**
56
- * Default content writer: hand the raw markdown string to `setContent`, which
57
- * tiptap-markdown overrides to parse the markdown into a ProseMirror doc.
58
- *
59
- * IMPORTANT: do NOT pass `parseOptions: { preserveWhitespace: "full" }` here.
60
- * In tiptap v3 the core `setContent` command routes `preserveWhitespace: "full"`
61
- * through `insertContentAt`, which tiptap-markdown ALSO overrides to re-run its
62
- * markdown parser. That double-parse stringifies the already-parsed PM doc and
63
- * re-parses it as HTML, so a clean heading/list/code block comes back as the
64
- * escaped, non-idempotent `&lt;h1&gt;…` — which then escalates every reconcile
65
- * cycle (`<p>` → `&lt;p&gt;` → `&amp;lt;p&amp;gt;` …). Letting the markdown
66
- * override parse the string directly (no `parseOptions`) round-trips byte-stably
67
- * for the GFM corpus, including code-block and empty-line whitespace. Content's
68
- * NFM path supplies its own `setContent` (it passes a pre-parsed PM doc) and is
69
- * unaffected by this default.
70
- */
71
- function defaultSetContent(editor, value, options) {
72
- if (options.addToHistory === false) {
73
- editor
74
- .chain()
75
- .command(({ tr }) => {
76
- // addToHistory:false so cmd+z (or Yjs undo) doesn't erase
77
- // externally-loaded content.
78
- tr.setMeta("addToHistory", false);
79
- return true;
80
- })
81
- .setContent(value, { emitUpdate: options.emitUpdate })
82
- .run();
83
- return;
84
- }
85
- editor.commands.setContent(value);
86
- }
87
- export function useCollabReconcile({ editor, ydoc = null, collabSynced = true, awareness = null, value, contentUpdatedAt, editable, getMarkdown = getEditorMarkdown, setContent = defaultSetContent, parseValue, normalizeValue = (v) => v, shouldSeed = defaultShouldSeed, initialAppliedUpdatedAt, }) {
88
- const collab = !!ydoc;
89
- const isSettingContentRef = useRef(false);
90
- const lastEmittedRef = useRef("");
91
- // Ring of recent local emissions (see pushEmittedRing). Lets the reconcile
92
- // recognize a stale-but-recent echo of our OWN (possibly partial, debounced)
93
- // save so a lagging poll never clobbers freshly-typed text.
94
- const recentEmittedRef = useRef([]);
95
- const lastTypedAtRef = useRef(0);
96
- // The raw authoritative `value` string the reconcile last applied. When the
97
- // SAME raw string is re-fetched (a lagging poll, or a source-sync that keeps
98
- // re-supplying the same stored markdown), applying it again would only
99
- // reproduce the doc we already hold — and if `value` is NON-idempotent
100
- // (serialize(parse(value)) !== value) re-applying compounds the divergence
101
- // every cycle (`<p>` → `&lt;p&gt;` → `&amp;lt;p&amp;gt;` …). Tracked so the
102
- // identical re-fetch is recognized and skipped.
103
- const lastAppliedValueRef = useRef(null);
104
- // The editor's SERIALIZED output captured right AFTER the last reconcile/seed
105
- // apply (`getMarkdown(editor)` once the content settled). For non-idempotent
106
- // input this is what autosave actually persists, so the NEXT poll hands it
107
- // back as the new `value`. Comparing the incoming value against this lets the
108
- // reconcile recognize its own echo even when the raw string changed once, so
109
- // it never re-parses content the editor already represents. This is the
110
- // doc-equivalence guard that breaks the escalation loop.
111
- const lastAppliedSerializedRef = useRef(null);
112
- const lastAppliedUpdatedAtRef = useRef(initialAppliedUpdatedAt !== undefined
113
- ? initialAppliedUpdatedAt
114
- : (contentUpdatedAt ?? null));
115
- // Whether THIS client is the one that seeds the empty shared doc / applies an
116
- // authoritative external snapshot into it. Exactly one client does, so the
117
- // content isn't inserted once per open editor. A sole client always leads.
118
- const [isLeadClient, setIsLeadClient] = useState(true);
119
- // Count of OTHER visible human collaborators. When >0, a peer's edit also
120
- // arrives via Yjs, so external markdown reconcile must defer (avoid applying
121
- // the same change through both Yjs and setContent).
122
- const peerCountRef = useRef(0);
123
- useEffect(() => {
124
- if (!collab || !awareness || !ydoc) {
125
- setIsLeadClient(true);
126
- peerCountRef.current = 0;
127
- return;
128
- }
129
- const update = () => {
130
- setIsLeadClient(isReconcileLeadClient(awareness, ydoc.clientID));
131
- let peers = 0;
132
- awareness.getStates().forEach((state, clientId) => {
133
- if (clientId === ydoc.clientID)
134
- return; // self
135
- if (clientId === AGENT_CLIENT_ID)
136
- return; // agent isn't a Yjs editor
137
- const s = state;
138
- if (s && s.user && s.visible !== false)
139
- peers += 1;
140
- });
141
- peerCountRef.current = peers;
142
- };
143
- update();
144
- awareness.on("change", update);
145
- document.addEventListener("visibilitychange", update);
146
- return () => {
147
- awareness.off("change", update);
148
- document.removeEventListener("visibilitychange", update);
149
- };
150
- }, [collab, awareness, ydoc]);
151
- // Collab seed: populate an empty shared Y.Doc from the markdown `value` once.
152
- // The Collaboration extension does NOT auto-seed; only the lead client does,
153
- // so two clients opening a brand-new block at once don't both seed (which
154
- // would duplicate the content via concurrent inserts at the same position).
155
- const seededRef = useRef(false);
156
- useEffect(() => {
157
- if (!collab || !editor || editor.isDestroyed || !ydoc)
158
- return;
159
- if (seededRef.current)
160
- return;
161
- if (!collabSynced)
162
- return;
163
- if (!isLeadClient)
164
- return;
165
- if (!value.trim())
166
- return;
167
- const fragment = ydoc.getXmlFragment("default");
168
- const currentMarkdown = getMarkdown(editor);
169
- // Seed only when the shared doc is genuinely empty — either the fragment has
170
- // no nodes yet, or it holds no semantic markdown (an empty paragraph, or an
171
- // app's sentinel-empty filler via a custom `shouldSeed`).
172
- if (!shouldSeed({ value, currentMarkdown, fragmentLength: fragment.length })) {
173
- seededRef.current = true;
174
- return;
175
- }
176
- let cancelled = false;
177
- // Defer via a timer task (NOT a microtask — microtasks can still run
178
- // inside React's commit and trigger flushSync-from-lifecycle warnings).
179
- const seedTimer = setTimeout(() => {
180
- if (cancelled || editor.isDestroyed)
181
- return;
182
- isSettingContentRef.current = true;
183
- try {
184
- setContent(editor, value, {});
185
- }
186
- finally {
187
- isSettingContentRef.current = false;
188
- }
189
- const serialized = getMarkdown(editor);
190
- lastEmittedRef.current = serialized;
191
- pushEmittedRing(recentEmittedRef.current, serialized);
192
- lastAppliedValueRef.current = value;
193
- lastAppliedSerializedRef.current = serialized;
194
- if (contentUpdatedAt)
195
- lastAppliedUpdatedAtRef.current = contentUpdatedAt;
196
- seededRef.current = true;
197
- }, 0);
198
- return () => {
199
- cancelled = true;
200
- clearTimeout(seedTimer);
201
- };
202
- }, [
203
- collab,
204
- collabSynced,
205
- editor,
206
- ydoc,
207
- value,
208
- isLeadClient,
209
- contentUpdatedAt,
210
- getMarkdown,
211
- setContent,
212
- shouldSeed,
213
- ]);
214
- // Reconcile authoritative external markdown (agent edit, source patch, or a
215
- // peer edit mirrored to SQL) into the live editor. In collab mode only the
216
- // lead client applies it through setContent; Yjs propagates the result to
217
- // every other client. In non-collab mode this is the original controlled-value
218
- // reconcile, unchanged.
219
- useEffect(() => {
220
- if (!editor || editor.isDestroyed)
221
- return;
222
- let cancelled = false;
223
- let retry = null;
224
- // With peers present, a peer's edit also arrives via Yjs. Defer one poll
225
- // cycle (+margin) and re-check before applying via setContent so the same
226
- // change isn't inserted twice (Yjs + setContent → duplicated region).
227
- const PEER_SETTLE_MS = 2500;
228
- const apply = (deferred = false) => {
229
- if (cancelled || editor.isDestroyed)
230
- return;
231
- // In collab mode, defer all reconcile until the shared doc is seeded so we
232
- // never setContent over an unseeded fragment.
233
- if (collab && (!collabSynced || !seededRef.current)) {
234
- retry = setTimeout(() => apply(deferred), 300);
235
- return;
236
- }
237
- const currentMarkdown = getMarkdown(editor);
238
- // Compare against the canonical form the editor would emit so a serializer
239
- // that re-normalizes (Content's NFM) still recognizes "already in sync".
240
- const normalizedValue = normalizeValue(value);
241
- // Whether the editor still holds exactly what THIS hook last applied (the
242
- // user hasn't edited since). Only then are the round-trip echo guards
243
- // below safe: if the user has since edited away from the applied content,
244
- // an external snapshot equal to a previously-applied value is a real
245
- // revert and must NOT be swallowed as an echo.
246
- const editorUnchangedSinceApply = lastAppliedSerializedRef.current !== null &&
247
- currentMarkdown === lastAppliedSerializedRef.current;
248
- const typingRecently = editor.isFocused && Date.now() - lastTypedAtRef.current < 1500;
249
- // Doc-equivalence skip. Never re-apply content the editor already
250
- // represents — comparing by DOC EQUIVALENCE, not raw strings/timestamps:
251
- // 1. `currentMarkdown === normalizedValue` — the editor's CURRENT
252
- // serialized doc already equals the (normalized) incoming value.
253
- // 2. `value === lastEmittedRef.current` — the incoming value is our own
254
- // just-emitted markdown echoing back.
255
- // 3. `value === lastAppliedValueRef.current` — the SAME raw value we
256
- // already applied is being re-supplied (a lagging poll or a
257
- // source-sync re-handing the same stored markdown). Applying it again
258
- // would only reproduce the doc we hold; for NON-idempotent input it
259
- // would compound divergence. Guarded by `editorUnchangedSinceApply`
260
- // so a deliberate revert-to-previous after a local edit still lands.
261
- // 4. `normalizedValue === lastAppliedSerializedRef.current` — the
262
- // incoming value round-trips to the serialized output we last
263
- // produced (our own autosaved echo coming back from SQL). For
264
- // non-idempotent input the raw string differs from what we were
265
- // handed, but it is doc-equivalent to what the editor already shows,
266
- // so re-parsing it must be skipped. This is the guard that stops the
267
- // `<p>` → `&lt;p&gt;` → `&amp;lt;p&amp;gt;` escalation.
268
- if (currentMarkdown === normalizedValue ||
269
- (typingRecently &&
270
- // A stale echo of our own (possibly partial) save while the user is
271
- // actively typing would clobber the fresh tail. Outside active
272
- // typing, the same bytes can be a deliberate newer external revert
273
- // (history restore / provider conflict choice) and must be allowed
274
- // through even if mount-time normalization emitted them.
275
- (value === lastEmittedRef.current ||
276
- recentEmittedRef.current.includes(value) ||
277
- recentEmittedRef.current.includes(normalizedValue))) ||
278
- (editorUnchangedSinceApply &&
279
- (value === lastAppliedValueRef.current ||
280
- normalizedValue === lastAppliedSerializedRef.current))) {
281
- if (contentUpdatedAt) {
282
- lastAppliedUpdatedAtRef.current = contentUpdatedAt;
283
- }
284
- return;
285
- }
286
- const externalNewer = !lastAppliedUpdatedAtRef.current ||
287
- !contentUpdatedAt ||
288
- contentUpdatedAt > lastAppliedUpdatedAtRef.current;
289
- // Only the lead client applies an authoritative snapshot into the shared
290
- // Y.Doc; peers receive it through Yjs sync.
291
- if (collab && !isLeadClient) {
292
- if (contentUpdatedAt && !externalNewer) {
293
- lastAppliedUpdatedAtRef.current = contentUpdatedAt;
294
- }
295
- return;
296
- }
297
- // Never clobber an in-progress edit. While the user is actively typing
298
- // (focused and a keystroke landed within the window) defer and re-check —
299
- // applying external content now would yank text out from under them and,
300
- // for non-idempotent input, fight every keystroke. Newer external content
301
- // retries so it still lands once they pause; older-or-equal content is a
302
- // stale poll and is dropped outright while focused.
303
- if (typingRecently) {
304
- if (externalNewer) {
305
- retry = setTimeout(() => apply(deferred), 700);
306
- }
307
- return;
308
- }
309
- // Older-or-equal content is a stale poll / lagging echo. Drop it while
310
- // focused (a peer/agent edit would be NEWER and retries above). In
311
- // NON-COLLAB mode there is no peer, so older-or-equal external content is
312
- // ALWAYS stale — dropping it regardless of focus stops a lagging
313
- // `get-visual-plan` poll from reverting a just-applied local structural
314
- // change (drag-to-columns) while the editor is blurred (the drag grips the
315
- // handle, not the prose, so `isFocused` is false at drop time). Gated on
316
- // `lastAppliedSerializedRef` so the very first seed (nothing applied yet,
317
- // also not-newer) still lands.
318
- const seeded = lastAppliedSerializedRef.current !== null;
319
- if (!externalNewer && (editor.isFocused || (!collab && seeded)))
320
- return;
321
- // Race guard: with peers present, let Yjs deliver a peer's edit first.
322
- // Defer once and re-check — a peer edit makes the equality check above
323
- // no-op next pass; an agent/source edit still differs and applies.
324
- if (collab && externalNewer && !deferred && peerCountRef.current > 0) {
325
- retry = setTimeout(() => apply(true), PEER_SETTLE_MS);
326
- return;
327
- }
328
- const applyTimer = setTimeout(() => {
329
- if (cancelled || editor.isDestroyed)
330
- return;
331
- // Re-check doc-equivalence at apply time. Between the decision above and
332
- // this task a peer/Yjs edit (or our own prior apply) may have made
333
- // the editor already represent this value — re-applying would be a
334
- // wasted setContent that, for non-idempotent input, re-triggers the
335
- // loop. Skip when the editor's current serialization already matches the
336
- // normalized value, or the value round-trips to what we last produced.
337
- const beforeMarkdown = getMarkdown(editor);
338
- const normalized = normalizeValue(value);
339
- const unchangedSinceApply = lastAppliedSerializedRef.current !== null &&
340
- beforeMarkdown === lastAppliedSerializedRef.current;
341
- if (beforeMarkdown === normalized ||
342
- (unchangedSinceApply &&
343
- normalized === lastAppliedSerializedRef.current)) {
344
- lastAppliedValueRef.current = value;
345
- if (contentUpdatedAt) {
346
- lastAppliedUpdatedAtRef.current = contentUpdatedAt;
347
- }
348
- return;
349
- }
350
- isSettingContentRef.current = true;
351
- // Surgical path first: replace only the changed top-level run so
352
- // unchanged block NodeViews are never torn down and (under
353
- // Collaboration) Yjs sees a minimal edit instead of a full-fragment
354
- // rewrite. Falls back to the classic whole-document setContent when no
355
- // parser is available or the targeted transaction cannot be applied.
356
- let appliedSurgically = false;
357
- if (parseValue !== false) {
358
- const parse = parseValue ?? defaultParseValue;
359
- const parsedDoc = parse(editor, value);
360
- if (parsedDoc) {
361
- const result = applyDocSurgically(editor, parsedDoc);
362
- appliedSurgically = result === "applied" || result === "noop";
363
- }
364
- }
365
- if (!appliedSurgically) {
366
- setContent(editor, value, { emitUpdate: false, addToHistory: false });
367
- }
368
- isSettingContentRef.current = false;
369
- // Capture the SERIALIZED result, not the raw value. For non-idempotent
370
- // input these differ; recording the serialized output is what lets the
371
- // next poll (which returns this serialized form) be recognized as our
372
- // own echo and skipped — stabilizing the doc after exactly one apply.
373
- const serialized = getMarkdown(editor);
374
- lastEmittedRef.current = serialized;
375
- pushEmittedRing(recentEmittedRef.current, serialized);
376
- lastAppliedValueRef.current = value;
377
- lastAppliedSerializedRef.current = serialized;
378
- if (contentUpdatedAt) {
379
- lastAppliedUpdatedAtRef.current = contentUpdatedAt;
380
- }
381
- }, 0);
382
- retry = applyTimer;
383
- };
384
- apply();
385
- return () => {
386
- cancelled = true;
387
- if (retry)
388
- clearTimeout(retry);
389
- };
390
- }, [
391
- contentUpdatedAt,
392
- editor,
393
- value,
394
- collab,
395
- collabSynced,
396
- isLeadClient,
397
- getMarkdown,
398
- setContent,
399
- parseValue,
400
- normalizeValue,
401
- ]);
402
- const shouldIgnoreUpdate = (transaction) => {
403
- if (!editable || isSettingContentRef.current)
404
- return true;
405
- // Collaboration can emit schema/default-paragraph transactions while the
406
- // persisted Y.Doc is still loading and before the authoritative value has
407
- // seeded/reconciled. Never let those transient pre-seed updates reach an
408
- // app's local mirror or autosave path (Content serializes an empty paragraph
409
- // as `<empty-block/>`, which is non-empty text and bypasses the generic
410
- // registerEmitted empty-string guard).
411
- if (collab && !seededRef.current)
412
- return true;
413
- if (transaction.getMeta(RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION)) {
414
- return true;
415
- }
416
- // In collab mode, never persist remote-originated changes (the initial Yjs
417
- // state load or a peer's edit arriving via sync). Each client saves only its
418
- // OWN local edits; a peer's edit is saved by that peer. Without this, a
419
- // lagging Y.Doc load would write stale markdown over newer SQL.
420
- if (collab && transaction && isChangeOrigin(transaction))
421
- return true;
422
- lastTypedAtRef.current = Date.now();
423
- return false;
424
- };
425
- const registerEmitted = (markdown) => {
426
- // Don't persist an empty doc before Collaboration has seeded — that would
427
- // clobber the saved block content with an empty string.
428
- if (collab && !markdown.trim())
429
- return false;
430
- lastEmittedRef.current = markdown;
431
- pushEmittedRing(recentEmittedRef.current, markdown);
432
- return true;
433
- };
434
- return {
435
- collab,
436
- isSettingContentRef,
437
- shouldIgnoreUpdate,
438
- registerEmitted,
439
- };
440
- }
441
- //# sourceMappingURL=useCollabReconcile.js.map