-
Notifications
You must be signed in to change notification settings - Fork 93
Expand file tree
/
Copy pathCompletionStep.tsx
More file actions
164 lines (145 loc) · 4.67 KB
/
CompletionStep.tsx
File metadata and controls
164 lines (145 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
* @license
* Copyright 2025 Vybestack LLC
* SPDX-License-Identifier: Apache-2.0
*/
import React, { useState, useCallback } from 'react';
import { Box, Text } from 'ink';
import { Colors } from '../../colors.js';
import { useKeypress } from '../../hooks/useKeypress.js';
interface CompletionStepProps {
provider: string;
model?: string;
authMethod: 'oauth' | 'api_key';
onSaveProfile: (name: string) => Promise<void>;
onDismiss: () => void;
isFocused?: boolean;
}
export const CompletionStep: React.FC<CompletionStepProps> = ({
provider,
model,
authMethod,
onSaveProfile,
onDismiss,
isFocused = true,
}) => {
const [showProfilePrompt, setShowProfilePrompt] = useState(true);
const [profileName, setProfileName] = useState('');
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | undefined>();
const providerDisplay = provider.charAt(0).toUpperCase() + provider.slice(1);
const authDisplay = authMethod === 'oauth' ? 'OAuth' : 'API Key';
const handleProfileSubmit = useCallback(async () => {
const trimmedName = profileName.trim();
if (!trimmedName) {
setError('Profile name is required');
return;
}
setSaving(true);
setError(undefined);
try {
await onSaveProfile(trimmedName);
setShowProfilePrompt(false);
} catch (err: unknown) {
setError(err instanceof Error ? err.message : 'Failed to save profile');
setSaving(false);
}
}, [profileName, onSaveProfile]);
// Handle keyboard input
useKeypress(
(key) => {
if (key.name === 'return') {
if (showProfilePrompt && !saving) {
handleProfileSubmit();
} else if (!showProfilePrompt) {
onDismiss();
}
return;
}
// Only accept input in profile prompt mode
if (!showProfilePrompt || saving) {
return;
}
if (key.name === 'backspace' || key.name === 'delete') {
setProfileName((prev) => prev.slice(0, -1));
return;
}
// Accept printable characters (including paste - multi-char sequences)
const char = key.sequence;
if (char && !key.ctrl && !key.meta) {
// Filter to only printable ASCII characters
const printable = char.replace(/[^\x20-\x7E]/g, '');
if (printable) {
setProfileName((prev) => prev + printable);
}
}
},
{ isActive: isFocused },
);
return (
<Box flexDirection="column">
<Box flexDirection="column" marginBottom={1}>
<Text bold color={Colors.AccentCyan}>
Step 5 of 5: Save Your Profile
</Text>
<Text> </Text>
<Text bold color={Colors.AccentGreen}>
{'✓ Authentication complete!'}
</Text>
</Box>
<Box flexDirection="column" marginBottom={1}>
<Text color={Colors.Foreground}>Provider: {providerDisplay}</Text>
{model && <Text color={Colors.Foreground}>Model: {model}</Text>}
<Text color={Colors.Foreground}>Authentication: {authDisplay}</Text>
</Box>
{showProfilePrompt ? (
<Box flexDirection="column">
<Box marginBottom={1}>
<Text bold color={Colors.Foreground}>
Save this setup as a profile
</Text>
</Box>
<Text color={Colors.Gray}>
This profile will be loaded automatically on startup.
</Text>
<Text color={Colors.Gray}>
Use /profile load <name> to switch profiles later.
</Text>
<Box marginTop={1} flexDirection="column">
{error && (
<Box marginBottom={1}>
<Text color={Colors.AccentRed}>{error}</Text>
</Box>
)}
{saving ? (
<Text color={Colors.Foreground}>Saving profile...</Text>
) : (
<Box>
<Text color={Colors.Foreground}>Profile name: </Text>
<Text>{profileName}</Text>
<Text color={Colors.AccentCyan}>▌</Text>
</Box>
)}
</Box>
<Box marginTop={1}>
<Text color={Colors.Gray}>
Enter a name and press Enter to save
</Text>
</Box>
</Box>
) : (
<Box flexDirection="column">
<Box marginBottom={1}>
<Text color={Colors.Foreground}>Try asking me something like:</Text>
<Text color={Colors.AccentCyan}>
{'"Explain how async/await works in JavaScript"'}
</Text>
</Box>
<Box marginTop={1}>
<Text color={Colors.Gray}>Press Enter to continue...</Text>
</Box>
</Box>
)}
</Box>
);
};