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..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
@@ -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,25 @@ 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(
+
+
+
+ {/* biome-ignore lint/a11y/noNoninteractiveTabindex: focusable stand-in for a node view inside the editor */}
+
+
+
+ )
+ 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 +154,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
}
/**