From 6a84f51655a5e9d6662b0d6af95a26ebce7d2388 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 11 Jul 2026 22:01:25 -0700 Subject: [PATCH 1/2] fix(global-commands): use isContentEditable for the editable guard --- .../global-commands-provider.test.tsx | 30 +++++++++++++++++-- .../providers/global-commands-provider.tsx | 9 +++--- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.test.tsx b/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.test.tsx index e6bf085f9ff..7ab2e18a525 100644 --- a/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.test.tsx @@ -94,6 +94,15 @@ describe('GlobalCommandsProvider owned-shortcut yielding', () => { }) describe('GlobalCommandsProvider editable guard', () => { + /** + * jsdom does not implement `isContentEditable`, so stub the browser's computed + * editability on the element the test focuses. + */ + function focusWithEditability(element: HTMLElement, isContentEditable: boolean) { + Object.defineProperty(element, 'isContentEditable', { value: isContentEditable }) + element.focus() + } + it('skips a non-editable command when focus is in an input', () => { const handler = vi.fn() mount( @@ -115,9 +124,24 @@ describe('GlobalCommandsProvider editable guard', () => {
) - ;(container.querySelector('[contenteditable]') as HTMLElement).focus() + focusWithEditability(container.querySelector('[contenteditable]') as HTMLElement, true) + pressModK() + expect(handler).not.toHaveBeenCalled() + }) + + it('skips a non-editable command when focus is on a descendant of a contenteditable root', () => { + const handler = vi.fn() + mount( + + +
+ +
+
+ ) + focusWithEditability(container.querySelector('span') as HTMLElement, true) pressModK() - expect(handler).toHaveBeenCalledTimes(0) + expect(handler).not.toHaveBeenCalled() }) it('fires a non-editable command when focus is in a contenteditable="false" element', () => { @@ -129,7 +153,7 @@ describe('GlobalCommandsProvider editable guard', () => {
) - ;(container.querySelector('[contenteditable]') as HTMLElement).focus() + focusWithEditability(container.querySelector('[contenteditable]') as HTMLElement, false) pressModK() expect(handler).toHaveBeenCalledTimes(1) }) diff --git a/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx b/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx index b03ce328735..ac154f11241 100644 --- a/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx +++ b/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.tsx @@ -95,14 +95,13 @@ function shortcutSignature(parsed: ParsedShortcut, isMac: boolean): string { /** * Whether `element` is an editable region for the purposes of the editable guard. - * `contenteditable="false"` (e.g. a rich-text editor in read-only mode) is not editable, - * so commands with `allowInEditable: false` still fire while such an element has focus. + * `isContentEditable` is the browser's computed editability, so focusable descendants of a + * `contenteditable` root count as editable, while `contenteditable="false"` (e.g. a rich-text + * editor in read-only mode) does not — commands with `allowInEditable: false` still fire there. */ function isEditableElement(element: Element | null): boolean { if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) return true - if (!(element instanceof HTMLElement)) return false - const contentEditable = element.getAttribute('contenteditable') - return contentEditable !== null && contentEditable.toLowerCase() !== 'false' + return element instanceof HTMLElement && element.isContentEditable } /** From b722f2d4493955c6dc4920119c24d77d815da9d9 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 11 Jul 2026 22:05:54 -0700 Subject: [PATCH 2/2] chore(lint): keep focusable span in editable-guard test with biome-ignore --- .../[workspaceId]/providers/global-commands-provider.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.test.tsx b/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.test.tsx index 7ab2e18a525..7fdca0d8ed0 100644 --- a/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/providers/global-commands-provider.test.tsx @@ -135,6 +135,7 @@ describe('GlobalCommandsProvider editable guard', () => {
+ {/* biome-ignore lint/a11y/noNoninteractiveTabindex: focusable stand-in for a node view inside the editor */}