embedia
Version:
Zero-configuration AI chatbot integration CLI - direct file copy with embedded API keys
219 lines (182 loc) • 6.83 kB
JavaScript
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;