/** * 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'); }