embedia
Version:
Zero-configuration AI chatbot integration CLI - direct file copy with embedded API keys
907 lines (750 loc) âĸ 26.2 kB
JavaScript
const BaseAdapter = require('../BaseAdapter');
const fs = require('fs-extra');
const path = require('path');
const chalk = require('chalk');
class UniversalAdapter extends BaseAdapter {
async integrate(embediaFiles) {
const componentType = embediaFiles.componentType || 'react';
console.log(chalk.yellow(`đĻ Using universal integration for ${componentType === 'webcomponent' ? 'web components' : 'React components'}\n`));
const results = {
success: false,
framework: this.framework.name || 'unknown',
universal: true,
componentType: componentType,
files: [],
errors: [],
instructions: []
};
try {
if (componentType === 'webcomponent') {
// Handle web component integration
await this.integrateWebComponent(embediaFiles, results);
} else {
// Handle React component integration (legacy)
await this.integrateReactComponent(embediaFiles, results);
}
results.success = true;
} catch (error) {
results.errors.push({
type: 'integration_error',
message: error.message
});
}
return results;
}
async integrateWebComponent(embediaFiles, results) {
// Create web component files in project root or public directory
const publicDir = path.join(this.projectPath, 'public');
const hasPublic = await fs.pathExists(publicDir);
const webComponentDir = hasPublic ? publicDir : this.projectPath;
// Copy web component bundle
const webComponentPath = path.join(webComponentDir, 'embedia-chatbot.js');
await fs.writeFile(webComponentPath, embediaFiles.component);
results.files.push(hasPublic ? 'public/embedia-chatbot.js' : 'embedia-chatbot.js');
// Create example HTML
const examplePath = await this.createWebComponentExample(embediaFiles, webComponentDir);
results.files.push(examplePath);
// Create integration examples for different frameworks
const examplesDir = await this.createWebComponentIntegrationExamples(embediaFiles);
results.files.push(`${examplesDir}/*`);
// Create comprehensive guide
const guidePath = await this.createWebComponentGuide(embediaFiles);
results.files.push(guidePath);
results.instructions = [
'Web component integration complete!',
'⨠Universal chatbot works in any framework',
'',
'đ Quick Start:',
'1. Open embedia-example.html to test',
'2. Copy integration code from embedia-examples/',
'3. See EMBEDIA_WEB_COMPONENT_GUIDE.md for details',
'',
'đą Works with: React, Vue, Angular, WordPress, Vanilla HTML'
];
}
async integrateReactComponent(embediaFiles, results) {
// Original React component integration logic
await this.createComponentFiles(embediaFiles);
results.files.push('components/generated/embedia-chat/*');
// Create standalone HTML example
const htmlPath = await this.createStandaloneHTML(embediaFiles);
results.files.push(htmlPath);
// Create integration examples for various frameworks
const examplesPath = await this.createIntegrationExamples();
results.files.push(examplesPath);
// Add manual integration instructions instead of creating wrapper
results.instructions.push(...this.getIntegrationInstructions());
// Create public/static files for direct access
const publicFiles = await this.createPublicFiles(embediaFiles);
results.files.push(...publicFiles);
// Create detailed integration guide
const guidePath = await this.createIntegrationGuide();
results.files.push(guidePath);
results.instructions = [
'Universal integration files created successfully!',
'See EMBEDIA_INTEGRATION_GUIDE.md for detailed instructions',
'Open embedia-chat-demo.html in a browser to test the chat',
'Copy the integration code for your specific framework'
];
}
async createWebComponentExample(embediaFiles, webComponentDir) {
const botId = embediaFiles.config?.botId || 'embedia-chatbot';
const exampleHTML = `
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Embedia Chatbot Example</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 40px;
}
.demo-content {
background: #f4f4f4;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>đ¤ Embedia Chatbot Demo</h1>
<p>This page demonstrates the Embedia web component integration.</p>
<p>The chatbot should appear in the bottom-right corner.</p>
</div>
<div class="demo-content">
<h2>How it works</h2>
<p>The chatbot is loaded as a web component using just two lines of code:</p>
<pre><code><script src="embedia-chatbot.js"></script>
<embedia-chatbot bot-id="${botId}"></embedia-chatbot></code></pre>
<h3>Features</h3>
<ul>
<li>â
Universal compatibility (works everywhere)</li>
<li>â
No framework dependencies</li>
<li>â
Shadow DOM encapsulation</li>
<li>â
Responsive design</li>
<li>â
Easy customization</li>
</ul>
</div>
</div>
<!-- Embedia Chatbot Integration -->
<script src="${path.basename(webComponentDir) === 'public' ? '' : './'}embedia-chatbot.js"></script>
<embedia-chatbot
bot-id="${botId}"
theme="default"
position="bottom-right">
</embedia-chatbot>
</body>
</html>`;
const examplePath = path.join(webComponentDir, 'embedia-example.html');
await fs.writeFile(examplePath, exampleHTML);
return path.basename(webComponentDir) === 'public' ? 'public/embedia-example.html' : 'embedia-example.html';
}
async createWebComponentIntegrationExamples(embediaFiles) {
const examplesDir = path.join(this.projectPath, 'embedia-examples');
await fs.ensureDir(examplesDir);
const botId = embediaFiles.config?.botId || 'embedia-chatbot';
// React example
const reactExample = `// React Integration Example
import { useEffect } from 'react';
function App() {
useEffect(() => {
// Load the web component script
const script = document.createElement('script');
script.src = '/embedia-chatbot.js';
script.async = true;
document.head.appendChild(script);
return () => {
// Cleanup
const existingScript = document.querySelector('script[src="/embedia-chatbot.js"]');
if (existingScript) {
document.head.removeChild(existingScript);
}
};
}, []);
return (
<div className="App">
<h1>My React App</h1>
{/* Your app content */}
{/* Embedia Chatbot */}
<embedia-chatbot bot-id="${botId}"></embedia-chatbot>
</div>
);
}
export default App;`;
// Vue example
const vueExample = `<!-- Vue Integration Example -->
<template>
<div id="app">
<h1>My Vue App</h1>
<!-- Your app content -->
<!-- Embedia Chatbot -->
<embedia-chatbot bot-id="${botId}"></embedia-chatbot>
</div>
</template>
<script>
export default {
name: 'App',
mounted() {
// Load the web component script
const script = document.createElement('script');
script.src = '/embedia-chatbot.js';
script.async = true;
document.head.appendChild(script);
},
beforeUnmount() {
// Cleanup
const existingScript = document.querySelector('script[src="/embedia-chatbot.js"]');
if (existingScript) {
document.head.removeChild(existingScript);
}
}
}
</script>`;
// WordPress example
const wordPressExample = `
// WordPress Integration Example
// Add this to your theme's functions.php file
function enqueue_embedia_chatbot() {
wp_enqueue_script(
'embedia-chatbot',
get_template_directory_uri() . '/embedia-chatbot.js',
array(),
'1.0.0',
true
);
}
add_action('wp_enqueue_scripts', 'enqueue_embedia_chatbot');
// Add this to your theme's footer.php before </body>
<embedia-chatbot bot-id="${botId}"></embedia-chatbot>`;
// Vanilla HTML example
const vanillaExample = `
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vanilla HTML Integration</title>
</head>
<body>
<h1>My Website</h1>
<!-- Your website content -->
<!-- Embedia Chatbot Integration -->
<script src="embedia-chatbot.js"></script>
<embedia-chatbot
bot-id="${botId}"
theme="default"
position="bottom-right">
</embedia-chatbot>
</body>
</html>`;
// Write example files
await fs.writeFile(path.join(examplesDir, 'react-integration.jsx'), reactExample);
await fs.writeFile(path.join(examplesDir, 'vue-integration.vue'), vueExample);
await fs.writeFile(path.join(examplesDir, 'wordpress-integration.php'), wordPressExample);
await fs.writeFile(path.join(examplesDir, 'vanilla-html.html'), vanillaExample);
return 'embedia-examples';
}
async createWebComponentGuide(embediaFiles) {
const botId = embediaFiles.config?.botId || 'embedia-chatbot';
const guide = `# Embedia Web Component Integration Guide
## Overview
Your Embedia chatbot has been generated as a universal web component that works in any web environment - React, Vue, Angular, WordPress, or plain HTML.
## Quick Start
### 1. Basic Integration
\`\`\`html
<script src="embedia-chatbot.js"></script>
<embedia-chatbot bot-id="${botId}"></embedia-chatbot>
\`\`\`
### 2. Test the Integration
Open \`embedia-example.html\` in your browser to see the chatbot in action.
## Framework-Specific Integration
### React/Next.js
See \`embedia-examples/react-integration.jsx\` for complete example.
Key points:
- Load script in useEffect
- Use the custom element directly in JSX
- Handle cleanup on unmount
### Vue.js
See \`embedia-examples/vue-integration.vue\` for complete example.
Key points:
- Load script in mounted() hook
- Use custom element in template
- Handle cleanup in beforeUnmount()
### WordPress
See \`embedia-examples/wordpress-integration.php\` for complete example.
Key points:
- Enqueue script in functions.php
- Add custom element to theme template
- Use WordPress hooks for proper loading
### Vanilla HTML
See \`embedia-examples/vanilla-html.html\` for complete example.
Simply include the script and add the custom element anywhere in your HTML.
## Customization
### Attributes
- \`bot-id\`: Your unique bot identifier (required)
- \`theme\`: Visual theme (\`default\`, \`dark\`, \`light\`)
- \`position\`: Position on screen (\`bottom-right\`, \`bottom-left\`, \`top-right\`, \`top-left\`)
- \`api-endpoint\`: Custom API endpoint (optional)
### Example with Customization
\`\`\`html
<embedia-chatbot
bot-id="${botId}"
theme="dark"
position="bottom-left">
</embedia-chatbot>
\`\`\`
## Browser Support
- Chrome 54+
- Firefox 63+
- Safari 10.1+
- Edge 79+
## Troubleshooting
### Common Issues
1. **Chatbot doesn't appear**
- Check that the script is loaded properly
- Ensure the custom element is in the DOM
- Check browser console for errors
2. **Styling conflicts**
- Web component uses Shadow DOM for style isolation
- Custom CSS won't affect the chatbot internals
- Use theme attributes for styling
3. **Framework compatibility**
- All modern frameworks support custom elements
- For older versions, you may need polyfills
## Support
For help and documentation, visit [https://docs.embedia.ai](https://docs.embedia.ai)
Generated by Embedia CLI v2.3.0
`;
const guidePath = path.join(this.projectPath, 'EMBEDIA_WEB_COMPONENT_GUIDE.md');
await fs.writeFile(guidePath, guide);
return 'EMBEDIA_WEB_COMPONENT_GUIDE.md';
}
async createStandaloneHTML(embediaFiles) {
const htmlPath = 'embedia-chat-demo.html';
const htmlContent = `
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Embedia Chat Demo</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 10px;
}
.subtitle {
color: #666;
margin-bottom: 30px;
}
.info {
background: #f0f9ff;
border: 1px solid #e0f2fe;
padding: 20px;
border-radius: 6px;
margin-bottom: 20px;
}
code {
background: #f3f4f6;
padding: 2px 6px;
border-radius: 3px;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>Embedia Chat Demo</h1>
<p class="subtitle">This is a standalone demo of your Embedia Chat widget</p>
<div class="info">
<h3>âšī¸ Integration Test</h3>
<p>The chat widget should appear in the bottom-right corner of this page.</p>
<p>If you don't see it, check the browser console for errors.</p>
</div>
<h2>Quick Integration</h2>
<p>To add this chat to your website, include these scripts before the closing <code></body></code> tag:</p>
<pre><code><!-- Embedia Chat Integration -->
<div id="embedia-chat-root"></div>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script>
// Load Embedia Chat Component
${embediaFiles.component}
// Initialize Chat
// Simplified mounting logic
</script></code></pre>
</div>
<!-- Embedia Chat Integration -->
<embedia-chatbot></embedia-chatbot>
<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
<script>
// Embedia Chat Configuration
window.EMBEDIA_CONFIG = ${JSON.stringify(embediaFiles.config, null, 2)};
// Load Component
${embediaFiles.component}
// Initialize
if (window.EmbediaChat && // Simplified web component logic
const container = document.getElementById('embedia-chat-root');
const root =
}
</script>
</body>
</html>`;
await this.createFile(htmlPath, htmlContent);
return htmlPath;
}
async createIntegrationExamples() {
const examplesPath = 'embedia-integration-examples.js';
const examples = `// Embedia Chat Integration Examples for Various Frameworks
// ==========================================
// Vanilla JavaScript / HTML
// ==========================================
function integrateVanillaJS() {
// Add to your HTML
const chatRoot = document.createElement('div');
chatRoot.id = 'embedia-chat-root';
document.body.appendChild(chatRoot);
// Load React if not already loaded
if (!window.React) {
const reactScript = document.createElement('script');
reactScript.src = 'https://unpkg.com/react@18/umd/react.production.min.js';
document.head.appendChild(reactScript);
}
if (!window.ReactDOM) {
const reactDOMScript = document.createElement('script');
reactDOMScript.src = 'https://unpkg.com/react-dom@18/umd/react-dom.production.min.js';
document.head.appendChild(reactDOMScript);
}
// Load Embedia Chat
import('/components/generated/embedia-chat/index.js').then((module) => {
const EmbediaChat = module.default;
// Simplified mounting
});
}
// ==========================================
// React (Create React App, Vite, etc.)
// ==========================================
import { useEffect } from 'react';
function App() {
useEffect(() => {
import('/components/generated/embedia-chat/index.js').then((module) => {
const EmbediaChat = module.default;
const container = document.getElementById('embedia-chat-root');
if (container && !container.hasChildNodes()) {
// Simplified mounting
}
});
}, []);
return (
<div>
{/* Your app content */}
<embedia-chatbot></embedia-chatbot>
</div>
);
}
// ==========================================
// Vue.js
// ==========================================
export default {
mounted() {
this.loadEmbediaChat();
},
methods: {
async loadEmbediaChat() {
// Ensure React is available
// React checks removed
// Load Embedia Chat
const module = await import('/components/generated/embedia-chat/index.js');
const EmbediaChat = module.default;
const container = document.getElementById('embedia-chat-root');
if (container) {
// Simplified mounting
}
},
async loadReact() {
// Load React for Vue integration
return new Promise((resolve) => {
const script1 = document.createElement('script');
script1.src = 'https://unpkg.com/react@18/umd/react.production.min.js';
script1.onload = () => {
const script2 = document.createElement('script');
script2.src = 'https://unpkg.com/react-dom@18/umd/react-dom.production.min.js';
script2.onload = resolve;
document.head.appendChild(script2);
};
document.head.appendChild(script1);
});
}
},
template: '<div id="app"><embedia-chatbot></embedia-chatbot></div>'
}
// ==========================================
// Angular
// ==========================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
template: '<embedia-chatbot></embedia-chatbot>'
})
export class AppComponent implements OnInit {
ngOnInit() {
this.loadEmbediaChat();
}
async loadEmbediaChat() {
// Load React if needed
// React checks removed
// Dynamic import
const module = await import('/components/generated/embedia-chat/index.js');
const EmbediaChat = module.default;
const container = document.getElementById('embedia-chat-root');
if (container) {
const root = (window as any).
}
}
loadReactLibraries(): Promise<void> {
// Implementation to load React libraries
return new Promise((resolve) => {
// Load scripts...
resolve();
});
}
}
// ==========================================
// Svelte
// ==========================================
<script>
import { onMount } from 'svelte';
onMount(async () => {
// Ensure React is loaded
if (!// Simplified web component logic
// Load React scripts
}
// Load Embedia Chat
const module = await import('/components/generated/embedia-chat/index.js');
const EmbediaChat = module.default;
const container = document.getElementById('embedia-chat-root');
if (container) {
const root = window.
}
});
</script>
<embedia-chatbot></embedia-chatbot>
// ==========================================
// WordPress Plugin
// ==========================================
function embedia_chat_enqueue_scripts() {
// Enqueue React
wp_enqueue_script('react', 'https://unpkg.com/react@18/umd/react.production.min.js');
wp_enqueue_script('react-dom', 'https://unpkg.com/react-dom@18/umd/react-dom.production.min.js');
// Enqueue Embedia Chat
wp_enqueue_script(
'embedia-chat',
plugin_dir_url(__FILE__) . 'components/generated/embedia-chat/index.js',
array('react', 'react-dom'),
'1.0.0',
true
);
// Initialize
wp_add_inline_script('embedia-chat', '
document.addEventListener("DOMContentLoaded", function() {
if (window.EmbediaChat && // Simplified web component logic
const container = document.createElement("div");
container.id = "embedia-chat-root";
document.body.appendChild(container);
const root =
}
});
');
}
add_action('wp_enqueue_scripts', 'embedia_chat_enqueue_scripts');
`;
await this.createFile(examplesPath, examples);
return examplesPath;
}
getIntegrationInstructions() {
return [
'Manual integration required. Add Embedia Chat to your application:',
'',
'For React applications:',
' import EmbediaChat from "/components/generated/embedia-chat"',
' <EmbediaChat />',
'',
'For vanilla JavaScript:',
' <script type="module">',
' import("/components/generated/embedia-chat/index.js").then(module => {',
' const EmbediaChat = module.default;',
' const container = document.getElementById("embedia-chat-root");',
' // Simplified mounting',
' });',
' </script>',
' <embedia-chatbot></embedia-chatbot>',
'',
'Note: The component is pre-compiled and works with both JavaScript and TypeScript.'
];
}
async createPublicFiles(embediaFiles) {
const publicFiles = [];
// Try to find public directory
const publicDirs = ['public', 'static', 'dist', 'www'];
let publicDir = null;
for (const dir of publicDirs) {
if (await this.fileExists(dir)) {
publicDir = dir;
break;
}
}
if (publicDir) {
const embediaPublicDir = `${publicDir}/embedia-chat`;
await fs.ensureDir(path.join(this.projectPath, embediaPublicDir));
// Copy files to public directory
await this.createFile(`${embediaPublicDir}/embedia-chat.js`, embediaFiles.component);
await this.createFile(`${embediaPublicDir}/config.json`, JSON.stringify(embediaFiles.config, null, 2));
publicFiles.push(`${embediaPublicDir}/*`);
}
return publicFiles;
}
async createIntegrationGuide() {
const guidePath = 'EMBEDIA_INTEGRATION_GUIDE.md';
const guide = `# Embedia Chat - Universal Integration Guide
## đ Quick Start
### Option 1: Standalone HTML
Open \`embedia-chat-demo.html\` in your browser to see a working demo.
### Option 2: Copy Integration Code
Check \`embedia-integration-examples.js\` for framework-specific examples.
### Option 3: Use Wrapper Component
Import \`components/EmbediaChatWrapper\` in your React/Next.js/Gatsby app.
## đĻ File Structure
\`\`\`
your-project/
âââ components/
â âââ generated/
â â âââ embedia-chat/ # Core chat component
â âââ EmbediaChatWrapper.* # React wrapper component
âââ embedia-chat-demo.html # Standalone demo
âââ embedia-integration-examples.js # Integration examples
\`\`\`
## đ§ Integration Steps
### 1. Environment Setup
Create a \`.env\` file with your AI provider key:
\`\`\`
GEMINI_API_KEY=your_api_key_here
# or
OPENAI_API_KEY=your_api_key_here
\`\`\`
### 2. Choose Your Integration Method
#### Method A: Dynamic Import (Recommended for React/Vue/Angular)
\`\`\`javascript
import('/components/generated/embedia-chat/index.js').then((module) => {
const EmbediaChat = module.default;
// Render the component
});
\`\`\`
#### Method B: Script Tag (For vanilla JS/HTML)
\`\`\`html
<embedia-chatbot></embedia-chatbot>
<script src="/path/to/embedia-chat.js"></script>
<script>
// Simplified mounting
</script>
\`\`\`
#### Method C: Wrapper Component (For React apps)
\`\`\`jsx
import EmbediaChatWrapper from './components/EmbediaChatWrapper';
function App() {
return (
<div>
<YourContent />
<EmbediaChatWrapper />
</div>
);
}
\`\`\`
### 3. API Endpoint Setup
The chat requires an API endpoint to communicate with your AI provider.
#### For Express.js:
\`\`\`javascript
app.post('/api/embedia/chat', async (req, res) => {
// API handler code from api/chat/route.js
});
\`\`\`
#### For serverless/Vercel:
Copy \`components/generated/embedia-chat/api/chat/route.js\` to your API directory.
## đ¨ Customization
Edit \`components/generated/embedia-chat/config.json\`:
\`\`\`json
{
"chatbotName": "Your Bot Name",
"themeColors": {
"primary": "#2563EB",
"background": "#FFFFFF"
},
"position": "bottom-right"
}
\`\`\`
## đ Troubleshooting
### Chat not appearing?
1. Check browser console for errors
2. Ensure React and ReactDOM are loaded
3. Verify the component path is correct
4. Check that the container element exists
### API errors?
1. Verify your API key is set correctly
2. Check network tab for failed requests
3. Ensure CORS is configured if needed
### Styling issues?
1. Check for CSS conflicts with existing styles
2. Increase z-index if chat is hidden behind elements
3. Use the wrapper component for better isolation
## đ Framework-Specific Notes
### React/Next.js/Gatsby
- Use the provided wrapper component
- Ensure React 18+ for createRoot API
### Vue.js
- Load React as external dependency
- Mount in mounted() lifecycle hook
### Angular
- Add React types: \`npm install @types/react @types/react-dom\`
- Use ngAfterViewInit for mounting
### WordPress
- Enqueue React before Embedia
- Use wp_add_inline_script for initialization
## đ Need Help?
1. Check the demo file for a working example
2. Review framework-specific examples
3. Visit https://docs.embedia.ai
4. Run \`npx embedia doctor\` for diagnostics
---
**Embedia Chat v2.0** - Universal Integration
`;
await this.createFile(guidePath, guide);
return guidePath;
}
}
module.exports = UniversalAdapter;