/**
* Dynamic navigation bar — fetches enabled apps from /api/apps and renders nav links.
* If the current page's app is disabled, redirects to home.
*
* Usage (ES Module):
* import { initNav } from '/static/shared/app-nav.js';
* initNav('omni'); // on omni page
* initNav(null); // on home page
*
* Usage (classic script):
*
*
*/
import { t } from '/static/shared/i18n-module.js';
const _NAV_SELECTOR = '.nav-links';
// Apps to hide from the global nav (still reachable by direct route).
const _NAV_HIDDEN_APP_IDS = new Set(['half_duplex_audio']);
// Map API app_id → i18n key for display name override.
const _APP_NAME_I18N = {
turnbased: 'turnbasedChat',
omni: 'omniFullDuplex',
audio_duplex: 'audioDuplexTitle',
};
async function _fetchApps() {
try {
const resp = await fetch('/api/apps');
if (!resp.ok) return null;
const data = await resp.json();
return data.apps;
} catch {
return null;
}
}
function _renderNav(apps, currentAppId) {
const navEl = document.querySelector(_NAV_SELECTOR);
if (!navEl) return;
const homeActive = !currentAppId ? ' class="active"' : '';
const extras = `${t.mobile}`;
const links = apps
.filter(a => !_NAV_HIDDEN_APP_IDS.has(a.app_id))
.map(a => {
const active = a.app_id === currentAppId ? ' class="active"' : '';
const i18nKey = _APP_NAME_I18N[a.app_id];
const label = (i18nKey && t[i18nKey]) || a.name;
return `${label}`;
});
navEl.innerHTML = `${t.home}` + extras + links.join('');
}
/**
* Initialize the navigation bar.
* @param {string|null} currentAppId - The app_id of the current page, or null for home.
* @returns {Promise} The list of enabled apps, or null on failure.
*/
async function initNav(currentAppId) {
const apps = await _fetchApps();
if (!apps) return null;
if (currentAppId && !apps.find(a => a.app_id === currentAppId)) {
window.location.href = '/';
return null;
}
_renderNav(apps, currentAppId);
return apps;
}
// Support both ES Module and classic script usage
if (typeof window !== 'undefined') {
window.AppNav = { init: initNav };
}
export { initNav };