"use client"; import type { ComponentType, ReactNode } from "react"; import { PageShell } from "@/components/page-shell"; import { lookupPluginIcon } from "@/lib/plugins/icons"; import { pluginRegistry } from "@/lib/plugins/registry"; import type { PluginRouteRegistration } from "@/lib/plugins/registry"; import type { NavItem, PluginIcon, PluginPageChrome } from "@/lib/plugins/types"; export interface ResolvedPageChrome { title: string; subtitle?: string; icon?: PluginIcon; backHref?: string; backLabel?: string; Actions?: ComponentType; } /** * Resolves the topbar chrome for a plugin route registration * (`registerRoute(path, Component, { topbar })`). Returns null when the * plugin opted out (`topbar: false`). Title fallback order: explicit chrome * title → the plugin's nav-item label registered for this path → the * plugin's display name → the route path. The icon similarly falls back to * the matching nav item's icon. */ export function resolvePluginPageChrome( registration: PluginRouteRegistration, navItems: NavItem[], pluginName: string | undefined, ): ResolvedPageChrome | null { const topbar = registration.options?.topbar ?? true; if (topbar === false) return null; const chrome: PluginPageChrome = topbar === true ? {} : topbar; const navItem = navItems.find((item) => item.path === registration.path); return { title: chrome.title ?? navItem?.label ?? pluginName ?? registration.path, subtitle: chrome.subtitle, icon: chrome.icon ?? navItem?.icon, backHref: chrome.backHref, backLabel: chrome.backLabel, Actions: chrome.actions, }; } type PluginPageFrameProps = { registration: PluginRouteRegistration; children: ReactNode; }; /** * Wraps a plugin-registered route in the shared `PageShell` (topbar + nav * trigger + scrollable content), matching first-party pages. Renders the * children bare (full-bleed) when the registration opted out via * `topbar: false`. */ export function PluginPageFrame({ registration, children }: PluginPageFrameProps) { const chrome = resolvePluginPageChrome( registration, pluginRegistry.getNavItems(), pluginRegistry.getPluginName(registration.pluginId), ); if (!chrome) return <>{children}; const Icon = lookupPluginIcon(chrome.icon); const Actions = chrome.Actions; return ( : undefined} backHref={chrome.backHref} backLabel={chrome.backLabel} actions={Actions ? : undefined} contentClassName="bg-background" > {children} ); }