import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { ChevronUp, ChevronDown, ChevronLeft, ChevronRight, Pencil, X, Plus, RotateCcw, } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, } from "@/components/sheet"; import type { TerminalHandle } from "./terminal-types"; interface MobileTerminalKeyboardProps { terminalRef: React.RefObject; } const CTRL_MAP: Record = { a: "\x01", c: "\x03", d: "\x04", k: "\x0B", l: "\x0C", r: "\x12", u: "\x15", w: "\x17", z: "\x1A", }; const DEFAULT_QUICK_KEYS = [ "/", "|", "~", "-", "_", "#", "\\", '"', "'", ";", ":", "!", "&", ]; const LS_KEY = "termix:mobileQuickKeys"; function loadQuickKeys(): string[] { try { const raw = localStorage.getItem(LS_KEY); if (raw) { const parsed = JSON.parse(raw); if (Array.isArray(parsed) && parsed.every((v) => typeof v === "string")) return parsed; } } catch { /* ignore */ } return DEFAULT_QUICK_KEYS; } function saveQuickKeys(keys: string[]) { try { localStorage.setItem(LS_KEY, JSON.stringify(keys)); } catch { /* ignore */ } } // Shared button styles const KEY_BASE = "flex items-center justify-center rounded border transition-colors select-none touch-none active:scale-95 shrink-0"; const KEY_NORMAL = "border-border bg-muted/50 text-foreground hover:bg-muted"; const KEY_ACTIVE = "border-accent-brand bg-accent-brand/20 text-accent-brand shadow-[0_0_0_1px_color-mix(in_oklab,var(--accent-brand)_30%,transparent)]"; const KEY_MD = "h-9 px-3 min-w-[2.75rem] text-xs font-medium"; const KEY_SM = "h-9 w-9"; const SEP = "w-px h-5 bg-border mx-0.5 shrink-0"; // --- QuickKeysSheet --- interface QuickKeysSheetProps { open: boolean; onOpenChange: (open: boolean) => void; quickKeys: string[]; onUpdateKeys: (keys: string[]) => void; } function QuickKeysSheet({ open, onOpenChange, quickKeys, onUpdateKeys, }: QuickKeysSheetProps) { const { t } = useTranslation(); const [newSymbol, setNewSymbol] = useState(""); const inputRef = useRef(null); useEffect(() => { if (!open) setNewSymbol(""); }, [open]); function addKey() { const sym = newSymbol.trim(); if (!sym || quickKeys.includes(sym) || sym.length > 8) { setNewSymbol(""); return; } onUpdateKeys([...quickKeys, sym]); setNewSymbol(""); inputRef.current?.focus(); } function removeKey(index: number) { onUpdateKeys(quickKeys.filter((_, i) => i !== index)); } function resetDefaults() { onUpdateKeys(DEFAULT_QUICK_KEYS); } return (
{t("mobileKeyboard.quickKeysTitle")} {t("mobileKeyboard.quickKeysDesc")}
{quickKeys.map((sym, i) => (
{sym}
))}
setNewSymbol(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") addKey(); }} maxLength={8} placeholder={t("mobileKeyboard.quickKeyPlaceholder")} className="h-9 text-xs font-mono" />
); } // --- CtrlPanel --- interface CtrlPanelProps { onSend: (letter: string) => void; } function CtrlPanel({ onSend }: CtrlPanelProps) { const CTRL_KEYS = ["c", "d", "l", "u", "z", "a", "r", "w", "k"]; return (
{CTRL_KEYS.map((k) => ( ))}
); } // --- MobileTerminalKeyboard --- export function MobileTerminalKeyboard({ terminalRef, }: MobileTerminalKeyboardProps) { const { t } = useTranslation(); const [ctrlActive, setCtrlActive] = useState(false); const [shiftActive, setShiftActive] = useState(false); const [quickKeys, setQuickKeys] = useState(loadQuickKeys); const [sheetOpen, setSheetOpen] = useState(false); function send(seq: string) { terminalRef.current?.sendInput?.(seq); } function toggleCtrl() { setCtrlActive((v) => !v); setShiftActive(false); } function toggleShift() { setShiftActive((v) => !v); setCtrlActive(false); } function sendArrow(normalSeq: string, appSeq: string, shiftSeq: string) { if (shiftActive) { send(shiftSeq); setShiftActive(false); return; } const appMode = terminalRef.current?.getApplicationCursorKeysMode?.() ?? false; send(appMode ? appSeq : normalSeq); } function handleTab() { send(shiftActive ? "\x1b[Z" : "\t"); setShiftActive(false); } function handleCtrlKey(letter: string) { const seq = CTRL_MAP[letter]; if (seq) send(seq); setCtrlActive(false); } function updateQuickKeys(next: string[]) { setQuickKeys(next); saveQuickKeys(next); } return (
{/* Row 1 — special keys */}
{/* ESC */} {/* Tab / back-tab */}
{/* Ctrl */} {/* Shift */}
{/* Arrow keys */}
{/* Home / End */}
{/* PgUp / PgDn / Del */}
{/* Ctrl combos panel */} {ctrlActive && } {/* Row 2 — quick keys */}
{quickKeys.map((sym, i) => ( ))}
); }