diff --git a/services/web/frontend/js/features/source-editor/extensions/context-menu.ts b/services/web/frontend/js/features/source-editor/extensions/context-menu.ts index ef12628609..4dbf626f96 100644 --- a/services/web/frontend/js/features/source-editor/extensions/context-menu.ts +++ b/services/web/frontend/js/features/source-editor/extensions/context-menu.ts @@ -4,6 +4,7 @@ import { Tooltip, TooltipView, keymap, + ViewPlugin, } from '@codemirror/view' import { Extension, @@ -230,7 +231,7 @@ function isClickOnGutter(target: HTMLElement): boolean { // Gutter context menu plugin const gutterContextMenuPlugin = (): Extension => EditorView.updateListener.of(update => { - if (!update.view.dom.parentElement) { + if (isTouchOnlyInput || !update.view.dom.parentElement) { return } @@ -243,9 +244,7 @@ const gutterContextMenuPlugin = (): Extension => gutters.setAttribute('data-context-menu-attached', 'true') gutters.addEventListener('contextmenu', (event: Event) => { const mouseEvent = event as MouseEvent - if (isTouchOnlyInput) { - return - } + const pos = update.view.posAtCoords({ x: mouseEvent.clientX, y: mouseEvent.clientY, @@ -269,6 +268,48 @@ const gutterContextMenuPlugin = (): Extension => }) }) +// Handle right-click on ol-cm-filler (empty line widget) +// domEventHandlers doesn't fire for contenteditable="false" elements, so we use a direct DOM listener +const emptyLineFillerContextMenuPlugin = (): Extension => { + if (isTouchOnlyInput) { + return [] + } + + return ViewPlugin.define(view => { + const contentDOM = view.contentDOM + + const handleContextMenu = (event: Event) => { + const mouseEvent = event as MouseEvent + const target = mouseEvent.target as HTMLElement + + // Only handle ol-cm-filler elements + if (!target.classList.contains('ol-cm-filler')) { + return + } + + event.preventDefault() + event.stopPropagation() + + // Re-dispatch on contentDOM so CodeMirror's domEventHandlers picks it up + const customEvent = new MouseEvent('contextmenu', { + bubbles: true, + cancelable: true, + clientX: mouseEvent.clientX, + clientY: mouseEvent.clientY, + }) + contentDOM.dispatchEvent(customEvent) + } + + contentDOM.addEventListener('contextmenu', handleContextMenu) + + return { + destroy() { + contentDOM.removeEventListener('contextmenu', handleContextMenu) + }, + } + }) +} + // Editor view context menu handlers const editorContextMenuHandlers = (): Extension => EditorView.domEventHandlers({ @@ -353,6 +394,7 @@ export const contextMenu = (enabled: boolean): Extension => contextMenuContainerTheme, contextMenuStateField, gutterContextMenuPlugin(), + emptyLineFillerContextMenuPlugin(), editorContextMenuHandlers(), contextMenuKeymap(), ]