From 74ee58c8f41b287b2594cd4e1ea195a0fe8b5f6b Mon Sep 17 00:00:00 2001 From: Quentin Roy Date: Wed, 8 Oct 2025 21:25:25 +0200 Subject: [PATCH 1/3] fix: prevent hotkeys when typing in monaco editor --- packages/client/setup/monaco.ts | 11 +++++++++++ packages/client/state/storage.ts | 25 ++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/client/setup/monaco.ts b/packages/client/setup/monaco.ts index a9447f2d49..f44e894114 100644 --- a/packages/client/setup/monaco.ts +++ b/packages/client/setup/monaco.ts @@ -20,6 +20,7 @@ import { SyncDescriptor } from 'monaco-editor/esm/vs/platform/instantiation/comm import ts from 'typescript' import { watchEffect } from 'vue' import { isDark } from '../logic/dark' +import { lockShortcuts, unlockShortcuts } from '../state' window.MonacoEnvironment = { getWorker(_, label) { @@ -97,6 +98,16 @@ const setup = createSingletonPromise(async () => { Object.assign(editorOptions, result?.editorOptions) } + // Disable shortcuts when focusing Monaco editor. + monaco.editor.onDidCreateEditor((editor) => { + editor.onDidFocusEditorWidget(() => { + lockShortcuts() + }) + editor.onDidBlurEditorWidget(() => { + unlockShortcuts() + }) + }) + // Use Shiki to highlight Monaco shikiToMonaco(highlighter, monaco) if (typeof themes === 'string') { diff --git a/packages/client/state/storage.ts b/packages/client/state/storage.ts index e769e0f823..bd2bcd3e5a 100644 --- a/packages/client/state/storage.ts +++ b/packages/client/state/storage.ts @@ -15,7 +15,30 @@ export const showOverview = ref(false) export const hmrSkipTransition = ref(false) export const disableTransition = ref(false) -export const shortcutsEnabled = ref(true) +const mutableShortcutsEnabled = ref(true) +/** + * Whether the keyboard shortcuts are enabled. Readonly, + * use `lockShortcuts` and `unlockShortcuts` to modify. + */ +export const shortcutsEnabled = computed(() => mutableShortcutsEnabled.value) + +// Use a lock counter to support multiple simultaneous locks +// and avoid race conditions. Race conditions may occur, for example, +// when locking shortcuts on editor focus and moving from one editor +// to another, as blur events can be triggered after focus. +let shortcutsLockCounter = 0 +export function lockShortcuts() { + shortcutsLockCounter++ + mutableShortcutsEnabled.value = false +} +export function unlockShortcuts() { + shortcutsLockCounter-- + if (shortcutsLockCounter <= 0) { + shortcutsLockCounter = 0 + mutableShortcutsEnabled.value = true + } +} + export const breakpoints = useBreakpoints({ xs: 460, ...breakpointsTailwind, From 421fb572b9f137b2ac69c9077280817ad7caa49a Mon Sep 17 00:00:00 2001 From: Quentin Roy Date: Fri, 10 Oct 2025 11:17:07 +0200 Subject: [PATCH 2/3] isolate shortcut lock requests --- packages/client/setup/monaco.ts | 9 ++++++--- packages/client/state/storage.ts | 17 +++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/client/setup/monaco.ts b/packages/client/setup/monaco.ts index f44e894114..77f04c7b8b 100644 --- a/packages/client/setup/monaco.ts +++ b/packages/client/setup/monaco.ts @@ -20,7 +20,7 @@ import { SyncDescriptor } from 'monaco-editor/esm/vs/platform/instantiation/comm import ts from 'typescript' import { watchEffect } from 'vue' import { isDark } from '../logic/dark' -import { lockShortcuts, unlockShortcuts } from '../state' +import { lockShortcuts, releaseShortcuts } from '../state' window.MonacoEnvironment = { getWorker(_, label) { @@ -100,11 +100,14 @@ const setup = createSingletonPromise(async () => { // Disable shortcuts when focusing Monaco editor. monaco.editor.onDidCreateEditor((editor) => { + let lock: symbol | null = null editor.onDidFocusEditorWidget(() => { - lockShortcuts() + lock = lockShortcuts() }) editor.onDidBlurEditorWidget(() => { - unlockShortcuts() + if (lock != null) { + releaseShortcuts(lock) + } }) }) diff --git a/packages/client/state/storage.ts b/packages/client/state/storage.ts index bd2bcd3e5a..ac0fe74448 100644 --- a/packages/client/state/storage.ts +++ b/packages/client/state/storage.ts @@ -18,23 +18,24 @@ export const disableTransition = ref(false) const mutableShortcutsEnabled = ref(true) /** * Whether the keyboard shortcuts are enabled. Readonly, - * use `lockShortcuts` and `unlockShortcuts` to modify. + * use `lockShortcuts` and `releaseShortcuts` to modify. */ export const shortcutsEnabled = computed(() => mutableShortcutsEnabled.value) -// Use a lock counter to support multiple simultaneous locks +// Use a locking mechanism to support multiple simultaneous locks // and avoid race conditions. Race conditions may occur, for example, // when locking shortcuts on editor focus and moving from one editor // to another, as blur events can be triggered after focus. -let shortcutsLockCounter = 0 +const shortcutsLocks = new Set() export function lockShortcuts() { - shortcutsLockCounter++ + const lock = Symbol('shortcuts lock') + shortcutsLocks.add(lock) mutableShortcutsEnabled.value = false + return lock } -export function unlockShortcuts() { - shortcutsLockCounter-- - if (shortcutsLockCounter <= 0) { - shortcutsLockCounter = 0 +export function releaseShortcuts(lock: symbol) { + shortcutsLocks.delete(lock) + if (shortcutsLocks.size === 0) { mutableShortcutsEnabled.value = true } } From 06bf13bb634582def87c3561b767bfd35dc6b145 Mon Sep 17 00:00:00 2001 From: Quentin Roy Date: Fri, 10 Oct 2025 11:23:26 +0200 Subject: [PATCH 3/3] sanity fix: clear lock once released --- packages/client/setup/monaco.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/setup/monaco.ts b/packages/client/setup/monaco.ts index 77f04c7b8b..b407e08147 100644 --- a/packages/client/setup/monaco.ts +++ b/packages/client/setup/monaco.ts @@ -107,6 +107,7 @@ const setup = createSingletonPromise(async () => { editor.onDidBlurEditorWidget(() => { if (lock != null) { releaseShortcuts(lock) + lock = null } }) })