{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "plugin-shortcuts-docs",
  "title": "Plugin Shortcuts",
  "description": "Configure keyboard shortcuts on Plate plugins.",
  "files": [
    {
      "path": "../../content/docs/(guides)/plugin-shortcuts.mdx",
      "content": "---\ntitle: Plugin Shortcuts\ndescription: Configure keyboard shortcuts on Plate plugins.\n---\n\nPlugin shortcuts map key combinations to plugin methods or explicit handlers. Plate resolves shortcuts during plugin setup, stores them on `editor.meta.shortcuts`, and renders them through `EditorHotkeysEffect` inside the editable. This guide covers linked methods, custom handlers, overrides, priorities, and default shortcut ownership.\n\n## How Shortcuts Resolve\n\nEach plugin owns a `shortcuts` object. At resolution time Plate namespaces every shortcut as `${plugin.key}.${shortcutName}`.\n\nWhen a shortcut has no `handler`, Plate looks for a matching plugin-specific method in this order:\n\n1. `editor.tf[plugin.key][shortcutName]`\n2. `editor.api[plugin.key][shortcutName]`\n\nIf neither method exists and no `handler` is provided, the shortcut is ignored by `EditorHotkeysEffect`.\n\n| Field | Meaning |\n| --- | --- |\n| `keys` | Key combination passed to `useHotkeys`. Use a string like `'mod+b'` or arrays like `[[Key.Mod, 'b']]`. |\n| `handler` | Explicit callback receiving `{ editor, event, eventDetails }`. |\n| `priority` | Shortcut priority. Defaults to the parent plugin priority. |\n| `preventDefault` | Passed through to `useHotkeys`. When omitted, Plate calls `event.preventDefault()` and `event.stopPropagation()` after handled shortcuts. |\n| `null` | Removes that named shortcut from the plugin. |\n\n## Linked Transform Shortcuts\n\nUse a linked transform when the shortcut name and plugin transform name are the same.\n\n```tsx title=\"plugins/signature-plugin.tsx\" showLineNumbers\nimport { Key, createPlatePlugin } from 'platejs/react';\n\nexport const SignaturePlugin = createPlatePlugin({\n  key: 'signature',\n})\n  .extendTransforms(({ editor }) => ({\n    insertSignature: () => {\n      editor.tf.insertText(' - Plate');\n    },\n  }))\n  .extend({\n    shortcuts: {\n      insertSignature: {\n        keys: [[Key.Mod, Key.Shift, 's']],\n      },\n    },\n  });\n```\n\nPressing `Mod+Shift+S` calls `editor.tf.signature.insertSignature()`.\n\n## Linked API Shortcuts\n\nIf there is no matching transform, Plate falls back to the plugin-specific API method.\n\n```tsx title=\"plugins/inspect-plugin.tsx\" showLineNumbers\nimport { Key, createPlatePlugin } from 'platejs/react';\n\nexport const InspectPlugin = createPlatePlugin({\n  key: 'inspect',\n})\n  .extendApi(({ editor }) => ({\n    logText: () => {\n      editor.api.debug.info('Editor text', editor.api.string([]));\n    },\n  }))\n  .extend({\n    shortcuts: {\n      logText: {\n        keys: [[Key.Mod, Key.Alt, 'l']],\n      },\n    },\n  });\n```\n\nPressing `Mod+Alt+L` calls `editor.api.inspect.logText()`.\n\n<Callout type=\"info\" title=\"Transforms win\">\n  If a transform and an API method share the same shortcut name, Plate uses the\n  transform. Pick distinct names when you need both actions.\n</Callout>\n\n## Custom Handlers\n\nUse a `handler` when the shortcut needs the keyboard event, custom branching, or work that should not live as a plugin API/transform method.\n\n```tsx title=\"plugins/draft-plugin.tsx\" showLineNumbers\nimport { Key, createPlatePlugin } from 'platejs/react';\n\nexport const DraftPlugin = createPlatePlugin({\n  key: 'draft',\n}).extend({\n  shortcuts: {\n    saveDraft: {\n      keys: [[Key.Mod, 's']],\n      handler: ({ editor }) => {\n        const text = editor.api.string([]);\n\n        if (text.trim().length === 0) return false;\n\n        editor.api.debug.info('Draft text', text);\n\n        return true;\n      },\n    },\n  },\n});\n```\n\nReturning `false` means \"not handled\"; Plate will not call `preventDefault()` for that key press. Returning `true` or `undefined` means handled when `preventDefault` is omitted.\n\n## Prevent Default\n\nPlate has two layers of default-prevention behavior:\n\n| Configuration | Behavior |\n| --- | --- |\n| `preventDefault` omitted and handler returns anything except `false` | Plate calls `event.preventDefault()` and `event.stopPropagation()`. |\n| Handler returns `false` | Plate leaves the event alone. |\n| `preventDefault` is set | Plate passes the option to `useHotkeys` and skips its own `preventDefault()` call. |\n\nUse the default omission for normal editor commands. Set `preventDefault` only when you intentionally want `useHotkeys` to own that behavior.\n\n## Configure Existing Shortcuts\n\nConfigure a named shortcut to change its keys.\n\n```tsx title=\"plugins/basic-marks.tsx\" showLineNumbers\nimport { BoldPlugin } from '@platejs/basic-nodes/react';\nimport { Key } from 'platejs/react';\n\nexport const AppBoldPlugin = BoldPlugin.configure({\n  shortcuts: {\n    toggle: {\n      keys: [[Key.Mod, Key.Shift, 'b']],\n    },\n  },\n});\n```\n\nSet a shortcut to `null` to remove it.\n\n```tsx title=\"plugins/basic-marks.tsx\" showLineNumbers\nimport { ItalicPlugin } from '@platejs/basic-nodes/react';\n\nexport const AppItalicPlugin = ItalicPlugin.configure({\n  shortcuts: {\n    toggle: null,\n  },\n});\n```\n\nThe `null` value removes `italic.toggle` from `editor.meta.shortcuts`.\n\n## Multiple Shortcuts\n\nA plugin can declare multiple shortcut names. Keep each name aligned with the method it should call.\n\n```tsx title=\"plugins/review-plugin.tsx\" showLineNumbers\nimport { Key, createPlatePlugin } from 'platejs/react';\n\nexport const ReviewPlugin = createPlatePlugin({\n  key: 'review',\n})\n  .extendTransforms(({ editor }) => ({\n    accept: () => editor.tf.insertText('Accepted'),\n    reject: () => editor.tf.insertText('Rejected'),\n  }))\n  .extend({\n    shortcuts: {\n      accept: {\n        keys: [[Key.Mod, Key.Alt, 'a']],\n      },\n      reject: {\n        keys: [[Key.Mod, Key.Alt, 'r']],\n      },\n    },\n  });\n```\n\nThis creates `review.accept` and `review.reject` in `editor.meta.shortcuts`.\n\n## Priority\n\nShortcut priority defaults to the parent plugin priority. Set `priority` on a shortcut when two handlers use the same key combination and one should win.\n\n```tsx title=\"plugins/priority-plugin.tsx\" showLineNumbers\nimport { createPlatePlugin } from 'platejs/react';\n\nexport const PriorityPlugin = createPlatePlugin({\n  key: 'priority',\n  priority: 20,\n}).extend({\n  shortcuts: {\n    openCommandMenu: {\n      keys: 'mod+k',\n      priority: 200,\n      handler: ({ editor }) => {\n        editor.api.debug.info('Open command menu');\n\n        return true;\n      },\n    },\n  },\n});\n```\n\nPlate stores the resolved priority with the shortcut and passes it to `useHotkeys`.\n\n## Editor-Level Shortcuts\n\n`createPlateEditor({ shortcuts })` attaches shortcuts to the root plugin. Use it for editor-wide commands that do not belong to one feature plugin.\n\n```tsx title=\"editor.ts\" showLineNumbers\nimport { createPlateEditor } from 'platejs/react';\n\nexport const editor = createPlateEditor({\n  shortcuts: {\n    reportWordCount: {\n      keys: 'mod+shift+w',\n      handler: ({ editor }) => {\n        const words = editor.api.string([]).trim().split(/\\s+/).filter(Boolean);\n\n        editor.api.debug.info('Word count', words.length);\n\n        return true;\n      },\n    },\n  },\n});\n```\n\nInternally this becomes a root shortcut, so plugin-owned shortcuts are still the better fit for feature-owned behavior.\n\n## Default Shortcuts\n\n| Plugin | Shortcut name | Keys |\n| --- | --- | --- |\n| `BoldPlugin` | `toggle` | `Mod+B` |\n| `ItalicPlugin` | `toggle` | `Mod+I` |\n| `UnderlinePlugin` | `toggle` | `Mod+U` |\n| `ParagraphPlugin` | `toggleParagraph` | `Mod+Alt+0`, `Mod+Shift+0` |\n| `CopilotPlugin` | `accept` | `Tab` |\n| `CopilotPlugin` | `reject` | `Escape` |\n\nOther plugins often expose `toggle`, `insert`, or feature-specific transforms without default keys. Add shortcuts in your app when those commands should be keyboard-accessible.\n\n## API Reference\n\n```ts title=\"Shortcut type\"\ntype Shortcut = HotkeysOptions & {\n  keys?: Keys | null;\n  priority?: number;\n  handler?: (ctx: {\n    editor: PlateEditor;\n    event: KeyboardEvent;\n    eventDetails: HotkeysEvent;\n  }) => boolean | void;\n};\n```\n\nDone. Name shortcuts after plugin-specific transforms or API methods by default, and use handlers only when the keyboard event is part of the behavior.\n",
      "type": "registry:file",
      "target": "content/docs/plate/(guides)/plugin-shortcuts.mdx"
    }
  ],
  "type": "registry:file"
}