// 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 `

${aiIcon} ${this.aiType} Assistant

Ferramenta de automação CodeFlow AI

${this.aiType === 'Qwen' ? ` ` : ''}
`; } 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;