UNPKG

@hhoangphuoc/escape-room-cli

Version:

A CLI for playing AI-generated escape room games. Install globally with: npm install -g @hhoangphuoc/escape-room-cli

75 lines (74 loc) 4.5 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import React, { useState, useMemo, useCallback } from 'react'; import { Box, Text, useInput, useStdout } from 'ink'; import Terminal from './components/Terminal.js'; import Title from './components/Title.js'; import UserRegistration from './components/UserRegistration.js'; import Instructions from './components/Instructions.js'; import { AuthProvider } from './context/AuthContext.js'; // Static footer component that never rerenders const FooterContent = () => (_jsx(Box, { marginTop: 0.5, flexShrink: 0, children: _jsx(Text, { color: "gray", children: "Type / for available commands \u2022 Ctrl+C to exit" }) })); const Footer = () => { const renderedRef = React.useRef(null); if (renderedRef.current === null) { renderedRef.current = _jsx(FooterContent, {}); } return renderedRef.current; }; // Aggressive memoization: never update const MemoizedFooter = React.memo(Footer, () => true); MemoizedFooter.displayName = 'Footer'; // Memoized main layout wrapper to isolate Terminal rerenders const MainLayout = React.memo(({ autoLogin, onTriggerRegister, onShowInstructions }) => { return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { flexShrink: 0, children: _jsx(Title, {}) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(Terminal, { autoLogin: autoLogin, onTriggerRegister: onTriggerRegister, onShowInstructions: onShowInstructions }) }), _jsx(MemoizedFooter, {})] })); }); MainLayout.displayName = 'MainLayout'; const AppContent = (props) => { const { stdout } = useStdout(); const terminalHeight = stdout?.rows || 40; const [isRegistering, setIsRegistering] = useState(props.register); const [showInstructions, setShowInstructions] = useState(false); const [instructionsKey, setInstructionsKey] = useState(0); const [layoutKey, setLayoutKey] = useState(0); // Handle ESC key to close Instructions overlay useInput((_, key) => { if (key.escape && showInstructions) { setShowInstructions(false); setInstructionsKey(prev => prev + 1); setLayoutKey(prev => prev + 1); } }); // Memoize callbacks to prevent recreating them on every render const handleRegistrationComplete = useCallback(() => { setIsRegistering(false); }, []); const handleTriggerRegister = useCallback(() => { setIsRegistering(true); }, []); const handleRegistrationCancel = useCallback(() => { setIsRegistering(false); }, []); const handleShowInstructions = useCallback(() => { setShowInstructions(true); setLayoutKey(prev => prev + 1); // Increment to clear MainLayout state }, []); // Memoize the registration component const registrationComponent = useMemo(() => { if (!isRegistering) return null; return (_jsx(UserRegistration, { onRegistrationComplete: handleRegistrationComplete, onCancel: handleRegistrationCancel, username: props.name, email: props.email })); }, [isRegistering, handleRegistrationComplete, handleRegistrationCancel, props.name, props.email]); if (registrationComponent) { return registrationComponent; } // Always render in a consistent container with explicit height to prevent buffer issues // The key on the outer Box ensures complete remount when switching views return (_jsx(Box, { flexDirection: "column", minHeight: terminalHeight, children: showInstructions ? ( // Instructions view - fills entire terminal screen _jsx(Box, { flexDirection: "column", height: terminalHeight, gap: 1, children: _jsxs(Box, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan", flexGrow: 1, children: [_jsx(Instructions, {}), _jsx(Box, { marginTop: 1, marginBottom: 1, justifyContent: "center", children: _jsx(Text, { color: "white", children: " \uD83D\uDCAAAll set? \u2022 \uD83C\uDFC3\uD83C\uDFFB\u200D\u2642\uFE0F Press ESC to close " }) })] }) })) : ( // Main view - fills entire terminal screen _jsx(Box, { flexDirection: "column", children: _jsx(MainLayout, { autoLogin: props.login, onTriggerRegister: handleTriggerRegister, onShowInstructions: handleShowInstructions }) })) }, showInstructions ? `instructions-${instructionsKey}` : `layout-${layoutKey}`)); }; export default function App(props) { return (_jsx(AuthProvider, { children: _jsx(AppContent, { ...props }) })); }