diff --git a/bin/goose-ultra-final/src/components/LayoutComponents.tsx b/bin/goose-ultra-final/src/components/LayoutComponents.tsx index ddfb2d1..56b22df 100644 --- a/bin/goose-ultra-final/src/components/LayoutComponents.tsx +++ b/bin/goose-ultra-final/src/components/LayoutComponents.tsx @@ -2472,11 +2472,12 @@ Format: { "ideas": [{ "title": "Short Title", "subtitle": "One line", "tag": "To } - // --- REPAIR MODE (F3: Retention & Match) --- - // "Broken frontend is treated as a REPAIR task" - const originalIntent = state.activeProject?.originalPrompt || "Unknown Intent"; + if (isQaFailureArtifact) { + // --- REPAIR MODE (F3: Retention & Match) --- + // "Broken frontend is treated as a REPAIR task" + const originalIntent = state.activeProject?.originalPrompt || "Unknown Intent"; - systemPrompt = `[SYSTEM INSTRUCTION]: REPAIR MODE ACTIVE. + systemPrompt = `[SYSTEM INSTRUCTION]: REPAIR MODE ACTIVE. The previous build FAILED Quality Assurance (Unstyled/Broken) or was completely lost. The user wants to FIX/REDO it. @@ -2489,32 +2490,32 @@ YOUR GOAL: Propose a repair plan to fix the broken output while STAYING TRUE to 2. YOU MUST PROPOSE A VALID, STYLED IMPLEMENTATION. 3. Start plan with '[PLAN]'. 4. Do NOT ask for clarification. JUST FIX IT.`; - } else if (isRedesignConfirmed) { - // --- REDESIGN APPROVED MODE --- - systemPrompt = `[SYSTEM INSTRUCTION]: REDESIGN APPROVED. + } else if (isRedesignConfirmed) { + // --- REDESIGN APPROVED MODE --- + systemPrompt = `[SYSTEM INSTRUCTION]: REDESIGN APPROVED. The user has explicitely authorized a redesign. 1. You may change layout, colors, and structure. 2. Ignore previous Design Lock constraints. 3. Propose a comprehensive plan starting with '[PLAN]'.`; - } else { - // --- CLIE: CONTEXT-LOCKED EXECUTION --- - try { - // 1. Analyze Intent - const intent = classifyIntent(userPrompt); + } else { + // --- CLIE: CONTEXT-LOCKED EXECUTION --- + try { + // 1. Analyze Intent + const intent = classifyIntent(userPrompt); - // 2. Load Manifest (Context Soul) - let manifest = await loadProjectManifest(state.activeProject.id); - if (!manifest && state.activeProject.originalPrompt) { - // Lazy init if missing - await initializeProjectContext(state.activeProject, state.activeProject.originalPrompt); - manifest = await loadProjectManifest(state.activeProject.id); - } + // 2. Load Manifest (Context Soul) + let manifest = await loadProjectManifest(state.activeProject.id); + if (!manifest && state.activeProject.originalPrompt) { + // Lazy init if missing + await initializeProjectContext(state.activeProject, state.activeProject.originalPrompt); + manifest = await loadProjectManifest(state.activeProject.id); + } - // 3. Enhance Prompt - const enhancedContext = enhancePromptWithContext(userPrompt, manifest, intent); + // 3. Enhance Prompt + const enhancedContext = enhancePromptWithContext(userPrompt, manifest, intent); - systemPrompt = `[CLIE v${CLIE_VERSION}] [SYSTEM INSTRUCTION]: EXECUTION MODE LOCKED. + systemPrompt = `[CLIE v${CLIE_VERSION}] [SYSTEM INSTRUCTION]: EXECUTION MODE LOCKED. ${enhancedContext} @@ -2531,10 +2532,10 @@ BEFORE proposing changes, state: - MUST_NOT_TOUCH: [layout, colors, typography, existing components, structure] Then provide a BRIEF plan starting with '[PLAN]' describing ONLY the changes needed.`; - } catch (e) { - console.error('[CLIE] Failed to enhance prompt:', e); - // Fallback to standard - systemPrompt = `[SYSTEM INSTRUCTION]: MODIFICATION MODE with DESIGN LOCK ENABLED. + } catch (e) { + console.error('[CLIE] Failed to enhance prompt:', e); + // Fallback to standard + systemPrompt = `[SYSTEM INSTRUCTION]: MODIFICATION MODE with DESIGN LOCK ENABLED. ${memoryBlock} @@ -2549,644 +2550,644 @@ DESIGN LOCK RULES: 3. ONLY implement the request. Brief plan starting with '[PLAN]'.`; - } - } - } else { - // P0-WF1: FORCE PLAN-FIRST - // "Every idea must produce a plan first" - // We ignore overrideInput / one-shot triggers for the initial build request. - requestKind = 'plan'; - systemPrompt = `[SYSTEM INSTRUCTION]: Propose an implementation plan starting with '[PLAN]'. DO NOT output code yet.`; -} - -if (state.preferredFramework) { - systemPrompt += `\n\n[USER PREFERENCE]: The user has explicitly requested to use the "${state.preferredFramework}" framework. You MUST prioritize this framework, along with its standard ecosystem (e.g. if React, use typical React libs; if Tailwind, use utility classes).`; -} - -// IMMEDIATE FEEDBACK: If we are planning, switch to Plan tab immediately so user sees the stream -if (requestKind === 'plan') { - dispatch({ type: 'TRANSITION', to: OrchestratorState.Planning }); - dispatch({ type: 'SET_TAB', tab: TabId.Plan }); - dispatch({ type: 'UPDATE_PLAN', plan: "# Analyzing Request...\n\n*Forging blueprint based on 2025 System Standards...*" }); -} - -setThinkingLabel('Thinking...'); - -if ((window as any).electron) { - const sessionId = Date.now().toString(); - let currentProjectId = state.activeProject?.id || null; - - if (!currentProjectId) { - const id = (globalThis.crypto && 'randomUUID' in globalThis.crypto) ? (globalThis.crypto as any).randomUUID() : Date.now().toString(); - const name = userPrompt.substring(0, 20) || "New Project"; - dispatch({ type: 'CREATE_PROJECT', id, createdAt: Date.now(), name }); - void ensureProjectOnDisk({ id, name, slug: name.toLowerCase().replace(/\s+/g, '-'), createdAt: Date.now(), description: 'New Vibe Project' }); - void writeLastActiveProjectId(id); - currentProjectId = id; - } - - const targetProjectId = currentProjectId; - - - - (window as any).electron.removeChatListeners(); - - - - const handleResponse = (response: string) => { - clearTimeout(timeoutId); - // Session gating: ignore if session was cancelled - if (state.activeRequestStatus === 'cancelled') { - console.log('[ChatPanel] Ignoring response from cancelled session'); - return; - } - - - dispatch({ type: 'ADD_LOG', log: { id: Date.now().toString(), timestamp: Date.now(), type: 'system', message: response } }); - const hasDoctype = response.includes(''); - const isPlan = isPlanMessage(response); - - if (requestKind === 'plan') { - // F5: Plan Streaming handled via onChatChunk below (lines 1550+) - // Here we just finalize the plan. - - // FORCE transition to PlanReady regardless of tag presence - // We demanded a plan, so we treat the output as a plan. - dispatch({ type: 'UPDATE_PLAN', plan: response }); - // LAYER 1 FIX: Transition to PlanReady - user MUST approve before building - dispatch({ type: 'TRANSITION', to: OrchestratorState.PlanReady }); - dispatch({ type: 'SET_TAB', tab: TabId.Plan }); - dispatch({ type: 'END_BUILD_SESSION', sessionId }); - dispatch({ type: 'UPDATE_STREAMING_CODE', code: null }); // Clear stream - finalizeRequest(); - return; - } - - if (isChatMode) { - // IT Expert: Check for JSON ActionProposal - if (state.chatPersona === 'it') { - try { - // Try to extract JSON from response - let jsonStr = response.trim(); - // Strip markdown fences if present - jsonStr = jsonStr.replace(/```json/gi, '').replace(/```/g, '').trim(); - // Find first { and last } - const first = jsonStr.indexOf('{'); - const last = jsonStr.lastIndexOf('}'); - if (first !== -1 && last > first) { - jsonStr = jsonStr.substring(first, last + 1); - const parsed = JSON.parse(jsonStr); - if (parsed.runner && parsed.script && parsed.proposalId) { - // Valid ActionProposal - const proposal: import('../types').ActionProposal = { - ...parsed, - status: 'pending' - }; - dispatch({ type: 'SET_PENDING_PROPOSAL', proposal }); - finalizeRequest(); - return; - } - } - } catch (e) { - // Not valid JSON, treat as normal chat response - console.log('[IT Expert] Response is not a valid proposal, rendering as chat.'); } } - finalizeRequest(); - return; + } else { + // P0-WF1: FORCE PLAN-FIRST + // "Every idea must produce a plan first" + // We ignore overrideInput / one-shot triggers for the initial build request. + requestKind = 'plan'; + systemPrompt = `[SYSTEM INSTRUCTION]: Propose an implementation plan starting with '[PLAN]'. DO NOT output code yet.`; } + if (state.preferredFramework) { + systemPrompt += `\n\n[USER PREFERENCE]: The user has explicitly requested to use the "${state.preferredFramework}" framework. You MUST prioritize this framework, along with its standard ecosystem (e.g. if React, use typical React libs; if Tailwind, use utility classes).`; + } - }; - - (window as any).electron.onChatComplete(handleResponse); - (window as any).electron.onChatError((error: string) => { - clearTimeout(timeoutId); - // Session gating - if (state.activeRequestStatus === 'cancelled') return; - + // IMMEDIATE FEEDBACK: If we are planning, switch to Plan tab immediately so user sees the stream if (requestKind === 'plan') { - dispatch({ type: 'UPDATE_PLAN', plan: "# Planning Failed\n\nThe orchestrator encountered an error while analyzing your request:\n\n> " + error + "\n\nPlease try again." }); + dispatch({ type: 'TRANSITION', to: OrchestratorState.Planning }); + dispatch({ type: 'SET_TAB', tab: TabId.Plan }); + dispatch({ type: 'UPDATE_PLAN', plan: "# Analyzing Request...\n\n*Forging blueprint based on 2025 System Standards...*" }); } - dispatch({ type: 'ADD_LOG', log: { id: Date.now().toString(), timestamp: Date.now(), type: 'error', message: "Error: " + error } }); - dispatch({ type: 'REQUEST_ERROR' }); - if (!isChatMode && requestKind !== 'plan') { - dispatch({ type: 'END_BUILD_SESSION', sessionId }); - dispatch({ type: 'UPDATE_STREAMING_CODE', code: null }); - } - setIsThinking(false); + setThinkingLabel('Thinking...'); + if ((window as any).electron) { + const sessionId = Date.now().toString(); + let currentProjectId = state.activeProject?.id || null; + + if (!currentProjectId) { + const id = (globalThis.crypto && 'randomUUID' in globalThis.crypto) ? (globalThis.crypto as any).randomUUID() : Date.now().toString(); + const name = userPrompt.substring(0, 20) || "New Project"; + dispatch({ type: 'CREATE_PROJECT', id, createdAt: Date.now(), name }); + void ensureProjectOnDisk({ id, name, slug: name.toLowerCase().replace(/\s+/g, '-'), createdAt: Date.now(), description: 'New Vibe Project' }); + void writeLastActiveProjectId(id); + currentProjectId = id; + } + + const targetProjectId = currentProjectId; + + + (window as any).electron.removeChatListeners(); + + + + const handleResponse = (response: string) => { + clearTimeout(timeoutId); + // Session gating: ignore if session was cancelled + if (state.activeRequestStatus === 'cancelled') { + console.log('[ChatPanel] Ignoring response from cancelled session'); + return; + } + + + dispatch({ type: 'ADD_LOG', log: { id: Date.now().toString(), timestamp: Date.now(), type: 'system', message: response } }); + const hasDoctype = response.includes(''); + const isPlan = isPlanMessage(response); + + if (requestKind === 'plan') { + // F5: Plan Streaming handled via onChatChunk below (lines 1550+) + // Here we just finalize the plan. + + // FORCE transition to PlanReady regardless of tag presence + // We demanded a plan, so we treat the output as a plan. + dispatch({ type: 'UPDATE_PLAN', plan: response }); + // LAYER 1 FIX: Transition to PlanReady - user MUST approve before building + dispatch({ type: 'TRANSITION', to: OrchestratorState.PlanReady }); + dispatch({ type: 'SET_TAB', tab: TabId.Plan }); + dispatch({ type: 'END_BUILD_SESSION', sessionId }); + dispatch({ type: 'UPDATE_STREAMING_CODE', code: null }); // Clear stream + finalizeRequest(); + return; + } + + if (isChatMode) { + // IT Expert: Check for JSON ActionProposal + if (state.chatPersona === 'it') { + try { + // Try to extract JSON from response + let jsonStr = response.trim(); + // Strip markdown fences if present + jsonStr = jsonStr.replace(/```json/gi, '').replace(/```/g, '').trim(); + // Find first { and last } + const first = jsonStr.indexOf('{'); + const last = jsonStr.lastIndexOf('}'); + if (first !== -1 && last > first) { + jsonStr = jsonStr.substring(first, last + 1); + const parsed = JSON.parse(jsonStr); + if (parsed.runner && parsed.script && parsed.proposalId) { + // Valid ActionProposal + const proposal: import('../types').ActionProposal = { + ...parsed, + status: 'pending' + }; + dispatch({ type: 'SET_PENDING_PROPOSAL', proposal }); + finalizeRequest(); + return; + } + } + } catch (e) { + // Not valid JSON, treat as normal chat response + console.log('[IT Expert] Response is not a valid proposal, rendering as chat.'); + } + } + finalizeRequest(); + return; + } + + + }; + + (window as any).electron.onChatComplete(handleResponse); + (window as any).electron.onChatError((error: string) => { + clearTimeout(timeoutId); + // Session gating + if (state.activeRequestStatus === 'cancelled') return; + + if (requestKind === 'plan') { + dispatch({ type: 'UPDATE_PLAN', plan: "# Planning Failed\n\nThe orchestrator encountered an error while analyzing your request:\n\n> " + error + "\n\nPlease try again." }); + } + dispatch({ type: 'ADD_LOG', log: { id: Date.now().toString(), timestamp: Date.now(), type: 'error', message: "Error: " + error } }); + dispatch({ type: 'REQUEST_ERROR' }); + if (!isChatMode && requestKind !== 'plan') { + dispatch({ type: 'END_BUILD_SESSION', sessionId }); + dispatch({ type: 'UPDATE_STREAMING_CODE', code: null }); + } + setIsThinking(false); + setThinkingLabel('Thinking...'); + if ((window as any).electron) { + (window as any).electron.removeChatListeners(); + } + }); + + // F5: PLAN STREAMING - Attach listener BEFORE startChat + if (requestKind === 'plan') { + let planStreamBuffer = ''; + (window as any).electron.onChatChunk((chunk: string) => { + planStreamBuffer += chunk; + // We use UPDATE_STREAMING_CODE so the Plan Tab (or overlay if visible) can see it. + // Note: PlanView must be capable of rendering streaming code if it's the active tab. + dispatch({ type: 'UPDATE_STREAMING_CODE', code: planStreamBuffer }); + }); + } + + (window as any).electron.startChat([ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userPrompt } + ], state.chatSettings.activeModel); + + } else { + clearTimeout(timeoutId); + setTimeout(() => { + dispatch({ type: 'ADD_LOG', log: { id: Date.now().toString(), timestamp: Date.now(), type: 'system', message: "[OFFLINE] Simulated response." } }); + finalizeRequest(); + }, 1000); } - }); - - // F5: PLAN STREAMING - Attach listener BEFORE startChat - if (requestKind === 'plan') { - let planStreamBuffer = ''; - (window as any).electron.onChatChunk((chunk: string) => { - planStreamBuffer += chunk; - // We use UPDATE_STREAMING_CODE so the Plan Tab (or overlay if visible) can see it. - // Note: PlanView must be capable of rendering streaming code if it's the active tab. - dispatch({ type: 'UPDATE_STREAMING_CODE', code: planStreamBuffer }); - }); - } - - (window as any).electron.startChat([ - { role: 'system', content: systemPrompt }, - { role: 'user', content: userPrompt } - ], state.chatSettings.activeModel); - -} else { - clearTimeout(timeoutId); - setTimeout(() => { - dispatch({ type: 'ADD_LOG', log: { id: Date.now().toString(), timestamp: Date.now(), type: 'system', message: "[OFFLINE] Simulated response." } }); - finalizeRequest(); - }, 1000); -} }; -const showStreaming = (state.state === OrchestratorState.Building) && state.activeBuildSessionId != null && state.streamingCode != null; + const showStreaming = (state.state === OrchestratorState.Building) && state.activeBuildSessionId != null && state.streamingCode != null; -useEffect(() => { - if (!showStreaming) return; - const id = setInterval(() => { - setActiveTechIndex((i) => (techTags.length ? (i + 1) % techTags.length : 0)); - }, 900); - return () => clearInterval(id); -}, [showStreaming, techTags.length]); + useEffect(() => { + if (!showStreaming) return; + const id = setInterval(() => { + setActiveTechIndex((i) => (techTags.length ? (i + 1) % techTags.length : 0)); + }, 900); + return () => clearInterval(id); + }, [showStreaming, techTags.length]); -return ( -
- - {/* STREAMING EDITOR OVERLAY (SLIDES IN) */} -
-
-
-
-
-
-
-
- FORGING_SEQUENCE -
- {/* Real-time Metrics */} -
-
- {(state.streamingCode?.length || 0).toLocaleString()} CHARS - | - ~{Math.round((state.streamingCode?.length || 0) / 4).toLocaleString()} TOKENS -
-
- -
- {/* Tech Cloud */} -
- {techTags.map((t, idx) => ( - - {t} - - ))} -
-
- {/* Checklist (Condensed) */} -
- {[ - { label: "Initializing Build Environment", threshold: 0 }, - { label: "Parsing Blueprint Architecture", threshold: 100 }, - { label: "Scaffolding HTML Structure", threshold: 500 }, - { label: "Applying Tailwind Utility Classes", threshold: 1500 }, - { label: "Injecting JavaScript Logic", threshold: 3000 }, - { label: "Finalizing & Optimizing Assets", threshold: 5000 } - ].map((step, idx) => { - const currentLen = state.streamingCode?.length || 0; - const isComplete = currentLen > step.threshold + 800; - const isCurrent = currentLen >= step.threshold && !isComplete; - const isPending = currentLen < step.threshold; - - if (isPending) return null; // Only show active/done steps to save space - - return ( -
-
- {isComplete ? : isCurrent ?
:
} -
-
- - {step.label} - -
-
- ); - })} -
- - {/* REAL CODE STREAM REMOVED -> REPLACED WITH AI THOUGHTS */} -
-
- - Builder Notes -
- - {(() => { - const code = state.streamingCode || ""; - const thinkMatch = code.match(/([\s\S]*?)(?:<\/thinking>|$)/i); - const thinking = thinkMatch ? thinkMatch[1].trim() : null; - - if (thinking) { - return ( -
- {thinking} - {code.includes("") && ( -
- - Strategy locked. Generatng artifact bundle... -
- )} -
- ); - } else { - return ( -
-
-
-
- Waiting for builder notes... -
- ); - } - })()} -
-
-
- - {/* CHAT CONTENT (SLIDES OUT) */} -
-
-
-
- AI ORCHESTRATOR -
-
- {state.globalMode === GlobalMode.Chat && ( - - )} - {state.globalMode === GlobalMode.Chat && state.chatPersona === 'custom' && ( - - )} - {state.globalMode === GlobalMode.Brainstorm && ( - - Brainstorm - - )} - {state.state} - - {state.chatDocked === 'bottom' && ( - - )} -
-
- - {/* AI SETTINGS MODAL */} - {showAISettings && setShowAISettings(false)} />} + return ( +
+ {/* STREAMING EDITOR OVERLAY (SLIDES IN) */}
{ - const el = e.currentTarget; - setAutoScrollEnabled(isNearBottom(el)); - }} + className={`absolute inset-0 bg-[#0B0B0C] z-30 flex flex-col transition-transform duration-500 ease-in-out ${showStreaming ? 'translate-x-0' : '-translate-x-full'}`} > - {state.timeline.filter(t => t.type === 'user' || t.type === 'system').map(log => ( -
-
- +
+
+
+
+
+
+
+ FORGING_SEQUENCE
- {new Date(log.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} -
- ))} - - {/* IT Expert Action Proposal */} - {state.pendingProposal && ( -
- -
- )} - - {/* Templates / Suggestions when empty */} - {/* Empty State: Idea Seeds */} - {state.timeline.length === 0 && !isThinking && ( -
-
-
-
-
-

What shall we build?

-

Generate tailored build ideas or start fresh.

-
-
-
- setIdeaSeed(e.target.value)} - onKeyDown={e => e.key === 'Enter' && handleGenerateIdeas()} - /> - -
- {ideaStatus === 'error' &&

Failed to generate ideas. Try fewer keywords.

} + {/* Real-time Metrics */} +
+
+ {(state.streamingCode?.length || 0).toLocaleString()} CHARS + | + ~{Math.round((state.streamingCode?.length || 0) / 4).toLocaleString()} TOKENS
- - {/* Results Grid */} - {(ideaResults.length > 0 || ideaStatus === 'loading') && ( -
- {ideaStatus === 'loading' - ? Array(6).fill(0).map((_, i) => ( -
- )) - : ideaResults.map((idea, i) => ( - - )) - } -
- )}
- )} - - {isThinking && ( -
-
-
-
-
-
-
-
- {thinkingLabel} - {state.streamingCode && state.streamingCode.length > 0 && ( - - {state.streamingCode.length} chars · ~{Math.ceil(state.streamingCode.length / 4)} tokens - - )} -
-
- - - Working... - {state.streamingCode && state.streamingCode.length > 0 && ( - - {state.streamingCode.length} chars | ~{Math.ceil(state.streamingCode.length / 4)} tokens - - )} - -
- )} - - {!autoScrollEnabled && ( -
-
+ {/* Tech Cloud */} +
+ {techTags.map((t, idx) => ( + - Jump to latest - -
- )} -
+ {t} + + ))} +
+
+ {/* Checklist (Condensed) */} +
+ {[ + { label: "Initializing Build Environment", threshold: 0 }, + { label: "Parsing Blueprint Architecture", threshold: 100 }, + { label: "Scaffolding HTML Structure", threshold: 500 }, + { label: "Applying Tailwind Utility Classes", threshold: 1500 }, + { label: "Injecting JavaScript Logic", threshold: 3000 }, + { label: "Finalizing & Optimizing Assets", threshold: 5000 } + ].map((step, idx) => { + const currentLen = state.streamingCode?.length || 0; + const isComplete = currentLen > step.threshold + 800; + const isCurrent = currentLen >= step.threshold && !isComplete; + const isPending = currentLen < step.threshold; + + if (isPending) return null; // Only show active/done steps to save space -
{ e.preventDefault(); setIsDragging(true); }} - onDragLeave={(e) => { - if (e.relatedTarget && !e.currentTarget.contains(e.relatedTarget as Node)) { - setIsDragging(false); - } - }} - onDrop={handleFileDrop} - > - {/* Skill Recommendations */} - {recommendedSkills.length > 0 && ( -
- Suggested Tools: - {recommendedSkills.map(skill => { - const IconComp = (Icons as any)[skill.icon] || Icons.Box; return ( - +
+
+ {isComplete ? : isCurrent ?
:
} +
+
+ + {step.label} + +
+
); })}
- )} - {/* Attachment chips */} - {attachments.length > 0 && ( -
- {attachments.map(att => ( -
- - {att.name} - -
- ))} + + {/* REAL CODE STREAM REMOVED -> REPLACED WITH AI THOUGHTS */} +
+
+ + Builder Notes +
+ + {(() => { + const code = state.streamingCode || ""; + const thinkMatch = code.match(/([\s\S]*?)(?:<\/thinking>|$)/i); + const thinking = thinkMatch ? thinkMatch[1].trim() : null; + + if (thinking) { + return ( +
+ {thinking} + {code.includes("") && ( +
+ + Strategy locked. Generatng artifact bundle... +
+ )} +
+ ); + } else { + return ( +
+
+
+
+ Waiting for builder notes... +
+ ); + } + })()}
- )} -
-
- {/* Hidden file input */} - - {/* Main Input Field - Adjusted padding for right-aligned icons */} - setInput(e.target.value)} - autoFocus - /> +
+
- {/* Right-aligned Action Buttons */} -
- {/* Paperclip button */} - - - {/* Tools Button / Skills */} - - - {/* Persona Quick Access */} - - - {/* Zap / Shortcuts (Also Skills for now, could be quick actions) */} - - -
{/* Divider */} - {/* Edit & Resend button (shows after cancel or error) */} - {(state.activeRequestStatus === 'cancelled' || state.activeRequestStatus === 'error') && state.lastUserMessageDraft && !isThinking && ( - )} - {/* Stop button (shows when thinking) */} - {isThinking ? ( - - ) : ( - + {state.globalMode === GlobalMode.Brainstorm && ( + + Brainstorm + + )} + {state.state} + + {state.chatDocked === 'bottom' && ( + )}
- {/* Modification mode indicator */} - {(state.state === OrchestratorState.PreviewReady || state.state === OrchestratorState.Editing) && state.files['index.html'] && ( -
- - Modify Existing App - - Changes will preserve your current design + + {/* AI SETTINGS MODAL */} + {showAISettings && setShowAISettings(false)} />} + +
{ + const el = e.currentTarget; + setAutoScrollEnabled(isNearBottom(el)); + }} + > + {state.timeline.filter(t => t.type === 'user' || t.type === 'system').map(log => ( +
+
+ +
+ {new Date(log.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} +
+ ))} + + {/* IT Expert Action Proposal */} + {state.pendingProposal && ( +
+ +
+ )} + + {/* Templates / Suggestions when empty */} + {/* Empty State: Idea Seeds */} + {state.timeline.length === 0 && !isThinking && ( +
+
+
+
+
+

What shall we build?

+

Generate tailored build ideas or start fresh.

+
+
+
+ setIdeaSeed(e.target.value)} + onKeyDown={e => e.key === 'Enter' && handleGenerateIdeas()} + /> + +
+ {ideaStatus === 'error' &&

Failed to generate ideas. Try fewer keywords.

} +
+ + {/* Results Grid */} + {(ideaResults.length > 0 || ideaStatus === 'loading') && ( +
+ {ideaStatus === 'loading' + ? Array(6).fill(0).map((_, i) => ( +
+ )) + : ideaResults.map((idea, i) => ( + + )) + } +
+ )} +
+ )} + + {isThinking && ( +
+
+
+
+
+
+
+
+ {thinkingLabel} + {state.streamingCode && state.streamingCode.length > 0 && ( + + {state.streamingCode.length} chars · ~{Math.ceil(state.streamingCode.length / 4)} tokens + + )} +
+
+ + + Working... + {state.streamingCode && state.streamingCode.length > 0 && ( + + {state.streamingCode.length} chars | ~{Math.ceil(state.streamingCode.length / 4)} tokens + + )} + +
+ )} + + {!autoScrollEnabled && ( +
+ +
+ )} +
+ + { e.preventDefault(); setIsDragging(true); }} + onDragLeave={(e) => { + if (e.relatedTarget && !e.currentTarget.contains(e.relatedTarget as Node)) { + setIsDragging(false); + } + }} + onDrop={handleFileDrop} + > + {/* Skill Recommendations */} + {recommendedSkills.length > 0 && ( +
+ Suggested Tools: + {recommendedSkills.map(skill => { + const IconComp = (Icons as any)[skill.icon] || Icons.Box; + return ( + + ); + })} +
+ )} + {/* Attachment chips */} + {attachments.length > 0 && ( +
+ {attachments.map(att => ( +
+ + {att.name} + +
+ ))} +
+ )} +
+
+ {/* Hidden file input */} + + {/* Main Input Field - Adjusted padding for right-aligned icons */} + setInput(e.target.value)} + autoFocus + /> + + {/* Right-aligned Action Buttons */} +
+ {/* Paperclip button */} + + + {/* Tools Button / Skills */} + + + {/* Persona Quick Access */} + + + {/* Zap / Shortcuts (Also Skills for now, could be quick actions) */} + + +
{/* Divider */} + {/* Edit & Resend button (shows after cancel or error) */} + {(state.activeRequestStatus === 'cancelled' || state.activeRequestStatus === 'error') && state.lastUserMessageDraft && !isThinking && ( + + )} + {/* Stop button (shows when thinking) */} + {isThinking ? ( + + ) : ( + + )} +
- )} - + {/* Modification mode indicator */} + {(state.state === OrchestratorState.PreviewReady || state.state === OrchestratorState.Editing) && state.files['index.html'] && ( +
+ + Modify Existing App + + Changes will preserve your current design +
+ )} + +
+ {showCustomPersona && setShowCustomPersona(false)} />} + {showSkillsSelector && setShowSkillsSelector(false)} onSelect={(cmd) => setInput(prev => prev + ' ' + cmd)} />} + {state.personaCreateModalOpen && }
- {showCustomPersona && setShowCustomPersona(false)} />} - {showSkillsSelector && setShowSkillsSelector(false)} onSelect={(cmd) => setInput(prev => prev + ' ' + cmd)} />} - {state.personaCreateModalOpen && } -
-); + ); }; const LogMessage = ({ message, type }: { message: string, type: 'user' | 'system' | 'error' }) => {