UNPKG

embedia

Version:

Zero-configuration AI chatbot integration CLI - direct file copy with embedded API keys

219 lines (182 loc) 6.83 kB
const BaseAdapter = require('../BaseAdapter'); const fs = require('fs-extra'); const path = require('path'); class GatsbyAdapter extends BaseAdapter { async integrate(embediaFiles) { const results = { success: false, framework: 'gatsby', files: [], errors: [], instructions: [] }; try { // Create component files await this.createComponentFiles(embediaFiles); results.files.push('components/generated/embedia-chat/*'); // Modify or create gatsby-browser.js const browserPath = await this.setupGatsbyBrowser(); results.files.push(browserPath); // Copy static files await this.copyToStatic(embediaFiles); results.files.push('static/embedia-chat/*'); // Create Gatsby Function for API const functionPath = await this.createGatsbyFunction(embediaFiles.apiRoute); results.files.push(functionPath); // Create wrapper component for easier integration const wrapperPath = await this.createGatsbyWrapper(); results.files.push(wrapperPath); results.success = true; results.instructions = [ 'Add your AI provider API key to .env.development and .env.production', 'Run gatsby develop to see the chat widget', 'The chat will automatically appear on all pages', 'To use in specific pages only, import the EmbediaChatWrapper component' ]; } catch (error) { results.errors.push({ type: 'integration_error', message: error.message }); } return results; } async setupGatsbyBrowser() { const browserPath = 'gatsby-browser.js'; let existingContent = ''; if (await this.fileExists(browserPath)) { existingContent = await this.readFile(browserPath); } // Check if Embedia is already integrated if (existingContent.includes('embedia-chat')) { console.log('Embedia integration already exists in gatsby-browser.js'); return browserPath; } const integration = ` // Embedia Chat Integration export const onClientEntry = () => { if (typeof window !== 'undefined') { // Wait for Gatsby to fully load window.addEventListener('load', () => { // Add React and ReactDOM to window if not already there if (!window.React) { import('react').then(React => { window.React = React; }); } if (!window.ReactDOM) { import('react-dom').then(ReactDOM => { window.ReactDOM = ReactDOM; }); } // Load Embedia Chat after a short delay to ensure React is available setTimeout(() => { const script = document.createElement('script'); script.src = '/embedia-chat/index.js'; script.onload = () => { if (window.EmbediaChat && // Simplified web component logic const container = document.createElement('div'); container.id = 'embedia-chat-root'; document.body.appendChild(container); const { createRoot } = window.ReactDOM; const root = createRoot(container); } }; script.onerror = (error) => { console.error('Failed to load Embedia Chat:', error); }; document.body.appendChild(script); }, 100); }); } }; ${existingContent} `; await this.createFile(browserPath, integration); return browserPath; } async copyToStatic(embediaFiles) { const staticDir = 'static/embedia-chat'; await fs.ensureDir(path.join(this.projectPath, staticDir)); // Copy the compiled component to static directory await this.createFile(`${staticDir}/index.js`, embediaFiles.component); await this.createFile(`${staticDir}/config.json`, JSON.stringify(embediaFiles.config, null, 2)); // Create a loader script that sets up globals const loaderScript = ` // Embedia Chat Loader (function() { // Ensure React and ReactDOM are available globally if (typeof window !== 'undefined') { window.EmbediaChat = (${embediaFiles.component}); } })(); `; await this.createFile(`${staticDir}/loader.js`, loaderScript); } async createGatsbyFunction(apiRoute) { const functionPath = 'src/api/embedia-chat.js'; // Convert the API route to Gatsby Functions format const gatsbyFunction = ` // Gatsby Function for Embedia Chat API ${apiRoute} // Export for Gatsby Functions exports.handler = async (req, res) => { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } try { // Call the original POST handler const response = await POST({ json: async () => req.body, headers: req.headers }); const data = await response.json(); return res.status(200).json(data); } catch (error) { console.error('Chat API error:', error); return res.status(500).json({ error: 'Internal server error' }); } }; `; await this.createFile(functionPath, gatsbyFunction); return functionPath; } async createGatsbyWrapper() { const isTS = await this.isTypeScriptProject(); const ext = isTS ? 'tsx' : 'jsx'; const wrapperPath = `src/components/EmbediaChatWrapper.${ext}`; const wrapperContent = `import React, { useEffect, useState } from 'react'${isTS ? '\nimport type { FC } from \'react\'' : ''} ${isTS ? 'interface EmbediaChatWrapperProps {\n position?: string;\n className?: string;\n}\n\n' : ''}const EmbediaChatWrapper${isTS ? ': FC<EmbediaChatWrapperProps>' : ''} = ({ position = 'bottom-right', className = '' }) => { const [loaded, setLoaded] = useState(false); useEffect(() => { if (typeof window !== 'undefined' && !loaded) { // Check if already loaded if (document.getElementById('embedia-chat-root')) { setLoaded(true); return; } // Load the chat component const script = document.createElement('script'); script.src = '/embedia-chat/index.js'; script.onload = () => { if (window.EmbediaChat && // Simplified web component logic const container = document.createElement('div'); container.id = 'embedia-chat-root'; container.className = className; document.body.appendChild(container); const { createRoot } = window.ReactDOM; const root = createRoot(container); setLoaded(true); } }; document.body.appendChild(script); } }, [loaded, position, className]); return null; // This component doesn't render anything directly }; export default EmbediaChatWrapper`; await this.createFile(wrapperPath, wrapperContent); return wrapperPath; } } module.exports = GatsbyAdapter;