дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
fix(editor): Ctrl/⌘-hover info replaces hover popups instead of stacking (#60)
Root cause: the quick-doc/problem hover and the Ctrl/⌘-hover navigation info were independent tooltips with no precedence, and the navigation modifier was only seen through the focused editor's key events, so holding Ctrl stacked both popups. Fix: a window-wide platform navigation modifier (`src/platform/navigationModifier.ts`, ⌘ on macOS / Ctrl elsewhere, unchorded); with it held the plain hover doesn't open, late LSP results are dropped, an open hover closes (unless the pointer is inside it, so Ctrl/⌘+C still copies), release clears the link and info. Siblings: problem/diagnostic hover, debugger value tooltip, inlay hint hovers (no doc or underline through hints, platform-correct Ctrl/⌘-click), consistent ⌘ vs ⌃ handling on macOS. Verified in the app on macOS (⌘): hover then ⌘ leaves only the nav info, ⌘ before hover shows only nav info, release clears both and plain hover returns. Not verified on Windows. Fixes #57 https://claude.ai/code/session_01VUYsdXA2nkF5v6zsC93dpn
Цей коміт міститься в:
1 джерело
7146779f6c
коміт
e16253a890
7 files changed
+307
-56
No files matched your search
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* The navigation modifier (⌘ on macOS, Ctrl elsewhere) as a live, window-wide
|
||||
* state: ⌘/Ctrl-hover shows the navigation link and its brief info, and every
|
||||
* plain hover popup (quick documentation, problems, debugger values) yields to
|
||||
* it. Tracked on the window so an editor sees the key even when it isn't focused.
|
||||
*/
|
||||
import { hasPrimaryModifier, isMacHost } from "./keymap";
|
||||
|
||||
type ModifierEvent = Pick<MouseEvent, "metaKey" | "ctrlKey" | "altKey" | "shiftKey">;
|
||||
|
||||
/** ⌘/Ctrl alone: no ⌥/⇧ (other gestures) and not the other platform's Ctrl/⌘. */
|
||||
export function isNavigationGesture(e: ModifierEvent): boolean {
|
||||
return hasPrimaryModifier(e) && !e.altKey && !e.shiftKey && !(isMacHost() ? e.ctrlKey : e.metaKey);
|
||||
}
|
||||
|
||||
let held = false;
|
||||
let pointer: Element | null = null;
|
||||
const listeners = new Set<(held: boolean) => void>();
|
||||
let installed: (() => void) | null = null;
|
||||
|
||||
function setHeld(value: boolean) {
|
||||
if (held === value) return;
|
||||
held = value;
|
||||
for (const fn of [...listeners]) fn(value);
|
||||
}
|
||||
|
||||
function install() {
|
||||
if (installed || typeof window === "undefined") return;
|
||||
const onKey = (e: KeyboardEvent) => setHeld(isNavigationGesture(e));
|
||||
// Also from pointer events: a key released outside the window never reports its keyup.
|
||||
const onMouse = (e: MouseEvent) => {
|
||||
pointer = e.target instanceof Element ? e.target : null;
|
||||
setHeld(isNavigationGesture(e));
|
||||
};
|
||||
const onBlur = () => setHeld(false);
|
||||
const opts = { capture: true, passive: true } as const;
|
||||
window.addEventListener("keydown", onKey, opts);
|
||||
window.addEventListener("keyup", onKey, opts);
|
||||
window.addEventListener("mousemove", onMouse, opts);
|
||||
window.addEventListener("mousedown", onMouse, opts);
|
||||
window.addEventListener("blur", onBlur);
|
||||
installed = () => {
|
||||
window.removeEventListener("keydown", onKey, opts);
|
||||
window.removeEventListener("keyup", onKey, opts);
|
||||
window.removeEventListener("mousemove", onMouse, opts);
|
||||
window.removeEventListener("mousedown", onMouse, opts);
|
||||
window.removeEventListener("blur", onBlur);
|
||||
installed = null;
|
||||
};
|
||||
}
|
||||
|
||||
/** Whether ⌘ (macOS) / Ctrl is held on its own right now. */
|
||||
export function navigationModifierHeld(): boolean {
|
||||
install();
|
||||
return held;
|
||||
}
|
||||
|
||||
/** Element under the pointer at the last mouse move (null before any). */
|
||||
export function pointerTarget(): Element | null {
|
||||
install();
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/** Called with the new state whenever the navigation modifier is pressed or released. */
|
||||
export function onNavigationModifier(fn: (held: boolean) => void): () => void {
|
||||
install();
|
||||
listeners.add(fn);
|
||||
return () => listeners.delete(fn);
|
||||
}
|
||||
|
||||
/** Tests: forget the tracked state and listeners. */
|
||||
export function resetNavigationModifier() {
|
||||
installed?.();
|
||||
held = false;
|
||||
pointer = null;
|
||||
listeners.clear();
|
||||
}
|
||||
Reference in new issue
Заблокувати користувача