/** * ConversationScreen.tsx * * Third screen of the onboarding wizard - AI-driven conversation * for project discovery with confidence meter and structured output parsing. * * Features: * - AI Terminal-like interface for familiarity * - Confidence progress bar (0-100%, red to yellow to green) * - Conversation display area with message history * - Input field at bottom for user responses * - "Let's get started!" button when ready=true and confidence>80 * - Structured output parsing (confidence, ready, message) */ import { useEffect, useRef, useState, useCallback } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { Brain } from 'lucide-react'; import type { Theme } from '../../../types'; import { useWizard, type WizardMessage } from '../WizardContext'; import { getConfidenceColor, getInitialQuestion, READY_CONFIDENCE_THRESHOLD, type ExistingDocument, } from '../services/wizardPrompts'; import { conversationManager, createUserMessage, createAssistantMessage, } from '../services/conversationManager'; import type { WizardError } from '../services/wizardErrorDetection'; import { AUTO_RUN_FOLDER_NAME, wizardDebugLogger } from '../services/phaseGenerator'; import { getNextFillerPhrase } from '../services/fillerPhrases'; import { ScreenReaderAnnouncement } from '../ScreenReaderAnnouncement'; interface ConversationScreenProps { theme: Theme; /** Whether to show AI thinking content instead of filler phrases */ showThinking: boolean; /** Callback to toggle thinking display (controlled by parent for global shortcut) */ setShowThinking: (value: boolean | ((prev: boolean) => boolean)) => void; } /** * Check if a string contains an emoji */ function containsEmoji(str: string): boolean { const emojiRegex = /[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F600}-\u{1F64F}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]/u; return emojiRegex.test(str); } /** * Format agent name with robot emoji prefix if no emoji present */ function formatAgentName(name: string): string { if (!name) return '🤖 Agent'; return containsEmoji(name) ? name : `🤖 ${name}`; } /** * Format timestamp for display */ function formatTimestamp(timestamp: number): string { const date = new Date(timestamp); return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } /** * Patterns that indicate the AI said it will do something asynchronously. * This is a UX problem because the wizard can't actually support async operations - * each message is a single turn. If the AI says "let me research this", the user * is left waiting with no indication that they need to respond. */ const DEFERRED_RESPONSE_PATTERNS = [ /let me (?:research|investigate|look into|think about|analyze|examine|check|explore)/i, /give me a (?:moment|minute|second)/i, /i(?:'ll| will) (?:look into|research|investigate|get back|check)/i, /(?:researching|investigating|looking into) (?:this|that|it)/i, /let me (?:take a )?(?:closer )?look/i, ]; /** * Check if a message contains phrases that imply deferred/async work. * The wizard can't actually support this - we need to auto-continue. */ function containsDeferredResponsePhrase(message: string): boolean { return DEFERRED_RESPONSE_PATTERNS.some((pattern) => pattern.test(message)); } /** * ConfidenceMeter - Horizontal progress bar with gradient fill */ function ConfidenceMeter({ confidence, theme }: { confidence: number; theme: Theme }): JSX.Element { const clampedConfidence = Math.max(0, Math.min(100, confidence)); const confidenceColor = getConfidenceColor(clampedConfidence); return (
Ready to create your Playbook!
)}{children}
, ul: ({ children }) =>
{children}
) : (
{children}
);
},
pre: ({ children }) => (
{children}
),
a: ({ href, children }) => (
),
h1: ({ children }) => {children}), }} > {message.content}
{detectedError.title}
)} {/* Error Message */}{detectedError ? detectedError.message : state.conversationError}
{/* Recovery Hint */} {detectedError && ({detectedError.recoveryHint}
)} {/* Action Button */}I think I have a good understanding of your project. Ready to create your Playbook?
Or continue chatting below to add more details