capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
139 lines • 5.51 kB
JavaScript
import React, { useState, useEffect } from 'react';
import { Box, useApp, useInput } from 'ink';
import { ConversationView } from './ConversationView.js';
import { InputBox } from './InputBox.js';
import { StatusBar } from './StatusBar.js';
import { CommandPalette } from './CommandPalette.js';
import { chatService } from '../../services/chat.js';
import { contextManager } from '../../services/context.js';
import { stateService } from '../../services/state.js';
import { v4 as uuidv4 } from 'uuid';
export const Prompt = () => {
const { exit } = useApp();
const [messages, setMessages] = useState([]);
const [currentInput, setCurrentInput] = useState('');
const [showCommandPalette, setShowCommandPalette] = useState(false);
const [mode, setMode] = useState('chat');
const [isProcessing, setIsProcessing] = useState(false);
const [terminalHeight, setTerminalHeight] = useState(process.stdout.rows || 24);
useInput((input, key) => {
if (key.ctrl && input === 'c') {
exit();
}
if (key.escape) {
setShowCommandPalette(false);
}
});
useEffect(() => {
const handleResize = () => {
setTerminalHeight(process.stdout.rows || 24);
};
process.stdout.on('resize', handleResize);
return () => {
process.stdout.off('resize', handleResize);
};
}, []);
useEffect(() => {
const context = contextManager.getCurrentContext();
const loadedMessages = context.messages.map(msg => ({
id: uuidv4(),
type: msg.role,
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
timestamp: new Date()
}));
setMessages(loadedMessages);
}, []);
const handleInputChange = (input) => {
setCurrentInput(input);
if (input.startsWith('/')) {
setShowCommandPalette(true);
}
else {
setShowCommandPalette(false);
}
};
const handleSubmit = async (input) => {
if (input.startsWith('/')) {
return;
}
setCurrentInput('');
const userMessage = {
id: uuidv4(),
type: 'user',
content: input,
timestamp: new Date()
};
setMessages(prev => [...prev, userMessage]);
setIsProcessing(true);
try {
const assistantMessage = {
id: uuidv4(),
type: 'assistant',
content: '',
timestamp: new Date()
};
setMessages(prev => [...prev, assistantMessage]);
const stream = chatService.stream(input, { mode });
let fullContent = '';
for await (const chunk of stream) {
if (chunk.delta) {
fullContent += chunk.delta;
setMessages(prev => prev.map(msg => msg.id === assistantMessage.id
? { ...msg, content: fullContent }
: msg));
}
}
}
catch (error) {
const errorMessage = {
id: uuidv4(),
type: 'error',
content: error.message,
timestamp: new Date()
};
setMessages(prev => [...prev, errorMessage]);
}
finally {
setIsProcessing(false);
}
};
const handleCommandSelect = (command) => {
setShowCommandPalette(false);
setCurrentInput('');
switch (command) {
case '/exit':
exit();
break;
case '/clear':
setMessages([]);
break;
case '/new':
const newContext = contextManager.createNewContext();
contextManager.setCurrentContext(newContext.id);
setMessages([]);
break;
default:
const systemMessage = {
id: uuidv4(),
type: 'system',
content: `Command ${command} not yet implemented`,
timestamp: new Date()
};
setMessages(prev => [...prev, systemMessage]);
}
};
const statusBarHeight = 1;
const inputBoxHeight = 3;
const commandPaletteHeight = showCommandPalette ? 7 : 0;
const conversationHeight = terminalHeight - statusBarHeight - inputBoxHeight - commandPaletteHeight - 2;
const stats = contextManager.getContextStats();
const model = stateService.getModel();
const provider = stateService.getProvider();
const tokenLimit = contextManager.getTokenLimit(model);
return (React.createElement(Box, { flexDirection: "column", height: terminalHeight },
React.createElement(ConversationView, { messages: messages, height: conversationHeight }),
showCommandPalette && (React.createElement(CommandPalette, { query: currentInput, onSelect: handleCommandSelect, onClose: () => setShowCommandPalette(false) })),
React.createElement(InputBox, { value: currentInput, onSubmit: handleSubmit, onChange: handleInputChange, placeholder: isProcessing ? "Processing..." : "Type a message or / for commands" }),
React.createElement(StatusBar, { mode: mode, model: model, provider: provider, tokenCount: stats.tokenCount, tokenLimit: tokenLimit })));
};
//# sourceMappingURL=Prompt.js.map