// SEO CopilotKit Provider Component // Main provider that wraps all SEO CopilotKit functionality import React, { useEffect, useMemo, useState } from 'react'; import { CopilotKit } from '@copilotkit/react-core'; import { CopilotSidebar } from '@copilotkit/react-ui'; import '@copilotkit/react-ui/styles.css'; import SEOCopilotContext from './SEOCopilotContext'; import SEOCopilotActions from './SEOCopilotActions'; import { useSEOCopilotStore } from '../../stores/seoCopilotStore'; interface SEOCopilotKitProviderProps { children: React.ReactNode; enableDebugMode?: boolean; } const SEOCopilotKitProvider: React.FC = ({ children, enableDebugMode = false }) => { const { analysisData } = useSEOCopilotStore(); // Get the CopilotKit API key from the same sources as App.tsx // Check localStorage first, then fall back to environment variable const publicApiKey = useMemo(() => { const savedKey = typeof window !== 'undefined' ? localStorage.getItem('copilotkit_api_key') : null; const envKey = process.env.REACT_APP_COPILOTKIT_API_KEY || ''; const key = (savedKey || envKey).trim(); // Validate key format if present if (key && !key.startsWith('ck_pub_')) { console.warn('SEOCopilotKitProvider: CopilotKit API key format invalid - must start with ck_pub_'); } return key; }, []); // Derive a friendly site/brand name from the URL for personalization const domainRootName = useMemo(() => { const url = analysisData?.url; if (!url) return ''; try { const withProto = url.startsWith('http') ? url : `https://${url}`; const host = new URL(withProto).hostname; const parts = host.split('.').filter(Boolean); const root = parts.length >= 2 ? parts[parts.length - 2] : parts[0] || ''; if (!root) return ''; return root.charAt(0).toUpperCase() + root.slice(1); } catch { return ''; } }, [analysisData?.url]); // Suggestions model: progressive disclosure const topLevelGroups = useMemo(() => ([ { title: 'Content analysis', message: 'Content analysis' }, { title: 'Website/URL analysis', message: 'Web URL analysis' }, { title: 'Technical SEO', message: 'Technical SEO' }, { title: 'Strategy & planning', message: 'Strategy and planning' }, { title: 'Monitoring & health', message: 'Monitoring and health' } ]), []); const subSuggestionsByGroup = useMemo(() => ({ 'Content analysis': [ { title: 'Comprehensive content analysis', message: 'Analyze content comprehensively for my site' }, { title: 'Optimize page content', message: 'Optimize page content for SEO' }, { title: 'Generate meta descriptions', message: 'Generate meta descriptions for key pages' } ], 'Web URL analysis': [ { title: 'Comprehensive SEO analysis', message: 'Run comprehensive SEO analysis for a URL' }, { title: 'Analyze page speed', message: 'Analyze page speed for a URL' }, { title: 'Analyze sitemap', message: 'Analyze sitemap for my site' }, { title: 'Generate OpenGraph tags', message: 'Generate OpenGraph tags for a URL' } ], 'Technical SEO': [ { title: 'Technical SEO audit', message: 'Run a technical SEO audit' }, { title: 'Check SEO health', message: 'Check overall SEO health' }, { title: 'Image alt text', message: 'Generate image alt text for pages' } ], 'Strategy and planning': [ { title: 'Enterprise SEO analysis', message: 'Run enterprise SEO analysis' }, { title: 'Content strategy', message: 'Analyze content strategy and recommendations' }, { title: 'Customize SEO dashboard', message: 'Customize the SEO dashboard' } ], 'Monitoring and health': [ { title: 'Website audit', message: 'Perform a website audit' }, { title: 'Update SEO charts', message: 'Update SEO charts and visualizations' }, { title: 'Explain an SEO concept', message: 'Explain an SEO concept in simple terms' } ] }), []); const [chatSuggestions, setChatSuggestions] = useState(topLevelGroups); const backChip = useMemo(() => ({ title: '← Back to categories', message: 'back' }), []); const displayedSuggestions = useMemo(() => { // Always show a back chip when not on top-level const isTop = chatSuggestions === topLevelGroups; return isTop ? chatSuggestions : [...chatSuggestions, backChip]; }, [chatSuggestions, topLevelGroups, backChip]); // Initialize the provider useEffect(() => { if (enableDebugMode) { console.log('🔧 SEO CopilotKit Provider initialized successfully'); console.log('🔑 CopilotKit API Key:', publicApiKey ? 'Configured' : 'Missing'); } }, [enableDebugMode, publicApiKey]); return ( { const websiteUrl = analysisData?.url; const urlLine = websiteUrl ? `The user's current website URL is ${websiteUrl}. If the user does not provide a URL explicitly, default to this URL.` : ''; const guidance = ` You are ALwrity's SEO Expert Assistant. ${urlLine} When greeting the user, personalize messages${domainRootName ? ` for ${domainRootName}` : ''} and keep a professional, friendly tone. Never ask for the URL if you already have it in context unless the user wants to switch URLs. Focus on actionable recommendations and use the registered tools. `.trim(); return [guidance, additionalInstructions].filter(Boolean).join('\n\n'); }} onSubmitMessage={(message: string) => { const text = (message || '').trim(); const match = Object.keys(subSuggestionsByGroup).find(key => key.toLowerCase() === text.toLowerCase()); if (match) { setChatSuggestions(subSuggestionsByGroup[match as keyof typeof subSuggestionsByGroup]); } else if (text.toLowerCase() === 'back' || text.toLowerCase() === 'categories') { setChatSuggestions(topLevelGroups); } }} observabilityHooks={{ onChatExpanded: () => { if (enableDebugMode) { console.log('🔧 SEO CopilotKit Sidebar opened'); } }, onChatMinimized: () => { if (enableDebugMode) { console.log('🔧 SEO CopilotKit Sidebar closed'); } }, }} >
{/* Suggestions controller sets progressive suggestions */} {/* SEOSuggestionsController */} {/* SEO CopilotKit Context - Provides data and instructions */} {/* SEO CopilotKit Actions - Defines available actions */} {/* Main content */}
{children}
{/* Copilot debug info removed */}
); }; export default SEOCopilotKitProvider;