/**
* Main App component for the XO Wallet CLI.
* Uses Ink for terminal rendering with React components.
*/
import React from 'react';
import { Box, Text, useApp } from 'ink';
import { NavigationProvider, useNavigation } from './hooks/useNavigation.js';
import { AppProvider, useAppContext, useDialog, useStatus } from './hooks/useAppContext.js';
import { InputLayerProvider, useBlockableInput } from './hooks/useInputLayer.js';
import type { AppConfig } from '../app.js';
import { colors, logoSmall } from './theme.js';
// Screen imports
import { SeedInputScreen } from './screens/SeedInput.js';
import { WalletStateScreen } from './screens/WalletState.js';
import { TemplateListScreen } from './screens/TemplateList.js';
import { ActionWizardScreen } from './screens/action-wizard/ActionWizardScreen.js';
import { InvitationScreen } from './screens/invitations/InvitationScreen.js';
import { TransactionScreen } from './screens/Transaction.js';
import { MessageDialog } from './components/Dialog.js';
/**
* Props for the App component.
*/
interface AppProps {
config: AppConfig;
}
/**
* Router component that renders the current screen.
*/
function Router(): React.ReactElement {
const { screen } = useNavigation();
switch (screen) {
case 'seed-input':
return ;
case 'wallet':
return ;
case 'templates':
return ;
case 'wizard':
return ;
case 'invitations':
return ;
case 'transaction':
return ;
default:
return Unknown screen: {screen};
}
}
/**
* Status bar component shown at the bottom of the screen.
*/
function StatusBar(): React.ReactElement {
const { status } = useStatus();
const { screen, canGoBack } = useNavigation();
return (
{logoSmall}
{status}
{canGoBack ? 'ESC: Back | ' : ''}q: Quit
);
}
/**
* Dialog overlay component for modals.
*/
function DialogOverlay(): React.ReactElement | null {
const { dialog } = useDialog();
if (!dialog?.visible) return null;
const borderColor = dialog.type === 'error' ? colors.error :
dialog.type === 'confirm' ? colors.warning :
colors.info;
return (
{})}
type={dialog.type as 'error' | 'info' | 'success'}
/>
);
}
/**
* Main content wrapper with global keybindings.
*/
function MainContent(): React.ReactElement {
const { exit } = useApp();
const { goBack, canGoBack } = useNavigation();
const { screen } = useNavigation();
const appContext = useAppContext();
// Global keybindings — auto-blocked when any dialog/overlay is capturing input.
useBlockableInput((input, key) => {
// Quit on Ctrl+C
if (key.ctrl && input === 'c') {
appContext.exit();
exit();
}
// Go back on Escape
if (key.escape && canGoBack) {
goBack();
// If we went back to the seed input screen, remove the current engine
// TODO: This was to support going back to seed input then re-opening your seed, but there is a bug in the engine which prevents it from closing the current
// storage instance, giving us an error about the database already being opened.
if (screen === 'seed-input') {
appContext.appService?.engine.stop();
appContext.appService = null;
}
}
});
return (
{/* Main content area */}
{/* Status bar */}
{/* Dialog overlay */}
);
}
/**
* Main App component.
* Sets up providers and renders the main content.
*/
export function App({ config }: AppProps): React.ReactElement {
const { exit } = useApp();
// Cleanup will be handled by React when components unmount
const handleExit = () => {
exit();
};
return (
);
}