"use client"; /** * SlotRenderer - 按 Config 渲染插件列表(v2.1 M8) * * 给定一个 slot 名称,从配置中过滤出该 slot 的可见插件,按 sortOrder 排序, * 查 Registry 取组件,注入 PluginProps,经 PluginLoader 挂载(含 ErrorBoundary)。 * * 关联:portal-shell spec §2.2、§5.1 */ import { useMemo, type ReactNode } from "react"; import { REGISTRY, isPluginRegistered } from "./Registry"; import { PluginLoader, PluginSkeleton } from "./PluginLoader"; import { parsePropsJson, parseSizeJson } from "./PropsMerger"; import type { PluginPlacement, PluginProps, Role } from "@/lib/types"; import type { AuthUser } from "@/providers/AuthProvider"; export interface SlotRendererProps { slotName: string; layoutId: string; plugins: PluginPlacement[]; user: AuthUser; role: Role; userId: string; } export function SlotRenderer({ slotName, layoutId, plugins, user, role, userId, }: SlotRendererProps): ReactNode { const visible = useMemo( () => plugins .filter((p) => p.slot === slotName && p.isVisible) .sort((a, b) => a.sortOrder - b.sortOrder), [plugins, slotName], ); if (visible.length === 0) { // 空 slot:main 区显示占位,topbar/side 区不渲染 if (slotName === "top" || slotName === "side") return null; return (