Spaces:
Running
Running
| <html lang="ar" dir="rtl"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>GitHub AI Assistant</title> | |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700&display=swap'); | |
| body { font-family: 'Tajawal', sans-serif; } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <!-- Header --> | |
| <header class="bg-white shadow-sm border-b"> | |
| <div class="max-w-7xl mx-auto px-4 py-4"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center space-x-2 space-x-reverse"> | |
| <i data-feather="git-branch" class="w-8 h-8 text-blue-600"></i> | |
| <h1 class="text-xl font-bold text-gray-900">GitHub AI Assistant</h1> | |
| </div> | |
| <div id="user-section" class="hidden"> | |
| <button id="logout-btn" class="text-gray-600 hover:text-gray-900"> | |
| <i data-feather="log-out" class="w-5 h-5"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="max-w-4xl mx-auto px-4 py-8"> | |
| <!-- Setup Section --> | |
| <div id="setup-section" class="bg-white rounded-lg shadow-md p-6 mb-6"> | |
| <h2 class="text-2xl font-bold text-gray-900 mb-4">إعدادات الربط</h2> | |
| <!-- OpenAI API Key --> | |
| <div class="mb-6"> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">OpenAI API Key</label> | |
| <input type="password" id="openai-key" placeholder="أدخل مفتاح OpenAI API الخاص بك" | |
| class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| </div> | |
| <!-- GitHub Token --> | |
| <div class="mb-6"> | |
| <label class="block text-sm font-medium text-gray-700 mb-2">GitHub Token</label> | |
| <input type="password" id="github-token" placeholder="أدخل رمز GitHub الشخصي الخاص بك" | |
| class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <p class="text-xs text-gray-500 mt-1">يجب أن يحتوي الرمز على أذونات repo</p> | |
| </div> | |
| <button id="connect-btn" class="w-full bg-blue-600 text-white py-3 px-4 rounded-md hover:bg-blue-700 transition duration-200"> | |
| ربط الحسابات | |
| </button> | |
| </div> | |
| <!-- Repositories Section --> | |
| <div id="repos-section" class="bg-white rounded-lg shadow-md p-6 mb-6 hidden"> | |
| <h2 class="text-2xl font-bold text-gray-900 mb-4">اختر المستودع</h2> | |
| <div id="repos-list" class="space-y-3"> | |
| <!-- Repositories will be loaded here --> | |
| </div> | |
| </div> | |
| <!-- Chat Section --> | |
| <div id="chat-section" class="bg-white rounded-lg shadow-md p-6 hidden"> | |
| <div class="flex items-center justify-between mb-6"> | |
| <h2 class="text-2xl font-bold text-gray-900" id="current-repo">المحادثة</h2> | |
| <div class="flex space-x-2 space-x-reverse"> | |
| <button id="merge-btn" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition duration-200 hidden"> | |
| دمج التغييرات | |
| </button> | |
| <button id="new-chat-btn" class="bg-gray-600 text-white px-4 py-2 rounded-md hover:bg-gray-700 transition duration-200"> | |
| محادثة جديدة | |
| </button> | |
| </div> | |
| </div> | |
| <div id="chat-messages" class="h-96 overflow-y-auto border border-gray-200 rounded-lg p-4 mb-4 bg-gray-50"> | |
| <!-- Chat messages will appear here --> | |
| </div> | |
| <div class="flex space-x-2 space-x-reverse"> | |
| <input type="text" id="message-input" placeholder="اكتب أمرك هنا..." | |
| class="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <button id="send-btn" class="bg-blue-600 text-white px-6 py-2 rounded-md hover:bg-blue-700 transition duration-200"> | |
| إرسال | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Status Messages --> | |
| <div id="status-message" class="fixed bottom-4 left-4 right-4 bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 rounded hidden"></div> | |
| </main> | |
| <script> | |
| // State management | |
| let currentState = { | |
| openaiKey: '', | |
| githubToken: '', | |
| selectedRepo: '', | |
| currentBranch: '', | |
| chatHistory: [] | |
| }; | |
| // DOM Elements | |
| const setupSection = document.getElementById('setup-section'); | |
| const reposSection = document.getElementById('repos-section'); | |
| const chatSection = document.getElementById('chat-section'); | |
| const reposList = document.getElementById('repos-list'); | |
| const chatMessages = document.getElementById('chat-messages'); | |
| const messageInput = document.getElementById('message-input'); | |
| const sendBtn = document.getElementById('send-btn'); | |
| const connectBtn = document.getElementById('connect-btn'); | |
| const mergeBtn = document.getElementById('merge-btn'); | |
| const newChatBtn = document.getElementById('new-chat-btn'); | |
| const userSection = document.getElementById('user-section'); | |
| const statusMessage = document.getElementById('status-message'); | |
| const currentRepo = document.getElementById('current-repo'); | |
| // Event Listeners | |
| connectBtn.addEventListener('click', connectAccounts); | |
| sendBtn.addEventListener('click', sendMessage); | |
| mergeBtn.addEventListener('click', mergeChanges); | |
| newChatBtn.addEventListener('click', startNewChat); | |
| messageInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') sendMessage(); | |
| }); | |
| // Functions | |
| function showStatus(message, type = 'info') { | |
| statusMessage.textContent = message; | |
| statusMessage.className = `fixed bottom-4 left-4 right-4 px-4 py-3 rounded ${type === 'error' ? 'bg-red-100 border border-red-400 text-red-700' : 'bg-blue-100 border border-blue-400 text-blue-700'}`; | |
| statusMessage.classList.remove('hidden'); | |
| setTimeout(() => statusMessage.classList.add('hidden'), 5000); | |
| } | |
| async function connectAccounts() { | |
| const openaiKey = document.getElementById('openai-key').value; | |
| const githubToken = document.getElementById('github-token').value; | |
| if (!openaiKey || !githubToken) { | |
| showStatus('يرجى ملء جميع الحقول', 'error'); | |
| return; | |
| } | |
| currentState.openaiKey = openaiKey; | |
| currentState.githubToken = githubToken; | |
| showStatus('جاري ربط الحسابات...'); | |
| try { | |
| // Test GitHub connection | |
| const repos = await fetchGitHubRepos(githubToken); | |
| if (repos) { | |
| setupSection.classList.add('hidden'); | |
| reposSection.classList.remove('hidden'); | |
| userSection.classList.remove('hidden'); | |
| loadRepositories(repos); | |
| showStatus('تم الربط بنجاح'); | |
| } | |
| } catch (error) { | |
| showStatus('فشل في الربط: ' + error.message, 'error'); | |
| } | |
| } | |
| async function fetchGitHubRepos(token) { | |
| const response = await fetch('https://api.github.com/user/repos', { | |
| headers: { | |
| 'Authorization': `token ${token}`, | |
| 'Accept': 'application/vnd.github.v3+json' | |
| } | |
| }); | |
| if (!response.ok) { | |
| throw new Error('فشل في جلب المستودعات'); | |
| } | |
| return await response.json(); | |
| } | |
| function loadRepositories(repos) { | |
| reposList.innerHTML = ''; | |
| repos.forEach(repo => { | |
| const repoElement = document.createElement('div'); | |
| repoElement.className = 'flex items-center justify-between p-3 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer'; | |
| repoElement.innerHTML = ` | |
| <div class="flex items-center space-x-3 space-x-reverse"> | |
| <i data-feather="folder" class="w-5 h-5 text-gray-500"></i> | |
| <span class="font-medium">${repo.full_name}</span> | |
| </div> | |
| <i data-feather="chevron-left" class="w-5 h-5 text-gray-400"></i> | |
| `; | |
| repoElement.addEventListener('click', () => selectRepository(repo)); | |
| reposList.appendChild(repoElement); | |
| }); | |
| feather.replace(); | |
| } | |
| async function selectRepository(repo) { | |
| currentState.selectedRepo = repo.full_name; | |
| reposSection.classList.add('hidden'); | |
| chatSection.classList.remove('hidden'); | |
| currentRepo.textContent = `المحادثة - ${repo.full_name}`; | |
| showStatus(`تم اختيار المستودع: ${repo.full_name}`); | |
| // Create a new branch for this session | |
| const branchName = `ai-assistant-${Date.now()}`; | |
| await createNewBranch(repo.full_name, branchName); | |
| currentState.currentBranch = branchName; | |
| mergeBtn.classList.remove('hidden'); | |
| addMessage('system', 'مرحباً! أنا مساعد الذكاء الاصطناعي. كيف يمكنني مساعدتك في هذا المستودع؟'); | |
| } | |
| async function createNewBranch(repo, branchName) { | |
| // This would need to be implemented with GitHub API | |
| // For now, we'll just set the branch name | |
| showStatus(`تم إنشاء فرع جديد: ${branchName}`); | |
| } | |
| function addMessage(role, content) { | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `mb-4 p-3 rounded-lg ${role === 'user' ? 'bg-blue-100 ml-12' : 'bg-gray-100 mr-12'}`; | |
| messageDiv.innerHTML = ` | |
| <div class="font-medium mb-1">${role === 'user' ? 'أنت' : 'المساعد'}</div> | |
| <div class="text-gray-700">${content}</div> | |
| `; | |
| chatMessages.appendChild(messageDiv); | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| currentState.chatHistory.push({ role, content }); | |
| } | |
| async function sendMessage() { | |
| const message = messageInput.value.trim(); | |
| if (!message) return; | |
| messageInput.value = ''; | |
| addMessage('user', message); | |
| showStatus('جاري معالجة الطلب...'); | |
| try { | |
| // Simulate AI response - in real implementation, this would call OpenAI API | |
| // and execute commands on the repository | |
| const response = await simulateAIResponse(message); | |
| addMessage('assistant', response); | |
| showStatus('تم تنفيذ الأمر بنجاح'); | |
| } catch (error) { | |
| showStatus('فشل في تنفيذ الأمر: ' + error.message, 'error'); | |
| addMessage('assistant', 'عذراً، حدث خطأ أثناء معالجة طلبك.'); | |
| } | |
| } | |
| async function simulateAIResponse(message) { | |
| // Simulate API call delay | |
| await new Promise(resolve => setTimeout(resolve, 2000)); | |
| // Simple response simulation - in real implementation, this would: | |
| // 1. Call OpenAI API with the message and repository context | |
| // 2. Parse the AI response to extract git commands | |
| // 3. Execute those commands on the repository via GitHub API | |
| // 4. Return the results | |
| const responses = [ | |
| 'تم تنفيذ التغييرات المطلوبة على الملفات بنجاح.', | |
| 'لقد قمت بتعديل الكود حسب طلبك. هل تريد أي تغييرات إضافية؟', | |
| 'تم إضافة الوظيفة الجديدة إلى المشروع.', | |
| 'تم إصلاح الأخطاء التي ذكرتها في الكود.', | |
| 'لقد قمت بتحسين أداء الوظيفة الحالية.' | |
| ]; | |
| return responses[Math.floor(Math.random() * responses.length)]; | |
| } | |
| async function mergeChanges() { | |
| if (!currentState.selectedRepo || !currentState.currentBranch) { | |
| showStatus('لا يوجد مستودع محدد', 'error'); | |
| return; | |
| } | |
| showStatus('جاري دمج التغييرات...'); | |
| try { | |
| // Simulate merge process | |
| await new Promise(resolve => setTimeout(resolve, 3000)); | |
| showStatus('تم دمج التغييرات بنجاح في الفرع الرئيسي'); | |
| mergeBtn.classList.add('hidden'); | |
| } catch (error) { | |
| showStatus('فشل في دمج التغييرات', 'error'); | |
| } | |
| } | |
| function startNewChat() { | |
| chatMessages.innerHTML = ''; | |
| currentState.chatHistory = []; | |
| addMessage('system', 'مرحباً! أنا مساعد الذكاء الاصطناعي. كيف يمكنني مساعدتك في هذا المستودع؟'); | |
| } | |
| // Initialize feather icons | |
| feather.replace(); | |
| </script> | |
| </body> | |
| </html> |