import React, { forwardRef, useImperativeHandle } from 'react'; import { useCopilotAction } from '@copilotkit/react-core'; import { blogWriterApi, BlogResearchResponse } from '../../services/blogWriterApi'; import { blogWriterCache } from '../../services/blogWriterCache'; interface OutlineGeneratorProps { research: BlogResearchResponse | null; onTaskStart: (taskId: string) => void; onPollingStart: (taskId: string) => void; onModalShow?: () => void; // Callback to show progress modal immediately navigateToPhase?: (phase: string) => void; onOutlineCreated?: (outline: any[], titleOptions?: any[]) => void; // Callback when outline is created/found (for cached outlines) } const useCopilotActionTyped = useCopilotAction as any; export const OutlineGenerator = forwardRef(({ research, onTaskStart, onPollingStart, onModalShow, navigateToPhase, onOutlineCreated }, ref) => { // Expose an imperative method to trigger outline generation directly (bypass LLM) useImperativeHandle(ref, () => ({ generateNow: async () => { if (!research) { return { success: false, message: 'No research yet. Please research a topic first.' }; } // Check cache first (shared utility) const researchKeywords = research.original_keywords || research.keyword_analysis?.primary || []; const cachedOutline = blogWriterCache.getCachedOutline(researchKeywords); if (cachedOutline) { console.log('[OutlineGenerator] Using cached outline', { sections: cachedOutline.outline.length }); // Return cached result - caller should handle setting outline state return { success: true, cached: true, outline: cachedOutline.outline, title_options: cachedOutline.title_options }; } try { onModalShow?.(); const { task_id } = await blogWriterApi.startOutlineGeneration({ research }); onTaskStart(task_id); onPollingStart(task_id); return { success: true, task_id }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return { success: false, message: errorMessage }; } } })); useCopilotActionTyped({ name: 'generateOutline', description: 'Generate outline from research results using AI analysis', parameters: [], handler: async () => { if (!research) { return { success: false, message: 'No research yet. Please research a topic first.' }; } // Check cache first (shared utility) const researchKeywords = research.original_keywords || research.keyword_analysis?.primary || []; const cachedOutline = blogWriterCache.getCachedOutline(researchKeywords); if (cachedOutline) { console.log('[OutlineGenerator] Using cached outline from CopilotKit action', { sections: cachedOutline.outline.length }); // Navigate to outline phase when cached outline is found navigateToPhase?.('outline'); // Update parent state with cached outline (same as header button does) if (onOutlineCreated) { onOutlineCreated(cachedOutline.outline, cachedOutline.title_options); } return { success: true, message: `✅ Outline already exists! ${cachedOutline.outline.length} sections loaded from cache.`, cached: true, outline: cachedOutline.outline, title_options: cachedOutline.title_options }; } try { // Navigate to outline phase when outline generation starts navigateToPhase?.('outline'); // Show progress modal immediately when user clicks "Create outline" onModalShow?.(); // Start async outline generation const { task_id } = await blogWriterApi.startOutlineGeneration({ research }); // Start polling immediately after getting task_id // This ensures we catch progress messages from the very beginning onTaskStart(task_id); onPollingStart(task_id); return { success: true, message: `🧩 Outline generation started! Task ID: ${task_id}. Progress will be shown below.`, task_id: task_id }; } catch (error) { console.error('Outline generation failed:', error); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; // Provide more specific error messages based on the error type let userMessage = '❌ Outline generation failed. '; if (errorMessage.includes('503') || errorMessage.includes('overloaded')) { userMessage += 'The AI service is temporarily overloaded. Please try again in a few minutes.'; } else if (errorMessage.includes('timeout')) { userMessage += 'The request timed out. Please try again.'; } else if (errorMessage.includes('Invalid outline structure')) { userMessage += 'The AI generated an invalid response. Please try again with different research data.'; } else { userMessage += `${errorMessage}. Please try again or contact support if the problem persists.`; } return { success: false, message: userMessage }; } }, render: ({ status }: any) => { if (status === 'inProgress' || status === 'executing') { return (

🧩 Generating Outline

• Analyzing research results and content angles...

• Structuring content based on keyword analysis...

• Creating logical flow and section hierarchy...

• Optimizing for SEO and reader engagement...

); } return null; } }); return null; // This component only provides the copilot action }); export default OutlineGenerator;