diff --git a/packages/client/setup/monaco.ts b/packages/client/setup/monaco.ts index a9447f2d49..b407e08147 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, releaseShortcuts } from '../state' window.MonacoEnvironment = { getWorker(_, label) { @@ -97,6 +98,20 @@ const setup = createSingletonPromise(async () => { Object.assign(editorOptions, result?.editorOptions) } + // Disable shortcuts when focusing Monaco editor. + monaco.editor.onDidCreateEditor((editor) => { + let lock: symbol | null = null + editor.onDidFocusEditorWidget(() => { + lock = lockShortcuts() + }) + editor.onDidBlurEditorWidget(() => { + if (lock != null) { + releaseShortcuts(lock) + lock = null + } + }) + }) + // 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..ac0fe74448 100644 --- a/packages/client/state/storage.ts +++ b/packages/client/state/storage.ts @@ -15,7 +15,31 @@ 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 `releaseShortcuts` to modify. + */ +export const shortcutsEnabled = computed(() => mutableShortcutsEnabled.value) + +// 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. +const shortcutsLocks = new Set() +export function lockShortcuts() { + const lock = Symbol('shortcuts lock') + shortcutsLocks.add(lock) + mutableShortcutsEnabled.value = false + return lock +} +export function releaseShortcuts(lock: symbol) { + shortcutsLocks.delete(lock) + if (shortcutsLocks.size === 0) { + mutableShortcutsEnabled.value = true + } +} + export const breakpoints = useBreakpoints({ xs: 460, ...breakpointsTailwind,