snow-ai 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/api/chat.d.ts +29 -0
  2. package/dist/api/chat.js +88 -0
  3. package/dist/api/models.d.ts +12 -0
  4. package/dist/api/models.js +40 -0
  5. package/dist/app.d.ts +6 -0
  6. package/dist/app.js +47 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +19 -0
  9. package/dist/constants/index.d.ts +18 -0
  10. package/dist/constants/index.js +18 -0
  11. package/dist/hooks/useGlobalExit.d.ts +5 -0
  12. package/dist/hooks/useGlobalExit.js +32 -0
  13. package/dist/types/index.d.ts +15 -0
  14. package/dist/types/index.js +1 -0
  15. package/dist/ui/components/ChatInput.d.ts +9 -0
  16. package/dist/ui/components/ChatInput.js +206 -0
  17. package/dist/ui/components/CommandPanel.d.ts +13 -0
  18. package/dist/ui/components/CommandPanel.js +22 -0
  19. package/dist/ui/components/Menu.d.ts +14 -0
  20. package/dist/ui/components/Menu.js +32 -0
  21. package/dist/ui/components/MessageList.d.ts +15 -0
  22. package/dist/ui/components/MessageList.js +16 -0
  23. package/dist/ui/components/PendingMessages.d.ts +6 -0
  24. package/dist/ui/components/PendingMessages.js +19 -0
  25. package/dist/ui/pages/ApiConfigScreen.d.ts +7 -0
  26. package/dist/ui/pages/ApiConfigScreen.js +126 -0
  27. package/dist/ui/pages/ChatScreen.d.ts +5 -0
  28. package/dist/ui/pages/ChatScreen.js +287 -0
  29. package/dist/ui/pages/ModelConfigScreen.d.ts +7 -0
  30. package/dist/ui/pages/ModelConfigScreen.js +239 -0
  31. package/dist/ui/pages/WelcomeScreen.d.ts +7 -0
  32. package/dist/ui/pages/WelcomeScreen.js +48 -0
  33. package/dist/utils/apiConfig.d.ts +17 -0
  34. package/dist/utils/apiConfig.js +86 -0
  35. package/dist/utils/commandExecutor.d.ts +11 -0
  36. package/dist/utils/commandExecutor.js +26 -0
  37. package/dist/utils/commands/clear.d.ts +2 -0
  38. package/dist/utils/commands/clear.js +12 -0
  39. package/dist/utils/index.d.ts +7 -0
  40. package/dist/utils/index.js +12 -0
  41. package/dist/utils/textBuffer.d.ts +52 -0
  42. package/dist/utils/textBuffer.js +310 -0
  43. package/dist/utils/textUtils.d.ts +33 -0
  44. package/dist/utils/textUtils.js +83 -0
  45. package/package.json +86 -0
  46. package/readme.md +9 -0
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ export default function MessageList({ messages, animationFrame, maxMessages = 6 }) {
4
+ if (messages.length === 0) {
5
+ return null;
6
+ }
7
+ return (React.createElement(Box, { marginBottom: 1, flexDirection: "column", paddingX: 1, paddingY: 1 }, messages.slice(-maxMessages).map((message, index) => (React.createElement(Box, { key: index, marginLeft: 1 },
8
+ React.createElement(Text, { color: message.role === 'user' ? 'blue' :
9
+ message.role === 'command' ? 'gray' :
10
+ message.streaming ? ['#FF6EBF', 'green', 'blue', 'cyan', '#B588F8'][animationFrame] : 'cyan', bold: true }, message.role === 'user' ? '⛇' : message.role === 'command' ? '⌘' : '❆'),
11
+ React.createElement(Box, { marginLeft: 1, marginBottom: 1, flexDirection: "column" }, message.role === 'command' ? (React.createElement(Text, { color: "gray" },
12
+ "\u2514\u2500 ",
13
+ message.commandName)) : (React.createElement(React.Fragment, null,
14
+ React.createElement(Text, { color: message.role === 'user' ? 'gray' : '' }, message.content),
15
+ message.discontinued && (React.createElement(Text, { color: "red", bold: true }, "\u2514\u2500 user discontinue"))))))))));
16
+ }
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface Props {
3
+ pendingMessages: string[];
4
+ }
5
+ export default function PendingMessages({ pendingMessages }: Props): React.JSX.Element | null;
6
+ export {};
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ export default function PendingMessages({ pendingMessages }) {
4
+ if (pendingMessages.length === 0) {
5
+ return null;
6
+ }
7
+ return (React.createElement(Box, { marginBottom: 1, flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1 },
8
+ React.createElement(Text, { color: "yellow", bold: true },
9
+ "\u2B11 Pending Messages (",
10
+ pendingMessages.length,
11
+ ")"),
12
+ pendingMessages.map((message, index) => (React.createElement(Box, { key: index, marginLeft: 1, marginY: 0 },
13
+ React.createElement(Text, { color: "blue", bold: true },
14
+ index + 1,
15
+ "."),
16
+ React.createElement(Box, { marginLeft: 1 },
17
+ React.createElement(Text, { color: "gray" }, message.length > 60 ? `${message.substring(0, 60)}...` : message))))),
18
+ React.createElement(Text, { color: "yellow", dimColor: true }, "Will be sent when AI finishes responding")));
19
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ onBack: () => void;
4
+ onSave: () => void;
5
+ };
6
+ export default function ApiConfigScreen({ onBack, onSave }: Props): React.JSX.Element;
7
+ export {};
@@ -0,0 +1,126 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ import Gradient from 'ink-gradient';
4
+ import { Select, Alert } from '@inkjs/ui';
5
+ import TextInput from 'ink-text-input';
6
+ import { getOpenAiConfig, updateOpenAiConfig, validateApiConfig, } from '../../utils/apiConfig.js';
7
+ export default function ApiConfigScreen({ onBack, onSave }) {
8
+ const [baseUrl, setBaseUrl] = useState('');
9
+ const [apiKey, setApiKey] = useState('');
10
+ const [requestMethod, setRequestMethod] = useState('chat');
11
+ const [currentField, setCurrentField] = useState('baseUrl');
12
+ const [errors, setErrors] = useState([]);
13
+ const [isEditing, setIsEditing] = useState(false);
14
+ const requestMethodOptions = [
15
+ {
16
+ label: 'Chat Completions - Modern chat API (GPT-4, GPT-3.5-turbo)',
17
+ value: 'chat',
18
+ },
19
+ {
20
+ label: 'Responses - New responses API (2025, with built-in tools)',
21
+ value: 'responses',
22
+ },
23
+ ];
24
+ useEffect(() => {
25
+ const config = getOpenAiConfig();
26
+ setBaseUrl(config.baseUrl);
27
+ setApiKey(config.apiKey);
28
+ setRequestMethod(config.requestMethod || 'chat');
29
+ }, []);
30
+ useInput((input, key) => {
31
+ // Don't handle input when Select component is active
32
+ if (isEditing && currentField === 'requestMethod') {
33
+ return;
34
+ }
35
+ // Handle save/exit globally
36
+ if (input === 's' && (key.ctrl || key.meta)) {
37
+ const validationErrors = validateApiConfig({ baseUrl, apiKey, requestMethod });
38
+ if (validationErrors.length === 0) {
39
+ updateOpenAiConfig({ baseUrl, apiKey, requestMethod });
40
+ setErrors([]);
41
+ onSave();
42
+ }
43
+ else {
44
+ setErrors(validationErrors);
45
+ }
46
+ }
47
+ else if (key.escape) {
48
+ const validationErrors = validateApiConfig({ baseUrl, apiKey, requestMethod });
49
+ if (validationErrors.length === 0) {
50
+ updateOpenAiConfig({ baseUrl, apiKey, requestMethod });
51
+ setErrors([]);
52
+ }
53
+ onBack();
54
+ }
55
+ else if (key.return) {
56
+ if (isEditing) {
57
+ // Exit edit mode, return to navigation
58
+ setIsEditing(false);
59
+ }
60
+ else {
61
+ // Enter edit mode for current field
62
+ setIsEditing(true);
63
+ }
64
+ }
65
+ else if (!isEditing && key.upArrow) {
66
+ if (currentField === 'apiKey') {
67
+ setCurrentField('baseUrl');
68
+ }
69
+ else if (currentField === 'requestMethod') {
70
+ setCurrentField('apiKey');
71
+ }
72
+ }
73
+ else if (!isEditing && key.downArrow) {
74
+ if (currentField === 'baseUrl') {
75
+ setCurrentField('apiKey');
76
+ }
77
+ else if (currentField === 'apiKey') {
78
+ setCurrentField('requestMethod');
79
+ }
80
+ }
81
+ });
82
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
83
+ React.createElement(Box, { marginBottom: 2, borderStyle: "double", borderColor: "cyan", paddingX: 2, paddingY: 1 },
84
+ React.createElement(Box, { flexDirection: "column" },
85
+ React.createElement(Gradient, { name: "rainbow" }, "OpenAI API Configuration"),
86
+ React.createElement(Text, { color: "gray", dimColor: true }, "Configure your OpenAI API settings"))),
87
+ React.createElement(Box, { flexDirection: "column", marginBottom: 2 },
88
+ React.createElement(Box, { marginBottom: 1 },
89
+ React.createElement(Box, { flexDirection: "column" },
90
+ React.createElement(Text, { color: currentField === 'baseUrl' ? 'green' : 'white' },
91
+ currentField === 'baseUrl' ? '➣ ' : ' ',
92
+ "Base URL:"),
93
+ currentField === 'baseUrl' && isEditing && (React.createElement(Box, { marginLeft: 3 },
94
+ React.createElement(TextInput, { value: baseUrl, onChange: setBaseUrl, placeholder: "https://api.openai.com/v1" }))),
95
+ (!isEditing || currentField !== 'baseUrl') && (React.createElement(Box, { marginLeft: 3 },
96
+ React.createElement(Text, { color: "gray" }, baseUrl || 'Not set'))))),
97
+ React.createElement(Box, { marginBottom: 1 },
98
+ React.createElement(Box, { flexDirection: "column" },
99
+ React.createElement(Text, { color: currentField === 'apiKey' ? 'green' : 'white' },
100
+ currentField === 'apiKey' ? '➣ ' : ' ',
101
+ "API Key:"),
102
+ currentField === 'apiKey' && isEditing && (React.createElement(Box, { marginLeft: 3 },
103
+ React.createElement(TextInput, { value: apiKey, onChange: setApiKey, placeholder: "sk-...", mask: "*" }))),
104
+ (!isEditing || currentField !== 'apiKey') && (React.createElement(Box, { marginLeft: 3 },
105
+ React.createElement(Text, { color: "gray" }, apiKey ? '*'.repeat(Math.min(apiKey.length, 20)) : 'Not set'))))),
106
+ React.createElement(Box, { marginBottom: 1 },
107
+ React.createElement(Box, { flexDirection: "column" },
108
+ React.createElement(Text, { color: currentField === 'requestMethod' ? 'green' : 'white' },
109
+ currentField === 'requestMethod' ? '➣ ' : ' ',
110
+ "Request Method:"),
111
+ currentField === 'requestMethod' && isEditing && (React.createElement(Box, { marginLeft: 3 },
112
+ React.createElement(Select, { options: requestMethodOptions, defaultValue: requestMethod, onChange: (value) => {
113
+ setRequestMethod(value);
114
+ setIsEditing(false); // Auto exit edit mode after selection
115
+ } }))),
116
+ (!isEditing || currentField !== 'requestMethod') && (React.createElement(Box, { marginLeft: 3 },
117
+ React.createElement(Text, { color: "gray" }, requestMethodOptions.find(opt => opt.value === requestMethod)?.label || 'Not set')))))),
118
+ errors.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 2 },
119
+ React.createElement(Text, { color: "red", bold: true }, "Errors:"),
120
+ errors.map((error, index) => (React.createElement(Text, { key: index, color: "red" },
121
+ "\u2022 ",
122
+ error))))),
123
+ React.createElement(Box, { flexDirection: "column" }, isEditing ? (React.createElement(React.Fragment, null,
124
+ React.createElement(Alert, { variant: "info" }, "Editing mode: Press Enter to save and exit editing (Make your changes and press Enter when done)"))) : (React.createElement(React.Fragment, null,
125
+ React.createElement(Alert, { variant: "info" }, "Use \u2191\u2193 to navigate between fields, press Enter to edit, and press Ctrl+S or Esc to save and return"))))));
126
+ }
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import '../../utils/commands/clear.js';
3
+ type Props = {};
4
+ export default function ChatScreen({}: Props): React.JSX.Element;
5
+ export {};
@@ -0,0 +1,287 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ import Gradient from 'ink-gradient';
4
+ import ChatInput from '../components/ChatInput.js';
5
+ import MessageList from '../components/MessageList.js';
6
+ import PendingMessages from '../components/PendingMessages.js';
7
+ import { createStreamingChatCompletion } from '../../api/chat.js';
8
+ import { getOpenAiConfig } from '../../utils/apiConfig.js';
9
+ // Import clear command to register it
10
+ import '../../utils/commands/clear.js';
11
+ export default function ChatScreen({}) {
12
+ const [messages, setMessages] = useState([]);
13
+ const [isStreaming, setIsStreaming] = useState(false);
14
+ const [animationFrame, setAnimationFrame] = useState(0);
15
+ const [abortController, setAbortController] = useState(null);
16
+ const [pendingMessages, setPendingMessages] = useState([]);
17
+ // Animation for streaming indicator
18
+ useEffect(() => {
19
+ if (!isStreaming)
20
+ return;
21
+ const interval = setInterval(() => {
22
+ setAnimationFrame(prev => (prev + 1) % 5);
23
+ }, 300);
24
+ return () => clearInterval(interval);
25
+ }, [isStreaming]);
26
+ // Auto-send pending messages when streaming stops
27
+ useEffect(() => {
28
+ if (!isStreaming && pendingMessages.length > 0) {
29
+ // Use setTimeout to ensure state updates are complete
30
+ const timer = setTimeout(() => {
31
+ processPendingMessages();
32
+ }, 100);
33
+ return () => clearTimeout(timer);
34
+ }
35
+ return undefined;
36
+ }, [isStreaming, pendingMessages.length]);
37
+ // ESC key handler to interrupt streaming
38
+ useInput((_, key) => {
39
+ if (key.escape && isStreaming && abortController) {
40
+ abortController.abort();
41
+ setMessages(prev => {
42
+ const newMessages = [...prev];
43
+ const lastMessage = newMessages[newMessages.length - 1];
44
+ if (lastMessage && lastMessage.streaming) {
45
+ lastMessage.streaming = false;
46
+ lastMessage.discontinued = true;
47
+ }
48
+ return newMessages;
49
+ });
50
+ // Reset streaming state, useEffect will handle pending messages
51
+ setIsStreaming(false);
52
+ setAbortController(null);
53
+ }
54
+ });
55
+ const handleCommandExecution = (commandName, result) => {
56
+ if (result.success && result.action === 'clear') {
57
+ // Clear all messages
58
+ setMessages([]);
59
+ // Add command execution feedback
60
+ const commandMessage = {
61
+ role: 'command',
62
+ content: '',
63
+ commandName: commandName
64
+ };
65
+ setMessages([commandMessage]);
66
+ }
67
+ };
68
+ const handleMessageSubmit = async (message) => {
69
+ // If streaming, add to pending messages instead of sending immediately
70
+ if (isStreaming) {
71
+ setPendingMessages(prev => [...prev, message]);
72
+ return;
73
+ }
74
+ // Process the message normally
75
+ await processMessage(message);
76
+ };
77
+ const processMessage = async (message) => {
78
+ const userMessage = { role: 'user', content: message };
79
+ setMessages(prev => [...prev, userMessage]);
80
+ setIsStreaming(true);
81
+ // Create new abort controller for this request
82
+ const controller = new AbortController();
83
+ setAbortController(controller);
84
+ const assistantMessage = { role: 'assistant', content: '', streaming: true };
85
+ setMessages(prev => [...prev, assistantMessage]);
86
+ try {
87
+ const config = getOpenAiConfig();
88
+ const model = config.advancedModel || 'gpt-4.1';
89
+ // Check if request method is responses (not yet implemented)
90
+ if (config.requestMethod === 'responses') {
91
+ setMessages(prev => {
92
+ const newMessages = [...prev];
93
+ const lastMessage = newMessages[newMessages.length - 1];
94
+ if (lastMessage) {
95
+ lastMessage.content = 'Responses API is not yet implemented. Please use "Chat Completions" method in API settings.';
96
+ lastMessage.streaming = false;
97
+ }
98
+ return newMessages;
99
+ });
100
+ // Don't return here, let it fall through to finally block
101
+ }
102
+ else {
103
+ const chatMessages = [
104
+ { role: 'system', content: 'You are a helpful coding assistant.' },
105
+ ...messages.filter(msg => msg.role !== 'command').map(msg => ({ role: msg.role, content: msg.content })),
106
+ { role: 'user', content: message }
107
+ ];
108
+ let fullResponse = '';
109
+ let currentLine = '';
110
+ for await (const chunk of createStreamingChatCompletion({
111
+ model,
112
+ messages: chatMessages,
113
+ temperature: 0
114
+ }, controller.signal)) {
115
+ if (controller.signal.aborted)
116
+ break;
117
+ currentLine += chunk;
118
+ // Check if we have a complete line (contains newline or certain punctuation)
119
+ if (chunk.includes('\n') || chunk.includes('.') || chunk.includes('!') || chunk.includes('?') || chunk.includes(';')) {
120
+ fullResponse += currentLine;
121
+ currentLine = '';
122
+ setMessages(prev => {
123
+ const newMessages = [...prev];
124
+ const lastMessage = newMessages[newMessages.length - 1];
125
+ if (lastMessage && lastMessage.streaming) {
126
+ lastMessage.content = fullResponse;
127
+ }
128
+ return newMessages;
129
+ });
130
+ }
131
+ }
132
+ // Add any remaining content
133
+ if (currentLine && !controller.signal.aborted) {
134
+ fullResponse += currentLine;
135
+ setMessages(prev => {
136
+ const newMessages = [...prev];
137
+ const lastMessage = newMessages[newMessages.length - 1];
138
+ if (lastMessage && lastMessage.streaming) {
139
+ lastMessage.content = fullResponse;
140
+ }
141
+ return newMessages;
142
+ });
143
+ }
144
+ setMessages(prev => {
145
+ const newMessages = [...prev];
146
+ const lastMessage = newMessages[newMessages.length - 1];
147
+ if (lastMessage && !lastMessage.discontinued) {
148
+ lastMessage.streaming = false;
149
+ }
150
+ return newMessages;
151
+ });
152
+ }
153
+ }
154
+ catch (error) {
155
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
156
+ setMessages(prev => {
157
+ const newMessages = [...prev];
158
+ const lastMessage = newMessages[newMessages.length - 1];
159
+ if (lastMessage) {
160
+ lastMessage.content = `Error: ${errorMessage}`;
161
+ lastMessage.streaming = false;
162
+ }
163
+ return newMessages;
164
+ });
165
+ }
166
+ finally {
167
+ setIsStreaming(false);
168
+ setAbortController(null);
169
+ }
170
+ };
171
+ const processPendingMessages = async () => {
172
+ if (pendingMessages.length === 0)
173
+ return;
174
+ // Get current pending messages and clear them immediately to prevent infinite loop
175
+ const messagesToProcess = [...pendingMessages];
176
+ setPendingMessages([]);
177
+ // Combine multiple pending messages into one
178
+ const combinedMessage = messagesToProcess.join('\n\n');
179
+ // Add user message to chat
180
+ const userMessage = { role: 'user', content: combinedMessage };
181
+ setMessages(prev => [...prev, userMessage]);
182
+ // Start streaming response (without calling processMessage to avoid recursion)
183
+ setIsStreaming(true);
184
+ // Create new abort controller for this request
185
+ const controller = new AbortController();
186
+ setAbortController(controller);
187
+ const assistantMessage = { role: 'assistant', content: '', streaming: true };
188
+ setMessages(prev => [...prev, assistantMessage]);
189
+ try {
190
+ const config = getOpenAiConfig();
191
+ const model = config.advancedModel || 'gpt-4.1';
192
+ // Check if request method is responses (not yet implemented)
193
+ if (config.requestMethod === 'responses') {
194
+ setMessages(prev => {
195
+ const newMessages = [...prev];
196
+ const lastMessage = newMessages[newMessages.length - 1];
197
+ if (lastMessage) {
198
+ lastMessage.content = 'Responses API is not yet implemented. Please use "Chat Completions" method in API settings.';
199
+ lastMessage.streaming = false;
200
+ }
201
+ return newMessages;
202
+ });
203
+ // Don't return here, let it fall through to finally block
204
+ }
205
+ else {
206
+ const chatMessages = [
207
+ { role: 'system', content: 'You are a helpful coding assistant.' },
208
+ ...messages.filter(msg => msg.role !== 'command').map(msg => ({ role: msg.role, content: msg.content })),
209
+ { role: 'user', content: combinedMessage }
210
+ ];
211
+ let fullResponse = '';
212
+ let currentLine = '';
213
+ for await (const chunk of createStreamingChatCompletion({
214
+ model,
215
+ messages: chatMessages,
216
+ temperature: 0
217
+ }, controller.signal)) {
218
+ if (controller.signal.aborted)
219
+ break;
220
+ currentLine += chunk;
221
+ // Check if we have a complete line (contains newline or certain punctuation)
222
+ if (chunk.includes('\n') || chunk.includes('.') || chunk.includes('!') || chunk.includes('?') || chunk.includes(';')) {
223
+ fullResponse += currentLine;
224
+ currentLine = '';
225
+ setMessages(prev => {
226
+ const newMessages = [...prev];
227
+ const lastMessage = newMessages[newMessages.length - 1];
228
+ if (lastMessage && lastMessage.streaming) {
229
+ lastMessage.content = fullResponse;
230
+ }
231
+ return newMessages;
232
+ });
233
+ }
234
+ }
235
+ // Add any remaining content
236
+ if (currentLine && !controller.signal.aborted) {
237
+ fullResponse += currentLine;
238
+ setMessages(prev => {
239
+ const newMessages = [...prev];
240
+ const lastMessage = newMessages[newMessages.length - 1];
241
+ if (lastMessage && lastMessage.streaming) {
242
+ lastMessage.content = fullResponse;
243
+ }
244
+ return newMessages;
245
+ });
246
+ }
247
+ setMessages(prev => {
248
+ const newMessages = [...prev];
249
+ const lastMessage = newMessages[newMessages.length - 1];
250
+ if (lastMessage && !lastMessage.discontinued) {
251
+ lastMessage.streaming = false;
252
+ }
253
+ return newMessages;
254
+ });
255
+ }
256
+ }
257
+ catch (error) {
258
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
259
+ setMessages(prev => {
260
+ const newMessages = [...prev];
261
+ const lastMessage = newMessages[newMessages.length - 1];
262
+ if (lastMessage) {
263
+ lastMessage.content = `Error: ${errorMessage}`;
264
+ lastMessage.streaming = false;
265
+ }
266
+ return newMessages;
267
+ });
268
+ }
269
+ finally {
270
+ setIsStreaming(false);
271
+ setAbortController(null);
272
+ // Note: No recursive call here, useEffect will handle next batch
273
+ }
274
+ };
275
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
276
+ React.createElement(Box, { marginBottom: 1, borderColor: 'cyan', borderStyle: "round", paddingX: 2, paddingY: 1 },
277
+ React.createElement(Box, { flexDirection: "column" },
278
+ React.createElement(Text, { color: "white", bold: true },
279
+ React.createElement(Text, { color: "cyan" }, "\u2746 "),
280
+ React.createElement(Gradient, { name: "rainbow" }, "Programming efficiency x10!")),
281
+ React.createElement(Text, { color: "gray", dimColor: true }, "\u2022 Ask for code explanations and debugging help"),
282
+ React.createElement(Text, { color: "gray", dimColor: true }, "\u2022 Press ESC during response to interrupt"))),
283
+ React.createElement(MessageList, { messages: messages, animationFrame: animationFrame, maxMessages: 6 }),
284
+ React.createElement(PendingMessages, { pendingMessages: pendingMessages }),
285
+ React.createElement(Box, { marginBottom: 0 },
286
+ React.createElement(ChatInput, { onSubmit: handleMessageSubmit, onCommand: handleCommandExecution, placeholder: "Ask me anything about coding...", disabled: false }))));
287
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ onBack: () => void;
4
+ onSave: () => void;
5
+ };
6
+ export default function ModelConfigScreen({ onBack, onSave }: Props): React.JSX.Element;
7
+ export {};