дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
Root cause: body is user-select: none and each feature had to opt back in; the Markdown preview (and other read-only views) never did, and the app-wide contextmenu guard left no Copy item. Fix: shared selectableText class (src/ui) for read-only content, and a fallback Copy context menu from browserGuards when right-clicking a selection or a selectable area (component menus still win). Siblings covered: Quick Documentation / hover / completion docs, hover errors, Quick Definition, debugger value tooltip, inspection messages, message dialogs (no drag on body text), notification titles, About dialog; existing selectable consoles now get Copy too. Verified in-app on macOS: select in Markdown preview, right-click Copy puts the text on the clipboard; tab context menu unchanged. Claude-Session: https://claude.ai/code/session_01VUYsdXA2nkF5v6zsC93dpn
71 рядки
2.4 KiB
TypeScript
71 рядки
2.4 KiB
TypeScript
/**
|
|
* Makes the webview behave like a desktop app: never the browser context menu
|
|
* (its Reload/AutoFill items would act on the webview), no reload/print/
|
|
* save-page/history shortcuts, no navigation on file drop. Copy/paste/undo
|
|
* shortcuts stay untouched. A right-click no component handled on selected or
|
|
* selectable read-only text gets IDEA's Copy menu instead of nothing.
|
|
*/
|
|
import { copyMenuFor, showContextMenu, useContextMenuStore } from "@/ui";
|
|
|
|
const BLOCKED = new Set([
|
|
"Meta+P",
|
|
"Ctrl+P",
|
|
"Meta+S",
|
|
"Meta+Shift+S",
|
|
"Meta+O",
|
|
"Meta+G",
|
|
"Meta+Shift+G",
|
|
"Meta+[",
|
|
"Meta+]",
|
|
"Meta+Alt+I",
|
|
"Meta+U",
|
|
]);
|
|
const RELOAD = new Set(["Meta+R", "Meta+Shift+R", "Ctrl+R", "Ctrl+Shift+R", "F5", "Shift+F5"]);
|
|
|
|
function keyId(e: KeyboardEvent) {
|
|
const key = e.code.startsWith("Key")
|
|
? e.code.slice(3)
|
|
: e.code === "BracketLeft"
|
|
? "["
|
|
: e.code === "BracketRight"
|
|
? "]"
|
|
: e.key;
|
|
return [e.ctrlKey && "Ctrl", e.altKey && "Alt", e.shiftKey && "Shift", e.metaKey && "Meta", key]
|
|
.filter(Boolean)
|
|
.join("+");
|
|
}
|
|
|
|
export function installBrowserGuards(opts: { blockReload?: boolean } = {}): () => void {
|
|
const blockReload = opts.blockReload ?? import.meta.env.PROD;
|
|
let menuBefore: unknown = null;
|
|
const beforeContextMenu = () => {
|
|
menuBefore = useContextMenuStore.getState().menu;
|
|
};
|
|
const onContextMenu = (e: MouseEvent) => {
|
|
const handled = e.defaultPrevented || useContextMenuStore.getState().menu !== menuBefore;
|
|
e.preventDefault();
|
|
if (handled) return;
|
|
const items = copyMenuFor(e.target);
|
|
if (items) showContextMenu(e, items);
|
|
};
|
|
const onKeyDown = (e: KeyboardEvent) => {
|
|
const id = keyId(e);
|
|
if (BLOCKED.has(id) || (blockReload && RELOAD.has(id))) e.preventDefault();
|
|
};
|
|
const onDrag = (e: DragEvent) => {
|
|
if (!(e.target as Element | null)?.closest?.("[data-drop-target]")) e.preventDefault();
|
|
};
|
|
window.addEventListener("contextmenu", beforeContextMenu, true);
|
|
window.addEventListener("contextmenu", onContextMenu);
|
|
window.addEventListener("keydown", onKeyDown);
|
|
window.addEventListener("dragover", onDrag);
|
|
window.addEventListener("drop", onDrag);
|
|
return () => {
|
|
window.removeEventListener("contextmenu", beforeContextMenu, true);
|
|
window.removeEventListener("contextmenu", onContextMenu);
|
|
window.removeEventListener("keydown", onKeyDown);
|
|
window.removeEventListener("dragover", onDrag);
|
|
window.removeEventListener("drop", onDrag);
|
|
};
|
|
}
|