From 8d4828c2cddb42b8ebdb0a367328ce6308dd1876 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:38:00 -0700 Subject: [PATCH] fix: use Radix context menu for line number gutter right-click Replaces Monaco's built-in context menu with a Radix DropdownMenu for the line number gutter, matching the VSCode approach of intercepting the gutter right-click and showing a custom menu. Shows the clicked line number in the menu items. Co-Authored-By: Claude Opus 4.6 --- .../src/components/editor/MonacoEditor.tsx | 147 +++++++++++------- 1 file changed, 93 insertions(+), 54 deletions(-) diff --git a/src/renderer/src/components/editor/MonacoEditor.tsx b/src/renderer/src/components/editor/MonacoEditor.tsx index bad317fd2bf..82925f4e0e6 100644 --- a/src/renderer/src/components/editor/MonacoEditor.tsx +++ b/src/renderer/src/components/editor/MonacoEditor.tsx @@ -1,6 +1,14 @@ -import React, { useRef, useCallback, useEffect } from 'react' +import React, { useRef, useCallback, useEffect, useState } from 'react' import Editor, { type OnMount } from '@monaco-editor/react' import type { editor } from 'monaco-editor' +import { Copy, MapPin } from 'lucide-react' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger +} from '@/components/ui/dropdown-menu' import { useAppStore } from '@/store' import '@/lib/monaco-setup' @@ -31,6 +39,11 @@ export default function MonacoEditor({ const settings = useAppStore((s) => s.settings) const setPendingEditorReveal = useAppStore((s) => s.setPendingEditorReveal) const setEditorCursorLine = useAppStore((s) => s.setEditorCursorLine) + + // Gutter context menu state + const [gutterMenuOpen, setGutterMenuOpen] = useState(false) + const [gutterMenuPoint, setGutterMenuPoint] = useState({ x: 0, y: 0 }) + const [gutterMenuLine, setGutterMenuLine] = useState(1) const isDark = settings?.theme === 'dark' || (settings?.theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches) @@ -54,7 +67,8 @@ export default function MonacoEditor({ setEditorCursorLine(filePath, e.position.lineNumber) }) - // Show context menu on line number gutter right-click + // Intercept right-click on line number gutter to show Radix context menu + // (same approach as VSCode: custom menu instead of Monaco's built-in one) editorInstance.onMouseDown((e) => { if ( e.event.rightButton && @@ -62,35 +76,11 @@ export default function MonacoEditor({ ) { e.event.preventDefault() e.event.stopPropagation() - // Move cursor to the clicked line so actions use the right line number - const line = e.target.position?.lineNumber - if (line) { - editorInstance.setPosition({ lineNumber: line, column: 1 }) - } - // Trigger Monaco's context menu programmatically - editorInstance.trigger('gutter', 'editor.action.showContextMenu', null) - } - }) - - // Add "Copy Path to Line" actions to Monaco's right-click context menu - editorInstance.addAction({ - id: 'orca.copyPathToLine', - label: 'Copy Path to Line', - contextMenuGroupId: '9_cutcopypaste', - contextMenuOrder: 100, - run: (ed) => { - const line = ed.getPosition()?.lineNumber ?? 1 - navigator.clipboard.writeText(`${filePath}#L${line}`) - } - }) - editorInstance.addAction({ - id: 'orca.copyRelativePathToLine', - label: 'Copy Relative Path to Line', - contextMenuGroupId: '9_cutcopypaste', - contextMenuOrder: 101, - run: (ed) => { - const line = ed.getPosition()?.lineNumber ?? 1 - navigator.clipboard.writeText(`${relativePath}#L${line}`) + const line = e.target.position?.lineNumber ?? 1 + editorInstance.setPosition({ lineNumber: line, column: 1 }) + setGutterMenuLine(line) + setGutterMenuPoint({ x: e.event.posx, y: e.event.posy }) + setGutterMenuOpen(true) } }) @@ -160,29 +150,78 @@ export default function MonacoEditor({ }, [revealLine, revealColumn, revealMatchLength, setPendingEditorReveal]) return ( - + <> + + + {/* Radix context menu for line number gutter right-click */} + + +