121 lines
3.7 KiB
TypeScript
121 lines
3.7 KiB
TypeScript
/**
|
|
* Role Selection Step - Allows the user to choose which role they want
|
|
* to take for the selected action.
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { Box, Text } from 'ink';
|
|
import { colors } from '../../../theme.js';
|
|
import type { XOTemplate } from '@xo-cash/types';
|
|
import type { FocusArea } from '../types.js';
|
|
|
|
interface RoleSelectStepProps {
|
|
/** The loaded template definition. */
|
|
template: XOTemplate;
|
|
/** The selected action identifier. */
|
|
actionIdentifier: string;
|
|
/** Role identifiers available for this action. */
|
|
availableRoles: string[];
|
|
/** The currently focused role index. */
|
|
selectedRoleIndex: number;
|
|
/** Whether the content area or button bar is focused. */
|
|
focusArea: FocusArea;
|
|
}
|
|
|
|
/**
|
|
* Displays the available roles for the selected action and
|
|
* lets the user navigate between them with arrow keys.
|
|
*/
|
|
export function RoleSelectStep({
|
|
template,
|
|
actionIdentifier,
|
|
availableRoles,
|
|
selectedRoleIndex,
|
|
focusArea,
|
|
}: RoleSelectStepProps): React.ReactElement {
|
|
const action = template.actions?.[actionIdentifier];
|
|
|
|
return (
|
|
<Box flexDirection="column">
|
|
<Text color={colors.text} bold>
|
|
Select your role for this action:
|
|
</Text>
|
|
|
|
{/* Action info */}
|
|
{action && (
|
|
<Box marginTop={1} flexDirection="column">
|
|
<Text color={colors.textMuted}>
|
|
{action.description || 'No description available'}
|
|
</Text>
|
|
</Box>
|
|
)}
|
|
|
|
{/* Role list */}
|
|
<Box
|
|
marginTop={1}
|
|
flexDirection="column"
|
|
borderStyle="single"
|
|
borderColor={colors.border}
|
|
paddingX={1}
|
|
>
|
|
{availableRoles.length === 0 ? (
|
|
<Text color={colors.textMuted}>No roles available</Text>
|
|
) : (
|
|
availableRoles.map((roleId, index) => {
|
|
const isCursor =
|
|
selectedRoleIndex === index && focusArea === 'content';
|
|
const roleDef = template.roles?.[roleId];
|
|
const actionRole = action?.roles?.[roleId];
|
|
const requirements = actionRole?.requirements;
|
|
|
|
return (
|
|
<Box key={roleId} flexDirection="column" marginY={0}>
|
|
<Text
|
|
color={isCursor ? colors.focus : colors.text}
|
|
bold={isCursor}
|
|
>
|
|
{isCursor ? '▸ ' : ' '}
|
|
{roleDef?.name || roleId}
|
|
</Text>
|
|
|
|
{/* Show role description indented below the name */}
|
|
{roleDef?.description && (
|
|
<Text color={colors.textMuted}>
|
|
{' '}
|
|
{roleDef.description}
|
|
</Text>
|
|
)}
|
|
|
|
{/* Show a brief summary of requirements */}
|
|
{requirements && (
|
|
<Box flexDirection="row" paddingLeft={4}>
|
|
{requirements.variables && requirements.variables.length > 0 && (
|
|
<Text color={colors.textMuted} dimColor>
|
|
{requirements.variables.length} variable
|
|
{requirements.variables.length !== 1 ? 's' : ''}
|
|
{' '}
|
|
</Text>
|
|
)}
|
|
{requirements.slots && requirements.slots.min > 0 && (
|
|
<Text color={colors.textMuted} dimColor>
|
|
{requirements.slots.min} input slot
|
|
{requirements.slots.min !== 1 ? 's' : ''}
|
|
</Text>
|
|
)}
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
})
|
|
)}
|
|
</Box>
|
|
|
|
<Box marginTop={1}>
|
|
<Text color={colors.textMuted} dimColor>
|
|
↑↓: Navigate • Next: Confirm selection
|
|
</Text>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
}
|