embedia
Version:
Zero-configuration AI chatbot integration CLI - direct file copy with embedded API keys
379 lines (354 loc) ⢠12.1 kB
JavaScript
const inquirer = require('inquirer');
const chalk = require('chalk');
class ConfigurationWizard {
async run(initialConfig = {}) {
console.log(chalk.cyan('\nšØ Embedia Chat Configuration Wizard\n'));
const answers = await this.askQuestions(initialConfig);
const config = this.buildConfig(answers);
// Show preview
await this.showPreview(config);
// Confirm configuration
const confirmed = await this.confirmConfig();
if (confirmed) {
return config;
} else {
// Restart wizard with current config as defaults
return this.run(config);
}
}
async askQuestions(defaults) {
const questions = [
{
type: 'input',
name: 'chatbotName',
message: 'What should we call your chatbot?',
default: defaults.chatbotName || 'AI Assistant',
validate: (input) => input.length > 0 || 'Name is required'
},
{
type: 'input',
name: 'subtitle',
message: 'Add a subtitle (optional):',
default: defaults.subtitle || 'Powered by AI'
},
{
type: 'editor',
name: 'systemPrompt',
message: 'Define your chatbot\'s personality and expertise:',
default: defaults.systemPrompt || 'You are a helpful AI assistant. Be friendly, professional, and concise in your responses.'
},
{
type: 'list',
name: 'position',
message: 'Where should the chat button appear?',
choices: [
{ name: 'Bottom Right (Recommended)', value: 'bottom-right' },
{ name: 'Bottom Left', value: 'bottom-left' },
{ name: 'Top Right', value: 'top-right' },
{ name: 'Top Left', value: 'top-left' },
{ name: 'Custom (CSS selector)', value: 'custom' }
],
default: defaults.position || 'bottom-right'
}
];
// Add custom position question if needed
const answers = await inquirer.prompt(questions);
if (answers.position === 'custom') {
const customPosition = await inquirer.prompt([
{
type: 'input',
name: 'customSelector',
message: 'Enter CSS selector for chat container:',
default: '#chat-container',
validate: (input) => input.length > 0 || 'Selector is required'
}
]);
answers.customSelector = customPosition.customSelector;
}
// Color scheme questions
const colorAnswers = await this.askColorQuestions(defaults);
// Advanced options
const { wantsAdvanced } = await inquirer.prompt([
{
type: 'confirm',
name: 'wantsAdvanced',
message: 'Configure advanced options?',
default: false
}
]);
let advancedAnswers = {};
if (wantsAdvanced) {
advancedAnswers = await this.askAdvancedQuestions(defaults);
}
return { ...answers, ...colorAnswers, ...advancedAnswers };
}
async askColorQuestions(defaults) {
const { colorScheme } = await inquirer.prompt([
{
type: 'list',
name: 'colorScheme',
message: 'Choose a color scheme:',
choices: [
{ name: chalk.blue('ā Blue (Professional)'), value: 'blue' },
{ name: chalk.green('ā Green (Friendly)'), value: 'green' },
{ name: chalk.magenta('ā Purple (Creative)'), value: 'purple' },
{ name: chalk.red('ā Red (Bold)'), value: 'red' },
{ name: chalk.gray('ā Dark (Elegant)'), value: 'dark' },
{ name: chalk.yellow('ā Light (Minimal)'), value: 'light' },
{ name: chalk.gray('ā Custom'), value: 'custom' }
],
default: defaults.colorScheme || 'blue'
}
]);
if (colorScheme === 'custom') {
return await this.askCustomColorQuestions(defaults);
}
return { colorScheme };
}
async askCustomColorQuestions(defaults) {
return inquirer.prompt([
{
type: 'input',
name: 'primaryColor',
message: 'Primary color (hex):',
default: defaults.primaryColor || '#2563EB',
validate: (input) => /^#[0-9A-F]{6}$/i.test(input) || 'Invalid hex color'
},
{
type: 'input',
name: 'primaryHoverColor',
message: 'Primary hover color (hex):',
default: defaults.primaryHoverColor || '#1D4ED8',
validate: (input) => /^#[0-9A-F]{6}$/i.test(input) || 'Invalid hex color'
},
{
type: 'input',
name: 'backgroundColor',
message: 'Background color (hex):',
default: defaults.backgroundColor || '#FFFFFF',
validate: (input) => /^#[0-9A-F]{6}$/i.test(input) || 'Invalid hex color'
},
{
type: 'input',
name: 'textColor',
message: 'Text color (hex):',
default: defaults.textColor || '#1F2937',
validate: (input) => /^#[0-9A-F]{6}$/i.test(input) || 'Invalid hex color'
},
{
type: 'input',
name: 'userMessageColor',
message: 'User message bubble color (hex):',
default: defaults.userMessageColor || '#3B82F6',
validate: (input) => /^#[0-9A-F]{6}$/i.test(input) || 'Invalid hex color'
}
]);
}
async askAdvancedQuestions(defaults) {
return inquirer.prompt([
{
type: 'input',
name: 'avatarUrl',
message: 'Avatar URL (leave empty for default):',
default: defaults.avatarUrl || ''
},
{
type: 'input',
name: 'backgroundImage',
message: 'Chat background image URL (optional):',
default: defaults.backgroundImage || ''
},
{
type: 'list',
name: 'aiProvider',
message: 'Choose your AI provider:',
choices: [
{ name: 'Google Gemini (Recommended)', value: 'gemini' },
{ name: 'OpenAI (GPT-4)', value: 'openai' },
{ name: 'Anthropic Claude', value: 'anthropic' },
{ name: 'Custom Endpoint', value: 'custom' }
],
default: defaults.aiProvider || 'gemini'
},
{
type: 'input',
name: 'apiEndpoint',
message: 'API endpoint:',
default: '/api/embedia/chat',
when: (answers) => answers.aiProvider === 'custom'
},
{
type: 'number',
name: 'maxTokens',
message: 'Maximum response tokens:',
default: defaults.maxTokens || 500,
validate: (input) => input > 0 && input <= 4000 || 'Must be between 1 and 4000'
},
{
type: 'number',
name: 'temperature',
message: 'AI temperature (0-1, higher = more creative):',
default: defaults.temperature || 0.7,
validate: (input) => input >= 0 && input <= 1 || 'Must be between 0 and 1'
},
{
type: 'confirm',
name: 'enableAnalytics',
message: 'Enable usage analytics?',
default: defaults.enableAnalytics || false
},
{
type: 'confirm',
name: 'enableSound',
message: 'Enable notification sounds?',
default: defaults.enableSound || true
},
{
type: 'confirm',
name: 'persistConversations',
message: 'Save conversations locally?',
default: defaults.persistConversations || true
},
{
type: 'input',
name: 'rateLimit',
message: 'Rate limit (messages per minute):',
default: defaults.rateLimit || 10,
validate: (input) => input > 0 || 'Must be greater than 0'
}
]);
}
buildConfig(answers) {
const colorSchemes = {
blue: {
primary: '#2563EB',
primaryHover: '#1D4ED8',
background: '#FFFFFF',
text: '#1F2937',
userBubble: '#3B82F6',
aiBubble: '#F3F4F6',
userText: '#FFFFFF',
aiText: '#1F2937'
},
green: {
primary: '#10B981',
primaryHover: '#059669',
background: '#FFFFFF',
text: '#1F2937',
userBubble: '#34D399',
aiBubble: '#F3F4F6',
userText: '#FFFFFF',
aiText: '#1F2937'
},
purple: {
primary: '#8B5CF6',
primaryHover: '#7C3AED',
background: '#FFFFFF',
text: '#1F2937',
userBubble: '#A78BFA',
aiBubble: '#F3F4F6',
userText: '#FFFFFF',
aiText: '#1F2937'
},
red: {
primary: '#EF4444',
primaryHover: '#DC2626',
background: '#FFFFFF',
text: '#1F2937',
userBubble: '#F87171',
aiBubble: '#F3F4F6',
userText: '#FFFFFF',
aiText: '#1F2937'
},
dark: {
primary: '#1F2937',
primaryHover: '#111827',
background: '#111827',
text: '#F9FAFB',
userBubble: '#374151',
aiBubble: '#1F2937',
userText: '#F9FAFB',
aiText: '#F9FAFB'
},
light: {
primary: '#6B7280',
primaryHover: '#4B5563',
background: '#FFFFFF',
text: '#1F2937',
userBubble: '#E5E7EB',
aiBubble: '#F9FAFB',
userText: '#1F2937',
aiText: '#1F2937'
}
};
const colors = answers.colorScheme === 'custom'
? {
primary: answers.primaryColor,
primaryHover: answers.primaryHoverColor,
background: answers.backgroundColor,
text: answers.textColor,
userBubble: answers.userMessageColor,
aiBubble: '#F3F4F6',
userText: '#FFFFFF',
aiText: answers.textColor
}
: colorSchemes[answers.colorScheme] || colorSchemes.blue;
return {
chatbotName: answers.chatbotName,
subtitle: answers.subtitle,
systemPrompt: answers.systemPrompt,
position: answers.position,
customSelector: answers.customSelector,
themeColors: colors,
welcomeMessage: `Hello! I'm ${answers.chatbotName}. How can I help you today?`,
placeholder: 'Type your message...',
avatarUrl: answers.avatarUrl || null,
backgroundImage: answers.backgroundImage || null,
aiProvider: answers.aiProvider || 'gemini',
apiEndpoint: answers.apiEndpoint || '/api/embedia/chat',
maxTokens: answers.maxTokens || 500,
temperature: answers.temperature || 0.7,
enableAnalytics: answers.enableAnalytics || false,
enableSound: answers.enableSound !== false,
persistConversations: answers.persistConversations !== false,
rateLimit: answers.rateLimit || 10,
features: {
darkMode: colors === colorSchemes.dark,
typingIndicator: true,
messageTimestamps: true,
readReceipts: false,
fileUpload: false,
voiceInput: false
}
};
}
async showPreview(config) {
console.log(chalk.cyan('\nš Configuration Preview:\n'));
console.log(chalk.white('Name: ') + chalk.bold(config.chatbotName));
console.log(chalk.white('Subtitle: ') + config.subtitle);
console.log(chalk.white('Position: ') + config.position);
console.log(chalk.white('AI Provider: ') + config.aiProvider);
console.log(chalk.white('Primary Color: ') + chalk.hex(config.themeColors.primary)('āāā'));
console.log(chalk.white('Background: ') + chalk.hex(config.themeColors.background)('āāā'));
console.log(chalk.white('System Prompt: ') + chalk.gray(config.systemPrompt.substring(0, 50) + '...'));
if (config.enableAnalytics || config.avatarUrl || config.backgroundImage) {
console.log(chalk.yellow('\nAdvanced Features:'));
if (config.enableAnalytics) console.log(' ā Analytics enabled');
if (config.avatarUrl) console.log(' ā Custom avatar');
if (config.backgroundImage) console.log(' ā Background image');
if (config.persistConversations) console.log(' ā Conversation persistence');
}
}
async confirmConfig() {
const { confirmed } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirmed',
message: 'Does this look good?',
default: true
}
]);
return confirmed;
}
}
module.exports = ConfigurationWizard;