fix: dispatch grapheme delete on live view in web composer
The Backspace handler computed positions from the `view` it was handed (`view.state.selection`, `view.state.doc`) but executed the delete against the closed-over React `editor`. When the editor is recreated (e.g. theme toggle resets the instance and doc), `editor.state.doc` is briefly a smaller, mismatched document, so a position valid in `view` overflows it - ProseMirror throws "Position N out of range" (APP-SBSP: ~87k events). Dispatch on the `view` the handler received so positions and document come from the same state, and clamp to docSize as a backstop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -231,10 +231,19 @@ export function TextInput({
|
||||
// otherwise, delete the last grapheme using deleteRange,
|
||||
// so that emojis are deleted as a whole
|
||||
const deleteFrom = cursorPosition - lastGrapheme.length
|
||||
editor?.commands.deleteRange({
|
||||
from: deleteFrom,
|
||||
to: cursorPosition,
|
||||
})
|
||||
// Resolve positions against `view`, not the closed-over
|
||||
// `editor`. The `editor` ref can briefly point at a stale
|
||||
// instance (e.g. after the editor is recreated on a theme
|
||||
// change or content reset) whose document is out of sync
|
||||
// with the live `view`, causing deleteRange to throw
|
||||
// "Position X out of range". Clamp to the live doc as a
|
||||
// final guard. See APP-SBSP -sfn
|
||||
const docSize = view.state.doc.content.size
|
||||
const from = Math.max(0, Math.min(deleteFrom, docSize))
|
||||
const to = Math.max(from, Math.min(cursorPosition, docSize))
|
||||
if (from < to) {
|
||||
view.dispatch(view.state.tr.delete(from, to))
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user