diff --git a/frontend/src/main.ts b/frontend/src/main.ts index a13014f..f4d5924 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -6,6 +6,9 @@ import i18n from './i18n'; import router from './router'; import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; import { registerDirectives } from './directives'; +import {EditorView} from "@codemirror/view"; + +(EditorView as any).EDIT_CONTEXT = false; const pinia = createPinia(); pinia.use(piniaPluginPersistedstate); diff --git a/frontend/src/views/editor/basic/cursorPositionExtension.ts b/frontend/src/views/editor/basic/cursorPositionExtension.ts index 4423e3b..084ad07 100644 --- a/frontend/src/views/editor/basic/cursorPositionExtension.ts +++ b/frontend/src/views/editor/basic/cursorPositionExtension.ts @@ -15,7 +15,7 @@ export function createCursorPositionExtension(documentId: number) { constructor(private view: EditorView) { const {debouncedFn, flush} = createDebounce( () => this.saveCursorPosition(), - {delay: 400} + {delay: 1000} ); this.debouncedSave = {fn: debouncedFn, flush}; diff --git a/frontend/src/views/editor/extensions/codeblock/decorations.ts b/frontend/src/views/editor/extensions/codeblock/decorations.ts index 174f2c1..7f32ef7 100644 --- a/frontend/src/views/editor/extensions/codeblock/decorations.ts +++ b/frontend/src/views/editor/extensions/codeblock/decorations.ts @@ -7,6 +7,14 @@ import { StateField, RangeSetBuilder, EditorState, Transaction } from "@codemirr import { blockState } from "./state"; import { codeBlockEvent, USER_EVENTS } from "./annotation"; +/** + * IME 输入状态追踪 + * + * 注意:CodeMirror 有 view.composing 和 view.compositionStarted 内置状态, + * 但 transactionFilter 中访问不到 view,所以需要用全局变量追踪 + */ +let isComposing = false; + /** * 块开始装饰组件 */ @@ -223,8 +231,16 @@ const preventFirstBlockFromBeingDeleted = EditorState.changeFilter.of((tr: any) /** * 防止选择在第一个块之前 * 使用 transactionFilter 来确保选择不会在第一个块之前 + * + * IME:在输入法组合输入期间(compositionstart ~ compositionend), + * 暂时禁用选区矫正,避免与 IME 选区管理冲突导致光标跳转 */ const preventSelectionBeforeFirstBlock = EditorState.transactionFilter.of((tr: any) => { + // IME 组合输入期间不矫正选区 + if (isComposing) { + return tr; + } + if (tr.annotation(codeBlockEvent)) { return tr; } @@ -256,6 +272,34 @@ const preventSelectionBeforeFirstBlock = EditorState.transactionFilter.of((tr: a return tr; }); +/** + * IME 输入监听器 + * 监听 composition 事件更新 isComposing 状态,供 transactionFilter 使用 + */ +const imeListener = ViewPlugin.fromClass( + class { + constructor(view: EditorView) { + view.dom.addEventListener('compositionstart', this.handleCompositionStart); + view.dom.addEventListener('compositionend', this.handleCompositionEnd); + } + + handleCompositionStart = () => { + isComposing = true; + }; + + handleCompositionEnd = () => { + // 延迟重置状态,确保所有相关事务都已处理完毕 + setTimeout(() => { + isComposing = false; + }, 0); + }; + + destroy() { + + } + } +); + /** * 获取块装饰扩展 - 简化选项 */ @@ -271,6 +315,7 @@ export function getBlockDecorationExtensions(options: { atomicNoteBlock, preventFirstBlockFromBeingDeleted, preventSelectionBeforeFirstBlock, + imeListener, // IME 状态监听器 ]; if (showBackground) {