// Frontend plugin registry. // Plugin modules call window.Naut.registerPlugin({...}) after they are imported. const registry = { plugins: [], views: [], sidebarSections: [], detailTabs: [], detailPanels: [], }; let sharedContext = {}; function normalizeList(value) { return Array.isArray(value) ? value : []; } function registerPlugin(plugin) { if (!plugin || !plugin.id || registry.plugins.some((p) => p.id === plugin.id)) return; registry.plugins.push(plugin); for (const view of normalizeList(plugin.views)) registry.views.push({ plugin, ...view }); for (const section of normalizeList(plugin.sidebarSections)) registry.sidebarSections.push({ plugin, ...section }); for (const tab of normalizeList(plugin.detailTabs)) registry.detailTabs.push({ plugin, ...tab }); for (const panel of normalizeList(plugin.detailPanels)) registry.detailPanels.push({ plugin, ...panel }); } window.Naut = { ...(window.Naut || {}), registerPlugin, plugins: registry, context: () => sharedContext, }; export async function loadPlugins(api, context) { sharedContext = context || {}; let manifest; try { manifest = await api.plugins(); } catch { manifest = { modules: [] }; } for (const modulePath of manifest.modules || []) { try { await import(modulePath); } catch (e) { console.error(`Failed to load plugin ${modulePath}`, e); } } return registry.plugins; } export function pluginViews() { return registry.views.filter((v) => v.id && v.label && typeof v.render === 'function'); } export function getPluginView(id) { return pluginViews().find((v) => v.id === id); } export function renderPluginView(id, host, context) { const view = getPluginView(id); if (!view) return false; view.render(host, context); return true; } export function renderPluginSidebarSections(context) { return registry.sidebarSections .filter((s) => s.id && s.title && typeof s.render === 'function') .map((s) => `