12378def4d
- Redesign useSpecialKeys hook with modifier state tracking - Add one-shot activation for Ctrl and Alt keys - Visual feedback: active modifiers shown with yellow highlight - Fix focusInput to use term.focus() instead of hidden input - Always refocus terminal after sending any special key - Add requestAnimationFrame for reliable focus restoration
78 lines
2.1 KiB
TypeScript
78 lines
2.1 KiB
TypeScript
import React from "react";
|
|
import { useSpecialKeys, type SpecialKey, type ModifierKey } from "../hooks/use-special-keys";
|
|
|
|
interface SpecialKeysStripProps {
|
|
onSend: (data: string) => void;
|
|
isVisible: boolean;
|
|
onMoreClick?: () => void;
|
|
onKeepFocus?: () => void;
|
|
}
|
|
|
|
const PRIMARY_KEYS: { key: SpecialKey; label: string; isModifier?: boolean }[] = [
|
|
{ key: "escape", label: "Esc" },
|
|
{ key: "tab", label: "Tab" },
|
|
{ key: "ctrl", label: "Ctrl", isModifier: true },
|
|
{ key: "alt", label: "Alt", isModifier: true },
|
|
{ key: "up", label: "↑" },
|
|
{ key: "down", label: "↓" },
|
|
{ key: "left", label: "←" },
|
|
{ key: "right", label: "→" },
|
|
];
|
|
|
|
export const SpecialKeysStrip: React.FC<SpecialKeysStripProps> = ({
|
|
onSend,
|
|
isVisible,
|
|
onMoreClick,
|
|
onKeepFocus,
|
|
}) => {
|
|
const { sendKey, activeModifier } = useSpecialKeys({ onSend });
|
|
|
|
const handlePointerDown = (e: React.PointerEvent, key: SpecialKey) => {
|
|
e.preventDefault();
|
|
sendKey(key);
|
|
// Always refocus terminal after sending
|
|
requestAnimationFrame(() => {
|
|
onKeepFocus?.();
|
|
});
|
|
};
|
|
|
|
const handleMorePointerDown = (e: React.PointerEvent) => {
|
|
e.preventDefault();
|
|
onMoreClick?.();
|
|
requestAnimationFrame(() => {
|
|
onKeepFocus?.();
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className={`special-keys-strip ${isVisible ? "visible" : "hidden"}`}>
|
|
{PRIMARY_KEYS.map(({ key, label, isModifier }) => (
|
|
<button
|
|
key={key}
|
|
className={`special-key-button ${
|
|
isModifier && activeModifier === key ? "active-modifier" : ""
|
|
}`}
|
|
onPointerDown={(e) => handlePointerDown(e, key)}
|
|
type="button"
|
|
tabIndex={-1}
|
|
aria-label={`Send ${label}`}
|
|
aria-pressed={isModifier && activeModifier === key}
|
|
>
|
|
{label}
|
|
</button>
|
|
))}
|
|
{onMoreClick && (
|
|
<button
|
|
className="special-key-button special-key-more"
|
|
onPointerDown={handleMorePointerDown}
|
|
type="button"
|
|
tabIndex={-1}
|
|
aria-label="More special keys"
|
|
>
|
|
More
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|