<div class="uncloseai" data-features="tts,translate,read"></div>
-<div class="uncloseai" data-features="translate"></div>
+<div class="uncloseai" data-features="translate"></div>
+<div class="uncloseai" data-features="smart-translate"></div>
- Available Features: chat, tts, translate, upload, read, full
+ Available Features: chat, tts, translate, smart-translate, upload, read, full
What You Get
diff --git a/src/translation.js b/src/translation.js
index 5348293..56524a1 100644
--- a/src/translation.js
+++ b/src/translation.js
@@ -4,7 +4,7 @@ import { API_KEY } from "./config.js";
import { getSelectedModel, getSelectedModelEndpoint } from "./models.js";
import { countTokens } from "./token_estimator.js";
-// Languages supported by Hermes 3 model (from Russell's implementation)
+// Languages supported by Hermes 3 model (in English for reference)
export const SUPPORTED_LANGUAGES = {
en: "English",
zh: "Chinese (Simplified)",
@@ -27,6 +27,29 @@ export const SUPPORTED_LANGUAGES = {
ko: "Korean",
};
+// Native language names with proper scripts, accents, and authentic forms
+export const NATIVE_LANGUAGE_NAMES = {
+ en: "English",
+ zh: "中文(简体)",
+ hi: "हिंदी",
+ es: "Español",
+ fr: "Français",
+ ar: "العَرَبِيَّة",
+ bn: "বাংলা",
+ ru: "Русский",
+ pt: "Português",
+ ur: "اُردُو",
+ id: "Bahasa Indonesia",
+ de: "Deutsch",
+ ja: "日本語",
+ sw: "Kiswahili",
+ mr: "मराठी",
+ te: "తెలుగు",
+ tr: "Türkçe",
+ "zh-tw": "中文(繁體)",
+ ko: "한국어",
+};
+
// Send message with custom history (for translation with minimal system prompt)
async function* sendMessageWithHistory(messageHistory) {
const apiUrl = `${getSelectedModelEndpoint()}/chat/completions`;
diff --git a/src/ui.js b/src/ui.js
index 6f0d5d7..2fb0097 100644
--- a/src/ui.js
+++ b/src/ui.js
@@ -460,6 +460,9 @@ export function createCustomInterface(container, features) {
case "translate":
createTranslateFeature(div);
break;
+ case "smart-translate":
+ createSmartTranslateFeature(div);
+ break;
case "upload":
createUploadFeature(div);
break;
@@ -509,12 +512,21 @@ export function createUploadFeature(container) {
export function createTranslateFeature(container) {
const translateDiv = document.createElement("div");
translateDiv.innerHTML = `
- Translation
- 🌐 Translate
+ Translation Modal
+ 🌐 Translation Modal
`;
container.appendChild(translateDiv);
}
+export function createSmartTranslateFeature(container) {
+ const smartTranslateDiv = document.createElement("div");
+ smartTranslateDiv.innerHTML = `
+ Smart Translate
+ 🤖 Smart Translate
+ `;
+ container.appendChild(smartTranslateDiv);
+}
+
export function createReadFeature(container) {
const readDiv = document.createElement("div");
readDiv.innerHTML = `
@@ -611,6 +623,156 @@ export async function handleUploadFromElement(button) {
}
}
+export async function handleSmartTranslate(button) {
+ // Check if dropdown already exists
+ const existingDropdown = button.parentElement.querySelector('.translate-dropdown');
+ if (existingDropdown) {
+ existingDropdown.remove();
+ return;
+ }
+
+ // Detect page language
+ button.textContent = "🔍 Detecting...";
+ button.disabled = true;
+
+ try {
+ const currentLang = await detectPageLanguage();
+ button.textContent = "🤖 Smart Translate";
+ button.disabled = false;
+
+ // Import supported languages and native names
+ const { SUPPORTED_LANGUAGES, NATIVE_LANGUAGE_NAMES, translateCurrentPage } = await import('./translation.js');
+ const colors = getThemeColors();
+
+ // Create dropdown
+ const dropdown = document.createElement('div');
+ dropdown.className = 'translate-dropdown';
+ dropdown.style.cssText = `
+ position: absolute;
+ top: 100%;
+ left: 0;
+ background: ${colors.panelBackground};
+ border: 1px solid ${colors.borderColor};
+ border-radius: 8px;
+ box-shadow: 0 4px 12px ${colors.shadowColor};
+ z-index: 1000;
+ min-width: 200px;
+ max-height: 300px;
+ overflow-y: auto;
+ margin-top: 4px;
+ `;
+
+ // Add current language indicator
+ const currentLangDiv = document.createElement('div');
+ currentLangDiv.style.cssText = `
+ padding: 8px 12px;
+ font-size: 12px;
+ color: ${colors.mutedText};
+ border-bottom: 1px solid ${colors.dividerColor};
+ background: ${colors.contentBackground};
+ `;
+ const currentLangName = NATIVE_LANGUAGE_NAMES[currentLang] || SUPPORTED_LANGUAGES[currentLang] || 'Unknown';
+ currentLangDiv.textContent = `Current page: ${currentLangName}`;
+ dropdown.appendChild(currentLangDiv);
+
+ // Add translation options with code, English, and native names
+ Object.entries(NATIVE_LANGUAGE_NAMES).forEach(([code, nativeName]) => {
+ // Skip current language
+ if (code === currentLang) return;
+
+ const englishName = SUPPORTED_LANGUAGES[code];
+
+ const option = document.createElement('div');
+ option.style.cssText = `
+ padding: 8px 12px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+ color: ${colors.primaryText};
+ font-size: 14px;
+ line-height: 1.4;
+ `;
+
+ // Format: "es • Spanish • Español"
+ option.innerHTML = `
+
+ ${code}
+ •
+ ${englishName}
+ •
+ ${nativeName}
+
+ `;
+
+ option.onmouseenter = () => {
+ option.style.background = colors.buttonHover;
+ };
+ option.onmouseleave = () => {
+ option.style.background = 'transparent';
+ };
+
+ option.onclick = async () => {
+ try {
+ // Show loading
+ option.innerHTML = `
+
+ ${code}
+ •
+ Translating...
+
+ `;
+ option.style.pointerEvents = 'none';
+
+ // Translate the page
+ const translatedHtml = await translateCurrentPage(code);
+
+ // Open in new tab
+ const newWindow = window.open('', '_blank');
+ newWindow.document.write(translatedHtml);
+ newWindow.document.close();
+ newWindow.document.title = `${document.title} (${name})`;
+
+ // Close dropdown
+ dropdown.remove();
+
+ } catch (error) {
+ alert(`Translation failed: ${error.message}`);
+ // Restore original format
+ option.innerHTML = `
+
+ ${code}
+ •
+ ${englishName}
+ •
+ ${nativeName}
+
+ `;
+ option.style.pointerEvents = 'auto';
+ }
+ };
+
+ dropdown.appendChild(option);
+ });
+
+ // Position dropdown relative to button
+ button.style.position = 'relative';
+ button.parentElement.appendChild(dropdown);
+
+ // Close dropdown when clicking outside
+ const closeDropdown = (e) => {
+ if (!dropdown.contains(e.target) && e.target !== button) {
+ dropdown.remove();
+ document.removeEventListener('click', closeDropdown);
+ }
+ };
+ setTimeout(() => document.addEventListener('click', closeDropdown), 100);
+
+ } catch (error) {
+ button.textContent = "🤖 Smart Translate";
+ button.disabled = false;
+ alert(`Language detection failed: ${error.message}`);
+ }
+}
+
// Initialize the legacy chat interface (for backward compatibility)
export function initializeChatInterface() {
// Only initialize if there are legacy elements (chat-container, user-input, etc.)
@@ -1406,7 +1568,8 @@ async function openUncloseaiEmbeddedModalNew() {
}
}},
{ text: "🔊 TTS Anything", action: (btn) => window.openTTSModal() },
- { text: "🌐 Translate", action: async (btn) => {
+ { text: "🌐 Translation Modal", action: (btn) => window.openTranslateModal() },
+ { text: "🤖 Smart Translate", action: async (btn) => {
// Create dropdown instead of opening modal
if (btn.nextElementSibling && btn.nextElementSibling.classList.contains('translate-dropdown')) {
// Toggle existing dropdown
@@ -1420,7 +1583,7 @@ async function openUncloseaiEmbeddedModalNew() {
try {
const currentLang = await detectPageLanguage();
- btn.textContent = "🌐 Translate";
+ btn.textContent = "🤖 Smart Translate";
btn.disabled = false;
// Import supported languages
@@ -1521,7 +1684,7 @@ async function openUncloseaiEmbeddedModalNew() {
setTimeout(() => document.addEventListener('click', closeDropdown), 100);
} catch (error) {
- btn.textContent = "🌐 Translate";
+ btn.textContent = "🤖 Smart Translate";
btn.disabled = false;
alert(`Language detection failed: ${error.message}`);
}
diff --git a/uncloseai.js b/uncloseai.js
index 4a6d044..dbe9ffc 100644
--- a/uncloseai.js
+++ b/uncloseai.js
@@ -113,6 +113,7 @@ window.showProgressIndicator = FileUpload.showProgressIndicator;
window.hideProgressIndicator = FileUpload.hideProgressIndicator;
window.handleTTSFromElement = UI.handleTTSFromElement;
window.handleUploadFromElement = UI.handleUploadFromElement;
+window.handleSmartTranslate = UI.handleSmartTranslate;
// Initialize on page load
window.addEventListener("load", () => {