/** * ds-chat-panel.js * AI chatbot panel with team+project context * MVP1: Integrates claude-service with ContextStore for team-aware assistance */ import claudeService from '../../services/claude-service.js'; import contextStore from '../../stores/context-store.js'; import { ComponentHelpers } from '../../utils/component-helpers.js'; class DSChatPanel extends HTMLElement { constructor() { super(); this.messages = []; this.isLoading = false; } async connectedCallback() { // Sync claude-service with ContextStore const context = contextStore.getMCPContext(); if (context.project_id) { claudeService.setProject(context.project_id); } // Subscribe to project changes this.unsubscribe = contextStore.subscribeToKey('projectId', (newProjectId) => { if (newProjectId) { claudeService.setProject(newProjectId); this.showSystemMessage(`Switched to project: ${newProjectId}`); } }); // Initialize MCP tools in background this.initializeMcpTools(); this.render(); this.setupEventListeners(); this.loadHistory(); this.showWelcomeMessage(); } /** * Initialize MCP tools to enable AI tool awareness */ async initializeMcpTools() { try { console.log('[DSChatPanel] Initializing MCP tools...'); await claudeService.getMcpTools(); console.log('[DSChatPanel] MCP tools initialized successfully'); } catch (error) { console.warn('[DSChatPanel] Failed to load MCP tools (non-blocking):', error.message); } } /** * Set context from parent component (ds-ai-chat-sidebar) * @param {Object} context - Context object with project, team, page */ setContext(context) { if (!context) return; // Handle project context (could be object with id or string id) if (context.project) { const projectId = typeof context.project === 'object' ? context.project.id : context.project; if (projectId && projectId !== claudeService.currentProjectId) { claudeService.setProject(projectId); console.log('[DSChatPanel] Context updated via setContext:', { projectId }); } } // Store team and page context for reference if (context.team) { this.currentTeam = context.team; } if (context.page) { this.currentPage = context.page; } } disconnectedCallback() { if (this.unsubscribe) { this.unsubscribe(); } } setupEventListeners() { const input = this.querySelector('#chat-input'); const sendBtn = this.querySelector('#chat-send-btn'); const clearBtn = this.querySelector('#chat-clear-btn'); const exportBtn = this.querySelector('#chat-export-btn'); if (sendBtn && input) { sendBtn.addEventListener('click', () => this.sendMessage()); input.addEventListener('keypress', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.sendMessage(); } }); } if (clearBtn) { clearBtn.addEventListener('click', () => this.clearChat()); } if (exportBtn) { exportBtn.addEventListener('click', () => this.exportChat()); } } loadHistory() { const history = claudeService.getHistory(); if (history && history.length > 0) { this.messages = history; this.renderMessages(); } } showWelcomeMessage() { if (this.messages.length === 0) { const context = contextStore.getMCPContext(); const teamId = context.team_id || 'ui'; const teamGreetings = { ui: 'I can help with token extraction, component audits, Storybook comparisons, and quick wins analysis.', ux: 'I can assist with Figma syncing, design tokens, asset management, and navigation flows.', qa: 'I can help with visual regression testing, accessibility audits, and ESRE validation.', admin: 'I can help manage projects, configure integrations, and oversee the design system.' }; const greeting = teamGreetings[teamId] || teamGreetings.admin; this.showSystemMessage( `đ Welcome to the ${teamId.toUpperCase()} team workspace!\n\n${greeting}\n\nI have access to all MCP tools for the active project.` ); } } showSystemMessage(text) { this.messages.push({ role: 'system', content: text, timestamp: new Date().toISOString() }); this.renderMessages(); } async sendMessage() { const input = this.querySelector('#chat-input'); const message = input?.value.trim(); if (!message || this.isLoading) return; // Check project context const context = contextStore.getMCPContext(); if (!context.project_id) { ComponentHelpers.showToast?.('Please select a project before chatting', 'error'); return; } // Add user message this.messages.push({ role: 'user', content: message, timestamp: new Date().toISOString() }); // Clear input input.value = ''; // Render and scroll this.renderMessages(); this.scrollToBottom(); // Show loading this.isLoading = true; this.updateLoadingState(); try { // Add team context to the request const teamContext = { projectId: context.project_id, teamId: context.team_id, userId: context.user_id, page: 'workdesk', capabilities: context.capabilities }; // Send to Claude with team+project context const response = await claudeService.chat(message, teamContext); // Add assistant response this.messages.push({ role: 'assistant', content: response, timestamp: new Date().toISOString() }); this.renderMessages(); this.scrollToBottom(); } catch (error) { console.error('[DSChatPanel] Failed to send message:', error); ComponentHelpers.showToast?.(`Chat error: ${error.message}`, 'error'); this.messages.push({ role: 'system', content: `â Error: ${error.message}\n\nPlease try again or check your connection.`, timestamp: new Date().toISOString() }); this.renderMessages(); } finally { this.isLoading = false; this.updateLoadingState(); } } clearChat() { if (!confirm('Clear all chat history?')) return; claudeService.clearHistory(); this.messages = []; this.renderMessages(); this.showWelcomeMessage(); ComponentHelpers.showToast?.('Chat history cleared', 'success'); } exportChat() { claudeService.exportConversation(); ComponentHelpers.showToast?.('Chat exported successfully', 'success'); } updateLoadingState() { const sendBtn = this.querySelector('#chat-send-btn'); const input = this.querySelector('#chat-input'); if (sendBtn) { sendBtn.disabled = this.isLoading; sendBtn.textContent = this.isLoading ? 'âŗ Sending...' : 'đ¤ Send'; } if (input) { input.disabled = this.isLoading; } } scrollToBottom() { const messagesContainer = this.querySelector('#chat-messages'); if (messagesContainer) { setTimeout(() => { messagesContainer.scrollTop = messagesContainer.scrollHeight; }, 100); } } renderMessages() { const messagesContainer = this.querySelector('#chat-messages'); if (!messagesContainer) return; if (this.messages.length === 0) { messagesContainer.innerHTML = `
Start a conversation to get help with your design system.