Skip to content

Commit 51f1787

Browse files
committed
refactor(desktop): split LinkHoverTooltip hook/component, tighten modifier listener
- Move useLinkHoverState into its own hooks/ folder (was exported alongside the component in violation of the one-component-per-file rule). - Effect now re-subscribes on hover start/end only (deps: hovering boolean), not on every modifier change. - Filter to Meta/Control/Shift/Alt key events so typing a letter while hovering doesn't churn state. - Skip setState when modifier/shift values didn't actually change, avoiding identity-change re-renders on repeat keydowns. - Extract tooltip offset constant.
1 parent f19b455 commit 51f1787

File tree

5 files changed

+64
-55
lines changed

5 files changed

+64
-55
lines changed

apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,8 @@ import { ScrollToBottomButton } from "renderer/screens/main/components/Workspace
2525
import { TerminalSearch } from "renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/TerminalSearch";
2626
import { useTheme } from "renderer/stores/theme";
2727
import { resolveTerminalThemeType } from "renderer/stores/theme/utils";
28-
import {
29-
LinkHoverTooltip,
30-
useLinkHoverState,
31-
} from "./components/LinkHoverTooltip";
28+
import { LinkHoverTooltip } from "./components/LinkHoverTooltip";
29+
import { useLinkHoverState } from "./hooks/useLinkHoverState";
3230
import { useTerminalAppearance } from "./hooks/useTerminalAppearance";
3331

3432
interface TerminalPaneProps {
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import type { ExternalApp } from "@superset/local-db";
2-
import { useCallback, useEffect, useState } from "react";
2+
import { useEffect, useState } from "react";
33
import { createPortal } from "react-dom";
44
import { getAppOption } from "renderer/components/OpenInExternalDropdown/constants";
55
import type { LinkHoverInfo } from "renderer/lib/terminal/terminal-runtime-registry";
66
import { electronTrpcClient } from "renderer/lib/trpc-client";
7+
import type { HoveredLink } from "../../hooks/useLinkHoverState";
78

8-
interface HoveredLink {
9-
clientX: number;
10-
clientY: number;
11-
info: LinkHoverInfo;
12-
modifier: boolean;
13-
shift: boolean;
14-
}
9+
const TOOLTIP_OFFSET_PX = 14;
1510

1611
interface LinkHoverTooltipProps {
1712
hoveredLink: HoveredLink | null;
@@ -64,52 +59,12 @@ export function LinkHoverTooltip({ hoveredLink }: LinkHoverTooltipProps) {
6459
<div
6560
className="pointer-events-none fixed z-50 w-fit rounded-md bg-foreground px-3 py-1.5 text-xs text-background"
6661
style={{
67-
left: hoveredLink.clientX + 14,
68-
top: hoveredLink.clientY + 14,
62+
left: hoveredLink.clientX + TOOLTIP_OFFSET_PX,
63+
top: hoveredLink.clientY + TOOLTIP_OFFSET_PX,
6964
}}
7065
>
7166
{label}
7267
</div>,
7368
document.body,
7469
);
7570
}
76-
77-
export function useLinkHoverState() {
78-
const [hoveredLink, setHoveredLink] = useState<HoveredLink | null>(null);
79-
80-
useEffect(() => {
81-
if (!hoveredLink) return;
82-
const update = (event: KeyboardEvent) => {
83-
setHoveredLink((prev) => {
84-
if (!prev) return null;
85-
return {
86-
...prev,
87-
modifier: event.metaKey || event.ctrlKey,
88-
shift: event.shiftKey,
89-
};
90-
});
91-
};
92-
window.addEventListener("keydown", update);
93-
window.addEventListener("keyup", update);
94-
return () => {
95-
window.removeEventListener("keydown", update);
96-
window.removeEventListener("keyup", update);
97-
};
98-
}, [hoveredLink]);
99-
100-
const onHover = useCallback((event: MouseEvent, info: LinkHoverInfo) => {
101-
setHoveredLink({
102-
clientX: event.clientX,
103-
clientY: event.clientY,
104-
info,
105-
modifier: event.metaKey || event.ctrlKey,
106-
shift: event.shiftKey,
107-
});
108-
}, []);
109-
110-
const onLeave = useCallback(() => {
111-
setHoveredLink(null);
112-
}, []);
113-
114-
return { hoveredLink, onHover, onLeave };
115-
}
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { LinkHoverTooltip, useLinkHoverState } from "./LinkHoverTooltip";
1+
export { LinkHoverTooltip } from "./LinkHoverTooltip";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { type HoveredLink, useLinkHoverState } from "./useLinkHoverState";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { useCallback, useEffect, useState } from "react";
2+
import type { LinkHoverInfo } from "renderer/lib/terminal/terminal-runtime-registry";
3+
4+
export interface HoveredLink {
5+
clientX: number;
6+
clientY: number;
7+
info: LinkHoverInfo;
8+
modifier: boolean;
9+
shift: boolean;
10+
}
11+
12+
const MODIFIER_KEYS = new Set(["Meta", "Control", "Shift", "Alt"]);
13+
14+
export function useLinkHoverState() {
15+
const [hoveredLink, setHoveredLink] = useState<HoveredLink | null>(null);
16+
const hovering = hoveredLink !== null;
17+
18+
useEffect(() => {
19+
if (!hovering) return;
20+
const update = (event: KeyboardEvent) => {
21+
if (!MODIFIER_KEYS.has(event.key)) return;
22+
setHoveredLink((prev) => {
23+
if (!prev) return null;
24+
const nextModifier = event.metaKey || event.ctrlKey;
25+
const nextShift = event.shiftKey;
26+
if (prev.modifier === nextModifier && prev.shift === nextShift) {
27+
return prev;
28+
}
29+
return { ...prev, modifier: nextModifier, shift: nextShift };
30+
});
31+
};
32+
window.addEventListener("keydown", update);
33+
window.addEventListener("keyup", update);
34+
return () => {
35+
window.removeEventListener("keydown", update);
36+
window.removeEventListener("keyup", update);
37+
};
38+
}, [hovering]);
39+
40+
const onHover = useCallback((event: MouseEvent, info: LinkHoverInfo) => {
41+
setHoveredLink({
42+
clientX: event.clientX,
43+
clientY: event.clientY,
44+
info,
45+
modifier: event.metaKey || event.ctrlKey,
46+
shift: event.shiftKey,
47+
});
48+
}, []);
49+
50+
const onLeave = useCallback(() => {
51+
setHoveredLink(null);
52+
}, []);
53+
54+
return { hoveredLink, onHover, onLeave };
55+
}

0 commit comments

Comments
 (0)