mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-10 01:50:20 -07:00
feat(cli,core): implement interactive Team Creator Wizard and persistence
- Add interactive 5-step TeamCreatorWizard with agent selection and objective editor - Implement /team slash command for team selection and creation - Add colorized Nerd Font logos and scroll support for agent roster selection - Refactor teamScaffolder to generate standalone .md files for all agents - Implement persistent activeTeam setting in workspace configuration - Add ActiveTeamChanged event to force immediate system instruction refresh - Support external editor (Ctrl+X) for team objective instructions - Enhance keyboard navigation with Tab/Shift+Tab field switching in wizard - Fix team selection logic to correctly pivot to creation wizard - Update various UI components and tests for team state management
This commit is contained in:
@@ -0,0 +1,691 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type React from 'react';
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import { Box, Text , useStdin } from 'ink';
|
||||
import {
|
||||
type ScaffoldTeamAgent,
|
||||
scaffoldTeam,
|
||||
type EditorType,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { TextInput } from './shared/TextInput.js';
|
||||
import { useTextBuffer } from './shared/text-buffer.js';
|
||||
import { BaseSelectionList } from './shared/BaseSelectionList.js';
|
||||
import { DialogFooter } from './shared/DialogFooter.js';
|
||||
import { useConfig } from '../contexts/ConfigContext.js';
|
||||
import { useUIActions } from '../contexts/UIActionsContext.js';
|
||||
import { useKeypress, type Key } from '../hooks/useKeypress.js';
|
||||
import { Command } from '../key/keyMatchers.js';
|
||||
import { useKeyMatchers } from '../hooks/useKeyMatchers.js';
|
||||
import { type SelectionListItem } from '../hooks/useSelectionList.js';
|
||||
import { useSettingsStore } from '../contexts/SettingsContext.js';
|
||||
import { formatCommand } from '../key/keybindingUtils.js';
|
||||
|
||||
interface TeamCreatorWizardProps {
|
||||
onComplete: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
type WizardStep =
|
||||
| 'identity'
|
||||
| 'roster'
|
||||
| 'objective'
|
||||
| 'confirmation'
|
||||
| 'success';
|
||||
|
||||
const EXTERNAL_TEMPLATES: RosterListItem[] = [
|
||||
{
|
||||
name: 'claude-coder',
|
||||
kind: 'external',
|
||||
provider: 'claude-code',
|
||||
description: 'Expert Claude coder focused on direct action.',
|
||||
logo: '',
|
||||
logoColor: '#D7AFFF', // Purple
|
||||
},
|
||||
{
|
||||
name: 'codex-architect',
|
||||
kind: 'external',
|
||||
provider: 'codex',
|
||||
description: 'Specialized code generation and architectural patterns.',
|
||||
logo: '',
|
||||
logoColor: '#87D7D7', // Cyan
|
||||
},
|
||||
{
|
||||
name: 'antigravity-creative',
|
||||
kind: 'external',
|
||||
provider: 'antigravity',
|
||||
description: 'Creative problem solving and unconventional solutions.',
|
||||
logo: '',
|
||||
logoColor: '#FFFFAF', // Yellow
|
||||
},
|
||||
{
|
||||
name: 'gemma-helper',
|
||||
kind: 'external',
|
||||
provider: 'gemma',
|
||||
description: 'Lightweight and efficient general purpose assistant.',
|
||||
logo: '',
|
||||
logoColor: '#87AFFF', // Blue
|
||||
},
|
||||
];
|
||||
|
||||
interface RosterListItem {
|
||||
name: string;
|
||||
kind: 'local' | 'external' | 'meta';
|
||||
provider?: string;
|
||||
description?: string;
|
||||
sourcePath?: string;
|
||||
logo?: string;
|
||||
logoColor?: string;
|
||||
}
|
||||
|
||||
export function TeamCreatorWizard({
|
||||
onComplete,
|
||||
onCancel,
|
||||
}: TeamCreatorWizardProps): React.JSX.Element {
|
||||
const config = useConfig();
|
||||
const { handleTeamSelect } = useUIActions();
|
||||
const keyMatchers = useKeyMatchers();
|
||||
const [step, setStep] = useState<WizardStep>('identity');
|
||||
const [teamName, setTeamName] = useState('');
|
||||
const [displayName, setDisplayName] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [instructions, setInstructions] = useState('');
|
||||
const [selectedAgents, setSelectedAgents] = useState<Set<string>>(new Set());
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [createdPath, setCreatedPath] = useState<string | null>(null);
|
||||
|
||||
const localAgents = useMemo(
|
||||
() =>
|
||||
config
|
||||
?.getAgentRegistry()
|
||||
.getAllDefinitions()
|
||||
.filter((a) => a.kind === 'local') || [],
|
||||
[config],
|
||||
);
|
||||
|
||||
const allAvailableAgents = useMemo((): RosterListItem[] => {
|
||||
const local: RosterListItem[] = localAgents.map((a) => {
|
||||
let logo = ''; // Default
|
||||
let logoColor = theme.text.accent;
|
||||
|
||||
if (a.name === 'codebase-investigator') {
|
||||
logo = '🔍';
|
||||
logoColor = '#87D7D7'; // Cyan
|
||||
} else if (a.name === 'cli-help') {
|
||||
logo = '';
|
||||
logoColor = '#FBBC04'; // Yellow
|
||||
} else if (a.name === 'generalist') {
|
||||
logo = '';
|
||||
logoColor = '#4285F4'; // Blue
|
||||
}
|
||||
|
||||
return {
|
||||
name: a.name,
|
||||
kind: 'local' as const,
|
||||
description: a.description,
|
||||
sourcePath: a.metadata?.filePath,
|
||||
logo: !a.metadata?.filePath ? logo : undefined, // Only for built-ins
|
||||
logoColor: !a.metadata?.filePath ? logoColor : undefined,
|
||||
};
|
||||
});
|
||||
const external: RosterListItem[] = EXTERNAL_TEMPLATES.map((a) => ({
|
||||
name: a.name,
|
||||
kind: 'external' as const,
|
||||
provider: a.provider,
|
||||
description: a.description,
|
||||
logo: a.logo,
|
||||
logoColor: a.logoColor,
|
||||
}));
|
||||
return [...local, ...external];
|
||||
}, [localAgents]);
|
||||
const { settings } = useSettingsStore();
|
||||
const { stdin, setRawMode } = useStdin();
|
||||
const getPreferredEditor = useCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
() => settings.merged.general.preferredEditor as EditorType,
|
||||
[settings.merged.general.preferredEditor],
|
||||
);
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
if (step === 'identity') {
|
||||
if (!teamName.trim()) {
|
||||
setError('Slug is required');
|
||||
return;
|
||||
}
|
||||
if (!displayName.trim()) {
|
||||
setError('Display Name is required');
|
||||
return;
|
||||
}
|
||||
setError(null);
|
||||
setStep('roster');
|
||||
} else if (step === 'roster') {
|
||||
setStep('objective');
|
||||
} else if (step === 'objective') {
|
||||
if (!instructions.trim()) {
|
||||
setError('Objective/Instructions are required');
|
||||
return;
|
||||
}
|
||||
setError(null);
|
||||
setStep('confirmation');
|
||||
}
|
||||
}, [step, teamName, displayName, instructions]);
|
||||
|
||||
const handleBack = useCallback(() => {
|
||||
if (step === 'roster') setStep('identity');
|
||||
else if (step === 'objective') setStep('roster');
|
||||
else if (step === 'confirmation') setStep('objective');
|
||||
else if (step === 'identity') onCancel();
|
||||
else if (step === 'success') onComplete();
|
||||
}, [step, onCancel, onComplete]);
|
||||
|
||||
const handleToggleAgent = (name: string) => {
|
||||
const next = new Set(selectedAgents);
|
||||
if (next.has(name)) next.delete(name);
|
||||
else next.add(name);
|
||||
setSelectedAgents(next);
|
||||
};
|
||||
|
||||
const handleCreate = useCallback(async () => {
|
||||
setIsSubmitting(true);
|
||||
setError(null);
|
||||
try {
|
||||
const agentsToScaffold: ScaffoldTeamAgent[] = allAvailableAgents
|
||||
.filter((a) => selectedAgents.has(a.name) && a.kind !== 'meta')
|
||||
.map((a) => ({
|
||||
name: a.name,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
kind: a.kind as 'local' | 'external',
|
||||
provider: a.provider,
|
||||
description: a.description,
|
||||
sourcePath: a.sourcePath,
|
||||
}));
|
||||
const path = await scaffoldTeam({
|
||||
name: teamName,
|
||||
displayName,
|
||||
description,
|
||||
instructions,
|
||||
agents: agentsToScaffold,
|
||||
targetDir: config.storage.getProjectTeamsDir(),
|
||||
});
|
||||
setCreatedPath(path);
|
||||
await config.getTeamRegistry().reload();
|
||||
setStep('success');
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : String(e));
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [
|
||||
allAvailableAgents,
|
||||
selectedAgents,
|
||||
teamName,
|
||||
displayName,
|
||||
description,
|
||||
instructions,
|
||||
config,
|
||||
]);
|
||||
|
||||
// Identity Step Buffers
|
||||
const nameBuffer = useTextBuffer({
|
||||
initialText: teamName,
|
||||
viewport: { width: 40, height: 1 },
|
||||
onChange: setTeamName,
|
||||
singleLine: true,
|
||||
});
|
||||
const displayNameBuffer = useTextBuffer({
|
||||
initialText: displayName,
|
||||
viewport: { width: 40, height: 1 },
|
||||
onChange: setDisplayName,
|
||||
singleLine: true,
|
||||
});
|
||||
const descBuffer = useTextBuffer({
|
||||
initialText: description,
|
||||
viewport: { width: 80, height: 1 },
|
||||
onChange: setDescription,
|
||||
singleLine: true,
|
||||
});
|
||||
|
||||
// Objective Step Buffer
|
||||
const instructionsBuffer = useTextBuffer({
|
||||
initialText: instructions,
|
||||
viewport: { width: 80, height: 5 },
|
||||
onChange: setInstructions,
|
||||
stdin,
|
||||
setRawMode,
|
||||
getPreferredEditor,
|
||||
});
|
||||
|
||||
const [activeIdentityField, setActiveIdentityField] = useState<0 | 1 | 2>(0);
|
||||
|
||||
const handleIdentityKeyPress = useCallback(
|
||||
(key: Key) => {
|
||||
const isNext =
|
||||
keyMatchers[Command.DIALOG_NAVIGATION_DOWN](key) ||
|
||||
(key.name === 'tab' && !key.shift);
|
||||
const isPrev =
|
||||
keyMatchers[Command.DIALOG_NAVIGATION_UP](key) ||
|
||||
(key.name === 'tab' && key.shift);
|
||||
|
||||
if (isNext) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
setActiveIdentityField(((activeIdentityField + 1) % 3) as 0 | 1 | 2);
|
||||
return true;
|
||||
}
|
||||
if (isPrev) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
setActiveIdentityField(((activeIdentityField + 2) % 3) as 0 | 1 | 2);
|
||||
return true;
|
||||
}
|
||||
if (keyMatchers[Command.ESCAPE](key)) {
|
||||
onCancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[activeIdentityField, keyMatchers, onCancel],
|
||||
);
|
||||
|
||||
// Handle Tab for "Back" and any key for success exit
|
||||
const handleGlobalKeys = useCallback(
|
||||
(key: Key) => {
|
||||
if (step === 'success') {
|
||||
if (key.sequence?.toLowerCase() === 'y') {
|
||||
handleTeamSelect(teamName);
|
||||
}
|
||||
onComplete();
|
||||
return true;
|
||||
}
|
||||
if (key.name === 'tab') {
|
||||
handleBack();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[step, handleBack, onComplete, handleTeamSelect, teamName],
|
||||
);
|
||||
|
||||
useKeypress(handleGlobalKeys, { isActive: true, priority: true });
|
||||
|
||||
useKeypress(handleIdentityKeyPress, {
|
||||
isActive: step === 'identity',
|
||||
priority: true,
|
||||
});
|
||||
|
||||
const handleObjectiveKeyPress = useCallback(
|
||||
(key: Key) => {
|
||||
if (keyMatchers[Command.OPEN_EXTERNAL_EDITOR](key)) {
|
||||
void instructionsBuffer.openInExternalEditor();
|
||||
return true;
|
||||
}
|
||||
if (keyMatchers[Command.ESCAPE](key)) {
|
||||
onCancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[instructionsBuffer, keyMatchers, onCancel],
|
||||
);
|
||||
|
||||
useKeypress(handleObjectiveKeyPress, {
|
||||
isActive: step === 'objective',
|
||||
priority: true,
|
||||
});
|
||||
|
||||
const handleConfirmationKeyPress = useCallback(
|
||||
(key: Key) => {
|
||||
if (keyMatchers[Command.RETURN](key)) {
|
||||
void handleCreate();
|
||||
return true;
|
||||
}
|
||||
if (keyMatchers[Command.ESCAPE](key)) {
|
||||
onCancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[handleCreate, keyMatchers, onCancel],
|
||||
);
|
||||
|
||||
useKeypress(handleConfirmationKeyPress, {
|
||||
isActive: step === 'confirmation' && !isSubmitting,
|
||||
priority: true,
|
||||
});
|
||||
|
||||
if (step === 'identity') {
|
||||
return (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={theme.border.default}
|
||||
padding={1}
|
||||
width="100%"
|
||||
>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Step 1: Team Identity
|
||||
</Text>
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
<Text color={theme.text.secondary}>
|
||||
Slug Name (e.g., coding-team):
|
||||
</Text>
|
||||
<TextInput
|
||||
buffer={nameBuffer}
|
||||
focus={activeIdentityField === 0}
|
||||
onSubmit={() => setActiveIdentityField(1)}
|
||||
/>
|
||||
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
<Text color={theme.text.secondary}>
|
||||
Display Name (e.g., The Coding Team):
|
||||
</Text>
|
||||
<TextInput
|
||||
buffer={displayNameBuffer}
|
||||
focus={activeIdentityField === 1}
|
||||
onSubmit={() => setActiveIdentityField(2)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
<Text color={theme.text.secondary}>Short Description:</Text>
|
||||
<TextInput
|
||||
buffer={descBuffer}
|
||||
focus={activeIdentityField === 2}
|
||||
onSubmit={handleNext}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<DialogFooter
|
||||
primaryAction="Enter to continue"
|
||||
navigationActions="Tab or ↑/↓ to switch fields"
|
||||
cancelAction="Esc to cancel"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (step === 'roster') {
|
||||
const rosterItems: Array<SelectionListItem<RosterListItem>> =
|
||||
allAvailableAgents.map((a) => ({
|
||||
key: a.name,
|
||||
value: a,
|
||||
}));
|
||||
rosterItems.push({
|
||||
key: 'done',
|
||||
value: { name: 'done', kind: 'meta' as const },
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={theme.border.default}
|
||||
padding={1}
|
||||
width="100%"
|
||||
>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Step 2: Team Roster
|
||||
</Text>
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.secondary}>
|
||||
Select agents to include in your team.
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<BaseSelectionList<RosterListItem>
|
||||
items={rosterItems}
|
||||
onSelect={(item) => {
|
||||
if (item.name === 'done') handleNext();
|
||||
else handleToggleAgent(item.name);
|
||||
}}
|
||||
maxItemsToShow={10}
|
||||
showScrollArrows={true}
|
||||
renderItem={(item, context) => {
|
||||
const value = item.value;
|
||||
if (value.name === 'done') {
|
||||
return (
|
||||
<Text
|
||||
bold
|
||||
color={
|
||||
context.isSelected
|
||||
? theme.text.accent
|
||||
: theme.text.primary
|
||||
}
|
||||
>
|
||||
[ Done - Continue to Objective ]
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
const isChecked = selectedAgents.has(value.name);
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Box flexDirection="row">
|
||||
<Text
|
||||
color={
|
||||
isChecked ? theme.status.success : theme.text.secondary
|
||||
}
|
||||
>
|
||||
[{isChecked ? 'x' : ' '}]
|
||||
</Text>
|
||||
<Text
|
||||
color={
|
||||
context.isSelected
|
||||
? theme.text.accent
|
||||
: theme.text.primary
|
||||
}
|
||||
>
|
||||
{' '}
|
||||
{value.logo && (
|
||||
<Text color={value.logoColor || theme.text.accent}>
|
||||
{value.logo}{' '}
|
||||
</Text>
|
||||
)}
|
||||
{value.name}
|
||||
</Text>
|
||||
{value.kind === 'external' ? (
|
||||
<Text color={theme.text.accent} bold>
|
||||
{' '}
|
||||
[EXTERNAL]
|
||||
</Text>
|
||||
) : !value.sourcePath ? (
|
||||
<Text color={theme.ui.comment} italic>
|
||||
{' '}
|
||||
[BUILT-IN]
|
||||
</Text>
|
||||
) : null}
|
||||
<Text color={theme.text.secondary}>
|
||||
{' '}
|
||||
({value.kind}
|
||||
{value.provider ? `: ${value.provider}` : ''})
|
||||
</Text>
|
||||
</Box>
|
||||
{value.description && (
|
||||
<Text color={theme.text.secondary} italic>
|
||||
{' '}
|
||||
{value.description}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<DialogFooter
|
||||
primaryAction="Enter to toggle/confirm"
|
||||
navigationActions="Tab to go back"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (step === 'objective') {
|
||||
return (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={theme.border.default}
|
||||
padding={1}
|
||||
width="100%"
|
||||
>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Step 3: Team Objective
|
||||
</Text>
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
<Text color={theme.text.secondary}>
|
||||
Define what this team does and how it should work.
|
||||
</Text>
|
||||
{selectedAgents.size > 0 && (
|
||||
<Box marginTop={1} flexDirection="row" flexWrap="wrap">
|
||||
<Text color={theme.ui.comment}>Referencing agents: </Text>
|
||||
{Array.from(selectedAgents).map((name, i) => {
|
||||
const agent = allAvailableAgents.find((a) => a.name === name);
|
||||
return (
|
||||
<Box key={name} flexDirection="row">
|
||||
{agent?.logo && (
|
||||
<Text color={agent.logoColor || theme.text.accent}>
|
||||
{agent.logo}{' '}
|
||||
</Text>
|
||||
)}
|
||||
<Text color={theme.ui.comment}>
|
||||
@{name}
|
||||
{i < selectedAgents.size - 1 ? ', ' : ''}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
<Box
|
||||
marginTop={1}
|
||||
borderStyle="single"
|
||||
borderColor={theme.ui.comment}
|
||||
paddingX={1}
|
||||
>
|
||||
<TextInput buffer={instructionsBuffer} onSubmit={handleNext} />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{error && (
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.status.error}>Error: {error}</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<DialogFooter
|
||||
primaryAction="Enter to continue"
|
||||
navigationActions={`Tab to go back · ${formatCommand(Command.OPEN_EXTERNAL_EDITOR)} for full editor`}
|
||||
cancelAction="Esc to cancel"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (step === 'confirmation') {
|
||||
return (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={theme.border.default}
|
||||
padding={1}
|
||||
width="100%"
|
||||
>
|
||||
<Text bold color={theme.text.primary}>
|
||||
Step 4: Confirm Team Creation
|
||||
</Text>
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
<Text color={theme.text.secondary}>
|
||||
Name:{' '}
|
||||
<Text color={theme.text.primary}>
|
||||
{displayName} ({teamName})
|
||||
</Text>
|
||||
</Text>
|
||||
<Text color={theme.text.secondary}>
|
||||
Description: <Text color={theme.text.primary}>{description}</Text>
|
||||
</Text>
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.secondary}>Roster:</Text>
|
||||
</Box>
|
||||
{Array.from(selectedAgents).map((name) => {
|
||||
const agent = allAvailableAgents.find((a) => a.name === name);
|
||||
return (
|
||||
<Box key={name} flexDirection="row">
|
||||
<Text color={theme.text.primary}>- </Text>
|
||||
{agent?.logo && (
|
||||
<Text color={agent.logoColor || theme.text.accent}>
|
||||
{agent.logo}{' '}
|
||||
</Text>
|
||||
)}
|
||||
<Text color={theme.text.primary}>{name}</Text>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
{selectedAgents.size === 0 && (
|
||||
<Text color={theme.status.warning} italic>
|
||||
No agents selected (Gemini CLI expert will be used by default)
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{error && (
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.status.error}>Error: {error}</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box marginTop={1}>
|
||||
{isSubmitting ? (
|
||||
<Text color={theme.text.accent}>Creating team...</Text>
|
||||
) : (
|
||||
<DialogFooter
|
||||
primaryAction="Enter to create team"
|
||||
navigationActions="Tab to go back"
|
||||
cancelAction="Esc to cancel"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (step === 'success') {
|
||||
return (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={theme.status.success}
|
||||
padding={1}
|
||||
width="100%"
|
||||
>
|
||||
<Text bold color={theme.status.success}>
|
||||
Team Created Successfully!
|
||||
</Text>
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
<Text color={theme.text.primary}>
|
||||
Your new team "{displayName}" is now ready to use.
|
||||
</Text>
|
||||
{createdPath && (
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.secondary}>Created at: </Text>
|
||||
<Text color={theme.text.primary}>{createdPath}</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.accent}>
|
||||
Would you like to switch to this team now? (y/N)
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.secondary}>
|
||||
Press any other key to finish...
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return <Box />;
|
||||
}
|
||||
Reference in New Issue
Block a user