fix: restore special keys bar on mobile terminal
- Add SpecialKeysStrip and SpecialKeysPanel to mobile terminal page - Store sendData and focusInput refs via onTerminalReady callback - Pass activeModifier/onModifierChange to TerminalComponent on mobile - Add virtual keyboard padding to prevent keyboard from covering terminal - Special keys bar sits at bottom of viewport, panel opens as overlay
This commit is contained in:
@@ -6,10 +6,14 @@ import {
|
|||||||
type TerminalSessionInfo,
|
type TerminalSessionInfo,
|
||||||
} from "../components/terminal-session-tabs";
|
} from "../components/terminal-session-tabs";
|
||||||
import { Icon } from "../components/icon";
|
import { Icon } from "../components/icon";
|
||||||
|
import { SpecialKeysStrip } from "../components/special-keys-strip";
|
||||||
|
import { SpecialKeysPanel } from "../components/special-keys-panel";
|
||||||
import { useMobileViewport } from "../hooks/use-mobile-viewport";
|
import { useMobileViewport } from "../hooks/use-mobile-viewport";
|
||||||
import { useAutoHide } from "../hooks/use-auto-hide";
|
import { useAutoHide } from "../hooks/use-auto-hide";
|
||||||
|
import { useVirtualKeyboard } from "../hooks/use-virtual-keyboard";
|
||||||
import { useTerminalSessions } from "../hooks/use-terminal-sessions";
|
import { useTerminalSessions } from "../hooks/use-terminal-sessions";
|
||||||
import type { TerminalSession } from "../api/terminal";
|
import type { TerminalSession } from "../api/terminal";
|
||||||
|
import type { ModifierKey } from "../hooks/use-special-keys";
|
||||||
|
|
||||||
const SESSIONS_TO_INFO = (sessions: TerminalSession[]): TerminalSessionInfo[] =>
|
const SESSIONS_TO_INFO = (sessions: TerminalSession[]): TerminalSessionInfo[] =>
|
||||||
sessions.map((s) => ({
|
sessions.map((s) => ({
|
||||||
@@ -40,7 +44,12 @@ export const TerminalPage: React.FC = () => {
|
|||||||
Record<string, TerminalStatus>
|
Record<string, TerminalStatus>
|
||||||
>({});
|
>({});
|
||||||
const changeFontSizeRef = useRef<((delta: number) => void) | null>(null);
|
const changeFontSizeRef = useRef<((delta: number) => void) | null>(null);
|
||||||
|
const sendDataRef = useRef<((data: string) => void) | null>(null);
|
||||||
|
const focusInputRef = useRef<(() => void) | null>(null);
|
||||||
const [showResetConfirm, setShowResetConfirm] = useState(false);
|
const [showResetConfirm, setShowResetConfirm] = useState(false);
|
||||||
|
const [showSpecialKeysPanel, setShowSpecialKeysPanel] = useState(false);
|
||||||
|
const [activeModifier, setActiveModifier] = useState<ModifierKey | null>(null);
|
||||||
|
const { isOpen: isKeyboardOpen, height: keyboardHeight } = useVirtualKeyboard();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
sessions,
|
sessions,
|
||||||
@@ -206,15 +215,17 @@ export const TerminalPage: React.FC = () => {
|
|||||||
|
|
||||||
const handleTerminalReady = useCallback(
|
const handleTerminalReady = useCallback(
|
||||||
(
|
(
|
||||||
_sendData: (data: string) => void,
|
sendData: (data: string) => void,
|
||||||
status: TerminalStatus,
|
status: TerminalStatus,
|
||||||
_focusInput: () => void,
|
focusInput: () => void,
|
||||||
changeFontSize: (delta: number) => void,
|
changeFontSize: (delta: number) => void,
|
||||||
) => {
|
) => {
|
||||||
setTerminalStatuses((prev) => ({
|
setTerminalStatuses((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[activeSessionId ?? "default"]: status,
|
[activeSessionId ?? "default"]: status,
|
||||||
}));
|
}));
|
||||||
|
sendDataRef.current = sendData;
|
||||||
|
focusInputRef.current = focusInput;
|
||||||
changeFontSizeRef.current = changeFontSize;
|
changeFontSizeRef.current = changeFontSize;
|
||||||
},
|
},
|
||||||
[activeSessionId],
|
[activeSessionId],
|
||||||
@@ -224,6 +235,10 @@ export const TerminalPage: React.FC = () => {
|
|||||||
changeFontSizeRef.current?.(delta);
|
changeFontSizeRef.current?.(delta);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleSendKey = useCallback((data: string) => {
|
||||||
|
sendDataRef.current?.(data);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleReset = useCallback(() => {
|
const handleReset = useCallback(() => {
|
||||||
if (activeSessionId && terminalRefs.current[activeSessionId]) {
|
if (activeSessionId && terminalRefs.current[activeSessionId]) {
|
||||||
terminalRefs.current[activeSessionId].current?.reset();
|
terminalRefs.current[activeSessionId].current?.reset();
|
||||||
@@ -318,6 +333,7 @@ export const TerminalPage: React.FC = () => {
|
|||||||
{/* Terminal content — always fills full viewport */}
|
{/* Terminal content — always fills full viewport */}
|
||||||
<div
|
<div
|
||||||
className="terminal-page-content mobile-full"
|
className="terminal-page-content mobile-full"
|
||||||
|
style={{ paddingBottom: isKeyboardOpen ? keyboardHeight : 0 }}
|
||||||
onClick={() => headerAutoHide.toggle()}
|
onClick={() => headerAutoHide.toggle()}
|
||||||
>
|
>
|
||||||
{error && <div className="terminal-error-banner">{error}</div>}
|
{error && <div className="terminal-error-banner">{error}</div>}
|
||||||
@@ -332,6 +348,8 @@ export const TerminalPage: React.FC = () => {
|
|||||||
onClose={() => handleClose(session.id)}
|
onClose={() => handleClose(session.id)}
|
||||||
isMobile={true}
|
isMobile={true}
|
||||||
showControls={false}
|
showControls={false}
|
||||||
|
activeModifier={activeModifier}
|
||||||
|
onModifierChange={setActiveModifier}
|
||||||
onTerminalReady={handleTerminalReady}
|
onTerminalReady={handleTerminalReady}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -342,6 +360,24 @@ export const TerminalPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<SpecialKeysStrip
|
||||||
|
onSend={handleSendKey}
|
||||||
|
isVisible={!showSpecialKeysPanel}
|
||||||
|
onMoreClick={() => setShowSpecialKeysPanel(true)}
|
||||||
|
onKeepFocus={() => focusInputRef.current?.()}
|
||||||
|
activeModifier={activeModifier}
|
||||||
|
onModifierChange={setActiveModifier}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SpecialKeysPanel
|
||||||
|
onSend={handleSendKey}
|
||||||
|
isOpen={showSpecialKeysPanel}
|
||||||
|
onClose={() => setShowSpecialKeysPanel(false)}
|
||||||
|
onKeepFocus={() => focusInputRef.current?.()}
|
||||||
|
activeModifier={activeModifier}
|
||||||
|
onModifierChange={setActiveModifier}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user