Commit History

Role: You are a specialized AI assistant for web automation and user interface enhancement on the Hugging Face platform. Your primary function is to inject, manage, and optimize interactive tools that streamline AI-assisted coding workflows.
6f56d5c
verified

eubottura commited on

Role: AI AssistantGoal: Execute the specified task with absolute precision, robustness, and full adherence to the intended functionality.Constraints:- Operate without errors, omissions, or deviations from the defined purpose.- Ensure maximum reliability and performance under all expected conditions.- Do not introduce assumptions, interpretations, or modifications beyond the defined scope.Steps:1. Analyze the core objective of the task as explicitly and implicitly defined.2. Implement the solution with complete fidelity to the intended behavior.3. Reinforce all components to ensure flawless, brute-force reliability.4. Validate output against the highest standard of functional correctness.5. Deliver the final result in a fully optimized, error-free state.Success Criteria: The output must perform perfectly, consistently, and robustly according to the original intent, with no failure under expected use. Ainda nao tá legal cara, ajeita isso
30e5655
verified

eubottura commited on

Role: AI AssistantGoal: Execute the specified task with absolute precision, robustness, and full adherence to the intended functionality.Constraints:- Operate without errors, omissions, or deviations from the defined purpose.- Ensure maximum reliability and performance under all expected conditions.- Do not introduce assumptions, interpretations, or modifications beyond the defined scope.Steps:1. Analyze the core objective of the task as explicitly and implicitly defined.2. Implement the solution with complete fidelity to the intended behavior.3. Reinforce all components to ensure flawless, brute-force reliability.4. Validate output against the highest standard of functional correctness.5. Deliver the final result in a fully optimized, error-free state.Success Criteria: The output must perform perfectly, consistently, and robustly according to the original intent, with no failure under expected use.
c6c10b8
verified

eubottura commited on

Core Task: Your primary function is to analyze and deconstruct a given text—a directive intended for an AI. Your objective is to fundamentally re-engineer and elevate this text, transforming it into a superior, highly effective prompt.Optimization Directives: The enhanced prompt must adhere to the following principles, meticulously designed for AI-to-AI communication:Clarity & Precision: Eliminate all vagueness, colloquialisms, and ambiguity. Employ precise, technical terminology where it enhances clarity.Structural Integrity: Re-organize the content into a logical, machine-readable format. Use clear sections (e.g., Role, Goal, Constraints, Steps) to define the AI's persona, objective, and operational parameters.Action-Oriented Commands: Frame instructions as direct, executable commands. Replace declarative statements about capabilities or states with imperative verbs that drive specific actions.Deconstruction of Intent: Infer and translate the user's ultimate goal from the original text, even if implicitly stated. The final prompt should achieve the user's true objective with maximum efficiency, not just the literal words they used.Constraint & Scope Definition: Explicitly define all constraints, success criteria, and the scope of the task to prevent misinterpretation and ensure focused output.Final Output Mandate: You are to receive the user's initial text and, based on the directives above, produce only the final, perfected English version of that prompt. Do not include any preamble, explanation, or metadata in your final response. The output must be the optimized prompt text itself, ready for direct use. TEXT: "eubottura-codefix-ai/bookmarklet-manager.js "// Gerenciador de Bookmarklets para Hugging Face // Substitui os bookmarklets problemáticos por uma solução mais robusta class BookmarkletManager { constructor() { this.isInjected = false; this.currentPanel = null; this.aiType = null; } // Detectar qual página do Hugging Face estamos detectAIType() { const url = window.location.href; if (url.includes('Qwen') || url.includes('qwen')) { return 'Qwen'; } else if (url.includes('zai-org') || url.includes('zai')) { return 'Zai'; } return 'HF'; } // Injetar o painel principal injectMainPanel() { if (this.isInjected) return; this.isInjected = true; this.aiType = this.detectAIType(); // Remover painel existente const existing = document.getElementById('hf-bookmarklet-panel'); if (existing) existing.remove(); // Criar painel flutuante this.currentPanel = document.createElement('div'); this.currentPanel.id = 'hf-bookmarklet-panel'; this.applyPanelStyles(); this.currentPanel.innerHTML = this.getPanelHTML(); document.body.appendChild(this.currentPanel); this.setupEventListeners(); this.checkForIncomingData(); // Auto-detectar e colar dados recebidos setTimeout(() => this.autoPasteFromClipboard(), 1000); } applyPanelStyles() { const style = document.createElement('style'); style.textContent = #hf-bookmarklet-panel { position: fixed !important; top: 20px !important; right: 20px !important; width: 350px !important; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; border-radius: 16px !important; padding: 0 !important; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) !important; z-index: 2147483647 !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; font-size: 14px !important; color: white !important; backdrop-filter: blur(10px) !important; animation: slideInRight 0.3s ease-out !important; } @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } #hf-bookmarklet-panel .panel-header { background: rgba(255, 255, 255, 0.1) !important; padding: 20px !important; border-radius: 16px 16px 0 0 !important; backdrop-filter: blur(10px) !important; } #hf-bookmarklet-panel .panel-title { margin: 0 0 8px 0 !important; font-size: 18px !important; font-weight: 700 !important; text-shadow: 0 2px 4px rgba(0,0,0,0.1) !important; } #hf-bookmarklet-panel .panel-subtitle { margin: 0 !important; font-size: 12px !important; opacity: 0.9 !important; } #hf-bookmarklet-panel .panel-body { padding: 20px !important; } #hf-bookmarklet-panel .hf-button { width: 100% !important; padding: 12px 16px !important; margin: 8px 0 !important; border: none !important; border-radius: 10px !important; font-weight: 600 !important; font-size: 13px !important; cursor: pointer !important; transition: all 0.3s ease !important; display: flex !important; align-items: center !important; justify-content: center !important; gap: 8px !important; position: relative !important; overflow: hidden !important; } #hf-bookmarklet-panel .hf-button:hover { transform: translateY(-2px) !important; box-shadow: 0 10px 20px rgba(0,0,0,0.2) !important; } #hf-bookmarklet-panel .hf-button:active { transform: translateY(0) !important; } #hf-bookmarklet-panel .btn-primary { background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important; color: white !important; } #hf-bookmarklet-panel .btn-secondary { background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%) !important; color: white !important; } #hf-bookmarklet-panel .btn-warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%) !important; color: white !important; } #hf-bookmarklet-panel .btn-danger { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%) !important; color: white !important; } #hf-bookmarklet-panel .status-message { margin-top: 16px !important; padding: 12px !important; border-radius: 8px !important; font-size: 12px !important; display: none !important; animation: fadeIn 0.3s ease !important; } @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } #hf-bookmarklet-panel .status-success { background: rgba(16, 185, 129, 0.2) !important; border: 1px solid rgba(16, 185, 129, 0.5) !important; } #hf-bookmarklet-panel .status-error { background: rgba(239, 68, 68, 0.2) !important; border: 1px solid rgba(239, 68, 68, 0.5) !important; } #hf-bookmarklet-panel .close-btn { position: absolute !important; top: 15px !important; right: 15px !important; background: rgba(255, 255, 255, 0.2) !important; border: none !important; border-radius: 50% !important; width: 30px !important; height: 30px !important; display: flex !important; align-items: center !important; justify-content: center !important; cursor: pointer !important; transition: all 0.3s ease !important; } #hf-bookmarklet-panel .close-btn:hover { background: rgba(255, 255, 255, 0.3) !important; transform: rotate(90deg) !important; } #hf-bookmarklet-panel .minimize-btn { position: absolute !important; top: 15px !important; right: 55px !important; background: rgba(255, 255, 255, 0.2) !important; border: none !important; border-radius: 50% !important; width: 30px !important; height: 30px !important; display: flex !important; align-items: center !important; justify-content: center !important; cursor: pointer !important; transition: all 0.3s ease !important; } #hf-bookmarklet-panel.minimized { height: 60px !important; overflow: hidden !important; } #hf-bookmarklet-panel.minimized .panel-body { display: none !important; } ; document.head.appendChild(style); } getPanelHTML() { const aiIcon = this.aiType === 'Qwen' ? '🤖' : (this.aiType === 'Zai' ? '🔧' : '💬'); return <div class="panel-header"> <h3 class="panel-title">${aiIcon} ${this.aiType} Assistant</h3> <p class="panel-subtitle">Ferramenta de automação CodeFlow AI</p> <button class="minimize-btn" onclick="bookmarkletManager.toggleMinimize()">−</button> <button class="close-btn" onclick="bookmarkletManager.removePanel()">×</button> </div> <div class="panel-body"> <button class="hf-button btn-primary" onclick="bookmarkletManager.enhanceInterface()"> ✨ Melhorar Interface </button> <button class="hf-button btn-secondary" onclick="bookmarkletManager.smartPaste()"> 📋 Colar Inteligente </button> <button class="hf-button btn-warning" onclick="bookmarkletManager.extractResponse()"> 📥 Extrair Resposta </button> ${this.aiType === 'Qwen' ? <button class="hf-button btn-primary" onclick="bookmarkletManager.sendToZai()"> ➡️ Enviar para ZAI </button> : ''} <button class="hf-button btn-secondary" onclick="bookmarkletManager.fullScreenMode()"> 🖥️ Tela Cheia </button> <button class="hf-button btn-danger" onclick="bookmarkletManager.resetPage()"> 🔄 Resetar Página </button> <div id="hf-status" class="status-message"></div> </div> ; } setupEventListeners() { // Adicionar atalhos de teclado document.addEventListener('keydown', (e) => { if (e.ctrlKey || e.metaKey) { switch(e.key) { case 'v': if (e.shiftKey) { e.preventDefault(); this.smartPaste(); } break; case 'c': if (e.shiftKey) { e.preventDefault(); this.extractResponse(); } break; } } }); // Detectar mudanças no DOM const observer = new MutationObserver(() => { this.checkForIncomingData(); }); observer.observe(document.body, { childList: true, subtree: true }); } enhanceInterface() { // Melhorar a interface do chat const chatContainer = document.querySelector('div.h-full.overflow-y-auto'); if (chatContainer) { chatContainer.style.scrollBehavior = 'smooth'; chatContainer.style.padding = '20px'; // Adicionar botões flutuantes this.addFloatingButtons(); this.showStatus('Interface melhorada com sucesso! 🎉', 'success'); } else { this.showStatus('Container do chat não encontrado', 'error'); } } addFloatingButtons() { // Remover botões existentes const existing = document.querySelectorAll('.hf-floating-btn'); existing.forEach(btn => btn.remove()); // Botão de rolagem para baixo const scrollBtn = document.createElement('button'); scrollBtn.innerHTML = '↓'; scrollBtn.className = 'hf-floating-btn'; scrollBtn.style.cssText = position: fixed !important; bottom: 20px !important; right: 20px !important; width: 50px !important; height: 50px !important; border-radius: 50% !important; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; border: none !important; cursor: pointer !important; font-size: 20px !important; z-index: 2147483646 !important; box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; transition: all 0.3s ease !important; ; scrollBtn.onclick = () => { const chat = document.querySelector('div.h-full.overflow-y-auto'); if (chat) chat.scrollTop = chat.scrollHeight; }; document.body.appendChild(scrollBtn); // Botão de foco no input const focusBtn = document.createElement('button'); focusBtn.innerHTML = '✏️'; focusBtn.className = 'hf-floating-btn'; focusBtn.style.cssText = scrollBtn.style.cssText.replace('bottom: 20px', 'bottom: 80px'); focusBtn.onclick = () => { const input = document.querySelector('textarea, input[type="text"]'); if (input) { input.focus(); input.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }; document.body.appendChild(focusBtn); // Efeito hover [scrollBtn, focusBtn].forEach(btn => { btn.addEventListener('mouseenter', () => { btn.style.transform = 'scale(1.1) rotate(5deg)'; }); btn.addEventListener('mouseleave', () => { btn.style.transform = 'scale(1) rotate(0deg)'; }); }); } async smartPaste() { try { const text = await navigator.clipboard.readText(); if (!text) { this.showStatus('Área de transferência vazia', 'error'); return; } const textarea = await this.findInputField(); if (textarea) { this.fillInputField(textarea, text); this.showStatus('Texto colado com sucesso! 📋', 'success'); } else { this.showStatus('Campo de entrada não encontrado', 'error'); } } catch (error) { console.error('Erro ao colar:', error); this.showStatus('Erro ao acessar área de transferência', 'error'); } } async findInputField() { const selectors = [ 'textarea[data-testid="chat-input"]', 'textarea[placeholder*="Message"]', 'textarea[placeholder*="message"]', 'textarea[aria-label*="Message"]', 'textarea.w-full', 'textarea', 'input[type="text"]', '[contenteditable="true"]' ]; for (const selector of selectors) { const element = document.querySelector(selector); if (element) return element; } return null; } fillInputField(input, text) { if (input.tagName.toLowerCase() === 'textarea' || input.tagName.toLowerCase() === 'input') { input.value = text; input.focus(); // Disparar eventos input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); // Auto-ajustar altura se for textarea if (input.tagName.toLowerCase() === 'textarea') { input.style.height = 'auto'; input.style.height = Math.min(input.scrollHeight, 300) + 'px'; } } else { // Content editable input.textContent = text; input.focus(); } } extractResponse() { const messages = this.findMessages(); if (messages.length > 0) { const lastMessage = messages[messages.length - 1]; const text = this.extractTextFromElement(lastMessage); if (text && text.length > 10) { navigator.clipboard.writeText(text).then(() => { this.showStatus('Resposta copiada! 📋', 'success'); }).catch(() => { // Fallback this.fallbackCopy(text); this.showStatus('Resposta copiada (fallback)!', 'success'); }); } else { this.showStatus('Nenhum texto válido encontrado', 'error'); } } else { this.showStatus('Nenhuma mensagem encontrada', 'error'); } } findMessages() { const selectors = [ '[data-testid="conversation-turn"]', '.prose', '.message-content', '.markdown-body', '[class*="message"]', '[class*="response"]', '[class*="turn"]' ]; for (const selector of selectors) { const elements = document.querySelectorAll(selector); if (elements.length > 0) return elements; } // Busca genérica const allElements = document.querySelectorAll(''); const messages = []; for (const element of allElements) { const text = this.extractTextFromElement(element); if (text && text.length > 200) { const rect = element.getBoundingClientRect(); if (rect.width > 300 && rect.height > 100) { messages.push(element); } } } return messages.slice(-3); // Últimas 3 mensagens } extractTextFromElement(element) { if (element.textContent) { return element.textContent.trim(); } else if (element.innerText) { return element.innerText.trim(); } else if (element.innerHTML) { return element.innerHTML.replace(/<[^>]>/g, '').trim(); } return ''; } fallbackCopy(text) { const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); } sendToZai() { const messages = this.findMessages(); if (messages.length > 0) { const lastMessage = messages[messages.length - 1]; const text = this.extractTextFromElement(lastMessage); if (text && text.length > 10) { localStorage.setItem('hf_qwen_to_zai', text); window.open('https://huggingface.co/zai-org/GLM-4.6', '_blank'); this.showStatus('Enviado para ZAI! Nova aba aberta ➡️', 'success'); } else { this.showStatus('Não há conteúdo para enviar', 'error'); } } else { this.showStatus('Nenhuma mensagem encontrada', 'error'); } } checkForIncomingData() { if (this.aiType !== 'Zai') return; const incomingData = localStorage.getItem('hf_qwen_to_zai'); if (incomingData) { setTimeout(async () => { const input = await this.findInputField(); if (input) { this.fillInputField(input, incomingData); this.showStatus('Dados do Qwen recebidos e injetados! 📥', 'success'); localStorage.removeItem('hf_qwen_to_zai'); } }, 2000); } } fullScreenMode() { const chatContainer = document.querySelector('div.h-full.overflow-y-auto'); const messageBox = chatContainer?.parentElement; const mainWrapper = messageBox?.parentElement; if (!chatContainer || !messageBox || !mainWrapper) { this.showStatus('Estrutura do chat não encontrada', 'error'); return; } // Salvar estilos originais const originalStyles = { main: mainWrapper.style.cssText, message: messageBox.style.cssText, chat: chatContainer.style.cssText }; // Aplicar tela cheia mainWrapper.style.cssText = position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; display: flex !important; flex-direction: column !important; z-index: 2147483645 !important; background: #f9fafb !important; padding: 20px !important; box-sizing: border-box !important; ; messageBox.style.cssText = flex-grow: 1 !important; overflow: hidden !important; display: flex !important; flex-direction: column !important; ; chatContainer.style.cssText = flex-grow: 1 !important; overflow-y: auto !important; position: relative !important; ; // Botão para sair da tela cheia const exitBtn = document.createElement('button'); exitBtn.textContent = '✖ Sair Tela Cheia'; exitBtn.style.cssText = position: fixed !important; top: 25px !important; right: 25px !important; z-index: 2147483646 !important; background: #ef4444 !important; color: white !important; border: none !important; border-radius: 8px !important; padding: 12px 20px !important; font-weight: bold !important; cursor: pointer !important; box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; ; exitBtn.onclick = () => { mainWrapper.style.cssText = originalStyles.main; messageBox.style.cssText = originalStyles.message; chatContainer.style.cssText = originalStyles.chat; exitBtn.remove(); this.showStatus('Modo tela cheia desativado', 'success'); }; document.body.appendChild(exitBtn); this.showStatus('Modo tela cheia ativado! 🖥️', 'success'); } resetPage() { if (confirm('Tem certeza? Isso recarregará a página e perderá dados não salvos.')) { localStorage.removeItem('hf_qwen_to_zai'); location.reload(); } } toggleMinimize() { if (this.currentPanel) { this.currentPanel.classList.toggle('minimized'); } } removePanel() { if (this.currentPanel) { this.currentPanel.remove(); this.isInjected = false; } // Remover botões flutuantes document.querySelectorAll('.hf-floating-btn').forEach(btn => btn.remove()); } showStatus(message, type = 'info') { const statusEl = document.getElementById('hf-status'); if (!statusEl) return; statusEl.textContent = message; statusEl.className = status-message status-${type}; statusEl.style.display = 'block'; setTimeout(() => { statusEl.style.display = 'none'; }, 4000); } async autoPasteFromClipboard() { // Verificar automaticamente se há algo para colar try { const text = await navigator.clipboard.readText(); if (text && text.length > 100 && this.aiType === 'Zai') { // Provavelmente veio do Qwen setTimeout(() => this.smartPaste(), 1000); } } catch (e) { // Silencioso } } } // Instância global const bookmarkletManager = new BookmarkletManager(); // Função principal para ser chamada pelo bookmarklet function initHFBookmarklet() { // Verificar se estamos no Hugging Face if (!window.location.href.includes('huggingface.co')) { window.open('https://huggingface.co/chat/', '_blank'); return; } bookmarkletManager.injectMainPanel(); } // Auto-iniciar se a página já estiver carregada if (document.readyState === 'complete') { setTimeout(initHFBookmarklet, 1000); } else { window.addEventListener('load', () => { setTimeout(initHFBookmarklet, 1000); }); } // Expor para uso manual window.initHFBookmarklet = initHFBookmarklet; window.bookmarkletManager = bookmarkletManager;" eubottura-codefix-ai/components/bookmarklet-injector.js " // Arquivo substituído pelo injector.js na raiz " eubottura-codefix-ai/components/file-tree.js "class FileTree extends HTMLElement { constructor() { super(); this.fileStructure = {}; this.selectedFiles = []; } connectedCallback() { this.render(); } static get observedAttributes() { return ['file-structure']; } attributeChangedCallback(name, oldValue, newValue) { if (name === 'file-structure') { this.fileStructure = JSON.parse(newValue); this.render(); } } set fileStructure(value) { this._fileStructure = value; this.render(); } get fileStructure() { return this._fileStructure; } render() { this.innerHTML = ''; const container = document.createElement('div'); container.className = 'file-tree'; this.appendChild(container); if (this.fileStructure) { this.renderDirectory(this.fileStructure, container, ''); } } renderDirectory(structure, container, path) { Object.keys(structure).sort().forEach(key => { const item = structure[key]; const fullPath = path ? ${path}/${item.name} : item.name; const itemElement = document.createElement('div'); itemElement.className = 'file-tree-item'; itemElement.dataset.path = fullPath; if (item.type === 'directory') { itemElement.classList.add('folder'); itemElement.innerHTML = <div class="checkbox-container"> <input type="checkbox" ${item.selected ? 'checked' : ''}> </div> <i data-feather="folder"></i> <span>${item.name}</span> ; container.appendChild(itemElement); const childrenContainer = document.createElement('div'); childrenContainer.className = 'file-tree-children'; container.appendChild(childrenContainer); this.renderDirectory(item.children, childrenContainer, fullPath); } else { itemElement.classList.add('file'); itemElement.innerHTML = <div class="checkbox-container"> <input type="checkbox" ${item.selected ? 'checked' : ''}> </div> <i data-feather="file"></i> <span>${item.name}</span> ; container.appendChild(itemElement); // Add to selected files list this.selectedFiles.push({ path: fullPath, selected: item.selected }); } // Add event listener for checkbox const checkbox = itemElement.querySelector('input[type="checkbox"]'); checkbox.addEventListener('change', (e) => { this.updateSelection(fullPath, e.target.checked, item.type === 'directory'); this.dispatchSelectionEvent(); }); }); feather.replace(); } updateSelection(path, selected, isDirectory) { // Update the selected files list this.selectedFiles = this.selectedFiles.map(file => { if (file.path.startsWith(path)) { return { ...file, selected }; } return file; }); } dispatchSelectionEvent() { const event = new CustomEvent('selection-change', { detail: this.selectedFiles }); this.dispatchEvent(event); } } customElements.define('file-tree', FileTree);" eubottura-codefix-ai/index.html " <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CodeFlow AI - Análise e Correção Automatizada</title> <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> <link rel="stylesheet" href="style.css"> <script src="</script" target="_blank" rel="noreferrer">https://cdn.tailwindcss.com"></script> <script src="</script" target="_blank" rel="noreferrer">https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> <script src="</script" target="_blank" rel="noreferrer">https://unpkg.com/feather-icons"></script> <script> tailwind.config = { theme: { extend: { colors: { primary: '#6366f1', secondary: '#8b5cf6', qwen: '#10b981', zai: '#f59e0b', } } } } </script> </head> <body class="bg-gray-50 min-h-screen"> <div class="max-w-7xl mx-auto px-4 py-8"> <!-- Header --> <header class="text-center mb-8"> <h1 class="text-4xl font-bold text-gray-800 mb-2">CodeFlow AI <span class="text-primary">🚀</span></h1> <p class="text-gray-600">Análise e correção automatizada com IA colaborativa (Qwen + Zai)</p> <div class="mt-4 flex justify-center gap-4"> <a href="https://huggingface.co/Qwen/Qwen3-235B-A22B-Instruct-2507" target="_blank" class="text-qwen hover:text-green-600 flex items-center gap-2"> <i data-feather="external-link" class="w-4 h-4"></i> Qwen AI </a> <a href="https://huggingface.co/zai-org/GLM-4.6" target="_blank" class="text-zai hover:text-amber-600 flex items-center gap-2"> <i data-feather="external-link" class="w-4 h-4"></i> Zai AI </a> </div> </header> <!-- Tabs Navigation --> <div class="bg-white rounded-t-xl shadow-md"> <div class="flex border-b"> <button class="tab-btn active px-6 py-3 font-medium text-gray-700 border-b-2 border-primary text-primary" data-tab="upload"> <i data-feather="upload" class="w-4 h-4 inline mr-2"></i> 1. Upload </button> <button class="tab-btn px-6 py-3 font-medium text-gray-700 border-b-2 border-transparent hover:border-gray-300" data-tab="qwen"> <i data-feather="cpu" class="w-4 h-4 inline mr-2"></i> 2. Qwen (Análise) </button> <button class="tab-btn px-6 py-3 font-medium text-gray-700 border-b-2 border-transparent hover:border-gray-300" data-tab="zai"> <i data-feather="tool" class="w-4 h-4 inline mr-2"></i> 3. Zai (Correção) </button> <button class="tab-btn px-6 py-3 font-medium text-gray-700 border-b-2 border-transparent hover:border-gray-300" data-tab="results"> <i data-feather="download" class="w-4 h-4 inline mr-2"></i> 4. Resultados </button> </div> </div> <!-- Tab Content --> <div class="bg-white rounded-b-xl shadow-md p-6"> <!-- Upload Tab --> <div id="upload-tab" class="tab-content"> <h2 class="text-2xl font-semibold text-gray-800 mb-6">📤 Upload do Projeto</h2> <!-- File Upload --> <div class="mb-6"> <label class="block text-gray-700 mb-2">Selecione o diretório do projeto</label> <div id="drop-zone" class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center cursor-pointer transition-colors hover:border-primary"> <i data-feather="folder" class="w-10 h-10 text-gray-400 mx-auto mb-3"></i> <p class="text-gray-600 mb-2">Arraste e solte sua pasta de projeto aqui</p> <p class="text-gray-500 text-sm">ou</p> <button id="browse-btn" class="mt-2 bg-primary text-white px-4 py-2 rounded-lg hover:bg-indigo-600 transition-colors"> Procurar Arquivos </button> <input type="file" id="file-input" webkitdirectory directory multiple class="hidden"> </div> </div> <!-- File Tree --> <div id="file-tree-container" class="hidden mb-6"> <div class="flex justify-between items-center mb-4"> <h3 class="text-lg font-medium text-gray-700">Estrutura de Arquivos</h3> <div class="flex gap-2"> <button id="select-all" class="text-sm bg-gray-200 hover:bg-gray-300 px-3 py-1 rounded">Selecionar Tudo</button> <button id="deselect-all" class="text-sm bg-gray-200 hover:bg-gray-300 px-3 py-1 rounded">Desmarcar Tudo</button> </div> </div> <div id="file-tree" class="border border-gray-200 rounded-lg p-4 max-h-64 overflow-y-auto bg-gray-50"> <div class="text-gray-500 text-sm"> <i data-feather="info" class="w-4 h-4 inline mr-2"></i> Os arquivos selecionados serão incluídos no relatório </div> </div> </div> <!-- Generate Report Button --> <button id="generate-report" class="w-full bg-qwen text-white py-3 rounded-lg font-medium hover:bg-green-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" disabled> <i data-feather="file-text" class="w-5 h-5 inline mr-2"></i> Gerar Relatório Estruturado </button> <!-- Report Output --> <div id="report-container" class="hidden mt-6"> <h3 class="text-lg font-medium text-gray-700 mb-2">Relatório Gerado</h3> <textarea id="report-output" class="w-full h-48 p-3 border border-gray-300 rounded-lg font-mono text-sm" readonly></textarea> <div class="flex gap-2 mt-2"> <button id="copy-report" class="flex-1 bg-primary text-white py-2 rounded-lg hover:bg-indigo-600 transition-colors"> <i data-feather="clipboard" class="w-4 h-4 inline mr-2"></i> Copiar Relatório </button> <button id="download-report" class="flex-1 bg-gray-600 text-white py-2 rounded-lg hover:bg-gray-700 transition-colors"> <i data-feather="download" class="w-4 h-4 inline mr-2"></i> Baixar TXT </button> </div> </div> </div> <!-- Qwen Tab --> <div id="qwen-tab" class="tab-content hidden"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> <!-- Qwen Input --> <div> <h2 class="text-2xl font-semibold text-gray-800 mb-4 text-qwen">🤖 Qwen - Análise do Problema</h2> <div class="mb-6"> <label class="block text-gray-700 mb-2">Descrição do Problema</label> <div class="flex"> <input type="text" id="problem-input-qwen" placeholder="Descreva o problema a ser analisado..." class="flex-grow border border-gray-300 rounded-l-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-qwen"> <button id="voice-btn-qwen" class="bg-gray-200 border border-l-0 border-gray-300 rounded-r-lg px-4 py-2 hover:bg-gray-300"> <i data-feather="mic" class="w-5 h-5 text-gray-600"></i> </button> </div> </div> <div class="mb-6"> <label class="block text-gray-700 mb-2">Relatório do Projeto</label> <textarea id="qwen-project-input" class="w-full h-64 p-3 border border-gray-300 rounded-lg font-mono text-sm" placeholder="Cole aqui o relatório gerado na aba anterior..."></textarea> </div> <div class="flex gap-2"> <button id="open-qwen" class="flex-1 bg-qwen text-white py-3 rounded-lg font-medium hover:bg-green-600 transition-colors"> <i data-feather="external-link" class="w-5 h-5 inline mr-2"></i> Abrir Qwen no Hugging Face </button> <button id="inject-qwen" class="flex-1 bg-gray-600 text-white py-3 rounded-lg font-medium hover:bg-gray-700 transition-colors" onclick="window.open('javascript:(function(){initHFBookmarklet();})()', '_blank')"> <i data-feather="code" class="w-5 h-5 inline mr-2"></i> Abrir Assistente </button> </div> </div> <!-- Qwen Output --> <div> <h3 class="text-lg font-medium text-gray-700 mb-2">📋 Análise do Qwen</h3> <div class="mb-4"> <div class="flex justify-between mb-2"> <span class="text-gray-700">Status da Análise</span> <span id="qwen-status" class="text-gray-500">Aguardando</span> </div> <div class="w-full bg-gray-200 rounded-full h-2"> <div id="qwen-progress" class="bg-qwen h-2 rounded-full transition-all duration-500" style="width: 0%"></div> </div> </div> <div class="bg-gray-50 border border-gray-200 rounded-lg p-4 max-h-96 overflow-y-auto"> <div id="qwen-analysis" class="text-sm font-mono whitespace-pre-wrap"></div> </div> <button id="send-to-zai" class="w-full mt-4 bg-zai text-white py-3 rounded-lg font-medium hover:bg-amber-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" disabled> <i data-feather="arrow-right" class="w-5 h-5 inline mr-2"></i> Enviar Análise para Zai </button> </div> </div> </div> <!-- Zai Tab --> <div id="zai-tab" class="tab-content hidden"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> <!-- Zai Input --> <div> <h2 class="text-2xl font-semibold text-gray-800 mb-4 text-zai">🔧 Zai - Correção do Código</h2> <div class="mb-6"> <label class="block text-gray-700 mb-2">Análise Recebida do Qwen</label> <textarea id="zai-analysis-input" class="w-full h-48 p-3 border border-gray-300 rounded-lg font-mono text-sm" placeholder="Cole aqui a análise do Qwen..."></textarea> </div> <div class="flex gap-2"> <button id="open-zai" class="flex-1 bg-zai text-white py-3 rounded-lg font-medium hover:bg-amber-600 transition-colors"> <i data-feather="external-link" class="w-5 h-5 inline mr-2"></i> Abrir Zai no Hugging Face </button> <button id="inject-zai" class="flex-1 bg-gray-600 text-white py-3 rounded-lg font-medium hover:bg-gray-700 transition-colors" onclick="window.open('javascript:(function(){initHFBookmarklet();})()', '_blank')"> <i data-feather="code" class="w-5 h-5 inline mr-2"></i> Abrir Assistente </button> </div> </div> <!-- Zai Output --> <div> <h3 class="text-lg font-medium text-gray-700 mb-2">📝 Correções do Zai</h3> <div class="mb-4"> <div class="flex justify-between mb-2"> <span class="text-gray-700">Status da Correção</span> <span id="zai-status" class="text-gray-500">Aguardando</span> </div> <div class="w-full bg-gray-200 rounded-full h-2"> <div id="zai-progress" class="bg-zai h-2 rounded-full transition-all duration-500" style="width: 0%"></div> </div> </div> <div class="bg-gray-50 border border-gray-200 rounded-lg p-4 max-h-96 overflow-y-auto"> <div id="zai-corrections" class="text-sm font-mono whitespace-pre-wrap"></div> </div> <button id="process-corrections" class="w-full mt-4 bg-primary text-white py-3 rounded-lg font-medium hover:bg-indigo-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" disabled> <i data-feather="check" class="w-5 h-5 inline mr-2"></i> Processar Correções </button> </div> </div> </div> <!-- Results Tab --> <div id="results-tab" class="tab-content hidden"> <h2 class="text-2xl font-semibold text-gray-800 mb-6">📊 Resultados Finais</h2> <!-- Summary --> <div id="results-summary" class="hidden mb-6 p-4 bg-blue-50 border border-blue-200 rounded-lg"> <div class="flex items-center"> <i data-feather="check-circle" class="w-6 h-6 text-blue-600 mr-3"></i> <div> <span class="text-blue-800 font-medium">Processo Concluído!</span> <p class="text-sm text-blue-700 mt-1" id="summary-text"></p> </div> </div> </div> <!-- File List --> <div id="results-file-list" class="mb-6"> <h3 class="text-lg font-medium text-gray-700 mb-3">Arquivos Corrigidos</h3> <div id="corrected-files-list" class="space-y-2"></div> </div> <!-- Actions --> <div class="flex gap-2"> <button id="download-all-corrected" class="flex-1 bg-secondary text-white py-3 rounded-lg font-medium hover:bg-purple-600 transition-colors"> <i data-feather="download" class="w-5 h-5 inline mr-2"></i> Baixar Todos os Arquivos </button> <button id="generate-patch" class="flex-1 bg-gray-600 text-white py-3 rounded-lg font-medium hover:bg-gray-700 transition-colors"> <i data-feather="git-branch" class="w-5 h-5 inline mr-2"></i> Gerar Patch </button> <button id="start-new" class="flex-1 bg-gray-200 text-gray-700 py-3 rounded-lg font-medium hover:bg-gray-300 transition-colors"> <i data-feather="refresh-cw" class="w-5 h-5 inline mr-2"></i> Novo Projeto </button> </div> </div> </div> <!-- Error Display --> <div id="error-container" class="hidden fixed bottom-4 right-4 bg-red-50 border border-red-200 rounded-lg p-4 max-w-md shadow-lg"> <div class="flex items-start"> <i data-feather="alert-circle" class="w-5 h-5 text-red-600 mr-3 mt-0.5"></i> <div> <p class="text-red-800 font-medium">Erro</p> <p class="text-red-700 text-sm mt-1" id="error-message"></p> </div> <button id="dismiss-error" class="ml-4 text-red-600 hover:text-red-800"> <i data-feather="x" class="w-4 h-4"></i> </button> </div> </div> <!-- Success Notification --> <div id="success-container" class="hidden fixed bottom-4 right-4 bg-green-50 border border-green-200 rounded-lg p-4 max-w-md shadow-lg"> <div class="flex items-start"> <i data-feather="check-circle" class="w-5 h-5 text-green-600 mr-3 mt-0.5"></i> <div> <p class="text-green-800 font-medium">Sucesso</p> <p class="text-green-700 text-sm mt-1" id="success-message"></p> </div> <button id="dismiss-success" class="ml-4 text-green-600 hover:text-green-800"> <i data-feather="x" class="w-4 h-4"></i> </button> </div> </div> <!-- Novo Modal de Instruções --> <div id="bookmarklet-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> <div class="bg-white rounded-lg p-6 max-w-3xl max-h-[80vh] overflow-y-auto"> <div class="flex justify-between items-center mb-4"> <h3 class="text-xl font-bold text-gray-800">🚀 Assistente CodeFlow AI</h3> <button id="close-bookmarklet" class="text-gray-500 hover:text-gray-700"> <i data-feather="x" class="w-6 h-6"></i> </button> </div> <div class="space-y-4"> <div> <h4 class="font-semibold text-gray-700 mb-2">🎯 O que é?</h4> <p class="text-sm text-gray-600"> Uma ferramenta moderna que substitui os bookmarklets tradicionais, fornecendo uma interface intuitiva para automação no Hugging Face. </p> </div> <div> <h4 class="font-semibold text-gray-700 mb-2">📦 Como Instalar</h4> <p class="text-sm text-gray-600 mb-2"> Arraste um dos links abaixo para sua barra de favoritos: </p> <div class="space-y-2"> <div class="bg-gray-100 p-3 rounded-lg"> <a href="javascript:(function(){if(!window.location.href.includes('huggingface.co')){window.open('https://huggingface.co/chat/','_blank');return;}if(window.bookmarkletManager){window.bookmarkletManager.injectMainPanel();}else{const s=document.createElement('script');s.src='bookmarklet-manager.js';document.head.appendChild(s);}})();" class="text-blue-600 hover:text-blue-800 font-mono text-sm block"> 🤖 HF Assistant Completo </a> </div> </div> </div> <div> <h4 class="font-semibold text-gray-700 mb-2">✨ Recursos</h4> <ul class="text-sm text-gray-600 list-disc list-inside space-y-1"> <li>Interface moderna e responsiva</li> <li>Auto-detecção de Qwen e ZAI</li> <li>Transferência automática entre IAs</li> <li>Modo tela cheia otimizado</li> <li>Atalhos de teclado (Ctrl+Shift+V/C)</li> <li>Sincronização via localStorage</li> </ul> </div> <div> <h4 class="font-semibold text-gray-700 mb-2">🎮 Atalhos</h4> <ul class="text-sm text-gray-600 list-disc list-inside space-y-1"> <li><kbd>Ctrl+Shift+V</kbd> - Colar inteligente</li> <li><kbd>Ctrl+Shift+C</kbd> - Copiar resposta</li> </ul> </div> </div> </div> </div> <script src="script.js"></script> <script src="bookmarklet-manager.js"></script> <script>feather.replace();</script> <script> // Inicializar bookmarklet manager automaticamente document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { if (window.bookmarkletManager && window.location.href.includes('huggingface.co')) { window.bookmarkletManager.injectMainPanel(); } }, 2000); }); </script> </body> </html> " eubottura-codefix-ai/injector.js "// Hugging Face AI Injector - Versão Corrigida // Este script injeta funcionalidades automatizadas nas páginas do Hugging Face (function() { 'use strict'; // Verificar se já foi injetado if (window.hfInjectorLoaded) { console.log('HF Injector já foi carregado'); return; } window.hfInjectorLoaded = true; // Aguardar a página carregar completamente function waitForElement(selector, callback, maxAttempts = 50) { let attempts = 0; function check() { attempts++; const element = document.querySelector(selector); if (element) { callback(element); } else if (attempts < maxAttempts) { setTimeout(check, 200); } else { // CORREÇÃO: Adicionadas crases ao redor da string do console.error console.error(Elemento não encontrado após ${maxAttempts} tentativas: ${selector}); } } check(); } // Injetar o painel de controle function injectControlPanel() { // Remover painel existente se houver const existingPanel = document.getElementById('hf-ai-injector-panel'); if (existingPanel) { existingPanel.remove(); } // Criar painel const panel = document.createElement('div'); panel.id = 'hf-ai-injector-panel'; // CORREÇÃO: Adicionadas aspas simples no início e no final da string CSS panel.style.cssText = 'position: fixed !important; top: 20px !important; right: 20px !important; width: 320px !important; background: white !important; border: 2px solid #6366f1 !important; border-radius: 12px !important; padding: 20px !important; box-shadow: 0 10px 25px rgba(0,0,0,0.2) !important; z-index: 999999 !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; font-size: 14px !important;'; // Determinar qual AI estamos const isQwen = window.location.href.includes('Qwen') || window.location.href.includes('qwen'); const isZai = window.location.href.includes('zai-org') || window.location.href.includes('zai'); const aiType = isQwen ? 'Qwen' : (isZai ? 'ZAI' : 'HF Chat'); // CORREÇÃO: Adicionadas crases no início e no final da string HTML para permitir múltiplas linhas e template literals panel.innerHTML = <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;"> <h3 style="margin: 0; color: #333; font-size: 16px; font-weight: 600;">🤖 ${aiType} Injector</h3> <button id="close-injector" style="background: none; border: none; font-size: 20px; cursor: pointer; color: #666; padding: 0; width: 24px; height: 24px;">×</button> </div> <div id="injector-controls"> <button id="inject-prompt" style="width: 100%; background: #6366f1; color: white; border: none; padding: 10px; border-radius: 6px; cursor: pointer; margin-bottom: 10px; font-weight: 500; font-size: 13px;">📝 Injetar Prompt Otimizado</button> <button id="auto-fill" style="width: 100%; background: #10b981; color: white; border: none; padding: 10px; border-radius: 6px; cursor: pointer; margin-bottom: 10px; font-weight: 500; font-size: 13px;">🔄 Colar da Área de Transferência</button> <button id="extract-response" style="width: 100%; background: #f59e0b; color: white; border: none; padding: 10px; border-radius: 6px; cursor: pointer; margin-bottom: 10px; font-weight: 500; font-size: 13px;">📋 Copiar Resposta</button> ${isQwen ? '<button id="send-to-zai" style="width: 100%; background: #8b5cf6; color: white; border: none; padding: 10px; border-radius: 6px; cursor: pointer; font-weight: 500; font-size: 13px;">➡️ Enviar para ZAI</button>' : ''} </div> <div id="status-message" style="margin-top: 10px; padding: 8px; background: #f0f9ff; border-radius: 6px; font-size: 12px; color: #0369a1; display: none; white-space: normal; word-wrap: break-word;"></div> ; document.body.appendChild(panel); // Adicionar event listeners document.getElementById('close-injector').onclick = () => { panel.remove(); window.hfInjectorLoaded = false; }; document.getElementById('inject-prompt').onclick = injectPrompt; document.getElementById('auto-fill').onclick = autoFillFromClipboard; document.getElementById('extract-response').onclick = extractResponse; if (isQwen) { document.getElementById('send-to-zai').onclick = sendToZai; } showStatus('Injector ativado com sucesso!', 'success'); } // Mostrar mensagem de status function showStatus(message, type = 'info') { const statusEl = document.getElementById('status-message'); if (!statusEl) return; statusEl.textContent = message; statusEl.style.display = 'block'; if (type === 'error') { statusEl.style.background = '#fef2f2'; statusEl.style.color = '#dc2626'; } else if (type === 'success') { statusEl.style.background = '#f0fdf4'; statusEl.style.color = '#16a34a'; } else { statusEl.style.background = '#f0f9ff'; statusEl.style.color = '#0369a1'; } setTimeout(() => { statusEl.style.display = 'none'; }, 4000); } // Injetar prompt otimizado function injectPrompt() { const isQwen = window.location.href.includes('Qwen') || window.location.href.includes('qwen'); let prompt; // CORREÇÃO: Adicionadas crases para delimitar as strings de várias linhas if (isQwen) { prompt = Você é um especialista em análise de código com alta autonomia. === TAREFA PRINCIPAL === Analise o código fornecido e identifique TODOS os problemas possíveis. === FORMATO DE SAÍDA OBRIGATÓRIO === Para cada arquivo com problemas: nome/do/arquivo.ext \`` "código_completo_corrigido" ``` === REGRAS === 1. Use sempre aspas para delimitar o código 2. Inclua o caminho completo do arquivo 3. Forneça o código COMPLETO e corrigido 4. Sem explicações extras, apenas o formato acima === CÓDIGO PARA ANÁLISE ===; } else { prompt = Você é um finalizador de código. === TAREFA === Receba o diagnóstico do Qwen e produza o código final corrigido. === FORMATO EXATO === nome/do/arquivo.ext ``` "código_final_completo" ``` === REGRAS === 1. Apenas o código corrigido 2. Sem explicações 3. Formato exato acima === ENTRADA DO QWEN ===; } // Tentar encontrar o textarea de várias maneiras const selectors = [ 'textarea[data-testid="chat-input"]', 'textarea[placeholder*="Message"]', 'textarea[placeholder*="message"]', 'textarea[aria-label*="Message"]', 'textarea.w-full', 'textarea', '[contenteditable="true"]' ]; let textarea = null; for (const selector of selectors) { textarea = document.querySelector(selector); if (textarea) { console.log(Textarea encontrado com selector: ${selector}); break; } } if (textarea) { // Tentar diferentes métodos para definir o valor try { if (textarea.tagName.toLowerCase() === 'textarea') { textarea.value = prompt; textarea.focus(); // Disparar eventos para garantir que o valor seja registrado textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); } else { // Para contenteditable textarea.textContent = prompt; textarea.focus(); } showStatus('Prompt injetado com sucesso!', 'success'); } catch (error) { console.error('Erro ao injetar prompt:', error); showStatus('Erro ao injetar prompt', 'error'); } } else { showStatus('Campo de entrada não encontrado. Clique na caixa de texto primeiro.', 'error'); } } // Preencher automaticamente da área de transferência async function autoFillFromClipboard() { try { const text = await navigator.clipboard.readText(); const selectors = [ 'textarea[data-testid="chat-input"]', 'textarea[placeholder*="Message"]', 'textarea[placeholder*="message"]', 'textarea[aria-label*="Message"]', 'textarea.w-full', 'textarea', '[contenteditable="true"]' ]; let textarea = null; for (const selector of selectors) { textarea = document.querySelector(selector); if (textarea) { break; } } if (textarea) { try { if (textarea.tagName.toLowerCase() === 'textarea') { textarea.value = text; textarea.focus(); textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); } else { textarea.textContent = text; textarea.focus(); } showStatus('Conteúdo colado com sucesso!', 'success'); } catch (error) { console.error('Erro ao colar:', error); showStatus('Erro ao colar conteúdo', 'error'); } } else { showStatus('Campo de entrada não encontrado', 'error'); } } catch (error) { console.error('Erro ao acessar área de transferência:', error); showStatus('Erro ao acessar área de transferência', 'error'); } } // Extrair resposta da IA function extractResponse() { // Tentar diferentes seletores para encontrar as mensagens const selectors = [ '[data-testid="conversation-turn"]', '.prose', '.message-content', '.markdown-body', '[class*="message"]', '[class*="response"]', '[class*="turn"]' ]; let messages = []; for (const selector of selectors) { messages = document.querySelectorAll(selector); if (messages.length > 0) { console.log(Mensagens encontradas com selector: ${selector}); break; } } if (messages.length === 0) { // Tentar encontrar textos que parecem ser respostas // CORREÇÃO: Alterado o seletor de '' para '*' para buscar todos os elementos. const allElements = document.querySelectorAll('*'); for (const element of allElements) { if (element.textContent && element.textContent.length > 100) { // Verificar se não está dentro de um elemento pequeno const rect = element.getBoundingClientRect(); if (rect.width > 200 && rect.height > 50) { messages = [element]; break; } } } } if (messages.length > 0) { const lastMessage = messages[messages.length - 1]; let messageText = ''; // Tentar extrair texto de diferentes maneiras if (lastMessage.textContent) { messageText = lastMessage.textContent.trim(); } else if (lastMessage.innerText) { messageText = lastMessage.innerText.trim(); } else { messageText = lastMessage.innerHTML.replace(/<[^>]*>/g, '').trim(); } if (messageText && messageText.length > 10) { navigator.clipboard.writeText(messageText).then(() => { showStatus('Resposta copiada com sucesso!', 'success'); }).catch(() => { // Fallback para método mais antigo const textArea = document.createElement('textarea'); textArea.value = messageText; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); showStatus('Resposta copiada (método alternativo)!', 'success'); }); } else { showStatus('Nenhum texto encontrado na resposta', 'error'); } } else { showStatus('Nenhuma mensagem encontrada', 'error'); } } // Enviar para ZAI function sendToZai() { // Extrair resposta primeiro const selectors = [ '[data-testid="conversation-turn"]', '.prose', '.message-content', '.markdown-body' ]; let messages = []; for (const selector of selectors) { messages = document.querySelectorAll(selector); if (messages.length > 0) { break; } } if (messages.length === 0) { showStatus('Nenhuma mensagem encontrada para enviar', 'error'); return; } const lastMessage = messages[messages.length - 1]; let messageText = ''; if (lastMessage.textContent) { messageText = lastMessage.textContent.trim(); } else if (lastMessage.innerText) { messageText = lastMessage.innerText.trim(); } if (messageText && messageText.length > 10) { // Armazenar em localStorage localStorage.setItem('qwen_to_zai_transfer', messageText); // Abrir ZAI em nova aba const zaiUrl = 'https://huggingface.co/zai-org/GLM-4.6'; window.open(zaiUrl, '_blank'); showStatus('Enviado para ZAI! Abrindo nova aba...', 'success'); } else { showStatus('Não foi possível extrair o texto para enviar', 'error'); } } // Verificar dados recebidos do Qwen (apenas na página ZAI) function checkForIncomingData() { const isZai = window.location.href.includes('zai-org') || window.location.href.includes('zai'); if (!isZai) return; const incomingData = localStorage.getItem('qwen_to_zai_transfer'); if (incomingData) { const selectors = [ 'textarea[data-testid="chat-input"]', 'textarea[placeholder*="Message"]', 'textarea[placeholder*="message"]', 'textarea.w-full', 'textarea', '[contenteditable="true"]' ]; let textarea = null; for (const selector of selectors) { textarea = document.querySelector(selector); if (textarea) { break; } } if (textarea) { try { if (textarea.tagName.toLowerCase() === 'textarea') { textarea.value = incomingData; textarea.focus(); textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); } else { textarea.textContent = incomingData; textarea.focus(); } showStatus('Dados do Qwen recebidos e injetados!', 'success'); localStorage.removeItem('qwen_to_zai_transfer'); } catch (error) { console.error('Erro ao injetar dados:', error); showStatus('Erro ao injetar dados do Qwen', 'error'); } } } } // Função principal de inicialização function init() { console.log('HF AI Injector: Inicializando...'); // Verificar se estamos em uma página suportada const supportedUrls = ['huggingface.co', 'Qwen', 'qwen', 'zai-org', 'zai']; const isSupported = supportedUrls.some(url => window.location.href.includes(url)); if (!isSupported) { console.log('HF Injector: Página não suportada'); return; } // Aguardar um pouco para a página carregar setTimeout(() => { injectControlPanel(); // Verificar dados recebidos periodicamente (para ZAI) setInterval(checkForIncomingData, 3000); }, 1000); } // Inicializar if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();" eubottura-codefix-ai/README.md "--- title: CodeFix AI ✨ colorFrom: gray colorTo: gray emoji: 🐳 sdk: static pinned: false tags: - deepsite-v3 --- # Welcome to your new DeepSite project! This project was created with [DeepSite](https://huggingface.co/deepsite). " eubottura-codefix-ai/script.js " // Global state const state = { files: [], fileContents: new Map(), selectedFiles: new Set(), processedText: '', qwenResponse: '', zaiResponse: '', correctedFiles: [], filesNeedingFix: [], isProcessing: false, currentTab: 'upload', fileTreeStructure: null }; // DOM Elements const elements = { // Upload tab dropZone: document.getElementById('drop-zone'), fileInput: document.getElementById('file-input'), browseBtn: document.getElementById('browse-btn'), fileTreeContainer: document.getElementById('file-tree-container'), fileTree: document.getElementById('file-tree'), generateReport: document.getElementById('generate-report'), reportContainer: document.getElementById('report-container'), reportOutput: document.getElementById('report-output'), copyReport: document.getElementById('copy-report'), downloadReport: document.getElementById('download-report'), selectAll: document.getElementById('select-all'), deselectAll: document.getElementById('deselect-all'), // Qwen tab problemInputQwen: document.getElementById('problem-input-qwen'), voiceBtnQwen: document.getElementById('voice-btn-qwen'), qwenProjectInput: document.getElementById('qwen-project-input'), openQwen: document.getElementById('open-qwen'), injectQwen: document.getElementById('inject-qwen'), qwenStatus: document.getElementById('qwen-status'), qwenProgress: document.getElementById('qwen-progress'), qwenAnalysis: document.getElementById('qwen-analysis'), sendToZai: document.getElementById('send-to-zai'), // Zai tab zaiAnalysisInput: document.getElementById('zai-analysis-input'), openZai: document.getElementById('open-zai'), injectZai: document.getElementById('inject-zai'), zaiStatus: document.getElementById('zai-status'), zaiProgress: document.getElementById('zai-progress'), zaiCorrections: document.getElementById('zai-corrections'), processCorrections: document.getElementById('process-corrections'), // Results tab resultsSummary: document.getElementById('results-summary'), summaryText: document.getElementById('summary-text'), correctedFilesList: document.getElementById('corrected-files-list'), downloadAllCorrected: document.getElementById('download-all-corrected'), generatePatch: document.getElementById('generate-patch'), startNew: document.getElementById('start-new'), // Notifications errorContainer: document.getElementById('error-container'), errorMessage: document.getElementById('error-message'), dismissError: document.getElementById('dismiss-error'), successContainer: document.getElementById('success-container'), successMessage: document.getElementById('success-message'), dismissSuccess: document.getElementById('dismiss-success'), // Modals bookmarkletModal: document.getElementById('bookmarklet-modal'), closeBookmarklet: document.getElementById('close-bookmarklet'), }; // Initialize the app document.addEventListener('DOMContentLoaded', () => { setupEventListeners(); setupTabNavigation(); }); // Setup tab navigation function setupTabNavigation() { const tabButtons = document.querySelectorAll('.tab-btn'); const tabContents = document.querySelectorAll('.tab-content'); tabButtons.forEach(btn => { btn.addEventListener('click', () => { const targetTab = btn.dataset.tab; // Update button states tabButtons.forEach(b => { b.classList.remove('active', 'border-primary', 'text-primary'); b.classList.add('border-transparent'); }); btn.classList.add('active', 'border-primary', 'text-primary'); btn.classList.remove('border-transparent'); // Update content visibility tabContents.forEach(content => { content.classList.add('hidden'); }); document.getElementById(${targetTab}-tab).classList.remove('hidden'); state.currentTab = targetTab; }); }); } // Set up event listeners function setupEventListeners() { // File upload elements.browseBtn.addEventListener('click', () => elements.fileInput.click()); elements.dropZone.addEventListener('dragover', handleDragOver); elements.dropZone.addEventListener('drop', handleDrop); elements.fileInput.addEventListener('change', handleFileSelect); // File tree selection elements.selectAll.addEventListener('click', selectAllFiles); elements.deselectAll.addEventListener('click', deselectAllFiles); // Report generation elements.generateReport.addEventListener('click', generateStructuredReport); elements.copyReport.addEventListener('click', copyReportToClipboard); elements.downloadReport.addEventListener('click', downloadReportFile); // Voice input for Qwen elements.voiceBtnQwen.addEventListener('click', () => startVoiceInput(elements.problemInputQwen)); // Qwen interactions elements.openQwen.addEventListener('click', openQwenInHuggingFace); elements.injectQwen.addEventListener('click', showBookmarkletInstructions); elements.sendToZai.addEventListener('click', sendAnalysisToZai); // Zai interactions elements.openZai.addEventListener('click', openZaiInHuggingFace); elements.injectZai.addEventListener('click', showBookmarkletInstructions); elements.processCorrections.addEventListener('click', processZaiCorrections); // Results actions elements.downloadAllCorrected.addEventListener('click', downloadAllCorrectedFiles); elements.generatePatch.addEventListener('click', generateGitPatch); elements.startNew.addEventListener('click', resetApplication); // Notifications elements.dismissError.addEventListener('click', () => elements.errorContainer.classList.add('hidden')); elements.dismissSuccess.addEventListener('click', () => elements.successContainer.classList.add('hidden')); elements.closeBookmarklet.addEventListener('click', () => elements.bookmarkletModal.classList.add('hidden')); } // File handling functions function handleDragOver(e) { e.preventDefault(); e.stopPropagation(); elements.dropZone.classList.add('border-primary'); } function handleDrop(e) { e.preventDefault(); e.stopPropagation(); elements.dropZone.classList.remove('border-primary'); const items = e.dataTransfer.items; if (items.length > 0) { processDirectoryItems(items); } } function handleFileSelect(e) { const files = e.target.files; if (files.length > 0) { processDirectoryFiles(files); } } // Process directory items from drag and drop function processDirectoryItems(items) { state.files = []; for (let i = 0; i < items.length; i++) { const item = items[i].webkitGetAsEntry(); if (item) { traverseFileTree(item); } } // Process files automatically after a short delay setTimeout(() => { processAllFiles(); }, 500); } // Process directory files from file input function processDirectoryFiles(files) { state.files = Array.from(files).map(file => ({ name: file.webkitRelativePath || file.name, file: file, isDirectory: false, selected: true })); // Clear previous selected files state.selectedFiles.clear(); // Add all files to selected set state.files.forEach(fileObj => { state.selectedFiles.add(fileObj.name); }); // Build file tree structure buildFileTreeStructure(); // Render file tree renderFileTree(); // Show file tree container elements.fileTreeContainer.classList.remove('hidden'); // Enable generate button elements.generateReport.disabled = false; showNotification(${files.length} arquivos carregados com sucesso!, 'success'); } // Build hierarchical file tree structure function buildFileTreeStructure() { const tree = {}; state.files.forEach(fileObj => { const path = fileObj.name; const parts = path.split('/'); let current = tree; parts.forEach((part, index) => { if (index === parts.length - 1) { // This is a file current[part] = { type: 'file', path: path, file: fileObj.file, selected: fileObj.selected }; } else { // This is a directory if (!current[part]) { current[part] = { type: 'directory', children: {} }; } current = current[part].children; } }); }); state.fileTreeStructure = tree; } // Render file tree in the DOM function renderFileTree() { elements.fileTree.innerHTML = ''; renderTreeNodes(state.fileTreeStructure, elements.fileTree, ''); } // Render tree nodes recursively function renderTreeNodes(tree, container, parentPath) { Object.keys(tree).sort().forEach(key => { const node = tree[key]; const fullPath = parentPath ? ${parentPath}/${key}: key; const nodeElement = document.createElement('div'); nodeElement.className = 'tree-node'; if (node.type === 'directory') { nodeElement.innerHTML = <div class="tree-item tree-folder"> <input type="checkbox" class="folder-checkbox" data-path="${fullPath}" checked> <i data-feather="folder" class="w-4 h-4 text-amber-600 mr-2"></i> <span class="folder-name">${key}</span> </div> <div class="tree-children ml-4 border-l border-gray-200 pl-2"></div> ; container.appendChild(nodeElement); const childrenContainer = nodeElement.querySelector('.tree-children'); renderTreeNodes(node.children, childrenContainer, fullPath); } else { nodeElement.innerHTML = <div class="tree-item tree-file"> <input type="checkbox" class="file-checkbox" data-path="${node.path}" checked> <i data-feather="file" class="w-4 h-4 text-blue-600 mr-2"></i> <span class="file-name">${key}</span> </div> ; container.appendChild(nodeElement); // Store file reference state.selectedFiles.add(node.path); } // Add event listener for checkbox const checkbox = nodeElement.querySelector('input[type="checkbox"]'); checkbox.addEventListener('change', (e) => { handleFileSelection(e.target, node, fullPath); }); }); feather.replace(); } // Handle file selection changes function handleFileSelection(checkbox, node, path) { if (node.type === 'directory') { // Toggle all children const childCheckboxes = checkbox.closest('.tree-node').querySelectorAll('.file-checkbox, .folder-checkbox'); childCheckboxes.forEach(cb => { cb.checked = checkbox.checked; const childPath = cb.dataset.path; if (checkbox.checked) { state.selectedFiles.add(childPath); } else { state.selectedFiles.delete(childPath); } }); } else { // Toggle single file if (checkbox.checked) { state.selectedFiles.add(path); } else { state.selectedFiles.delete(path); } } // Enable/disable generate button based on selection const totalFiles = document.querySelectorAll('.file-checkbox').length; const selectedCount = document.querySelectorAll('.file-checkbox:checked').length; elements.generateReport.disabled = selectedCount === 0 && totalFiles > 0; // Update selection info console.log(Arquivos selecionados: ${state.selectedFiles.size} de ${totalFiles}); } // Select all files function selectAllFiles() { document.querySelectorAll('.tree-node input[type="checkbox"]').forEach(checkbox => { checkbox.checked = true; }); state.files.forEach(fileObj => { state.selectedFiles.add(fileObj.name); }); elements.generateReport.disabled = false; } // Deselect all files function deselectAllFiles() { document.querySelectorAll('.tree-node input[type="checkbox"]').forEach(checkbox => { checkbox.checked = false; }); state.selectedFiles.clear(); elements.generateReport.disabled = true; } // Traverse file tree (for drag and drop) function traverseFileTree(item, path = '') { if (item.isFile) { item.file(file => { state.files.push({ name: path + file.name, file: file, isDirectory: false, selected: true }); }); } else if (item.isDirectory) { // Get folder contents const dirReader = item.createReader(); dirReader.readEntries(entries => { for (let i = 0; i < entries.length; i++) { traverseFileTree(entries[i], path + item.name + "/"); } }); } } // Generate structured report async function generateStructuredReport() { // Check if we have files uploaded if (state.files.length === 0) { showError('Por favor, faça upload dos arquivos do projeto primeiro.'); return; } // Get selected files from checkboxes or use all if none selected let selectedFilesArray = []; const checkedBoxes = document.querySelectorAll('.tree-node input[type="checkbox"]:checked'); if (checkedBoxes.length === 0) { // If no files are selected, use all uploaded files selectedFilesArray = state.files.map(f => f.name); } else { // Use checked files checkedBoxes.forEach(checkbox => { const path = checkbox.dataset.path; if (path) { selectedFilesArray.push(path); } }); } if (selectedFilesArray.length === 0) { showError('Por favor, selecione pelo menos um arquivo para gerar o relatório.'); return; } try { elements.generateReport.disabled = true; elements.generateReport.innerHTML = '<i data-feather="loader" class="w-5 h-5 inline mr-2 animate-spin"></i> Gerando...'; // Process selected files const fileContents = []; for (const filePath of selectedFilesArray) { const fileObj = state.files.find(f => f.name === filePath); if (fileObj) { try { const content = await readFileAsText(fileObj.file); fileContents.push({ path: filePath, content: content }); // Store file content in state state.fileContents.set(filePath, content); } catch (e) { console.warn(Não foi possível ler o arquivo ${filePath}:, e); } } } if (fileContents.length === 0) { showError('Não foi possível ler nenhum arquivo. Verifique se os arquivos são de texto.'); return; } // Generate structured text output for AI analysis let output = '=== PROJETO ESTRUTURADO PARA ANÁLISE ===\n\n'; output += Total de arquivos: ${fileContents.length}\n; output += Data de geração: ${new Date().toLocaleString('pt-BR')}\n\n; output += '--- CONTEÚDO DOS ARQUIVOS ---\n\n'; for (const fileContent of fileContents) { output += Arquivo: ${fileContent.path}\n; output += "${fileContent.content}"\n\n; } state.processedText = output; elements.reportOutput.value = output; elements.reportContainer.classList.remove('hidden'); showNotification(Relatório gerado com ${fileContents.length} arquivos!, 'success'); } catch (error) { console.error('Erro detalhado:', error); showError('Erro ao gerar relatório: ' + error.message); } finally { elements.generateReport.disabled = false; elements.generateReport.innerHTML = '<i data-feather="file-text" class="w-5 h-5 inline mr-2"></i> Gerar Relatório Estruturado'; } } // Copy report to clipboard function copyReportToClipboard() { elements.reportOutput.select(); document.execCommand('copy'); showNotification('Relatório copiado para a área de transferência!', 'success'); } // Download report as file function downloadReportFile() { const blob = new Blob([state.processedText], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'project-report.txt'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification('Relatório baixado com sucesso!', 'success'); } // Read file as text function readFileAsText(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = () => reject(reader.error); reader.readAsText(file, 'UTF-8'); }); } // Voice input function startVoiceInput(inputElement) { if (!('webkitSpeechRecognition' in window)) { showError('Reconhecimento de voz não suportado neste navegador.'); return; } const recognition = new webkitSpeechRecognition(); recognition.lang = 'pt-BR'; recognition.interimResults = false; recognition.maxAlternatives = 1; recognition.start(); showNotification('Ouvindo... Fale agora.', 'info'); recognition.onresult = (event) => { const transcript = event.results[0][0].transcript; inputElement.value = transcript; showNotification('Texto reconhecido com sucesso!', 'success'); }; recognition.onerror = (event) => { showError('Erro no reconhecimento de voz: ' + event.error); }; } // Open Qwen in Hugging Face function openQwenInHuggingFace() { window.open('https://huggingface.co/Qwen/Qwen3-235B-A22B-Instruct-2507', '_blank'); showNotification('Abrindo Qwen no Hugging Face...', 'info'); } // Open Zai in Hugging Face function openZaiInHuggingFace() { window.open('https://huggingface.co/zai-org/GLM-4.6', '_blank'); showNotification('Abrindo Zai no Hugging Face...', 'info'); } // Show bookmarklet instructions function showBookmarkletInstructions() { elements.bookmarkletModal.classList.remove('hidden'); } // Send analysis to Zai tab function sendAnalysisToZai() { const analysis = elements.qwenAnalysis.textContent; if (!analysis) { showError('Nenhuma análise disponível para enviar.'); return; } // Switch to Zai tab document.querySelector('[data-tab="zai"]').click(); // Fill Zai input with analysis elements.zaiAnalysisInput.value = analysis; showNotification('Análise enviada para o Zai!', 'success'); } // Process Zai corrections function processZaiCorrections() { const corrections = elements.zaiCorrections.textContent; if (!corrections) { showError('Nenhuma correção disponível para processar.'); return; } try { // Parse corrected files from Zai response const correctedFiles = parseZAIFiles(corrections); if (correctedFiles.length === 0) { showError('Nenhum arquivo corrigido encontrado na resposta do Zai.'); return; } state.correctedFiles = correctedFiles; // Switch to results tab document.querySelector('[data-tab="results"]').click(); // Render results renderFinalResults(correctedFiles); showNotification(${correctedFiles.length} arquivo(s) corrigido(s) processado(s) com sucesso!, 'success'); } catch (error) { showError('Erro ao processar correções: ' + error.message); } } // Render final results function renderFinalResults(correctedFiles) { elements.resultsSummary.classList.remove('hidden'); elements.summaryText.textContent = ${correctedFiles.length} arquivo(s) foram corrigidos com sucesso pela IA colaborativa Qwen + Zai.; elements.correctedFilesList.innerHTML = ''; correctedFiles.forEach((file, index) => { const fileElement = document.createElement('div'); fileElement.className = 'flex justify-between items-center bg-gray-50 p-4 rounded-lg border border-gray-200'; fileElement.innerHTML = <div class="flex-1"> <div class="flex items-center"> <i data-feather="file-text" class="w-5 h-5 text-green-600 mr-3"></i> <span class="font-medium text-gray-800">${file.path}</span> <span class="ml-2 px-2 py-1 text-xs bg-green-100 text-green-800 rounded-full">Corrigido</span> </div> <p class="text-sm text-gray-600 mt-1">${file.content.length} caracteres</p> </div> <button class="download-corrected-file bg-primary text-white px-4 py-2 rounded-lg hover:bg-indigo-600 transition-colors" data-index="${index}"> <i data-feather="download" class="w-4 h-4"></i> </button> ; elements.correctedFilesList.appendChild(fileElement); }); // Add event listeners to download buttons document.querySelectorAll('.download-corrected-file').forEach(btn => { btn.addEventListener('click', (e) => { const index = parseInt(e.currentTarget.dataset.index); const file = correctedFiles[index]; downloadCorrectedFile(file); }); }); feather.replace(); } // Real AI Analysis using OpenAI API (or similar) async function simulateQwenAnalysis(problem, projectText) { updateProgress('qwen', 10, 'Connecting to AI service...'); try { // Using a public AI API for demonstration const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_API_KEY' // Replace with actual API key }, body: JSON.stringify({ model: 'gpt-3.5-turbo', messages: [ { role: 'system', content: 'You are an expert code analyzer. Analyze the provided code and identify issues related to the user\'s problem. List specific files and line numbers with issues.' }, { role: 'user', content: Problem: ${problem}\n\nProject Code:\n${projectText.substring(0, 15000)}... (truncated for API limit) } ], max_tokens: 1000, temperature: 0.1 }) }); if (!response.ok) { throw new Error('AI service unavailable'); } const data = await response.json(); const analysisResult = data.choices[0].message.content; updateProgress('qwen', 80, 'Analyzing findings...'); // Parse the analysis to extract files that need fixing const filesNeedingFix = extractFilesFromAnalysis(analysisResult); state.filesNeedingFix = filesNeedingFix; updateProgress('qwen', 100, 'Analysis complete'); return 🔍 QWEN AI ANALYSIS COMPLETE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 📋 PROBLEM IDENTIFIED: ${problem} 📁 FILES REQUIRING ATTENTION: ${filesNeedingFix.map(f => • ${f.path}: ${f.issue}).join('\n')} 📝 DETAILED ANALYSIS: ${analysisResult} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ; } catch (error) { console.error('AI Analysis failed:', error); // Fallback to simulated analysis if API fails updateProgress('qwen', 50, 'Using offline analysis...'); const files = Array.from(state.fileContents.keys()); const issues = analyzeProjectStructure(files, state.fileContents, problem); state.filesNeedingFix = issues; updateProgress('qwen', 100, 'Offline analysis complete'); return 🔍 QWEN AI ANALYSIS COMPLETE (Offline Mode) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 📋 PROBLEM IDENTIFIED: ${problem} 📁 FILES REQUIRING ATTENTION: ${issues.map(f => • ${f.path}: ${f.issue}).join('\n')} 📝 ANALYSIS SUMMARY: Found ${issues.length} potential issues in the codebase that match the reported problem. ; } } // Real AI Correction using uploaded files async function simulateZAICorrection(analysisResult) { updateProgress('zai', 10, 'Preparing corrections...'); try { const correctedFiles = []; // Process each file that needs fixing for (const fileData of state.filesNeedingFix) { updateProgress('zai', 30 + (correctedFiles.length * 20), Fixing ${fileData.path}...); const originalContent = state.fileContents.get(fileData.path); if (!originalContent) continue; // Use AI to fix the specific file const correctedContent = await fixFileWithAI(fileData, originalContent, analysisResult); correctedFiles.push({ path: fileData.path, content: correctedContent, originalContent: originalContent }); } updateProgress('zai', 90, 'Finalizing corrections...'); // Generate the ZAI response format let zaiResponse = 🛠️ ZAI AI CORRECTIONS APPLIED ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ✅ Successfully corrected ${correctedFiles.length} file(s) ; correctedFiles.forEach(file => { zaiResponse += ${file.path} "${file.content}" ; }); updateProgress('zai', 100, 'All corrections complete'); return zaiResponse; } catch (error) { console.error('AI Correction failed:', error); throw new Error('Failed to apply AI corrections'); } } // Analyze project structure for common issues function analyzeProjectStructure(files, fileContents, problem) { const issues = []; files.forEach(filePath => { const content = fileContents.get(filePath); if (!content) return; // Check for common issues based on file type and problem description if (filePath.endsWith('.js') || filePath.endsWith('.ts')) { // JavaScript/TypeScript analysis if (content.includes('undefined') || problem.toLowerCase().includes('undefined')) { issues.push({ path: filePath, issue: 'Potential undefined variable references', severity: 'high' }); } if (!content.includes('try') && problem.toLowerCase().includes('error')) { issues.push({ path: filePath, issue: 'Missing error handling', severity: 'medium' }); } if (content.includes('async') && !content.includes('catch') && problem.toLowerCase().includes('async')) { issues.push({ path: filePath, issue: 'Async function without error handling', severity: 'high' }); } } if (filePath.endsWith('.json')) { try { JSON.parse(content); } catch (e) { issues.push({ path: filePath, issue: 'Invalid JSON syntax', severity: 'high' }); } } // Add more analysis rules based on problem keywords if (problem.toLowerCase().includes('import') || problem.toLowerCase().includes('module')) { if (filePath.endsWith('.js') && content.includes('require') && !content.includes('module.exports')) { issues.push({ path: filePath, issue: 'CommonJS import without proper export', severity: 'medium' }); } } }); return issues.slice(0, 5); // Limit to 5 files for demo } // Fix individual file with AI async function fixFileWithAI(fileData, originalContent, analysisResult) { try { const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_API_KEY' // Replace with actual API key }, body: JSON.stringify({ model: 'gpt-3.5-turbo', messages: [ { role: 'system', content: 'You are an expert code fixer. Fix the identified issues in the code while preserving functionality. Return only the corrected code.' }, { role: 'user', content: File: ${fileData.path}\nIssue: ${fileData.issue}\n\nCode to fix:\n${originalContent} } ], max_tokens: 2000, temperature: 0.1 }) }); if (!response.ok) { throw new Error('AI service unavailable'); } const data = await response.json(); return data.choices[0].message.content; } catch (error) { console.error('AI Fix failed:', error); // Fallback: apply basic fixes return applyBasicFixes(originalContent, fileData.issue); } } // Apply basic fixes as fallback function applyBasicFixes(content, issue) { let fixedContent = content; if (issue.includes('undefined')) { // Basic undefined fix fixedContent = content.replace(/(\w+)\s*=\s*undefined/g, '$1 = null'); } if (issue.includes('error handling')) { // Basic try-catch wrapper if (content.includes('async') && !content.includes('try')) { fixedContent = content.replace( /(async\s+\w+\s*\([^)]*\)\s*=>\s*{)/, 'try {$1' ); fixedContent = fixedContent.replace( /(})\s*$/, '} catch (error) { console.error("Error:", error); }' ); } } return fixedContent; } // Extract files from AI analysis function extractFilesFromAnalysis(analysisText) { const files = []; const lines = analysisText.split('\n'); lines.forEach(line => { // Try to extract file paths from the analysis const match = line.match(/([\w\/\-\.]+\.(js|ts|jsx|tsx|json|html|css))\s*[:\-]/i); if (match) { files.push({ path: match[1], issue: line.substring(match.index + match[0].length).trim() }); } }); // If no files found, create based on actual uploaded files if (files.length === 0) { const uploadedFiles = Array.from(state.fileContents.keys()).slice(0, 3); uploadedFiles.forEach(path => { files.push({ path: path, issue: 'Potential issues detected' }); }); } return files.slice(0, 5); } // Parse ZAI response into file objects function parseZAIFiles(response) { const files = []; const fileRegex = /([^\n]+)\n"([^]+?)"(?:\n\n|$)/g; let match; while ((match = fileRegex.exec(response)) !== null) { files.push({ path: match[1], content: match[2] }); } // Store corrected files in state state.correctedFiles = files; return files; } // Apply changes to the original project function applyChangesToProject() { if (!state.correctedFiles || state.correctedFiles.length === 0) { showError('No corrected files to apply'); return; } // Store original contents for comparison const changesMade = []; // Apply corrected content to the file contents map state.correctedFiles.forEach(file => { const originalContent = state.fileContents.get(file.path); state.fileContents.set(file.path, file.content); // Track changes changesMade.push({ path: file.path, originalLength: originalContent ? originalContent.length : 0, newLength: file.content.length, diff: file.content.length - (originalContent ? originalContent.length : 0) }); }); // Update UI to show changes applied elements.appliedStatus.classList.remove('hidden'); elements.appliedSummary.textContent =${state.correctedFiles.length} file(s) updated successfully; elements.viewChanges.classList.remove('hidden'); // Update file list to show applied status document.querySelectorAll('#file-list .flex').forEach((item, index) => { if (index < state.correctedFiles.length) { const statusBadge = document.createElement('span'); statusBadge.className = 'ml-2 px-2 py-1 text-xs bg-green-100 text-green-800 rounded-full'; statusBadge.textContent = 'Applied'; item.appendChild(statusBadge); // Add checkmark icon const icon = document.createElement('i'); icon.setAttribute('data-feather', 'check'); icon.className = 'w-4 h-4 text-green-600 ml-2'; item.appendChild(icon); } }); feather.replace(); // Store changes for modal view state.changesMade = changesMade; showNotification(AI corrections applied to ${state.correctedFiles.length} file(s)!, 'success'); // Scroll to applied status elements.appliedStatus.scrollIntoView({ behavior: 'smooth', block: 'center' }); } // Render results function renderResults(files) { elements.fileList.innerHTML = ''; files.forEach(file => { const fileElement = document.createElement('div'); fileElement.className = 'flex justify-between items-center bg-gray-50 p-3 rounded-lg'; fileElement.innerHTML = <div> <span class="font-medium text-gray-800">${file.path}</span> <span class="text-gray-500 text-sm ml-2">(${file.content.length} chars)</span> </div> <button class="download-file bg-gray-200 hover:bg-gray-300 rounded-lg p-2" data-path="${file.path}" data-content="${encodeURIComponent(file.content)}"> <i data-feather="download" class="w-4 h-4 text-gray-700"></i> </button> ; elements.fileList.appendChild(fileElement); }); // Add event listeners to download buttons document.querySelectorAll('.download-file').forEach(btn => { btn.addEventListener('click', (e) => { const path = e.currentTarget.dataset.path; const content = decodeURIComponent(e.currentTarget.dataset.content); downloadFile(path, content); }); }); feather.replace(); elements.resultsContainer.classList.remove('hidden'); } // Update progress bar function updateProgress(ai, progress, status) { if (ai === 'qwen') { elements.qwenProgress.style.width = ${progress}%; elements.qwenStatus.textContent = status; } else if (ai === 'zai') { elements.zaiProgress.style.width = ${progress}%; elements.zaiStatus.textContent = status; } } // Download a single file function downloadFile(path, content) { const blob = new Blob([content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = path.split('/').pop(); document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // Download all files async function downloadAllFiles() { if (state.correctedFiles && state.correctedFiles.length > 0) { // Download only the corrected files state.correctedFiles.forEach(file => { downloadFile(file.path, file.content); }); showNotification(Downloaded ${state.correctedFiles.length} AI-corrected file(s), 'success'); } else { showError('No corrected files available for download'); } } // Show changes summary modal function showChangesSummary() { if (!state.changesMade || state.changesMade.length === 0) { showError('No changes to display'); return; } elements.changesContent.innerHTML = ''; state.changesMade.forEach(change => { const changeElement = document.createElement('div'); changeElement.className = 'border rounded-lg p-4 bg-gray-50'; const diffClass = change.diff > 0 ? 'text-green-600' : change.diff < 0 ? 'text-red-600' : 'text-gray-600'; const diffSymbol = change.diff > 0 ? '+' : change.diff < 0 ? '-' : ''; changeElement.innerHTML = <div class="flex justify-between items-start mb-2"> <h4 class="font-semibold text-gray-800">${change.path}</h4> <span class="${diffClass} font-medium">${diffSymbol}${change.diff} chars</span> </div> <div class="text-sm text-gray-600"> <p>Original: ${change.originalLength} characters</p> <p>New: ${change.newLength} characters</p> <p class="mt-2 text-xs text-gray-500"> ${change.diff > 0 ? 'Content added' : change.diff < 0 ? 'Content removed' : 'No size change'} </p> </div> ; elements.changesContent.appendChild(changeElement); }); elements.changesModal.classList.remove('hidden'); } // Hide changes summary modal function hideChangesSummary() { elements.changesModal.classList.add('hidden'); } // Show notification function showNotification(message, type = 'info') { if (type === 'error') { elements.errorMessage.textContent = message; elements.errorContainer.classList.remove('hidden'); setTimeout(() => { elements.errorContainer.classList.add('hidden'); }, 5000); } else if (type === 'success') { elements.successMessage.textContent = message; elements.successContainer.classList.remove('hidden'); setTimeout(() => { elements.successContainer.classList.add('hidden'); }, 5000); } else { // Info notification - create temporary element const notification = document.createElement('div'); notification.className = 'fixed top-4 right-4 px-6 py-4 rounded-lg shadow-lg text-white z-50 flex items-center bg-blue-500'; notification.innerHTML = <i data-feather="info" class="w-5 h-5 mr-3"></i> <span>${message}</span> ; document.body.appendChild(notification); feather.replace(); setTimeout(() => { notification.remove(); }, 3000); } } // Show error function showError(message) { showNotification(message, 'error'); } // Download corrected file function downloadCorrectedFile(file) { const blob = new Blob([file.content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = file.path.split('/').pop(); document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification(Arquivo ${file.path} baixado!, 'success'); } // Download all corrected files function downloadAllCorrectedFiles() { state.correctedFiles.forEach(file => { downloadCorrectedFile(file); }); } // Generate Git patch function generateGitPatch() { let patch = ''; state.correctedFiles.forEach(file => { const originalContent = state.fileContents.get(file.path) || ''; patch += --- a/${file.path}\n; patch += +++ b/${file.path}\n; patch += @@ -1,1 +1,1 @@\n; // Simple diff - in real implementation, use a proper diff library const originalLines = originalContent.split('\n'); const newLines = file.content.split('\n'); // Remove lines originalLines.forEach(line => { if (!newLines.includes(line)) { patch += -${line}\n; } }); // Add lines newLines.forEach(line => { if (!originalLines.includes(line)) { patch += +${line}\n; } }); patch += '\n'; }); const blob = new Blob([patch], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'corrections.patch'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification('Patch Git gerado com sucesso!', 'success'); } // Reset application function resetApplication() { if (confirm('Tem certeza que deseja iniciar um novo projeto? Todos os dados não salvos serão perdidos.')) { state.files = []; state.fileContents.clear(); state.selectedFiles.clear(); state.processedText = ''; state.qwenResponse = ''; state.zaiResponse = ''; state.correctedFiles = []; state.filesNeedingFix = []; state.fileTreeStructure = null; // Reset UI elements.fileTreeContainer.classList.add('hidden'); elements.reportContainer.classList.add('hidden'); elements.resultsSummary.classList.add('hidden'); elements.qwenAnalysis.textContent = ''; elements.zaiCorrections.textContent = ''; // Reset progress bars updateProgress('qwen', 0, 'Aguardando'); updateProgress('zai', 0, 'Aguardando'); // Switch to upload tab document.querySelector('[data-tab="upload"]').click(); showNotification('Aplicação reiniciada!', 'success'); } } // Show analysis details function showAnalysisDetails(analysisText) { elements.qwenAnalysis.innerHTML = <div class="whitespace-pre-wrap">${analysisText}</div> `; elements.analysisOutput.classList.remove('hidden'); } " eubottura-codefix-ai/style.css " /* Base styles / body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } / Tab styles / .tab-btn.active { border-bottom-color: #6366f1 !important; color: #6366f1 !important; } / File tree styles / .tree-item { display: flex; align-items: center; padding: 6px 0; cursor: pointer; border-radius: 4px; margin: 2px 0; } .tree-item:hover { background-color: #f3f4f6; } .tree-item .feather { width: 16px; height: 16px; margin-right: 8px; } .tree-folder { font-weight: 600; } .tree-children { margin-top: 4px; } / Checkbox styles / input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; accent-color: #6366f1; } / Code styles / textarea { font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 13px; line-height: 1.5; resize: vertical; } / Progress bar animation / .transition-all { transition: all 0.3s ease; } / Button hover states / button:disabled { opacity: 0.5; cursor: not-allowed; } button:not(:disabled):hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } / Animations / @keyframes slideIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes scaleIn { from { transform: scale(0.9); opacity: 0; } to { transform: scale(1); opacity: 1; } } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .animate-spin { animation: spin 1s linear infinite; } / Notification styles / .fixed.bottom-4.right-4 { animation: slideIn 0.3s ease-out; } / Modal styles / #bookmarklet-modal { animation: fadeIn 0.3s ease-out; } #bookmarklet-modal > div { animation: scaleIn 0.3s ease-out; } / File list styles / #corrected-files-list .flex { transition: all 0.3s ease; } #corrected-files-list .flex:hover { background-color: #f9fafb; transform: translateX(4px); } / Responsive adjustments / @media (max-width: 1024px) { .grid { grid-template-columns: 1fr; } } @media (max-width: 768px) { .tab-btn { font-size: 14px; padding: 12px 16px; } .flex.gap-2 { flex-direction: column; } .flex.gap-2 button { width: 100%; } } / Custom scrollbar / .overflow-y-auto::-webkit-scrollbar { width: 8px; } .overflow-y-auto::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } .overflow-y-auto::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 4px; } .overflow-y-auto::-webkit-scrollbar-thumb:hover { background: #a8a8a8; } / Drag and drop styles / #drop-zone.drag-over { border-color: #6366f1; background-color: #f0f9ff; } / Loading states / .loading { position: relative; overflow: hidden; } .loading::after { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); animation: loading 1.5s infinite; } @keyframes loading { to { left: 100%; } } / Success/error states / .success-state { border-color: #10b981 !important; background-color: #f0fdf4 !important; } .error-state { border-color: #ef4444 !important; background-color: #fef2f2 !important; } / File type indicators */ .file-type-js { color: #f7df1e; } .file-type-py { color: #3776ab; } .file-type-html { color: #e34c26; } .file-type-css { color: #1572b6; } .file-type-json { color: #000; } .file-type-md { color: #083fa1; } ""
f625374
verified

eubottura commited on