fix: fix cursor jumping and IME focus loss#176
Conversation
…nt in template editor
…ing of empty text adjacent to templates
WalkthroughThe changes focus on improving the TemplateEditor component's data consistency, selection management, and cursor handling. Type definitions for user-facing text and template items now allow optional IDs. The logic for detecting empty templates is refined, and the EditorRange interface is extended with DOM element references. Several internal functions are reworked or added for robust placeholder and template handling. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant TemplateEditor
participant Model
participant History
User->>TemplateEditor: Update model (external change)
TemplateEditor->>TemplateEditor: Save selection range
TemplateEditor->>TemplateEditor: Update internal data
TemplateEditor->>History: Commit history
TemplateEditor->>Model: Update model
User->>TemplateEditor: Input event (typing, deletion)
TemplateEditor->>TemplateEditor: Update selection range map
TemplateEditor->>TemplateEditor: Process input (cleanup, placeholders)
TemplateEditor->>History: Commit history
TemplateEditor->>Model: Update model
User->>TemplateEditor: Selection change
TemplateEditor->>TemplateEditor: Adjust caret if on placeholder
Estimated code review effort🎯 4 (Complex) | ⏱️ ~35 minutes Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/components/src/sender/components/TemplateEditor.vue (1)
194-220: Good internal/external update detection logic.The serialization comparison effectively prevents redundant processing and history commits. The selection range preservation ensures proper cursor restoration.
Regarding the TODO comment on line 200: Consider using a flag or metadata to explicitly track internal updates instead of relying on data comparison, which could be more performant and explicit.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/components/src/sender/components/TemplateEditor.vue(18 hunks)packages/components/src/sender/index.type.ts(1 hunks)packages/components/src/sender/index.vue(1 hunks)packages/components/src/sender/types/editor.type.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/components/src/sender/index.vue (1)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
🧬 Code Graph Analysis (1)
packages/components/src/sender/index.type.ts (1)
packages/components/src/sender/types/editor.type.ts (2)
TextItem(7-9)TemplateItem(11-15)
🔇 Additional comments (10)
packages/components/src/sender/index.type.ts (1)
127-129: LGTM! Good type design for optional ID handling.The change to make
idoptional in user-facing types while keeping it required internally is a solid pattern. This allows the system to generate IDs when not provided by users while ensuring internal data consistency.packages/components/src/sender/types/editor.type.ts (1)
29-36: LGTM! Useful addition for DOM element tracking.Adding
startElandendElto theEditorRangeinterface provides direct DOM element references, which improves performance and simplifies cursor positioning logic - directly supporting the PR's cursor behavior fixes.packages/components/src/sender/index.vue (1)
278-281: LGTM! More robust template emptiness check.The enhanced logic correctly identifies templates as empty when all nodes are zero-width space placeholders, not just when there's exactly one. This aligns well with the improved placeholder management in the TemplateEditor component.
packages/components/src/sender/components/TemplateEditor.vue (7)
2-2: LGTM! Clean setup and constants definition.Good simplification of the zero-width character constants and appropriate lifecycle hook imports for the new selection change listener.
Also applies to: 38-42
56-72: LGTM! Proper ID handling in data transformations.The transformations now correctly preserve existing IDs or generate new ones, ensuring consistent identity tracking between user and internal data representations. This is essential for maintaining proper references during editing operations.
76-107: LGTM! Excellent placeholder management logic.The rewritten
setOriginalDataeffectively prevents templates from appearing at start/end positions by adding placeholder text nodes. The cleanup of placeholders from nodes with actual content prevents accumulation. This directly addresses the cursor positioning issues mentioned in the PR objectives.
449-542: LGTM! Comprehensive input processing improvements.The refactored
processInputfunction properly handles:
- Template deletion tracking
- Empty item cleanup
- Placeholder management to prevent templates at start/end
- Prefix/suffix restoration
This addresses the data synchronization issues mentioned in the PR objectives for undo/redo operations.
763-784: LGTM! Proper IME composition handling.Moving the range map updates before processing ensures the selection state is captured before DOM modifications. This directly addresses the IME focus loss issues mentioned in the PR objectives, particularly the issue where "Loss of editor focus after confirming a Chinese character selection by pressing the space key."
875-922: Excellent cursor position adjustment logic!The
handleSelectionChangelistener effectively prevents the cursor from getting stuck at zero-width placeholder characters by automatically adjusting the position. This directly addresses the "cursor jumping" issues mentioned in the PR title, particularly "Incorrect cursor positioning when typing English characters on the right side of the editing block."The implementation properly:
- Checks for composition to avoid conflicts with IME
- Handles both start and end placeholder positions
- Uses the DOM element references from the enhanced EditorRange
1-14: Good architectural simplification.The removal of the
handleSentinelNodeForwardDeletionfunction (mentioned in the summary) and replacement with the unified placeholder management and selection change handling represents a cleaner, more maintainable approach to cursor management.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/components/src/sender/components/TemplateEditor.vue (2)
76-107: Improved placeholder handling with consistent approach.The rewritten
setOriginalDatafunction provides a more systematic approach to handling placeholders, replacing browser-specific logic with consistent behavior. The logic correctly ensures templates don't appear at start/end positions.Minor optimization suggestion:
Consider caching the regex outside the function to avoid recreating it on each call:
+const PLACEHOLDER_REGEX = new RegExp(PLACEHOLDER, 'g') + const setOriginalData = (items: (TextItem | TemplateItem)[]) => { // ... - const regex = new RegExp(PLACEHOLDER, 'g') if (items.length > 0) { if (items[0].content !== PLACEHOLDER) { - items[0].content = items[0].content.replace(regex, '') + items[0].content = items[0].content.replace(PLACEHOLDER_REGEX, '') } if (items[items.length - 1].content !== PLACEHOLDER) { - items[items.length - 1].content = items[items.length - 1].content.replace(regex, '') + items[items.length - 1].content = items[items.length - 1].content.replace(PLACEHOLDER_REGEX, '') } } }
194-220: Smart detection of internal vs external updates.The enhanced watch function correctly prevents infinite loops by detecting internal updates through data comparison. The selection range preservation before processing external updates is crucial for maintaining cursor position.
Consider a more robust way to detect internal updates that doesn't rely on JSON serialization order:
// Alternative approach using a flag const isInternalUpdate = ref(false) // Set flag before internal updates const updateModelValue = () => { isInternalUpdate.value = true model.value = transformInternalToUser(originalData.value) nextTick(() => { isInternalUpdate.value = false }) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/components/src/sender/components/TemplateEditor.vue(17 hunks)
🔇 Additional comments (14)
packages/components/src/sender/components/TemplateEditor.vue (14)
2-2: LGTM! Import additions support new functionality.The addition of
onMountedandonUnmountedimports and theBlockimport are appropriate for the new selection change handling and component structure.Also applies to: 14-14
38-41: Good consolidation of zero-width character constants.Unifying the handling with
ZERO_WIDTH_CHARand creating appropriate aliases improves maintainability and reduces the chance of inconsistencies.
57-62: Proper handling of optional IDs in data transformation.The fallback to
randomId()whenitem.idis not provided ensures consistent ID handling between user data and internal representations.
71-71: Correct inclusion of ID in user data transformation.Including the
idproperty in the transformed user data maintains consistency with the updated type definitions.
433-434: Enhanced range tracking with DOM element references.Adding
startElandendElproperties to the range transformation enables more precise DOM element tracking, which is essential for the improved selection management functionality.Also applies to: 438-439
403-415: Proper range mapping before input processing.Adding range mapping before processing input ensures that undo/redo operations can restore the correct cursor position, addressing the data synchronization issues mentioned in the PR objectives.
455-541: Comprehensive input processing with cleanup logic.The enhanced
processInputfunction properly handles:
- Template deletion tracking
- Empty template and text item cleanup
- Prevention of templates at start/end positions
- Prefix/suffix restoration
- History commits and model synchronization
This addresses the cursor jumping issues mentioned in the PR objectives.
767-772: Consistent range mapping in composition handling.The addition of range mapping in
handleCompositionEndensures consistent behavior with other input handling functions, addressing IME focus loss issues.
320-321: Proper history management and model synchronization.Adding history commits and model.value updates in
insertNewTextAndSetCaretPositionensures that undo/redo operations work correctly and the external model stays synchronized with internal state.Also applies to: 326-327, 336-336
875-913: Essential cursor position adjustment for placeholder handling.The
handleSelectionChangefunction directly addresses the cursor jumping issues mentioned in the PR objectives by:
- Detecting cursor position at placeholder text boundaries
- Adjusting cursor position to prevent being stuck at zero-width characters
- Properly avoiding interference with IME composition input
This is a key fix for the cursor behavior problems.
915-921: Proper event listener lifecycle management.The
onMountedandonUnmountedhooks correctly register and clean up theselectionchangeevent listener, following Vue.js best practices for DOM event management.
273-274: Improved comment clarity.The updated comment better explains the fallback behavior when the first child is not a text node.
847-848: Essential model synchronization after data restoration.Adding
model.valueupdate inrestoreDataAndCaretPositionensures the external model stays synchronized after undo/redo operations, addressing the data synchronization issues mentioned in the PR objectives.
856-867: LGTM! Template activation logic maintained.The
activateFirstFieldfunction maintains its core logic while benefiting from improved formatting and structure.
修复输入块光标问题
场景:编辑块右侧无内容时
【已处理】1. 右侧输入英文时,光标位置不正确
【已处理】2. 右侧输入中文时,按下空格按键确认选择后,编辑器失焦
【已处理】3. 右侧输入中文,
中文输入法下,输入第二个字符时,光标移动至前一个编辑块,空格确认时,文字出现在前面的编辑块中;
另外修复了undo redo操作后数据未同步的问题
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Documentation