fix: lift modifier state to MobileTerminalWrapper for virtual keyboard integration

- Remove useSpecialKeys hook state, export pure utility functions instead
- MobileTerminalWrapper now owns activeModifier state
- SpecialKeysStrip and SpecialKeysPanel receive modifier via props
- TerminalComponent applies modifier to virtual keyboard input via activeModifier prop
- Modifier now works with both special keys AND virtual keyboard input
- Modifier clears after any key press (special or virtual keyboard)
This commit is contained in:
Fusion
2026-05-24 12:45:07 +02:00
parent 3d9ff44d1a
commit e555561a2d
5 changed files with 98 additions and 51 deletions
+21 -2
View File
@@ -4,10 +4,14 @@ import { FitAddon } from "xterm-addon-fit";
import { WebLinksAddon } from "xterm-addon-web-links";
import "xterm/css/xterm.css";
import { applyModifierToChar, type ModifierKey } from "../hooks/use-special-keys";
interface TerminalProps {
instanceId: string;
onClose?: () => void;
isMobile?: boolean;
activeModifier?: ModifierKey | null;
onModifierChange?: (modifier: ModifierKey | null) => void;
onTerminalReady?: (
sendData: (data: string) => void,
connectionStatus: "connecting" | "connected" | "disconnected" | "error",
@@ -25,6 +29,8 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
instanceId,
onClose,
isMobile = false,
activeModifier,
onModifierChange,
onTerminalReady,
}) => {
const terminalRef = useRef<HTMLDivElement>(null);
@@ -39,6 +45,8 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
"connecting" | "connected" | "disconnected" | "error"
>("connecting");
const [error, setError] = useState<string | null>(null);
const activeModifierRef = useRef(activeModifier);
activeModifierRef.current = activeModifier;
const [fontSize, setFontSize] = useState(() => {
if (typeof window === "undefined") return isMobile ? 16 : 14;
const stored = localStorage.getItem(FONT_SIZE_KEY);
@@ -161,9 +169,20 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
// Handle terminal input
term.onData((data) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(data);
if (ws.readyState !== WebSocket.OPEN) return;
// Apply active modifier to single-character input
const modifier = activeModifierRef.current;
if (modifier && data.length === 1) {
const modified = applyModifierToChar(data, modifier);
if (modified) {
ws.send(modified);
onModifierChange?.(null);
return;
}
}
ws.send(data);
});
// Handle resize with debounce