/**
* Single bundle for all plugin admin screens: persistent top bar + client-side
* navigation so only the main content lazy-loads when switching submenu items.
*/
import React, { Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { TopBar } from './components/top-bar';
import { PromoModal } from './components/promo-modal';
import { NavSidebar } from './components/nav-sidebar';
import { ScreenLoader } from './components/screen-loader';
import { trackSidebarToggled } from './lib/mixpanel';
import { __ } from './lib/i18n';
import {
applyAdminShellBootstrap,
applyShellGlobalsFromWindow,
fetchAdminShellBootstrap,
} from './hooks/use-bootstrap';
import {
lazyAdminPage,
preloadAdminPage,
type AdminPageEntry,
} from './lib/lazy-admin-page';
import './index.css';
const SIDEBAR_STORAGE_KEY = 'llmagnet_sidebar_collapsed';
const FADE_MS = 150;
/**
* Catches render errors from any admin page so a single broken screen shows a
* recoverable fallback instead of blanking the whole admin area.
*/
class AdminShellErrorBoundary extends React.Component<
{ children: React.ReactNode },
{ hasError: boolean; message?: string }
> {
state: { hasError: boolean; message?: string } = { hasError: false };
static getDerivedStateFromError(err: Error) {
return { hasError: true, message: err.message };
}
componentDidCatch(err: Error, info: React.ErrorInfo) {
console.error('[LLMagnet] admin screen render error', err, info.componentStack);
}
render() {
if (this.state.hasError) {
return (
{__('Something went wrong loading this screen.', 'llmagnet-llm-txt-generator')}
{this.state.message && (
{this.state.message}
)}
);
}
return this.props.children;
}
}
function readSidebarCollapsed(): boolean {
try {
const stored = localStorage.getItem(SIDEBAR_STORAGE_KEY);
if (stored !== null) return stored === '1';
} catch {
// ignore
}
return false; // expanded (open) by default
}
function writeSidebarCollapsed(value: boolean): void {
try {
localStorage.setItem(SIDEBAR_STORAGE_KEY, value ? '1' : '0');
} catch {
// ignore
}
}
(window as unknown as { __LLMAGNET_SHELL_ACTIVE__?: boolean }).__LLMAGNET_SHELL_ACTIVE__ = true;
const PLUGIN_WP_PAGES = new Set([
'llmagnet-ai-seo-optimizer',
'llmagnet-ai-seo-overview',
'llmagnet-ai-seo-pages',
'llmagnet-ai-seo-bot-analytics',
'llmagnet-ai-seo-connections',
'llmagnet-ai-seo-reports',
'llmagnet-ai-seo-content-settings',
'llmagnet-ai-seo-agent-ready',
'llmagnet-ai-seo-mcp',
'llmagnet-ai-seo-system-information',
]);
function parsePluginPageFromHref(href: string): string | null {
try {
const u = new URL(href, window.location.origin);
const page = u.searchParams.get('page');
if (!page || !PLUGIN_WP_PAGES.has(page)) {
return null;
}
return page;
} catch {
return null;
}
}
function getPluginUrl(): string {
const w = window as unknown as {
llmagnetDashboardData?: { pluginUrl?: string };
llmagnetPagesData?: { pluginUrl?: string };
llmagnetBotAnalyticsData?: { pluginUrl?: string };
llmagnetReportsData?: { pluginUrl?: string };
llmagnetContentSettingsData?: { pluginUrl?: string };
llmagnetAgentReadyData?: { pluginUrl?: string };
llmagnetConnectionsData?: { pluginUrl?: string };
llmagnetMcpData?: { pluginUrl?: string };
llmagnetSystemInfoData?: { pluginUrl?: string };
};
return (
w.llmagnetDashboardData?.pluginUrl ||
w.llmagnetPagesData?.pluginUrl ||
w.llmagnetBotAnalyticsData?.pluginUrl ||
w.llmagnetReportsData?.pluginUrl ||
w.llmagnetContentSettingsData?.pluginUrl ||
w.llmagnetAgentReadyData?.pluginUrl ||
w.llmagnetConnectionsData?.pluginUrl ||
w.llmagnetMcpData?.pluginUrl ||
w.llmagnetSystemInfoData?.pluginUrl ||
''
);
}
function syncWpAdminSubmenuCurrent(pageSlug: string): void {
const menu = document.getElementById('adminmenu');
if (!menu) {
return;
}
menu.querySelectorAll('li.wp-has-submenu').forEach((li) => {
li.classList.remove('wp-has-current-submenu');
li.classList.add('wp-not-current-submenu');
});
menu.querySelectorAll('ul.wp-submenu li').forEach((li) => {
li.classList.remove('current');
});
const link = menu.querySelector(`a[href*="page=${pageSlug}"]`);
if (!link) {
return;
}
const item = link.closest('li');
if (item) {
item.classList.add('current');
}
const top = link.closest('.wp-has-submenu');
if (top) {
top.classList.remove('wp-not-current-submenu');
top.classList.add('wp-has-current-submenu');
}
}
const ADMIN_PAGE_ENTRIES: Record = {
'llmagnet-ai-seo-optimizer': {
file: 'dashboard',
exportName: 'DashboardApp',
devImport: () => import('./dashboard-main'),
},
'llmagnet-ai-seo-overview': {
file: 'overview',
exportName: 'OverviewApp',
devImport: () => import('./overview-main'),
},
'llmagnet-ai-seo-pages': {
file: 'pages',
exportName: 'PagesApp',
devImport: () => import('./pages-main'),
},
'llmagnet-ai-seo-bot-analytics': {
file: 'bot-analytics',
exportName: 'BotAnalyticsApp',
devImport: () => import('./bot-analytics-main'),
},
'llmagnet-ai-seo-connections': {
file: 'connections',
exportName: 'ConnectionsApp',
devImport: () => import('./connections-main'),
},
'llmagnet-ai-seo-reports': {
file: 'reports',
exportName: 'ReportsApp',
// Pro owns this screen. Production loads pro/assets via pageScripts.
// A variable specifier keeps Vite from copying the page into the free bundle.
devImport: () => {
const specifier = './pro/reports-main';
return import(/* @vite-ignore */ specifier);
},
},
'llmagnet-ai-seo-content-settings': {
file: 'content-settings',
exportName: 'ContentSettingsApp',
devImport: () => import('./content-settings-main'),
},
'llmagnet-ai-seo-agent-ready': {
file: 'agent-ready',
exportName: 'AgentReadyApp',
devImport: () => import('./agent-ready-main'),
},
'llmagnet-ai-seo-mcp': {
file: 'mcp',
exportName: 'McpApp',
devImport: () => import('./mcp-main'),
},
'llmagnet-ai-seo-system-information': {
file: 'system-information',
exportName: 'SystemInformationApp',
devImport: () => import('./system-information-main'),
},
};
const PAGE_COMPONENTS: Record> =
Object.fromEntries(
Object.entries(ADMIN_PAGE_ENTRIES).map(([slug, entry]) => [slug, lazyAdminPage(slug, entry)]),
);
const PAGE_PRELOADERS: Record Promise> = Object.fromEntries(
Object.entries(ADMIN_PAGE_ENTRIES).map(([slug, entry]) => [
slug,
() => preloadAdminPage(slug, entry),
]),
);
function AdminShellApp() {
const initial = window.llmagnetAdminShell?.wpPage || '';
const [wpPage, setWpPage] = useState(initial);
const wpPageRef = useRef(initial);
const [contentVisible, setContentVisible] = useState(true);
const navLockRef = useRef(false);
const [sidebarCollapsed, setSidebarCollapsed] = useState(readSidebarCollapsed);
const [isMobileWidth, setIsMobileWidth] = useState(() => window.innerWidth < 750);
useEffect(() => {
const onResize = () => setIsMobileWidth(window.innerWidth < 750);
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
// Effective collapsed: always collapsed on narrow screens, user preference otherwise.
const effectiveCollapsed = isMobileWidth || sidebarCollapsed;
const handleSidebarToggle = useCallback(() => {
setSidebarCollapsed((prev) => {
const next = !prev;
writeSidebarCollapsed(next);
trackSidebarToggled(next ? 'collapsed' : 'expanded');
return next;
});
}, []);
const pluginUrl = useMemo(() => getPluginUrl(), [wpPage]);
const navigateTo = useCallback(
async (target: string, pushUrl: string | null) => {
if (!target || target === wpPageRef.current) {
document.documentElement.classList.remove('llmagnet-nav');
return;
}
if (navLockRef.current) return;
navLockRef.current = true;
try {
setContentVisible(false);
const preload = PAGE_PRELOADERS[target];
const [payload] = await Promise.all([
fetchAdminShellBootstrap(target),
preload ? preload().catch(() => {}) : Promise.resolve(),
new Promise((r) => setTimeout(r, FADE_MS)),
]);
applyAdminShellBootstrap(payload);
wpPageRef.current = target;
setWpPage(target);
if (pushUrl) {
window.history.pushState({ llmagnetWpPage: target }, '', pushUrl);
}
syncWpAdminSubmenuCurrent(target);
await new Promise((resolve) => {
let resolved = false;
const done = () => {
if (!resolved) {
resolved = true;
resolve();
}
};
requestAnimationFrame(() => requestAnimationFrame(done));
setTimeout(done, 80);
});
setContentVisible(true);
} catch (e) {
console.error('[LLMagnet] admin-shell navigation failed, falling back to full load', e);
setContentVisible(true);
if (pushUrl) {
window.location.assign(pushUrl);
} else {
window.location.assign(`admin.php?page=${encodeURIComponent(target)}`);
}
} finally {
navLockRef.current = false;
document.documentElement.classList.remove('llmagnet-nav');
}
},
[],
);
useEffect(() => {
syncWpAdminSubmenuCurrent(wpPageRef.current);
}, []);
useEffect(() => {
const onPopState = () => {
const slug = parsePluginPageFromHref(window.location.href);
if (slug && slug !== wpPageRef.current) {
void navigateTo(slug, null);
}
};
window.addEventListener('popstate', onPopState);
return () => window.removeEventListener('popstate', onPopState);
}, [navigateTo]);
useEffect(() => {
const onClick = (e: MouseEvent) => {
if (e.defaultPrevented || e.button !== 0) {
return;
}
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
return;
}
const el = (e.target as HTMLElement).closest('a[href]');
if (!el) {
return;
}
if (el.getAttribute('target') === '_blank' || el.hasAttribute('download')) {
return;
}
const hrefAttr = el.getAttribute('href') || '';
if (!hrefAttr || hrefAttr.startsWith('#') || /^(blob|data):/i.test(hrefAttr)) {
return;
}
const pageSlug = parsePluginPageFromHref(el.href);
if (!pageSlug) {
return;
}
e.preventDefault();
void navigateTo(pageSlug, el.href);
};
document.addEventListener('click', onClick, true);
return () => document.removeEventListener('click', onClick, true);
}, [navigateTo]);
const handleNavigate = useCallback(
(slug: string) => {
const adminUrl = `${window.location.pathname}?page=${encodeURIComponent(slug)}`;
if (!PAGE_COMPONENTS[slug]) {
window.location.assign(adminUrl);
return;
}
void navigateTo(slug, adminUrl);
},
[navigateTo],
);
const Page = PAGE_COMPONENTS[wpPage];
return (
}>
{Page ? (
) : (
{__('Unknown admin page.', 'llmagnet-llm-txt-generator')}
)}
);
}
applyShellGlobalsFromWindow();
const container = document.getElementById('llms-admin-shell-root');
if (container) {
try {
const root = createRoot(container);
root.render();
window.dispatchEvent(new CustomEvent('llmagnet:admin-shell-mounted'));
if (typeof console !== 'undefined' && console.info) {
console.info('[LLMagnet] Admin shell mounted', {
wpPage: window.llmagnetAdminShell?.wpPage,
assetsBaseUrl: window.llmagnetAdminShell?.assetsBaseUrl,
});
}
} catch (err) {
console.error('[LLMagnet] Admin shell mount error', err);
container.innerHTML =
'' +
'LLMagnet admin UI failed to start.
' +
(err instanceof Error ? err.message : String(err)) +
' ';
}
} else {
console.error('[LLMagnet] Missing #llms-admin-shell-root');
}