fix(editor): resync WebKit caret after paste

This commit is contained in:
Melody Ruch authored and GitHub committed 2026-08-15 23:29:38 +08:00
1 parent 2f2303e1d6
commit 030627cc10
3 files changed
+79

No files matched your search

@@ -116,6 +116,7 @@ import { createDbxCodeMirrorSqlDialect, type CodeMirrorSqlDialectName } from "@/
import { sqlSemanticTableNameSpansForSyntaxTree } from "@/lib/editor/codemirrorSqlSemanticHighlight";
import { startsQueryEditorRectangularSelection, usesQueryEditorObjectNavigationModifier } from "@/lib/editor/queryEditorPointerSelection";
import { LARGE_PASTE_HISTORY_USER_EVENT, normalizeQueryEditorPasteText, recoverableNativePasteSuffix, shouldRecoverLargeTauriPaste } from "@/lib/editor/queryEditorLargePaste";
import { computePasteCaretResyncTarget } from "@/lib/editor/queryEditorPasteCaretResync";
import { extendQueryEditorSelection, runQueryEditorAltExtendSelection } from "@/lib/editor/queryEditorExtendSelection";
import type { StatementExecutionMarker } from "@/lib/tabs/tabPresentation";
import { isSchemaAware, isSingleDatabase, supportsDatabaseNameCompletion, supportsDatabaseSchemaQualifier, supportsSqlInListPaste } from "@/lib/database/databaseFeatureSupport";
@@ -1487,6 +1488,21 @@ async function pasteClipboardAsSqlInCondition(): Promise<boolean> {
return true;
}
// See queryEditorPasteCaretResync.ts for why this nudge is needed (WebKit-only caret bug).
function resyncCaretAfterPaste(view: EditorViewType) {
const EditorSelection = codeMirrorEditorSelection;
if (!EditorSelection) return;
const selection = view.state.selection;
const pos = selection.main.head;
const nudged = computePasteCaretResyncTarget(selection, view.state.doc.length);
if (nudged === null) return;
requestAnimationFrame(() => {
if (!view.dom.isConnected || view.state.selection.ranges.length !== 1 || view.state.selection.main.head !== pos || !view.state.selection.main.empty) return;
view.dispatch({ selection: EditorSelection.cursor(nudged) });
view.dispatch({ selection: EditorSelection.cursor(pos) });
});
}
function recoverLargeTauriPaste(event: ClipboardEvent, currentView: EditorViewType): boolean {
const eventText = event.clipboardData?.getData("text/plain") ?? "";
if (props.readOnly || currentView.state.selection.ranges.length !== 1 || !shouldRecoverLargeTauriPaste(eventText, isTauriRuntime())) return false;
@@ -4929,6 +4945,9 @@ onMounted(async () => {
scheduleSqlCompletionStart(update.view);
}
}
if (update.transactions.some((tr) => tr.isUserEvent("input.paste"))) {
resyncCaretAfterPaste(update.view);
}
}
if (update.selectionSet || update.docChanged) {
syncContextMenuState(update.view);