UNPKG

capsule-ai-cli

Version:

The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing

113 lines 4.79 kB
import React, { useState, useEffect } from 'react'; import { Box, Text, useInput, useApp } from 'ink'; import Spinner from 'ink-spinner'; import chalk from 'chalk'; export const InteractiveChat = ({ onSendMessage, initialMessages = [] }) => { const [messages, setMessages] = useState(initialMessages); const [input, setInput] = useState(''); const [isLoading, setIsLoading] = useState(false); const [cursorPosition, setCursorPosition] = useState(0); const { exit } = useApp(); useInput((input, key) => { if (key.escape || (key.ctrl && input === 'c')) { exit(); return; } if (key.return && !isLoading && input.trim()) { handleSendMessage(); return; } if (key.backspace || key.delete) { if (cursorPosition > 0) { setInput(prev => prev.slice(0, cursorPosition - 1) + prev.slice(cursorPosition)); setCursorPosition(prev => Math.max(0, prev - 1)); } return; } if (key.leftArrow) { setCursorPosition(prev => Math.max(0, prev - 1)); return; } if (key.rightArrow) { setCursorPosition(prev => Math.min(input.length, prev + 1)); return; } if (input && !key.ctrl && !key.meta) { setInput(prev => prev.slice(0, cursorPosition) + input + prev.slice(cursorPosition)); setCursorPosition(prev => prev + input.length); } }); const handleSendMessage = async () => { const userMessage = { role: 'user', content: input, timestamp: new Date() }; setMessages(prev => [...prev, userMessage]); setInput(''); setCursorPosition(0); setIsLoading(true); try { await onSendMessage(input); } catch (error) { } finally { setIsLoading(false); } }; return (React.createElement(Box, { flexDirection: "column", height: "100%" }, React.createElement(Box, { flexDirection: "column", flexGrow: 1, paddingX: 1 }, messages.map((msg, index) => (React.createElement(Box, { key: index, marginY: 0.5 }, React.createElement(MessageDisplay, { message: msg })))), isLoading && (React.createElement(Box, { marginY: 0.5 }, React.createElement(Text, { color: "cyan" }, React.createElement(Spinner, { type: "dots" }), " Thinking...")))), React.createElement(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginX: 1, marginBottom: 1 }, React.createElement(Text, null, React.createElement(Text, { color: "cyan" }, "\u276F "), input.slice(0, cursorPosition), React.createElement(Text, { backgroundColor: "cyan", color: "black" }, input[cursorPosition] || ' '), input.slice(cursorPosition + 1))), React.createElement(Box, { paddingX: 1, paddingBottom: 1 }, React.createElement(StatusLine, null)), React.createElement(Box, { paddingX: 1 }, React.createElement(Text, { color: "yellow" }, "\u26A1 Model Power Level: Over 9000! \u26A1")))); }; const MessageDisplay = ({ message }) => { const roleColor = message.role === 'user' ? 'blue' : 'green'; const roleSymbol = message.role === 'user' ? '▶' : '◀'; return (React.createElement(Box, null, React.createElement(Text, { color: roleColor }, roleSymbol, " "), React.createElement(Text, null, formatContent(message.content)), message.cost && (React.createElement(Text, { dimColor: true }, " ($", message.cost.toFixed(4), ")")))); }; const StatusLine = () => { const [time, setTime] = useState(new Date()); useEffect(() => { const timer = setInterval(() => setTime(new Date()), 1000); return () => clearInterval(timer); }, []); return (React.createElement(Box, { justifyContent: "space-between", width: "100%" }, React.createElement(Text, { dimColor: true }, process.cwd().replace(process.env.HOME || '', '~')), React.createElement(Text, { dimColor: true }, "Capsule CLI v0.1.0 | ", time.toLocaleTimeString()))); }; function formatContent(content) { if (content.includes('```')) { return content.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => { return chalk.gray('```' + (lang || '')) + '\n' + chalk.yellow(code.trim()) + '\n' + chalk.gray('```'); }); } return content; } //# sourceMappingURL=InteractiveChat.js.map