import type { LayoutNode, AppRegistryMeta } from 'officerdev'; import { WorkspaceView } from 'officerdev'; import { useAuthoredLayout } from 'state/useAuthoredLayout'; // The screen every plugin route renders. THE PLUGIN DOES NOT RENDER A SCREEN. // // ── The rule, and why it is shape rather than policy ── // // Every plugin route renders a Workspace with at least one panel. A plugin that exported a component // could render anything at all — a bare div, a full-page form, its own navigation — and the platform // would be a shell hosting strangers' layouts rather than one application. So a plugin does not get to // render the screen: it contributes panels and says how they are arranged, and this renders the // Workspace around them. // // Non-compliance is therefore not refused, it is unrepresentable. There is nowhere to put a screen. // // `locked`, like every core screen: a plugin's layout is its author's design, not a workspace the user // rearranges — and `appTypes.allowed` pins it to that plugin's own panels, so a persisted layout naming // something else falls back rather than rendering another plugin's panel inside this one. export function PluginScreen({ screenId, panels, layout, }: { /** * This SCREEN's id, not the plugin's — `caldav/calendar`, not `caldav`. * * A plugin with two screens has two of these, and they must differ: the id is both the layout * persistence key and the owner of the panel allow-list below, so sharing one would have Calendar and * Contacts persisting over each other's sizes and each accepting the other's panels. */ screenId: string; panels: AppRegistryMeta[]; layout: LayoutNode; }) { // Per-user and per-plugin, so two plugins never share a layout and a user's arrangement is their own. // // `useAuthoredLayout`, not `useDashboardState`: the STRUCTURE here is the plugin author's and comes from // `layout` on every render, while only the sizes are the user's and persisted. That is what lets a plugin // ship a new layout and have it reach people who have already opened the screen — and it is what stops // the `appTypes` repair below from being written back as though the user had chosen it. See the hook. const workspace = useAuthoredLayout(`screens/plugin/${screenId}`, layout); const allowed = panels.map((panel) => panel.key); return (
); }