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
+31 -40
View File
@@ -1,4 +1,4 @@
import { useState, useCallback } from "react";
export type SpecialKey =
| "escape"
@@ -103,47 +103,38 @@ const MODIFIER_PREFIXES: Record<string, { ctrl: string; alt: string; ctrlAlt: st
"9": { ctrl: "9", alt: "\x1B9", ctrlAlt: "\x1B9" },
};
interface UseSpecialKeysOptions {
onSend: (data: string) => void;
export function getSequenceWithModifier(
key: SpecialKey,
activeModifier: ModifierKey | null
): { sequence: string; clearModifier: boolean } | null {
// Handle modifier keys (one-shot)
if (key === "ctrl" || key === "alt") {
return null; // Modifiers don't send anything themselves
}
const sequence = KEY_SEQUENCES[key];
if (!sequence) return null;
// Check if we have an active modifier and the key is a single character
if (activeModifier && sequence.length === 1) {
const char = sequence;
const mapping = MODIFIER_PREFIXES[char.toLowerCase()];
if (mapping) {
return { sequence: mapping[activeModifier], clearModifier: true };
}
}
return { sequence, clearModifier: !!activeModifier };
}
export function useSpecialKeys({ onSend }: UseSpecialKeysOptions) {
const [activeModifier, setActiveModifier] = useState<ModifierKey | null>(null);
const sendKey = useCallback(
(key: SpecialKey) => {
// Handle modifier keys (one-shot)
if (key === "ctrl" || key === "alt") {
// Toggle modifier
setActiveModifier((current) => (current === key ? null : key));
return;
}
const sequence = KEY_SEQUENCES[key];
if (!sequence) return;
// Check if we have an active modifier and the key is a single character
if (activeModifier && sequence.length === 1) {
const char = sequence;
const mapping = MODIFIER_PREFIXES[char.toLowerCase()];
if (mapping) {
onSend(mapping[activeModifier]);
setActiveModifier(null);
return;
}
}
onSend(sequence);
setActiveModifier(null);
},
[onSend, activeModifier]
);
const clearModifier = useCallback(() => {
setActiveModifier(null);
}, []);
return { sendKey, activeModifier, clearModifier };
export function applyModifierToChar(
char: string,
modifier: ModifierKey
): string | null {
if (char.length !== 1) return null;
const mapping = MODIFIER_PREFIXES[char.toLowerCase()];
if (!mapping) return null;
return mapping[modifier];
}
export { KEY_SEQUENCES };