|
|
<!DOCTYPE html> |
|
|
<html lang="fr"> |
|
|
|
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" |
|
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"> |
|
|
|
|
|
|
|
|
<meta name="theme-color" content="#0a0a0b"> |
|
|
<meta name="apple-mobile-web-app-capable" content="yes"> |
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> |
|
|
<meta name="application-name" content="Babel"> |
|
|
<meta name="apple-mobile-web-app-title" content="Babel"> |
|
|
|
|
|
|
|
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> |
|
|
<meta http-equiv="Pragma" content="no-cache"> |
|
|
<meta http-equiv="Expires" content="0"> |
|
|
|
|
|
<title>Babel</title> |
|
|
<link |
|
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+Arabic:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" |
|
|
rel="stylesheet"> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<link rel="stylesheet" href="./enhanced.css"> |
|
|
<link rel="manifest" href="./manifest.json"> |
|
|
<style> |
|
|
|
|
|
</style> |
|
|
</head> |
|
|
|
|
|
<body> |
|
|
<div class="app-layout"> |
|
|
|
|
|
<aside class="grok-sidebar"> |
|
|
<div class="sidebar-top"> |
|
|
<div class="sidebar-logo"> |
|
|
<i class="fa-solid fa-cube"></i> |
|
|
</div> |
|
|
<button class="new-chat" onclick="location.reload()" title="New Session"> |
|
|
<i class="fa-solid fa-plus"></i> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<nav class="sidebar-menu"> |
|
|
<div class="menu-item active"> |
|
|
<i class="fa-solid fa-comment-dots"></i> |
|
|
<span>Chat</span> |
|
|
</div> |
|
|
<div class="menu-item" id="sidebar-voice-mode"> |
|
|
<i class="fa-solid fa-microphone-lines"></i> |
|
|
<span>Voice</span> |
|
|
</div> |
|
|
|
|
|
<div class="menu-item" id="settings-trigger"> |
|
|
<i class="fa-solid fa-sliders"></i> |
|
|
<span>Settings</span> |
|
|
</div> |
|
|
</nav> |
|
|
|
|
|
<div class="sidebar-footer"> |
|
|
<div class="user-profile"> |
|
|
<div class="avatar">U</div> |
|
|
<div class="info"> |
|
|
<span class="name">User</span> |
|
|
<span class="status">Pro</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</aside> |
|
|
|
|
|
|
|
|
<main class="grok-main"> |
|
|
|
|
|
<header class="main-header"> |
|
|
<span class="model-badge">Babel v2.0</span> |
|
|
</header> |
|
|
|
|
|
|
|
|
<div id="chat-stage" class="chat-stage"> |
|
|
|
|
|
<div class="empty-state" id="greeting"> |
|
|
<div class="grok-logo-hero">Babel</div> |
|
|
<p class="grok-hero-text">Intelligence Vocale Instantanee</p> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="result-display" style="display: none; padding: 20px; text-align: center;"> |
|
|
<div id="original-display" style="font-size: 1.1em; color: #aaa; margin-bottom: 10px;"></div> |
|
|
<div id="translation-display" style="font-size: 1.5em; color: #fff; font-weight: bold;"></div> |
|
|
</div> |
|
|
|
|
|
<div id="chat-history" class="chat-history"></div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="grok-input-wrapper"> |
|
|
|
|
|
|
|
|
<div class="grok-input-bar"> |
|
|
<div class="input-icon left"> |
|
|
<i class="fa-solid fa-paperclip"></i> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="input-status-area"> |
|
|
<span id="status-placeholder" class="status-text">Prêt à traduire...</span> |
|
|
<span id="latency-badge" |
|
|
style="display:none; font-size: 0.7em; color: #666; margin-left: 10px;">0ms</span> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="input-icon right action" id="record-btn"> |
|
|
<i class="fa-solid fa-microphone"></i> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="grok-chips"> |
|
|
|
|
|
|
|
|
<div class="chip-select-wrapper"> |
|
|
<select id="source-lang-quick" class="chip-select"> |
|
|
<option value="auto">🎯 Auto</option> |
|
|
<option value="ar-SA">🇲🇦 Darija</option> |
|
|
<option value="fr-FR">🇫🇷 Français</option> |
|
|
<option value="en-US">🇬🇧 English</option> |
|
|
<option value="es-ES">🇪🇸 Español</option> |
|
|
</select> |
|
|
<i class="fa-solid fa-chevron-down"></i> |
|
|
</div> |
|
|
|
|
|
|
|
|
<button id="swap-langs" class="chip-icon" title="Swap"> |
|
|
<i class="fa-solid fa-arrow-right-arrow-left"></i> |
|
|
</button> |
|
|
|
|
|
|
|
|
<div class="chip-select-wrapper"> |
|
|
<select id="target-lang-quick" class="chip-select"> |
|
|
<option value="en-US" selected>🇬🇧 English</option> |
|
|
<option value="fr-FR">🇫🇷 Français</option> |
|
|
<option value="ar-SA">🇲🇦 Darija</option> |
|
|
<option value="es-ES">🇪🇸 Español</option> |
|
|
<option value="de-DE">🇩🇪 Deutsch</option> |
|
|
<option value="it-IT">🇮🇹 Italiano</option> |
|
|
<option value="pt-PT">🇵🇹 Português</option> |
|
|
<option value="zh-CN">🇨🇳 中文</option> |
|
|
<option value="ja-JP">🇯🇵 日本語</option> |
|
|
<option value="ko-KR">🇰🇷 한국어</option> |
|
|
<option value="ru-RU">🇷🇺 Русский</option> |
|
|
<option value="tr-TR">🇹🇷 Türkçe</option> |
|
|
</select> |
|
|
<i class="fa-solid fa-chevron-down"></i> |
|
|
</div> |
|
|
|
|
|
<div class="chip-divider"></div> |
|
|
|
|
|
|
|
|
<button id="smart-mode-toggle" class="chip-pill active" |
|
|
title="Auto-detect language and smart target"> |
|
|
<i class="fa-solid fa-brain"></i> Smart |
|
|
</button> |
|
|
|
|
|
|
|
|
<button id="voice-clone-toggle" class="chip-pill active" title="Clone voice from input audio"> |
|
|
<i class="fa-solid fa-user-secret"></i> Clone Voice |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</main> |
|
|
|
|
|
|
|
|
<audio id="audio-player" style="display: none;"></audio> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="modal" id="settings-modal"> |
|
|
<button id="close-settings" class="close-modal-btn"> |
|
|
<i class="fa-solid fa-xmark"></i> |
|
|
</button> |
|
|
<div class="modal-content"> |
|
|
<h2>Parametres</h2> |
|
|
<div class="form-group"> |
|
|
<label>Votre Langue</label> |
|
|
<select id="source-lang-selector" class="form-control" style="height: auto; max-height: 200px;"> |
|
|
<option value="auto">Detection Automatique</option> |
|
|
<optgroup label="Langues Principales"> |
|
|
<option value="fr">Français</option> |
|
|
<option value="ar">Darija / Arabe</option> |
|
|
<option value="en">Anglais</option> |
|
|
<option value="es">Espagnol</option> |
|
|
<option value="de">Allemand</option> |
|
|
<option value="it">Italien</option> |
|
|
<option value="zh">Chinois</option> |
|
|
<option value="ja">Japonais</option> |
|
|
<option value="ru">Russe</option> |
|
|
</optgroup> |
|
|
<optgroup label="✨ Toutes les Langues"> |
|
|
<option value="af">Afrikaans</option> |
|
|
<option value="sq">Albanian (Albanais)</option> |
|
|
<option value="am">Amharic (Amharique)</option> |
|
|
<option value="hy">Armenian (Arménien)</option> |
|
|
<option value="az">Azerbaijani (Azéri)</option> |
|
|
<option value="eu">Basque</option> |
|
|
<option value="be">Belarusian (Biélorusse)</option> |
|
|
<option value="bn">Bengali</option> |
|
|
<option value="bs">Bosnian (Bosnien)</option> |
|
|
<option value="bg">Bulgarian (Bulgare)</option> |
|
|
<option value="ca">Catalan</option> |
|
|
<option value="ceb">Cebuano</option> |
|
|
<option value="co">Corsican (Corse)</option> |
|
|
<option value="hr">Croatian (Croate)</option> |
|
|
<option value="cs">Czech (Tchèque)</option> |
|
|
<option value="da">Danish (Danois)</option> |
|
|
<option value="nl">Dutch (Néerlandais)</option> |
|
|
<option value="et">Estonian (Estonien)</option> |
|
|
<option value="fi">Finnish (Finnois)</option> |
|
|
<option value="gl">Galician (Galicien)</option> |
|
|
<option value="ka">Georgian (Géorgien)</option> |
|
|
<option value="el">Greek (Grec)</option> |
|
|
<option value="gu">Gujarati</option> |
|
|
<option value="ht">Haitian Creole (Créole Haïtien)</option> |
|
|
<option value="ha">Hausa</option> |
|
|
<option value="haw">Hawaiian (Hawaïen)</option> |
|
|
<option value="he">Hebrew (Hébreu)</option> |
|
|
<option value="hi">Hindi</option> |
|
|
<option value="hmn">Hmong</option> |
|
|
<option value="hu">Hungarian (Hongrois)</option> |
|
|
<option value="is">Icelandic (Islandais)</option> |
|
|
<option value="ig">Igbo</option> |
|
|
<option value="id">Indonesian (Indonésien)</option> |
|
|
<option value="ga">Irish (Irlandais)</option> |
|
|
<option value="jw">Javanese (Javanais)</option> |
|
|
<option value="kn">Kannada</option> |
|
|
<option value="kk">Kazakh</option> |
|
|
<option value="km">Khmer</option> |
|
|
<option value="ko">Korean (Coréen)</option> |
|
|
<option value="ku">Kurdish (Kurde)</option> |
|
|
<option value="ky">Kyrgyz (Kirghize)</option> |
|
|
<option value="lo">Lao</option> |
|
|
<option value="la">Latin</option> |
|
|
<option value="lv">Latvian (Letton)</option> |
|
|
<option value="lt">Lithuanian (Lituanien)</option> |
|
|
<option value="lb">Luxembourgish (Luxembourgeois)</option> |
|
|
<option value="mk">Macedonian (Macédonien)</option> |
|
|
<option value="mg">Malagasy (Malgache)</option> |
|
|
<option value="ms">Malay (Malais)</option> |
|
|
<option value="ml">Malayalam</option> |
|
|
<option value="mt">Maltese (Maltais)</option> |
|
|
<option value="mi">Maori</option> |
|
|
<option value="mr">Marathi</option> |
|
|
<option value="mn">Mongolian (Mongol)</option> |
|
|
<option value="my">Myanmar (Birman)</option> |
|
|
<option value="ne">Nepali (Népalais)</option> |
|
|
<option value="no">Norwegian (Norvégien)</option> |
|
|
<option value="ny">Nyanja (Chichewa)</option> |
|
|
<option value="ps">Pashto (Pachto)</option> |
|
|
<option value="fa">Persian (Persan/Farsi)</option> |
|
|
<option value="pl">Polish (Polonais)</option> |
|
|
<option value="pa">Punjabi</option> |
|
|
<option value="ro">Romanian (Roumain)</option> |
|
|
<option value="sm">Samoan</option> |
|
|
<option value="gd">Scottish Gaelic</option> |
|
|
<option value="sr">Serbian (Serbe)</option> |
|
|
<option value="sn">Shona</option> |
|
|
<option value="sd">Sindhi</option> |
|
|
<option value="si">Sinhala (Cingalais)</option> |
|
|
<option value="sk">Slovak (Slovaque)</option> |
|
|
<option value="sl">Slovenian (Slovêne)</option> |
|
|
<option value="so">Somali</option> |
|
|
<option value="su">Sundanese (Soundanais)</option> |
|
|
<option value="sw">Swahili</option> |
|
|
<option value="sv">Swedish (Suédois)</option> |
|
|
<option value="tl">Tagalog (Tagalog/Filipino)</option> |
|
|
<option value="tg">Tajik (Tadjik)</option> |
|
|
<option value="ta">Tamil</option> |
|
|
<option value="te">Telugu</option> |
|
|
<option value="th">Thai (Thaï)</option> |
|
|
<option value="tr">Turkish (Turc)</option> |
|
|
<option value="uk">Ukrainian (Ukrainien)</option> |
|
|
<option value="ur">Urdu</option> |
|
|
<option value="uz">Uzbek (Ouzbek)</option> |
|
|
<option value="vi">Vietnamese (Vietnamien)</option> |
|
|
<option value="cy">Welsh (Gallois)</option> |
|
|
<option value="xh">Xhosa</option> |
|
|
<option value="yi">Yiddish</option> |
|
|
<option value="yo">Yoruba</option> |
|
|
<option value="zu">Zulu</option> |
|
|
<div class="form-group"> |
|
|
<label>Langue Cible</label> |
|
|
|
|
|
<select id="target-lang-quick" class="lang-quick-select"> |
|
|
<option value="French">‡«‡· French</option> |
|
|
<option value="Arabic">‡¸‡¦ Arabic</option> |
|
|
<option value="English">‡¬‡§ English</option> |
|
|
<option value="Darija">‡²‡¦ Darija</option> |
|
|
<option value="Spanish">‡ª‡¸ Spanish</option> |
|
|
|
|
|
<optgroup label="Ϭ African & Middle Eastern"> |
|
|
<option value="Amharic">‡ª‡¹ Amharic</option> |
|
|
<option value="Arabic">‡¸‡¦ Arabic (Standard)</option> |
|
|
<option value="Darija">‡²‡¦ Arabic (Moroccan Darija)</option> |
|
|
<option value="Berber">™“ Amazigh (Berber)</option> |
|
|
<option value="Egyptian">‡ª‡¬ Arabic (Egyptian)</option> |
|
|
<option value="Hausa">‡³‡¬ Hausa</option> |
|
|
<option value="Hebrew">‡®‡± Hebrew</option> |
|
|
<option value="Igbo">‡³‡¬ Igbo</option> |
|
|
<option value="Persian">‡®‡· Persian (Farsi)</option> |
|
|
<option value="Somali">‡¸‡´ Somali</option> |
|
|
<option value="Swahili">‡°‡ª Swahili</option> |
|
|
<option value="Turkish">‡¹‡· Turkish</option> |
|
|
<option value="Yoruba">‡³‡¬ Yoruba</option> |
|
|
<option value="Zulu">‡¿‡¦ Zulu</option> |
|
|
</optgroup> |
|
|
|
|
|
<optgroup label="✨ Asian & Pacific"> |
|
|
<option value="Bengali">‡§‡© Bengali</option> |
|
|
<option value="Chinese">‡¨‡³ Chinese (Mandarin)</option> |
|
|
<option value="Cantonese">‡‡° Chinese (Cantonese)</option> |
|
|
<option value="Filipino">‡µ‡ Filipino (Tagalog)</option> |
|
|
<option value="Gujarati">‡®‡³ Gujarati</option> |
|
|
<option value="Hindi">‡®‡³ Hindi</option> |
|
|
<option value="Indonesian">‡®‡© Indonesian</option> |
|
|
<option value="Japanese">‡¯‡µ Japanese</option> |
|
|
<option value="Javanese">‡®‡© Javanese</option> |
|
|
<option value="Kannada">‡®‡³ Kannada</option> |
|
|
<option value="Khmer">‡°‡ Khmer</option> |
|
|
<option value="Korean">‡°‡· Korean</option> |
|
|
<option value="Lao">‡±‡¦ Lao</option> |
|
|
<option value="Malay">‡²‡¾ Malay</option> |
|
|
<option value="Malayalam">‡®‡³ Malayalam</option> |
|
|
<option value="Marathi">‡®‡³ Marathi</option> |
|
|
<option value="Myanmar">‡²‡² Myanmar (Burmese)</option> |
|
|
<option value="Nepali">‡³‡µ Nepali</option> |
|
|
<option value="Punjabi">‡®‡³ Punjabi</option> |
|
|
<option value="Sinhala">‡±‡° Sinhala</option> |
|
|
<option value="Tamil">‡®‡³ Tamil</option> |
|
|
<option value="Telugu">‡®‡³ Telugu</option> |
|
|
<option value="Thai">‡¹‡ Thai</option> |
|
|
<option value="Urdu">‡µ‡° Urdu</option> |
|
|
<option value="Vietnamese">‡»‡³ Vietnamese</option> |
|
|
</optgroup> |
|
|
|
|
|
<optgroup label="✨ European"> |
|
|
<option value="Albanian">‡¦‡± Albanian</option> |
|
|
<option value="Armenian">‡¦‡² Armenian</option> |
|
|
<option value="Azerbaijani">‡¦‡¿ Azerbaijani</option> |
|
|
<option value="Bosnian">‡§‡¦ Bosnian</option> |
|
|
<option value="Bulgarian">‡§‡¬ Bulgarian</option> |
|
|
<option value="Catalan">‡ª‡¸ Catalan</option> |
|
|
<option value="Croatian">‡‡· Croatian</option> |
|
|
<option value="Czech">‡¨‡¿ Czech</option> |
|
|
<option value="Danish">‡©‡° Danish</option> |
|
|
<option value="Dutch">‡³‡± Dutch</option> |
|
|
<option value="Estonian">‡ª‡ª Estonian</option> |
|
|
<option value="Finnish">‡«‡® Finnish</option> |
|
|
<option value="French">‡«‡· French</option> |
|
|
<option value="Georgian">‡¬‡ª Georgian</option> |
|
|
<option value="German">‡©‡ª German</option> |
|
|
<option value="Greek">‡¬‡· Greek</option> |
|
|
<option value="Hungarian">‡‡º Hungarian</option> |
|
|
<option value="Icelandic">‡®‡¸ Icelandic</option> |
|
|
<option value="Irish">‡®‡ª Irish</option> |
|
|
<option value="Italian">‡®‡¹ Italian</option> |
|
|
<option value="Latvian">‡±‡» Latvian</option> |
|
|
<option value="Lithuanian">‡±‡¹ Lithuanian</option> |
|
|
<option value="Macedonian">‡²‡° Macedonian</option> |
|
|
<option value="Maltese">‡²‡¹ Maltese</option> |
|
|
<option value="Norwegian">‡³‡´ Norwegian</option> |
|
|
<option value="Polish">‡µ‡± Polish</option> |
|
|
<option value="Portuguese">‡µ‡¹ Portuguese</option> |
|
|
<option value="Romanian">‡·‡´ Romanian</option> |
|
|
<option value="Russian">‡·‡º Russian</option> |
|
|
<option value="Serbian">‡·‡¸ Serbian</option> |
|
|
<option value="Slovak">‡¸‡° Slovak</option> |
|
|
<option value="Slovenian">‡¸‡® Slovenian</option> |
|
|
<option value="Spanish">‡ª‡¸ Spanish</option> |
|
|
<option value="Swedish">‡¸‡ª Swedish</option> |
|
|
<option value="Ukrainian">‡º‡¦ Ukrainian</option> |
|
|
<option value="Welsh">´ó §ó ¢ó ·ó ¬ó ³ó ¿ Welsh</option> |
|
|
</optgroup> |
|
|
</select> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="form-group" style="margin-top: 16px;"> |
|
|
<label>Mode d'Intelligence (Cerveau)</label> |
|
|
<select id="ai-model-selector" class="form-control"> |
|
|
<option value="gpt-4o-mini">Ÿ¢ OpenAI GPT-4o (Stable & Illimité)</option> |
|
|
<option value="gemini-flash-latest">š¡ Google Gemini Flash (Experimental)</option> |
|
|
<option value="gemini-pro">’Ž Google Gemini Pro (Balanced)</option> |
|
|
</select> |
|
|
</div> |
|
|
|
|
|
|
|
|
<audio id="audio-player"></audio> |
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> |
|
|
<script src="/static/enhanced.js"></script> |
|
|
<script src="/static/script.js"></script> |
|
|
|
|
|
|
|
|
<script> |
|
|
|
|
|
function log(m) { console.log(m); } |
|
|
|
|
|
|
|
|
const modal = document.getElementById('settings-modal'); |
|
|
document.getElementById('settings-trigger').onclick = () => modal.classList.add('open'); |
|
|
document.getElementById('close-settings').onclick = () => modal.classList.remove('open'); |
|
|
document.getElementById('save-settings').onclick = () => { |
|
|
if (window.saveModalSettings) window.saveModalSettings(); |
|
|
modal.classList.remove('open'); |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const greeting = document.getElementById('greeting'); |
|
|
const status = document.getElementById('status-placeholder'); |
|
|
|
|
|
const observer = new MutationObserver((mutations) => { |
|
|
mutations.forEach(m => { |
|
|
if (m.target.id === 'original-text') { |
|
|
const txt = m.target.innerText; |
|
|
if (txt && txt !== '...') { |
|
|
status.innerText = txt.substring(0, 50) + (txt.length > 50 ? '...' : ''); |
|
|
greeting.style.display = 'none'; |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
observer.observe(document.getElementById('original-text'), { childList: true, characterData: true, subtree: true }); |
|
|
|
|
|
|
|
|
const smartModeToggle = document.getElementById('smart-mode-toggle'); |
|
|
const statusPlaceholder = document.getElementById('status-placeholder'); |
|
|
|
|
|
const savedSmartMode = localStorage.getItem('smartModeEnabled'); |
|
|
if (savedSmartMode === 'false') { |
|
|
smartModeToggle.classList.remove('active'); |
|
|
} else { |
|
|
smartModeToggle.classList.add('active'); |
|
|
localStorage.setItem('smartModeEnabled', 'true'); |
|
|
} |
|
|
|
|
|
smartModeToggle.onclick = () => { |
|
|
smartModeToggle.classList.toggle('active'); |
|
|
const isActive = smartModeToggle.classList.contains('active'); |
|
|
localStorage.setItem('smartModeEnabled', isActive); |
|
|
|
|
|
|
|
|
if (isActive) { |
|
|
console.log('Smart Conversation Mode ENABLED'); |
|
|
statusPlaceholder.innerText = 'Mode intelligent'; |
|
|
setTimeout(() => { |
|
|
if (statusPlaceholder.innerText === 'Mode intelligent') { |
|
|
statusPlaceholder.innerText = 'Pret'; |
|
|
} |
|
|
}, 2000); |
|
|
} else { |
|
|
console.log('Smart Mode DISABLED - Using fixed target'); |
|
|
statusPlaceholder.innerText = 'Cible fixe'; |
|
|
setTimeout(() => { |
|
|
if (statusPlaceholder.innerText === 'Cible fixe') { |
|
|
statusPlaceholder.innerText = 'Pret'; |
|
|
} |
|
|
}, 2000); |
|
|
} |
|
|
}; |
|
|
|
|
|
document.addEventListener('reset-ui', () => { |
|
|
document.getElementById('status-placeholder').innerText = 'Pret'; |
|
|
}); |
|
|
|
|
|
|
|
|
let voiceCloneEnabled = localStorage.getItem('voiceCloneEnabled') === 'true'; |
|
|
const cloneToggle = document.getElementById('clone-toggle'); |
|
|
|
|
|
if (cloneToggle) { |
|
|
function updateCloneToggle() { |
|
|
if (voiceCloneEnabled) { |
|
|
cloneToggle.classList.add('active'); |
|
|
cloneToggle.title = 'Clonage de Voix: Active'; |
|
|
} else { |
|
|
cloneToggle.classList.remove('active'); |
|
|
cloneToggle.title = 'Clonage de Voix: Desactive'; |
|
|
} |
|
|
} |
|
|
|
|
|
cloneToggle.addEventListener('click', () => { |
|
|
voiceCloneEnabled = !voiceCloneEnabled; |
|
|
localStorage.setItem('voiceCloneEnabled', voiceCloneEnabled); |
|
|
updateCloneToggle(); |
|
|
}); |
|
|
|
|
|
updateCloneToggle(); |
|
|
} |
|
|
|
|
|
|
|
|
let grammarCorrectionEnabled = localStorage.getItem('grammarCorrectionEnabled') !== 'false'; |
|
|
const grammarToggle = document.getElementById('grammar-toggle'); |
|
|
|
|
|
if (grammarToggle) { |
|
|
function updateGrammarToggle() { |
|
|
if (grammarCorrectionEnabled) { |
|
|
grammarToggle.classList.add('active'); |
|
|
grammarToggle.title = 'Correction Intelligente: Active'; |
|
|
} else { |
|
|
grammarToggle.classList.remove('active'); |
|
|
grammarToggle.title = 'Correction Intelligente: Desactive'; |
|
|
} |
|
|
} |
|
|
|
|
|
grammarToggle.addEventListener('click', () => { |
|
|
grammarCorrectionEnabled = !grammarCorrectionEnabled; |
|
|
localStorage.setItem('grammarCorrectionEnabled', grammarCorrectionEnabled); |
|
|
updateGrammarToggle(); |
|
|
}); |
|
|
|
|
|
updateGrammarToggle(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const savedAiCorrection = localStorage.getItem('aiCorrectionEnabled'); |
|
|
let aiCorrectionEnabled = savedAiCorrection === 'true'; |
|
|
console.log('📦 AI Correction from localStorage:', savedAiCorrection, '→', aiCorrectionEnabled); |
|
|
|
|
|
const aiCorrectionToggle = document.getElementById('ai-correction-toggle'); |
|
|
const aiCorrectionHidden = document.getElementById('ai-correction'); |
|
|
const aiCorrectionCheckbox = document.getElementById('ai-correction-checkbox'); |
|
|
|
|
|
function updateAiCorrectionState() { |
|
|
console.log('🔄 AI Correction State Update:', aiCorrectionEnabled); |
|
|
|
|
|
|
|
|
if (aiCorrectionHidden) aiCorrectionHidden.value = aiCorrectionEnabled ? 'true' : 'false'; |
|
|
|
|
|
|
|
|
if (aiCorrectionCheckbox) aiCorrectionCheckbox.checked = aiCorrectionEnabled; |
|
|
|
|
|
|
|
|
if (aiCorrectionToggle) { |
|
|
if (aiCorrectionEnabled) { |
|
|
aiCorrectionToggle.classList.add('active'); |
|
|
aiCorrectionToggle.style.background = 'linear-gradient(135deg, #667eea, #764ba2)'; |
|
|
aiCorrectionToggle.style.color = '#fff'; |
|
|
aiCorrectionToggle.title = '🧠 AI Correction: ACTIVÉ (Claude/GPT)'; |
|
|
aiCorrectionToggle.innerHTML = '<i class="fa-solid fa-sparkles"></i> AI ✓'; |
|
|
} else { |
|
|
aiCorrectionToggle.classList.remove('active'); |
|
|
aiCorrectionToggle.style.background = 'rgba(255,255,255,0.1)'; |
|
|
aiCorrectionToggle.style.color = 'rgba(255,255,255,0.5)'; |
|
|
aiCorrectionToggle.title = '❌ AI Correction: DÉSACTIVÉ'; |
|
|
aiCorrectionToggle.innerHTML = '<i class="fa-solid fa-sparkles"></i> AI ✗'; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (aiCorrectionToggle) { |
|
|
aiCorrectionToggle.addEventListener('click', (e) => { |
|
|
e.preventDefault(); |
|
|
e.stopPropagation(); |
|
|
|
|
|
|
|
|
aiCorrectionEnabled = !aiCorrectionEnabled; |
|
|
|
|
|
|
|
|
localStorage.setItem('aiCorrectionEnabled', aiCorrectionEnabled.toString()); |
|
|
|
|
|
|
|
|
updateAiCorrectionState(); |
|
|
|
|
|
|
|
|
console.log('🧠 AI Correction TOGGLED to:', aiCorrectionEnabled ? 'ENABLED ✓' : 'DISABLED ✗'); |
|
|
|
|
|
|
|
|
aiCorrectionToggle.style.transform = 'scale(0.95)'; |
|
|
setTimeout(() => aiCorrectionToggle.style.transform = '', 150); |
|
|
}); |
|
|
} |
|
|
|
|
|
if (aiCorrectionCheckbox) { |
|
|
aiCorrectionCheckbox.addEventListener('change', () => { |
|
|
aiCorrectionEnabled = aiCorrectionCheckbox.checked; |
|
|
localStorage.setItem('aiCorrectionEnabled', aiCorrectionEnabled.toString()); |
|
|
updateAiCorrectionState(); |
|
|
console.log('🧠 AI Correction (checkbox) set to:', aiCorrectionEnabled); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
updateAiCorrectionState(); |
|
|
console.log('✅ AI Correction Toggle initialized:', aiCorrectionEnabled ? 'ON' : 'OFF'); |
|
|
|
|
|
|
|
|
const swapBtn = document.getElementById('swap-langs'); |
|
|
|
|
|
const srcLang = document.getElementById('source-lang-quick'); |
|
|
const tgtLang = document.getElementById('target-lang-quick'); |
|
|
|
|
|
if (swapBtn && srcLang && tgtLang) { |
|
|
swapBtn.addEventListener('click', (e) => { |
|
|
e.preventDefault(); |
|
|
|
|
|
|
|
|
const currentSource = srcLang.value; |
|
|
const currentTarget = tgtLang.value; |
|
|
|
|
|
|
|
|
if (currentSource !== 'auto') { |
|
|
|
|
|
const targetOptions = Array.from(tgtLang.options).map(o => o.value); |
|
|
const sourceOptions = Array.from(srcLang.options).map(o => o.value); |
|
|
|
|
|
if (targetOptions.includes(currentSource) && sourceOptions.includes(currentTarget)) { |
|
|
srcLang.value = currentTarget; |
|
|
tgtLang.value = currentSource; |
|
|
} else { |
|
|
|
|
|
srcLang.value = 'auto'; |
|
|
tgtLang.value = currentSource === 'auto' ? 'fr-FR' : currentSource; |
|
|
} |
|
|
} else { |
|
|
|
|
|
const oldTarget = currentTarget; |
|
|
tgtLang.value = oldTarget === 'fr-FR' ? 'ar-SA' : 'fr-FR'; |
|
|
} |
|
|
|
|
|
|
|
|
localStorage.setItem('sourceLangQuick', srcLang.value); |
|
|
localStorage.setItem('targetLangQuick', tgtLang.value); |
|
|
|
|
|
|
|
|
swapBtn.style.transform = 'rotate(180deg)'; |
|
|
setTimeout(() => swapBtn.style.transform = '', 300); |
|
|
|
|
|
console.log(`🔄 Languages swapped: ${srcLang.value} → ${tgtLang.value}`); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const sttSelector = document.getElementById('stt-selector-settings'); |
|
|
const sttEngineHidden = document.getElementById('stt-engine'); |
|
|
const savedSttEngine = localStorage.getItem('sttEngine') || 'seamless-m4t'; |
|
|
|
|
|
if (sttSelector) { |
|
|
sttSelector.value = savedSttEngine; |
|
|
if (sttEngineHidden) sttEngineHidden.value = savedSttEngine; |
|
|
|
|
|
sttSelector.addEventListener('change', function () { |
|
|
localStorage.setItem('sttEngine', this.value); |
|
|
if (sttEngineHidden) sttEngineHidden.value = this.value; |
|
|
console.log('🎤 STT Engine set to:', this.value); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const ttsSelector = document.getElementById('tts-selector'); |
|
|
const ttsEngineHidden = document.getElementById('tts-engine'); |
|
|
const savedTtsEngine = localStorage.getItem('ttsEngine') || 'seamless'; |
|
|
|
|
|
if (ttsSelector) { |
|
|
ttsSelector.value = savedTtsEngine; |
|
|
if (ttsEngineHidden) ttsEngineHidden.value = savedTtsEngine; |
|
|
|
|
|
ttsSelector.addEventListener('change', function () { |
|
|
localStorage.setItem('ttsEngine', this.value); |
|
|
if (ttsEngineHidden) ttsEngineHidden.value = this.value; |
|
|
console.log('🔊 TTS Engine set to:', this.value); |
|
|
|
|
|
|
|
|
this.style.backgroundColor = this.value === 'seamless' ? '#4CAF50' : '#2196F3'; |
|
|
setTimeout(() => this.style.backgroundColor = '', 500); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const translationEngineSelector = document.getElementById('translation-engine-selector'); |
|
|
const translationEngineHidden = document.getElementById('translation-engine'); |
|
|
const nllbModelSizeSelector = document.getElementById('nllb-model-size-selector'); |
|
|
const nllbModelSizeHidden = document.getElementById('nllb-model-size'); |
|
|
const nllbModelSizeGroup = document.getElementById('nllb-model-size-group'); |
|
|
|
|
|
const savedTranslationEngine = localStorage.getItem('translationEngine') || 'nllb'; |
|
|
const savedNllbModelSize = localStorage.getItem('nllbModelSize') || 'fast'; |
|
|
|
|
|
function updateNllbModelSizeVisibility() { |
|
|
if (nllbModelSizeGroup) { |
|
|
nllbModelSizeGroup.style.display = |
|
|
translationEngineSelector && translationEngineSelector.value === 'nllb' ? 'block' : 'none'; |
|
|
} |
|
|
} |
|
|
|
|
|
if (translationEngineSelector) { |
|
|
translationEngineSelector.value = savedTranslationEngine; |
|
|
if (translationEngineHidden) translationEngineHidden.value = savedTranslationEngine; |
|
|
updateNllbModelSizeVisibility(); |
|
|
|
|
|
translationEngineSelector.addEventListener('change', function () { |
|
|
localStorage.setItem('translationEngine', this.value); |
|
|
if (translationEngineHidden) translationEngineHidden.value = this.value; |
|
|
console.log('🌍 Translation Engine set to:', this.value); |
|
|
updateNllbModelSizeVisibility(); |
|
|
}); |
|
|
} |
|
|
|
|
|
if (nllbModelSizeSelector) { |
|
|
nllbModelSizeSelector.value = savedNllbModelSize; |
|
|
if (nllbModelSizeHidden) nllbModelSizeHidden.value = savedNllbModelSize; |
|
|
|
|
|
nllbModelSizeSelector.addEventListener('change', function () { |
|
|
localStorage.setItem('nllbModelSize', this.value); |
|
|
if (nllbModelSizeHidden) nllbModelSizeHidden.value = this.value; |
|
|
console.log('🧠 NLLB Model Size set to:', this.value); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const smartModeToggle = document.getElementById('smart-mode-toggle'); |
|
|
|
|
|
|
|
|
|
|
|
const replayBtn = document.getElementById('replay-trigger'); |
|
|
if (replayBtn) { |
|
|
replayBtn.addEventListener('click', () => { |
|
|
if (window.lastBotAudio) { |
|
|
console.log('”„ Replaying last audio...'); |
|
|
|
|
|
const icon = replayBtn.querySelector('i'); |
|
|
icon.style.transition = 'transform 0.5s ease'; |
|
|
icon.style.transform = 'rotate(-360deg)'; |
|
|
|
|
|
window.lastBotAudio.currentTime = 0; |
|
|
window.lastBotAudio.play().catch(e => console.error("Replay failed:", e)); |
|
|
|
|
|
setTimeout(() => { |
|
|
icon.style.transition = 'none'; |
|
|
icon.style.transform = 'rotate(0deg)'; |
|
|
}, 500); |
|
|
} else { |
|
|
|
|
|
console.log('š ï¸ No audio to replay'); |
|
|
replayBtn.style.animation = 'shake 0.4s cubic-bezier(.36,.07,.19,.97) both'; |
|
|
setTimeout(() => replayBtn.style.animation = '', 400); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const styleSheet = document.createElement("style"); |
|
|
styleSheet.innerText = ` |
|
|
@keyframes shake { |
|
|
10%, 90% { transform: translate3d(-1px, 0, 0); } |
|
|
20%, 80% { transform: translate3d(2px, 0, 0); } |
|
|
30%, 50%, 70% { transform: translate3d(-4px, 0, 0); } |
|
|
40%, 60% { transform: translate3d(4px, 0, 0); } |
|
|
} |
|
|
`; |
|
|
document.head.appendChild(styleSheet); |
|
|
|
|
|
|
|
|
let voiceGenderPreference = localStorage.getItem('voiceGenderPreference') || 'auto'; |
|
|
const voiceGenderToggle = document.getElementById('voice-gender-toggle'); |
|
|
|
|
|
if (voiceGenderToggle) { |
|
|
const icons = { |
|
|
'auto': 'fa-user-gear', |
|
|
'male': 'fa-person', |
|
|
'female': 'fa-person-dress' |
|
|
}; |
|
|
|
|
|
const titles = { |
|
|
'auto': 'Voix: Auto', |
|
|
'male': 'Voix: Masculine', |
|
|
'female': 'Voix: Feminine' |
|
|
}; |
|
|
|
|
|
function updateVoiceGenderToggle() { |
|
|
voiceGenderToggle.classList.remove('auto', 'male', 'female'); |
|
|
voiceGenderToggle.classList.add(voiceGenderPreference); |
|
|
|
|
|
const iconElement = voiceGenderToggle.querySelector('i'); |
|
|
iconElement.className = `fa-solid ${icons[voiceGenderPreference]}`; |
|
|
voiceGenderToggle.title = titles[voiceGenderPreference]; |
|
|
} |
|
|
|
|
|
voiceGenderToggle.addEventListener('click', () => { |
|
|
if (voiceGenderPreference === 'auto') { |
|
|
voiceGenderPreference = 'male'; |
|
|
} else if (voiceGenderPreference === 'male') { |
|
|
voiceGenderPreference = 'female'; |
|
|
} else { |
|
|
voiceGenderPreference = 'auto'; |
|
|
} |
|
|
localStorage.setItem('voiceGenderPreference', voiceGenderPreference); |
|
|
updateVoiceGenderToggle(); |
|
|
}); |
|
|
|
|
|
updateVoiceGenderToggle(); |
|
|
} |
|
|
|
|
|
|
|
|
const sourceLangQuick = document.getElementById('source-lang-quick'); |
|
|
const targetLangQuick = document.getElementById('target-lang-quick'); |
|
|
const swapLangsBtn = document.getElementById('swap-langs'); |
|
|
const quickLangSelector = document.getElementById('quick-lang-selector'); |
|
|
|
|
|
|
|
|
const savedSourceLang = localStorage.getItem('sourceLangQuick') || 'auto'; |
|
|
const savedTargetLang = localStorage.getItem('targetLangQuick') || 'French'; |
|
|
|
|
|
if (sourceLangQuick) sourceLangQuick.value = savedSourceLang; |
|
|
if (targetLangQuick) targetLangQuick.value = savedTargetLang; |
|
|
if (quickLangSelector) quickLangSelector.value = savedTargetLang; |
|
|
|
|
|
|
|
|
if (sourceLangQuick) { |
|
|
sourceLangQuick.addEventListener('change', function () { |
|
|
localStorage.setItem('sourceLangQuick', this.value); |
|
|
console.log('ޤ Source language set to:', this.value); |
|
|
|
|
|
|
|
|
fetch('/clear_cache', { method: 'POST' }); |
|
|
|
|
|
|
|
|
const langName = this.options[this.selectedIndex].text; |
|
|
statusPlaceholder.innerText = langName; |
|
|
setTimeout(() => { |
|
|
if (statusPlaceholder.innerText === langName) { |
|
|
statusPlaceholder.innerText = 'Pret'; |
|
|
} |
|
|
}, 1500); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if (targetLangQuick) { |
|
|
targetLangQuick.addEventListener('change', function () { |
|
|
localStorage.setItem('targetLangQuick', this.value); |
|
|
localStorage.setItem('targetLang', this.value); |
|
|
|
|
|
|
|
|
if (quickLangSelector) quickLangSelector.value = this.value; |
|
|
|
|
|
console.log('✨ Target language set to:', this.value); |
|
|
|
|
|
|
|
|
fetch('https://instant-translat-production.up.railway.app/clear_cache', { method: 'POST' }); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if (swapLangsBtn && sourceLangQuick && targetLangQuick) { |
|
|
swapLangsBtn.addEventListener('click', function () { |
|
|
|
|
|
const sourceToTarget = { |
|
|
'ar-SA': 'Arabic', |
|
|
'fr-FR': 'French', |
|
|
'en-US': 'English', |
|
|
'es-ES': 'Spanish', |
|
|
'de-DE': 'German', |
|
|
'auto': 'auto' |
|
|
}; |
|
|
|
|
|
const targetToSource = { |
|
|
'Arabic': 'ar-SA', |
|
|
'Moroccan Darija': 'ar-SA', |
|
|
'French': 'fr-FR', |
|
|
'English': 'en-US', |
|
|
'Spanish': 'es-ES', |
|
|
'German': 'de-DE' |
|
|
}; |
|
|
|
|
|
const currentSource = sourceLangQuick.value; |
|
|
const currentTarget = targetLangQuick.value; |
|
|
|
|
|
|
|
|
const newSource = targetToSource[currentTarget] || 'auto'; |
|
|
const newTarget = sourceToTarget[currentSource] || 'French'; |
|
|
|
|
|
sourceLangQuick.value = newSource; |
|
|
targetLangQuick.value = newTarget; |
|
|
|
|
|
|
|
|
localStorage.setItem('sourceLangQuick', newSource); |
|
|
localStorage.setItem('targetLangQuick', newTarget); |
|
|
localStorage.setItem('targetLang', newTarget); |
|
|
|
|
|
if (quickLangSelector) quickLangSelector.value = newTarget; |
|
|
|
|
|
|
|
|
this.style.transform = 'rotate(180deg)'; |
|
|
setTimeout(() => { |
|
|
this.style.transform = 'rotate(0deg)'; |
|
|
}, 300); |
|
|
|
|
|
console.log('”„ Languages swapped:', newSource, '†”', newTarget); |
|
|
|
|
|
|
|
|
fetch('/clear_cache', { method: 'POST' }); |
|
|
}); |
|
|
} |
|
|
</script> |
|
|
</body> |
|
|
|
|
|
</html> |