diff --git a/cache_plane_variables.txt b/cache_plane_variables.txt deleted file mode 100644 index 8c83b01..0000000 --- a/cache_plane_variables.txt +++ /dev/null @@ -1,2 +0,0 @@ -wing_sections: [{'chord_root': 1.0, 'chord_tip': 1.0, 'span_fraction': 8.0, 'sweep': np.float64(0.5235987755982988), 'alpha': np.float64(0.05235987755982989), 'dihedral': np.float64(0.06981317007977318), 'NACA_root': '3215', 'NACA_tip': '1310'}] -horizontal_stabilizer: {'x_translate': 2.0, 'z_translate': 0.0, 'NACA_root': '3215', 'NACA_tip': '3215', 'chord_root': 0.5, 'chord_tip': 0.5, 'span_fraction': 2.0, 'sweep': np.float64(0.5235987755982988), 'alpha': np.float64(0.0), 'dihedral': np.float64(0.0)} diff --git a/src/vlm.old/VLM_method.ipynb b/src/vlm.old/VLM_method.ipynb index 981424a..82a4a0b 100644 --- a/src/vlm.old/VLM_method.ipynb +++ b/src/vlm.old/VLM_method.ipynb @@ -206,7 +206,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.13.7" } }, "nbformat": 4, diff --git a/src/vlm/app.py b/src/vlm/app.py index 6581665..1261185 100644 --- a/src/vlm/app.py +++ b/src/vlm/app.py @@ -13,6 +13,7 @@ from functools import wraps from pathlib import Path from typing import Any, Dict, Optional +from datetime import datetime # Third-party imports kept together; optional/possibly heavy imports guarded for clearer errors try: @@ -275,8 +276,9 @@ def _vlm_create_and_save(plane: dict, u: float, rho: float, alpha: float, beta: vlm = VLM(plane, u, rho, alpha, beta, n, m) # operación que depende de VLM vlm.save_and_load_plane_variables(filename=str(BASE_DIR / 'data' / 'cache_plane_variables.txt'), option='save_and_load') - state_path = SAVED_STATES / f"{session_id}.pkl" - vlm.save_state(str(state_path)) + #timestamp = datetime.now().strftime("%d%m%y%H%M%S") + #state_path = SAVED_STATES / f"{session_id}_{timestamp}.pkl" + #vlm.save_state(str(state_path)) with vlm_sessions_lock: vlm_sessions[session_id] = vlm return {"status": "success", "message": "Wing design computed."} @@ -473,6 +475,10 @@ def compute(vlm: VLM): if vlm.results is None: future = thread_pool.submit(compute, vlm) vlm = future.result() + #save state after computation + timestamp = datetime.now().strftime("%d%m%y%H%M%S") + state_path = SAVED_STATES / f"{"results"}_{timestamp}.pkl" + vlm.save_state(str(state_path)) wing_lift_total = np.sum(vlm.lift_sum.get('lift_wing', [])) if hasattr(vlm, 'lift_sum') else None hs_lift_total = np.sum(vlm.lift_sum.get('lift_hs', [])) if hasattr(vlm, 'lift_sum') else None @@ -576,14 +582,33 @@ def load_airfoil(): def load_vlm_state(): try: session_id = session.get("session_id") - state_file = SAVED_STATES / f"{session_id}.pkl" - if not state_file.exists(): - return jsonify({"status": "error", "message": "No saved state found"}), 404 + if 'state_file' not in request.files: + return jsonify({"status": "error", "message": "No state_file provided"}), 400 + file = request.files['state_file'] + if file.filename == '': + return jsonify({"status": "error", "message": "No file selected"}), 400 + + # Path to load the state file + path = SAVED_STATES / file.filename with vlm_sessions_lock: if session_id not in vlm_sessions: return jsonify({"status": "error", "message": "No active VLM session"}), 400 - vlm_sessions[session_id].load_state(str(state_file)) + vlm_sessions[session_id].load_state(str(path)) + vlm = vlm_sessions[session_id] + #update vlm session + wing_lift_total = np.sum(vlm.lift_sum.get('lift_wing', [])) if hasattr(vlm, 'lift_sum') else None + hs_lift_total = np.sum(vlm.lift_sum.get('lift_hs', [])) if hasattr(vlm, 'lift_sum') else None + vs_lift_total = np.sum(vlm.lift_sum.get('lift_vs', [])) if hasattr(vlm, 'lift_sum') else None + + cl = f"{vlm.CL:.4f}" if getattr(vlm, 'CL', None) is not None else "--" + cd = f"{vlm.CD:.4f}" if getattr(vlm, 'CD', None) is not None else "--" + total_lift = f"{vlm.lift:.2f}" if getattr(vlm, 'lift', None) is not None else "--" + total_drag = f"{vlm.drag:.2f}" if getattr(vlm, 'drag', None) is not None else "--" + wing_lift = f"{wing_lift_total:.2f}" if wing_lift_total is not None else "--" + hs_lift = f"{hs_lift_total:.2f}" if hs_lift_total is not None else "--" + vs_lift = f"{vs_lift_total:.2f}" if vs_lift_total is not None else "--" + return jsonify({"status": "success", "message": "VLM state loaded successfully"}), 200 except Exception as e: logger.exception("Error in /load_vlm_state") diff --git a/src/vlm/data/cache_plane_variables.txt b/src/vlm/data/cache_plane_variables.txt index f4c5cc9..6bebb08 100644 --- a/src/vlm/data/cache_plane_variables.txt +++ b/src/vlm/data/cache_plane_variables.txt @@ -1 +1,3 @@ -wing_sections: [{'chord_root': 1.0, 'chord_tip': 0.8, 'span_fraction': 8.0, 'sweep': np.float64(0.5235987755982988), 'alpha': np.float64(0.05235987755982989), 'dihedral': np.float64(0.06981317007977318), 'NACA_root': '3215', 'NACA_tip': '1310'}] +wing_sections: [{'chord_root': 1.0, 'chord_tip': 1.0, 'span_fraction': 8.0, 'sweep': np.float64(0.5235987755982988), 'alpha': np.float64(0.0523598775598299), 'dihedral': np.float64(0.06981317007977318), 'NACA_root': '3215', 'NACA_tip': '1310'}] +horizontal_stabilizer: {'x_translate': 2.0, 'z_translate': 0.0, 'NACA_root': '3215', 'NACA_tip': '3215', 'chord_root': 0.5, 'chord_tip': 0.5, 'span_fraction': 2.0, 'sweep': np.float64(0.5235987755982988), 'alpha': np.float64(0.0), 'dihedral': np.float64(0.0)} +vertical_stabilizer: {'x_translate': 2.0, 'z_translate': 0.0, 'NACA_root': '3215', 'NACA_tip': '3215', 'chord_root': 0.5, 'chord_tip': 0.5, 'span_fraction': 0.5, 'sweep': np.float64(0.5235987755982988), 'alpha': np.float64(0.0), 'dihedral': np.float64(1.5707963267948966)} diff --git a/src/vlm/data/saved_states/results_051125154305.pkl b/src/vlm/data/saved_states/results_051125154305.pkl new file mode 100644 index 0000000..0ba8a79 Binary files /dev/null and b/src/vlm/data/saved_states/results_051125154305.pkl differ diff --git a/src/vlm/data/saved_states/results_051125154653.pkl b/src/vlm/data/saved_states/results_051125154653.pkl new file mode 100644 index 0000000..0ba8a79 Binary files /dev/null and b/src/vlm/data/saved_states/results_051125154653.pkl differ diff --git a/src/vlm/data/saved_states/results_051125171625.pkl b/src/vlm/data/saved_states/results_051125171625.pkl new file mode 100644 index 0000000..0ba8a79 Binary files /dev/null and b/src/vlm/data/saved_states/results_051125171625.pkl differ diff --git a/src/vlm/data/saved_states/results_051125194910.pkl b/src/vlm/data/saved_states/results_051125194910.pkl new file mode 100644 index 0000000..6142cd1 Binary files /dev/null and b/src/vlm/data/saved_states/results_051125194910.pkl differ diff --git a/src/vlm/saved_states/7d6e13f6-1f72-4763-9243-838b95750455.pkl b/src/vlm/saved_states/7d6e13f6-1f72-4763-9243-838b95750455.pkl deleted file mode 100644 index 84b58e2..0000000 Binary files a/src/vlm/saved_states/7d6e13f6-1f72-4763-9243-838b95750455.pkl and /dev/null differ diff --git a/src/vlm/static/css/style.css b/src/vlm/static/css/style.css index b5d746f..13ed939 100644 --- a/src/vlm/static/css/style.css +++ b/src/vlm/static/css/style.css @@ -40,6 +40,8 @@ --landing-container: 1200px; --landing-shadow: var(--shadow); --landing-glass-border: rgba(255,255,255,0.04); + --info-button: #e3e3e3; + --info-button-active: #007bff; } /* Light Mode Variables */ @@ -387,9 +389,9 @@ html, body { color: var(--button-text); border: none; border-radius: var(--radius-full); - padding: 0.7rem 1rem; + padding: 0.5rem; cursor: pointer; - font-size: 1.2rem; + font-size: 1rem; box-shadow: var(--shadow); transition: background-color var(--transition), color var(--transition), box-shadow var(--transition), transform 0.2s cubic-bezier(.68,-0.55,.27,1.55); display: flex; @@ -579,11 +581,14 @@ button { color: var(--button-text); min-width: 3rem; margin-bottom: 1rem; + cursor: pointer; + transition: background 0.2s, transform 0.2s; } button:hover { - background-color: color-mix(in srgb, var(--button-color) 80%, black 20%); - box-shadow: var(--shadow); + background: color-mix(in srgb, var(--button-color) 80%, black 20%); + transform: scale(1.05); + outline: none; } .toggle-button { @@ -1365,6 +1370,102 @@ input:focus, select:focus { transform: translateY(0); } +/* ==== Modern Language Selector for VLMPy ==== */ +.language-selector { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + justify-content: center; + align-items: center; +} + +.language-btn { + display: flex; + align-items: center; + justify-content: center; + padding: 0.4rem 0.8rem; + background: var(--landing-glass, rgba(255,255,255,0.04)); + border: 1px solid var(--landing-glass-border, #2c2c2c); + border-radius: 999px; + color: var(--text-color, #e0e0e0); + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + transition: background 0.22s, border-color 0.22s, color 0.22s, box-shadow 0.22s, transform 0.18s; + box-shadow: 0 1px 4px rgba(0,0,0,0.05); + outline: none; + position: relative; + width: 36px; + height: 36px; +} + +.language-btn:hover, +.language-btn:focus-visible { + background: var(--accent-color, #007bff); + color: #fff; + border-color: var(--accent-color, #007bff); + box-shadow: 0 3px 12px rgba(0,123,255,0.15); + transform: translateY(-1px) scale(1.05); +} + +.language-btn.active { + background: linear-gradient(135deg, var(--accent-color, #007bff) 80%, #06b6d4 100%); + color: #fff; + border-color: var(--accent-color, #007bff); + box-shadow: 0 4px 16px rgba(0,123,255,0.18); + transform: scale(1.05); +} + +.language-btn::before { + font-size: 1.2em; + filter: drop-shadow(0 1px 2px rgba(0,0,0,0.08)); + transition: filter 0.2s; +} + +.language-btn[data-lang="en"]::before { content: "🇬🇧"; } +.language-btn[data-lang="es"]::before { content: "🇪🇸"; } +.language-btn[data-lang="fr"]::before { content: "🇫🇷"; } +.language-btn[data-lang="de"]::before { content: "🇩🇪"; } +.language-btn[data-lang="pt"]::before { content: "🇵🇹"; } +.language-btn[data-lang="it"]::before { content: "🇮🇹"; } +.language-btn[data-lang="zh"]::before { content: "🇨🇳"; } +.language-btn[data-lang="ja"]::before { content: "🇯🇵"; } + + +/* Add subtle ripple effect on click */ +.language-btn:active::after { + content: ""; + position: absolute; + left: 50%; top: 50%; + transform: translate(-50%, -50%); + width: 120%; + height: 120%; + border-radius: 999px; + background: rgba(0,123,255,0.08); + pointer-events: none; + animation: rippleLang 0.4s linear; + z-index: 1; +} +@keyframes rippleLang { + 0% { opacity: 0.5; } + 100% { opacity: 0; } +} + +/* Responsive for selector */ +@media (max-width: 768px) { + .language-selector { + flex-direction: column; + gap: 0.5rem; + align-items: stretch; + } + .language-btn { + justify-content: flex-start; + min-width: 0; + width: 100%; + } +} + + /* Animations */ @keyframes fadeIn { @@ -1549,7 +1650,7 @@ body.welcome-page .main-content { } } -/* Navigation bar moderna para landing */ +/* Navigation bar */ .welcome-nav { width: 100%; min-width: 100%; @@ -1642,7 +1743,7 @@ body.welcome-page .main-content { font-size: 1.2rem; } -/* Hero Section moderna */ +/* Hero Section */ .landing-hero { max-width: var(--landing-container); margin: 4rem auto; @@ -1745,7 +1846,7 @@ body.welcome-page .main-content { font-size: 0.95rem; } -/* Hero Right - Preview Card con efecto 3D */ +/* Hero Right - Preview Card */ .landing-hero-right { perspective: 1000px; } @@ -1952,3 +2053,186 @@ body.welcome-page .footer { display: none !important; } } + +/* Settings section */ +.settings-toggle { + position: fixed; + right: 1rem; + top: 4rem; + z-index: 999; +} + +.settings-options { + background: var(--secondary-bg); + border-radius: var(--border-radius); + padding: 0.75rem; + box-shadow: var(--shadow); + backdrop-filter: blur(8px); + border: 1px solid var(--glass-border); + display: none; + flex-direction: column; + gap: 0.75rem; + opacity: 0; + transform: scale(0.95) translateX(-3rem); + min-width: 180px; +} + +.settings-section { + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +.settings-section h4 { + margin: 0 0 0.3rem 0; + font-size: 0.85rem; + color: var(--text-color); + text-transform: uppercase; + letter-spacing: 0.05em; + opacity: 0.8; +} + +.theme-buttons { + display: flex; + gap: 0.5rem; + justify-content: center; +} + +.language-selector { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + justify-content: center; +} + +.toggle-buttons.show-options .settings-options { + display: flex; + animation: appearancePanelOpen 0.45s cubic-bezier(.68,-0.55,.27,1.55); + opacity: 1; + transform: scale(1) translateX(-3rem); +} + +.toggle-buttons.hide-options .settings-options { + display: flex; + animation: appearancePanelClose 0.35s cubic-bezier(.68,-0.55,.27,1.55); + opacity: 0; + transform: scale(0.8) translateX(3rem); + pointer-events: none; +} + + +/* Info pop-up */ +.form-group-header { + position: relative; + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.5rem; +} + +.info-button { + background: none; + border: none; + color: var(--info-button); + font-size: 1rem; + cursor: pointer; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.2s, transform 0.2s; + margin-bottom: 0rem; + width: 1em; + height: 1em; + min-width: 1em; + min-height: 1em; + border-radius: 50%; +} +.info-button:hover, +.info-button:focus { + color: color-mix(in srgb, var(--info-button-active) 80%, black 20%); + transform: scale(1.08); + outline: none; +} + +/* Popup */ +.info-popup { + display: none; + position: fixed; + top: 20%; + left: 50%; + transform: translate(-50%, 0); + width: min(40rem, 90vw); + max-height: 60vh; + padding: 1.5rem; + overflow-y: auto; + border-radius: 1rem; + z-index: 1001; + background: rgba(255,255,255,0.02); + box-shadow: var(--shadow); + color: var(--text-color); + backdrop-filter: blur(12px); +} + +/* Asegura que el contenido esté nítido*/ +.info-popup > * { + position: relative; + z-index: 1002; +} + +/* estilo para el botón de cerrar*/ +.info-popup .close-btn { + margin-top: 1rem; +} + +.info-popup.hide { + animation: fadeOut 0.8s cubic-bezier(0.1, 0, 0.1, 0.1) forwards; + pointer-events: none; +} + +@keyframes infoPopupIn { + 0% { + opacity: 0; + transform: translate(-50%, 0) scale(0.92); + box-shadow: 0 0 0 rgba(0,0,0,0); + } + 60% { + opacity: 0.5; + transform: translate(-50%, 0) scale(0.96); + box-shadow: 0 8px 32px rgba(0,0,0,0.12); + } + 100% { + opacity: 1; + transform: translate(-50%, 0) scale(1); + box-shadow: var(--shadow); + } +} + +.info-popup::-webkit-scrollbar { + display: none; /* Chrome/Safari */ +} +.info-popup h4 { + margin-top: 0; + color: var(--text-color); + font-size: 1.2rem; + margin-bottom: 0.1rem; + backdrop-filter: blur(12px); +} +.info-popup p { + font-size: 1rem; + line-height: 1.5; + margin-top: 0rem; + margin-bottom: 1em; + backdrop-filter: blur(12px); +} +.info-popup ul { + padding-left: 18px; + margin-top: 0; + margin-bottom: 1em; + backdrop-filter: blur(12px); +} +.info-popup li { + margin-bottom: 0.5em; + font-size: 0.98em; + backdrop-filter: blur(12px); +} \ No newline at end of file diff --git a/src/vlm/static/js/auto-translate.js b/src/vlm/static/js/auto-translate.js index d6647b1..db28f53 100644 --- a/src/vlm/static/js/auto-translate.js +++ b/src/vlm/static/js/auto-translate.js @@ -1,130 +1,91 @@ -// static/js/auto-translate.js -(function(){ +(function() { if (window._vlmpy_translate_loaded) return; window._vlmpy_translate_loaded = true; - // Opciones: idiomas que quieres ofrecer - const SUPPORTED_LANGS = ['es','fr','de','it','pt']; // añade/elimina códigos ISO + const SUPPORTED_LANGS = [ + { code: 'en', label: 'EN', flag: '🇬🇧' }, + { code: 'es', label: 'ES', flag: '🇪🇸' }, + { code: 'fr', label: 'FR', flag: '🇫🇷' }, + { code: 'de', label: 'DE', flag: '🇩🇪' }, + { code: 'it', label: 'IT', flag: '🇮🇹' }, + { code: 'pt', label: 'PT', flag: '🇵🇹' }, + { code: 'zh', label: 'ZH', flag: '🇨🇳' }, + { code: 'ja', label: 'JA', flag: '🇯🇵' } + ]; const PAGE_LANG = (document.documentElement.lang || 'en').split('-')[0]; - // Helper: set cookie for Google Translate (used by widget) function setGoogTransCookie(from, to) { - // No domain => cookie para el host actual document.cookie = 'googtrans=/' + from + '/' + to + ';path=/'; document.cookie = 'googtrans=/' + from + '/' + to + ';path=/;domain=' + location.hostname; } + function getStoredLanguage() { + return localStorage.getItem('vlmpy-google-translate-lang'); + } + function saveLanguage(lang) { + localStorage.setItem('vlmpy-google-translate-lang', lang); + } - // Create container for the Google widget (it will render the select if present) - const container = document.createElement('div'); - container.id = 'google_translate_element'; - // minimal hidden container: we will use our custom UI, but widget needs an element - container.style.display = 'none'; - document.body.appendChild(container); + // Modern floating selector UI + const selectorWrap = document.createElement('div'); + selectorWrap.className = 'language-selector modern'; + selectorWrap.setAttribute('role', 'group'); + selectorWrap.setAttribute('aria-label', 'Selector de idioma'); - // Build a small floating UI (bottom-right) - const floatWrap = document.createElement('div'); - floatWrap.setAttribute('aria-hidden','false'); - floatWrap.style.position = 'fixed'; - floatWrap.style.right = '1rem'; - floatWrap.style.bottom = '1rem'; - floatWrap.style.zIndex = 999999; - floatWrap.style.fontFamily = 'Inter, Arial, sans-serif'; - floatWrap.style.userSelect = 'none'; - floatWrap.style.boxShadow = '0 6px 18px rgba(0,0,0,0.12)'; - floatWrap.style.borderRadius = '10px'; - floatWrap.style.overflow = 'hidden'; - floatWrap.style.background = 'var(--card-bg, #fff)'; - floatWrap.style.backdropFilter = 'blur(4px)'; - floatWrap.style.minWidth = '48px'; - - // Toggle button - const toggleBtn = document.createElement('button'); - toggleBtn.type = 'button'; - toggleBtn.title = 'Idioma / Language'; - toggleBtn.style.border = 'none'; - toggleBtn.style.background = 'transparent'; - toggleBtn.style.padding = '0.6rem'; - toggleBtn.style.cursor = 'pointer'; - toggleBtn.style.display = 'flex'; - toggleBtn.style.alignItems = 'center'; - toggleBtn.style.gap = '0.5rem'; - toggleBtn.style.fontSize = '0.9rem'; - - const globe = document.createElement('span'); - globe.textContent = '🌐'; - toggleBtn.appendChild(globe); - const label = document.createElement('span'); - label.textContent = (navigator.language && navigator.language.startsWith('es')) ? 'ES' : 'Lang'; - label.style.fontWeight = '600'; - toggleBtn.appendChild(label); - floatWrap.appendChild(toggleBtn); - - // Panel with language buttons (hidden by default) - const panel = document.createElement('div'); - panel.style.display = 'none'; - panel.style.padding = '0.4rem'; - panel.style.borderTop = '1px solid rgba(0,0,0,0.06)'; - panel.style.background = 'transparent'; - panel.style.display = 'grid'; - panel.style.gridTemplateColumns = 'repeat(2, auto)'; - panel.style.gap = '0.25rem'; - - // Add language buttons - SUPPORTED_LANGS.forEach(code => { - const b = document.createElement('button'); - b.type = 'button'; - b.textContent = code.toUpperCase(); - b.dataset.lang = code; - b.style.border = 'none'; - b.style.padding = '0.4rem 0.6rem'; - b.style.borderRadius = '6px'; - b.style.cursor = 'pointer'; - b.style.fontWeight = '600'; - b.style.background = 'rgba(0,0,0,0.04)'; - b.addEventListener('click', () => { - // Set cookie then reload to apply translation via Google widget + SUPPORTED_LANGS.forEach(({code, label, flag}) => { + const btn = document.createElement('button'); + btn.type = 'button'; + btn.className = 'language-btn'; + btn.innerHTML = `${label}`; + btn.dataset.lang = code; + btn.setAttribute('aria-label', 'Cambiar a ' + code); + + if ( + (getStoredLanguage() && getStoredLanguage() === code) || + (!getStoredLanguage() && PAGE_LANG === code) + ) { + btn.classList.add('active'); + } + + btn.addEventListener('click', () => { setGoogTransCookie(PAGE_LANG, code); - // give a tiny delay for cookie to be set + saveLanguage(code); + + selectorWrap.querySelectorAll('.language-btn').forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + setTimeout(() => location.reload(), 300); }); - panel.appendChild(b); + + selectorWrap.appendChild(btn); }); - // Button to reset to original language - const reset = document.createElement('button'); - reset.type = 'button'; - reset.textContent = 'ORIG'; - reset.style.border = 'none'; - reset.style.padding = '0.4rem 0.6rem'; - reset.style.borderRadius = '6px'; - reset.style.cursor = 'pointer'; - reset.style.gridColumn = '1 / -1'; - reset.style.fontWeight = '600'; - reset.style.background = 'rgba(0,0,0,0.04)'; - reset.addEventListener('click', () => { + // Reset button + const resetBtn = document.createElement('button'); + resetBtn.type = 'button'; + resetBtn.className = 'language-btn'; + resetBtn.innerHTML = `🌐 ORIG`; + resetBtn.setAttribute('aria-label', 'Idioma original'); + resetBtn.dataset.lang = PAGE_LANG; + + resetBtn.addEventListener('click', () => { setGoogTransCookie(PAGE_LANG, PAGE_LANG); + localStorage.removeItem('vlmpy-google-translate-lang'); + + selectorWrap.querySelectorAll('.language-btn').forEach(b => b.classList.remove('active')); + resetBtn.classList.add('active'); + setTimeout(() => location.reload(), 300); }); - panel.appendChild(reset); - floatWrap.appendChild(panel); - document.body.appendChild(floatWrap); + selectorWrap.appendChild(resetBtn); + document.body.appendChild(selectorWrap); - // Toggle panel open/close - let open = false; - toggleBtn.addEventListener('click', (e) => { - e.stopPropagation(); - open = !open; - panel.style.display = open ? 'grid' : 'none'; - }); - document.addEventListener('click', (e) => { - if (!floatWrap.contains(e.target)) { - open = false; - panel.style.display = 'none'; - } - }); + // Hidden Google Translate widget + const container = document.createElement('div'); + container.id = 'google_translate_element'; + container.style.display = 'none'; + document.body.appendChild(container); - // Define callback expected by Google's script window.googleTranslateElementInit = function() { try { new google.translate.TranslateElement({ @@ -132,24 +93,26 @@ layout: google.translate.TranslateElement.InlineLayout.SIMPLE }, 'google_translate_element'); } catch (e) { - // ignore - // console.warn('Google translate init failed', e); + console.warn('[VLMPy AutoTranslate] Google Translate init failed:', e); } }; - // If the browser language is one of supported, auto-set cookie to translate automatically const userLang = (navigator.language || navigator.userLanguage || 'en').split('-')[0]; - if (SUPPORTED_LANGS.includes(userLang) && userLang !== PAGE_LANG) { - // set cookie to auto-translate from PAGE_LANG -> userLang (applied after script loads) + const storedLang = getStoredLanguage(); + + if (storedLang && SUPPORTED_LANGS.some(l => l.code === storedLang) && storedLang !== PAGE_LANG) { + setGoogTransCookie(PAGE_LANG, storedLang); + } else if (SUPPORTED_LANGS.some(l => l.code === userLang) && userLang !== PAGE_LANG) { setGoogTransCookie(PAGE_LANG, userLang); - label.textContent = userLang.toUpperCase(); + saveLanguage(userLang); } - // Load Google Translate script - const s = document.createElement('script'); - s.src = '//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit'; - s.async = true; - s.defer = true; - document.head.appendChild(s); + const script = document.createElement('script'); + script.src = '//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit'; + script.async = true; + script.defer = true; + document.head.appendChild(script); + console.log('[VLMPy AutoTranslate] Modern selector initialized with page language:', PAGE_LANG); + console.log('[VLMPy AutoTranslate] Supported languages:', SUPPORTED_LANGS.map(l => l.code)); })(); diff --git a/src/vlm/templates/airfoil.html b/src/vlm/templates/airfoil.html index 6fe18ba..282af21 100644 --- a/src/vlm/templates/airfoil.html +++ b/src/vlm/templates/airfoil.html @@ -21,13 +21,17 @@

NACA Airfoil Definition

- - - - -
+

Manage Airfoil Configuration

+
+ + + + +
+
diff --git a/src/vlm/templates/base.html b/src/vlm/templates/base.html index 947ae16..91ddcfa 100644 --- a/src/vlm/templates/base.html +++ b/src/vlm/templates/base.html @@ -9,7 +9,7 @@ - + {# #} diff --git a/src/vlm/templates/results.html b/src/vlm/templates/results.html index bd6a942..8fa6a25 100644 --- a/src/vlm/templates/results.html +++ b/src/vlm/templates/results.html @@ -74,7 +74,6 @@

Visualize a determined section of the span

-

Analis for different angles of attack

@@ -85,20 +84,19 @@

Analis for different angles of attack

+
+ + +
- - -
-
-