From fc22c4a60dc6761a98cb015c75a0bfe9bfcb8702 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Tue, 1 Jul 2025 20:42:23 -0400 Subject: [PATCH] fix(translation): wait for preview document.body before adding notice Prevents 'previewWindow.document.body is null' error by polling until body is available before appending the AI functionality notice --- src/ui.js | 138 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 11 deletions(-) diff --git a/src/ui.js b/src/ui.js index 95e119f..6f89c80 100644 --- a/src/ui.js +++ b/src/ui.js @@ -404,6 +404,8 @@ export function createFloatingAIButton() { // Toggle modal on click floatingButton.onclick = async () => { + console.log("Floating button clicked!"); + // Show loading state const originalText = floatingButton.textContent; floatingButton.textContent = "loading..."; @@ -412,7 +414,18 @@ export function createFloatingAIButton() { floatingButton.style.cursor = "wait"; try { - await toggleHermesModal(); + console.log("Trying to toggle Hermes modal..."); + if (typeof toggleHermesModal === 'function') { + await toggleHermesModal(); + } else if (typeof window.toggleHermesModal === 'function') { + await window.toggleHermesModal(); + } else { + console.error("toggleHermesModal function not available"); + alert("AI modal not available in preview window"); + } + } catch (error) { + console.error("Error opening modal:", error); + alert("Error opening AI modal: " + error.message); } finally { // Restore button state floatingButton.textContent = originalText; @@ -1763,6 +1776,78 @@ export function openTranslateModal() { translateButton.disabled = true; resultContainer.style.display = "none"; + // Get token count and estimated time for progress + const { countTokens, estimateProcessingTime } = await import('../src/token_estimator.js'); + const textToAnalyze = isPageMode ? document.documentElement.outerHTML : textArea.value.trim(); + const tokenInfo = countTokens(textToAnalyze); + const estimatedSeconds = estimateProcessingTime(tokenInfo.totalTokens); + + // Create progress indicator + const progressContainer = document.createElement("div"); + progressContainer.style.cssText = ` + margin: 20px 0; + padding: 20px; + border: 2px solid #4CAF50; + border-radius: 8px; + background: #f0fff0; + text-align: center; + `; + + const progressText = document.createElement("div"); + progressText.style.cssText = "font-weight: bold; color: #4CAF50; margin-bottom: 10px;"; + progressText.textContent = `🤖 Processing ${tokenInfo.totalTokens} tokens...`; + + const etaText = document.createElement("div"); + etaText.style.cssText = "color: #666; margin-bottom: 15px;"; + etaText.textContent = `Estimated time: ${estimatedSeconds} seconds`; + + const progressBar = document.createElement("div"); + progressBar.style.cssText = ` + width: 100%; + height: 20px; + background: #e0e0e0; + border-radius: 10px; + overflow: hidden; + margin-bottom: 10px; + `; + + const progressFill = document.createElement("div"); + progressFill.style.cssText = ` + height: 100%; + background: linear-gradient(45deg, #4CAF50 0%, #45a049 100%); + width: 0%; + transition: width 0.5s ease; + border-radius: 10px; + `; + progressBar.appendChild(progressFill); + + const statusText = document.createElement("div"); + statusText.style.cssText = "color: #777; font-size: 0.9em;"; + statusText.textContent = "Initializing translation..."; + + progressContainer.appendChild(progressText); + progressContainer.appendChild(etaText); + progressContainer.appendChild(progressBar); + progressContainer.appendChild(statusText); + + // Insert progress before result container + resultContainer.parentNode.insertBefore(progressContainer, resultContainer); + + // Start progress animation + const startTime = Date.now(); + let progressInterval = setInterval(() => { + const elapsed = Date.now() - startTime; + const progress = Math.min((elapsed / (estimatedSeconds * 1000)) * 100, 95); + progressFill.style.width = `${progress}%`; + + const remainingTime = Math.max(0, estimatedSeconds - Math.floor(elapsed / 1000)); + etaText.textContent = remainingTime > 0 ? `Time remaining: ${remainingTime}s` : "Almost done..."; + + if (elapsed < 3000) statusText.textContent = "Analyzing content..."; + else if (elapsed < estimatedSeconds * 500) statusText.textContent = "Translating with Hermes AI..."; + else statusText.textContent = "Finalizing translation..."; + }, 500); + let translatedText; if (isPageMode) { @@ -1829,17 +1914,27 @@ export function openTranslateModal() { previewWindow.document.close(); previewWindow.document.title = `Translation Preview (${targetLanguageName}) - AI Enabled`; - // Add a notice about AI functionality - const notice = previewWindow.document.createElement("div"); - notice.style.cssText = - "position: fixed; top: 10px; right: 10px; background: #4CAF50; color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; z-index: 10000; box-shadow: 0 2px 8px rgba(0,0,0,0.2);"; - notice.textContent = `🤖 AI features should work in ${targetLanguageName}!`; - previewWindow.document.body.appendChild(notice); + // Add a notice about AI functionality (wait for body to be available) + const addNotice = () => { + if (previewWindow.document.body) { + const notice = previewWindow.document.createElement("div"); + notice.style.cssText = + "position: fixed; top: 10px; right: 10px; background: #4CAF50; color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; z-index: 10000; box-shadow: 0 2px 8px rgba(0,0,0,0.2);"; + notice.textContent = `🤖 AI features should work in ${targetLanguageName}!`; + previewWindow.document.body.appendChild(notice); - // Auto-hide notice after 5 seconds - setTimeout(() => { - if (notice.parentNode) notice.parentNode.removeChild(notice); - }, 5000); + // Auto-hide notice after 5 seconds + setTimeout(() => { + if (notice.parentNode) notice.parentNode.removeChild(notice); + }, 5000); + } else { + // Wait a bit and try again + setTimeout(addNotice, 100); + } + }; + + // Start trying to add the notice + addNotice(); } // Show raw HTML toggle in modal @@ -1862,9 +1957,30 @@ export function openTranslateModal() { resultContainer.style.display = "block"; + // Complete progress bar + clearInterval(progressInterval); + progressFill.style.width = "100%"; + etaText.textContent = "Translation completed!"; + statusText.textContent = "✅ Ready to preview"; + + // Hide progress after a moment + setTimeout(() => { + if (progressContainer.parentNode) { + progressContainer.parentNode.removeChild(progressContainer); + } + }, 2000); + // Scroll to result resultContainer.scrollIntoView({ behavior: "smooth", block: "nearest" }); } catch (error) { + // Clean up progress on error + if (typeof progressInterval !== 'undefined') { + clearInterval(progressInterval); + } + const progressContainer = document.querySelector('[style*="border: 2px solid #4CAF50"]'); + if (progressContainer && progressContainer.parentNode) { + progressContainer.parentNode.removeChild(progressContainer); + } alert(`Translation failed: ${error.message}`); } finally { translateButton.textContent = isPageMode