Spaces:
Running
Running
| class AIAgent extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.attachShadow({ mode: 'open' }); | |
| this.isOpen = false; | |
| this.conversation = [ | |
| { | |
| role: 'assistant', | |
| content: 'π Hello! I\'m your AI coding assistant.\n\nI can help you with:\n\nπ» Writing and debugging code\nπ Explaining programming concepts\nπ Setting up new projects\nβ‘ Optimizing your existing code\n\nJust tell me what you need help with! For example:\n\n- "Write a Python function to calculate factorial"\n- "Explain how React hooks work"\n- "Help me debug this Node.js error"\n- "Create a React component for a login form"' | |
| } | |
| ]; | |
| this.projectContext = { | |
| currentFile: null, | |
| projectType: null, | |
| language: null | |
| }; | |
| } | |
| connectedCallback() { | |
| this.render(); | |
| this.setupEventListeners(); | |
| } | |
| render() { | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| position: fixed; | |
| bottom: 20px; | |
| right: 20px; | |
| z-index: 1000; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| .chat-container { | |
| position: absolute; | |
| bottom: 70px; | |
| right: 0; | |
| width: 400px; | |
| height: 500px; | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| transform: translateY(20px); | |
| opacity: 0; | |
| visibility: hidden; | |
| transition: all 0.3s ease; | |
| } | |
| .chat-container.open { | |
| transform: translateY(0); | |
| opacity: 1; | |
| visibility: visible; | |
| } | |
| .chat-header { | |
| background: #0088CC; | |
| color: white; | |
| padding: 16px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .chat-title { | |
| font-weight: 600; | |
| font-size: 16px; | |
| } | |
| .close-btn { | |
| background: none; | |
| border: none; | |
| color: white; | |
| cursor: pointer; | |
| font-size: 20px; | |
| } | |
| .chat-messages { | |
| flex: 1; | |
| padding: 16px; | |
| overflow-y: auto; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| .message { | |
| max-width: 80%; | |
| padding: 12px 16px; | |
| border-radius: 18px; | |
| font-size: 14px; | |
| line-height: 1.4; | |
| } | |
| .user-message { | |
| align-self: flex-end; | |
| background: #0088CC; | |
| color: white; | |
| border-bottom-right-radius: 4px; | |
| } | |
| .assistant-message { | |
| align-self: flex-start; | |
| background: #f1f5f9; | |
| color: #334155; | |
| border-bottom-left-radius: 4px; | |
| } | |
| .chat-input { | |
| display: flex; | |
| padding: 16px; | |
| border-top: 1px solid #e2e8f0; | |
| background: white; | |
| } | |
| .chat-input input { | |
| flex: 1; | |
| padding: 12px 16px; | |
| border: 1px solid #cbd5e1; | |
| border-radius: 24px; | |
| outline: none; | |
| font-size: 14px; | |
| } | |
| .chat-input input:focus { | |
| border-color: #0088CC; | |
| } | |
| .send-btn { | |
| background: #0088CC; | |
| color: white; | |
| border: none; | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| margin-left: 10px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .send-btn:hover { | |
| background: #0077b3; | |
| } | |
| .agent-button { | |
| width: 60px; | |
| height: 60px; | |
| border-radius: 50%; | |
| background: #0088CC; | |
| color: white; | |
| border: none; | |
| cursor: pointer; | |
| box-shadow: 0 4px 12px rgba(0, 136, 204, 0.3); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 24px; | |
| transition: all 0.2s ease; | |
| } | |
| .agent-button:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 6px 16px rgba(0, 136, 204, 0.4); | |
| } | |
| .typing-indicator { | |
| align-self: flex-start; | |
| background: #f1f5f9; | |
| padding: 12px 16px; | |
| border-radius: 18px; | |
| display: none; | |
| } | |
| .typing-indicator span { | |
| height: 8px; | |
| width: 8px; | |
| background: #94a3b8; | |
| border-radius: 50%; | |
| display: inline-block; | |
| margin: 0 2px; | |
| animation: bounce 1.3s linear infinite; | |
| } | |
| .typing-indicator span:nth-child(2) { | |
| animation-delay: 0.15s; | |
| } | |
| .typing-indicator span:nth-child(3) { | |
| animation-delay: 0.3s; | |
| } | |
| @keyframes bounce { | |
| 0%, 60%, 100% { | |
| transform: translateY(0); | |
| } | |
| 30% { | |
| transform: translateY(-5px); | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .chat-container { | |
| width: calc(100vw - 40px); | |
| height: 70vh; | |
| } | |
| } | |
| </style> | |
| <div class="chat-container" id="chatContainer"> | |
| <div class="chat-header"> | |
| <div class="chat-title">AI Coding Assistant</div> | |
| <button class="close-btn" id="closeBtn">Γ</button> | |
| </div> | |
| <div class="chat-messages" id="chatMessages"> | |
| ${this.renderMessages()} | |
| <div class="typing-indicator" id="typingIndicator"> | |
| <span></span> | |
| <span></span> | |
| <span></span> | |
| </div> | |
| </div> | |
| <div class="chat-input"> | |
| <input type="text" id="messageInput" placeholder="Ask me to create projects, write code, or explain concepts..." /> | |
| <button class="send-btn" id="sendBtn"> | |
| <i data-feather="send"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <button class="agent-button" id="agentButton"> | |
| <i data-feather="message-square"></i> | |
| </button> | |
| `; | |
| // Initialize feather icons | |
| import('https://unpkg.com/feather-icons').then(() => { | |
| feather.replace(); | |
| }); | |
| } | |
| renderMessages() { | |
| return this.conversation.map(msg => ` | |
| <div class="message ${msg.role}-message"> | |
| ${msg.content} | |
| </div> | |
| `).join(''); | |
| } | |
| setupEventListeners() { | |
| const agentButton = this.shadowRoot.getElementById('agentButton'); | |
| const closeBtn = this.shadowRoot.getElementById('closeBtn'); | |
| const chatContainer = this.shadowRoot.getElementById('chatContainer'); | |
| const messageInput = this.shadowRoot.getElementById('messageInput'); | |
| const sendBtn = this.shadowRoot.getElementById('sendBtn'); | |
| const chatMessages = this.shadowRoot.getElementById('chatMessages'); | |
| const typingIndicator = this.shadowRoot.getElementById('typingIndicator'); | |
| agentButton.addEventListener('click', () => { | |
| this.isOpen = !this.isOpen; | |
| chatContainer.classList.toggle('open', this.isOpen); | |
| }); | |
| closeBtn.addEventListener('click', () => { | |
| this.isOpen = false; | |
| chatContainer.classList.remove('open'); | |
| }); | |
| sendBtn.addEventListener('click', () => this.sendMessage()); | |
| messageInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') { | |
| this.sendMessage(); | |
| } | |
| }); | |
| // Auto-scroll to bottom of messages | |
| const observer = new MutationObserver(() => { | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| }); | |
| observer.observe(chatMessages, { childList: true, subtree: true }); | |
| } | |
| async sendMessage() { | |
| const messageInput = this.shadowRoot.getElementById('messageInput'); | |
| const chatMessages = this.shadowRoot.getElementById('chatMessages'); | |
| const typingIndicator = this.shadowRoot.getElementById('typingIndicator'); | |
| const message = messageInput.value.trim(); | |
| if (!message) return; | |
| // Add user message to conversation | |
| this.conversation.push({ | |
| role: 'user', | |
| content: message | |
| }); | |
| // Clear input | |
| messageInput.value = ''; | |
| // Re-render messages | |
| const messagesContainer = this.shadowRoot.getElementById('chatMessages'); | |
| messagesContainer.innerHTML = this.renderMessages() + ` | |
| <div class="typing-indicator" id="typingIndicator"> | |
| <span></span> | |
| <span></span> | |
| <span></span> | |
| </div> | |
| `; | |
| // Show typing indicator | |
| typingIndicator.style.display = 'block'; | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| try { | |
| // Call Grok API | |
| const response = await this.callGrokAPI(message); | |
| // Hide typing indicator | |
| typingIndicator.style.display = 'none'; | |
| // Add assistant response to conversation | |
| this.conversation.push({ | |
| role: 'assistant', | |
| content: response | |
| }); | |
| // Re-render messages | |
| messagesContainer.innerHTML = this.renderMessages() + ` | |
| <div class="typing-indicator" id="typingIndicator"> | |
| <span></span> | |
| <span></span> | |
| <span></span> | |
| </div> | |
| `; | |
| } catch (error) { | |
| // Hide typing indicator | |
| typingIndicator.style.display = 'none'; | |
| // Show error message | |
| this.conversation.push({ | |
| role: 'assistant', | |
| content: 'Sorry, I encountered an error. Please try again.' | |
| }); | |
| // Re-render messages | |
| messagesContainer.innerHTML = this.renderMessages() + ` | |
| <div class="typing-indicator" id="typingIndicator"> | |
| <span></span> | |
| <span></span> | |
| <span></span> | |
| </div> | |
| `; | |
| } | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| } | |
| async callGrokAPI(message) { | |
| // Simulated API call - replace with actual API in production | |
| try { | |
| // Check for special commands | |
| if (message.toLowerCase().includes('create project')) { | |
| return this.handleProjectCreation(message); | |
| } else if (message.toLowerCase().includes('write code')) { | |
| return this.handleCodeRequest(message); | |
| } else if (message.toLowerCase().includes('explain')) { | |
| return this.handleExplanationRequest(message); | |
| } else if (message.toLowerCase().includes('debug')) { | |
| return this.handleDebugRequest(message); | |
| } | |
| // Default response | |
| return `I can help you with coding tasks. Try asking me to: | |
| - "Create a new React project" | |
| - "Write a Python function to calculate factorial" | |
| - "Explain how promises work in JavaScript" | |
| - "Debug this code: [paste your code]"`; | |
| } catch (error) { | |
| console.error('Error:', error); | |
| return 'Sorry, I encountered an error. Please try again.'; | |
| } | |
| } | |
| handleProjectCreation(message) { | |
| // Extract project type from message | |
| const projectType = message.match(/create (.*?) project/i)?.[1] || 'web'; | |
| this.projectContext.projectType = projectType; | |
| let response = `I'll help you create a ${projectType} project. `; | |
| switch(projectType.toLowerCase()) { | |
| case 'react': | |
| response += `Here's how to set up a basic React project:\n\n1. Create these files:\n- index.html\n- app.js\n- styles.css\n\n2. Add this to index.html:\n\`\`\`html\n<!DOCTYPE html>\n<html>\n<head>\n <title>React App</title>\n <link rel="stylesheet" href="styles.css">\n</head>\n<body>\n <div id="root"></div>\n <script src="app.js"></script>\n</body>\n</html>\n\`\`\`\n\n3. Add this to app.js:\n\`\`\`javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nfunction App() {\n return (\n <div>\n <h1>Hello React!</h1>\n </div>\n );\n}\n\nReactDOM.render(<App />, document.getElementById('root'));\n\`\`\``; | |
| this.projectContext.language = 'javascript'; | |
| break; | |
| case 'python': | |
| response += `Here's a basic Python project structure:\n\n1. Create main.py:\n\`\`\`python\ndef main():\n print("Hello Python!")\n\nif __name__ == "__main__":\n main()\n\`\`\`\n\n2. Create requirements.txt for dependencies`; | |
| this.projectContext.language = 'python'; | |
| break; | |
| default: | |
| response += `Here's a basic web project structure:\n\n1. Create index.html:\n\`\`\`html\n<!DOCTYPE html>\n<html>\n<head>\n <title>My Project</title>\n <link rel="stylesheet" href="styles.css">\n</head>\n<body>\n <h1>Hello World!</h1>\n <script src="script.js"></script>\n</body>\n</html>\n\`\`\`\n\n2. Create styles.css for styling\n3. Create script.js for JavaScript`; | |
| this.projectContext.language = 'javascript'; | |
| } | |
| return response; | |
| } | |
| handleCodeRequest(message) { | |
| if (!this.projectContext.language) { | |
| return "First tell me what kind of project you're working on (e.g., 'Create a React project') so I can provide language-specific help."; | |
| } | |
| const codeType = message.match(/write (.*?) code/i)?.[1] || 'function'; | |
| let codeExample = ''; | |
| switch(this.projectContext.language) { | |
| case 'javascript': | |
| if (codeType.includes('function')) { | |
| codeExample = `\`\`\`javascript\n// Function example\nfunction ${this.getFunctionName(message)}(${this.getFunctionParams(message)}) {\n // ${this.getFunctionDescription(message)}\n return result;\n}\n\`\`\``; | |
| } else if (codeType.includes('component')) { | |
| codeExample = `\`\`\`javascript\n// React component example\nfunction ${this.getComponentName(message)}() {\n return (\n <div>\n <h1>${this.getComponentTitle(message)}</h1>\n </div>\n );\n}\n\`\`\``; | |
| } | |
| break; | |
| case 'python': | |
| codeExample = `\`\`\`python\ndef ${this.getFunctionName(message)}(${this.getFunctionParams(message)}):\n """\n ${this.getFunctionDescription(message)}\n """\n return result\n\`\`\``; | |
| break; | |
| default: | |
| codeExample = `Here's a generic code example for your request:\n\`\`\`\n// Implement your ${codeType} here\n\`\`\``; | |
| } | |
| return `Here's ${this.projectContext.language} code for ${codeType}:\n\n${codeExample}\n\nWould you like me to explain any part of this?`; | |
| } | |
| handleExplanationRequest(message) { | |
| const concept = message.match(/explain (.*)/i)?.[1] || 'this concept'; | |
| return `Here's an explanation of ${concept}:\n\n${this.getConceptExplanation(concept)}\n\nLet me know if you'd like more details or examples.`; | |
| } | |
| handleDebugRequest(message) { | |
| const codeBlock = message.match(/```[\s\S]*?```/)?.[0] || 'your code'; | |
| return `Looking at ${codeBlock.includes('```') ? 'the code' : 'your code'}, here are potential issues to check:\n\n1. Syntax errors\n2. Variable scope issues\n3. Type mismatches\n4. Async/await problems\n\nFor more specific help, please share the exact error message you're getting.`; | |
| } | |
| // Helper methods | |
| getFunctionName(message) { | |
| return message.match(/function named (.*?)[\s,.]/i)?.[1] || 'exampleFunction'; | |
| } | |
| getFunctionParams(message) { | |
| return message.match(/parameters? (.*?)[\s,.]/i)?.[1] || 'param1, param2'; | |
| } | |
| getFunctionDescription(message) { | |
| return message.match(/that (.*?)[\s,.]/i)?.[1] || 'Does something useful'; | |
| } | |
| getComponentName(message) { | |
| return message.match(/component named (.*?)[\s,.]/i)?.[1] || 'ExampleComponent'; | |
| } | |
| getComponentTitle(message) { | |
| return message.match(/title (.*?)[\s,.]/i)?.[1] || 'My Component'; | |
| } | |
| getConceptExplanation(concept) { | |
| const explanations = { | |
| 'react': 'React is a JavaScript library for building user interfaces. It lets you create reusable UI components and efficiently update the DOM when data changes.', | |
| 'python': 'Python is a high-level, interpreted programming language known for its readability and versatility. It\'s great for web development, data analysis, AI, and more.', | |
| 'javascript': 'JavaScript is the programming language of the web. It runs in browsers and can manipulate HTML/CSS, handle user interactions, and make network requests.', | |
| 'promises': 'Promises in JavaScript represent the eventual completion (or failure) of an asynchronous operation and its resulting value. They help avoid callback hell.' | |
| }; | |
| return explanations[concept.toLowerCase()] || | |
| `I'll explain ${concept} in simple terms...`; | |
| } | |
| } | |
| customElements.define('ai-agent', AIAgent); | |