Clean up and fixes

This commit is contained in:
2026-02-08 02:32:50 +00:00
parent eb1bf9020e
commit da096af0fa
36 changed files with 2119 additions and 1751 deletions

View File

@@ -11,7 +11,10 @@ import { Box, Text, useInput } from 'ink';
import { useNavigation } from '../hooks/useNavigation.js';
import { useAppContext, useStatus } from '../hooks/useAppContext.js';
import { colors, logoSmall } from '../theme.js';
import type { XOTemplate, XOTemplateStartingActions } from '@xo-cash/types';
// XO Imports
import { generateTemplateIdentifier } from '@xo-cash/engine';
import type { XOTemplate, XOTemplateActionRoleRequirement, XOTemplateStartingActions } from '@xo-cash/types';
/**
* Template item with metadata.
@@ -28,7 +31,7 @@ interface TemplateItem {
*/
export function TemplateListScreen(): React.ReactElement {
const { navigate } = useNavigation();
const { walletController, showError } = useAppContext();
const { appService, showError } = useAppContext();
const { setStatus } = useStatus();
// State
@@ -39,20 +42,24 @@ export function TemplateListScreen(): React.ReactElement {
const [isLoading, setIsLoading] = useState(true);
/**
* Loads templates from the wallet controller.
* Loads templates from the engine.
*/
const loadTemplates = useCallback(async () => {
if (!appService) {
showError('AppService not initialized');
return;
}
try {
setIsLoading(true);
setStatus('Loading templates...');
const templateList = await walletController.getTemplates();
const { generateTemplateIdentifier } = await import('@xo-cash/engine');
const templateList = await appService.engine.listImportedTemplates();
const loadedTemplates = await Promise.all(
templateList.map(async (template) => {
const templateIdentifier = generateTemplateIdentifier(template);
const startingActions = await walletController.getStartingActions(templateIdentifier);
const startingActions = await appService.engine.listStartingActions(templateIdentifier);
return { template, templateIdentifier, startingActions };
})
);
@@ -66,7 +73,7 @@ export function TemplateListScreen(): React.ReactElement {
showError(`Failed to load templates: ${error instanceof Error ? error.message : String(error)}`);
setIsLoading(false);
}
}, [walletController, setStatus, showError]);
}, [appService, setStatus, showError]);
// Load templates on mount
useEffect(() => {
@@ -127,31 +134,39 @@ export function TemplateListScreen(): React.ReactElement {
});
return (
<Box flexDirection="column" flexGrow={1}>
<Box flexDirection='column' flexGrow={1}>
{/* Header */}
<Box borderStyle="single" borderColor={colors.secondary} paddingX={1}>
<Box borderStyle='single' borderColor={colors.secondary} paddingX={1}>
<Text color={colors.primary} bold>{logoSmall} - Select Template & Action</Text>
</Box>
{/* Main content - two columns */}
<Box flexDirection="row" marginTop={1} flexGrow={1}>
<Box flexDirection='row' marginTop={1} flexGrow={1}>
{/* Left column: Template list */}
<Box flexDirection="column" width="40%" paddingRight={1}>
<Box flexDirection='column' width='40%' paddingRight={1}>
<Box
borderStyle="single"
borderStyle='single'
borderColor={focusedPanel === 'templates' ? colors.focus : colors.primary}
flexDirection="column"
flexDirection='column'
paddingX={1}
flexGrow={1}
>
<Text color={colors.primary} bold> Templates </Text>
<Box marginTop={1} flexDirection="column">
{isLoading ? (
<Text color={colors.textMuted}>Loading...</Text>
) : templates.length === 0 ? (
<Text color={colors.textMuted}>No templates imported</Text>
) : (
templates.map((item, index) => (
<Box marginTop={1} flexDirection='column'>
{(() => {
// Loading State
if (isLoading) {
return <Text color={colors.textMuted}>Loading...</Text>;
}
// No templates state
if (templates.length === 0) {
return <Text color={colors.textMuted}>No templates imported</Text>;
}
// Templates state
return templates.map((item, index) => (
<Text
key={item.templateIdentifier}
color={index === selectedTemplateIndex ? colors.focus : colors.text}
@@ -160,29 +175,43 @@ export function TemplateListScreen(): React.ReactElement {
{index === selectedTemplateIndex && focusedPanel === 'templates' ? '▸ ' : ' '}
{index + 1}. {item.template.name || 'Unnamed Template'}
</Text>
))
)}
));
})()}
</Box>
</Box>
</Box>
{/* Right column: Actions list */}
<Box flexDirection="column" width="60%" paddingLeft={1}>
<Box flexDirection='column' width='60%' paddingLeft={1}>
<Box
borderStyle="single"
borderStyle='single'
borderColor={focusedPanel === 'actions' ? colors.focus : colors.border}
flexDirection="column"
flexDirection='column'
paddingX={1}
flexGrow={1}
>
<Text color={colors.primary} bold> Starting Actions </Text>
<Box marginTop={1} flexDirection="column">
{!currentTemplate ? (
<Text color={colors.textMuted}>Select a template...</Text>
) : currentActions.length === 0 ? (
<Text color={colors.textMuted}>No starting actions available</Text>
) : (
currentActions.map((action, index) => {
<Box marginTop={1} flexDirection='column'>
{(() => {
// Loading state
if (isLoading) {
return <Text color={colors.textMuted}>Loading...</Text>;
}
// No template selected state
if (!currentTemplate) {
return <Text color={colors.textMuted}>Select a template...</Text>;
}
// No starting actions state
if (currentActions.length === 0) {
return <Text color={colors.textMuted}>No starting actions available</Text>;
}
// Starting actions state
return currentActions.map((action, index) => {
const actionDef = currentTemplate.template.actions?.[action.action];
const name = actionDef?.name || action.action;
return (
@@ -195,8 +224,9 @@ export function TemplateListScreen(): React.ReactElement {
{index + 1}. {name} (as {action.role})
</Text>
);
})
)}
});
})()}
</Box>
</Box>
</Box>
@@ -205,16 +235,18 @@ export function TemplateListScreen(): React.ReactElement {
{/* Description box */}
<Box marginTop={1}>
<Box
borderStyle="single"
borderStyle='single'
borderColor={colors.border}
flexDirection="column"
flexDirection='column'
paddingX={1}
paddingY={1}
width="100%"
width='100%'
>
<Text color={colors.primary} bold> Description </Text>
{currentTemplate ? (
<Box marginTop={1} flexDirection="column">
{/* Show template description when templates panel is focused */}
{focusedPanel === 'templates' && currentTemplate ? (
<Box marginTop={1} flexDirection='column'>
<Text color={colors.text} bold>
{currentTemplate.template.name || 'Unnamed Template'}
</Text>
@@ -227,7 +259,7 @@ export function TemplateListScreen(): React.ReactElement {
</Text>
)}
{currentTemplate.template.roles && (
<Box marginTop={1} flexDirection="column">
<Box marginTop={1} flexDirection='column'>
<Text color={colors.text}>Roles:</Text>
{Object.entries(currentTemplate.template.roles).map(([roleId, role]) => (
<Text key={roleId} color={colors.textMuted}>
@@ -237,9 +269,70 @@ export function TemplateListScreen(): React.ReactElement {
</Box>
)}
</Box>
) : (
) : focusedPanel === 'templates' && !currentTemplate ? (
<Text color={colors.textMuted}>Select a template to see details</Text>
)}
) : null}
{/* Show action description when actions panel is focused */}
{focusedPanel === 'actions' && currentTemplate && currentActions.length > 0 ? (
<Box marginTop={1} flexDirection='column'>
{(() => {
const action = currentActions[selectedActionIndex];
if (!action) return null;
const actionDef = currentTemplate.template.actions?.[action.action];
const roleDef = currentTemplate.template.roles?.[action.role];
// if (!actionDef || !roleDef) return null;
const [_roleName, role] = Object.entries(actionDef?.roles ?? {}).find(([roleId, role]) => roleId === action.role) || [];
console.log(JSON.stringify(role, null, 2));
const variableKeys = role?.requirements?.variables || []
console.log('variables', variableKeys);
return (
<>
<Text color={colors.text} bold>
{actionDef?.name || action.action}
</Text>
<Text color={colors.textMuted}>
{actionDef?.description || 'No description available'}
</Text>
<Box marginTop={1} flexDirection='column'>
<Text color={colors.text}>
Role: {roleDef?.name || action.role}
</Text>
{roleDef?.description && (
<Text color={colors.textMuted}>
{' '}{roleDef.description}
</Text>
)}
</Box>
{/* Display variables if available */}
{
variableKeys.length > 0 && (
<Box marginTop={1} flexDirection='column'>
<Text color={colors.text}>Variables:</Text>
{variableKeys.map((variableKey) => (
<Text key={variableKey} color={colors.text}>
- {currentTemplate.template.variables?.[variableKey]?.name || variableKey}: {currentTemplate.template.variables?.[variableKey]?.description || 'No description'}
</Text>
))}
</Box>
)
}
</>
);
})()}
</Box>
) : focusedPanel === 'actions' && !currentTemplate ? (
<Text color={colors.textMuted}>Select a template first</Text>
) : focusedPanel === 'actions' && currentActions.length === 0 ? (
<Text color={colors.textMuted}>No starting actions available</Text>
) : null}
</Box>
</Box>