Some checks failed
CI / Lint (bash) (push) Has been cancelled
CI / Lint (markdown) (push) Has been cancelled
CI / Lint (nickel) (push) Has been cancelled
CI / Lint (nushell) (push) Has been cancelled
CI / Lint (rust) (push) Has been cancelled
CI / Benchmark (push) Has been cancelled
CI / Security Audit (push) Has been cancelled
CI / License Compliance (push) Has been cancelled
CI / Code Coverage (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (macos-latest) (push) Has been cancelled
CI / Build (ubuntu-latest) (push) Has been cancelled
CI / Build (windows-latest) (push) Has been cancelled
Replace all TOML form definitions in examples/ and config/ with type-checked Nickel equivalents. Update cli_loader to prefer .ncl (via nickel export) over .toml in config search order. TOML support retained as fallback — no breaking change. - El loader usa nickel export --format json + serde_json como puente — evita reimplementar un parser Nickel en Rust y aprovecha el binario ya existente. - El orden de búsqueda .ncl > .toml permite migración incremental: cualquier config vieja sigue funcionando sin tocarla. - Los contratos Nickel (| default, | String) en los configs sustituyen la validación que antes era implícita en el parsing TOML — el error llega antes (en nickel export) con mensajes más descriptivos.
2 lines
23 KiB
HTML
2 lines
23 KiB
HTML
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title data-en="TypeDialog - Type-Safe Interactive Dialogs" data-es="TypeDialog - Diálogos Interactivos con Type-Safety" >TypeDialog</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" /><style>:root{--bg-primary:#0f0f1a;--bg-gradient-1:rgba(79,70,229,0.12);--bg-gradient-2:rgba(99,102,241,0.10);--bg-gradient-3:rgba(58,58,80,0.08);--text-primary:#ffffff;--text-secondary:#cbd5e1;--text-muted:#94a3b8;--text-dark:#64748b;--border-light:rgba(255,255,255,0.1);--accent:#4f46e5;--accent-light:#6366f1;--primary-gray:#3a3a50;}html.light-mode{--bg-primary:#f9fafb;--bg-gradient-1:rgba(79,70,229,0.06);--bg-gradient-2:rgba(99,102,241,0.05);--bg-gradient-3:rgba(58,58,80,0.03);--text-primary:#1a1a1a;--text-secondary:#374151;--text-muted:#6b7280;--text-dark:#9ca3af;--border-light:rgba(0,0,0,0.1);}*{margin:0;padding:0;box-sizing:border-box;}body{font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:var(--bg-primary);color:var(--text-primary);overflow-x:hidden;transition:background-color 0.3s ease,color 0.3s ease;}.gradient-bg{position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;background:radial-gradient(circle at 20% 40%,var(--bg-gradient-1) 0%,transparent 50%),radial-gradient(circle at 80% 70%,var(--bg-gradient-2) 0%,transparent 50%),radial-gradient(circle at 50% 90%,var(--bg-gradient-3) 0%,transparent 50%);transition:background 0.3s ease;}.language-toggle{position:fixed;top:2rem;right:2rem;z-index:100;display:flex;gap:0.5rem;background:rgba(255,255,255,0.05);border:1px solid rgba(79,70,229,0.3);border-radius:20px;padding:0.3rem;}.lang-btn{background:transparent;border:none;color:#94a3b8;padding:0.5rem 1rem;border-radius:18px;cursor:pointer;font-weight:600;font-size:0.85rem;text-transform:uppercase;transition:all 0.3s ease;font-family:"Inter",sans-serif;text-decoration:none;display:inline-block;}.lang-btn.active{background:linear-gradient(135deg,#4f46e5 0%,#6366f1 100%);color:#fff;}.lang-btn:hover{color:#4f46e5;}.theme-toggle{background:transparent;border:none;color:var(--text-primary);padding:0.5rem 1rem;border-radius:18px;cursor:pointer;font-weight:700;font-size:1.2rem;transition:all 0.3s ease;}.theme-toggle:hover{color:#4f46e5;}.container{max-width:1200px;margin:0 auto;padding:2rem;position:relative;}header{text-align:center;padding:5rem 0 4rem;animation:fadeInUp 0.8s ease-out;}@keyframes fadeInUp{from{opacity:0;transform:translateY(30px);}to{opacity:1;transform:translateY(0);}}.status-badge{display:inline-block;background:rgba(79,70,229,0.2);border:1px solid #4f46e5;color:#4f46e5;padding:0.5rem 1.5rem;border-radius:50px;font-size:0.85rem;font-weight:600;margin-bottom:1.5rem;}.logo-container{margin-bottom:2rem;text-align:center;}.logo-container img{max-width:440px;width:100%;height:auto;filter:drop-shadow(0 0 20px rgba(79,70,229,0.3));margin:0 auto;}.tagline{font-size:0.95rem;color:#4f46e5;font-weight:400;letter-spacing:0.1em;text-transform:uppercase;margin-bottom:1rem;}h1{font-size:2.8rem;font-weight:800;line-height:1.2;margin-bottom:1.5rem;background:linear-gradient(135deg,#4f46e5 0%,#6366f1 50%,#818cf8 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}.hero-subtitle{font-size:1.15rem;color:var(--text-secondary);max-width:800px;margin:0 auto 2rem;line-height:1.8;}.highlight{color:#4f46e5;font-weight:700;}.section{margin:4rem 0;animation:fadeInUp 0.8s ease-out;}.section-title{font-size:2rem;font-weight:800;margin-bottom:2rem;color:#4f46e5;text-align:center;}.section-title span{background:linear-gradient(135deg,#6366f1 0%,#4f46e5 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}.problems-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:1.5rem;margin-top:2rem;}.problem-card{background:rgba(255,255,255,0.03);border:1px solid rgba(79,70,229,0.3);border-radius:12px;padding:2rem;transition:all 0.3s ease;position:relative;overflow:hidden;}.problem-card:hover{transform:translateY(-5px);background:rgba(255,255,255,0.05);border-color:rgba(79,70,229,0.5);}.problem-number{font-size:2rem;font-weight:800;background:linear-gradient(135deg,#4f46e5 0%,#6366f1 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;line-height:1;margin-bottom:0.5rem;}.problem-card h3{color:#818cf8;font-size:1.05rem;margin-bottom:0.7rem;font-weight:700;}.problem-card p{color:var(--text-secondary);font-size:0.9rem;line-height:1.6;}.tech-stack{display:flex;flex-wrap:wrap;gap:1rem;margin-top:2rem;justify-content:center;}.tech-badge{background:rgba(79,70,229,0.15);border:1px solid #4f46e5;padding:0.5rem 1rem;border-radius:20px;font-size:0.8rem;color:#4f46e5;font-weight:600;}.features-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:2rem;margin-top:2rem;}.feature-box{background:linear-gradient(135deg,rgba(79,70,229,0.08) 0%,rgba(99,102,241,0.08) 100%);border-radius:12px;padding:2rem;border-left:4px solid #4f46e5;transition:all 0.3s ease;}.feature-box:hover{background:linear-gradient(135deg,rgba(79,70,229,0.12) 0%,rgba(99,102,241,0.12) 100%);transform:translateY(-3px);}.feature-icon{font-size:2.5rem;margin-bottom:1rem;}.feature-title{font-size:1.15rem;font-weight:700;color:#4f46e5;margin-bottom:0.7rem;}.feature-text{color:var(--text-secondary);font-size:0.95rem;line-height:1.7;}.backends-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1rem;margin-top:2rem;}.backend-item{background:rgba(79,70,229,0.08);padding:1.2rem;border-radius:8px;font-size:0.9rem;border:1px solid rgba(79,70,229,0.3);transition:all 0.2s ease;text-align:center;}.backend-item:hover{background:rgba(79,70,229,0.12);transform:translateY(-2px);}.backend-name{color:#818cf8;font-weight:700;display:block;margin-bottom:0.3rem;}.backend-role{color:var(--text-muted);font-size:0.85rem;}.cta-section{text-align:center;margin:5rem 0 3rem;padding:4rem 2rem;background:linear-gradient(135deg,rgba(79,70,229,0.08) 0%,rgba(99,102,241,0.08) 100%);border-radius:20px;border:1px solid rgba(79,70,229,0.3);}.cta-title{font-size:2rem;font-weight:800;margin-bottom:1rem;background:linear-gradient(135deg,#4f46e5 0%,#818cf8 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}.cta-button{display:inline-block;background:linear-gradient(135deg,#4f46e5 0%,#6366f1 50%,#818cf8 100%);color:#fff;padding:1.1rem 2.8rem;border-radius:50px;text-decoration:none;font-weight:800;font-size:1rem;transition:all 0.3s ease;box-shadow:0 10px 30px rgba(79,70,229,0.3);text-transform:uppercase;letter-spacing:0.05em;border:none;cursor:pointer;}.cta-button:hover{transform:translateY(-3px) scale(1.05);box-shadow:0 20px 50px rgba(79,70,229,0.5);}footer{text-align:center;padding:3rem 0 2rem;color:var(--text-dark);border-top:1px solid var(--border-light);margin-top:4rem;font-size:0.9rem;}footer p:first-child{font-weight:700;color:var(--text-muted);}footer p:last-child{margin-top:0.5rem;font-size:0.85rem;}@media (max-width:768px){h1{font-size:2rem;}.hero-subtitle{font-size:1rem;}.logo-container img{max-width:352px;}.section-title{font-size:1.6rem;}.cta-title{font-size:1.6rem;}.language-toggle{top:1rem;right:1rem;}}</style></head><body><div class="gradient-bg"></div><div class="language-toggle"><button class="lang-btn active" data-lang="en" onclick="switchLanguage('en')">EN</button><button class="lang-btn" data-lang="es" onclick="switchLanguage('es')">ES</button><button class="theme-toggle" onclick="toggleTheme()" title="Toggle light/dark mode"><span id="theme-icon">☾</span></button><a href="architecture-diagram.html" class="lang-btn" style="background: rgba(79, 70, 229, 0.2); border: 1px solid rgba(79, 70, 229, 0.5); text-decoration: none;" data-en="ARCHITECTURE" data-es="ARQUITECTURA" >ARCHITECTURE</a></div><div class="container"><header><span class="status-badge" data-en="3,818 Tests | 6 Backends | 4 LLM Providers" data-es="3.818 Tests | 6 Backends | 4 Proveedores LLM" >3,818 Tests | 6 Backends | 4 LLM Providers</span><div class="logo-container"><img id="logo-dark" src="../typedialog_logo_h.svg" alt="TypeDialog" style="display: block;" /><img id="logo-light" src="../typedialog_logo_h.svg" alt="TypeDialog" style="display: none;" /></div><p class="tagline">Typed dialogs for inputs, forms and schemas you can trust</p><h1 data-en="Create Type-Safe<br>Interactive Dialogs" data-es="Crea Diálogos Interactivos<br>con Type-Safety" >Create Type-Safe<br>Interactive Dialogs</h1><p class="hero-subtitle"><span class="highlight" data-en="Declarative forms" data-es="Formularios declarativos" >Declarative forms</span><span data-en=" with Nickel/TOML definitions, 6 backends (CLI, TUI, Web, AI, Agent, Prov-Gen), and type-safe validation. From interactive prompts to infrastructure generation." data-es=" con definiciones Nickel/TOML, 6 backends (CLI, TUI, Web, AI, Agent, Prov-Gen) y validación type-safe. Desde prompts interactivos hasta generación de infraestructura." > with Nickel/TOML definitions, 6 backends (CLI, TUI, Web, AI, Agent, Prov-Gen), and type-safe validation. From interactive prompts to infrastructure generation.</span><br><strong data-en="One schema. Every surface." data-es="Un schema. Toda superficie." >One schema. Every surface.</strong></p></header><section class="section"><h2 class="section-title"><span data-en="What TypeDialog Solves" data-es="Lo que TypeDialog Resuelve" >What TypeDialog Solves</span></h2><div class="problems-grid"><div class="problem-card"><div class="problem-number">01</div><h3 data-en="Per-Backend Code" data-es="Código Por-Backend">Per-Backend Code</h3><p data-en="One Nickel or TOML definition drives CLI, TUI, Web, and AI. No per-backend form code. The schema is the single source of truth." data-es="Una definición Nickel o TOML controla CLI, TUI, Web y AI. Sin código de formularios por backend. El schema es la fuente única de verdad." >One Nickel or TOML definition drives CLI, TUI, Web, and AI. No per-backend form code. The schema is the single source of truth.</p></div><div class="problem-card"><div class="problem-number">02</div><h3 data-en="Fail-Open Validation" data-es="Validación Fail-Open">Fail-Open Validation</h3><p data-en="Nickel contracts validate every predicate at load time. Unknown predicates cause hard failures, not silent passes. No parallel Rust reimplementation." data-es="Los contratos Nickel validan cada predicado en tiempo de carga. Predicados desconocidos causan fallos duros, no passes silenciosos. Sin reimplementación paralela en Rust." >Nickel contracts validate every predicate at load time. Unknown predicates cause hard failures, not silent passes. No parallel Rust reimplementation.</p></div><div class="problem-card"><div class="problem-number">03</div><h3 data-en="Hidden I/O in Execution" data-es="I/O Oculto en Ejecución">Hidden I/O in Execution</h3><p data-en="Fragment loading happens once at load_form(). The three-phase executor is pure — no filesystem access, no side effects during user interaction." data-es="La carga de fragmentos ocurre una vez en load_form(). El executor de tres fases es puro — sin acceso al filesystem, sin efectos secundarios durante la interacción." >Fragment loading happens once at load_form(). The three-phase executor is pure — no filesystem access, no side effects during user interaction.</p></div><div class="problem-card"><div class="problem-number">04</div><h3 data-en="Manual IaC Assembly" data-es="Ensamblaje Manual de IaC">Manual IaC Assembly</h3><p data-en="Interactive forms generate validated infrastructure configurations for 6 cloud providers. 7-layer validation pipeline from forms to final JSON." data-es="Formularios interactivos generan configuraciones de infraestructura validadas para 6 proveedores cloud. Pipeline de validación de 7 capas desde formularios hasta JSON final." >Interactive forms generate validated infrastructure configurations for 6 cloud providers. 7-layer validation pipeline from forms to final JSON.</p></div></div></section><section class="section"><h2 class="section-title"><span data-en="How It Works" data-es="Cómo Funciona" >How It Works</span></h2><div class="features-grid"><div class="feature-box"><div class="feature-icon"><></div><h3 class="feature-title" data-en="Declarative Forms" data-es="Formularios Declarativos" >Declarative Forms</h3><p class="feature-text" data-en="Define forms in Nickel (.ncl) or TOML (.toml). Nickel provides contracts, imports, and type-safe composition. TOML provides zero-dependency simplicity. Both produce identical FormDefinition structs." data-es="Define formularios en Nickel (.ncl) o TOML (.toml). Nickel provee contratos, imports y composición type-safe. TOML provee simplicidad sin dependencias. Ambos producen structs FormDefinition idénticos." >Define forms in Nickel (.ncl) or TOML (.toml). Nickel provides contracts, imports, and type-safe composition. TOML provides zero-dependency simplicity. Both produce identical FormDefinition structs.</p></div><div class="feature-box" style="border-left-color: #6366f1;"><div class="feature-icon">⚙</div><h3 class="feature-title" style="color: #6366f1;" data-en="Three-Phase Execution" data-es="Ejecución en Tres Fases" >Three-Phase Execution</h3><p class="feature-text" data-en="Phase 1: Execute selector fields that control conditionals. Phase 2: Build element list (pure, no I/O). Phase 3: Dispatch to backend with when/when_false evaluation. Complete or field-by-field rendering modes." data-es="Fase 1: Ejecutar campos selector que controlan condicionales. Fase 2: Construir lista de elementos (puro, sin I/O). Fase 3: Despachar al backend con evaluación when/when_false. Modos de renderizado completo o campo a campo." >Phase 1: Execute selector fields that control conditionals. Phase 2: Build element list (pure, no I/O). Phase 3: Dispatch to backend with when/when_false evaluation. Complete or field-by-field rendering modes.</p></div><div class="feature-box" style="border-left-color: #818cf8;"><div class="feature-icon">🔌</div><h3 class="feature-title" style="color: #818cf8;" data-en="BackendFactory" data-es="BackendFactory" >BackendFactory</h3><p class="feature-text" data-en="Compile-time feature gates (#[cfg(feature)]) eliminate dead backend code. Runtime BackendType match dispatches to Box<dyn FormBackend>. Auto-detection via TYPEDIALOG_BACKEND env var with CLI fallback." data-es="Feature gates en tiempo de compilación (#[cfg(feature)]) eliminan código muerto. Match runtime de BackendType despacha a Box<dyn FormBackend>. Auto-detección via env TYPEDIALOG_BACKEND con fallback CLI." >Compile-time feature gates (#[cfg(feature)]) eliminate dead backend code. Runtime BackendType match dispatches to Box<dyn FormBackend>. Auto-detection via TYPEDIALOG_BACKEND env var with CLI fallback.</p></div><div class="feature-box" style="border-left-color: #a855f7;"><div class="feature-icon">🤖</div><h3 class="feature-title" style="color: #a855f7;" data-en="AI & Agent Backends" data-es="Backends AI y Agent" >AI & Agent Backends</h3><p class="feature-text" data-en="AI backend with RAG, embeddings, and semantic search. Agent backend executes .agent.mdx files with multi-LLM support (Claude, OpenAI, Gemini, Ollama). Template variables, file imports, streaming output." data-es="Backend AI con RAG, embeddings y búsqueda semántica. Backend Agent ejecuta archivos .agent.mdx con soporte multi-LLM (Claude, OpenAI, Gemini, Ollama). Variables de template, imports de archivos, output en streaming." >AI backend with RAG, embeddings, and semantic search. Agent backend executes .agent.mdx files with multi-LLM support (Claude, OpenAI, Gemini, Ollama). Template variables, file imports, streaming output.</p></div><div class="feature-box" style="border-left-color: #10b981;"><div class="feature-icon">☁</div><h3 class="feature-title" style="color: #10b981;" data-en="Infrastructure Generation" data-es="Generación de Infraestructura" >Infrastructure Generation</h3><p class="feature-text" data-en="Prov-Gen transforms form answers into IaC configurations. 6 cloud providers (AWS, GCP, Azure, Hetzner, UpCloud, LXD). 7-layer validation: Forms → Constraints → Values → Validators → Schemas → Defaults → JSON." data-es="Prov-Gen transforma respuestas de formularios en configuraciones IaC. 6 proveedores cloud (AWS, GCP, Azure, Hetzner, UpCloud, LXD). Validación de 7 capas: Forms → Constraints → Values → Validators → Schemas → Defaults → JSON." >Prov-Gen transforms form answers into IaC configurations. 6 cloud providers (AWS, GCP, Azure, Hetzner, UpCloud, LXD). 7-layer validation: Forms → Constraints → Values → Validators → Schemas → Defaults → JSON.</p></div><div class="feature-box" style="border-left-color: #f59e0b;"><div class="feature-icon">🔄</div><h3 class="feature-title" style="color: #f59e0b;" data-en="Nickel Roundtrip" data-es="Roundtrip Nickel" >Nickel Roundtrip</h3><p class="feature-text" data-en="Read .ncl schemas, collect user input via any backend, generate validated .ncl output preserving contracts. ContractParser extracts validators. TemplateRenderer preserves formatting. when_false ensures all schema fields have values." data-es="Lee schemas .ncl, recolecta input del usuario via cualquier backend, genera output .ncl validado preservando contratos. ContractParser extrae validadores. TemplateRenderer preserva formato. when_false asegura que todos los campos del schema tengan valores." >Read .ncl schemas, collect user input via any backend, generate validated .ncl output preserving contracts. ContractParser extracts validators. TemplateRenderer preserves formatting. when_false ensures all schema fields have values.</p></div></div></section><section class="section"><h2 class="section-title"><span data-en="Technology Stack" data-es="Stack Tecnológico" >Technology Stack</span></h2><div class="tech-stack"><span class="tech-badge">Rust (8 crates)</span><span class="tech-badge">Nickel Contracts</span><span class="tech-badge">TOML Forms</span><span class="tech-badge">Inquire (CLI)</span><span class="tech-badge">Ratatui (TUI)</span><span class="tech-badge">Axum (Web)</span><span class="tech-badge">Fluent i18n</span><span class="tech-badge">Tera Templates</span><span class="tech-badge">Nushell Plugin</span><span class="tech-badge">Claude API</span><span class="tech-badge">OpenAI API</span><span class="tech-badge">Gemini API</span><span class="tech-badge">Ollama (local)</span><span class="tech-badge">Multi-Cloud IaC</span></div></section><section class="section"><h2 class="section-title"><span data-en="Backends" data-es="Backends" >Backends</span></h2><div class="backends-grid"><div class="backend-item"><span class="backend-name" data-en="CLI" data-es="CLI" >CLI</span><span class="backend-role" data-en="inquire — interactive prompts" data-es="inquire — prompts interactivos" >inquire — interactive prompts</span></div><div class="backend-item"><span class="backend-name" data-en="TUI" data-es="TUI" >TUI</span><span class="backend-role" data-en="ratatui — terminal UI" data-es="ratatui — interfaz de terminal" >ratatui — terminal UI</span></div><div class="backend-item"><span class="backend-name" data-en="Web" data-es="Web" >Web</span><span class="backend-role" data-en="axum — HTTP forms" data-es="axum — formularios HTTP" >axum — HTTP forms</span></div><div class="backend-item"><span class="backend-name" data-en="AI" data-es="AI" >AI</span><span class="backend-role" data-en="RAG & embeddings" data-es="RAG y embeddings" >RAG & embeddings</span></div><div class="backend-item"><span class="backend-name" data-en="Agent" data-es="Agent" >Agent</span><span class="backend-role" data-en="Multi-LLM execution" data-es="Ejecución multi-LLM" >Multi-LLM execution</span></div><div class="backend-item"><span class="backend-name" data-en="Prov-Gen" data-es="Prov-Gen" >Prov-Gen</span><span class="backend-role" data-en="IaC generation" data-es="Generación IaC" >IaC generation</span></div></div></section><div class="cta-section"><h2 class="cta-title" data-en="Type-safe dialogs for every surface" data-es="Diálogos type-safe para toda superficie" >Type-safe dialogs for every surface</h2><p style="color: #94a3b8; margin-bottom: 2rem; font-size: 1.05rem;" data-en="Built with Rust | Open Source | MIT License" data-es="Construido con Rust | Open Source | Licencia MIT" >Built with Rust | Open Source | MIT License</p><a href="https://github.com/anthropics/typedialog" class="cta-button" data-en="Explore on GitHub →" data-es="Explorar en GitHub →" >Explore on GitHub →</a></div><footer><p data-en="TypeDialog" data-es="TypeDialog">TypeDialog</p><p data-en="Typed dialogs for inputs, forms and schemas you can trust" data-es="Diálogos tipados para inputs, formularios y schemas en los que puedes confiar" >Typed dialogs for inputs, forms and schemas you can trust</p><p style="margin-top: 1rem; font-size: 0.8rem;" data-en="Multi-Backend Form Orchestration | Nickel + TOML | 6 Backends | 4 LLM Providers" data-es="Orquestación Multi-Backend de Formularios | Nickel + TOML | 6 Backends | 4 Proveedores LLM" >Multi-Backend Form Orchestration | Nickel + TOML | 6 Backends | 4 LLM Providers</p></footer></div><script> const LANG_KEY = "typedialog-lang";function getCurrentLanguage(){return localStorage.getItem(LANG_KEY)|| "en";}function switchLanguage(lang){localStorage.setItem(LANG_KEY,lang);document.querySelectorAll(".lang-btn").forEach(function(btn){btn.classList.remove("active");if(btn.dataset.lang === lang){btn.classList.add("active");}});document.querySelectorAll("[data-en][data-es]").forEach(function(el){var content = el.dataset[lang];if(el.tagName === "H1" || el.tagName === "H2" || el.tagName === "H3"){el.innerHTML = content;}else{el.textContent = content;}});document.documentElement.lang = lang;}document.addEventListener("DOMContentLoaded",function(){var currentLang = getCurrentLanguage();switchLanguage(currentLang);var currentTheme = getTheme();setTheme(currentTheme);});var THEME_KEY = "typedialog-theme";function getTheme(){return localStorage.getItem(THEME_KEY)|| "dark";}function setTheme(theme){localStorage.setItem(THEME_KEY,theme);var html = document.documentElement;var icon = document.getElementById("theme-icon");var logoDark = document.getElementById("logo-dark");var logoLight = document.getElementById("logo-light");if(theme === "light"){html.classList.add("light-mode");icon.textContent = "\u263D";if(logoDark)logoDark.style.display = "none";if(logoLight)logoLight.style.display = "block";}else{html.classList.remove("light-mode");icon.textContent = "\u2600";if(logoDark)logoDark.style.display = "block";if(logoLight)logoLight.style.display = "none";}}function toggleTheme(){var currentTheme = getTheme();var newTheme = currentTheme === "dark" ? "light" : "dark";setTheme(newTheme);}</script></body></html>
|