diff --git a/src/platform/env.js b/src/platform/env.js
index 187e0f3..60b1266 100644
--- a/src/platform/env.js
+++ b/src/platform/env.js
@@ -84,6 +84,7 @@ export const CONFIG_KEYS = {
BRAND_LOGO: 'BRAND_LOGO',
THEME_COLOR: 'THEME_COLOR',
UI_SHELL: 'UI_SHELL',
+ INITIAL_ROUTE: 'INITIAL_ROUTE',
STORAGE_BACKEND: 'STORAGE_BACKEND',
API_BASE_URL: 'API_BASE_URL',
MODULES: 'MODULES',
@@ -152,6 +153,7 @@ export function initEnv(appConfig) {
BRAND_LOGO: appConfig.brand_logo || appConfig.brandLogo || appConfig.icons?.[0]?.src || '/favicon.svg',
THEME_COLOR: appConfig.theme_color || appConfig.themeColor || '#000000',
UI_SHELL: appConfig.ui_shell || appConfig.uiShell || 'EmptyShell',
+ INITIAL_ROUTE: appConfig.initial_route || appConfig.initialRoute || appConfig.ui?.initial_route || appConfig.ui?.initialRoute || '/home',
STORAGE_BACKEND: appConfig.storage?.backend || 'localStorage',
API_BASE_URL: resolvedApiBaseURL,
MODULES: appConfig.modules || [],
diff --git a/src/ui/App.jsx b/src/ui/App.jsx
index b402e4a..70e6cab 100644
--- a/src/ui/App.jsx
+++ b/src/ui/App.jsx
@@ -6,7 +6,13 @@
import React, { createContext, useContext, useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { TamaguiProvider, Theme, createTamagui, YStack } from 'tamagui';
-import { clearPWACache, getServiceWorkerStatus } from '../platform/sw-register.js';
+import {
+ clearPWACache,
+ clearAllCaches,
+ clearAllStorage,
+ getServiceWorkerStatus,
+ unregisterAllServiceWorkers
+} from '../platform/sw-register.js';
import { getProvider } from '../platform/storage.js';
import * as apiClient from '../platform/api.js';
import * as storageModuleRef from '../platform/storage.js';
@@ -14,6 +20,7 @@ import * as menuRef from '../platform/menu.js';
import * as envModuleRef from '../platform/env.js';
import { getConfig, setConfig, CONFIG_KEYS, createLogger, startTrace, isDevelopment } from '../platform/env.js';
import { EmptyShell, LandingShell, DashboardShell, AppInfo, Router } from './components/index.js';
+import { resolveRegisteredShell } from './components/shell-registry.js';
import { LoginPage } from '../security/pages/LoginPage.jsx';
import { getStyleTheme, DEFAULT_STYLE_THEME, normalizeStyleThemeName, setActiveStyleThemeName } from './styles/index.js';
import { THEME_MODE_CONFIG_KEY, THEME_NAME_CONFIG_KEY, THEME_MODES, themeManager } from './theme-controller.js';
@@ -48,8 +55,11 @@ function resolveShellComponent(shellName = 'EmptyShell') {
case 'dashboardshell':
return DashboardShell;
case 'emptyshell':
- default:
return EmptyShell;
+ default: {
+ const registeredShell = resolveRegisteredShell(shellName);
+ return registeredShell || EmptyShell;
+ }
}
}
@@ -84,6 +94,9 @@ function App({
const [menuItems, setMenuItems] = useState([]);
const [initialized, setInitialized] = useState(false);
const [ShellComponent, setShellComponent] = useState(() => resolveShellComponent(initialProfile?.ui_shell ?? 'EmptyShell'));
+ const [initialRoute, setInitialRoute] = useState(
+ initialProfile?.initial_route ?? initialProfile?.initialRoute ?? initialProfile?.ui?.initial_route ?? initialProfile?.ui?.initialRoute ?? '/home'
+ );
const [bootResult, setBootResult] = useState(null);
const [bootModeOverride, setBootModeOverride] = useState(null);
@@ -282,6 +295,9 @@ function App({
const Shell = resolveShellComponent(shellName);
setShellComponent(() => Shell);
appLogger.log(`Using shell: ${shellName}`);
+
+ const configuredInitialRoute = await services.env.getConfig(CONFIG_KEYS.INITIAL_ROUTE, '/home');
+ setInitialRoute(configuredInitialRoute || '/home');
// Get menu items from primary menu
if (services.menu) {
@@ -405,7 +421,7 @@ function App({
appContent = ;
} else if (!shouldRenderBootScreen && !shouldHoldDuringInit) {
appContent = (
-
+
{/* Declarative route registration (commented out - routes now registered programmatically via modules)
@@ -520,18 +536,9 @@ if (typeof window !== 'undefined') {
window.clearPWACache = clearPWACache;
window.__PWA_UTILS__ = {
clearPWACache,
- clearAllCaches: async () => {
- const { clearAllCaches } = await import('../platform/sw-register.js');
- return clearAllCaches();
- },
- unregisterAllServiceWorkers: async () => {
- const { unregisterAllServiceWorkers } = await import('../platform/sw-register.js');
- return unregisterAllServiceWorkers();
- },
- clearAllStorage: async () => {
- const { clearAllStorage } = await import('../platform/sw-register.js');
- return clearAllStorage();
- }
+ clearAllCaches,
+ unregisterAllServiceWorkers,
+ clearAllStorage
};
console.log('💡 PWA Utilities available:');
diff --git a/src/ui/components/IconMapper.jsx b/src/ui/components/IconMapper.jsx
index 618eec2..44c2e32 100644
--- a/src/ui/components/IconMapper.jsx
+++ b/src/ui/components/IconMapper.jsx
@@ -394,6 +394,7 @@ const iconMap = {
'wifi': wrap(WifiHigh, 'WifiHigh'),
'wifi-off': wrap(WifiSlash, 'WifiSlash'),
'lightning': wrap(Lightning, 'Lightning'),
+ 'game': wrap(Lightning, 'Lightning'),
// ── Volume ─────────────────────────────────────────────────────────────
'volume-up': wrap(SpeakerHigh, 'SpeakerHigh'),
diff --git a/src/ui/components/index.js b/src/ui/components/index.js
index 7ca9a8f..d01e562 100644
--- a/src/ui/components/index.js
+++ b/src/ui/components/index.js
@@ -25,6 +25,7 @@ export { Panel, default as PanelDefault } from './Panel.jsx';
export { SettingsPanel, default as SettingsPanelDefault } from './SettingsPanel.jsx';
export { GeneralConfig, default as GeneralConfigDefault } from './GeneralConfig.jsx';
export { IdentityConfig, default as IdentityConfigDefault } from './IdentityConfig.jsx';
+export { registerShell, unregisterShell, resolveRegisteredShell, listRegisteredShells, clearRegisteredShells } from './shell-registry.js';
export * from './grid/index.js';
export { getTypographyRoleProps, getStyleTypography, TYPOGRAPHY_ROLE_KEYS } from '../styles/index.js';
diff --git a/src/ui/components/shell-registry.js b/src/ui/components/shell-registry.js
new file mode 100644
index 0000000..25bad64
--- /dev/null
+++ b/src/ui/components/shell-registry.js
@@ -0,0 +1,49 @@
+const SHELL_REGISTRY_KEY = '__bface_shell_registry__';
+
+function getShellRegistry() {
+ const scope = typeof globalThis !== 'undefined' ? globalThis : window;
+ if (!scope[SHELL_REGISTRY_KEY]) {
+ scope[SHELL_REGISTRY_KEY] = new Map();
+ }
+ return scope[SHELL_REGISTRY_KEY];
+}
+
+function normalizeShellName(name = '') {
+ return String(name).trim().toLowerCase();
+}
+
+export function registerShell(name, component) {
+ const key = normalizeShellName(name);
+ if (!key || typeof component !== 'function') {
+ return false;
+ }
+
+ getShellRegistry().set(key, component);
+ return true;
+}
+
+export function unregisterShell(name) {
+ const key = normalizeShellName(name);
+ if (!key) {
+ return false;
+ }
+
+ return getShellRegistry().delete(key);
+}
+
+export function resolveRegisteredShell(name) {
+ const key = normalizeShellName(name);
+ if (!key) {
+ return null;
+ }
+
+ return getShellRegistry().get(key) || null;
+}
+
+export function listRegisteredShells() {
+ return Array.from(getShellRegistry().keys());
+}
+
+export function clearRegisteredShells() {
+ getShellRegistry().clear();
+}