import React, { useState, useEffect, useRef } from "react"; import { Box, Text } from "ink"; import { GrokAgent, ChatEntry } from "../../agent/grok-agent"; import { useInputHandler } from "../../hooks/use-input-handler"; import { LoadingSpinner } from "./loading-spinner"; import { CommandSuggestions } from "./command-suggestions"; import { ModelSelection } from "./model-selection"; import { ChatHistory } from "./chat-history"; import { ChatInput } from "./chat-input"; import { MCPStatus } from "./mcp-status"; import ConfirmationDialog from "./confirmation-dialog"; import { ConfirmationService, ConfirmationOptions, } from "../../utils/confirmation-service"; import ApiKeyInput from "./api-key-input"; import cfonts from "cfonts"; interface ChatInterfaceProps { agent?: GrokAgent; } // Main chat component that handles input when agent is available function ChatInterfaceWithAgent({ agent }: { agent: GrokAgent }) { const [chatHistory, setChatHistory] = useState([]); const [isProcessing, setIsProcessing] = useState(false); const [processingTime, setProcessingTime] = useState(0); const [tokenCount, setTokenCount] = useState(0); const [isStreaming, setIsStreaming] = useState(false); const [confirmationOptions, setConfirmationOptions] = useState(null); const scrollRef = useRef(); const processingStartTime = useRef(0); const confirmationService = ConfirmationService.getInstance(); const { input, cursorPosition, showCommandSuggestions, selectedCommandIndex, showModelSelection, selectedModelIndex, commandSuggestions, availableModels, autoEditEnabled, } = useInputHandler({ agent, chatHistory, setChatHistory, setIsProcessing, setIsStreaming, setTokenCount, setProcessingTime, processingStartTime, isProcessing, isStreaming, isConfirmationActive: !!confirmationOptions, }); useEffect(() => { // Only clear console on non-Windows platforms or if not PowerShell // Windows PowerShell can have issues with console.clear() causing flickering const isWindows = process.platform === "win32"; const isPowerShell = process.env.ComSpec?.toLowerCase().includes("powershell") || process.env.PSModulePath !== undefined; if (!isWindows || !isPowerShell) { console.clear(); } // Add top padding console.log(" "); // Generate logo with margin to match Ink paddingX={2} const logoOutput = cfonts.render("GROK", { font: "3d", align: "left", colors: ["magenta", "gray"], space: true, maxLength: "0", gradient: ["magenta", "cyan"], independentGradient: false, transitionGradient: true, env: "node", }); // Add horizontal margin (2 spaces) to match Ink paddingX={2} const logoLines = (logoOutput as any).string.split("\n"); logoLines.forEach((line: string) => { if (line.trim()) { console.log(" " + line); // Add 2 spaces for horizontal margin } else { console.log(line); // Keep empty lines as-is } }); console.log(" "); // Spacing after logo setChatHistory([]); }, []); useEffect(() => { const handleConfirmationRequest = (options: ConfirmationOptions) => { setConfirmationOptions(options); }; confirmationService.on("confirmation-requested", handleConfirmationRequest); return () => { confirmationService.off( "confirmation-requested", handleConfirmationRequest ); }; }, [confirmationService]); useEffect(() => { if (!isProcessing && !isStreaming) { setProcessingTime(0); return; } if (processingStartTime.current === 0) { processingStartTime.current = Date.now(); } const interval = setInterval(() => { setProcessingTime( Math.floor((Date.now() - processingStartTime.current) / 1000) ); }, 1000); return () => clearInterval(interval); }, [isProcessing, isStreaming]); const handleConfirmation = (dontAskAgain?: boolean) => { confirmationService.confirmOperation(true, dontAskAgain); setConfirmationOptions(null); }; const handleRejection = (feedback?: string) => { confirmationService.rejectOperation(feedback); setConfirmationOptions(null); // Reset processing states when operation is cancelled setIsProcessing(false); setIsStreaming(false); setTokenCount(0); setProcessingTime(0); processingStartTime.current = 0; }; return ( {/* Show tips only when no chat history and no confirmation dialog */} {chatHistory.length === 0 && !confirmationOptions && ( Tips for getting started: 1. Ask questions, edit files, or run commands. 2. Be specific for the best results. 3. Create GROK.md files to customize your interactions with Grok. 4. Press Shift+Tab to toggle auto-edit mode. 5. /help for more information. )} Type your request in natural language. Type 'exit' or Ctrl+C to quit. {/* Show confirmation dialog if one is pending */} {confirmationOptions && ( )} {!confirmationOptions && ( <> {autoEditEnabled ? "▶" : "⏸"} auto-edit:{" "} {autoEditEnabled ? "on" : "off"} (shift + tab) ≋ {agent.getCurrentModel()} )} ); } // Main component that handles API key input or chat interface export default function ChatInterface({ agent }: ChatInterfaceProps) { const [currentAgent, setCurrentAgent] = useState( agent || null ); const handleApiKeySet = (newAgent: GrokAgent) => { setCurrentAgent(newAgent); }; if (!currentAgent) { return ; } return ; }