@agent-native/toolkit 0.4.10 → 0.5.1

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 (144) hide show
  1. package/README.md +17 -6
  2. package/dist/collab-ui/agent-identity.d.ts +9 -0
  3. package/dist/collab-ui/agent-identity.d.ts.map +1 -0
  4. package/dist/collab-ui/agent-identity.js +9 -0
  5. package/dist/collab-ui/agent-identity.js.map +1 -0
  6. package/dist/collab-ui/index.d.ts +2 -0
  7. package/dist/collab-ui/index.d.ts.map +1 -1
  8. package/dist/collab-ui/index.js +2 -0
  9. package/dist/collab-ui/index.js.map +1 -1
  10. package/dist/collab-ui/lead-client.d.ts +3 -0
  11. package/dist/collab-ui/lead-client.d.ts.map +1 -0
  12. package/dist/collab-ui/lead-client.js +29 -0
  13. package/dist/collab-ui/lead-client.js.map +1 -0
  14. package/dist/context-ui/ContextMeter.d.ts +10 -0
  15. package/dist/context-ui/ContextMeter.d.ts.map +1 -0
  16. package/dist/context-ui/ContextMeter.js +20 -0
  17. package/dist/context-ui/ContextMeter.js.map +1 -0
  18. package/dist/context-ui/ContextSegmentRow.d.ts +14 -0
  19. package/dist/context-ui/ContextSegmentRow.d.ts.map +1 -0
  20. package/dist/context-ui/ContextSegmentRow.js +25 -0
  21. package/dist/context-ui/ContextSegmentRow.js.map +1 -0
  22. package/dist/context-ui/ContextTreemap.d.ts +7 -0
  23. package/dist/context-ui/ContextTreemap.d.ts.map +1 -0
  24. package/dist/context-ui/ContextTreemap.js +55 -0
  25. package/dist/context-ui/ContextTreemap.js.map +1 -0
  26. package/dist/context-ui/ContextXRayPanel.d.ts +13 -0
  27. package/dist/context-ui/ContextXRayPanel.d.ts.map +1 -0
  28. package/dist/context-ui/ContextXRayPanel.js +120 -0
  29. package/dist/context-ui/ContextXRayPanel.js.map +1 -0
  30. package/dist/context-ui/format.d.ts +6 -0
  31. package/dist/context-ui/format.d.ts.map +1 -0
  32. package/dist/context-ui/format.js +58 -0
  33. package/dist/context-ui/format.js.map +1 -0
  34. package/dist/context-ui/index.d.ts +7 -0
  35. package/dist/context-ui/index.d.ts.map +1 -0
  36. package/dist/context-ui/index.js +6 -0
  37. package/dist/context-ui/index.js.map +1 -0
  38. package/dist/context-ui/types.d.ts +39 -0
  39. package/dist/context-ui/types.d.ts.map +1 -0
  40. package/dist/context-ui/types.js +2 -0
  41. package/dist/context-ui/types.js.map +1 -0
  42. package/dist/design-tweaks/index.d.ts +2 -0
  43. package/dist/design-tweaks/index.d.ts.map +1 -0
  44. package/dist/design-tweaks/index.js +2 -0
  45. package/dist/design-tweaks/index.js.map +1 -0
  46. package/dist/design-tweaks/visual-style-controls.d.ts +90 -0
  47. package/dist/design-tweaks/visual-style-controls.d.ts.map +1 -0
  48. package/dist/design-tweaks/visual-style-controls.js +499 -0
  49. package/dist/design-tweaks/visual-style-controls.js.map +1 -0
  50. package/dist/editor/BubbleToolbar.d.ts +37 -0
  51. package/dist/editor/BubbleToolbar.d.ts.map +1 -0
  52. package/dist/editor/BubbleToolbar.js +171 -0
  53. package/dist/editor/BubbleToolbar.js.map +1 -0
  54. package/dist/editor/CodeBlockNode.d.ts +49 -0
  55. package/dist/editor/CodeBlockNode.d.ts.map +1 -0
  56. package/dist/editor/CodeBlockNode.js +127 -0
  57. package/dist/editor/CodeBlockNode.js.map +1 -0
  58. package/dist/editor/DragHandle.d.ts +97 -0
  59. package/dist/editor/DragHandle.d.ts.map +1 -0
  60. package/dist/editor/DragHandle.js +1066 -0
  61. package/dist/editor/DragHandle.js.map +1 -0
  62. package/dist/editor/DragHandle.spec.d.ts +2 -0
  63. package/dist/editor/DragHandle.spec.d.ts.map +1 -0
  64. package/dist/editor/DragHandle.spec.js +407 -0
  65. package/dist/editor/DragHandle.spec.js.map +1 -0
  66. package/dist/editor/ImageExtension.d.ts +63 -0
  67. package/dist/editor/ImageExtension.d.ts.map +1 -0
  68. package/dist/editor/ImageExtension.js +243 -0
  69. package/dist/editor/ImageExtension.js.map +1 -0
  70. package/dist/editor/ImageExtension.roundtrip.spec.d.ts +2 -0
  71. package/dist/editor/ImageExtension.roundtrip.spec.d.ts.map +1 -0
  72. package/dist/editor/ImageExtension.roundtrip.spec.js +103 -0
  73. package/dist/editor/ImageExtension.roundtrip.spec.js.map +1 -0
  74. package/dist/editor/RichMarkdownEditor.collab.spec.d.ts +2 -0
  75. package/dist/editor/RichMarkdownEditor.collab.spec.d.ts.map +1 -0
  76. package/dist/editor/RichMarkdownEditor.collab.spec.js +65 -0
  77. package/dist/editor/RichMarkdownEditor.collab.spec.js.map +1 -0
  78. package/dist/editor/RichMarkdownEditor.d.ts +52 -0
  79. package/dist/editor/RichMarkdownEditor.d.ts.map +1 -0
  80. package/dist/editor/RichMarkdownEditor.js +38 -0
  81. package/dist/editor/RichMarkdownEditor.js.map +1 -0
  82. package/dist/editor/RichMarkdownEditor.roundtrip.spec.d.ts +2 -0
  83. package/dist/editor/RichMarkdownEditor.roundtrip.spec.d.ts.map +1 -0
  84. package/dist/editor/RichMarkdownEditor.roundtrip.spec.js +186 -0
  85. package/dist/editor/RichMarkdownEditor.roundtrip.spec.js.map +1 -0
  86. package/dist/editor/RunId.d.ts +28 -0
  87. package/dist/editor/RunId.d.ts.map +1 -0
  88. package/dist/editor/RunId.js +60 -0
  89. package/dist/editor/RunId.js.map +1 -0
  90. package/dist/editor/SharedRichEditor.d.ts +110 -0
  91. package/dist/editor/SharedRichEditor.d.ts.map +1 -0
  92. package/dist/editor/SharedRichEditor.js +144 -0
  93. package/dist/editor/SharedRichEditor.js.map +1 -0
  94. package/dist/editor/SlashCommandMenu.d.ts +41 -0
  95. package/dist/editor/SlashCommandMenu.d.ts.map +1 -0
  96. package/dist/editor/SlashCommandMenu.js +221 -0
  97. package/dist/editor/SlashCommandMenu.js.map +1 -0
  98. package/dist/editor/disableHistory.spec.d.ts +2 -0
  99. package/dist/editor/disableHistory.spec.d.ts.map +1 -0
  100. package/dist/editor/disableHistory.spec.js +73 -0
  101. package/dist/editor/disableHistory.spec.js.map +1 -0
  102. package/dist/editor/extensions.d.ts +178 -0
  103. package/dist/editor/extensions.d.ts.map +1 -0
  104. package/dist/editor/extensions.js +224 -0
  105. package/dist/editor/extensions.js.map +1 -0
  106. package/dist/editor/gfmDoc.d.ts +24 -0
  107. package/dist/editor/gfmDoc.d.ts.map +1 -0
  108. package/dist/editor/gfmDoc.js +83 -0
  109. package/dist/editor/gfmDoc.js.map +1 -0
  110. package/dist/editor/gfmDoc.spec.d.ts +2 -0
  111. package/dist/editor/gfmDoc.spec.d.ts.map +1 -0
  112. package/dist/editor/gfmDoc.spec.js +194 -0
  113. package/dist/editor/gfmDoc.spec.js.map +1 -0
  114. package/dist/editor/index.d.ts +14 -0
  115. package/dist/editor/index.d.ts.map +1 -0
  116. package/dist/editor/index.js +13 -0
  117. package/dist/editor/index.js.map +1 -0
  118. package/dist/editor/structuralReorderUndo.spec.d.ts +2 -0
  119. package/dist/editor/structuralReorderUndo.spec.d.ts.map +1 -0
  120. package/dist/editor/structuralReorderUndo.spec.js +120 -0
  121. package/dist/editor/structuralReorderUndo.spec.js.map +1 -0
  122. package/dist/editor/surgical-apply.d.ts +66 -0
  123. package/dist/editor/surgical-apply.d.ts.map +1 -0
  124. package/dist/editor/surgical-apply.js +147 -0
  125. package/dist/editor/surgical-apply.js.map +1 -0
  126. package/dist/editor/surgical-apply.spec.d.ts +2 -0
  127. package/dist/editor/surgical-apply.spec.d.ts.map +1 -0
  128. package/dist/editor/surgical-apply.spec.js +257 -0
  129. package/dist/editor/surgical-apply.spec.js.map +1 -0
  130. package/dist/editor/useCollabReconcile.concurrent.spec.d.ts +2 -0
  131. package/dist/editor/useCollabReconcile.concurrent.spec.d.ts.map +1 -0
  132. package/dist/editor/useCollabReconcile.concurrent.spec.js +329 -0
  133. package/dist/editor/useCollabReconcile.concurrent.spec.js.map +1 -0
  134. package/dist/editor/useCollabReconcile.d.ts +110 -0
  135. package/dist/editor/useCollabReconcile.d.ts.map +1 -0
  136. package/dist/editor/useCollabReconcile.idempotent.spec.d.ts +2 -0
  137. package/dist/editor/useCollabReconcile.idempotent.spec.d.ts.map +1 -0
  138. package/dist/editor/useCollabReconcile.idempotent.spec.js +248 -0
  139. package/dist/editor/useCollabReconcile.idempotent.spec.js.map +1 -0
  140. package/dist/editor/useCollabReconcile.js +440 -0
  141. package/dist/editor/useCollabReconcile.js.map +1 -0
  142. package/dist/editor.css +510 -0
  143. package/migration-manifest.json +4 -0
  144. package/package.json +65 -9
@@ -0,0 +1,440 @@
1
+ import { isChangeOrigin } from "@tiptap/extension-collaboration";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { AGENT_CLIENT_ID, isReconcileLeadClient } from "../collab-ui/index.js";
4
+ import { RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION, applyDocSurgically, defaultParseValue, } from "./surgical-apply.js";
5
+ export { RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION };
6
+ /** Reads the current markdown out of the tiptap-markdown storage. */
7
+ export function getEditorMarkdown(editor) {
8
+ const markdownStorage = editor.storage;
9
+ return markdownStorage.markdown?.getMarkdown?.() ?? "";
10
+ }
11
+ /**
12
+ * Push a value onto the bounded ring of recently-emitted markdown (most recent
13
+ * last, deduped, capped). The reconcile uses this to recognize a stale-but-recent
14
+ * echo of OUR OWN edits: a debounced autosave can persist a PARTIAL burst, and
15
+ * the next poll re-supplies that partial value with a newer timestamp — applying
16
+ * it would clobber the freshly-typed tail. An external change (agent/peer) never
17
+ * byte-matches one of our own recent emissions, and if it somehow did the content
18
+ * is identical, so skipping it is safe by construction.
19
+ */
20
+ const EMITTED_RING_MAX = 16;
21
+ function pushEmittedRing(ring, value) {
22
+ if (!value)
23
+ return;
24
+ if (ring[ring.length - 1] === value)
25
+ return;
26
+ const dupe = ring.indexOf(value);
27
+ if (dupe !== -1)
28
+ ring.splice(dupe, 1);
29
+ ring.push(value);
30
+ if (ring.length > EMITTED_RING_MAX)
31
+ ring.shift();
32
+ }
33
+ /**
34
+ * The subtle seed / reconcile / lead-client logic for the shared markdown
35
+ * editor, extracted once so it can never be duplicated across embedders.
36
+ *
37
+ * Responsibilities (reproducing the Plan editor's behavior exactly):
38
+ * - Track whether THIS client is the reconcile lead (sole client always leads;
39
+ * otherwise elected via {@link isReconcileLeadClient}) and how many other
40
+ * visible human peers are present.
41
+ * - Seed an empty shared Y.Doc once from `value` — lead client only — so two
42
+ * clients opening a brand-new block don't both insert the content.
43
+ * - Reconcile authoritative external markdown (agent edit, source patch, peer
44
+ * edit mirrored to SQL) into the editor: in collab mode only the lead client
45
+ * applies it through `setContent` and Yjs propagates; in non-collab mode this
46
+ * is the original controlled-value reconcile.
47
+ * - Provide the `onUpdate` guards (`shouldIgnoreUpdate`, `registerEmitted`) so
48
+ * the component never persists remote-origin or pre-seed empty content.
49
+ */
50
+ /** Default seed predicate: seed only when the shared doc is genuinely empty. */
51
+ function defaultShouldSeed({ currentMarkdown, fragmentLength, }) {
52
+ return fragmentLength === 0 || !currentMarkdown.trim();
53
+ }
54
+ /**
55
+ * Default content writer: hand the raw markdown string to `setContent`, which
56
+ * tiptap-markdown overrides to parse the markdown into a ProseMirror doc.
57
+ *
58
+ * IMPORTANT: do NOT pass `parseOptions: { preserveWhitespace: "full" }` here.
59
+ * In tiptap v3 the core `setContent` command routes `preserveWhitespace: "full"`
60
+ * through `insertContentAt`, which tiptap-markdown ALSO overrides to re-run its
61
+ * markdown parser. That double-parse stringifies the already-parsed PM doc and
62
+ * re-parses it as HTML, so a clean heading/list/code block comes back as the
63
+ * escaped, non-idempotent `<h1>…` — which then escalates every reconcile
64
+ * cycle (`<p>` → `&lt;p&gt;` → `&amp;lt;p&amp;gt;` …). Letting the markdown
65
+ * override parse the string directly (no `parseOptions`) round-trips byte-stably
66
+ * for the GFM corpus, including code-block and empty-line whitespace. Content's
67
+ * NFM path supplies its own `setContent` (it passes a pre-parsed PM doc) and is
68
+ * unaffected by this default.
69
+ */
70
+ function defaultSetContent(editor, value, options) {
71
+ if (options.addToHistory === false) {
72
+ editor
73
+ .chain()
74
+ .command(({ tr }) => {
75
+ // addToHistory:false so cmd+z (or Yjs undo) doesn't erase
76
+ // externally-loaded content.
77
+ tr.setMeta("addToHistory", false);
78
+ return true;
79
+ })
80
+ .setContent(value, { emitUpdate: options.emitUpdate })
81
+ .run();
82
+ return;
83
+ }
84
+ editor.commands.setContent(value);
85
+ }
86
+ export function useCollabReconcile({ editor, ydoc = null, collabSynced = true, awareness = null, value, contentUpdatedAt, editable, getMarkdown = getEditorMarkdown, setContent = defaultSetContent, parseValue, normalizeValue = (v) => v, shouldSeed = defaultShouldSeed, initialAppliedUpdatedAt, }) {
87
+ const collab = !!ydoc;
88
+ const isSettingContentRef = useRef(false);
89
+ const lastEmittedRef = useRef("");
90
+ // Ring of recent local emissions (see pushEmittedRing). Lets the reconcile
91
+ // recognize a stale-but-recent echo of our OWN (possibly partial, debounced)
92
+ // save so a lagging poll never clobbers freshly-typed text.
93
+ const recentEmittedRef = useRef([]);
94
+ const lastTypedAtRef = useRef(0);
95
+ // The raw authoritative `value` string the reconcile last applied. When the
96
+ // SAME raw string is re-fetched (a lagging poll, or a source-sync that keeps
97
+ // re-supplying the same stored markdown), applying it again would only
98
+ // reproduce the doc we already hold — and if `value` is NON-idempotent
99
+ // (serialize(parse(value)) !== value) re-applying compounds the divergence
100
+ // every cycle (`<p>` → `&lt;p&gt;` → `&amp;lt;p&amp;gt;` …). Tracked so the
101
+ // identical re-fetch is recognized and skipped.
102
+ const lastAppliedValueRef = useRef(null);
103
+ // The editor's SERIALIZED output captured right AFTER the last reconcile/seed
104
+ // apply (`getMarkdown(editor)` once the content settled). For non-idempotent
105
+ // input this is what autosave actually persists, so the NEXT poll hands it
106
+ // back as the new `value`. Comparing the incoming value against this lets the
107
+ // reconcile recognize its own echo even when the raw string changed once, so
108
+ // it never re-parses content the editor already represents. This is the
109
+ // doc-equivalence guard that breaks the escalation loop.
110
+ const lastAppliedSerializedRef = useRef(null);
111
+ const lastAppliedUpdatedAtRef = useRef(initialAppliedUpdatedAt !== undefined
112
+ ? initialAppliedUpdatedAt
113
+ : (contentUpdatedAt ?? null));
114
+ // Whether THIS client is the one that seeds the empty shared doc / applies an
115
+ // authoritative external snapshot into it. Exactly one client does, so the
116
+ // content isn't inserted once per open editor. A sole client always leads.
117
+ const [isLeadClient, setIsLeadClient] = useState(true);
118
+ // Count of OTHER visible human collaborators. When >0, a peer's edit also
119
+ // arrives via Yjs, so external markdown reconcile must defer (avoid applying
120
+ // the same change through both Yjs and setContent).
121
+ const peerCountRef = useRef(0);
122
+ useEffect(() => {
123
+ if (!collab || !awareness || !ydoc) {
124
+ setIsLeadClient(true);
125
+ peerCountRef.current = 0;
126
+ return;
127
+ }
128
+ const update = () => {
129
+ setIsLeadClient(isReconcileLeadClient(awareness, ydoc.clientID));
130
+ let peers = 0;
131
+ awareness.getStates().forEach((state, clientId) => {
132
+ if (clientId === ydoc.clientID)
133
+ return; // self
134
+ if (clientId === AGENT_CLIENT_ID)
135
+ return; // agent isn't a Yjs editor
136
+ const s = state;
137
+ if (s && s.user && s.visible !== false)
138
+ peers += 1;
139
+ });
140
+ peerCountRef.current = peers;
141
+ };
142
+ update();
143
+ awareness.on("change", update);
144
+ document.addEventListener("visibilitychange", update);
145
+ return () => {
146
+ awareness.off("change", update);
147
+ document.removeEventListener("visibilitychange", update);
148
+ };
149
+ }, [collab, awareness, ydoc]);
150
+ // Collab seed: populate an empty shared Y.Doc from the markdown `value` once.
151
+ // The Collaboration extension does NOT auto-seed; only the lead client does,
152
+ // so two clients opening a brand-new block at once don't both seed (which
153
+ // would duplicate the content via concurrent inserts at the same position).
154
+ const seededRef = useRef(false);
155
+ useEffect(() => {
156
+ if (!collab || !editor || editor.isDestroyed || !ydoc)
157
+ return;
158
+ if (seededRef.current)
159
+ return;
160
+ if (!collabSynced)
161
+ return;
162
+ if (!isLeadClient)
163
+ return;
164
+ if (!value.trim())
165
+ return;
166
+ const fragment = ydoc.getXmlFragment("default");
167
+ const currentMarkdown = getMarkdown(editor);
168
+ // Seed only when the shared doc is genuinely empty — either the fragment has
169
+ // no nodes yet, or it holds no semantic markdown (an empty paragraph, or an
170
+ // app's sentinel-empty filler via a custom `shouldSeed`).
171
+ if (!shouldSeed({ value, currentMarkdown, fragmentLength: fragment.length })) {
172
+ seededRef.current = true;
173
+ return;
174
+ }
175
+ let cancelled = false;
176
+ // Defer via a timer task (NOT a microtask — microtasks can still run
177
+ // inside React's commit and trigger flushSync-from-lifecycle warnings).
178
+ const seedTimer = setTimeout(() => {
179
+ if (cancelled || editor.isDestroyed)
180
+ return;
181
+ isSettingContentRef.current = true;
182
+ try {
183
+ setContent(editor, value, {});
184
+ }
185
+ finally {
186
+ isSettingContentRef.current = false;
187
+ }
188
+ const serialized = getMarkdown(editor);
189
+ lastEmittedRef.current = serialized;
190
+ pushEmittedRing(recentEmittedRef.current, serialized);
191
+ lastAppliedValueRef.current = value;
192
+ lastAppliedSerializedRef.current = serialized;
193
+ if (contentUpdatedAt)
194
+ lastAppliedUpdatedAtRef.current = contentUpdatedAt;
195
+ seededRef.current = true;
196
+ }, 0);
197
+ return () => {
198
+ cancelled = true;
199
+ clearTimeout(seedTimer);
200
+ };
201
+ }, [
202
+ collab,
203
+ collabSynced,
204
+ editor,
205
+ ydoc,
206
+ value,
207
+ isLeadClient,
208
+ contentUpdatedAt,
209
+ getMarkdown,
210
+ setContent,
211
+ shouldSeed,
212
+ ]);
213
+ // Reconcile authoritative external markdown (agent edit, source patch, or a
214
+ // peer edit mirrored to SQL) into the live editor. In collab mode only the
215
+ // lead client applies it through setContent; Yjs propagates the result to
216
+ // every other client. In non-collab mode this is the original controlled-value
217
+ // reconcile, unchanged.
218
+ useEffect(() => {
219
+ if (!editor || editor.isDestroyed)
220
+ return;
221
+ let cancelled = false;
222
+ let retry = null;
223
+ // With peers present, a peer's edit also arrives via Yjs. Defer one poll
224
+ // cycle (+margin) and re-check before applying via setContent so the same
225
+ // change isn't inserted twice (Yjs + setContent → duplicated region).
226
+ const PEER_SETTLE_MS = 2500;
227
+ const apply = (deferred = false) => {
228
+ if (cancelled || editor.isDestroyed)
229
+ return;
230
+ // In collab mode, defer all reconcile until the shared doc is seeded so we
231
+ // never setContent over an unseeded fragment.
232
+ if (collab && (!collabSynced || !seededRef.current)) {
233
+ retry = setTimeout(() => apply(deferred), 300);
234
+ return;
235
+ }
236
+ const currentMarkdown = getMarkdown(editor);
237
+ // Compare against the canonical form the editor would emit so a serializer
238
+ // that re-normalizes (Content's NFM) still recognizes "already in sync".
239
+ const normalizedValue = normalizeValue(value);
240
+ // Whether the editor still holds exactly what THIS hook last applied (the
241
+ // user hasn't edited since). Only then are the round-trip echo guards
242
+ // below safe: if the user has since edited away from the applied content,
243
+ // an external snapshot equal to a previously-applied value is a real
244
+ // revert and must NOT be swallowed as an echo.
245
+ const editorUnchangedSinceApply = lastAppliedSerializedRef.current !== null &&
246
+ currentMarkdown === lastAppliedSerializedRef.current;
247
+ const typingRecently = editor.isFocused && Date.now() - lastTypedAtRef.current < 1500;
248
+ // Doc-equivalence skip. Never re-apply content the editor already
249
+ // represents — comparing by DOC EQUIVALENCE, not raw strings/timestamps:
250
+ // 1. `currentMarkdown === normalizedValue` — the editor's CURRENT
251
+ // serialized doc already equals the (normalized) incoming value.
252
+ // 2. `value === lastEmittedRef.current` — the incoming value is our own
253
+ // just-emitted markdown echoing back.
254
+ // 3. `value === lastAppliedValueRef.current` — the SAME raw value we
255
+ // already applied is being re-supplied (a lagging poll or a
256
+ // source-sync re-handing the same stored markdown). Applying it again
257
+ // would only reproduce the doc we hold; for NON-idempotent input it
258
+ // would compound divergence. Guarded by `editorUnchangedSinceApply`
259
+ // so a deliberate revert-to-previous after a local edit still lands.
260
+ // 4. `normalizedValue === lastAppliedSerializedRef.current` — the
261
+ // incoming value round-trips to the serialized output we last
262
+ // produced (our own autosaved echo coming back from SQL). For
263
+ // non-idempotent input the raw string differs from what we were
264
+ // handed, but it is doc-equivalent to what the editor already shows,
265
+ // so re-parsing it must be skipped. This is the guard that stops the
266
+ // `<p>` → `&lt;p&gt;` → `&amp;lt;p&amp;gt;` escalation.
267
+ if (currentMarkdown === normalizedValue ||
268
+ (typingRecently &&
269
+ // A stale echo of our own (possibly partial) save while the user is
270
+ // actively typing would clobber the fresh tail. Outside active
271
+ // typing, the same bytes can be a deliberate newer external revert
272
+ // (history restore / provider conflict choice) and must be allowed
273
+ // through even if mount-time normalization emitted them.
274
+ (value === lastEmittedRef.current ||
275
+ recentEmittedRef.current.includes(value) ||
276
+ recentEmittedRef.current.includes(normalizedValue))) ||
277
+ (editorUnchangedSinceApply &&
278
+ (value === lastAppliedValueRef.current ||
279
+ normalizedValue === lastAppliedSerializedRef.current))) {
280
+ if (contentUpdatedAt) {
281
+ lastAppliedUpdatedAtRef.current = contentUpdatedAt;
282
+ }
283
+ return;
284
+ }
285
+ const externalNewer = !lastAppliedUpdatedAtRef.current ||
286
+ !contentUpdatedAt ||
287
+ contentUpdatedAt > lastAppliedUpdatedAtRef.current;
288
+ // Only the lead client applies an authoritative snapshot into the shared
289
+ // Y.Doc; peers receive it through Yjs sync.
290
+ if (collab && !isLeadClient) {
291
+ if (contentUpdatedAt && !externalNewer) {
292
+ lastAppliedUpdatedAtRef.current = contentUpdatedAt;
293
+ }
294
+ return;
295
+ }
296
+ // Never clobber an in-progress edit. While the user is actively typing
297
+ // (focused and a keystroke landed within the window) defer and re-check —
298
+ // applying external content now would yank text out from under them and,
299
+ // for non-idempotent input, fight every keystroke. Newer external content
300
+ // retries so it still lands once they pause; older-or-equal content is a
301
+ // stale poll and is dropped outright while focused.
302
+ if (typingRecently) {
303
+ if (externalNewer) {
304
+ retry = setTimeout(() => apply(deferred), 700);
305
+ }
306
+ return;
307
+ }
308
+ // Older-or-equal content is a stale poll / lagging echo. Drop it while
309
+ // focused (a peer/agent edit would be NEWER and retries above). In
310
+ // NON-COLLAB mode there is no peer, so older-or-equal external content is
311
+ // ALWAYS stale — dropping it regardless of focus stops a lagging
312
+ // `get-visual-plan` poll from reverting a just-applied local structural
313
+ // change (drag-to-columns) while the editor is blurred (the drag grips the
314
+ // handle, not the prose, so `isFocused` is false at drop time). Gated on
315
+ // `lastAppliedSerializedRef` so the very first seed (nothing applied yet,
316
+ // also not-newer) still lands.
317
+ const seeded = lastAppliedSerializedRef.current !== null;
318
+ if (!externalNewer && (editor.isFocused || (!collab && seeded)))
319
+ return;
320
+ // Race guard: with peers present, let Yjs deliver a peer's edit first.
321
+ // Defer once and re-check — a peer edit makes the equality check above
322
+ // no-op next pass; an agent/source edit still differs and applies.
323
+ if (collab && externalNewer && !deferred && peerCountRef.current > 0) {
324
+ retry = setTimeout(() => apply(true), PEER_SETTLE_MS);
325
+ return;
326
+ }
327
+ const applyTimer = setTimeout(() => {
328
+ if (cancelled || editor.isDestroyed)
329
+ return;
330
+ // Re-check doc-equivalence at apply time. Between the decision above and
331
+ // this task a peer/Yjs edit (or our own prior apply) may have made
332
+ // the editor already represent this value — re-applying would be a
333
+ // wasted setContent that, for non-idempotent input, re-triggers the
334
+ // loop. Skip when the editor's current serialization already matches the
335
+ // normalized value, or the value round-trips to what we last produced.
336
+ const beforeMarkdown = getMarkdown(editor);
337
+ const normalized = normalizeValue(value);
338
+ const unchangedSinceApply = lastAppliedSerializedRef.current !== null &&
339
+ beforeMarkdown === lastAppliedSerializedRef.current;
340
+ if (beforeMarkdown === normalized ||
341
+ (unchangedSinceApply &&
342
+ normalized === lastAppliedSerializedRef.current)) {
343
+ lastAppliedValueRef.current = value;
344
+ if (contentUpdatedAt) {
345
+ lastAppliedUpdatedAtRef.current = contentUpdatedAt;
346
+ }
347
+ return;
348
+ }
349
+ isSettingContentRef.current = true;
350
+ // Surgical path first: replace only the changed top-level run so
351
+ // unchanged block NodeViews are never torn down and (under
352
+ // Collaboration) Yjs sees a minimal edit instead of a full-fragment
353
+ // rewrite. Falls back to the classic whole-document setContent when no
354
+ // parser is available or the targeted transaction cannot be applied.
355
+ let appliedSurgically = false;
356
+ if (parseValue !== false) {
357
+ const parse = parseValue ?? defaultParseValue;
358
+ const parsedDoc = parse(editor, value);
359
+ if (parsedDoc) {
360
+ const result = applyDocSurgically(editor, parsedDoc);
361
+ appliedSurgically = result === "applied" || result === "noop";
362
+ }
363
+ }
364
+ if (!appliedSurgically) {
365
+ setContent(editor, value, { emitUpdate: false, addToHistory: false });
366
+ }
367
+ isSettingContentRef.current = false;
368
+ // Capture the SERIALIZED result, not the raw value. For non-idempotent
369
+ // input these differ; recording the serialized output is what lets the
370
+ // next poll (which returns this serialized form) be recognized as our
371
+ // own echo and skipped — stabilizing the doc after exactly one apply.
372
+ const serialized = getMarkdown(editor);
373
+ lastEmittedRef.current = serialized;
374
+ pushEmittedRing(recentEmittedRef.current, serialized);
375
+ lastAppliedValueRef.current = value;
376
+ lastAppliedSerializedRef.current = serialized;
377
+ if (contentUpdatedAt) {
378
+ lastAppliedUpdatedAtRef.current = contentUpdatedAt;
379
+ }
380
+ }, 0);
381
+ retry = applyTimer;
382
+ };
383
+ apply();
384
+ return () => {
385
+ cancelled = true;
386
+ if (retry)
387
+ clearTimeout(retry);
388
+ };
389
+ }, [
390
+ contentUpdatedAt,
391
+ editor,
392
+ value,
393
+ collab,
394
+ collabSynced,
395
+ isLeadClient,
396
+ getMarkdown,
397
+ setContent,
398
+ parseValue,
399
+ normalizeValue,
400
+ ]);
401
+ const shouldIgnoreUpdate = (transaction) => {
402
+ if (!editable || isSettingContentRef.current)
403
+ return true;
404
+ // Collaboration can emit schema/default-paragraph transactions while the
405
+ // persisted Y.Doc is still loading and before the authoritative value has
406
+ // seeded/reconciled. Never let those transient pre-seed updates reach an
407
+ // app's local mirror or autosave path (Content serializes an empty paragraph
408
+ // as `<empty-block/>`, which is non-empty text and bypasses the generic
409
+ // registerEmitted empty-string guard).
410
+ if (collab && !seededRef.current)
411
+ return true;
412
+ if (transaction.getMeta(RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION)) {
413
+ return true;
414
+ }
415
+ // In collab mode, never persist remote-originated changes (the initial Yjs
416
+ // state load or a peer's edit arriving via sync). Each client saves only its
417
+ // OWN local edits; a peer's edit is saved by that peer. Without this, a
418
+ // lagging Y.Doc load would write stale markdown over newer SQL.
419
+ if (collab && transaction && isChangeOrigin(transaction))
420
+ return true;
421
+ lastTypedAtRef.current = Date.now();
422
+ return false;
423
+ };
424
+ const registerEmitted = (markdown) => {
425
+ // Don't persist an empty doc before Collaboration has seeded — that would
426
+ // clobber the saved block content with an empty string.
427
+ if (collab && !markdown.trim())
428
+ return false;
429
+ lastEmittedRef.current = markdown;
430
+ pushEmittedRing(recentEmittedRef.current, markdown);
431
+ return true;
432
+ };
433
+ return {
434
+ collab,
435
+ isSettingContentRef,
436
+ shouldIgnoreUpdate,
437
+ registerEmitted,
438
+ };
439
+ }
440
+ //# sourceMappingURL=useCollabReconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCollabReconcile.js","sourceRoot":"","sources":["../../src/editor/useCollabReconcile.ts"],"names":[],"mappings":"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"}