History

fix(web): bypass xterm for global terminal shortcuts (#1580)

Ariaj committed 7 months ago

Commit 39ca3ee1 parent 52a60673 files · +36 −0Browse files

Unified · Split

3 files
  1. apps/web/src/components/ChatView.tsx +5 −0
  2. apps/web/src/components/ThreadTerminalDrawer.browser.tsx +2 −0
  3. apps/web/src/components/ThreadTerminalDrawer.tsx +29 −0
apps/web/src/components/ChatView.tsx+5−0
@@ -10,6 +10,7 @@ import { · 9 unmodified lines
10 type ProjectId,10 type ProjectId,
11 type ProviderApprovalDecision,11 type ProviderApprovalDecision,
12 type ServerProvider,12 type ServerProvider,
13 type ResolvedKeybindingsConfig,
13 type ScopedThreadRef,14 type ScopedThreadRef,
14 type ThreadId,15 type ThreadId,
15 type TurnId,16 type TurnId,
@@ -415,6 +416,7 @@ interface PersistentThreadTerminalDrawerProps { · 399 unmodified lines
415 splitShortcutLabel: string | undefined;416 splitShortcutLabel: string | undefined;
416 newShortcutLabel: string | undefined;417 newShortcutLabel: string | undefined;
417 closeShortcutLabel: string | undefined;418 closeShortcutLabel: string | undefined;
419 keybindings: ResolvedKeybindingsConfig;
418 onAddTerminalContext: (selection: TerminalContextSelection) => void;420 onAddTerminalContext: (selection: TerminalContextSelection) => void;
419}421}
420 422
@@ -427,6 +429,7 @@ const PersistentThreadTerminalDrawer = memo(function PersistentThreadTerminalDra · 6 unmodified lines
427 splitShortcutLabel,429 splitShortcutLabel,
428 newShortcutLabel,430 newShortcutLabel,
429 closeShortcutLabel,431 closeShortcutLabel,
432 keybindings,
430 onAddTerminalContext,433 onAddTerminalContext,
431}: PersistentThreadTerminalDrawerProps) {434}: PersistentThreadTerminalDrawerProps) {
432 const serverThread = useStore(useMemo(() => createThreadSelectorByRef(threadRef), [threadRef]));435 const serverThread = useStore(useMemo(() => createThreadSelectorByRef(threadRef), [threadRef]));
@@ -570,6 +573,7 @@ const PersistentThreadTerminalDrawer = memo(function PersistentThreadTerminalDra · 137 unmodified lines
570 splitShortcutLabel={visible ? splitShortcutLabel : undefined}573 splitShortcutLabel={visible ? splitShortcutLabel : undefined}
571 newShortcutLabel={visible ? newShortcutLabel : undefined}574 newShortcutLabel={visible ? newShortcutLabel : undefined}
572 closeShortcutLabel={visible ? closeShortcutLabel : undefined}575 closeShortcutLabel={visible ? closeShortcutLabel : undefined}
576 keybindings={keybindings}
573 onActiveTerminalChange={activateTerminal}577 onActiveTerminalChange={activateTerminal}
574 onCloseTerminal={closeTerminal}578 onCloseTerminal={closeTerminal}
575 onHeightChange={setTerminalHeight}579 onHeightChange={setTerminalHeight}
@@ -3443,6 +3447,7 @@ export default function ChatView(props: ChatViewProps) { · 2867 unmodified lines
3443 splitShortcutLabel={splitTerminalShortcutLabel ?? undefined}3447 splitShortcutLabel={splitTerminalShortcutLabel ?? undefined}
3444 newShortcutLabel={newTerminalShortcutLabel ?? undefined}3448 newShortcutLabel={newTerminalShortcutLabel ?? undefined}
3445 closeShortcutLabel={closeTerminalShortcutLabel ?? undefined}3449 closeShortcutLabel={closeTerminalShortcutLabel ?? undefined}
3450 keybindings={keybindings}
3446 onAddTerminalContext={addTerminalContextToDraft}3451 onAddTerminalContext={addTerminalContextToDraft}
3447 />3452 />
3448 ))}3453 ))}
apps/web/src/components/ThreadTerminalDrawer.browser.tsx+2−0
@@ -177,6 +177,7 @@ async function mountTerminalViewport(props: { · 176 unmodified lines
177 autoFocus={false}177 autoFocus={false}
178 resizeEpoch={0}178 resizeEpoch={0}
179 drawerHeight={320}179 drawerHeight={320}
180 keybindings={[]}
180 />,181 />,
181 { container: host },182 { container: host },
182 );183 );
@@ -196,6 +197,7 @@ async function mountTerminalViewport(props: { · 13 unmodified lines
196 autoFocus={false}197 autoFocus={false}
197 resizeEpoch={0}198 resizeEpoch={0}
198 drawerHeight={320}199 drawerHeight={320}
200 keybindings={[]}
199 />,201 />,
200 );202 );
201 },203 },
apps/web/src/components/ThreadTerminalDrawer.tsx+29−0
@@ -1,6 +1,7 @@
1import { FitAddon } from "@xterm/addon-fit";1import { FitAddon } from "@xterm/addon-fit";
2import { Plus, SquareSplitHorizontal, TerminalSquare, Trash2, XIcon } from "lucide-react";2import { Plus, SquareSplitHorizontal, TerminalSquare, Trash2, XIcon } from "lucide-react";
3import {3import {
4 type ResolvedKeybindingsConfig,
4 type ScopedThreadRef,5 type ScopedThreadRef,
5 type TerminalEvent,6 type TerminalEvent,
6 type TerminalSessionSnapshot,7 type TerminalSessionSnapshot,
@@ -29,7 +30,12 @@ import { · 22 unmodified lines
29 wrappedTerminalLinkRangeIntersectsBufferLine,30 wrappedTerminalLinkRangeIntersectsBufferLine,
30} from "../terminal-links";31} from "../terminal-links";
31import {32import {
33 isDiffToggleShortcut,
32 isTerminalClearShortcut,34 isTerminalClearShortcut,
35 isTerminalCloseShortcut,
36 isTerminalNewShortcut,
37 isTerminalSplitShortcut,
38 isTerminalToggleShortcut,
33 terminalDeleteShortcutData,39 terminalDeleteShortcutData,
34 terminalNavigationShortcutData,40 terminalNavigationShortcutData,
35} from "../keybindings";41} from "../keybindings";
@@ -255,6 +261,7 @@ interface TerminalViewportProps { · 219 unmodified lines
255 autoFocus: boolean;261 autoFocus: boolean;
256 resizeEpoch: number;262 resizeEpoch: number;
257 drawerHeight: number;263 drawerHeight: number;
264 keybindings: ResolvedKeybindingsConfig;
258}265}
259 266
260export function TerminalViewport({267export function TerminalViewport({
@@ -271,6 +278,7 @@ export function TerminalViewport({ · 10 unmodified lines
271 autoFocus,278 autoFocus,
272 resizeEpoch,279 resizeEpoch,
273 drawerHeight,280 drawerHeight,
281 keybindings,
274}: TerminalViewportProps) {282}: TerminalViewportProps) {
275 const containerRef = useRef<HTMLDivElement>(null);283 const containerRef = useRef<HTMLDivElement>(null);
276 const terminalRef = useRef<Terminal | null>(null);284 const terminalRef = useRef<Terminal | null>(null);
@@ -282,6 +290,7 @@ export function TerminalViewport({ · 5 unmodified lines
282 const selectionActionRequestIdRef = useRef(0);290 const selectionActionRequestIdRef = useRef(0);
283 const selectionActionOpenRef = useRef(false);291 const selectionActionOpenRef = useRef(false);
284 const selectionActionTimerRef = useRef<number | null>(null);292 const selectionActionTimerRef = useRef<number | null>(null);
293 const keybindingsRef = useRef(keybindings);
285 const lastAppliedTerminalEventIdRef = useRef(0);294 const lastAppliedTerminalEventIdRef = useRef(0);
286 const terminalHydratedRef = useRef(false);295 const terminalHydratedRef = useRef(false);
287 const handleSessionExited = useEffectEvent(() => {296 const handleSessionExited = useEffectEvent(() => {
@@ -292,6 +301,10 @@ export function TerminalViewport({ · 4 unmodified lines
292 });301 });
293 const readTerminalLabel = useEffectEvent(() => terminalLabel);302 const readTerminalLabel = useEffectEvent(() => terminalLabel);
294 303
304 useEffect(() => {
305 keybindingsRef.current = keybindings;
306 }, [keybindings]);
307
295 useEffect(() => {308 useEffect(() => {
296 const mount = containerRef.current;309 const mount = containerRef.current;
297 if (!mount) return;310 if (!mount) return;
@@ -403,6 +416,18 @@ export function TerminalViewport({ · 105 unmodified lines
403 };416 };
404 417
405 terminal.attachCustomKeyEventHandler((event) => {418 terminal.attachCustomKeyEventHandler((event) => {
419 const currentKeybindings = keybindingsRef.current;
420 const options = { context: { terminalFocus: true, terminalOpen: true } };
421 if (
422 isTerminalToggleShortcut(event, currentKeybindings, options) ||
423 isTerminalSplitShortcut(event, currentKeybindings, options) ||
424 isTerminalNewShortcut(event, currentKeybindings, options) ||
425 isTerminalCloseShortcut(event, currentKeybindings, options) ||
426 isDiffToggleShortcut(event, currentKeybindings, options)
427 ) {
428 return false;
429 }
430
406 const navigationData = terminalNavigationShortcutData(event);431 const navigationData = terminalNavigationShortcutData(event);
407 if (navigationData !== null) {432 if (navigationData !== null) {
408 event.preventDefault();433 event.preventDefault();
@@ -795,6 +820,7 @@ interface ThreadTerminalDrawerProps { · 386 unmodified lines
795 onCloseTerminal: (terminalId: string) => void;820 onCloseTerminal: (terminalId: string) => void;
796 onHeightChange: (height: number) => void;821 onHeightChange: (height: number) => void;
797 onAddTerminalContext: (selection: TerminalContextSelection) => void;822 onAddTerminalContext: (selection: TerminalContextSelection) => void;
823 keybindings: ResolvedKeybindingsConfig;
798}824}
799 825
800interface TerminalActionButtonProps {826interface TerminalActionButtonProps {
@@ -848,6 +874,7 @@ export default function ThreadTerminalDrawer({ · 47 unmodified lines
848 onCloseTerminal,874 onCloseTerminal,
849 onHeightChange,875 onHeightChange,
850 onAddTerminalContext,876 onAddTerminalContext,
877 keybindings,
851}: ThreadTerminalDrawerProps) {878}: ThreadTerminalDrawerProps) {
852 const [drawerHeight, setDrawerHeight] = useState(() => clampDrawerHeight(height));879 const [drawerHeight, setDrawerHeight] = useState(() => clampDrawerHeight(height));
853 const [resizeEpoch, setResizeEpoch] = useState(0);880 const [resizeEpoch, setResizeEpoch] = useState(0);
@@ -1166,6 +1193,7 @@ export default function ThreadTerminalDrawer({ · 312 unmodified lines
1166 autoFocus={terminalId === resolvedActiveTerminalId}1193 autoFocus={terminalId === resolvedActiveTerminalId}
1167 resizeEpoch={resizeEpoch}1194 resizeEpoch={resizeEpoch}
1168 drawerHeight={drawerHeight}1195 drawerHeight={drawerHeight}
1196 keybindings={keybindings}
1169 />1197 />
1170 </div>1198 </div>
1171 </div>1199 </div>
@@ -1188,6 +1216,7 @@ export default function ThreadTerminalDrawer({ · 16 unmodified lines
1188 autoFocus1216 autoFocus
1189 resizeEpoch={resizeEpoch}1217 resizeEpoch={resizeEpoch}
1190 drawerHeight={drawerHeight}1218 drawerHeight={drawerHeight}
1219 keybindings={keybindings}
1191 />1220 />
1192 </div>1221 </div>
1193 )}1222 )}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t