UNPKG

besper-frontend-site-dev-0935

Version:

Professional B-esper Frontend Site - Site-wide integration toolkit for full website bot deployment

921 lines (808 loc) 27.2 kB
// src/components/demo-interface.js - Full Demo Interface // Extracted from tobedeveloped_demo.html /** * BesperDemoInterface - Complete demo interface with chat and knowledge management * This replicates the functionality from tobedeveloped_demo.html */ export class BesperDemoInterface { constructor(botId, options = {}) { this.botId = botId; this.options = { environment: 'dev', container: 'besperDemoContainer', ...options, }; this.state = { threadId: null, isProcessing: false, connectedWebsite: null, currentKnowledgeData: [], currentPage: 1, itemsPerPage: 10, knowledgePollingInterval: null, isInitialListKnowledge: true, robotTxtMessageSent: false, hasShownInitialKnowledgeQuestions: false, dataPolicyUrl: null, messages: [], }; this.els = {}; this.translations = this.getTranslations(); } /** * Initialize the demo interface */ async init() { if (this.isMobileDevice()) { console.log('📱 Mobile device detected - demo interface hidden'); return null; } // Create container if it doesn't exist this.createContainer(); // Inject CSS this.injectCSS(); // Create HTML structure this.createHTML(); // Get element references this.getElements(); // Set up event listeners this.setupEventListeners(); // Initialize the demo await this.initializeDemo(); return this; } /** * Check if device is mobile */ isMobileDevice() { return ( window.innerWidth <= 768 || /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent ) ); } /** * Create container element if it doesn't exist */ createContainer() { let container = document.getElementById(this.options.container); if (!container) { container = document.createElement('div'); container.id = this.options.container; document.body.appendChild(container); } this.container = container; } /** * Get translations for the interface */ getTranslations() { return { title: 'B-esper AI Assistant Demo', subtitle: 'Interactive Knowledge Integration', connecting: 'Connecting...', online: 'Online', enterMessage: 'Type your message...', connect: 'Connect', enterValidUrl: 'Please enter a valid URL', disconnectFirst: 'Please disconnect the current website first', connectionFailed: 'Failed to connect to website', websiteConnected: 'Website connected', websiteDisconnected: 'Website disconnected', downloaded: 'Conversation downloaded', downloadFailed: 'Download failed', issueError: 'Sorry, I encountered an issue. Please try again.', robotTxtDetected: 'What can you tell me about this website?', demoQuestion1: 'What services do you offer?', demoQuestion2: 'How can I get started?', demoQuestion3: 'What are your business hours?', pages: 'pages', words: 'words', pageName: 'Page Name', source: 'Source', connectedWebsiteTitle: 'Connected Website', analyzingWebsite: 'Analyzing website content', connectingWebsite: 'This may take a few moments...', dataPolicy: 'Data Policy', }; } /** * Inject CSS styles for the demo interface */ injectCSS() { if (document.getElementById('besper-demo-styles')) return; const style = document.createElement('style'); style.id = 'besper-demo-styles'; style.textContent = ` /* Demo Interface Styles - Extracted from tobedeveloped_demo.html */ #${this.options.container} .besper-demo-wrapper { width: 100%; height: 900px; max-height: 90vh; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif; background: #ffffff; overflow: hidden; display: flex; flex-direction: column; position: relative; border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.06), 0 2px 8px rgba(0, 0, 0, 0.04); } #${this.options.container} { width: 100%; max-width: 100%; margin: 0 auto; } #${this.options.container} .besper-demo-wrapper * { margin: 0; padding: 0; box-sizing: border-box; } #${this.options.container} .besper-demo-header { background: linear-gradient(135deg, #ffffff 0%, #f8fafe 100%); padding: 20px 24px; border-bottom: 1px solid rgba(88, 151, 222, 0.1); text-align: center; flex-shrink: 0; position: relative; overflow: hidden; } #${this.options.container} .besper-header-title { font-size: 18px; font-weight: 600; color: #022d54; margin-bottom: 4px; position: relative; display: inline-block; } #${this.options.container} .besper-header-subtitle { font-size: 13px; color: #6b7684; display: flex; align-items: center; justify-content: center; gap: 8px; position: relative; z-index: 1; } #${this.options.container} .besper-live-indicator { display: inline-flex; align-items: center; gap: 6px; background: rgba(16, 185, 129, 0.1); padding: 4px 10px; border-radius: 20px; font-size: 11px; font-weight: 600; color: #10b981; } #${this.options.container} .besper-live-dot { width: 6px; height: 6px; background: #10b981; border-radius: 50%; animation: pulse 2s ease-in-out infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } #${this.options.container} .besper-demo-main { flex: 1; display: flex; background: #fafbfc; overflow: hidden; min-height: 0; } #${this.options.container} .besper-chat-section { background: #ffffff; display: flex; flex-direction: column; border-right: 1px solid #e1e7ef; min-height: 0; flex: 1; transition: flex 0.4s cubic-bezier(0.4, 0, 0.2, 1); max-width: 50%; overflow: hidden; } #${this.options.container} .besper-chat-header { padding: 16px 24px; border-bottom: 1px solid rgba(229, 231, 235, 0.8); display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; } #${this.options.container} .besper-assistant-info { display: flex; align-items: center; gap: 12px; } #${this.options.container} .besper-logo-avatar { width: 40px; height: 40px; background-color: #003057; border-radius: 50%; display: flex; justify-content: center; align-items: center; color: white; font-size: 24px; font-weight: bold; font-family: Arial, sans-serif; } #${this.options.container} .besper-assistant-details h3 { font-size: 14px; font-weight: 600; color: #1a1f2c; margin-bottom: 2px; } #${this.options.container} .besper-assistant-status { font-size: 12px; transition: color 0.3s ease; } #${this.options.container} .besper-assistant-status.connecting { color: #f59e0b; } #${this.options.container} .besper-assistant-status.online { color: #10b981; } #${this.options.container} .besper-chat-actions { display: flex; gap: 8px; } #${this.options.container} .besper-icon-btn { width: 36px; height: 36px; background: #ffffff; border: 1px solid #e1e7ef; border-radius: 10px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: #6b7684; transition: all 0.2s ease; position: relative; overflow: hidden; } #${this.options.container} .besper-icon-btn:hover { background: #f0f4f8; border-color: #5897de; color: #5897de; transform: translateY(-1px); } #${this.options.container} .besper-messages-container { flex: 1; padding: 20px; overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column; gap: 16px; min-height: 0; scroll-behavior: smooth; } #${this.options.container} .besper-message { display: flex; gap: 12px; align-items: flex-start; animation: messageSlide 0.3s ease-out; } @keyframes messageSlide { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } #${this.options.container} .besper-message.user { flex-direction: row-reverse; } #${this.options.container} .besper-avatar { width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 600; flex-shrink: 0; } #${this.options.container} .besper-avatar.ai { background-color: #003057; color: white; font-size: 20px; font-weight: bold; font-family: Arial, sans-serif; } #${this.options.container} .besper-avatar.user { background: #f0f4f8; color: #022d54; } #${this.options.container} .besper-message-bubble { max-width: 70%; padding: 12px 16px; border-radius: 16px; font-size: 14px; line-height: 1.5; word-wrap: break-word; color: #1a1f2c; overflow: hidden; } #${this.options.container} .besper-message.ai .besper-message-bubble { background: #f0f4f8; color: #1a1f2c; border-bottom-left-radius: 4px; } #${this.options.container} .besper-message.user .besper-message-bubble { background: linear-gradient(135deg, #5897de 0%, #4a85c9 100%); color: white; border-bottom-right-radius: 4px; } #${this.options.container} .besper-input-area { padding: 16px 20px; border-top: 1px solid rgba(229, 231, 235, 0.8); background: #ffffff; flex-shrink: 0; } #${this.options.container} .besper-input-wrapper { display: flex; gap: 12px; align-items: center; background: #ffffff; border: 1px solid #e1e7ef; border-radius: 24px; padding: 4px; transition: border-color 0.2s ease; } #${this.options.container} .besper-input-wrapper:focus-within { border-color: #5897de; box-shadow: 0 0 0 3px rgba(88, 151, 222, 0.1); } #${this.options.container} .besper-input-field { flex: 1; padding: 10px 16px; border: none; font-size: 14px; outline: none; background: transparent; border-radius: 20px; } #${this.options.container} .besper-send-btn { width: 36px; height: 36px; background: linear-gradient(135deg, #5897de 0%, #4a85c9 100%); border: none; border-radius: 18px; color: white; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; } #${this.options.container} .besper-send-btn:hover:not(:disabled) { transform: scale(1.05); box-shadow: 0 4px 12px rgba(88, 151, 222, 0.3); } #${this.options.container} .besper-send-btn:disabled { opacity: 0.5; cursor: not-allowed; } #${this.options.container} .besper-knowledge-panel { background: #ffffff; display: flex; flex-direction: column; min-height: 0; padding: 24px; overflow-y: auto; gap: 20px; flex: 1; transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); position: relative; max-width: 50%; } #${this.options.container} .besper-connection-card { background: #f8f9fb; border-radius: 12px; padding: 20px; border: 1px solid #e1e7ef; transition: all 0.2s ease; } #${this.options.container} .besper-url-input-wrapper { display: flex; gap: 8px; align-items: center; background: #ffffff; border: 1px solid #e1e7ef; border-radius: 24px; padding: 4px; transition: all 0.2s ease; } #${this.options.container} .besper-url-input { flex: 1; padding: 10px 16px; border: none; font-size: 13px; outline: none; background: transparent; border-radius: 20px; } #${this.options.container} .besper-connect-btn { padding: 8px 20px; background: linear-gradient(135deg, #5897de 0%, #4a85c9 100%); color: white; border: none; border-radius: 20px; font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; white-space: nowrap; } #${this.options.container} .besper-connect-btn:hover:not(:disabled) { transform: scale(1.05); box-shadow: 0 4px 12px rgba(88, 151, 222, 0.3); } #${this.options.container} .besper-connect-btn:disabled { opacity: 0.5; cursor: not-allowed; } #${this.options.container} .besper-typing-indicator { display: flex; gap: 10px; align-items: center; } #${this.options.container} .besper-typing-bubble { background: #f0f4f8; padding: 12px 16px; border-radius: 16px; border-bottom-left-radius: 4px; display: flex; gap: 4px; } #${this.options.container} .besper-typing-dot { width: 6px; height: 6px; background: #9ca3af; border-radius: 50%; animation: typing 1.4s ease-in-out infinite; } #${this.options.container} .besper-typing-dot:nth-child(2) { animation-delay: 0.2s; } #${this.options.container} .besper-typing-dot:nth-child(3) { animation-delay: 0.4s; } @keyframes typing { 0%, 60%, 100% { transform: translateY(0); opacity: 0.5; } 30% { transform: translateY(-10px); opacity: 1; } } /* Hide on mobile devices */ @media (max-width: 768px) { #${this.options.container} { display: none !important; } } `; document.head.appendChild(style); } /** * Create the HTML structure for the demo interface */ createHTML() { this.container.innerHTML = ` <div class="besper-demo-wrapper" id="besperDemoWrapper"> <div class="besper-demo-header"> <div class="besper-header-title">${this.translations.title}</div> <div class="besper-header-subtitle"> ${this.translations.subtitle} <div class="besper-live-indicator"> <div class="besper-live-dot"></div> Live Demo </div> </div> </div> <div class="besper-demo-main"> <div class="besper-chat-section"> <div class="besper-chat-header"> <div class="besper-assistant-info"> <div class="besper-logo-avatar">B</div> <div class="besper-assistant-details"> <h3>B-esper Assistant</h3> <div class="besper-assistant-status connecting" id="besperStatus">${this.translations.connecting}</div> </div> </div> <div class="besper-chat-actions"> <button class="besper-icon-btn" id="besperDownloadBtn" title="Download conversation"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/> </svg> </button> <button class="besper-icon-btn" id="besperRestartBtn" title="Restart conversation"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M23 4v6h-6M1 20v-6h6M20.49 9A9 9 0 005.64 5.64L1 10m22 4a9 9 0 01-14.85 3.36L23 14"/> </svg> </button> <button class="besper-icon-btn" id="besperDeleteBtn" title="Delete conversation"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/> </svg> </button> </div> </div> <div class="besper-messages-container" id="besperMessages"> <!-- Messages will be added here --> </div> <div class="besper-input-area"> <div class="besper-input-wrapper"> <input type="text" class="besper-input-field" id="besperInput" placeholder="${this.translations.enterMessage}"> <button class="besper-send-btn" id="besperSendBtn"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <line x1="22" y1="2" x2="11" y2="13"></line> <polygon points="22,2 15,22 11,13 2,9 22,2"></polygon> </svg> </button> </div> </div> </div> <div class="besper-knowledge-panel"> <div class="besper-connection-card" id="besperConnectionArea"> <h3 style="font-size: 16px; font-weight: 600; color: #1a1f2c; margin-bottom: 12px;">Connect Website</h3> <p style="font-size: 13px; color: #6b7684; margin-bottom: 16px;">Connect a website to enable AI analysis and knowledge extraction</p> <div class="besper-url-input-wrapper"> <input type="url" class="besper-url-input" id="besperUrlInput" placeholder="Enter website URL..."> <button class="besper-connect-btn" id="besperConnectBtn">${this.translations.connect}</button> </div> </div> <div id="besperKnowledgeList" style="display: none;"> <!-- Knowledge content will be added here --> </div> </div> </div> </div> `; } /** * Get references to DOM elements */ getElements() { this.els = { status: document.getElementById('besperStatus'), messages: document.getElementById('besperMessages'), input: document.getElementById('besperInput'), sendBtn: document.getElementById('besperSendBtn'), downloadBtn: document.getElementById('besperDownloadBtn'), restartBtn: document.getElementById('besperRestartBtn'), deleteBtn: document.getElementById('besperDeleteBtn'), connectionArea: document.getElementById('besperConnectionArea'), urlInput: document.getElementById('besperUrlInput'), connectBtn: document.getElementById('besperConnectBtn'), knowledgeList: document.getElementById('besperKnowledgeList'), }; } /** * Set up event listeners */ setupEventListeners() { // Send message this.els.sendBtn.addEventListener('click', () => this.sendMessage()); this.els.input.addEventListener('keypress', e => { if (e.key === 'Enter') this.sendMessage(); }); // Connect website this.els.connectBtn.addEventListener('click', () => this.connectWebsite()); // Download conversation this.els.downloadBtn.addEventListener('click', () => this.downloadConversation() ); // Restart conversation this.els.restartBtn.addEventListener('click', () => this.restartConversation() ); // Delete conversation this.els.deleteBtn.addEventListener('click', () => this.deleteConversation() ); } /** * Initialize the demo with API connection */ async initializeDemo() { try { // Generate a unique thread ID this.state.threadId = 'demo-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9); // Update status to online this.els.status.textContent = this.translations.online; this.els.status.classList.remove('connecting'); this.els.status.classList.add('online'); console.log('[SUCCESS] Demo interface initialized successfully'); } catch (error) { console.error('[ERROR] Failed to initialize demo:', error); } } /** * Add a message to the chat */ addMessage(content, sender = 'ai') { const messageDiv = document.createElement('div'); messageDiv.className = `besper-message ${sender}`; const messageId = 'msg-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9); messageDiv.id = `message-${messageId}`; messageDiv.innerHTML = ` <div class="besper-avatar ${sender}">${sender === 'ai' ? 'B' : 'U'}</div> <div class="besper-message-bubble"> ${this.parseMarkdown(content)} </div> `; this.els.messages.appendChild(messageDiv); // Auto-scroll to bottom setTimeout(() => { this.els.messages.scrollTop = this.els.messages.scrollHeight; }, 50); } /** * Simple markdown parser */ parseMarkdown(text) { if (!text) return ''; // Basic markdown parsing return text .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>') .replace(/\*(.+?)\*/g, '<em>$1</em>') .replace(/`(.+?)`/g, '<code>$1</code>') .replace(/\n/g, '<br>'); } /** * Send a message */ async sendMessage() { const message = this.els.input.value.trim(); if (!message || this.state.isProcessing) return; this.addMessage(message, 'user'); this.els.input.value = ''; this.state.isProcessing = true; this.els.input.disabled = true; this.els.sendBtn.disabled = true; this.showTypingIndicator(); try { // Simple demo response - in real implementation this would call the API await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 2000) ); const responses = [ "I'm a demo AI assistant. I can help you with various questions about your website and business.", 'This is a demonstration of the B-esper AI interface. You can connect a website to enable knowledge extraction.', "I'm designed to provide intelligent responses based on your connected website content and general knowledge.", 'Feel free to ask me anything! In a real deployment, I would be connected to your specific knowledge base.', 'This demo showcases the chat interface. Connect a website above to see knowledge management in action.', ]; const response = responses[Math.floor(Math.random() * responses.length)]; this.removeTypingIndicator(); this.addMessage(response, 'ai'); } catch (error) { this.removeTypingIndicator(); console.error('Send message error:', error); this.addMessage(this.translations.issueError, 'ai'); } finally { this.state.isProcessing = false; this.els.input.disabled = false; this.els.sendBtn.disabled = false; this.els.input.focus(); } } /** * Show typing indicator */ showTypingIndicator() { const typingDiv = document.createElement('div'); typingDiv.className = 'besper-typing-indicator'; typingDiv.id = 'besperTypingIndicator'; typingDiv.innerHTML = ` <div class="besper-avatar ai">B</div> <div class="besper-typing-bubble"> <div class="besper-typing-dot"></div> <div class="besper-typing-dot"></div> <div class="besper-typing-dot"></div> </div> `; this.els.messages.appendChild(typingDiv); setTimeout(() => { this.els.messages.scrollTop = this.els.messages.scrollHeight; }, 50); } /** * Remove typing indicator */ removeTypingIndicator() { const indicator = document.getElementById('besperTypingIndicator'); if (indicator) indicator.remove(); } /** * Connect website (demo version) */ async connectWebsite() { const url = this.els.urlInput.value.trim(); if (!url) return; try { new URL(url); } catch (e) { alert(this.translations.enterValidUrl); return; } this.els.connectBtn.disabled = true; this.els.connectBtn.textContent = this.translations.connecting; // Demo connection setTimeout(() => { this.addMessage( `Connected to ${new URL(url).hostname}! This is a demo - in production, I would analyze the website content.`, 'ai' ); this.els.connectionArea.style.display = 'none'; this.els.knowledgeList.style.display = 'block'; this.els.knowledgeList.innerHTML = ` <div style="padding: 20px; text-align: center; color: #6b7684;"> <h3 style="margin-bottom: 10px; color: #1a1f2c;">Demo Mode</h3> <p>Website analysis would appear here in the full version.</p> </div> `; this.els.connectBtn.disabled = false; this.els.connectBtn.textContent = this.translations.connect; }, 2000); } /** * Download conversation */ downloadConversation() { const messages = Array.from(this.els.messages.children) .filter(el => el.classList.contains('besper-message')) .map(el => { const sender = el.classList.contains('user') ? 'User' : 'Assistant'; const content = el.querySelector('.besper-message-bubble').textContent; return `${sender}: ${content}`; }) .join('\n\n'); const blob = new Blob([messages], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `besper-demo-chat-${new Date().toISOString().split('T')[0]}.txt`; a.click(); URL.revokeObjectURL(url); } /** * Restart conversation */ restartConversation() { this.els.messages.innerHTML = ''; this.state.messages = []; this.addMessage( "Hello! I'm your B-esper AI assistant. How can I help you today?", 'ai' ); } /** * Delete conversation */ deleteConversation() { this.els.messages.innerHTML = ''; this.state.messages = []; } /** * Destroy the demo interface */ destroy() { if (this.container) { this.container.innerHTML = ''; } // Remove CSS const style = document.getElementById('besper-demo-styles'); if (style) { style.remove(); } console.log('[SUCCESS] Demo interface destroyed'); } }