{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "api-core-plate-controller-docs",
  "title": "Plate Controller",
  "description": "API reference for PlateController.",
  "files": [
    {
      "path": "../../content/docs/api/core/plate-controller.mdx",
      "content": "---\ntitle: Plate Controller\ndescription: API reference for PlateController.\n---\n\n`PlateController` lets UI outside a single `<Plate>` subtree read the active editor store. Use it for shared toolbars, side panels, inspectors, and multi-editor shells.\n\n## Quick Use\n\nWrap the shared UI and all editors in `PlateController`. `PlateContent` registers each mounted editor store through `PlateControllerEffect`.\n\n```tsx title=\"components/editor-shell.tsx\" showLineNumbers\nimport {\n  Plate,\n  PlateContent,\n  PlateController,\n  usePlateEditor,\n} from 'platejs/react';\n\nexport function EditorShell() {\n  return (\n    <PlateController>\n      <ActiveEditorLabel />\n      <MainEditor />\n      <SecondaryEditor />\n    </PlateController>\n  );\n}\n\nfunction MainEditor() {\n  const editor = usePlateEditor({ id: 'main' });\n\n  return (\n    <Plate editor={editor}>\n      <PlateContent />\n    </Plate>\n  );\n}\n\nfunction SecondaryEditor() {\n  const editor = usePlateEditor({ id: 'secondary' });\n\n  return (\n    <Plate editor={editor} primary={false}>\n      <PlateContent />\n    </Plate>\n  );\n}\n```\n\n`primary` belongs on `Plate`, not on `createPlateEditor` or `usePlateEditor`.\n\n## Active Editor Lookup\n\nHooks such as `useEditorRef()` and `useEditorMounted()` normally read the nearest `Plate` store. Inside `PlateController`, the same hooks can resolve a store outside a specific editor tree.\n\n| Lookup | Behavior |\n|--------|----------|\n| `useEditorRef('main')` | Resolves the store registered for `main`. |\n| `useEditorRef()` | Resolves the active editor store, then the first mounted primary editor store. |\n| Missing store with controller | Returns the fallback store, so `useEditorRef()` returns a fallback editor. |\n| Missing store without controller | Throws `Plate hooks must be used inside a Plate or PlateController`. |\n\nController lookup order without an explicit ID:\n\n1. `activeId`\n2. each ID in `primaryEditorIds`\n3. fallback store when no store is available\n\n## Fallback Editors\n\nThe fallback editor exists so read-only UI can render while no editor is active. It is not safe for transforms.\n\n```tsx title=\"components/active-editor-label.tsx\"\nimport { useEditorMounted, useEditorRef } from 'platejs/react';\n\nexport function ActiveEditorLabel() {\n  const editor = useEditorRef();\n  const mounted = useEditorMounted();\n\n  if (!mounted || editor.meta.isFallback) {\n    return <p>No editor selected.</p>;\n  }\n\n  return <p>Active editor: {editor.id}</p>;\n}\n```\n\n<Callout type=\"warning\" title=\"Guard transforms\">\n  Check `useEditorMounted(id?)` or `!editor.meta.isFallback` before running\n  transforms from UI that lives under `PlateController`.\n</Callout>\n\n## Registration\n\n`PlateControllerEffect` runs inside `PlateContent`. It registers the current `Plate` store by editor ID, appends primary editors to `primaryEditorIds`, removes them on unmount, and sets `activeId` when Slate focus enters that editor.\n\n| State | Owner | Behavior |\n|-------|-------|----------|\n| `editorStores` | `PlateControllerEffect` | Maps mounted editor IDs to their Jotai stores. Unmounted IDs are set to `null`. |\n| `primaryEditorIds` | `PlateControllerEffect` | Appends mounted editors whose `Plate` store has `primary: true`; removes them on unmount. |\n| `activeId` | `PlateControllerEffect` | Set to the focused editor ID. Cleared on unmount when the unmounted editor was active. |\n\n## API Reference\n\n### `PlateController`\n\nProvider for cross-editor lookup state.\n\n<API name=\"PlateController\">\n<APIProps>\n  <APIItem name=\"children\" type=\"React.ReactNode\">\n    Shared UI and editor trees that should participate in controller lookup.\n  </APIItem>\n  <APIItem name=\"activeId\" type=\"string | null\" optional>\n    Initial active editor ID.\n  </APIItem>\n  <APIItem name=\"editorStores\" type=\"Record<string, JotaiStore | null>\" optional>\n    Initial editor-store map.\n  </APIItem>\n  <APIItem name=\"primaryEditorIds\" type=\"string[]\" optional>\n    Initial primary editor ID list.\n  </APIItem>\n</APIProps>\n</API>\n\n### Controller Store State\n\n| State | Type | Default |\n|-------|------|---------|\n| `activeId` | `string \\| null` | `null` |\n| `editorStores` | `Record<string, JotaiStore \\| null>` | `{}` |\n| `primaryEditorIds` | `string[]` | `[]` |\n\n### `usePlateControllerStore`\n\nResolve a Plate Jotai store from the controller.\n\n<API name=\"usePlateControllerStore\">\n<APIParameters>\n  <APIItem name=\"idProp\" type=\"string\" optional>\n    Editor ID to resolve directly.\n  </APIItem>\n</APIParameters>\n<APIReturns type=\"JotaiStore | null\">\n  Matching editor store, active editor store, first mounted primary editor store, or `null`.\n</APIReturns>\n</API>\n\n### `usePlateControllerExists`\n\nCheck whether a local controller provider exists.\n\n<API name=\"usePlateControllerExists\">\n<APIReturns type=\"boolean\">\n  `true` when `usePlateControllerLocalStore()` finds a controller store.\n</APIReturns>\n</API>\n\n### `usePlateControllerLocalStore`\n\nRead the local controller atom store.\n\n<API name=\"usePlateControllerLocalStore\">\n<APIParameters>\n  <APIItem name=\"options\" type=\"string | { scope?: string; warnIfNoStore?: boolean }\" optional>\n    Scope options passed to the generated controller store hook. A string is treated as `scope`.\n  </APIItem>\n</APIParameters>\n<APIReturns type=\"PlateControllerStore\">\n  Local controller store hook result.\n</APIReturns>\n</API>\n\n### `PlateControllerEffect`\n\nRegister a `Plate` store with the nearest controller.\n\n<API name=\"PlateControllerEffect\">\n<APIProps>\n  <APIItem name=\"id\" type=\"string\" optional>\n    Editor ID to register. Defaults to the ID from the current Plate store.\n  </APIItem>\n</APIProps>\n</API>\n\n`PlateContent` renders `PlateControllerEffect` for you. Render it directly only when you build a custom content surface that still needs controller registration.\n",
      "type": "registry:file",
      "target": "content/docs/plate/api/core/plate-controller.mdx"
    }
  ],
  "type": "registry:file"
}