diff --git a/.changeset/fn-6737-terminal-ctrl-shortcuts.md b/.changeset/fn-6737-terminal-ctrl-shortcuts.md new file mode 100644 index 0000000000..e3d908c951 --- /dev/null +++ b/.changeset/fn-6737-terminal-ctrl-shortcuts.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": patch +--- + +Fix terminal shortcut focus preservation so on-screen Ctrl combinations emit control bytes reliably on touch and pointer devices while keeping physical Ctrl behavior intact. diff --git a/packages/dashboard/app/components/TerminalModal.tsx b/packages/dashboard/app/components/TerminalModal.tsx index 40fd5d5053..a6cf94c775 100644 --- a/packages/dashboard/app/components/TerminalModal.tsx +++ b/packages/dashboard/app/components/TerminalModal.tsx @@ -6,6 +6,8 @@ import { useCallback, type CSSProperties, type MouseEvent as ReactMouseEvent, + type PointerEvent as ReactPointerEvent, + type TouchEvent as ReactTouchEvent, } from "react"; import { useTranslation } from "react-i18next"; import { getErrorMessage } from "@fusion/core"; @@ -1256,10 +1258,21 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG /* FNXC:Terminal 2026-06-19-05:05: FN-6697 root cause: shortcut-bar buttons took browser focus on hardware-keyboard surfaces before their click handlers injected bytes, leaving xterm's helper textarea blurred even though the active session's sendInput path was correct. Preserve focus on mousedown and refocus xterm after every shortcut action so sticky modifiers, literal keys, arrows, and Ctrl-letter shortcuts deliver input without stranding subsequent hardware-keyboard typing across desktop and touch surfaces. + + FNXC:Terminal 2026-06-19-10:38: + FN-6737 root cause: touch-primary Ctrl shortcuts still allowed the browser's touchstart default action on shortcut buttons, so a tap on sticky Ctrl could move focus away from xterm's helper textarea before the composed Ctrl-letter byte reached the active PTY. Prevent the focus-taking default for mouse and touch activation, then keep the existing xterm refocus path so Ctrl control codes work from the sticky shortcut panel and physical Ctrl key paths on desktop, touch, and touch-with-hardware-keyboard surfaces. */ - const preserveShortcutFocus = useCallback((event: ReactMouseEvent) => { - event.preventDefault(); - }, []); + const preserveShortcutFocus = useCallback( + ( + event: + | ReactMouseEvent + | ReactPointerEvent + | ReactTouchEvent, + ) => { + event.preventDefault(); + }, + [], + ); const refocusTerminalAfterShortcut = useCallback(() => { xtermRef.current?.focus(); @@ -1569,7 +1582,9 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG stickyModifier === "ctrl" ? "is-active" : "" }`} data-testid="terminal-modifier-ctrl" + onPointerDown={preserveShortcutFocus} onMouseDown={preserveShortcutFocus} + onTouchStart={preserveShortcutFocus} onClick={() => toggleModifier("ctrl")} aria-pressed={stickyModifier === "ctrl"} > @@ -1581,7 +1596,9 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG stickyModifier === "alt" ? "is-active" : "" }`} data-testid="terminal-modifier-alt" + onPointerDown={preserveShortcutFocus} onMouseDown={preserveShortcutFocus} + onTouchStart={preserveShortcutFocus} onClick={() => toggleModifier("alt")} aria-pressed={stickyModifier === "alt"} > @@ -1590,7 +1607,9 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG