Monday, December 22, 2025
1 change · saas-18.2
Code cleanup and technical improvements
The HTML editor now handles nested editing actions more reliably when preserving the cursor position. This reduces the risk of cursor jumps or failed text selection restoration during formatting changes, improving the editing experience without changing visible features.
Original PR description
Summary: Refactor `preserveSelection()` to use a stack-based approach (`preservedCursors` array) instead of a single cursor reference. This allows nested calls to `preserveSelection()` to operate…
Summary:
Refactor `preserveSelection()` to use a stack-based approach (`preservedCursors` array) instead of a single cursor reference. This allows nested calls to `preserveSelection()` to operate independently while keeping cursor updates synchronized across active contexts.
Problem:
Using a single stored cursor caused issues in nested calls to `preserveSelection()`:
1. **State overwrite:** Inner calls could overwrite or clear the outer cursor.
2. **Stale references:** If an inner function replaced a DOM node, the outer cursor could still point to a removed node and fail on restore.
Solution:
Use an array of cursor subscribers
- **Shared updates:** When calling `remapNode` on a cursor, it iterates over all active subscribers in the stack. This ensures node replacements performed in inner contexts also update outer cursor references.
- **Scoped cleanup:** `restore()` now removes only the corresponding cursor instance from the stack, ensuring proper lifecycle management.
Example:
The key improvement is that outer scopes receive updates performed by inner scopes.
```javascript
// Function A (outer)
function wrapperFunction() {
const cursor = this.preserveSelection();
replaceTextWithSpan();
cursor.restore();
}
// Function B (inner)
function replaceTextWithSpan() {
const innerCursor = this.preserveSelection();
const oldNode = document.querySelector('text');
const newNode = document.createElement('span');
oldNode.replaceWith(newNode);
innerCursor.remapNode(oldNode, newNode);
innerCursor.restore();
}
```
opw-5386862
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#238989