fix(editor): don't let a title save echo drop typed characters - #2485
Open
julien-f wants to merge 1 commit into
Open
fix(editor): don't let a title save echo drop typed characters#2485julien-f wants to merge 1 commit into
julien-f wants to merge 1 commit into
Conversation
The reconcile effect in TitleEditor treats the React Query cache as the
source of truth and replaces the editor content whenever the cached title
differs from it. saveTitle already guards against applying a stale
response, but that check runs synchronously in the mutation's `.then()`,
while the effect only runs after React re-renders. A keystroke landing in
that gap is destroyed:
T+0ms save response arrives with title "foo bar"
guard: response === editor text -> passes -> updatePageData()
T+15ms user types "x" -> editor is one char ahead
T+20ms effect: title !== getText() -> setContent(title), "x" is gone
setContent also resets the selection, so the caret jumps and subsequent
keystrokes are inserted in the wrong place, which is what turns a single
lost character into a visibly mangled title.
Track the title we last pushed to the server and skip the reconcile when
the incoming value is our own echo, since in that window the echo is
always the stale side. Genuine remote renames and page switches still
replace the editor content as before.
Reproduced deterministically by holding the /pages/update response and
releasing it a fixed offset before a keystroke: 3 of 5 offsets in the
10-20ms range lose the character before this change, 0 of 8 after.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
salihudickson
left a comment
Collaborator
There was a problem hiding this comment.
Thanks for taking this up.
I think this works. But updating the title content immediately an update comes in also causes a similar issue.
Perhaps we can also check for local changes and only apply the update if none. @Philipinho, thoughts?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Typing a page title can silently drop characters and scramble the result.
This happens very often on our company instance, which has many users and a large number of pages — to the point that renaming a page is unreliable: characters vanish while typing, the title reverts to an older value mid-keystroke, and you end up having to fix the title up afterwards. It is much more likely the faster you type, and the higher the latency between the browser and the server.
Cause
The reconcile effect in
TitleEditortreats the React Query cache as the source of truth and replaces the editor content whenever the cachedtitlediffers from it:saveTitlealready tries to avoid applying a stale response:but that check runs synchronously inside the mutation's
.then(), while the effect only runs after React re-renders. A keystroke landing in that gap is destroyed:setContentalso resets the selection, so the caret jumps to the start and subsequent keystrokes are inserted in the wrong place. That is what turns a single lost character into a visibly mangled title.Higher latency means more save cycles per title, and therefore more of these windows — which is why a busy instance with real network latency hits it constantly while a local dev setup essentially never does.
Fix
Track the title we last pushed to the server, and skip the reconcile when the incoming value is our own echo — in that window the echo is always the stale side. Genuine remote renames and actual page switches still replace the editor content exactly as before.
Reproduction
Deterministic, by holding the
/api/pages/updateresponse and releasing it a fixed offset before the next keystroke (typed, wait for the debounced save to fire, release the response, then typeXafteroffsetms; expected result…dX):ctrl1dX→ctrl1dctrl3dX→ctrl3dctrl4dX→ctrl4d3 of 5 offsets lose the character before the change, 0 of 8 after (the previously failing offsets were re-run twice). The harness was validated by stashing the fix and confirming it still reproduces.
Regressions checked
page.title !== titleEditor.getText()guard.tsc --noEmitclean.Note: I deliberately used "is this our own echo?" rather than "is the editor focused?". A focus check would also block genuine remote renames while the cursor sits idle in the title, with no way to reconcile afterwards, since the effect would not re-run on blur.
🤖 Generated with Claude Code