mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-05 13:29:44 +02:00
* fix(editor): sync ContentEditor when defaultValue changes externally
Tiptap v3 `useEditor` reads `content` only at mount (ueberdosis/tiptap#5831
— by design), so when an issue description is updated remotely (WS event,
another agent, another client), the editor kept showing stale content
until the issue was closed and reopened. `key={id}` in issue-detail only
force-remounts on issue switch, not on same-issue updates.
Add a useEffect in ContentEditor that watches `defaultValue` and applies
it via `editor.commands.setContent()` with four guards:
1. Focused AND dirty — protect bytes the user is actively typing.
Focused-but-clean intentionally falls through: onBlur has no replay
path, so an unconditional `if (isFocused) return` would drop the
sync forever for users who click into the editor without typing.
2. Unfocused AND dirty — covers the blur → debounce (1500ms) window
where the editor holds unsaved content but isFocused is already
false. The pending onUpdate flush reconciles via the cache;
overwriting here would be silent data loss.
3. Normalized-equal short-circuit — avoids a no-op transaction when
the cache reflects a write this editor just emitted.
4. `emitUpdate: false` — Tiptap v3 flipped setContent's emitUpdate
default to true; without this the sync would re-trigger onUpdate
→ server save → self-write loop.
After setContent, clamp the prior selection to the new doc size so the
caret doesn't snap to position 0.
Tests cover five cases: unfocused+dirty-content (sync fires),
focused+dirty (skip), focused+clean (must sync — regression guard for
the focused-but-clean hole), unfocused+dirty (blur-before-debounce
window, skip), and normalized-equal short-circuit (skip).
Closes #2409
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
* test(editor): cover normalized-equal sync path with a distinct defaultValue
The previous rerender passed the same `defaultValue` string, so React's
dep-array equality short-circuited the sync effect entirely — the test
only exercised the first-mount equality check, not the actual
normalized-equal guard.
Pass a different-but-trimEnd-equivalent value so the effect re-runs and
the normalized-equal short-circuit is what keeps setContent uncalled.
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>