@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
JavaScript
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 }) }));
}