Skip to content
Source
Sign inSign up
Open History to see the last commit

OpenInPickerShortcut.ts

111 lines3.8 KB

1import {
2 buildRemoteOpenUrl,
3 type EditorId,
4 type EnvironmentId,
5 type ResolvedKeybindingsConfig,
6} from "@t3tools/contracts";
7import { useEffect } from "react";
8​
9import { usePreferredEditor } from "../../editorPreferences";
10import { editorLabelForPlatform } from "../../editorLabels";
11import { isOpenFavoriteEditorShortcut } from "../../keybindings";
12import { announce } from "../../lib/announce";
13import { openRemoteEditorUrl, useRemoteCapableEditors, useRemoteOpenState } from "../../remoteOpen";
14import { registerCommands } from "../../shortcuts/dispatcher";
15import { shellEnvironment } from "../../state/shell";
16import { useAtomCommand } from "../../state/use-atom-command";
17​
18/** Marks an OpenInPicker's primary button as the target of `editor.openFavorite`. */
19export const OPEN_FAVORITE_EDITOR_ATTRIBUTE = "data-open-favorite-editor";
20​
21/**
22 * `editor.openFavorite` presses the marked OpenInPicker primary button, so the shortcut opens
23 * exactly what the button on screen would. A missing or disabled button leaves the chord to the
24 * bubble-phase fallback below. Capture phase, so a focused terminal does not swallow the chord.
25 */
26export function registerOpenFavoriteEditorCommand(): () => void {
27 return registerCommands(
28 {
29 "editor.openFavorite": (event) => {
30 if (event.repeat) return false;
31 const button = [
32 ...document.querySelectorAll<HTMLButtonElement>(`[${OPEN_FAVORITE_EDITOR_ATTRIBUTE}]`),
33 ].at(-1);
34 if (button === undefined || button.disabled) return false;
35 event.preventDefault();
36 button.click();
37 },
38 },
39 { capture: true },
40 );
42​
43export function useOpenFavoriteEditorShortcut({
44 enabled,
45 environmentId,
46 keybindings,
47 availableEditors,
48 openInCwd,
49}: {
50 enabled: boolean;
51 environmentId: EnvironmentId;
52 keybindings: ResolvedKeybindingsConfig;
53 availableEditors: ReadonlyArray<EditorId>;
54 openInCwd: string | null;
55}) {
56 const openInEditorMutation = useAtomCommand(shellEnvironment.openInEditor, "open in editor");
57 const remote = useRemoteOpenState(environmentId);
58 const remoteCapableEditors = useRemoteCapableEditors();
59 // Matches the picker: remote editors run on the viewing machine.
60 const [preferredEditor] = usePreferredEditor(
61 remote.mode === "local-exec" ? availableEditors : remoteCapableEditors,
62 );
63​
64 // oxlint-disable-next-line t3code/no-unreasoned-state-sync -- subscribes a window keydown listener for the open-in-editor shortcut while enabled
65 useEffect(() => {
66 if (!enabled || remote.mode === "remote-unavailable") return;
67 const handler = (event: globalThis.KeyboardEvent) => {
68 if (
69 event.repeat ||
70 event.defaultPrevented ||
71 !isOpenFavoriteEditorShortcut(event, keybindings)
72 )
73 return;
74 if (!openInCwd || !preferredEditor) return;
75​
76 event.preventDefault();
77 const label = editorLabelForPlatform(preferredEditor, navigator.platform);
78 if (remote.mode === "remote-links") {
79 const url = buildRemoteOpenUrl({
80 editor: preferredEditor,
81 ...remote.host,
82 absolutePath: openInCwd,
83 });
84 if (url === undefined) return;
85 void openRemoteEditorUrl(url).then((opened) => {
86 if (opened) announce(`Opened in ${label}`);
87 });
88 return;
89 }
90 void openInEditorMutation({
91 environmentId,
92 input: {
93 cwd: openInCwd,
94 editor: preferredEditor,
95 },
96 }).then((result) => {
97 if (result._tag === "Success") announce(`Opened in ${label}`);
98 });
99 };
100 window.addEventListener("keydown", handler);
101 return () => window.removeEventListener("keydown", handler);
102 }, [
103 enabled,
104 environmentId,
105 keybindings,
106 openInCwd,
107 openInEditorMutation,
108 preferredEditor,
109 remote,
110 ]);