| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | import { FitAddon } from "@xterm/addon-fit"; | 1 | import { FitAddon } from "@xterm/addon-fit"; |
| 2 | import { Plus, SquareSplitHorizontal, TerminalSquare, Trash2, XIcon } from "lucide-react"; | 2 | import { Plus, SquareSplitHorizontal, TerminalSquare, Trash2, XIcon } from "lucide-react"; |
| 3 | import { | 3 | import { |
| | 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 { | ... | @@ -29,7 +30,12 @@ import { |
| 29 | wrappedTerminalLinkRangeIntersectsBufferLine, | 30 | wrappedTerminalLinkRangeIntersectsBufferLine, |
| 30 | } from "../terminal-links"; | 31 | } from "../terminal-links"; |
| 31 | import { | 32 | import { |
| | 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 { | ... | @@ -255,6 +261,7 @@ interface TerminalViewportProps { |
| 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 | |
| 260 | export function TerminalViewport({ | 267 | export function TerminalViewport({ |
| ... | @@ -271,6 +278,7 @@ export function TerminalViewport({ | ... | @@ -271,6 +278,7 @@ export function TerminalViewport({ |
| 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({ | ... | @@ -282,6 +290,7 @@ export function TerminalViewport({ |
| 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({ | ... | @@ -292,6 +301,10 @@ export function TerminalViewport({ |
| 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({ | ... | @@ -403,6 +416,18 @@ export function TerminalViewport({ |
| 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 { | ... | @@ -795,6 +820,7 @@ interface ThreadTerminalDrawerProps { |
| 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 | |
| 800 | interface TerminalActionButtonProps { | 826 | interface TerminalActionButtonProps { |
| ... | @@ -848,6 +874,7 @@ export default function ThreadTerminalDrawer({ | ... | @@ -848,6 +874,7 @@ export default function ThreadTerminalDrawer({ |
| 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({ | ... | @@ -1166,6 +1193,7 @@ export default function ThreadTerminalDrawer({ |
| 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({ | ... | @@ -1188,6 +1216,7 @@ export default function ThreadTerminalDrawer({ |
| 1188 | autoFocus | 1216 | autoFocus |
| 1189 | resizeEpoch={resizeEpoch} | 1217 | resizeEpoch={resizeEpoch} |
| 1190 | drawerHeight={drawerHeight} | 1218 | drawerHeight={drawerHeight} |
| | 1219 | keybindings={keybindings} |
| 1191 | /> | 1220 | /> |
| 1192 | </div> | 1221 | </div> |
| 1193 | )} | 1222 | )} |