window.renderWebuiHeader = async function renderWebuiHeader() { const mount = document.getElementById('webui-header'); if (!mount || mount.children.length) return; const scriptVersion = (() => { try { const script = document.querySelector('script[src*="/static/js/webui-header.js"]'); if (!script) return 'v1'; return new URL(script.src, window.location.href).searchParams.get('v') || 'v1'; } catch { return 'v1'; } })(); const HEADER_HTML_CACHE_KEY = `grok2api.webui_header_html.${scriptVersion}`; const META_VERSION_CACHE_KEY = `grok2api.meta_version.${scriptVersion}`; let appVersion = ''; const readSessionCache = (key) => { try { return sessionStorage.getItem(key) || ''; } catch { return ''; } }; const writeSessionCache = (key, value) => { if (!value) return; try { sessionStorage.setItem(key, value); } catch {} }; const languageCodes = { zh: 'CN', en: 'EN', ja: 'JA', es: 'ES', de: 'DE', fr: 'FR', }; const initLanguageMenu = () => { const menu = mount.querySelector('#hd-lang-menu'); const trigger = mount.querySelector('#hd-lang-trigger'); const code = mount.querySelector('#hd-lang-code'); const options = Array.from(mount.querySelectorAll('.admin-lang-option')); if (!menu || !trigger || !code || !options.length) return; const close = () => { menu.classList.remove('open'); trigger.setAttribute('aria-expanded', 'false'); }; const sync = () => { const current = window.I18n?.getLang?.() || localStorage.getItem('grok2api_lang') || 'zh'; code.textContent = languageCodes[current] || current.toUpperCase(); options.forEach((option) => { option.classList.toggle('active', option.dataset.lang === current); }); }; trigger.addEventListener('click', (event) => { event.stopPropagation(); const open = !menu.classList.contains('open'); menu.classList.toggle('open', open); trigger.setAttribute('aria-expanded', open ? 'true' : 'false'); }); options.forEach((option) => { option.addEventListener('click', () => { const lang = option.dataset.lang; if (!lang) return; close(); if (window.I18n?.setLang) { I18n.setLang(lang); } else { localStorage.setItem('grok2api_lang', lang); location.reload(); } }); }); document.addEventListener('click', (event) => { const target = event.target; if (!(target instanceof Node) || !menu.contains(target)) close(); }); document.addEventListener('keydown', (event) => { if (event.key === 'Escape') close(); }); sync(); return sync; }; const applyHeaderI18n = () => { if (window.I18n?.apply) I18n.apply(mount); const trigger = mount.querySelector('#hd-lang-trigger'); if (trigger) { const label = window.t ? t('header.languageLabel') : 'Language'; trigger.title = label; trigger.setAttribute('aria-label', label); } const logout = mount.querySelector('#hd-logout'); if (logout) { const hidden = mount.dataset.hideLogout === 'true'; logout.hidden = hidden; if (!hidden) { const label = window.t ? t('header.logout') : 'Logout'; logout.title = label; logout.setAttribute('aria-label', label); } } }; const loadVersion = async () => { const cachedVersion = window.__grok2apiMetaVersion || readSessionCache(META_VERSION_CACHE_KEY); if (cachedVersion) { appVersion = String(cachedVersion).trim(); window.__grok2apiMetaVersion = appVersion; return; } try { const res = await fetch('/meta'); if (!res.ok) throw new Error('meta unavailable'); const data = await res.json(); appVersion = String(data?.version || '').trim(); window.__grok2apiMetaVersion = appVersion; writeSessionCache(META_VERSION_CACHE_KEY, appVersion); } catch { appVersion = ''; } }; const applyVersion = () => { const right = mount.querySelector('.admin-header-right'); if (!right) return; let node = mount.querySelector('#hd-version'); if (!appVersion) { node?.remove(); return; } if (!node) { node = document.createElement('span'); node.id = 'hd-version'; node.className = 'admin-header-version'; right.insertBefore(node, right.firstChild); } const value = `v${appVersion}`; node.textContent = value; node.title = value; }; await loadVersion(); try { const cachedHtml = window.__grok2apiWebuiHeaderHtml || readSessionCache(HEADER_HTML_CACHE_KEY); if (cachedHtml) { mount.innerHTML = cachedHtml; } else { const res = await fetch('/static/webui/header.html'); if (!res.ok) throw new Error('header unavailable'); const html = await res.text(); mount.innerHTML = html; window.__grok2apiWebuiHeaderHtml = html; writeSessionCache(HEADER_HTML_CACHE_KEY, html); } } catch { mount.innerHTML = `
`; } const active = mount.dataset.active || location.pathname; mount.querySelectorAll('[data-nav]').forEach((link) => { link.classList.toggle('active', link.dataset.nav === active); }); const syncLanguageMenu = initLanguageMenu(); applyHeaderI18n(); applyVersion(); syncLanguageMenu?.(); };