diff --git a/.changeset/config.json b/.changeset/config.json
index 17b9abdcde..af53e39d80 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -2,7 +2,17 @@
"$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json",
"changelog": ["./changelog-config", { "repo": "udecode/plate" }],
"commit": false,
- "linked": [["@udecode/*"]],
+ "linked": [
+ [
+ "@udecode/plate",
+ "@udecode/plate*",
+ "@udecode/slate",
+ "@udecode/slate*",
+ "@udecode/react*",
+ "@udecode/cn",
+ "@udecode/utils"
+ ]
+ ],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
diff --git a/.changeset/itchy-cups-wonder.md b/.changeset/itchy-cups-wonder.md
deleted file mode 100644
index e8bf89880e..0000000000
--- a/.changeset/itchy-cups-wonder.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@udecode/plate-dnd': patch
----
-
-Fix: `useDndNode` calls a state setter during its render function
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index e4b702d0ab..326e0906d6 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -36,6 +36,14 @@ module.exports = {
'**/scripts/*.mts',
],
overrides: [
+ {
+ files: ['**/registry/default/**/*'],
+ rules: {
+ 'jsx-a11y/iframe-has-title': 'off',
+ 'jsx-a11y/media-has-caption': 'off',
+ 'react/jsx-no-comment-textnodes': 'off',
+ },
+ },
{
files: ['*.ts', '*.tsx', '*.mts'],
parserOptions: {
diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md
index 9ff93ba845..ecfd36216d 100644
--- a/BREAKING_CHANGES.md
+++ b/BREAKING_CHANGES.md
@@ -1,5 +1,37 @@
For older changelogs, see https://github.com/udecode/plate/blob/main/docs
+# 40.0.0
+
+## @udecode/slate-react@40.0.0
+
+### Major Changes
+
+- [#3744](https://github.com/udecode/plate/pull/3744) by [@zbeyens](https://github.com/zbeyens) –
+ - Add `slate-dom` as a peer dependency.
+ - Update `slate-react` peer dependency to `>=0.111.0`
+
+## @udecode/plate-ai@40.0.0
+
+### Major Changes
+
+- [#3744](https://github.com/udecode/plate/pull/3744) by [@zbeyens](https://github.com/zbeyens) –
+ - Remove `scrollContainerSelector` option in favor of `useEditorContainerRef`
+
+## @udecode/plate-heading@40.0.0
+
+### Major Changes
+
+- [#3744](https://github.com/udecode/plate/pull/3744) by [@zbeyens](https://github.com/zbeyens) –
+ - Remove `scrollContainerSelector` option in favor of `useEditorContainerRef`
+
+## @udecode/plate-layout@40.0.0
+
+### Major Changes
+
+- [#3744](https://github.com/udecode/plate/pull/3744) by [@zbeyens](https://github.com/zbeyens) –
+ - Remove `toggleColumns` in favor of `toggleColumnGroup`
+ - Remove `insertEmptyColumn` in favor of `insertColumn`
+
# 39.0.0
## @udecode/plate-dnd@39.0.0
diff --git a/README.md b/README.md
index 9275f058e0..148d2c95a4 100644
--- a/README.md
+++ b/README.md
@@ -10,16 +10,6 @@ Plate
-
-
-
-
-
![hero](apps/www/public/og.png)
@@ -35,12 +25,11 @@ Plate
You can choose one of the following templates to get started:
-| Option | NextJS | Plate | Plugins | AI & Backend |
-| --------------------------------------------------------------------------------- | ------ | ----- | ------- | ------------ |
-| [Notion-like template](https://pro.platejs.org/docs/templates/potion) | ✅ | ✅ | ✅ | ✅ |
-| [Plate playground template](https://github.com/udecode/plate-playground-template) | ✅ | ✅ | ✅ | |
-| [Plate minimal template](https://github.com/udecode/plate-template) | ✅ | ✅ | | |
-| [NextJS template](https://platejs.org/docs/components/installation/next) | ✅ | | | |
+| Option | Plate | Plugins | AI | Backend |
+| --------------------------------------------------------------------------------- | ----- | ------- | --- | ------- |
+| [Notion-like template](https://pro.platejs.org/docs/templates/potion) | ✅ | ✅ | ✅ | ✅ |
+| [Plate playground template](https://github.com/udecode/plate-playground-template) | ✅ | ✅ | ✅ | |
+| [Plate minimal template](https://github.com/udecode/plate-template) | ✅ | | | |
## Documentation
diff --git a/apps/www/content/docs/ai.mdx b/apps/www/content/docs/ai.mdx
index 21d8766ffb..7d5ae429eb 100644
--- a/apps/www/content/docs/ai.mdx
+++ b/apps/www/content/docs/ai.mdx
@@ -1,12 +1,11 @@
---
title: AI
-description: AI menu with commands, streaming responses in a preview or directly into the editor.
docs:
- route: https://pro.platejs.org/docs/examples/ai
title: AI
---
-
+
@@ -172,7 +171,6 @@ const plugins = [
? PROMPT_TEMPLATES.userSelecting
: PROMPT_TEMPLATES.userDefault;
},
- scrollContainerSelector: '#scroll_container',
systemTemplate: ({ isBlockSelecting, isSelecting }) => {
return isBlockSelecting
? PROMPT_TEMPLATES.systemBlockSelecting
@@ -183,18 +181,11 @@ const plugins = [
},
render: { afterEditable: () => },
}),
- SelectionOverlayPlugin,
];
```
- [AIMenu](/docs/components/ai-menu)
-The [SelectionOverlayPlugin](https://pro.platejs.org/docs/components/cursor-overlay):
-
-- Maintains selection highlight when editor loses focus
-- Essential for AI menu and other external input interactions
-- Prevents double selection with `data-plate-prevent-overlay` attribute
-
### AI SDK
This plugin is depending on the [ai](https://npmjs.com/package/ai) package:
@@ -278,14 +269,6 @@ Template for generating prompts. Supports placeholders:
-
-
-CSS selector for scrollable container.
-
-- **Default:** `'#scroll_container'`
-
-
-
Template for system messages. Supports same placeholders as `promptTemplate`.
diff --git a/apps/www/content/docs/alignment.mdx b/apps/www/content/docs/alignment.mdx
index b56f61b62d..ec03790708 100644
--- a/apps/www/content/docs/alignment.mdx
+++ b/apps/www/content/docs/alignment.mdx
@@ -1,12 +1,11 @@
---
title: Alignment
-description: Align your content to different positions.
docs:
- route: /docs/components/align-dropdown-menu
title: Align Dropdown Menu
---
-
+
@@ -66,6 +65,7 @@ The editor instance.
The alignment value.
+Options for the `setNodes` function.
diff --git a/apps/www/content/docs/api/core.mdx b/apps/www/content/docs/api/core.mdx
index f033d49df0..985d573cf1 100644
--- a/apps/www/content/docs/api/core.mdx
+++ b/apps/www/content/docs/api/core.mdx
@@ -160,6 +160,18 @@ a
A new `PlatePlugin` with precise type control.
+### useEditorContainerRef
+
+Get the editor container DOM reference.
+
+### useEditorScrollRef
+
+Get the editor scroll container reference.
+
+### useScrollRef
+
+Get the editor scroll container reference. Returns the scroll ref if it exists, otherwise returns the container ref.
+
### useEditorPlugin
Get editor and plugin context.
diff --git a/apps/www/content/docs/api/core/plate-plugin.mdx b/apps/www/content/docs/api/core/plate-plugin.mdx
index 617b9dbea9..b97aec51b4 100644
--- a/apps/www/content/docs/api/core/plate-plugin.mdx
+++ b/apps/www/content/docs/api/core/plate-plugin.mdx
@@ -63,6 +63,28 @@ Defines how the plugin injects functionality into other plugins or the editor.
Properties used by Plate to inject props into any node component.
+
+
+
+An array of plugin keys to exclude from node prop injection.
+
+
+
+An array of plugin keys. Node prop injection will be excluded for any nodes that are descendants of elements with these plugin types.
+
+
+If true, only matches block elements. Used to restrict prop injection to block-level nodes.
+
+
+
+If true, only matches element nodes. Used to restrict prop injection to element nodes.
+
+
+If true, only matches leaf nodes. Used to restrict prop injection to leaf nodes.
+
+
+Maximum nesting level for node prop injection. Nodes deeper than this level will not receive injected props.
+
Property that can be used by a plugin to allow other plugins to inject code.
diff --git a/apps/www/content/docs/api/core/plate.mdx b/apps/www/content/docs/api/core/plate.mdx
index 8f03361f75..8bd88de24a 100644
--- a/apps/www/content/docs/api/core/plate.mdx
+++ b/apps/www/content/docs/api/core/plate.mdx
@@ -25,7 +25,7 @@ Controlled callback called when the editor state changes.
Callback called when the editor selection changes.
-
+
Callback called when the editor value changes.
@@ -49,6 +49,9 @@ Custom render function for leaf nodes.
Props for the [Editable](https://docs.slatejs.org/libraries/slate-react/editable) component. Extends `TextareaHTMLAttributes`.
+
+Automatically focus the editor when it transitions from read-only to editable mode (when `readOnly` changes from `true` to `false`).
+
Custom `Editable` node.
diff --git a/apps/www/content/docs/api/core/store.mdx b/apps/www/content/docs/api/core/store.mdx
index e4cead40b0..1e00ae52e1 100644
--- a/apps/www/content/docs/api/core/store.mdx
+++ b/apps/www/content/docs/api/core/store.mdx
@@ -29,6 +29,10 @@ A unique ID used as a provider scope. Use it if you have multiple `Plate` in the
- **Default:** random id
+
+A reference to the editor container element.
+
+
Function used to decorate ranges in the editor.
diff --git a/apps/www/content/docs/api/floating.mdx b/apps/www/content/docs/api/floating.mdx
new file mode 100644
index 0000000000..c0c1c63706
--- /dev/null
+++ b/apps/www/content/docs/api/floating.mdx
@@ -0,0 +1,164 @@
+---
+title: Floating
+description: API reference for floating UI components and hooks.
+---
+
+
+
+## Features
+
+- Virtual floating elements that follow cursor position
+- Floating toolbar that appears on text selection
+- Built on top of Floating UI
+- Customizable positioning and behavior
+- Automatic updates on scroll and resize
+
+
+
+## Installation
+
+```bash
+npm install @udecode/plate-floating
+```
+
+## API Hooks
+
+### useVirtualFloating
+
+Creates a floating element with a controlled virtual element, typically used to follow cursor position.
+
+
+
+ Options for the virtual floating element.
+
+
+ Function to get the bounding client rect.
+ - **Default:** Returns a zero-sized rect at (0,0)
+
+
+ Controls visibility of the floating element.
+
+
+ Callback when elements are mounted.
+ - **Default:** `autoUpdate` (updates on scroll and resize)
+
+
+
+
+
+
+
+ Style object to apply to the floating element.
+
+
+ Reference to the virtual element.
+
+
+ References for floating UI positioning.
+
+
+ Function to manually update positioning.
+
+
+
+### useFloatingToolbar
+
+Creates a floating toolbar that appears when text is selected in the editor.
+
+
+
+ State options for the floating toolbar.
+
+
+ Options passed to useVirtualFloating.
+
+
+ Force hide the toolbar.
+
+
+ Show toolbar in read-only mode.
+
+
+
+
+
+
+
+ Ref to detect clicks outside the toolbar.
+
+
+ Whether the toolbar should be hidden.
+
+
+ Props to spread on the toolbar element.
+
+
+ Ref to attach to the toolbar element.
+
+
+
+## API
+
+### getBoundingClientRect
+
+Gets the bounding client rectangle for a location or array of locations in the editor.
+
+
+
+ The editor instance.
+
+
+ The location(s) to get the bounding rectangle for. If not provided, uses the current editor selection.
+
+
+
+
+
+ The merged bounding client rectangle of all specified locations, or undefined if no valid rectangles found.
+
+
+
+### getDOMSelectionBoundingClientRect
+
+Gets the bounding client rectangle of the current DOM selection.
+
+
+
+ The bounding client rectangle of the DOM selection. Returns a zero-sized rect at (0,0) if no selection exists.
+
+
+
+### getRangeBoundingClientRect
+
+Gets the bounding client rectangle for a specific Slate range.
+
+
+
+ The editor instance.
+
+
+ The Slate range to get the bounding rectangle for.
+
+
+
+
+
+ The bounding client rectangle of the range. Returns a zero-sized rect at (0,0) if the range is null or invalid.
+
+
+
+### getSelectionBoundingClientRect
+
+Gets the bounding client rectangle of the current editor selection.
+
+
+
+ The editor instance.
+
+
+
+
+
+ The bounding client rectangle of the selection. Returns a zero-sized rect at (0,0) if the selection is not expanded.
+
+
\ No newline at end of file
diff --git a/apps/www/content/docs/api/resizable.mdx b/apps/www/content/docs/api/resizable.mdx
new file mode 100644
index 0000000000..49454388d4
--- /dev/null
+++ b/apps/www/content/docs/api/resizable.mdx
@@ -0,0 +1,206 @@
+---
+title: Resizable
+---
+
+
+
+## Features
+
+- Resizable elements with handles
+- Configurable min/max width constraints
+- Center/left/right alignment support
+- Width persistence in editor state
+
+
+
+## Installation
+
+```bash
+npm install @udecode/plate-resizable
+```
+
+## API
+
+### useResizableState
+
+Manages state for resizable elements.
+
+
+
+
+
+ Node alignment.
+ - **Default:** `'center'`
+
+
+ Maximum width constraint.
+ - **Default:** `'100%'`
+
+
+ Minimum width constraint.
+ - **Default:** `92`
+
+
+ Whether the element is resizable in read-only mode.
+
+
+
+
+
+
+
+ Current alignment setting.
+
+
+ Maximum width constraint.
+
+
+ Minimum width constraint.
+
+
+ Updates node width in editor state.
+
+
+ Updates width in local state.
+
+
+ Current width value.
+
+
+
+### useResizable
+
+Provides resize behavior props and handlers for resizable elements.
+
+
+
+ State from useResizableState.
+
+
+
+
+
+
+
+ Handler for resize events.
+
+
+
+
+ Style props for the resizable element:
+ - maxWidth: Maximum width constraint
+ - minWidth: Minimum width constraint
+ - position: 'relative'
+ - width: Current width
+
+
+ Style props for the wrapper element:
+ - position: 'relative'
+
+
+ Reference to the wrapper element.
+
+
+
+### useResizeHandleState
+
+Manages state for resize handle elements.
+
+
+
+
+
+ Direction of resize.
+ - **Default:** `'left'`
+
+
+ Initial size of the resizable element.
+
+
+ Callback when handle is hovered.
+
+
+ Callback when handle hover ends.
+
+
+ Custom mouse down handler.
+
+
+ Custom resize handler. Falls back to store handler if not provided.
+
+
+ Custom touch start handler.
+
+
+
+
+
+
+
+ Current resize direction.
+
+
+ Initial cursor/touch position.
+
+
+ Initial element size.
+
+
+ Whether resize direction is horizontal.
+
+
+ Whether resize is in progress.
+
+
+ Editor read-only state.
+
+
+ Update initial position.
+
+
+ Update initial size.
+
+
+ Update resize state.
+
+
+ Hover callback.
+
+
+ Hover end callback.
+
+
+ Mouse down handler.
+
+
+ Resize handler.
+
+
+ Touch start handler.
+
+
+
+### useResizeHandle
+
+Provides handlers and props for resize handle elements.
+
+
+
+ State from useResizeHandleState.
+
+
+
+
+
+ Whether the handle should be hidden (in read-only mode).
+
+
+ Props to spread on the handle element:
+ - onMouseDown: Mouse down event handler
+ - onMouseOut: Mouse out event handler
+ - onMouseOver: Mouse over event handler
+ - onTouchEnd: Touch end event handler
+ - onTouchMove: Touch move event handler
+ - onTouchStart: Touch start event handler
+
+
\ No newline at end of file
diff --git a/apps/www/content/docs/api/slate-utils.mdx b/apps/www/content/docs/api/slate-utils.mdx
index 1d93561ca5..a7126f8fe3 100644
--- a/apps/www/content/docs/api/slate-utils.mdx
+++ b/apps/www/content/docs/api/slate-utils.mdx
@@ -771,6 +771,19 @@ The number of children moved.
+### removeEditorText
+
+Removes all non-empty text nodes from the editor.
+
+
+
+ The editor instance.
+
+
+ Options for removing nodes. The `match` function in options will be combined with the text length check.
+
+
+
### removeMark
Removes a mark and triggers `onChange` if the selection is collapsed.
diff --git a/apps/www/content/docs/api/utils.mdx b/apps/www/content/docs/api/utils.mdx
index 204b23a8c1..c6d32c03b5 100644
--- a/apps/www/content/docs/api/utils.mdx
+++ b/apps/www/content/docs/api/utils.mdx
@@ -121,6 +121,16 @@ If true, merges its props onto its immediate child.
## Hooks
+### useEditorString
+
+A hook that returns the entire text content of the editor as a string.
+
+
+
+ The concatenated text content of all text nodes in the editor.
+
+
+
### useMarkToolbarButtonState
Generates the state for a mark toolbar button.
diff --git a/apps/www/content/docs/autoformat.mdx b/apps/www/content/docs/autoformat.mdx
index 7aacbb6529..296d5568a5 100644
--- a/apps/www/content/docs/autoformat.mdx
+++ b/apps/www/content/docs/autoformat.mdx
@@ -1,6 +1,5 @@
---
title: Autoformat
-description: Apply formatting automatically using shortcodes.
docs:
- route: /docs/basic-elements
title: Basic Elements
@@ -12,7 +11,7 @@ docs:
title: List
---
-
+
diff --git a/apps/www/content/docs/basic-elements.mdx b/apps/www/content/docs/basic-elements.mdx
index 6c7e185ae1..512f46359b 100644
--- a/apps/www/content/docs/basic-elements.mdx
+++ b/apps/www/content/docs/basic-elements.mdx
@@ -1,6 +1,5 @@
---
title: Basic Elements
-description: Enhance your editor with essential formatting elements.
docs:
- route: /docs/components/blockquote-element
title: Block Quote Element
@@ -14,7 +13,7 @@ docs:
title: Heading Element
---
-
+
diff --git a/apps/www/content/docs/basic-marks.mdx b/apps/www/content/docs/basic-marks.mdx
index 2174419934..4b74d8b24a 100644
--- a/apps/www/content/docs/basic-marks.mdx
+++ b/apps/www/content/docs/basic-marks.mdx
@@ -1,12 +1,11 @@
---
title: Basic Marks
-description: Set of essential text formatting options.
docs:
- route: /docs/components/mark-toolbar-button
title: Mark Toolbar Button
---
-
+
diff --git a/apps/www/content/docs/block-menu.mdx b/apps/www/content/docs/block-menu.mdx
index 79689f632b..cce8309711 100644
--- a/apps/www/content/docs/block-menu.mdx
+++ b/apps/www/content/docs/block-menu.mdx
@@ -1,6 +1,5 @@
---
title: Block Menu
-description: Provides quick access to block-specific actions.
docs:
- route: /docs/components/block-context-menu
title: Block Context Menu
@@ -13,7 +12,7 @@ docs:
---
-
+
@@ -45,18 +44,6 @@ const plugins = [
NodeIdPlugin,
BlockSelectionPlugin.configure({
options: {
- areaOptions: {
- behaviour: {
- scrolling: {
- speedDivider: 1.5,
- },
- startThreshold: 10,
- },
- boundaries: '#scroll_container',
- container: '#scroll_container',
- selectables: '#scroll_container .slate-selectable',
- selectionAreaClass: 'slate-selection-area',
- },
enableContextMenu: true,
},
}),
diff --git a/apps/www/content/docs/block-selection.mdx b/apps/www/content/docs/block-selection.mdx
index 643ebe5a9a..d737855c04 100644
--- a/apps/www/content/docs/block-selection.mdx
+++ b/apps/www/content/docs/block-selection.mdx
@@ -1,9 +1,11 @@
---
title: Block Selection
-description: Select and manipulate entire text blocks.
+docs:
+ - route: /docs/components/block-selection
+ title: Block Selection
---
-
+
@@ -42,162 +44,134 @@ const plugins = [
];
```
-## Configuration
+### Exclude blocks from selection
-### Set scrollable container
+You can exclude certain plugins from block selection using:
-To control the scrollable container, configure the `boundaries` and `container` options within `areaOptions`. These options accept CSS selectors, such as `#selection-demo #scroll_container`, which are used with `document.querySelector()`.
+```ts
+BlockSelectionPlugin.configure({
+ inject: {
+ // Exclude blocks below table rows
+ excludeBelowPlugins: ['tr'],
+ // Exclude block types
+ excludePlugins: ['table', 'code_line', 'column_group', 'column'],
+ }
+})
+```
-For this to work effectively:
+- `excludeBelowPlugins`: Plugin keys of non-selectable block descendants. Use this to prevent selection below specific blocks. For example, excluding 'tr' prevents selecting individual cells while still allowing table row selection.
-1. Add an `id` or `className` to your scroll container.If you not sure about the container, you can add it to the ` ` component.
-2. Use the appropriate selector in your configuration.
-3. Don't forget to set `position: relative` to your scroll container.
+- `excludePlugins`: Plugin keys of non-selectable blocks.
-Example configuration:
+### Set scrollable container
-```js
-{
- areaOptions: {
- boundaries: '#your-scroll-container-id',
- container: '#your-scroll-container-id',
- selectables: '#your-scroll-container-id .slate-selectable',
- selectionAreaClass: 'slate-selection-area',
- }
-}
-```
+If you're using `EditorContainer` from [Editor](/docs/components/editor), you can skip this section.
-This setup ensures that the block selection functionality is properly constrained within your designated scrollable area.
+To control the scrollable container, configure the `boundaries` and `container` options within `areaOptions`. These options accept CSS selectors, such as `#selection-demo #${editor.uid}`, which are used with `document.querySelector()`.
-### Set selectable element
+For this to work effectively:
-Add data-plate-selectable to the container or the element you want to start block selection.
+1. Add an `id` or `className` to your scroll container. If you're not sure about the container, you can add it to the ` ` component. We recommend using `id={editor.uid}`.
+2. Use the appropriate selector in your configuration.
+3. Don't forget to set `position: relative` to the container.
-Example:
-```tsx
-
+Default configuration:
+
+```js
+BlockSelectionPlugin.configure({
+ options: {
+ areaOptions: {
+ boundaries: `#${editor.uid}`,
+ container: `#${editor.uid}`,
+ selectables: `#${editor.uid} .slate-selectable`,
+ },
+ },
+});
```
### Set scroll speed
-useing `options.areaOptions.behaviour.scrolling.speedDivider` to set the scroll speed.
+Use `options.areaOptions.behaviour.scrolling.speedDivider` to set the scroll speed.
-The value of `1.5` is our recommended speed.Since it's same with the default speed of the browser.
+The value `0.8` is our recommended speed since it's near the browser-native speed.
```ts
- areaOptions: {
+BlockSelectionPlugin.configure({
+ options: {
+ areaOptions: {
behaviour: {
- scrolling: {
- speedDivider: 1.5,
- },
- },
- },
-```
-
-Full configuration recommended:
-
-```ts
-options: {
- areaOptions: {
- behaviour: {
scrolling: {
+ // You can slow down the scroll speed by setting a bigger value.
speedDivider: 1.5,
},
// The distance needed to move for the selection area to appear.
- // If it’s too small, it may cause the mouse click event to be blocked. 30 is a good default.
- startThreshold: 30,
+ // If it’s too small, it may cause the mouse click event to be blocked. 10 is a good default.
+ startThreshold: 4,
},
- boundaries: '#your-scroll-container-id',
- container: '#your-scroll-container-id',
- selectables: '#your-scroll-container-id .slate-selectable',
- selectionAreaClass: 'slate-selection-area',
},
- // if not using plate-ui context menu, set it to false
- enableContextMenu: true,
-},
+}
```
-## Styling
-
-### Selection area
-
-Style the selection area using `.slate-selection-area` class to your `EditorContainer` component. For example:
+### Add selectable element
-```js
-'[&_.slate-selection-area]:border [&_.slate-selection-area]:border-primary [&_.slate-selection-area]:bg-primary/10'
-```
+Add `data-plate-selectable="true"` to any element you want to start block selection.
-### Selected element
+### Prevent unselect
-To determine if an element is selected, use the new `useBlockSelected` hook. You can render a visual indicator around selected blocks using our `BlockSelection` component or create your own:
+To prevent unselecting blocks when clicking on certain elements, add the `data-plate-prevent-unselect` attribute to those components
+For example:
```tsx
-import React from 'react';
-import { useBlockSelected } from '@udecode/plate-selection/react';
-
-export function BlockSelection() {
- const isBlockSelected = useBlockSelected();
-
- return (
-
- );
-}
+
```
-This component should be rendered inside each block element for consistent selection feedback. Plate UI is doing it in `plate-element.tsx`.
+### Full Page Selection
-## Selectable and resetable
+#### Making Elements Selectable
-### full page selectable
+You can enable block selection for elements outside the ` ` component, similar to the [Potion](https://potion.platejs.org/) template. Add the `data-plate-selectable` attribute to any component you want to make selectable:
-Like the [potion](https://potion.platejs.org/) template, you can enable block selection outside of the ` ` component.
+```tsx
+
+
+```
-Simply add the `data-plate-selectable` attribute to any component outside the editor that you want to make selectable. You can even make the entire page selectable if desired.
+This works for any element, even those outside the editor's DOM tree.
-For example:
+#### Resetting Selection
+There are two ways to handle resetting selection across the full page:
+
+1. Direct API call:
```tsx
-
+editor.api.blockSelection.unselect();
```
-### full page resetable
+2. Click outside handler:
+```tsx
+const handleClickOutside = (event: MouseEvent) => {
+ if (!(event.target as HTMLElement).closest('[data-plate-selectable]')) {
+ editor.api.blockSelection.unselect();
+ }
+};
+```
-To reset the selection, you need call the `api.blockSelection.unselect();` method.
+## Styling
-If you want to make the full page resetable by click, this means you need to be able to access the editor outside of ` `.
+### Selection area
-Or you can implement a click outside handler to reset the selection.
+Style the selection area by adding the `.slate-selection-area` class to your editor container component. For example:
+```js
+'[&_.slate-selection-area]:border [&_.slate-selection-area]:border-primary [&_.slate-selection-area]:bg-primary/10'
+```
-## Prevent unselect
+### Selected element
-To prevent unselecting blocks when clicking on certain elements, add the `data-plate-prevent-unselect` attribute to those components
+To determine if an element is selected, use `useBlockSelected` hook. You can render a visual indicator around selected blocks using our [BlockSelection](/docs/components/block-selection) component or create your own.
-For example:
-```tsx
-
-```
+This component should be rendered inside each block element for consistent selection feedback. Plate UI is doing it in [PlateElement](/docs/components/plate-element).
## Plugins
@@ -209,9 +183,9 @@ Options for the selection area. Example:
```ts
{
- boundaries: ['#selection-demo #scroll_container'],
- container: ['#selection-demo #scroll_container'],
- selectables: ['#selection-demo #scroll_container .slate-selectable'],
+ boundaries: [`#${editor.uid}`],
+ container: [`#${editor.uid}`],
+ selectables: [`#${editor.uid} .slate-selectable`],
selectionAreaClass: 'slate-selection-area',
}
```
@@ -254,7 +228,6 @@ A set of IDs for the currently selected blocks.
## API
-
### editor.api.blockSelection.focus
Focuses the block selection shadow input. This input handles copy, delete, and paste events for selected blocks.
@@ -268,9 +241,6 @@ Adds a selected row to the block selection.
-
- The HTML node above which to add the selection.
-
Whether to clear existing selections before adding the new one.
- **Default:** `true`
@@ -364,68 +334,34 @@ Sets text properties on the selected nodes.
-## API Components
+## Hooks
+
+### useBlockSelectable
-### BlockSelectable
+A hook that provides props for making a block element selectable, including context menu behavior.
-
-
+
+
+ Props to be spread on the block element:
-
- The element to render the block selectable.
+
+ `'slate-selectable'` - Required class for selection functionality
-
- Whether the selection is active.
+
+ Handles right-click context menu behavior:
+ - Opens context menu for selected blocks
+ - Opens for void elements
+ - Opens for elements with `data-plate-open-context-menu="true"`
+ - Adds block to selection with Shift key for multi-select
-
-
-### BlockSelection
-
-A wrapper component that adds block selection functionality to its children.
-
-
-
- The content to be wrapped with block selection functionality.
-
-
-
-## Hooks
+
### useBlockSelected
Returns true if context block is selected.
-### useBlockSelectableState
-
-
-
- Whether the block is active for selection.
-
-
- The element associated with the block.
-
-
- The path of the block in the editor.
-
-
- A ref to the block's DOM element.
-
-
-
-### useBlockSelectable
-
-
-
- Props to be spread on the block's wrapper element.
-
-
-
-### useSelectionArea
-
-A hook that initializes and manages the selection area functionality.
-
### useBlockSelectionNodes
Returns an array of node entries for the currently selected blocks.
@@ -437,3 +373,7 @@ Returns an array of nodes for the currently selected blocks.
### useBlockSelectionFragmentProp
Returns fragment prop for the currently selected blocks.
+
+### useSelectionArea
+
+A hook that initializes and manages the selection area functionality.
diff --git a/apps/www/content/docs/callout.mdx b/apps/www/content/docs/callout.mdx
index b634f38dab..6320c23ef8 100644
--- a/apps/www/content/docs/callout.mdx
+++ b/apps/www/content/docs/callout.mdx
@@ -1,6 +1,5 @@
---
title: Callout
-description: Highlight important information or add special notes.
docs:
- route: https://pro.platejs.org/docs/components/callout-element
title: Callout Element
diff --git a/apps/www/content/docs/caption.mdx b/apps/www/content/docs/caption.mdx
index 03a9930a14..382585b112 100644
--- a/apps/www/content/docs/caption.mdx
+++ b/apps/www/content/docs/caption.mdx
@@ -1,12 +1,11 @@
---
title: Caption
-description: Add captions to your blocks.
docs:
- - route: /docs/components/draggable
- title: Draggable
+ - route: /docs/components/caption
+ title: Caption
---
-
+
@@ -26,25 +25,35 @@ npm install @udecode/plate-caption
```tsx
import { CaptionPlugin } from '@udecode/plate-caption/react';
-import { ImagePlugin, MediaEmbedPlugin } from '@udecode/plate-media/react';
+import {
+ AudioPlugin,
+ FilePlugin,
+ ImagePlugin,
+ MediaEmbedPlugin,
+ VideoPlugin,
+} from '@udecode/plate-media/react';
+```
+```tsx
const plugins = [
// ...otherPlugins,
- CaptionPlugin,
ImagePlugin,
+ VideoPlugin,
+ AudioPlugin,
+ FilePlugin,
MediaEmbedPlugin,
-];
-
-const editor = createPlateEditor({
- plugins,
- override: {
- plugins: {
- [CaptionPlugin.key]: {
- plugins: [ImagePlugin.key, MediaEmbedPlugin.key],
- },
+ CaptionPlugin.configure({
+ options: {
+ plugins: [
+ ImagePlugin,
+ VideoPlugin,
+ AudioPlugin,
+ FilePlugin,
+ MediaEmbedPlugin,
+ ],
},
- },
-});
+ }),
+];
```
## Plugins
diff --git a/apps/www/content/docs/collaboration.mdx b/apps/www/content/docs/collaboration.mdx
index cdcec9197d..c6a959e7d2 100644
--- a/apps/www/content/docs/collaboration.mdx
+++ b/apps/www/content/docs/collaboration.mdx
@@ -1,6 +1,5 @@
---
title: Collaboration
-description: Collaborate with others in a single document.
---
@@ -14,6 +13,8 @@ description: Collaborate with others in a single document.
## Features
- The yjs plugin enables support for collaboration using [slate-yjs](https://docs.slate-yjs.dev/) and [Hocuspocus](https://docs.slate-yjs.dev/walkthroughs/collaboration-hocuspocus).
+- By default remote cursors are not rendered unless you install the remote cursor overlay and include it in the config.
+- Cursors are rendered slightly faded and become solid on hover. Use the `data` field in `cursorOptions` to customise the display name and color.
@@ -29,15 +30,24 @@ npm install @udecode/plate-yjs
```tsx
import { YjsPlugin } from '@udecode/plate-yjs/react';
+import { RemoteCursorOverlay } from '@/components/plate-ui/remote-cursor-overlay';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
YjsPlugin.configure({
options: {
+ render: {
+ afterEditable: RemoteCursorOverlay,
+ },
+ cursorOptions: {
+ autoSend: true,
+ data: { name: 'A plate user', color: '#5AC990' },
+ },
+ disableCursors: false,
hocuspocusProviderOptions: {
- url: "https://hocuspocus.test/hocuspocus",
- name: "test",
+ url: 'https://hocuspocus.test/hocuspocus',
+ name: 'test',
},
},
}),
@@ -45,6 +55,23 @@ const editor = createPlateEditor({
});
```
+### Editor Container
+
+The editor requires a container component above `PlateContent` to ensure correct cursor overlay positioning:
+
+```tsx
+export const EditorContainer = (
+ props: React.HTMLAttributes
+) => {
+ const editor = useEditorRef();
+ const containerRef = useEditorContainerRef();
+
+ return
;
+};
+```
+
+This component is available in [Editor](/docs/components/editor).
+
## Backend
Follow the backend instructions in [Hocuspocus docs](https://tiptap.dev/hocuspocus/getting-started).
diff --git a/apps/www/content/docs/column.mdx b/apps/www/content/docs/column.mdx
index 4a0305841f..310c0d55c2 100644
--- a/apps/www/content/docs/column.mdx
+++ b/apps/www/content/docs/column.mdx
@@ -1,6 +1,5 @@
---
title: Column
-description: Add Columns to your document.
docs:
- route: /docs/components/column-element
title: Column Element
@@ -8,7 +7,7 @@ docs:
title: Column Group Element
---
-
+
@@ -77,9 +76,13 @@ Insert a columnGroup with two empty columns.
The editor instance.
+
+ - `layout`: Array of column widths or number of equal-width columns (default: 2)
+ - Other InsertNodesOptions to control insert behavior
+
-### insertEmptyColumn
+### insertColumn
Insert an empty column. You can set the width by options.width default is "33%"
@@ -125,6 +128,19 @@ If you want to set the `layout` use setNodes.
+### toggleColumnGroup
+
+Convert a block into a column group layout. The selected block becomes the content of the first column.
+
+
+
+ The editor instance.
+
+
+ - `layout`: Array of column widths or number of equal-width columns (default: 2)
+
+
+
## API Components
### useColumnState
diff --git a/apps/www/content/docs/combobox.mdx b/apps/www/content/docs/combobox.mdx
index d2048ff989..9d8001d03f 100644
--- a/apps/www/content/docs/combobox.mdx
+++ b/apps/www/content/docs/combobox.mdx
@@ -1,80 +1,219 @@
---
title: Combobox
-description: Utilities for adding comboboxes to your editor.
+docs:
+ - route: /docs/mention
+ title: Mention Plugin
+ - route: /docs/slash-command
+ title: Slash Command Plugin
+ - route: /docs/emoji
+ title: Emoji Plugin
---
-The `TriggerComboboxPluginOptions` mixin configures your plugin to insert a combobox input element when the user types a specified trigger character.
+The `TriggerComboboxPluginOptions` configures your plugin to insert a combobox input element when the user types a specified trigger character.
-For example, the [Mention](/docs/mention) plugin uses TriggerComboboxPluginOptions to insert an `MentionInputPlugin.key` whenever the user types `@`.
+For example:
+- [Mention](/docs/mention) plugin inserts a combobox when typing `@`
+- [Slash Command](/docs/slash-command) plugin activates with `/`
+- [Emoji](/docs/emoji) plugin shows suggestions with `:`
-### Usage
+## Usage
-Extend the editor using `withTriggerCombobox` and specify default values for the required options. (See below for the full list of options).
+Create an input plugin for the combobox:
```ts
-import { withTriggerCombobox } from '@udecode/plate-combobox';
+const ComboboxInputPlugin = createPlatePlugin({
+ key: 'combobox_input',
+ node: {
+ isElement: true,
+ isInline: true,
+ isVoid: true,
+ },
+});
+```
+
+
+Create your main plugin with `withTriggerCombobox`:
+
+```ts
const MyPlugin = createPlatePlugin({
- // ...
+ key: 'my_plugin',
extendEditor: withTriggerCombobox,
+ // Plugin node options
+ node: {
+ isElement: true,
+ isInline: true,
+ isVoid: true,
+ },
+ // Combobox options
options: {
createComboboxInput: (trigger) => ({
children: [{ text: '' }],
trigger,
- type: 'input',
+ type: ComboboxInputPlugin.key,
}),
trigger: '@',
triggerPreviousCharPattern: /^\s?$/,
- } as TriggerComboboxPluginOptions,
+ },
+ // Include the input plugin
+ plugins: [ComboboxInputPlugin],
});
```
-
+
-Define your input element as an inline void element. It's often useful to do this inside a nested plugin.
-
-```ts
-const MyPlugin = createPlatePlugin({
- // ...
- plugins: [
- createPlatePlugin({
- key: 'input',
- node: {
- isElement: true,
- isInline: true,
- isVoid: true,
- },
- }),
- ],
-});
-```
-
-The input element component can be built using [Inline Combobox](/docs/components/inline-combobox).
+The input element component can be built using [Inline Combobox](/docs/components/inline-combobox).
-
-### Options
-
-
-
-
- A function to create the input node.
-
-
-
- The character that triggers the combobox.
-
-
-
- Only trigger the combobox if the char before the trigger character matches a regular expression. For example, `/^\s?$/` matches beginning of the line or a space.
-
-
-
- A query function to enable the behavior.
-
+## Examples
+
+
+
+
+
+## Types
+
+### TriggerComboboxPluginOptions
+
+
+
+ Function to create the input node when trigger is activated.
+
+
+ Character(s) that trigger the combobox. Can be:
+ - A single character (e.g. '@')
+ - An array of characters
+ - A regular expression
+
+
+ Pattern to match the character before trigger.
+ - **Example:** `/^\s?$/` matches start of line or space
+
+
+ Custom query function to control when trigger is active.
+
+
+
+## Hooks
+
+### useComboboxInput
+
+Hook for managing combobox input behavior and keyboard interactions.
+
+
+
+
+
+ Reference to the input element.
+
+
+ Auto focus the input when mounted.
+ - **Default:** `true`
+
+
+ Cancel on arrow keys.
+ - **Default:** `true`
+
+
+ Cancel on backspace at start.
+ - **Default:** `true`
+
+
+ Cancel on blur.
+ - **Default:** `true`
+
+
+ Cancel when deselected.
+ - **Default:** `true`
+
+
+ Cancel on escape key.
+ - **Default:** `true`
+
+
+ Current cursor position state.
+
+
+ Forward undo/redo to editor.
+ - **Default:** `true`
+
+
+ Callback when input is cancelled.
+
+
+
+
+
+
+
+ Function to cancel the input.
+
+
+
+
+ Blur event handler.
+
+
+ Keydown event handler.
+
+
+
+
+ Function to remove the input node.
+
+
+
+Example:
+
+```tsx
+const MyCombobox = () => {
+ const inputRef = useRef(null);
+ const cursorState = useHTMLInputCursorState(inputRef);
+
+ const { props: inputProps, removeInput } = useComboboxInput({
+ ref: inputRef,
+ cursorState,
+ cancelInputOnBlur: false,
+ onCancelInput: (cause) => {
+ if (cause !== 'backspace') {
+ insertText(editor, trigger + value);
+ }
+ if (cause === 'arrowLeft' || cause === 'arrowRight') {
+ moveSelection(editor, {
+ distance: 1,
+ reverse: cause === 'arrowLeft',
+ });
+ }
+ },
+ });
+
+ return ;
+};
+```
-
\ No newline at end of file
+### useHTMLInputCursorState
+
+Hook for tracking cursor position in an HTML input element.
+
+
+
+ Reference to the input element to track.
+
+
+
+
+
+
+
+ Whether cursor is at the start of input.
+
+
+ Whether cursor is at the end of input.
+
+
+
+
\ No newline at end of file
diff --git a/apps/www/content/docs/comments.mdx b/apps/www/content/docs/comments.mdx
index 0796c4cf46..652b59fda2 100644
--- a/apps/www/content/docs/comments.mdx
+++ b/apps/www/content/docs/comments.mdx
@@ -1,6 +1,5 @@
---
title: Comments
-description: Add comments to text as marks.
docs:
- route: /docs/components/comment-leaf
title: Comment Leaf
@@ -10,7 +9,7 @@ docs:
title: Comments Popover
---
-
+
@@ -79,7 +78,7 @@ The currently active comment.
## API
-### editor.insert.comment
+### editor.tf.insert.comment
Insert a new comment mark.
diff --git a/apps/www/content/docs/components/changelog.mdx b/apps/www/content/docs/components/changelog.mdx
index 9724dd1e57..abfeefd01f 100644
--- a/apps/www/content/docs/components/changelog.mdx
+++ b/apps/www/content/docs/components/changelog.mdx
@@ -11,6 +11,93 @@ Use the [CLI](https://platejs.org/docs/components/cli) to install the latest ver
## November 2024 #16
+### November 26 #16.9
+
+https://github.com/udecode/plate/pull/3809/files
+- Add `select-editor`, `tag-element`, `label`, `form`
+- Replace `cmdk` dependency with `@udecode/cmdk`. It's a controllable version of `cmdk`.
+- `command`: add variants
+- `editor`: add `select` variant
+- `popover`: add `animate` variant
+
+https://github.com/udecode/plate/pull/3807/files
+- `toc-element`: remove `` tag using `` instead to fix html2canvas issue
+- `editor`: remove `role="button"` to fix html2canvas issue
+
+### November 21 #16.8
+
+Shadcn sync:
+
+- `input`: add `text-base md:text-sm`
+- `textarea`: add `text-base md:text-sm`
+- `editor`(`ai`, `aiChat` variants): add `text-base md:text-sm`
+
+### November 14 #16.7
+
+- `toolbar`: Add `ToolbarSplitButton`, `ToolbarSplitButtonPrimary`, `ToolbarSplitButtonSecondary`
+- `media-toolbar-button`: use `ToolbarSplitButton`
+
+### November 13 #16.6
+
+- `resizable`: hide `ResizeHandle` when read-only
+
+### November 8 #16.5
+
+- Add this to your tailwind config:
+```ts
+// plugins
+require("tailwind-scrollbar-hide")
+
+// theme.extend.screens
+screens: {
+ /**
+ * Matches devices where the primary pointing device is capable of
+ * hovering conveniently. Usage: main-hover:group-hover:bg-red-500 See:
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover
+ *
+ * On iOS devices, buttons inside a .group element that contains
+ * descendent elements with `group-hover:` CSS rules require two taps to
+ * click. As a workaround, we disable these rules on devices that cannot
+ * conveniently hover using `main-hover:`.
+ */
+ 'main-hover': {
+ raw: '(hover: hover)',
+ },
+},
+```
+- `editor`:
+ - `EditorContainer`: remove `ref`, use `useEditorContainerRef` instead
+ - add `caret-primary selection:bg-brand/25`
+ - add `id={editor.uid}` to `EditorContainer` so you can remove `scroll_container` or any id you may have defined.
+- `draggable`:
+ - fix a **critical** mobile ux issue: focusing the editor required two taps because of the group hover styles. Fixed with `main-hover:group-hover:opacity-100`.
+ - for xs media, show only if selected
+- `table-element`: fix width
+- `table-row-element`, `table-cell-element`: support row selection
+- Add `draggable` prop to `date-element`, `mention-element`
+- Add `data-plate-focus` attribute to `link-toolbar-button`, `link-floating-toolbar`, `ai-menu` input
+- `cursor-overlay`:
+ - support collapsed selection
+ - remove `DragOverCursorPlugin`, `SelectionOverlayPlugin`. Use `@udecode/plate-selection` instead.
+- `ghost-text`: hide for xs media, add `pointer-events-none`
+- `floating-toolbar`: add `overflow-x-auto scrollbar-hide` to allow horizontal scrolling (mobile)
+- `fixed-toolbar`: add `scrollbar-hide`
+- `emoji-picker-content`: add emoji font
+- `column-element`: add `w-full` as default
+- New hook: `use-is-touch-device`
+- `block-context-menu`: disable on touch device
+- `ai-toolbar-button`: add `onMouseDown`
+- `ai-menu-items`: add undefined check
+- `block-selection-plugins`: add
+```ts
+inject: {
+ excludeBelowPlugins: ['tr'],
+ excludePlugins: ['table', 'code_line', 'column_group', 'column'],
+},
+```
+- Add `floating-toolbar-plugin`, `fixed-toolbar-plugin`
+- misc: `hr-element`, `plate-element`, `transforms`
+
### November 7 #16.4
- `block-context-menu`: prevent unselect when clicking on the context menu
diff --git a/apps/www/content/docs/components/cli.mdx b/apps/www/content/docs/components/cli.mdx
index 6488d65701..e925154f58 100644
--- a/apps/www/content/docs/components/cli.mdx
+++ b/apps/www/content/docs/components/cli.mdx
@@ -32,6 +32,7 @@ Options:
-f, --force force overwrite of existing components.json. (default: false)
-y, --yes skip confirmation prompt. (default: false)
-c, --cwd
the working directory. defaults to the current directory.
+ -a, --all add all available components. (default: false)
-n, --name registry name. (default: plate)
-s, --silent mute output (default: false)
--src-dir use the src directory when creating a new project (default: false)
@@ -118,13 +119,6 @@ Here's an example `components.json` file configured for [shadcn/ui](https://ui.s
"style": "default",
"rsc": true,
"tsx": true,
- "aliases": {
- "components": "@/components",
- "hooks": "@/hooks",
- "lib": "@/lib",
- "ui": "@/components/ui",
- "utils": "@/lib/utils"
- },
"tailwind": {
"baseColor": "slate",
"config": "tailwind.config.ts",
@@ -132,6 +126,14 @@ Here's an example `components.json` file configured for [shadcn/ui](https://ui.s
"cssVariables": true,
"prefix": ""
},
+ "aliases": {
+ "components": "@/components",
+ "hooks": "@/hooks",
+ "lib": "@/lib",
+ "ui": "@/components/ui",
+ "utils": "@/lib/utils"
+ },
+ "iconLibrary": "lucide",
"registries": {
"plate": {
"aliases": {
diff --git a/apps/www/content/docs/components/components-json.mdx b/apps/www/content/docs/components/components-json.mdx
index 28fc3f247d..b1c121b161 100644
--- a/apps/www/content/docs/components/components-json.mdx
+++ b/apps/www/content/docs/components/components-json.mdx
@@ -19,7 +19,7 @@ You can create a `components.json` file in your project by running the following
npx shadcx@latest init plate
```
-See the [CLI section](/docs/cli) for more information.
+See the [CLI section](/docs/components/cli) for more information.
## $schema
diff --git a/apps/www/content/docs/components/dark-mode/next.mdx b/apps/www/content/docs/components/dark-mode/next.mdx
index 9bcb06461a..188e59c8c6 100644
--- a/apps/www/content/docs/components/dark-mode/next.mdx
+++ b/apps/www/content/docs/components/dark-mode/next.mdx
@@ -22,9 +22,11 @@ npm install next-themes
import * as React from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
-import { type ThemeProviderProps } from 'next-themes/dist/types';
-export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
+export function ThemeProvider({
+ children,
+ ...props
+}: React.ComponentProps) {
return {children} ;
}
```
diff --git a/apps/www/content/docs/components/installation/manual.mdx b/apps/www/content/docs/components/installation/manual.mdx
index da4444fe93..70c3b4fc87 100644
--- a/apps/www/content/docs/components/installation/manual.mdx
+++ b/apps/www/content/docs/components/installation/manual.mdx
@@ -16,11 +16,13 @@ Components are styled using Tailwind CSS. You need to install Tailwind CSS in yo
Add the following dependencies to your project:
```bash
-npm install tailwindcss-animate class-variance-authority lucide-react @udecode/cn @udecode/plate-common slate slate-react slate-history slate-hyperscript
+npm install slate slate-dom slate-react slate-history slate-hyperscript @udecode/plate-common @udecode/cn class-variance-authority tailwindcss-animate tailwind-scrollbar-hide lucide-react
```
### Configure path aliases
+Configure the path aliases in your `tsconfig.json` file.
+
```json {3-6} title="tsconfig.json"
{
"compilerOptions": {
@@ -42,7 +44,7 @@ npm install tailwindcss-animate class-variance-authority lucide-react @udecode/c
### Configure components.json
-Create [components.json](/docs/components/components-json) at the root of your project, then add the following:
+Create a [components.json](/docs/components/components-json) in the root of your project.
```json
{
@@ -50,13 +52,6 @@ Create [components.json](/docs/components/components-json) at the root of your p
"style": "default",
"rsc": true,
"tsx": true,
- "aliases": {
- "components": "@/components",
- "hooks": "@/hooks",
- "lib": "@/lib",
- "ui": "@/components/ui",
- "utils": "@/lib/utils"
- },
"tailwind": {
"baseColor": "slate",
"config": "tailwind.config.ts",
@@ -64,6 +59,14 @@ Create [components.json](/docs/components/components-json) at the root of your p
"cssVariables": true,
"prefix": ""
},
+ "aliases": {
+ "components": "@/components",
+ "hooks": "@/hooks",
+ "lib": "@/lib",
+ "ui": "@/components/ui",
+ "utils": "@/lib/utils"
+ },
+ "iconLibrary": "lucide",
"registries": {
"plate": {
"aliases": {
diff --git a/apps/www/content/docs/components/index.mdx b/apps/www/content/docs/components/introduction.mdx
similarity index 100%
rename from apps/www/content/docs/components/index.mdx
rename to apps/www/content/docs/components/introduction.mdx
diff --git a/apps/www/content/docs/copilot.mdx b/apps/www/content/docs/copilot.mdx
index 4f48f17955..9769c1d10c 100644
--- a/apps/www/content/docs/copilot.mdx
+++ b/apps/www/content/docs/copilot.mdx
@@ -1,6 +1,5 @@
---
title: Copilot
-description: Render AI suggestions ghost text as you type.
docs:
- route: /docs/components/ghost-text
title: Ghost Text
@@ -8,7 +7,7 @@ docs:
title: Ghost Text
---
-
+
diff --git a/apps/www/content/docs/csv.mdx b/apps/www/content/docs/csv.mdx
index d3e2dea22c..f9840589c4 100644
--- a/apps/www/content/docs/csv.mdx
+++ b/apps/www/content/docs/csv.mdx
@@ -1,9 +1,8 @@
---
title: Serializing CSV
-description: Copy paste from CSV to Slate.
---
-
+
diff --git a/apps/www/content/docs/cursor-overlay.mdx b/apps/www/content/docs/cursor-overlay.mdx
new file mode 100644
index 0000000000..55d4f26cba
--- /dev/null
+++ b/apps/www/content/docs/cursor-overlay.mdx
@@ -0,0 +1,138 @@
+---
+title: Cursor Overlay
+docs:
+ - route: /docs/components/cursor-overlay
+ title: Cursor Overlay
+---
+
+
+
+
+
+The Cursor Overlay feature provides visual feedback for selections and cursor positions, particularly useful for maintaining context when the editor loses focus or during drag operations.
+
+## Features
+
+- Maintains selection highlight when another element is focused
+- Dynamic selection (e.g. during AI streaming)
+- Shows cursor position during drag operations
+- Customizable styling for cursors and selections
+- Essential for external UI interactions (e.g. link toolbar, AI combobox)
+
+
+
+## Installation
+
+```bash
+npm install @udecode/plate-selection
+```
+
+## Usage
+
+```tsx
+import { CursorOverlayPlugin } from '@udecode/plate-selection/react';
+import { CursorOverlay } from '@/components/plate-ui/cursor-overlay';
+```
+
+- [CursorOverlay](/docs/components/cursor-overlay)
+
+```tsx
+const plugins = [
+ // ...otherPlugins,
+ CursorOverlayPlugin.configure({
+ render: { afterEditable: () => },
+ }),
+];
+```
+
+### Editor Container
+
+The editor requires a container component above `PlateContent` to ensure correct cursor overlay positioning:
+
+```tsx
+export const EditorContainer = (props: React.HTMLAttributes) => {
+ const editor = useEditorRef();
+ const containerRef = useEditorContainerRef();
+
+ return
;
+};
+```
+
+This component is available in [Editor](/docs/components/editor).
+
+### Preserving Selection Focus
+
+To maintain the editor's selection state when focusing UI elements, add the `data-plate-focus="true"` attribute to those elements:
+
+```tsx
+
+ {/* toolbar content */}
+
+```
+
+## Plugins
+
+### CursorOverlayPlugin
+
+Plugin that manages cursor and selection overlays.
+
+
+
+Object containing cursor states. Default: `{}`
+
+
+
+## API
+
+### editor.api.cursorOverlay.addCursor
+
+Adds a cursor overlay with the specified key and state.
+
+
+
+ Unique identifier for the cursor (e.g., 'blur', 'drag', 'custom').
+
+
+ The cursor state including selection and optional styling data.
+
+
+
+### editor.api.cursorOverlay.removeCursor
+
+Removes a cursor overlay by its key.
+
+
+
+ The key of the cursor to remove.
+
+
+
+## Hooks
+
+### useCursorOverlay
+
+A hook that manages cursor and selection overlay states, providing real-time cursor positions and selection rectangles.
+
+
+
+
+
+ Minimum width in pixels for a selection rectangle. Useful for making cursor carets more visible.
+ - **Default:** `1`
+
+
+ Whether to recalculate cursor positions when the container is resized.
+ - **Default:** `true`
+
+
+
+
+
+
+
+ Array of cursor states with their corresponding selection rectangles and styling data.
+
+
+ Function to manually trigger a recalculation of cursor positions.
+
+
diff --git a/apps/www/content/docs/date.mdx b/apps/www/content/docs/date.mdx
index bd567c0a36..92c516108d 100644
--- a/apps/www/content/docs/date.mdx
+++ b/apps/www/content/docs/date.mdx
@@ -1,12 +1,11 @@
---
title: Date
-description: Insert and format dates in your document.
docs:
- route: /docs/components/date-element
title: Date Element
---
-
+
@@ -74,7 +73,7 @@ A boolean indicating the direction to check. If true, checks the previous node;
## Plugin Transforms
-### editor.insert.date
+### editor.tf.insert.date
Insert a date into the editor.
diff --git a/apps/www/content/docs/dnd.mdx b/apps/www/content/docs/dnd.mdx
index f0e553cf30..c53dfc2bf8 100644
--- a/apps/www/content/docs/dnd.mdx
+++ b/apps/www/content/docs/dnd.mdx
@@ -1,12 +1,13 @@
---
title: Drag & Drop
-description: Allows movement of blocks, such as paragraph or tables, within the editor.
docs:
- route: /docs/components/draggable
title: Draggable
+ - route: /docs/dnd
+ title: Dnd Plugin
---
-
+
diff --git a/apps/www/content/docs/docx.mdx b/apps/www/content/docs/docx.mdx
index e2cf5afdab..a58689fd94 100644
--- a/apps/www/content/docs/docx.mdx
+++ b/apps/www/content/docs/docx.mdx
@@ -1,9 +1,8 @@
---
title: Serializing Docx
-description: Copy paste from DOCX to Slate.
---
-
+
diff --git a/apps/www/content/docs/emoji.mdx b/apps/www/content/docs/emoji.mdx
index ad72e523cb..91aa713c18 100644
--- a/apps/www/content/docs/emoji.mdx
+++ b/apps/www/content/docs/emoji.mdx
@@ -1,6 +1,5 @@
---
title: Emoji
-description: Insert emoji inline.
docs:
- route: /docs/combobox
title: Combobox
@@ -8,11 +7,9 @@ docs:
title: Emoji Input Element
- route: /docs/components/emoji-dropdown-menu
title: Emoji Dropdown Menu
- - route: /docs/components/emoji-toolbar-dropdown
- title: Emoji Toolbar Dropdown
---
-
+
diff --git a/apps/www/content/docs/equation.mdx b/apps/www/content/docs/equation.mdx
index 0428e14e94..9940811ad0 100644
--- a/apps/www/content/docs/equation.mdx
+++ b/apps/www/content/docs/equation.mdx
@@ -1,6 +1,5 @@
---
title: Equation
-description: Enables the insertion and rendering of LaTeX equations in your editor.
docs:
- route: https://pro.platejs.org/docs/components/equation-element
title: Equation Element
diff --git a/apps/www/content/docs/examples/editable-voids.mdx b/apps/www/content/docs/examples/editable-voids.mdx
index b19c706cec..6c60256f1f 100644
--- a/apps/www/content/docs/examples/editable-voids.mdx
+++ b/apps/www/content/docs/examples/editable-voids.mdx
@@ -1,6 +1,5 @@
---
title: Editable Voids
-description: Nest editors in void nodes.
---
diff --git a/apps/www/content/docs/examples/export.mdx b/apps/www/content/docs/examples/export.mdx
new file mode 100644
index 0000000000..54c1b8c809
--- /dev/null
+++ b/apps/www/content/docs/examples/export.mdx
@@ -0,0 +1,47 @@
+---
+title: Export
+---
+
+
+
+
+
+## Features
+
+- Export editor content to:
+- Client-side export with no server dependencies
+
+
+
+## Usage
+
+Install the [Export Toolbar Button](/docs/components/export-toolbar-button) component.
+
+## Examples
+
+### Plate UI
+
+Refer to the preview above.
+
+### Plate Plus
+
+- Server-side PDF export:
+ - High-quality PDF generation
+ - Custom fonts and styling
+ - Headers and footers
+ - Page numbers
+ - Font selectable
+- Advanced export options:
+ - Paper size selection
+ - Margin controls
+ - Orientation settings
+ - Compression level
+{/* - Enterprise-ready features: */}
+ {/* - Batch processing */}
+ {/* - Watermarking */}
+ {/* - Custom templates */}
+ {/* - Password protection */}
+
+Try it out with our server-side PDF export:
+
+
diff --git a/apps/www/content/docs/examples/hundreds-blocks.mdx b/apps/www/content/docs/examples/hundreds-blocks.mdx
index a9be7c73a2..6dfaa36ce2 100644
--- a/apps/www/content/docs/examples/hundreds-blocks.mdx
+++ b/apps/www/content/docs/examples/hundreds-blocks.mdx
@@ -1,6 +1,5 @@
---
title: Hundreds Blocks
-description: Render an editor with 700 blocks.
---
diff --git a/apps/www/content/docs/examples/hundreds-editors.mdx b/apps/www/content/docs/examples/hundreds-editors.mdx
index 76fbacc9f6..4d80814ce1 100644
--- a/apps/www/content/docs/examples/hundreds-editors.mdx
+++ b/apps/www/content/docs/examples/hundreds-editors.mdx
@@ -1,6 +1,5 @@
---
title: Hundreds Editors
-description: Render 300 editors.
---
diff --git a/apps/www/content/docs/examples/iframe.mdx b/apps/www/content/docs/examples/iframe.mdx
index 5495211c21..762614c993 100644
--- a/apps/www/content/docs/examples/iframe.mdx
+++ b/apps/www/content/docs/examples/iframe.mdx
@@ -1,6 +1,5 @@
---
title: IFrame
-description: Rendering in iframes.
---
diff --git a/apps/www/content/docs/examples/preview-markdown.mdx b/apps/www/content/docs/examples/preview-markdown.mdx
index 293d20d2cb..24960b6e38 100644
--- a/apps/www/content/docs/examples/preview-markdown.mdx
+++ b/apps/www/content/docs/examples/preview-markdown.mdx
@@ -1,6 +1,5 @@
---
title: Preview Markdown
-description: Decorate texts with markdown preview.
---
-
+
diff --git a/apps/www/content/docs/examples/version-history.mdx b/apps/www/content/docs/examples/version-history.mdx
index c9d1cae935..3786f3fe98 100644
--- a/apps/www/content/docs/examples/version-history.mdx
+++ b/apps/www/content/docs/examples/version-history.mdx
@@ -1,6 +1,5 @@
---
title: Version History
-description: Show a diff of two different points in a Plate document's history.
---
diff --git a/apps/www/content/docs/excalidraw.mdx b/apps/www/content/docs/excalidraw.mdx
index 199e66d5ce..35f41e0465 100644
--- a/apps/www/content/docs/excalidraw.mdx
+++ b/apps/www/content/docs/excalidraw.mdx
@@ -1,12 +1,11 @@
---
title: Excalidraw
-description: Create drawings and diagrams as block nodes.
docs:
- route: /docs/components/excalidraw-element
title: Excalidraw Element
---
-
+
diff --git a/apps/www/content/docs/exit-break.mdx b/apps/www/content/docs/exit-break.mdx
index 5835acf72a..f4b47203cc 100644
--- a/apps/www/content/docs/exit-break.mdx
+++ b/apps/www/content/docs/exit-break.mdx
@@ -1,9 +1,8 @@
---
title: Exit Break
-description: Exit a large block using a shortcut.
---
-
+
diff --git a/apps/www/content/docs/font.mdx b/apps/www/content/docs/font.mdx
index fe0a3e4820..d4001cf990 100644
--- a/apps/www/content/docs/font.mdx
+++ b/apps/www/content/docs/font.mdx
@@ -6,7 +6,7 @@ docs:
title: Color Dropdown Menu
---
-
+
diff --git a/apps/www/content/docs/forced-layout.mdx b/apps/www/content/docs/forced-layout.mdx
index 87b030b3ae..84cb0fb0fc 100644
--- a/apps/www/content/docs/forced-layout.mdx
+++ b/apps/www/content/docs/forced-layout.mdx
@@ -1,9 +1,8 @@
---
title: Forced Layout
-description: Strict document structure.
---
-{/* */}
+{/* */}
diff --git a/apps/www/content/docs/getting-started.mdx b/apps/www/content/docs/getting-started.mdx
index 8a3bfbd8fa..d74045bc19 100644
--- a/apps/www/content/docs/getting-started.mdx
+++ b/apps/www/content/docs/getting-started.mdx
@@ -9,29 +9,33 @@ description: A quick tutorial to get you up and running with Plate.
You can choose one of the following templates to get started:
-| Option | NextJS | Plate | Plugins | AI & Backend |
-| --------------------------------------------------------------------------------- | ------ | ----- | ------- | ------------ |
-| [Notion-like template](https://pro.platejs.org/docs/templates/potion) | ✅ | ✅ | ✅ | ✅ |
-| [Plate playground template](https://github.com/udecode/plate-playground-template) | ✅ | ✅ | ✅ | |
-| [Plate minimal template](https://github.com/udecode/plate-template) | ✅ | ✅ | | |
-| [NextJS template](/docs/components/installation/next) | ✅ | | | |
+| Option | Plate | Plugins | AI | Backend |
+| --------------------------------------------------------------------------------- | ----- | ------- | --- | ------- |
+| [Notion-like template](https://pro.platejs.org/docs/templates/potion) | ✅ | ✅ | ✅ | ✅ |
+| [Plate playground template](https://github.com/udecode/plate-playground-template) | ✅ | ✅ | ✅ | |
+| [Plate minimal template](https://github.com/udecode/plate-template) | ✅ | | | |
-For an existing project, jump to the next step.
+For an existing React project, jump to the next step.
### Add dependencies
-Install the core and the plugins you need. You need at least:
+First, install the core dependencies:
```bash
-npm install @udecode/plate-common slate slate-react slate-history slate-hyperscript react react-dom
+npm install @udecode/plate-common slate slate-dom slate-react slate-history
```
-Alternatively you can install **`@udecode/plate`** that contains all the packages excluding the ones with heavy dependencies (e.g. **`@udecode/plate-dnd`**).
+For the examples in this guide, we'll also use these plugins:
```bash
-npm install @udecode/plate slate slate-react slate-history slate-hyperscript react react-dom
+npm install @udecode/plate-basic-marks @udecode/plate-heading @udecode/plate-block-quote @udecode/cn
```
+- `@udecode/plate-basic-marks` provides bold, italic, underline, and code formatting.
+- `@udecode/plate-heading` adds h1-h6 support.
+- `@udecode/plate-block-quote` adds blockquote support.
+- `@udecode/cn` helps with component styling (optional).
+
### Basic Editor
Let's start with a minimal editor setup.
@@ -64,6 +68,8 @@ Let's give our editor some styles: [Editor](/docs/components/editor) is a styled
+To keep things simple, we'll continue to use `PlateContent` in the following code snippets.
+
**Note**: `Editor` is just an example of a styled editor using Tailwind, and
if you're using it, make sure to follow the installation steps in the [Manual
@@ -114,7 +120,7 @@ export default function BasicEditor() {
At this stage, it's crucial to monitor editor modifications in order to store the values appropriately. The **`onChange`** prop will serve this purpose. You can also persist the editor's state by saving the value to local storage or a database and loading it back when needed.
-```tsx showLineNumbers {4-5,8,14-16}
+```tsx showLineNumbers {4-5,8,14-17}
// ...
export default function BasicEditor() {
@@ -129,6 +135,7 @@ export default function BasicEditor() {
{
+ // For performance, debounce your saving logic
localStorage.setItem('editorContent', JSON.stringify(value));
}}
>
@@ -143,23 +150,21 @@ export default function BasicEditor() {
### Plugins
- Use our [interactive builder](/?builder=true) to pick your plugins.
+ See the full list of plugins in the [Plugins](/docs/plugins) section.
-Let's use the basic plugins for a rich-text editor.
+Let's use a few basic plugins.
-```tsx showLineNumbers {20-28}
+```tsx showLineNumbers {3-9,17-23}
// ...
import {
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
- CodePlugin,
} from '@udecode/plate-basic-marks/react';
import { HeadingPlugin } from '@udecode/plate-heading/react';
import { BlockquotePlugin } from '@udecode/plate-block-quote/react';
-import { CodeBlockPlugin } from '@udecode/plate-code-block/react';
const value = [
// ...
@@ -167,16 +172,14 @@ const value = [
export default function BasicEditor() {
const editor = usePlateEditor({
- value,
plugins: [
HeadingPlugin,
BlockquotePlugin,
- CodeBlockPlugin,
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
- CodePlugin,
],
+ value,
});
return (
@@ -202,33 +205,58 @@ The plugins are functioning correctly. However, since we haven't specified any c
**Note**: Plate plugins are packaged unstyled, implying that you have complete
control over markup and styling, hence you can integrate your own design
- system or [Plate UI](/docs/components). If using the latter, use our [interactive builder](/?builder=true) to pick your components.
+ system or [Plate UI](/docs/components).
-To plug-in all the components in one place, we can use the **`override.components`** option in **`usePlateEditor`**:
+To plug-in all the components in one place, use the **`override.components`** option in **`usePlateEditor`**. We'll use the **`withProps`** helper to pass additional props to the components with Tailwind CSS classes.
-```tsx showLineNumbers {14,20-22}
+```tsx showLineNumbers {3,6-7,20-47}
// ...
-// Import Prism for code highlighting
-import Prism from 'prismjs';
-
-// This is a local file, you will need to create this file in your project
-import { createPlateUI } from '@/lib/create-plate-ui';
+import { withProps } from '@udecode/cn';
+import {
+ Plate,
+ PlateElement,
+ PlateLeaf,
+ usePlateEditor,
+} from '@udecode/plate-common/react';
export default function BasicEditor() {
const editor = usePlateEditor({
plugins: [
HeadingPlugin,
BlockquotePlugin,
- CodeBlockPlugin.configure({ options: { prism: Prism } }),
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
- CodePlugin,
],
override: {
- components: createPlateUI(),
+ components: {
+ blockquote: withProps(PlateElement, {
+ as: 'blockquote',
+ className: 'mb-4 border-l-4 border-[#d0d7de] pl-4 text-[#636c76]',
+ }),
+ bold: withProps(PlateLeaf, { as: 'strong' }),
+ h1: withProps(PlateElement, {
+ as: 'h1',
+ className:
+ 'mb-4 mt-6 text-3xl font-semibold tracking-tight lg:text-4xl',
+ }),
+ h2: withProps(PlateElement, {
+ as: 'h2',
+ className: 'mb-4 mt-6 text-2xl font-semibold tracking-tight',
+ }),
+ h3: withProps(PlateElement, {
+ as: 'h3',
+ className: 'mb-4 mt-6 text-xl font-semibold tracking-tight',
+ }),
+ italic: withProps(PlateLeaf, { as: 'em' }),
+ p: withProps(PlateElement, {
+ as: 'p',
+ className: 'mb-4',
+ }),
+ underline: withProps(PlateLeaf, { as: 'u' }),
+ },
},
});
@@ -240,23 +268,21 @@ export default function BasicEditor() {
}
```
-
-
### Initializing Editor's Value with HTML String
You can also specify the initial content of the editor using an HTML string and the corresponding plugins.
-```tsx showLineNumbers {3,7}
+```tsx showLineNumbers {3,8}
// ...
const htmlValue = 'This is bold and italic text!
';
export default function BasicEditor() {
const editor = usePlateEditor({
+ // ...
value: htmlValue,
- plugins: [BoldPlugin, ItalicPlugin],
});
return (
diff --git a/apps/www/content/docs/highlight.mdx b/apps/www/content/docs/highlight.mdx
index 3c9dd10426..821a2cbe0a 100644
--- a/apps/www/content/docs/highlight.mdx
+++ b/apps/www/content/docs/highlight.mdx
@@ -1,12 +1,11 @@
---
title: Highlight
-description: Mark and reference text for review.
docs:
- route: /docs/components/highlight-leaf
title: Highlight Leaf
---
-
+
diff --git a/apps/www/content/docs/horizontal-rule.mdx b/apps/www/content/docs/horizontal-rule.mdx
index 5196e49839..f48bdd1757 100644
--- a/apps/www/content/docs/horizontal-rule.mdx
+++ b/apps/www/content/docs/horizontal-rule.mdx
@@ -1,12 +1,11 @@
---
title: Horizontal Rule
-description: Visually divide and organize content sections with a horizontal line.
docs:
- route: /docs/components/hr-element
title: Hr Element
---
-
+
diff --git a/apps/www/content/docs/html.mdx b/apps/www/content/docs/html.mdx
index ba82380ad8..b2b1949642 100644
--- a/apps/www/content/docs/html.mdx
+++ b/apps/www/content/docs/html.mdx
@@ -1,9 +1,8 @@
---
-title: HTML Parser
-description: Copy paste from HTML to Slate.
+title: Serializing HTML
---
-
+
diff --git a/apps/www/content/docs/indent-list.mdx b/apps/www/content/docs/indent-list.mdx
index 5c4c957606..0102c250d3 100644
--- a/apps/www/content/docs/indent-list.mdx
+++ b/apps/www/content/docs/indent-list.mdx
@@ -1,12 +1,11 @@
---
title: Indent List
-description: Turn any block into a list item.
docs:
- route: /docs/components/indent-list-toolbar-button
title: Indent List Toolbar Button
---
-
+
diff --git a/apps/www/content/docs/indent.mdx b/apps/www/content/docs/indent.mdx
index bc9b7abee5..da0419383a 100644
--- a/apps/www/content/docs/indent.mdx
+++ b/apps/www/content/docs/indent.mdx
@@ -1,6 +1,5 @@
---
title: Indent
-description: Customize text indentation.
docs:
- route: /docs/components/indent-toolbar-button
title: Indent Toolbar Button
@@ -8,7 +7,7 @@ docs:
title: Outdent Toolbar Button
---
-
+
diff --git a/apps/www/content/docs/kbd.mdx b/apps/www/content/docs/kbd.mdx
new file mode 100644
index 0000000000..2decc77033
--- /dev/null
+++ b/apps/www/content/docs/kbd.mdx
@@ -0,0 +1,49 @@
+---
+title: Keyboard Input
+docs:
+ - route: /docs/components/kbd-leaf
+ title: Kbd Leaf
+---
+
+
+
+
+
+## Features
+
+- Adds support for keyboard input marks
+
+
+
+## Installation
+
+```bash
+npm install @udecode/plate-kbd
+```
+
+## Usage
+
+```tsx
+import { KbdPlugin } from '@udecode/plate-kbd';
+```
+
+```tsx
+const plugins = [
+ // ...otherPlugins,
+ KbdPlugin,
+];
+```
+
+```tsx
+const components ={
+ [KbdPlugin.key]: KbdLeaf,
+}
+```
+
+- [KbdLeaf](/docs/components/kbd-leaf)
+
+## Plugins
+
+### KbdPlugin
+
+Enables keyboard input marks.
diff --git a/apps/www/content/docs/line-height.mdx b/apps/www/content/docs/line-height.mdx
index eeb43542ca..d5478f6c56 100644
--- a/apps/www/content/docs/line-height.mdx
+++ b/apps/www/content/docs/line-height.mdx
@@ -1,12 +1,11 @@
---
title: Line Height
-description: Adjust the height between lines of text.
docs:
- route: /docs/components/line-height-dropdown-menu
title: Line Height Dropdown Menu
---
-
+
diff --git a/apps/www/content/docs/link.mdx b/apps/www/content/docs/link.mdx
index 35d2c74326..1b02aac67f 100644
--- a/apps/www/content/docs/link.mdx
+++ b/apps/www/content/docs/link.mdx
@@ -1,6 +1,5 @@
---
title: Link
-description: Insert and manage hyperlinks.
docs:
- route: /docs/components/link-element
title: Link Element
@@ -10,7 +9,7 @@ docs:
title: Link Toolbar Button
---
-
+
@@ -47,7 +46,7 @@ const plugins = [
## Plugin Transforms
-### editor.insert.link
+### editor.tf.insert.link
Inserts a link node into the editor.
diff --git a/apps/www/content/docs/list.mdx b/apps/www/content/docs/list.mdx
index 691ca1305c..07ed62d8f3 100644
--- a/apps/www/content/docs/list.mdx
+++ b/apps/www/content/docs/list.mdx
@@ -1,6 +1,5 @@
---
title: List
-description: Organize nestable items in a bulleted or numbered list.
docs:
- route: /docs/components/list-element
title: List Element
@@ -10,7 +9,7 @@ docs:
title: Todo List Element
---
-
+
diff --git a/apps/www/content/docs/markdown.mdx b/apps/www/content/docs/markdown.mdx
index a443cb1be2..86eb96de4c 100644
--- a/apps/www/content/docs/markdown.mdx
+++ b/apps/www/content/docs/markdown.mdx
@@ -1,19 +1,16 @@
---
title: Serializing Markdown
-description: Copy paste from Markdown to Slate.
+toc: false
---
-
+
## Features
-- Convert Markdown content to a Slate value.
-
-
- **Note**: Converting a Slate value to Markdown is not yet supported.
-
+- Convert Markdown string to Slate JSON.
+- Convert Slate JSON to Markdown string.
@@ -25,7 +22,7 @@ npm install @udecode/plate-markdown
## Usage
-### Markdown -> Slate
+### Markdown to Slate
```tsx
import { MarkdownPlugin } from '@udecode/plate-markdown';
@@ -40,7 +37,9 @@ const editor = createPlateEditor({
const value = editor.api.markdown.deserialize('**Hello world!**');
```
-### Slate -> Markdown
+
+
+### Slate to Markdown
Currently supported plugins: paragraph, link, list, heading, italic, bold and code.
diff --git a/apps/www/content/docs/media-placeholder.mdx b/apps/www/content/docs/media-placeholder.mdx
deleted file mode 100644
index 9babde21a1..0000000000
--- a/apps/www/content/docs/media-placeholder.mdx
+++ /dev/null
@@ -1,119 +0,0 @@
----
-title: Media Placeholder
-description: Media placeholders to be used as clickable placeholders for various media types (image, video, audio, file).
-docs:
- - route: https://pro.platejs.org/docs/components/media-placeholder-element
- title: Media Placeholder Element
----
-
-
-
-## Features
-
-- Supports multiple media types: image, video, audio, and file
-- Transforms for inserting different types of media placeholders
-
-
-
-## Installation
-
-```bash
-npm install @udecode/plate-media
-```
-
-## Usage
-
-```tsx
-import {
- AudioPlugin,
- FilePlugin,
- ImagePlugin,
- MediaEmbedPlugin,
- PlaceholderPlugin,
- VideoPlugin,
-} from '@udecode/plate-media/react';
-```
-
-```tsx
-const plugins = [
- // ...otherPlugins,
- PlaceholderPlugin,
-];
-```
-
-```tsx
-const components = {
- // ...otherComponents,
- [PlaceholderPlugin.key]: MediaPlaceholderElement,
-};
-```
-
-- [MediaPlaceholderElement](https://pro.platejs.org/docs/components/media-placeholder-element) (Plus)
-
-## Examples
-
-### Plate UI
-
-Work in progress.
-
-### Plate Plus
-
-
-
-## Plugins
-
-### PlaceholderPlugin
-
-Media placeholder element plugin.
-
-## Transforms
-
-### editor.tf.insert.audioPlaceholder
-
-Inserts an audio placeholder element.
-
-
-
- Options for the insert nodes transform.
-
-
-
-### editor.tf.insert.filePlaceholder
-
-Inserts a file placeholder element.
-
-
-
- Options for the insert nodes transform.
-
-
-
-### editor.tf.insert.imagePlaceholder
-
-Inserts an image placeholder element.
-
-
-
- Options for the insert nodes transform.
-
-
-
-### editor.tf.insert.videoPlaceholder
-
-Inserts a video placeholder element.
-
-
-
- Options for the insert nodes transform.
-
-
-
-## Types
-
-### TPlaceholderElement
-
-```tsx
-type TPlaceholderElement = TElement & {
- mediaType: string;
-};
-```
diff --git a/apps/www/content/docs/media.mdx b/apps/www/content/docs/media.mdx
index 155e6e8820..9f6d37f0c9 100644
--- a/apps/www/content/docs/media.mdx
+++ b/apps/www/content/docs/media.mdx
@@ -1,30 +1,67 @@
---
title: Media
-description: Embed medias like videos or tweets into your document.
docs:
- route: /docs/components/image-element
title: Image Element
+ - route: /docs/components/media-video-element
+ title: Video Element
+ - route: /docs/components/media-audio-element
+ title: Audio Element
+ - route: /docs/components/media-file-element
+ title: File Element
- route: /docs/components/media-embed-element
title: Media Embed Element
- route: /docs/components/media-popover
title: Media Popover
+ - route: /docs/components/media-placeholder-element
+ title: Media Placeholder Element
+ - route: /docs/components/media-upload-toast
+ title: Media Upload Toast
- route: /docs/components/media-toolbar-button
title: Media Toolbar Button
+ - route: https://pro.platejs.org/docs/examples/upload
+ title: Upload
- route: https://pro.platejs.org/docs/components/media-toolbar
title: Media Toolbar
---
-
+
## Features
-- Allows insertion of embeddable media: images, videos, and tweets.
-- Supports multiple media providers: video, youtube, vimeo, dailymotion, youku, coub, twitter.
-- Editable captions.
-- Resizable.
-- Use [Plate Cloud](/docs/cloud) for easy cloud uploads and server-side image resizing.
+### Media Features
+
+- Editable captions
+- Resizable elements
+
+### Media Support
+- **File types**:
+ - Image
+ - Video
+ - Audio
+ - Others (PDF, Word, etc.)
+- **Video providers**:
+ - Local video files
+ - YouTube, Vimeo, Dailymotion, Youku, Coub
+- **Embed providers**:
+ - Tweets
+
+### Upload
+
+- **Multiple upload methods**:
+ - Toolbar button with file picker
+ - Drag and drop from file system
+ - Paste from clipboard (images)
+ - URL embedding for external media
+- **Upload experience**:
+ - Real-time progress tracking
+ - Preview during upload
+ - Automatically converts the placeholder to the appropriate media element (image, video, audio, file) once the upload or embed is submitted
+ - Error handling
+ - File size validation
+ - Type validation
@@ -37,38 +74,311 @@ npm install @udecode/plate-media
## Usage
```tsx
-import { CaptionPlugin } from '@udecode/plate-caption/react';
-import { ImagePlugin, MediaEmbedPlugin } from '@udecode/plate-media/react';
+import {
+ AudioPlugin,
+ FilePlugin,
+ ImagePlugin,
+ MediaEmbedPlugin,
+ PlaceholderPlugin,
+ VideoPlugin,
+} from '@udecode/plate-media/react';
import { SelectOnBackspacePlugin } from '@udecode/plate-select';
+```
+```tsx
const plugins = [
// ...otherPlugins,
- CaptionPlugin.configure({
- options: { plugins: [ImagePlugin, MediaEmbedPlugin] },
- }),
ImagePlugin,
+ VideoPlugin,
+ AudioPlugin,
+ FilePlugin,
MediaEmbedPlugin,
SelectOnBackspacePlugin.configure({
options: {
query: {
- allow: [ImagePlugin.key, MediaEmbedPlugin.key],
+ allow: [ImagePlugin.key, VideoPlugin.key, AudioPlugin.key, FilePlugin.key, MediaEmbedPlugin.key],
},
},
}),
+ PlaceholderPlugin.configure({
+ options: { disableEmptyPlaceholder: true },
+ render: { afterEditable: MediaUploadToast },
+ }),
];
```
+```tsx
+const components = {
+ // ...otherComponents,
+ [ImagePlugin.key]: ImageElement,
+ [VideoPlugin.key]: VideoElement,
+ [AudioPlugin.key]: AudioElement,
+ [FilePlugin.key]: FileElement,
+ [MediaEmbedPlugin.key]: MediaEmbedElement,
+ [PlaceholderPlugin.key]: MediaPlaceholderElement,
+};
+```
+
+### Caption
+
+To enable media captions, use the [Caption Plugin](/docs/caption).
+
+### Upload
+
+There are two ways to implement file uploads in your editor:
+
+1. Using our UploadThing implementation
+2. Creating a custom implementation with your preferred upload solution
+
+#### UploadThing
+
+1. Add [MediaPlaceholderElement](/docs/components/media-placeholder-element) component
+
+2. Add API routes for UploadThing:
+
+
+
+3. Get your secret key from [UploadThing](https://uploadthing.com/dashboard/settings) for free
+4. Add your UploadThing secret key to `.env`:
+
+```bash title=".env"
+UPLOADTHING_TOKEN=xxx
+```
+
+#### Custom Implementation
+
+For custom implementations, you'll need to create an upload hook that matches our interface. This can work with any upload backend (AWS S3, UploadThing, Cloudinary, Firebase Storage, etc.).
+
+The upload hook should implement this interface:
+
+```ts
+interface UseUploadFileProps {
+ onUploadComplete?: (file: UploadedFile) => void;
+ onUploadError?: (error: unknown) => void;
+ headers?: Record;
+ onUploadBegin?: (fileName: string) => void;
+ onUploadProgress?: (progress: { progress: number }) => void;
+ skipPolling?: boolean;
+}
+
+interface UploadedFile {
+ key: string; // Unique identifier
+ url: string; // Public URL of the uploaded file
+ name: string; // Original filename
+ size: number; // File size in bytes
+ type: string; // MIME type
+}
+```
+
+Example implementation with S3 presigned URLs:
+
+```ts
+export function useUploadFile({
+ onUploadComplete,
+ onUploadError,
+ onUploadProgress
+}: UseUploadFileProps = {}) {
+ const [uploadedFile, setUploadedFile] = useState();
+ const [uploadingFile, setUploadingFile] = useState();
+ const [progress, setProgress] = useState(0);
+ const [isUploading, setIsUploading] = useState(false);
+
+ async function uploadFile(file: File) {
+ setIsUploading(true);
+ setUploadingFile(file);
+
+ try {
+ // Get presigned URL and final URL from your backend
+ const { presignedUrl, fileUrl, fileKey } = await fetch('/api/upload', {
+ method: 'POST',
+ body: JSON.stringify({
+ filename: file.name,
+ contentType: file.type,
+ }),
+ }).then(r => r.json());
+
+ // Upload to S3 using presigned URL
+ await axios.put(presignedUrl, file, {
+ headers: { 'Content-Type': file.type },
+ onUploadProgress: (progressEvent) => {
+ const progress = (progressEvent.loaded / progressEvent.total) * 100;
+ setProgress(progress);
+ onUploadProgress?.({ progress });
+ },
+ });
+
+ const uploadedFile = {
+ key: fileKey,
+ url: fileUrl,
+ name: file.name,
+ size: file.size,
+ type: file.type,
+ };
+
+ setUploadedFile(uploadedFile);
+ onUploadComplete?.(uploadedFile);
+
+ return uploadedFile;
+ } catch (error) {
+ onUploadError?.(error);
+ throw error;
+ } finally {
+ setProgress(0);
+ setIsUploading(false);
+ setUploadingFile(undefined);
+ }
+ }
+
+ return {
+ isUploading,
+ progress,
+ uploadFile,
+ uploadedFile,
+ uploadingFile,
+ };
+}
+```
+
+## Examples
+
+### Plate UI
+
+Refer to the preview above.
+
+### Plate Plus
+
+
+
## Plugins
+### PlaceholderPlugin
+
+Plugin for void media placeholder elements. Handles file uploads, drag & drop, and clipboard paste events.
+
+
+
+
+
+Configuration for different file types. Default configuration:
+
+```ts
+{
+ audio: {
+ maxFileCount: 1,
+ maxFileSize: '8MB',
+ mediaType: AudioPlugin.key,
+ minFileCount: 1,
+ },
+ blob: {
+ maxFileCount: 1,
+ maxFileSize: '8MB',
+ mediaType: FilePlugin.key,
+ minFileCount: 1,
+ },
+ image: {
+ maxFileCount: 3,
+ maxFileSize: '4MB',
+ mediaType: ImagePlugin.key,
+ minFileCount: 1,
+ },
+ pdf: {
+ maxFileCount: 1,
+ maxFileSize: '4MB',
+ mediaType: FilePlugin.key,
+ minFileCount: 1,
+ },
+ text: {
+ maxFileCount: 1,
+ maxFileSize: '64KB',
+ mediaType: FilePlugin.key,
+ minFileCount: 1,
+ },
+ video: {
+ maxFileCount: 1,
+ maxFileSize: '16MB',
+ mediaType: VideoPlugin.key,
+ minFileCount: 1,
+ },
+}
+```
+
+Supported file types: `'image' | 'video' | 'audio' | 'pdf' | 'text' | 'blob'`
+
+
+
+ The media plugin keys that this config is for: `'audio' | 'file' | 'image' | 'video'`
+
+
+ The maximum number of files of this type that can be uploaded.
+
+
+ The maximum file size for a file of this type. Format: `${1|2|4|8|16|32|64|128|256|512|1024}${B|KB|MB|GB}`
+
+
+ The minimum number of files of this type that must be uploaded.
+
+
+
+
+
+
+Disable empty placeholder when no file is uploading.
+
+- **Default:** `false`
+
+
+
+Disable drag and drop file upload functionality.
+
+- **Default:** `false`
+
+
+
+Maximum number of files that can be uploaded at once, if not specified by `uploadConfig`.
+
+- **Default:** `5`
+
+
+
+Allow multiple files of the same type to be uploaded.
+
+- **Default:** `true`
+
+
+
+
+### MediaPluginOptions
+
+Plugin options used by media plugins.
+
+
+
+ A function to check whether a text string is a URL.
+
+
+ A function to transform the URL.
+
+
+
### ImagePlugin
+Plugin for void image elements. Options extends [MediaPluginOptions](#mediapluginoptions).
+
-Extends `MediaPluginOptions`.
+Extends [MediaPluginOptions](#mediapluginoptions).
-An optional method that will upload the image to a server. The method receives the base64 dataUrl of the uploaded image, and should return the URL of the uploaded image.
+An optional method that will upload the image to a server. The method receives either:
+- A data URL (string) from `FileReader.readAsDataURL`
+- An ArrayBuffer from clipboard data
+
+Should return either:
+- A URL string to the uploaded image
+- The original data URL/ArrayBuffer if no upload is needed
+
+If not provided, the original data URL/ArrayBuffer will be used as the image source.
@@ -79,12 +389,63 @@ Disables URL embed on data insertion if set to true.
+### VideoPlugin
+
+Plugin for void video elements.
+
+### AudioPlugin
+
+Plugin for void audio elements.
+
+### FilePlugin
+
+Plugin for void file elements.
+
### MediaEmbedPlugin
-Options extends `MediaPluginOptions`.
+Plugin for void media embed elements. Options extends `MediaPluginOptions`.
## API Placeholder
+### editor.tf.insert.media()
+
+Inserts media files into the editor with upload placeholders.
+
+
+
+ Files to upload. Validates against configured file types and limits.
+
+
+
+
+ Location to insert the media. Defaults to current selection.
+
+
+ Whether to insert a new block after the media.
+ - **Default:** `true`
+
+
+
+
+
+The transform:
+- Validates files against configured limits (size, count, type)
+- Creates placeholder elements for each file
+- Handles multiple file uploads sequentially
+- Maintains upload history for undo/redo operations
+- Triggers error handling if validation fails
+
+Error codes:
+```ts
+enum UploadErrorCode {
+ INVALID_FILE_TYPE = 400,
+ TOO_MANY_FILES = 402,
+ INVALID_FILE_SIZE = 403,
+ TOO_LESS_FILES = 405,
+ TOO_LARGE = 413,
+}
+```
+
### editor.tf.insert.audioPlaceholder
Inserts a placeholder. Converts to an audio element when completed.
@@ -95,7 +456,6 @@ Inserts a placeholder. Converts to an audio element when completed.
-
### editor.tf.insert.filePlaceholder
Inserts a placeholder. Converts to a file element when completed.
@@ -124,41 +484,47 @@ Inserts a placeholder. Converts to a video element when completed.
+### editor.api.placeholder.addUploadingFile()
-## API Media
+Tracks a file that is currently being uploaded.
+
+
+
+ Unique identifier for the placeholder element.
+
+
+ The file being uploaded.
+
+
-### insertMedia
+### editor.api.placeholder.getUploadingFile()
-Inserts media (image or media embed) into the editor. The type of media to insert is determined by the `type` parameter.
+Gets a file that is currently being uploaded.
-
-The editor instance.
-
-
-
-
-A function that returns a promise resolving to the URL of the media to
-be inserted. If not provided, a prompt will be displayed to enter the
-URL.
-
-
-The type of media to insert. Defaults to the editor's image element
-type.
+
+ Unique identifier for the placeholder element.
+
+
-- **Default:** `editor.getType(ImagePlugin)`
+
+
+ The uploading file if found, undefined otherwise.
+
+
-
-
+### editor.api.placeholder.removeUploadingFile()
-
+Removes a file from the uploading tracking state after upload completes or fails.
+
+
+
+ Unique identifier for the placeholder element to remove.
+
+## API Media
+
### parseMediaUrl
Parses a media URL and returns the data associated with it based on the configured rules of the media plugin.
@@ -199,19 +565,6 @@ Submits the floating media element by setting its URL and performing necessary t
-### MediaPluginOptions
-
-Common attributes shared by image and media embed plugins.
-
-
-
- A function to check whether a text string is a URL.
-
-
- A function to transform the URL.
-
-
-
### EmbedUrlData
A type defining the data returned from parsing an embed URL.
@@ -323,6 +676,7 @@ The key of the media embed element.
+The options for inserting nodes.
@@ -557,3 +911,26 @@ A behavior hook for a media toolbar button.
+
+## Types
+
+### TMediaElement
+
+```tsx
+export interface TMediaElement extends TElement {
+ url: string;
+ id?: string;
+ align?: 'center' | 'left' | 'right';
+ isUpload?: boolean;
+ name?: string;
+ placeholderId?: string;
+}
+```
+
+### TPlaceholderElement
+
+```tsx
+export interface TPlaceholderElement extends TElement {
+ mediaType: string;
+}
+```
\ No newline at end of file
diff --git a/apps/www/content/docs/mention.mdx b/apps/www/content/docs/mention.mdx
index ddc5532b0e..6fbd2266c0 100644
--- a/apps/www/content/docs/mention.mdx
+++ b/apps/www/content/docs/mention.mdx
@@ -1,6 +1,5 @@
---
title: Mention
-description: Enable autocompletion for user mentions.
docs:
- route: /docs/combobox
title: Combobox
@@ -10,7 +9,7 @@ docs:
title: Mention Input Element
---
-
+
diff --git a/apps/www/content/docs/multi-select.mdx b/apps/www/content/docs/multi-select.mdx
new file mode 100644
index 0000000000..1ccd332961
--- /dev/null
+++ b/apps/www/content/docs/multi-select.mdx
@@ -0,0 +1,225 @@
+---
+title: Multi Select
+docs:
+ - route: /docs/components/tag-element
+ - route: /docs/components/select-editor
+---
+
+
+
+
+
+## Features
+
+Unlike traditional input-based multi-selects, this component is built on top of Plate editor, providing:
+
+- Full history support (undo/redo)
+- Native cursor navigation between and within tags
+- Select one to many tags
+- Copy/paste tags
+- Drag and drop to reorder tags
+- Read-only mode
+- Duplicate tags prevention
+- Create new tags, case insensitive
+- Search text cleanup
+- Whitespace trimming
+- Fuzzy search with [cmdk](https://github.com/pacocoursey/cmdk)
+
+
+
+## Installation
+
+```bash
+npm install @udecode/plate-tag
+```
+
+## Usage
+
+```tsx
+import { MultiSelectPlugin } from '@udecode/plate-tag/react';
+import { TagElement } from '@/components/plate-ui/tag-element';
+import {
+ SelectEditor,
+ SelectEditorContent,
+ SelectEditorInput,
+ SelectEditorCombobox,
+ type SelectItem,
+} from '@/components/plate-ui/select-editor';
+
+// Define your items
+const ITEMS: SelectItem[] = [
+ { value: 'React' },
+ { value: 'TypeScript' },
+ { value: 'JavaScript' },
+];
+
+export default function MySelectEditor() {
+ const [value, setValue] = React.useState([ITEMS[0]]);
+
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+- [TagElement](/docs/components/tag-element)
+- [SelectEditor](/docs/components/select-editor)
+
+## Plugins
+
+### TagPlugin
+
+Inline void element plugin.
+
+### MultiSelectPlugin
+
+Extension of the TagPlugin that constrains the editor to tag elements.
+
+## API
+
+### editor.tf.insert.tag
+
+Inserts a new multi-select element at the current selection.
+
+
+
+Properties for the multi-select element:
+
+
+The unique value of the multi-select element.
+
+
+
+
+
+## Hooks
+
+### useSelectedItems
+
+Hook to get the currently selected tag items in the editor.
+
+
+
+ Array of selected tag items, each containing a value and any additional properties.
+
+
+
+### getSelectedItems
+
+Gets all tag items in the editor.
+
+
+
+ The Slate editor instance.
+
+
+
+
+
+ Array of tag items in the editor.
+
+
+
+### isEqualTags
+
+Utility function to compare two sets of tags for equality, ignoring order.
+
+
+
+ The Slate editor instance.
+
+
+ New set of tags to compare against the current editor tags.
+
+
+
+
+
+ `true` if both sets contain the same values, `false` otherwise.
+
+
+
+### useSelectableItems
+
+Hook to get the available items that can be selected, filtered by search and excluding already selected items.
+
+
+
+
+
+Whether to allow creating new items.
+
+- **Default:** `true`
+
+
+Custom filter function for items.
+
+
+Array of available items.
+
+
+Filter function for new items.
+
+
+Position of new items in the list.
+
+- **Default:** `'end'`
+
+
+
+
+
+
+
+ Filtered array of selectable items.
+
+
+
+### useSelectEditorCombobox
+
+Hook to handle combobox behavior in the editor, including text cleanup and item selection.
+
+
+
+
+
+Whether the combobox is open.
+
+
+Function to select the first item in the combobox.
+
+
+Callback when selected items change.
+
+
+
+
+
+## Types
+
+### TTagElement
+
+```ts
+type TTagElement = TElement & {
+ value: string;
+ [key: string]: unknown;
+};
+```
+
+### TagLike
+
+```ts
+type TagLike = {
+ value: string;
+ [key: string]: unknown;
+};
+```
\ No newline at end of file
diff --git a/apps/www/content/docs/node-id.mdx b/apps/www/content/docs/node-id.mdx
new file mode 100644
index 0000000000..04738d3dfd
--- /dev/null
+++ b/apps/www/content/docs/node-id.mdx
@@ -0,0 +1,171 @@
+---
+title: Node ID
+---
+
+{/* */}
+
+
+
+## Features
+
+- Plugin that automatically assigns and manages unique IDs for nodes in the editor.
+- Configurable ID generation and storage
+- Handles node operations (insert, split) with ID preservation
+- Optional ID reuse for undo/redo and copy/paste operations
+
+
+
+## Installation
+
+```bash
+npm install @udecode/plate-node-id
+```
+
+## Usage
+
+```tsx
+import { NodeIdPlugin } from '@udecode/plate-node-id';
+```
+
+```tsx
+const plugins = [
+ // ...otherPlugins,
+ NodeIdPlugin.configure({
+ options: {
+ idKey: 'id',
+ filterInline: true,
+ filterText: true,
+ idCreator: () => nanoid(10),
+ },
+ }),
+];
+```
+
+## Plugins
+
+### NodeIdPlugin
+
+Plugin that automatically assigns and manages unique IDs for nodes in the editor.
+
+
+
+ Disable using existing IDs when inserting nodes.
+ - When `false`: Keeps existing IDs if they don't exist in the document
+ - When `true`: Always generates new IDs
+ - **Default:** `false`
+
+
+
+ Filter inline Element nodes from receiving IDs.
+ - **Default:** `true`
+
+
+
+ Filter Text nodes from receiving IDs.
+ - **Default:** `true`
+
+
+
+ Function to generate unique IDs.
+ - **Default:** `() => nanoid(10)`
+
+
+
+ Property key used to store the ID on nodes.
+ - **Default:** `'id'`
+
+
+
+ Whether to normalize all nodes in the initial value.
+ - When `false`: Only checks first and last nodes
+ - When `true`: Normalizes all nodes
+ - **Default:** `false`
+
+
+
+ Reuse IDs on undo/redo and copy/paste operations.
+ - When `true`: Keeps IDs if they don't exist in document
+ - When `false`: Always generates new IDs (safer across documents)
+ - **Default:** `false`
+
+
+
+ List of node types that should receive IDs.
+
+
+
+ List of node types that should not receive IDs.
+
+
+
+ Custom filter function for nodes that should receive IDs.
+ - **Default:** `() => true`
+
+
+
+### Behavior
+
+The plugin handles several scenarios:
+
+1. **Node Insertion**:
+```tsx
+
+ test
+
+
+// Insert node with existing ID (e.g. copy/paste)
+editor.insertNode(inserted );
+// Results in:
+
+ test
+ inserted {/* Gets new ID */}
+
+
+// Insert multiple nodes
+editor.insertNodes([
+ inserted ,
+ test ,
+]);
+// Results in:
+
+ test
+ inserted
+ test
+
+```
+
+2. **Node Splitting**:
+```tsx
+// Before split
+te|st
+// After split:
+te
+st
+```
+
+3. **Filtering**:
+```tsx
+// With filterText=false
+
+ text
+
+
+// With allow=['p'] exclude=['blockquote']
+text
+quote
+```
+
+4. **Undo/Redo**:
+```tsx
+// With reuseId=true
+editor.insertNode(text );
+editor.undo();
+editor.redo();
+// Node keeps id="1" if not in use
+
+// With reuseId=false
+editor.insertNode(text );
+editor.undo();
+editor.redo();
+// Node gets new id="2"
+```
\ No newline at end of file
diff --git a/apps/www/content/docs/reset-node.mdx b/apps/www/content/docs/reset-node.mdx
index 3b62ae3c9c..b109f7ce86 100644
--- a/apps/www/content/docs/reset-node.mdx
+++ b/apps/www/content/docs/reset-node.mdx
@@ -1,9 +1,8 @@
---
title: Reset Node
-description: Reset the block type using rules.
---
-
+
diff --git a/apps/www/content/docs/select.mdx b/apps/www/content/docs/select.mdx
new file mode 100644
index 0000000000..cb25163746
--- /dev/null
+++ b/apps/www/content/docs/select.mdx
@@ -0,0 +1,152 @@
+---
+title: Select
+---
+
+{/* */}
+
+
+
+## Features
+
+- Set a list of element types to remove on backspace
+- Set a list of element types to select on backspace, instead of removing
+
+
+
+## Installation
+
+```bash
+npm install @udecode/plate-select
+```
+
+## Usage
+
+```tsx
+import { DeletePlugin, SelectOnBackspacePlugin } from '@udecode/plate-select';
+```
+
+```tsx
+const plugins = [
+ // ...otherPlugins,
+ SelectOnBackspacePlugin.configure({
+ options: {
+ query: {
+ allow: ['img', 'hr'],
+ },
+ },
+ }),
+ DeletePlugin,
+];
+```
+
+## Plugins
+
+### DeletePlugin
+
+Plugin that removes empty blocks when pressing delete (forward delete) if they match the query options.
+
+
+
+ Query options to filter which empty blocks can be removed.
+ - **Default:** `{ allow: ['p'] }`
+
+
+
+For example:
+
+```tsx
+const plugins = [
+ DeletePlugin.configure({
+ options: {
+ // Only remove empty paragraphs and blockquotes
+ query: {
+ allow: ['p', 'blockquote'],
+ },
+ },
+ }),
+];
+```
+
+The plugin will:
+1. Check if the current block is empty and matches the query options
+2. If true: Remove the entire block
+3. If false: Use default delete behavior
+
+```tsx
+// Empty paragraph followed by code block
+
+
+
+
+
+ test
+
+
+
+// Pressing delete will remove the empty paragraph instead of nothing
+
+
+ test
+
+
+```
+
+### SelectOnBackspacePlugin
+
+Plugin that selects nodes instead of deleting them when pressing backspace. Useful for nodes like images or horizontal rules.
+
+
+
+ Query options to determine which nodes trigger selection on backspace.
+
+
+ Whether to remove the node if it's empty when backspacing.
+ - **Default:** `false`
+
+
+
+For example:
+
+```tsx
+const plugins = [
+ SelectOnBackspacePlugin.configure({
+ options: {
+ // Select these nodes instead of deleting them
+ query: {
+ allow: ['img', 'hr'],
+ },
+ // Remove current node if empty
+ removeNodeIfEmpty: true,
+ },
+ }),
+];
+```
+
+The plugin will:
+1. When backspace is pressed at the start of a block:
+2. Check if the previous node matches query options
+3. If true:
+ - Select the previous node instead of deleting it
+ - Optionally remove current node if it's empty
+4. If false: Use default backspace behavior
+
+```tsx
+// Empty paragraph after an image
+
+
+
+
+
+
+
+// Pressing backspace will select the image instead of deleting it
+
+
+
+
+
+// If removeNodeIfEmpty is true, the empty paragraph is also removed
+
+
+
+```
\ No newline at end of file
diff --git a/apps/www/content/docs/single-line.mdx b/apps/www/content/docs/single-line.mdx
index 569204048c..47dba87f2e 100644
--- a/apps/www/content/docs/single-line.mdx
+++ b/apps/www/content/docs/single-line.mdx
@@ -1,9 +1,8 @@
---
title: Single Line
-description: Restrict the editor to a single block.
---
-
+
diff --git a/apps/www/content/docs/slash-command.mdx b/apps/www/content/docs/slash-command.mdx
index fb64355191..a7c1871de6 100644
--- a/apps/www/content/docs/slash-command.mdx
+++ b/apps/www/content/docs/slash-command.mdx
@@ -1,6 +1,5 @@
---
title: Slash Command
-description: Slash command menu for quick insertion of various content types.
docs:
- route: /docs/combobox
title: Combobox
@@ -10,7 +9,7 @@ docs:
title: Slash Input Element
---
-
+
diff --git a/apps/www/content/docs/soft-break.mdx b/apps/www/content/docs/soft-break.mdx
index 3118d8913a..40fc65b607 100644
--- a/apps/www/content/docs/soft-break.mdx
+++ b/apps/www/content/docs/soft-break.mdx
@@ -1,9 +1,8 @@
---
title: Soft Break
-description: Insert line breaks within a block of text without starting a new block.
---
-
+
diff --git a/apps/www/content/docs/tabbable.mdx b/apps/www/content/docs/tabbable.mdx
index 347a11d76c..01a740cfaf 100644
--- a/apps/www/content/docs/tabbable.mdx
+++ b/apps/www/content/docs/tabbable.mdx
@@ -1,9 +1,8 @@
---
title: Tabbable
-description: Maintain a consistent tab order for tabbable elements.
---
-
+
diff --git a/apps/www/content/docs/table.mdx b/apps/www/content/docs/table.mdx
index 4934612bfc..6892d0b83d 100644
--- a/apps/www/content/docs/table.mdx
+++ b/apps/www/content/docs/table.mdx
@@ -1,6 +1,5 @@
---
title: Table
-description: Organize and display data in a structured and resizable tabular format.
docs:
- route: /docs/components/table-cell-element
title: Table Cell Element
@@ -12,9 +11,7 @@ docs:
title: Table Row Element
---
-
-
-
+
@@ -50,6 +47,10 @@ const plugins = [
];
```
+### Without merging
+
+
+
## Plugins
### TablePlugin
diff --git a/apps/www/content/docs/tag.mdx b/apps/www/content/docs/tag.mdx
new file mode 100644
index 0000000000..4d7de1360b
--- /dev/null
+++ b/apps/www/content/docs/tag.mdx
@@ -0,0 +1,7 @@
+---
+title: Tag
+docs:
+ - route: /docs/components/tag-element
+ title: Tag Element
+---
+
diff --git a/apps/www/content/docs/toc.mdx b/apps/www/content/docs/toc.mdx
index 1e98845e1a..aeacdeab8c 100644
--- a/apps/www/content/docs/toc.mdx
+++ b/apps/www/content/docs/toc.mdx
@@ -1,6 +1,5 @@
---
title: Table of Contents
-description: Renders a table of contents element with clickable links to headings in the document.
docs:
- route: components/toc-element
title: Toc Element
@@ -8,7 +7,7 @@ docs:
title: Toc Sidebar
---
-
+
@@ -39,7 +38,6 @@ const plugins = [
NodeIdPlugin,
TocPlugin.configure({
options: {
- scrollContainerSelector: `#your-scroll-container-id`,
topOffset: 80,
},
}),
@@ -55,27 +53,36 @@ const components = {
- [TocElement](/docs/components/toc-element)
-### Set scroll container
+### Scroll container
-The `TocPlugin` requires a scroll container selector to function properly. This selector is used to identify the scrollable area containing your content.
+- If your scrolling element is [EditorContainer](/docs/components/editor), you can skip this step.
+- If your scrolling element is the editor container, pass `useEditorContainerRef()` as the `ref` prop. For example:
```tsx
-TocPlugin.configure({
- options: {
- scrollContainerSelector: '#your-scroll-container-id',
- },
-})
-```
-
-You can use any valid CSS selector:
+// Should be rendered below component
+function EditorContainer({ children }: { children: React.ReactNode }) {
+ const containerRef = useEditorContainerRef();
-- ID: `#scrollable-container`
-- Class: `.content-wrapper`
-- Nested selector: `body #main-content`
+ return {children}
;
+}
+```
-Apply this selector to your ` ` component or a wrapper `` around it, depending on your layout.
+- If your scrolling element is an ancestor of the editor container, pass `useEditorScrollRef()` as the `ref` prop. For example:
-For consistency, use the same container for both `TocPlugin` and `BlockSelectionPlugin`.
+```tsx
+// Should be rendered below
component
+function Layout() {
+ const scrollRef = useEditorScrollRef();
+
+ return (
+
+
+
+
+
+ );
+}
+```
### Components
@@ -111,14 +118,7 @@ The top offset to apply when scrolling to a heading.
A custom function to query headings from the editor.
-
-
-The CSS selector for the scrollable container. Required to use scrolling behavior.
-
-- **Default:** `'#scroll_container'`
-
-
## Transforms
### insertToc
diff --git a/apps/www/content/docs/toggle.mdx b/apps/www/content/docs/toggle.mdx
index ed5875aa2d..101f6be7ee 100644
--- a/apps/www/content/docs/toggle.mdx
+++ b/apps/www/content/docs/toggle.mdx
@@ -1,6 +1,5 @@
---
title: Toggle
-description: Add Toggles to your document.
docs:
- route: /docs/components/toggle-element
title: Toggle Element
@@ -8,7 +7,7 @@ docs:
title: Toggle Button
---
-
+
diff --git a/apps/www/content/docs/trailing-block.mdx b/apps/www/content/docs/trailing-block.mdx
new file mode 100644
index 0000000000..1a8c6f0c51
--- /dev/null
+++ b/apps/www/content/docs/trailing-block.mdx
@@ -0,0 +1,62 @@
+---
+title: Trailing Block
+---
+
+
+
+
+
+## Features
+
+- Ensures a specific block type is always present at the end of the document.
+
+
+
+## Installation
+
+```bash
+npm install @udecode/plate-trailing-block
+```
+
+## Usage
+
+```tsx
+import { TrailingBlockPlugin } from '@udecode/plate-trailing-block';
+```
+
+```tsx
+const plugins = [
+ // ...otherPlugins,
+ TrailingBlockPlugin.configure({
+ options: {
+ level: 0,
+ type: 'p',
+ },
+ }),
+];
+```
+
+## Plugins
+
+### TrailingBlockPlugin
+
+
+
+ Level where the trailing node should be, the first level being 0.
+ - **Default:** `0` (root level)
+
+
+ Type of the trailing block.
+ - **Default:** `editor.getType(ParagraphPlugin)`
+
+
+ Filter nodes that match any of these types.
+ - **Default:** `[]` (all types)
+
+
+ Filter nodes that don't match any of these types.
+
+
+ Custom filter function.
+
+
\ No newline at end of file
diff --git a/apps/www/content/docs/upload.mdx b/apps/www/content/docs/upload.mdx
deleted file mode 100644
index 943184b4e1..0000000000
--- a/apps/www/content/docs/upload.mdx
+++ /dev/null
@@ -1,34 +0,0 @@
----
-title: Upload
-description: Allows you to upload files and images into your editor.
-docs:
- - route: https://pro.platejs.org/docs/components/upload
- title: Upload
----
-
-{/* ### UploadThing Integration
-
-This component uses UploadThing for file uploads. UploadThing provides a simple and efficient way to handle file uploads in your application.
-
-To use UploadThing:
-
-1. Set up an UploadThing account and configure your upload endpoints.
-2. Install the UploadThing client in your project:
-
-```bash
-npm install uploadthing
-```
-
-3. Configure the UploadThing client in your application.
-
-For more details on setting up UploadThing, refer to their [documentation](https://docs.uploadthing.com/). */}
-
-## Examples
-
-### Plate UI
-
-Work in progress.
-
-### Plate Plus
-
-
\ No newline at end of file
diff --git a/apps/www/contentlayer.config.js b/apps/www/contentlayer.config.js
index a7b0a2333c..2328c95c5d 100644
--- a/apps/www/contentlayer.config.js
+++ b/apps/www/contentlayer.config.js
@@ -60,7 +60,7 @@ export const Doc = defineDocumentType(() => ({
type: 'boolean',
},
description: {
- required: true,
+ // required: true,
type: 'string',
},
docs: {
diff --git a/apps/www/next.config.ts b/apps/www/next.config.ts
index 69dcb75eb0..3676e3a373 100644
--- a/apps/www/next.config.ts
+++ b/apps/www/next.config.ts
@@ -26,6 +26,11 @@ const nextConfig = async (phase: string) => {
],
},
+ outputFileTracingIncludes: {
+ '/api/registry/*': ['./src/registry/**/*'],
+ '/docs/*': ['./src/registry/**/*'],
+ },
+
// Configure domains to allow for optimized image loading.
// https://nextjs.org/docs/api-reference/next.config.js/react-strict-mod
reactStrictMode: true,
@@ -37,44 +42,7 @@ const nextConfig = async (phase: string) => {
// ignoreDuringBuilds: true,
// },
- serverExternalPackages: ['@prisma/client'],
-
staticPageGenerationTimeout: 1200,
-
- // redirects() {
- // return [
- // {
- // source: '/components',
- // destination: '/docs/components/accordion',
- // permanent: true,
- // },
- // {
- // source: '/docs/components',
- // destination: '/docs/components/accordion',
- // permanent: true,
- // },
- // {
- // source: '/examples',
- // destination: '/examples/dashboard',
- // permanent: false,
- // },
- // {
- // source: '/docs/primitives/:path*',
- // destination: '/docs/components/:path*',
- // permanent: true,
- // },
- // {
- // source: '/figma',
- // destination: '/docs/figma',
- // permanent: true,
- // },
- // {
- // source: '/docs/forms',
- // destination: '/docs/forms/react-hook-form',
- // permanent: false,
- // },
- // ];
- // },
};
if (phase === 'phase-development-server') {
diff --git a/apps/www/package.json b/apps/www/package.json
index fcd52c7871..a2a303c607 100644
--- a/apps/www/package.json
+++ b/apps/www/package.json
@@ -15,7 +15,8 @@
"lint:fix": "yarn lint --fix",
"preview": "next build && next start",
"start": "next start",
- "typecheck": "yarn prebuild && tsc --noEmit"
+ "typecheck": "yarn prebuild && tsc --noEmit",
+ "typecheck:watch": "yarn typecheck --watch"
},
"browserslist": {
"production": [
@@ -37,7 +38,8 @@
"@ai-sdk/openai": "^0.0.67",
"@ariakit/react": "0.4.11",
"@faker-js/faker": "^9.0.2",
- "@next/third-parties": "15.0.0",
+ "@hookform/resolvers": "3.9.1",
+ "@next/third-parties": "15.0.3",
"@radix-ui/colors": "3.0.0",
"@radix-ui/react-accessible-icon": "^1.1.0",
"@radix-ui/react-accordion": "^1.2.0",
@@ -69,6 +71,8 @@
"@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-toolbar": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
+ "@slate-yjs/react": "1.1.0",
+ "@udecode/cmdk": "workspace:^",
"@udecode/cn": "workspace:^",
"@udecode/plate": "workspace:^",
"@udecode/plate-ai": "workspace:^",
@@ -86,7 +90,6 @@
"@udecode/plate-common": "workspace:^",
"@udecode/plate-core": "workspace:^",
"@udecode/plate-csv": "workspace:^",
- "@udecode/plate-cursor": "workspace:^",
"@udecode/plate-date": "workspace:^",
"@udecode/plate-diff": "workspace:^",
"@udecode/plate-dnd": "workspace:^",
@@ -122,6 +125,7 @@
"@udecode/plate-suggestion": "workspace:^",
"@udecode/plate-tabbable": "workspace:^",
"@udecode/plate-table": "workspace:^",
+ "@udecode/plate-tag": "workspace:^",
"@udecode/plate-test-utils": "workspace:^",
"@udecode/plate-toggle": "workspace:^",
"@udecode/plate-trailing-block": "workspace:^",
@@ -132,6 +136,7 @@
"@udecode/slate-react": "workspace:^",
"@udecode/slate-utils": "workspace:^",
"@udecode/utils": "workspace:^",
+ "@uploadthing/react": "7.1.0",
"@vercel/og": "^0.6.2",
"ai": "^3.4.10",
"class-variance-authority": "^0.7.0",
@@ -139,30 +144,37 @@
"contentlayer2": "^0.4.6",
"date-fns": "^3.6.0",
"framer-motion": "^11.5.4",
+ "fzf": "0.5.2",
+ "html2canvas": "^1.4.1",
"lodash.template": "^4.5.0",
- "lucide-react": "^0.441.0",
+ "lucide-react": "0.460.0",
"match-sorter": "6.3.4",
- "next": "15.0.0",
+ "next": "15.0.3",
"next-contentlayer2": "^0.4.6",
- "next-themes": "^0.3.0",
+ "next-themes": "^0.4.3",
"nuqs": "^2.0.3",
+ "pdf-lib": "^1.17.1",
"prismjs": "^1.29.0",
"react": "^18.3.1",
"react-day-picker": "^8.10.1",
"react-dnd": "16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.3.1",
+ "react-hook-form": "7.53.2",
"react-lite-youtube-embed": "^2.4.0",
"react-markdown": "9.0.1",
+ "react-player": "2.16.0",
"react-resizable-panels": "^2.0.22",
"react-syntax-highlighter": "^15.5.0",
"react-tweet": "^3.2.1",
"react-wrap-balancer": "^1.1.1",
+ "remark-emoji": "5.0.1",
"sass": "^1.78.0",
- "slate": "0.103.0",
- "slate-history": "0.109.0",
+ "slate": "0.110.2",
+ "slate-dom": "0.111.0",
+ "slate-history": "0.110.3",
"slate-hyperscript": "0.100.0",
- "slate-react": "0.110.1",
+ "slate-react": "0.111.0",
"slate-test-utils": "1.3.2",
"sonner": "^1.5.0",
"swr": "2.2.6-beta.3",
@@ -170,7 +182,9 @@
"ts-morph": "^22.0.0",
"unist-builder": "4.0.0",
"unist-util-visit": "^5.0.0",
- "vaul": "0.9.0"
+ "uploadthing": "7.2.0",
+ "use-file-picker": "2.1.2",
+ "vaul": "1.1.1"
},
"devDependencies": {
"@shikijs/compat": "^1.17.5",
@@ -192,6 +206,7 @@
"remark-gfm": "^4.0.0",
"rimraf": "^6.0.1",
"shiki": "^1.17.5",
+ "tailwind-scrollbar-hide": "1.1.7",
"tailwindcss": "^3.4.11",
"tailwindcss-animate": "^1.0.7",
"ts-node": "^10.9.2",
diff --git a/apps/www/public/ai-selection.png b/apps/www/public/ai-selection.png
deleted file mode 100644
index a512f46326..0000000000
Binary files a/apps/www/public/ai-selection.png and /dev/null differ
diff --git a/apps/www/public/r/icons/index.json b/apps/www/public/r/icons/index.json
new file mode 100644
index 0000000000..bf65dca463
--- /dev/null
+++ b/apps/www/public/r/icons/index.json
@@ -0,0 +1,150 @@
+{
+ "AlertCircle": {
+ "lucide": "AlertCircle",
+ "radix": "ExclamationTriangleIcon"
+ },
+ "ArrowLeft": {
+ "lucide": "ArrowLeft",
+ "radix": "ArrowLeftIcon"
+ },
+ "ArrowRight": {
+ "lucide": "ArrowRight",
+ "radix": "ArrowRightIcon"
+ },
+ "ArrowUpDown": {
+ "lucide": "ArrowUpDown",
+ "radix": "CaretSortIcon"
+ },
+ "BellRing": {
+ "lucide": "BellRing",
+ "radix": "BellIcon"
+ },
+ "Bold": {
+ "lucide": "Bold",
+ "radix": "FontBoldIcon"
+ },
+ "Calculator": {
+ "lucide": "Calculator",
+ "radix": "ComponentPlaceholderIcon"
+ },
+ "Calendar": {
+ "lucide": "Calendar",
+ "radix": "CalendarIcon"
+ },
+ "Check": {
+ "lucide": "Check",
+ "radix": "CheckIcon"
+ },
+ "ChevronDown": {
+ "lucide": "ChevronDown",
+ "radix": "ChevronDownIcon"
+ },
+ "ChevronLeft": {
+ "lucide": "ChevronLeft",
+ "radix": "ChevronLeftIcon"
+ },
+ "ChevronRight": {
+ "lucide": "ChevronRight",
+ "radix": "ChevronRightIcon"
+ },
+ "ChevronUp": {
+ "lucide": "ChevronUp",
+ "radix": "ChevronUpIcon"
+ },
+ "ChevronsUpDown": {
+ "lucide": "ChevronsUpDown",
+ "radix": "CaretSortIcon"
+ },
+ "Circle": {
+ "lucide": "Circle",
+ "radix": "DotFilledIcon"
+ },
+ "Copy": {
+ "lucide": "Copy",
+ "radix": "CopyIcon"
+ },
+ "CreditCard": {
+ "lucide": "CreditCard",
+ "radix": "ComponentPlaceholderIcon"
+ },
+ "GripVertical": {
+ "lucide": "GripVertical",
+ "radix": "DragHandleDots2Icon"
+ },
+ "Italic": {
+ "lucide": "Italic",
+ "radix": "FontItalicIcon"
+ },
+ "Loader2": {
+ "lucide": "Loader2",
+ "radix": "ReloadIcon"
+ },
+ "Mail": {
+ "lucide": "Mail",
+ "radix": "EnvelopeClosedIcon"
+ },
+ "MailOpen": {
+ "lucide": "MailOpen",
+ "radix": "EnvelopeOpenIcon"
+ },
+ "Minus": {
+ "lucide": "Minus",
+ "radix": "MinusIcon"
+ },
+ "Moon": {
+ "lucide": "Moon",
+ "radix": "MoonIcon"
+ },
+ "MoreHorizontal": {
+ "lucide": "MoreHorizontal",
+ "radix": "DotsHorizontalIcon"
+ },
+ "PanelLeft": {
+ "lucide": "PanelLeft",
+ "radix": "ViewVerticalIcon"
+ },
+ "Plus": {
+ "lucide": "Plus",
+ "radix": "PlusIcon"
+ },
+ "Search": {
+ "lucide": "Search",
+ "radix": "MagnifyingGlassIcon"
+ },
+ "Send": {
+ "lucide": "Send",
+ "radix": "PaperPlaneIcon"
+ },
+ "Settings": {
+ "lucide": "Settings",
+ "radix": "GearIcon"
+ },
+ "Slash": {
+ "lucide": "Slash",
+ "radix": "SlashIcon"
+ },
+ "Smile": {
+ "lucide": "Smile",
+ "radix": "FaceIcon"
+ },
+ "Sun": {
+ "lucide": "Sun",
+ "radix": "SunIcon"
+ },
+ "Terminal": {
+ "lucide": "Terminal",
+ "radix": "RocketIcon"
+ },
+ "Underline": {
+ "lucide": "Underline",
+ "radix": "UnderlineIcon"
+ },
+ "User": {
+ "lucide": "User",
+ "radix": "PersonIcon"
+ },
+ "X": {
+ "lucide": "X",
+ "radix": "Cross2Icon"
+ }
+}
\ No newline at end of file
diff --git a/apps/www/public/r/index.json b/apps/www/public/r/index.json
index 03532355a2..91522e2b79 100644
--- a/apps/www/public/r/index.json
+++ b/apps/www/public/r/index.json
@@ -192,8 +192,7 @@
"description": "A resizable column component for layout.",
"docs": [
{
- "route": "/docs/column",
- "title": "Column"
+ "route": "/docs/column"
},
{
"route": "https://pro.platejs.org/docs/components/column-element"
@@ -224,8 +223,7 @@
"description": "A resizable column component for layout.",
"docs": [
{
- "route": "/docs/column",
- "title": "Column"
+ "route": "/docs/column"
},
{
"route": "https://pro.platejs.org/docs/components/column-group-element"
@@ -264,7 +262,7 @@
}
],
"examples": [
- "comment-demo",
+ "comments-demo",
"comments-pro"
]
},
@@ -350,8 +348,7 @@
"description": "A drawing component powered by Excalidraw.",
"docs": [
{
- "route": "/docs/excalidraw",
- "title": "Excalidraw"
+ "route": "/docs/excalidraw"
}
]
},
@@ -400,6 +397,11 @@
"dependencies": [],
"doc": {
"description": "A text highlighter with customizable colors.",
+ "docs": [
+ {
+ "route": "/docs/highlight"
+ }
+ ],
"examples": [
"highlight-demo"
]
@@ -427,7 +429,7 @@
}
],
"examples": [
- "hr-demo"
+ "horizontal-rule-demo"
],
"title": "Horizontal Rule Element"
},
@@ -454,6 +456,9 @@
{
"route": "/docs/media"
},
+ {
+ "route": "/docs/api/resizable"
+ },
{
"route": "https://pro.platejs.org/docs/components/image-element"
}
@@ -513,6 +518,12 @@
],
"doc": {
"description": "A component for styling keyboard shortcuts.",
+ "docs": [
+ {
+ "route": "/docs/kbd",
+ "title": "Keyboard Input"
+ }
+ ],
"examples": [
"kbd-demo"
]
@@ -565,8 +576,7 @@
"description": "A list element for ordered and unordered items.",
"docs": [
{
- "route": "/docs/list",
- "title": "List"
+ "route": "/docs/list"
}
],
"examples": [
@@ -585,6 +595,39 @@
],
"type": "registry:ui"
},
+ {
+ "dependencies": [
+ "@udecode/plate-media",
+ "@udecode/plate-resizable"
+ ],
+ "doc": {
+ "description": "An audio player component with caption support.",
+ "docs": [
+ {
+ "route": "/docs/media"
+ },
+ {
+ "route": "https://pro.platejs.org/docs/components/media-audio-element"
+ }
+ ],
+ "examples": [
+ "media-demo",
+ "upload-pro"
+ ]
+ },
+ "files": [
+ {
+ "path": "plate-ui/media-audio-element.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "media-audio-element",
+ "registryDependencies": [
+ "caption",
+ "plate-element"
+ ],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@udecode/plate-media",
@@ -598,13 +641,16 @@
{
"route": "/docs/media"
},
+ {
+ "route": "/docs/api/resizable"
+ },
{
"route": "https://pro.platejs.org/docs/components/media-embed-element"
}
],
"examples": [
"media-demo",
- "media-toolbar-pro"
+ "upload-pro"
]
},
"files": [
@@ -622,6 +668,113 @@
],
"type": "registry:ui"
},
+ {
+ "dependencies": [
+ "@udecode/plate-media",
+ "@udecode/plate-resizable"
+ ],
+ "doc": {
+ "description": "A file attachment component with download capability and caption.",
+ "docs": [
+ {
+ "route": "/docs/media"
+ },
+ {
+ "route": "https://pro.platejs.org/docs/components/media-file-element"
+ }
+ ],
+ "examples": [
+ "media-demo",
+ "upload-pro"
+ ]
+ },
+ "files": [
+ {
+ "path": "plate-ui/media-file-element.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "media-file-element",
+ "registryDependencies": [
+ "caption",
+ "plate-element"
+ ],
+ "type": "registry:ui"
+ },
+ {
+ "dependencies": [
+ "@udecode/plate-media",
+ "use-file-picker"
+ ],
+ "doc": {
+ "description": "A placeholder for media upload progress indication.",
+ "docs": [
+ {
+ "route": "/docs/media"
+ },
+ {
+ "route": "https://pro.platejs.org/docs/components/media-placeholder-element"
+ }
+ ],
+ "examples": [
+ "media-demo",
+ "upload-pro"
+ ]
+ },
+ "files": [
+ {
+ "path": "plate-ui/media-placeholder-element.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "media-placeholder-element",
+ "registryDependencies": [
+ "plate-element",
+ "spinner",
+ "uploadthing"
+ ],
+ "type": "registry:ui"
+ },
+ {
+ "dependencies": [
+ "@udecode/plate-media",
+ "@udecode/plate-resizable",
+ "react-player",
+ "react-lite-youtube-embed"
+ ],
+ "doc": {
+ "description": "A video player component with YouTube and file upload support.",
+ "docs": [
+ {
+ "route": "/docs/media"
+ },
+ {
+ "route": "/docs/api/resizable"
+ },
+ {
+ "route": "https://pro.platejs.org/docs/components/media-video-element"
+ }
+ ],
+ "examples": [
+ "media-demo",
+ "upload-pro"
+ ]
+ },
+ "files": [
+ {
+ "path": "plate-ui/media-video-element.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "media-video-element",
+ "registryDependencies": [
+ "media-popover",
+ "caption",
+ "resizable",
+ "plate-element"
+ ],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@udecode/plate-mention"
@@ -716,7 +869,12 @@
{
"dependencies": [],
"doc": {
- "description": "A component that highlights search results in text."
+ "description": "A component that highlights search results in text.",
+ "docs": [
+ {
+ "route": "/docs/highlight"
+ }
+ ]
},
"files": [
{
@@ -742,6 +900,10 @@
"doc": {
"description": "A command input component for inserting various elements.",
"docs": [
+ {
+ "route": "/docs/slash-command",
+ "title": "Slash"
+ },
{
"route": "https://pro.platejs.org/docs/components/slash-input-element"
}
@@ -858,6 +1020,29 @@
],
"type": "registry:ui"
},
+ {
+ "dependencies": [],
+ "doc": {
+ "description": "A tag element component with selection states and styling.",
+ "docs": [
+ {
+ "route": "/docs/multi-select"
+ }
+ ],
+ "examples": [
+ "select-editor-demo"
+ ]
+ },
+ "files": [
+ {
+ "path": "plate-ui/tag-element.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "tag-element",
+ "registryDependencies": [],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@udecode/plate-heading"
@@ -866,8 +1051,7 @@
"description": "A table of contents component with links to document headings.",
"docs": [
{
- "route": "/docs/basic-elements",
- "title": "Basic Elements"
+ "route": "/docs/basic-elements"
},
{
"route": "https://pro.platejs.org/docs/components/toc-element"
@@ -898,8 +1082,7 @@
"description": "A checkbox list element with interactive todo items.",
"docs": [
{
- "route": "/docs/list",
- "title": "List"
+ "route": "/docs/list"
}
],
"examples": [
@@ -927,8 +1110,7 @@
"description": "A collapsible component for toggling content visibility.",
"docs": [
{
- "route": "/docs/toggle",
- "title": "Toggle"
+ "route": "/docs/toggle"
}
],
"examples": [
@@ -948,12 +1130,37 @@
],
"type": "registry:ui"
},
+ {
+ "dependencies": [
+ "@radix-ui/react-alert-dialog"
+ ],
+ "doc": {
+ "description": "A modal dialog that interrupts the user with important content and expects a response.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/alert-dialog"
+ }
+ },
+ "files": [
+ {
+ "path": "plate-ui/alert-dialog.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "alert-dialog",
+ "registryDependencies": [
+ "button"
+ ],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@radix-ui/react-avatar"
],
"doc": {
- "description": "An image element with a fallback for representing the user."
+ "description": "An image element with a fallback for representing the user.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/avatar"
+ }
},
"files": [
{
@@ -990,7 +1197,10 @@
"react-day-picker@8.10.1"
],
"doc": {
- "description": "A date field component that allows users to enter and edit date."
+ "description": "A date field component that allows users to enter and edit date.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/calendar"
+ }
},
"files": [
{
@@ -1009,7 +1219,10 @@
"@radix-ui/react-checkbox"
],
"doc": {
- "description": "A control that allows the user to toggle between checked and not checked."
+ "description": "A control that allows the user to toggle between checked and not checked.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/checkbox"
+ }
},
"files": [
{
@@ -1024,10 +1237,13 @@
{
"dependencies": [
"@radix-ui/react-dialog",
- "cmdk"
+ "@udecode/cmdk"
],
"doc": {
- "description": "Fast, composable, unstyled command menu for React."
+ "description": "Fast, composable, unstyled command menu for React.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/command"
+ }
},
"files": [
{
@@ -1067,7 +1283,10 @@
"@radix-ui/react-dialog"
],
"doc": {
- "description": "A window overlaid on either the primary window or another dialog window, rendering the content underneath inert."
+ "description": "A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/dialog"
+ }
},
"files": [
{
@@ -1084,7 +1303,10 @@
"@radix-ui/react-dropdown-menu"
],
"doc": {
- "description": "Displays a menu to the user — such as a set of actions or functions — triggered by a button."
+ "description": "Displays a menu to the user — such as a set of actions or functions — triggered by a button.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/dropdown-menu"
+ }
},
"files": [
{
@@ -1096,10 +1318,39 @@
"registryDependencies": [],
"type": "registry:ui"
},
+ {
+ "dependencies": [
+ "react-hook-form",
+ "zod",
+ "@hookform/resolvers",
+ "@radix-ui/react-label",
+ "@radix-ui/react-slot"
+ ],
+ "doc": {
+ "description": "Building forms with React Hook Form and Zod.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/form"
+ }
+ },
+ "files": [
+ {
+ "path": "plate-ui/form.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "form",
+ "registryDependencies": [
+ "label"
+ ],
+ "type": "registry:ui"
+ },
{
"dependencies": [],
"doc": {
- "description": "Displays a form input field or a component that looks like an input field."
+ "description": "Displays a form input field or a component that looks like an input field.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/input"
+ }
},
"files": [
{
@@ -1111,12 +1362,35 @@
"registryDependencies": [],
"type": "registry:ui"
},
+ {
+ "dependencies": [
+ "@radix-ui/react-label"
+ ],
+ "doc": {
+ "description": "Renders an accessible label associated with controls.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/label"
+ }
+ },
+ "files": [
+ {
+ "path": "plate-ui/label.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "label",
+ "registryDependencies": [],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@radix-ui/react-popover"
],
"doc": {
- "description": "Displays rich content in a portal, triggered by a button."
+ "description": "Displays rich content in a portal, triggered by a button.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/popover"
+ }
},
"files": [
{
@@ -1133,7 +1407,10 @@
"@radix-ui/react-separator"
],
"doc": {
- "description": "Visually or semantically separates content."
+ "description": "Visually or semantically separates content.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/separator"
+ }
},
"files": [
{
@@ -1170,7 +1447,10 @@
"@radix-ui/react-tooltip"
],
"doc": {
- "description": "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it."
+ "description": "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/tooltip"
+ }
},
"files": [
{
@@ -1182,6 +1462,43 @@
"registryDependencies": [],
"type": "registry:ui"
},
+ {
+ "doc": {
+ "description": "A loading spinner component with size variants."
+ },
+ "files": [
+ {
+ "path": "plate-ui/spinner.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "spinner",
+ "registryDependencies": [],
+ "type": "registry:ui"
+ },
+ {
+ "dependencies": [
+ "@slate-yjs/react"
+ ],
+ "doc": {
+ "description": "A cursor overlay to display multiplayer cursors in the yjs plugin.",
+ "docs": [
+ {
+ "route": "/docs/collaboration"
+ }
+ ],
+ "examples": []
+ },
+ "files": [
+ {
+ "path": "plate-ui/remote-cursor-overlay.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "remote-cursor-overlay",
+ "registryDependencies": [],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@udecode/plate-ai",
@@ -1328,7 +1645,8 @@
"registryDependencies": [
"calendar",
"plate-element",
- "context-menu"
+ "context-menu",
+ "use-is-touch-device"
],
"type": "registry:ui"
},
@@ -1362,6 +1680,37 @@
"registryDependencies": [],
"type": "registry:ui"
},
+ {
+ "dependencies": [
+ "html2canvas",
+ "pdf-lib"
+ ],
+ "doc": {
+ "description": "A toolbar button to export editor content as PDF.",
+ "docs": [
+ {
+ "route": "/docs/export",
+ "title": "Export"
+ }
+ ],
+ "examples": [
+ "basic-nodes-demo"
+ ],
+ "label": "New",
+ "title": "Export Toolbar Button"
+ },
+ "files": [
+ {
+ "path": "plate-ui/export-toolbar-button.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "export-toolbar-button",
+ "registryDependencies": [
+ "toolbar"
+ ],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@udecode/plate-caption"
@@ -1370,15 +1719,14 @@
"description": "A text field for adding captions to media elements.",
"docs": [
{
- "route": "/docs/caption",
- "title": "Caption"
+ "route": "/docs/caption"
},
{
"route": "https://pro.platejs.org/docs/components/caption"
}
],
"examples": [
- "upload-demo"
+ "media-demo"
]
},
"files": [
@@ -1402,8 +1750,7 @@
"description": "A color picker with text and background color controls.",
"docs": [
{
- "route": "/docs/font",
- "title": "Font"
+ "route": "/docs/font"
},
{
"route": "https://pro.platejs.org/docs/components/color-dropdown-menu"
@@ -1457,15 +1804,14 @@
"description": "A toolbar button for adding inline comments.",
"docs": [
{
- "route": "/docs/comments",
- "title": "Comments"
+ "route": "/docs/comments"
},
{
"route": "https://pro.platejs.org/docs/components/comment-toolbar-button"
}
],
"examples": [
- "comment-demo",
+ "comments-demo",
"floating-toolbar-demo",
"comments-pro"
]
@@ -1489,15 +1835,14 @@
"description": "A popover interface for managing comments and replies.",
"docs": [
{
- "route": "/docs/comments",
- "title": "Comments"
+ "route": "/docs/comments"
},
{
"route": "https://pro.platejs.org/docs/components/comments-popover"
}
],
"examples": [
- "comment-demo",
+ "comments-demo",
"comments-pro"
]
},
@@ -1544,13 +1889,14 @@
},
{
"dependencies": [
- "@udecode/plate-cursor",
- "@udecode/plate-dnd",
"@udecode/plate-selection"
],
"doc": {
- "description": "A visual overlay for collaborative cursors and selections.",
+ "description": "A visual overlay for cursors and selections.",
"docs": [
+ {
+ "route": "/docs/cursor-overlay"
+ },
{
"route": "https://pro.platejs.org/docs/components/cursor-overlay"
}
@@ -1632,8 +1978,7 @@
"examples": [
"editor-default",
"editor-disabled",
- "editor-full-width",
- "editor-ai-chat"
+ "editor-full-width"
]
},
"files": [
@@ -1646,6 +1991,39 @@
"registryDependencies": [],
"type": "registry:ui"
},
+ {
+ "dependencies": [
+ "fzf@0.5.2",
+ "@udecode/plate-tag",
+ "@udecode/cmdk"
+ ],
+ "doc": {
+ "description": "An editor to select tags.",
+ "docs": [
+ {
+ "route": "/docs/multi-select"
+ }
+ ],
+ "examples": [
+ "select-editor-demo"
+ ],
+ "label": "New"
+ },
+ "files": [
+ {
+ "path": "plate-ui/select-editor.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "select-editor",
+ "registryDependencies": [
+ "editor",
+ "command",
+ "popover",
+ "tag-element"
+ ],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@udecode/plate-emoji",
@@ -1655,8 +2033,7 @@
"description": "A dropdown menu for emoji selection and insertion.",
"docs": [
{
- "route": "/docs/emoji",
- "title": "Emoji"
+ "route": "/docs/emoji"
},
{
"route": "https://pro.platejs.org/docs/components/emoji-picker"
@@ -1716,12 +2093,13 @@
"@udecode/plate-basic-marks",
"@udecode/plate-font",
"@udecode/plate-indent-list",
- "@udecode/plate-media"
+ "@udecode/plate-media",
+ "@udecode/plate-highlight"
],
"doc": {
"description": "A set of commonly used formatting buttons.",
"examples": [
- "toolbar-demo"
+ "basic-nodes-demo"
]
},
"files": [
@@ -1738,6 +2116,7 @@
"color-dropdown-menu",
"comment-toolbar-button",
"emoji-dropdown-menu",
+ "history-toolbar-button",
"indent-list-toolbar-button",
"indent-todo-toolbar-button",
"indent-toolbar-button",
@@ -1751,7 +2130,8 @@
"outdent-toolbar-button",
"table-dropdown-menu",
"toggle-toolbar-button",
- "turn-into-dropdown-menu"
+ "turn-into-dropdown-menu",
+ "export-toolbar-button"
],
"type": "registry:ui"
},
@@ -1764,11 +2144,11 @@
],
"files": [
{
- "path": "plate-ui/fixed-toolbar-buttons-list.tsx",
+ "path": "plate-ui/fixed-toolbar-list-buttons.tsx",
"type": "registry:ui"
}
],
- "name": "fixed-toolbar-buttons-list",
+ "name": "fixed-toolbar-list-buttons",
"registryDependencies": [
"toolbar",
"ai-toolbar-button",
@@ -1795,7 +2175,7 @@
"doc": {
"description": "A fixed toolbar that stays at the top of the editor.",
"examples": [
- "toolbar-demo"
+ "basic-nodes-demo"
]
},
"files": [
@@ -1817,6 +2197,9 @@
"doc": {
"description": "A set of formatting buttons for the floating toolbar.",
"docs": [
+ {
+ "route": "/docs/api/floating"
+ },
{
"route": "https://pro.platejs.org/docs/components/floating-toolbar-buttons"
}
@@ -1851,6 +2234,9 @@
"doc": {
"description": "A contextual toolbar that appears over selected text.",
"docs": [
+ {
+ "route": "/docs/api/floating"
+ },
{
"route": "https://pro.platejs.org/docs/components/floating-toolbar"
}
@@ -1880,8 +2266,7 @@
"description": "A text suggestion system that displays AI-generated content after the cursor.",
"docs": [
{
- "route": "/docs/copilot",
- "title": "Copilot"
+ "route": "/docs/copilot"
},
{
"route": "https://pro.platejs.org/docs/components/ghost-text"
@@ -1916,6 +2301,32 @@
"name": "indent-fire-marker",
"type": "registry:ui"
},
+ {
+ "dependencies": [],
+ "doc": {
+ "description": "Toolbar buttons for undo and redo operations.",
+ "docs": [
+ {
+ "route": "https://docs.slatejs.org/libraries/slate-history",
+ "title": "Slate History"
+ }
+ ],
+ "examples": [
+ "basic-nodes-demo"
+ ]
+ },
+ "files": [
+ {
+ "path": "plate-ui/history-toolbar-button.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "history-toolbar-button",
+ "registryDependencies": [
+ "toolbar"
+ ],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@udecode/plate-indent-list"
@@ -1924,8 +2335,7 @@
"description": "A toolbar control for adjusting list indentation.",
"docs": [
{
- "route": "/docs/indent-list",
- "title": "Indent List"
+ "route": "/docs/indent-list"
}
],
"examples": [
@@ -1952,8 +2362,7 @@
"description": "A checkbox marker for interactive todo lists.",
"docs": [
{
- "route": "/docs/indent-list",
- "title": "Indent List"
+ "route": "/docs/indent-list"
},
{
"route": "https://pro.platejs.org/docs/components/indent-todo-marker"
@@ -1983,8 +2392,7 @@
"description": "A toolbar control for creating todo list items.",
"docs": [
{
- "route": "/docs/indent-list",
- "title": "Indent List"
+ "route": "/docs/indent-list"
}
],
"examples": [
@@ -2011,8 +2419,7 @@
"description": "A toolbar control for block indentation.",
"docs": [
{
- "route": "/docs/indent",
- "title": "Indent"
+ "route": "/docs/indent"
}
],
"examples": [
@@ -2040,8 +2447,7 @@
"description": "A combobox for inline suggestions.",
"docs": [
{
- "route": "/docs/combobox",
- "title": "Combobox"
+ "route": "/docs/combobox"
},
{
"route": "https://pro.platejs.org/docs/components/inline-combobox"
@@ -2107,8 +2513,7 @@
"description": "A menu for controlling text line spacing.",
"docs": [
{
- "route": "/docs/line-height",
- "title": "Line Height"
+ "route": "/docs/line-height"
}
],
"examples": [
@@ -2137,8 +2542,10 @@
"description": "A floating interface for link editing.",
"docs": [
{
- "route": "/docs/link",
- "title": "Link"
+ "route": "/docs/link"
+ },
+ {
+ "route": "/docs/api/floating"
},
{
"route": "https://pro.platejs.org/docs/components/link-floating-toolbar"
@@ -2172,8 +2579,7 @@
"description": "A toolbar control for link management.",
"docs": [
{
- "route": "/docs/link",
- "title": "Link"
+ "route": "/docs/link"
},
{
"route": "https://pro.platejs.org/docs/components/link-toolbar-button"
@@ -2204,8 +2610,7 @@
"description": "A toolbar control for indenting lists.",
"docs": [
{
- "route": "/docs/list",
- "title": "List"
+ "route": "/docs/list"
}
],
"examples": [
@@ -2232,8 +2637,7 @@
"description": "A toolbar control for list creation and management.",
"docs": [
{
- "route": "/docs/list",
- "title": "List"
+ "route": "/docs/list"
}
],
"examples": [
@@ -2260,8 +2664,7 @@
"description": "A toolbar control for basic text formatting.",
"docs": [
{
- "route": "/docs/basic-marks",
- "title": "Basic Marks"
+ "route": "/docs/basic-marks"
}
],
"examples": [
@@ -2289,8 +2692,7 @@
"description": "A popover interface for media settings.",
"docs": [
{
- "route": "/docs/media",
- "title": "Media"
+ "route": "/docs/media"
}
],
"examples": [
@@ -2315,14 +2717,15 @@
},
{
"dependencies": [
- "@udecode/plate-media"
+ "@udecode/plate-media",
+ "use-file-picker",
+ "sonner"
],
"doc": {
"description": "Toolbar button for inserting and managing media.",
"docs": [
{
- "route": "/docs/media",
- "title": "Media"
+ "route": "/docs/media"
}
],
"examples": [
@@ -2338,10 +2741,40 @@
],
"name": "media-toolbar-button",
"registryDependencies": [
- "toolbar"
+ "toolbar",
+ "input",
+ "dropdown-menu",
+ "alert-dialog"
],
"type": "registry:ui"
},
+ {
+ "dependencies": [
+ "@udecode/plate-media",
+ "sonner"
+ ],
+ "doc": {
+ "description": "Show toast notifications for media uploads.",
+ "docs": [
+ {
+ "route": "/docs/media"
+ }
+ ],
+ "examples": [
+ "media-demo",
+ "upload-pro"
+ ]
+ },
+ "files": [
+ {
+ "path": "plate-ui/media-upload-toast.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "media-upload-toast",
+ "registryDependencies": [],
+ "type": "registry:ui"
+ },
{
"dependencies": [
"@radix-ui/react-dropdown-menu"
@@ -2349,7 +2782,7 @@
"doc": {
"description": "A menu for switching between editor modes.",
"examples": [
- "mode-demo"
+ "basic-nodes-demo"
]
},
"files": [
@@ -2369,7 +2802,6 @@
"dependencies": [
"@radix-ui/react-dropdown-menu",
"@udecode/plate-basic-marks",
- "@udecode/plate-highlight",
"@udecode/plate-kbd"
],
"doc": {
@@ -2405,8 +2837,7 @@
"description": "A toolbar button for decreasing block indentation.",
"docs": [
{
- "route": "/docs/indent",
- "title": "Indent"
+ "route": "/docs/indent"
}
],
"examples": [
@@ -2433,15 +2864,14 @@
"description": "A text placeholder for empty editor blocks.",
"docs": [
{
- "route": "/docs/basic-elements",
- "title": "Basic Elements"
+ "route": "/docs/basic-elements"
},
{
"route": "https://pro.platejs.org/docs/components/placeholder"
}
],
"examples": [
- "placeholder-demo",
+ "basic-elements-demo",
"placeholder-pro"
]
},
@@ -2463,8 +2893,7 @@
"description": "A base element with block selection support.",
"docs": [
{
- "route": "/docs/block-selection",
- "title": "Block Selection"
+ "route": "/docs/block-selection"
}
],
"examples": [
@@ -2491,6 +2920,9 @@
"doc": {
"description": "A resizable wrapper with resize handles.",
"docs": [
+ {
+ "route": "/docs/api/resizable"
+ },
{
"route": "https://pro.platejs.org/docs/components/resizable"
}
@@ -2519,8 +2951,7 @@
"description": "A menu for table manipulation and formatting.",
"docs": [
{
- "route": "/docs/table",
- "title": "Table"
+ "route": "/docs/table"
}
],
"examples": [
@@ -2548,8 +2979,7 @@
"description": "A toolbar button for expanding and collapsing blocks.",
"docs": [
{
- "route": "/docs/toggle",
- "title": "Toggle"
+ "route": "/docs/toggle"
}
],
"examples": [
diff --git a/apps/www/public/r/styles/default/ai-demo.json b/apps/www/public/r/styles/default/ai-demo.json
index 3ad63eddef..2c71372b97 100644
--- a/apps/www/public/r/styles/default/ai-demo.json
+++ b/apps/www/public/r/styles/default/ai-demo.json
@@ -1,21 +1,52 @@
{
- "dependencies": [
- "@udecode/plate-ai",
- "@udecode/plate-markdown"
- ],
"doc": {
"description": "AI menu with commands, streaming responses in a preview or directly into the editor.",
+ "docs": [
+ {
+ "route": "/docs/ai",
+ "title": "AI"
+ }
+ ],
"title": "AI"
},
"files": [
{
- "content": "'use client';\n\nimport React, { useRef } from 'react';\n\nimport type { ValueId } from '@/config/customizer-plugins';\n\nimport { cn } from '@udecode/cn';\nimport { AutoformatPlugin } from '@udecode/plate-autoformat/react';\nimport { SingleLinePlugin } from '@udecode/plate-break/react';\nimport { CommentsPlugin } from '@udecode/plate-comments/react';\nimport { Plate, usePlateEditor } from '@udecode/plate-common/react';\nimport { ExcalidrawPlugin } from '@udecode/plate-excalidraw/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { ListPlugin, TodoListPlugin } from '@udecode/plate-list/react';\nimport { NormalizeTypesPlugin } from '@udecode/plate-normalizers';\nimport { PlaywrightPlugin } from '@udecode/plate-playwright';\nimport { TablePlugin } from '@udecode/plate-table/react';\n\nimport { CheckPlugin } from '@/components/context/check-plugin';\nimport { settingsStore } from '@/components/context/settings-store';\nimport { getAutoformatOptions } from '@/lib/plate/demo/plugins/autoformatOptions';\nimport { createPlateUI } from '@/plate/create-plate-ui';\nimport { editableProps } from '@/plate/demo/editableProps';\nimport { isEnabled } from '@/plate/demo/is-enabled';\nimport { DragOverCursorPlugin } from '@/plate/demo/plugins/DragOverCursorPlugin';\nimport { usePlaygroundValue } from '@/plate/demo/values/usePlaygroundValue';\nimport { editorPlugins } from '@/components/editor/plugins/editor-plugins';\nimport { CommentsPopover } from '@/components/plate-ui/comments-popover';\nimport { CursorOverlay } from '@/components/plate-ui/cursor-overlay';\nimport { Editor, EditorContainer } from '@/components/plate-ui/editor';\nimport { FixedToolbar } from '@/components/plate-ui/fixed-toolbar';\nimport { FixedToolbarButtons } from '@/components/plate-ui/fixed-toolbar-buttons';\nimport { FixedToolbarButtonsList } from '@/components/plate-ui/fixed-toolbar-buttons-list';\nimport { FloatingToolbar } from '@/components/plate-ui/floating-toolbar';\nimport { FloatingToolbarButtons } from '@/components/plate-ui/floating-toolbar-buttons';\n\nimport { usePlaygroundEnabled } from './usePlaygroundEnabled';\n\nexport const usePlaygroundEditor = (id: any = '') => {\n const enabledPlugins = settingsStore.use.checkedPlugins();\n const overridePlugins = usePlaygroundEnabled(id);\n const autoformatOptions = getAutoformatOptions(id, enabledPlugins);\n\n const value = usePlaygroundValue(id);\n const key = settingsStore.use.version();\n const editorId = id || 'playground-' + key;\n\n return usePlateEditor(\n {\n id: editorId,\n override: {\n components: createPlateUI({\n draggable: isEnabled('dnd', id),\n placeholder: isEnabled('placeholder', id),\n }),\n plugins: overridePlugins,\n },\n plugins: [\n ...editorPlugins,\n\n AutoformatPlugin.configure({\n options: autoformatOptions,\n }),\n TablePlugin.configure({\n options: {\n enableMerging: id === 'tableMerge',\n },\n }),\n ListPlugin,\n TodoListPlugin,\n ExcalidrawPlugin,\n NormalizeTypesPlugin.configure({\n options: {\n rules: [{ path: [0], strictType: HEADING_KEYS.h1 }],\n },\n }),\n SingleLinePlugin,\n\n // Testing\n PlaywrightPlugin.configure({\n enabled: process.env.NODE_ENV !== 'production',\n }),\n ],\n value: value,\n },\n []\n );\n};\n\nexport default function PlaygroundDemo({\n id,\n className,\n}: {\n id?: ValueId;\n className?: string;\n scrollSelector?: string;\n}) {\n const containerRef = useRef(null);\n const enabled = settingsStore.use.checkedComponents();\n\n const editor = usePlaygroundEditor(id);\n\n return (\n \n \n \n \n \n {id === 'list' ? (\n \n ) : (\n \n )}\n \n \n \n\n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n \n\n \n
\n \n \n );\n}\n\nconst DemoIdContext = React.createContext(undefined);\n\nexport function DemoId({\n id,\n children,\n}: {\n children: React.ReactNode;\n id?: string;\n}) {\n return {children} ;\n}\n\nexport function useDemoId() {\n return React.useContext(DemoIdContext);\n}\n",
- "path": "example/playground-demo.tsx",
- "target": "components/playground-demo.tsx",
+ "content": "'use client';\n\nimport React from 'react';\n\nimport { Plate } from '@udecode/plate-common/react';\n\nimport { editorPlugins } from '@/components/editor/plugins/editor-plugins';\nimport { useCreateEditor } from '@/components/editor/use-create-editor';\nimport { Editor, EditorContainer } from '@/components/plate-ui/editor';\n\nimport { DEMO_VALUES } from './values/demo-values';\n\nexport default function Demo({ id }: { id: string }) {\n const editor = useCreateEditor({\n plugins: [...editorPlugins],\n value: DEMO_VALUES[id],\n });\n\n return (\n \n \n \n \n \n );\n}\n",
+ "path": "example/demo.tsx",
+ "target": "components/demo.tsx",
+ "type": "registry:example"
+ },
+ {
+ "content": "'use client';\n\nimport React from 'react';\n\nimport { withProps } from '@udecode/cn';\nimport { AIChatPlugin, AIPlugin } from '@udecode/plate-ai/react';\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport {\n CodeBlockPlugin,\n CodeLinePlugin,\n CodeSyntaxPlugin,\n} from '@udecode/plate-code-block/react';\nimport {\n ParagraphPlugin,\n PlateLeaf,\n createPlateEditor,\n} from '@udecode/plate-common/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { LinkPlugin } from '@udecode/plate-link/react';\nimport { MarkdownPlugin } from '@udecode/plate-markdown';\n\nimport { cursorOverlayPlugin } from '@/components/editor/plugins/cursor-overlay-plugin';\nimport { AIMenu } from '@/components/plate-ui/ai-menu';\nimport { BlockquoteElement } from '@/components/plate-ui/blockquote-element';\nimport { CodeBlockElement } from '@/components/plate-ui/code-block-element';\nimport { CodeLeaf } from '@/components/plate-ui/code-leaf';\nimport { CodeLineElement } from '@/components/plate-ui/code-line-element';\nimport { CodeSyntaxLeaf } from '@/components/plate-ui/code-syntax-leaf';\nimport { HeadingElement } from '@/components/plate-ui/heading-element';\nimport { HrElement } from '@/components/plate-ui/hr-element';\nimport { LinkElement } from '@/components/plate-ui/link-element';\nimport { ParagraphElement } from '@/components/plate-ui/paragraph-element';\n\nimport { basicNodesPlugins } from './basic-nodes-plugins';\nimport { blockSelectionReadOnlyPlugin } from './block-selection-plugins';\nimport { indentListPlugins } from './indent-list-plugins';\nimport { linkPlugin } from './link-plugin';\n\nconst createAIEditor = () => {\n const editor = createPlateEditor({\n id: 'ai',\n override: {\n components: {\n [BlockquotePlugin.key]: BlockquoteElement,\n [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }),\n [CodeBlockPlugin.key]: CodeBlockElement,\n [CodeLinePlugin.key]: CodeLineElement,\n [CodePlugin.key]: CodeLeaf,\n [CodeSyntaxPlugin.key]: CodeSyntaxLeaf,\n [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }),\n [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }),\n [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }),\n [HorizontalRulePlugin.key]: HrElement,\n [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }),\n [LinkPlugin.key]: LinkElement,\n [ParagraphPlugin.key]: ParagraphElement,\n [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }),\n [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }),\n },\n },\n plugins: [\n ...basicNodesPlugins,\n ...indentListPlugins,\n HorizontalRulePlugin,\n linkPlugin,\n MarkdownPlugin.configure({ options: { indentList: true } }),\n blockSelectionReadOnlyPlugin,\n ],\n });\n\n return editor;\n};\n\nconst systemCommon = `\\\nYou are an advanced AI-powered note-taking assistant, designed to enhance productivity and creativity in note management.\nRespond directly to user prompts with clear, concise, and relevant content. Maintain a neutral, helpful tone.\n\nRules:\n- is the entire note the user is working on.\n- is a reminder of how you should reply to INSTRUCTIONS. It does not apply to questions.\n- Anything else is the user prompt.\n- Your response should be tailored to the user's prompt, providing precise assistance to optimize note management.\n- For INSTRUCTIONS: Follow the exactly. Provide ONLY the content to be inserted or replaced. No explanations or comments.\n- For QUESTIONS: Provide a helpful and concise answer. You may include brief explanations if necessary.\n- CRITICAL: Distinguish between INSTRUCTIONS and QUESTIONS. Instructions typically ask you to modify or add content. Questions ask for information or clarification.\n`;\n\nconst systemDefault = `\\\n${systemCommon}\n- is the current block of text the user is working on.\n- Ensure your output can seamlessly fit into the existing structure.\n- CRITICAL: Provide only a single block of text. DO NOT create multiple paragraphs or separate blocks.\n\n{block}\n \n`;\n\nconst systemSelecting = `\\\n${systemCommon}\n- is the block of text containing the user's selection, providing context.\n- Ensure your output can seamlessly fit into the existing structure.\n- is the specific text the user has selected in the block and wants to modify or ask about.\n- Consider the context provided by , but only modify . Your response should be a direct replacement for .\n\n{block}\n \n\n{selection}\n \n`;\n\nconst systemBlockSelecting = `\\\n${systemCommon}\n- represents the full blocks of text the user has selected and wants to modify or ask about.\n- Your response should be a direct replacement for the entire .\n- Maintain the overall structure and formatting of the selected blocks, unless explicitly instructed otherwise.\n- CRITICAL: Provide only the content to replace . Do not add additional blocks or change the block structure unless specifically requested.\n\n{block}\n \n`;\n\nconst userDefault = `\nCRITICAL: DO NOT use block formatting. You can only use inline formatting.\nCRITICAL: DO NOT start new lines or paragraphs.\nNEVER write .\n \n{prompt}`;\n\nconst userSelecting = `\nIf this is a question, provide a helpful and concise answer about .\nIf this is an instruction, provide ONLY the text to replace . No explanations.\nEnsure it fits seamlessly within . If is empty, write ONE random sentence.\nNEVER write or .\n \n{prompt} about `;\n\nconst userBlockSelecting = `\nIf this is a question, provide a helpful and concise answer about .\nIf this is an instruction, provide ONLY the content to replace the entire . No explanations.\nMaintain the overall structure unless instructed otherwise.\nNEVER write or .\n \n{prompt} about `;\n\nexport const PROMPT_TEMPLATES = {\n systemBlockSelecting,\n systemDefault,\n systemSelecting,\n userBlockSelecting,\n userDefault,\n userSelecting,\n};\n\nexport const aiPlugins = [\n cursorOverlayPlugin,\n MarkdownPlugin.configure({ options: { indentList: true } }),\n AIPlugin,\n AIChatPlugin.configure({\n options: {\n createAIEditor,\n promptTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.userBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.userSelecting\n : PROMPT_TEMPLATES.userDefault;\n },\n systemTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.systemBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.systemSelecting\n : PROMPT_TEMPLATES.systemDefault;\n },\n },\n render: { afterEditable: () => },\n }),\n] as const;\n",
+ "path": "components/editor/plugins/ai-plugins.tsx",
+ "target": "components/ai-plugins.tsx",
+ "type": "registry:example"
+ },
+ {
+ "content": "import { jsx } from '@udecode/plate-test-utils';\n\njsx;\n\nexport const aiValue: any = (\n \n AI Menu \n Generate and refine content with AI. \n Access the AI menu in many ways: \n \n Press \"⌘ + J\". \n \n \n Select text and click \"Ask AI\" in the floating toolbar \n \n \n Right-click a block and select \"Ask AI\" \n \n \n Press space in an empty block. Try it out: \n \n \n \n \n Once opened, you can: \n \n Search commands in the input field: \n \n \n Use arrow keys to navigate, Enter to select \n \n \n Generating commands: \n \n \n Continue writing \n \n \n Add a summary \n \n \n Explain \n \n \n Generating suggestions: \n \n \n Accept \n \n \n Discard \n \n \n Try again \n \n \n Editing commands: \n \n \n Improve writing \n \n \n Make it longer or shorter \n \n \n Fix spelling & grammar \n \n \n Simplify language \n \n Editing suggestions: \n \n Replace the selection \n \n \n Insert below \n \n \n Discard \n \n \n Try again \n \n \n Note: chat history is preserved until the menu is closed. \n \n \n);\n",
+ "path": "example/values/ai-value.tsx",
+ "target": "components/ai-value.tsx",
+ "type": "registry:example"
+ },
+ {
+ "content": "'use client';\n\nimport type { Value } from '@udecode/plate-common';\n\nimport { withProps } from '@udecode/cn';\nimport { AIPlugin } from '@udecode/plate-ai/react';\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n SubscriptPlugin,\n SuperscriptPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport {\n CodeBlockPlugin,\n CodeLinePlugin,\n CodeSyntaxPlugin,\n} from '@udecode/plate-code-block/react';\nimport { CommentsPlugin } from '@udecode/plate-comments/react';\nimport {\n type CreatePlateEditorOptions,\n ParagraphPlugin,\n PlateLeaf,\n usePlateEditor,\n} from '@udecode/plate-common/react';\nimport { DatePlugin } from '@udecode/plate-date/react';\nimport { EmojiInputPlugin } from '@udecode/plate-emoji/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { TocPlugin } from '@udecode/plate-heading/react';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { KbdPlugin } from '@udecode/plate-kbd/react';\nimport { ColumnItemPlugin, ColumnPlugin } from '@udecode/plate-layout/react';\nimport { LinkPlugin } from '@udecode/plate-link/react';\nimport {\n AudioPlugin,\n FilePlugin,\n ImagePlugin,\n MediaEmbedPlugin,\n PlaceholderPlugin,\n VideoPlugin,\n} from '@udecode/plate-media/react';\nimport {\n MentionInputPlugin,\n MentionPlugin,\n} from '@udecode/plate-mention/react';\nimport { SlashInputPlugin } from '@udecode/plate-slash-command/react';\nimport {\n TableCellHeaderPlugin,\n TableCellPlugin,\n TablePlugin,\n TableRowPlugin,\n} from '@udecode/plate-table/react';\nimport { TogglePlugin } from '@udecode/plate-toggle/react';\n\nimport { AILeaf } from '@/components/plate-ui/ai-leaf';\nimport { BlockquoteElement } from '@/components/plate-ui/blockquote-element';\nimport { CodeBlockElement } from '@/components/plate-ui/code-block-element';\nimport { CodeLeaf } from '@/components/plate-ui/code-leaf';\nimport { CodeLineElement } from '@/components/plate-ui/code-line-element';\nimport { CodeSyntaxLeaf } from '@/components/plate-ui/code-syntax-leaf';\nimport { ColumnElement } from '@/components/plate-ui/column-element';\nimport { ColumnGroupElement } from '@/components/plate-ui/column-group-element';\nimport { CommentLeaf } from '@/components/plate-ui/comment-leaf';\nimport { DateElement } from '@/components/plate-ui/date-element';\nimport { EmojiInputElement } from '@/components/plate-ui/emoji-input-element';\nimport { HeadingElement } from '@/components/plate-ui/heading-element';\nimport { HighlightLeaf } from '@/components/plate-ui/highlight-leaf';\nimport { HrElement } from '@/components/plate-ui/hr-element';\nimport { ImageElement } from '@/components/plate-ui/image-element';\nimport { KbdLeaf } from '@/components/plate-ui/kbd-leaf';\nimport { LinkElement } from '@/components/plate-ui/link-element';\nimport { MediaAudioElement } from '@/components/plate-ui/media-audio-element';\nimport { MediaEmbedElement } from '@/components/plate-ui/media-embed-element';\nimport { MediaFileElement } from '@/components/plate-ui/media-file-element';\nimport { MediaPlaceholderElement } from '@/components/plate-ui/media-placeholder-element';\nimport { MediaVideoElement } from '@/components/plate-ui/media-video-element';\nimport { MentionElement } from '@/components/plate-ui/mention-element';\nimport { MentionInputElement } from '@/components/plate-ui/mention-input-element';\nimport { ParagraphElement } from '@/components/plate-ui/paragraph-element';\nimport { withPlaceholders } from '@/components/plate-ui/placeholder';\nimport { SlashInputElement } from '@/components/plate-ui/slash-input-element';\nimport {\n TableCellElement,\n TableCellHeaderElement,\n} from '@/components/plate-ui/table-cell-element';\nimport { TableElement } from '@/components/plate-ui/table-element';\nimport { TableRowElement } from '@/components/plate-ui/table-row-element';\nimport { TocElement } from '@/components/plate-ui/toc-element';\nimport { ToggleElement } from '@/components/plate-ui/toggle-element';\nimport { withDraggables } from '@/components/plate-ui/with-draggables';\n\nimport { editorPlugins, viewPlugins } from './plugins/editor-plugins';\n\nexport const viewComponents = {\n [AudioPlugin.key]: MediaAudioElement,\n [BlockquotePlugin.key]: BlockquoteElement,\n [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }),\n [CodeBlockPlugin.key]: CodeBlockElement,\n [CodeLinePlugin.key]: CodeLineElement,\n [CodePlugin.key]: CodeLeaf,\n [CodeSyntaxPlugin.key]: CodeSyntaxLeaf,\n [ColumnItemPlugin.key]: ColumnElement,\n [ColumnPlugin.key]: ColumnGroupElement,\n [CommentsPlugin.key]: CommentLeaf,\n [DatePlugin.key]: DateElement,\n [FilePlugin.key]: MediaFileElement,\n [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }),\n [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }),\n [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }),\n [HEADING_KEYS.h4]: withProps(HeadingElement, { variant: 'h4' }),\n [HEADING_KEYS.h5]: withProps(HeadingElement, { variant: 'h5' }),\n [HEADING_KEYS.h6]: withProps(HeadingElement, { variant: 'h6' }),\n [HighlightPlugin.key]: HighlightLeaf,\n [HorizontalRulePlugin.key]: HrElement,\n [ImagePlugin.key]: ImageElement,\n [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }),\n [KbdPlugin.key]: KbdLeaf,\n [LinkPlugin.key]: LinkElement,\n [MediaEmbedPlugin.key]: MediaEmbedElement,\n [MentionPlugin.key]: MentionElement,\n [ParagraphPlugin.key]: ParagraphElement,\n [PlaceholderPlugin.key]: MediaPlaceholderElement,\n [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }),\n [SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }),\n [SuperscriptPlugin.key]: withProps(PlateLeaf, { as: 'sup' }),\n [TableCellHeaderPlugin.key]: TableCellHeaderElement,\n [TableCellPlugin.key]: TableCellElement,\n [TablePlugin.key]: TableElement,\n [TableRowPlugin.key]: TableRowElement,\n [TocPlugin.key]: TocElement,\n [TogglePlugin.key]: ToggleElement,\n [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }),\n [VideoPlugin.key]: MediaVideoElement,\n};\n\nexport const editorComponents = {\n ...viewComponents,\n [AIPlugin.key]: AILeaf,\n [EmojiInputPlugin.key]: EmojiInputElement,\n [MentionInputPlugin.key]: MentionInputElement,\n [SlashInputPlugin.key]: SlashInputElement,\n};\n\nexport const useCreateEditor = (\n {\n components,\n override,\n readOnly,\n ...options\n }: {\n components?: Record;\n plugins?: any[];\n readOnly?: boolean;\n } & Omit = {},\n deps: any[] = []\n) => {\n return usePlateEditor(\n {\n override: {\n components: {\n ...(readOnly\n ? viewComponents\n : withPlaceholders(withDraggables(editorComponents))),\n ...components,\n },\n ...override,\n },\n plugins: (readOnly ? viewPlugins : editorPlugins) as any,\n ...options,\n },\n deps\n );\n};\n",
+ "path": "components/editor/use-create-editor.ts",
+ "target": "components/use-create-editor.ts",
+ "type": "registry:example"
+ },
+ {
+ "content": "'use client';\n\nimport { CalloutPlugin } from '@udecode/plate-callout/react';\nimport { ParagraphPlugin } from '@udecode/plate-common/react';\nimport { DatePlugin } from '@udecode/plate-date/react';\nimport { DocxPlugin } from '@udecode/plate-docx';\nimport { EmojiPlugin } from '@udecode/plate-emoji/react';\nimport {\n FontBackgroundColorPlugin,\n FontColorPlugin,\n FontSizePlugin,\n} from '@udecode/plate-font/react';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { JuicePlugin } from '@udecode/plate-juice';\nimport { KbdPlugin } from '@udecode/plate-kbd/react';\nimport { ColumnPlugin } from '@udecode/plate-layout/react';\nimport { MarkdownPlugin } from '@udecode/plate-markdown';\nimport {\n EquationPlugin,\n InlineEquationPlugin,\n} from '@udecode/plate-math/react';\nimport { SlashPlugin } from '@udecode/plate-slash-command/react';\nimport { TogglePlugin } from '@udecode/plate-toggle/react';\nimport { TrailingBlockPlugin } from '@udecode/plate-trailing-block';\n\nimport { FixedToolbarPlugin } from '@/components/editor/plugins/fixed-toolbar-plugin';\nimport { FloatingToolbarPlugin } from '@/components/editor/plugins/floating-toolbar-plugin';\n\nimport { aiPlugins } from './ai-plugins';\nimport { alignPlugin } from './align-plugin';\nimport { autoformatPlugin } from './autoformat-plugin';\nimport { basicNodesPlugins } from './basic-nodes-plugins';\nimport { blockMenuPlugins } from './block-menu-plugins';\nimport { commentsPlugin } from './comments-plugin';\nimport { cursorOverlayPlugin } from './cursor-overlay-plugin';\nimport { deletePlugins } from './delete-plugins';\nimport { dndPlugins } from './dnd-plugins';\nimport { exitBreakPlugin } from './exit-break-plugin';\nimport { indentListPlugins } from './indent-list-plugins';\nimport { lineHeightPlugin } from './line-height-plugin';\nimport { linkPlugin } from './link-plugin';\nimport { mediaPlugins } from './media-plugins';\nimport { mentionPlugin } from './mention-plugin';\nimport { resetBlockTypePlugin } from './reset-block-type-plugin';\nimport { softBreakPlugin } from './soft-break-plugin';\nimport { tablePlugin } from './table-plugin';\nimport { tocPlugin } from './toc-plugin';\n\nexport const viewPlugins = [\n ...basicNodesPlugins,\n HorizontalRulePlugin,\n linkPlugin,\n DatePlugin,\n mentionPlugin,\n tablePlugin,\n TogglePlugin,\n tocPlugin,\n ...mediaPlugins,\n InlineEquationPlugin,\n EquationPlugin,\n CalloutPlugin,\n ColumnPlugin,\n\n // Marks\n FontColorPlugin,\n FontBackgroundColorPlugin,\n FontSizePlugin,\n HighlightPlugin,\n KbdPlugin,\n\n // Block Style\n alignPlugin,\n ...indentListPlugins,\n lineHeightPlugin,\n\n // Collaboration\n commentsPlugin,\n] as const;\n\nexport const editorPlugins = [\n // AI\n ...aiPlugins,\n\n // Nodes\n ...viewPlugins,\n\n // Functionality\n SlashPlugin,\n autoformatPlugin,\n cursorOverlayPlugin,\n ...blockMenuPlugins,\n ...dndPlugins,\n EmojiPlugin,\n exitBreakPlugin,\n resetBlockTypePlugin,\n ...deletePlugins,\n softBreakPlugin,\n TrailingBlockPlugin.configure({ options: { type: ParagraphPlugin.key } }),\n\n // Deserialization\n DocxPlugin,\n MarkdownPlugin.configure({ options: { indentList: true } }),\n JuicePlugin,\n\n // UI\n FixedToolbarPlugin,\n FloatingToolbarPlugin,\n];\n",
+ "path": "components/editor/plugins/editor-plugins.tsx",
+ "target": "components/editor-plugins.tsx",
"type": "registry:example"
}
],
"name": "ai-demo",
- "registryDependencies": [],
+ "registryDependencies": [
+ "basic-nodes-plugins",
+ "block-selection-plugins",
+ "indent-list-plugins",
+ "link-plugin"
+ ],
"type": "registry:example"
}
\ No newline at end of file
diff --git a/apps/www/public/r/styles/default/ai-menu.json b/apps/www/public/r/styles/default/ai-menu.json
index f50ecb8350..35919c7148 100644
--- a/apps/www/public/r/styles/default/ai-menu.json
+++ b/apps/www/public/r/styles/default/ai-menu.json
@@ -27,13 +27,13 @@
},
"files": [
{
- "content": "'use client';\n\nimport * as React from 'react';\n\nimport { AIChatPlugin, useEditorChat } from '@udecode/plate-ai/react';\nimport {\n type TElement,\n type TNodeEntry,\n getAncestorNode,\n getBlocks,\n isElementEmpty,\n isHotkey,\n isSelectionAtBlockEnd,\n} from '@udecode/plate-common';\nimport {\n type PlateEditor,\n toDOMNode,\n useEditorPlugin,\n useHotkeys,\n} from '@udecode/plate-common/react';\nimport {\n BlockSelectionPlugin,\n useIsSelecting,\n} from '@udecode/plate-selection/react';\nimport { Loader2Icon } from 'lucide-react';\n\nimport { useChat } from '@/components/editor/use-chat';\n\nimport { AIChatEditor } from './ai-chat-editor';\nimport { AIMenuItems } from './ai-menu-items';\nimport { Command, CommandList, InputCommand } from './command';\nimport { Popover, PopoverAnchor, PopoverContent } from './popover';\n\nexport function AIMenu() {\n const { api, editor, useOption } = useEditorPlugin(AIChatPlugin);\n const open = useOption('open');\n const mode = useOption('mode');\n const isSelecting = useIsSelecting();\n\n const aiEditorRef = React.useRef(null);\n const [value, setValue] = React.useState('');\n\n const chat = useChat();\n\n const { input, isLoading, messages, setInput } = chat;\n const [anchorElement, setAnchorElement] = React.useState(\n null\n );\n\n const setOpen = (open: boolean) => {\n if (open) {\n api.aiChat.show();\n } else {\n api.aiChat.hide();\n }\n };\n\n const show = (anchorElement: HTMLElement) => {\n setAnchorElement(anchorElement);\n setOpen(true);\n };\n\n useEditorChat({\n chat,\n onOpenBlockSelection: (blocks: TNodeEntry[]) => {\n show(toDOMNode(editor, blocks.at(-1)![0])!);\n },\n onOpenChange: (open) => {\n if (!open) {\n setAnchorElement(null);\n setInput('');\n }\n },\n onOpenCursor: () => {\n const ancestor = getAncestorNode(editor)?.[0] as TElement;\n\n if (!isSelectionAtBlockEnd(editor) && !isElementEmpty(editor, ancestor)) {\n editor\n .getApi(BlockSelectionPlugin)\n .blockSelection.addSelectedRow(ancestor.id as string);\n }\n\n show(toDOMNode(editor, ancestor)!);\n },\n onOpenSelection: () => {\n show(toDOMNode(editor, getBlocks(editor).at(-1)![0])!);\n },\n });\n\n useHotkeys(\n 'meta+j',\n () => {\n api.aiChat.show();\n },\n { enableOnContentEditable: true, enableOnFormTags: true }\n );\n\n return (\n \n \n\n {\n e.preventDefault();\n\n if (isLoading) {\n api.aiChat.stop();\n } else {\n api.aiChat.hide();\n }\n }}\n align=\"center\"\n avoidCollisions={false}\n side=\"bottom\"\n >\n \n {mode === 'chat' && isSelecting && messages.length > 0 && (\n \n )}\n\n {isLoading ? (\n \n \n {messages.length > 1 ? 'Editing...' : 'Thinking...'}\n
\n ) : (\n {\n if (isHotkey('backspace')(e) && input.length === 0) {\n e.preventDefault();\n api.aiChat.hide();\n }\n if (isHotkey('enter')(e) && !e.shiftKey && !value) {\n e.preventDefault();\n void api.aiChat.submit();\n }\n }}\n onValueChange={setInput}\n placeholder=\"Ask AI anything...\"\n autoFocus\n />\n )}\n\n {!isLoading && (\n \n \n \n )}\n \n \n \n );\n}\n",
+ "content": "'use client';\n\nimport * as React from 'react';\n\nimport { AIChatPlugin, useEditorChat } from '@udecode/plate-ai/react';\nimport {\n type TElement,\n type TNodeEntry,\n getAncestorNode,\n getBlocks,\n isElementEmpty,\n isHotkey,\n isSelectionAtBlockEnd,\n} from '@udecode/plate-common';\nimport {\n type PlateEditor,\n toDOMNode,\n useEditorPlugin,\n useHotkeys,\n} from '@udecode/plate-common/react';\nimport {\n BlockSelectionPlugin,\n useIsSelecting,\n} from '@udecode/plate-selection/react';\nimport { Loader2Icon } from 'lucide-react';\n\nimport { useChat } from '@/components/editor/use-chat';\n\nimport { AIChatEditor } from './ai-chat-editor';\nimport { AIMenuItems } from './ai-menu-items';\nimport { Command, CommandList, InputCommand } from './command';\nimport { Popover, PopoverAnchor, PopoverContent } from './popover';\n\nexport function AIMenu() {\n const { api, editor, useOption } = useEditorPlugin(AIChatPlugin);\n const open = useOption('open');\n const mode = useOption('mode');\n const isSelecting = useIsSelecting();\n\n const aiEditorRef = React.useRef(null);\n const [value, setValue] = React.useState('');\n\n const chat = useChat();\n\n const { input, isLoading, messages, setInput } = chat;\n const [anchorElement, setAnchorElement] = React.useState(\n null\n );\n\n const setOpen = (open: boolean) => {\n if (open) {\n api.aiChat.show();\n } else {\n api.aiChat.hide();\n }\n };\n\n const show = (anchorElement: HTMLElement) => {\n setAnchorElement(anchorElement);\n setOpen(true);\n };\n\n useEditorChat({\n chat,\n onOpenBlockSelection: (blocks: TNodeEntry[]) => {\n show(toDOMNode(editor, blocks.at(-1)![0])!);\n },\n onOpenChange: (open) => {\n if (!open) {\n setAnchorElement(null);\n setInput('');\n }\n },\n onOpenCursor: () => {\n const ancestor = getAncestorNode(editor)?.[0] as TElement;\n\n if (!isSelectionAtBlockEnd(editor) && !isElementEmpty(editor, ancestor)) {\n editor\n .getApi(BlockSelectionPlugin)\n .blockSelection.addSelectedRow(ancestor.id as string);\n }\n\n show(toDOMNode(editor, ancestor)!);\n },\n onOpenSelection: () => {\n show(toDOMNode(editor, getBlocks(editor).at(-1)![0])!);\n },\n });\n\n useHotkeys(\n 'meta+j',\n () => {\n api.aiChat.show();\n },\n { enableOnContentEditable: true, enableOnFormTags: true }\n );\n\n return (\n \n \n\n {\n e.preventDefault();\n\n if (isLoading) {\n api.aiChat.stop();\n } else {\n api.aiChat.hide();\n }\n }}\n align=\"center\"\n avoidCollisions={false}\n side=\"bottom\"\n >\n \n {mode === 'chat' && isSelecting && messages.length > 0 && (\n \n )}\n\n {isLoading ? (\n \n \n {messages.length > 1 ? 'Editing...' : 'Thinking...'}\n
\n ) : (\n {\n if (isHotkey('backspace')(e) && input.length === 0) {\n e.preventDefault();\n api.aiChat.hide();\n }\n if (isHotkey('enter')(e) && !e.shiftKey && !value) {\n e.preventDefault();\n void api.aiChat.submit();\n }\n }}\n onValueChange={setInput}\n placeholder=\"Ask AI anything...\"\n data-plate-focus\n autoFocus\n />\n )}\n\n {!isLoading && (\n \n \n \n )}\n \n \n \n );\n}\n",
"path": "plate-ui/ai-menu.tsx",
"target": "components/plate-ui/ai-menu.tsx",
"type": "registry:ui"
},
{
- "content": "'use client';\n\nimport { useEffect, useMemo } from 'react';\n\nimport { AIChatPlugin, AIPlugin } from '@udecode/plate-ai/react';\nimport {\n getAncestorNode,\n getEndPoint,\n getNodeString,\n} from '@udecode/plate-common';\nimport {\n type PlateEditor,\n focusEditor,\n useEditorPlugin,\n} from '@udecode/plate-common/react';\nimport { useIsSelecting } from '@udecode/plate-selection/react';\nimport {\n Album,\n BadgeHelp,\n Check,\n CornerUpLeft,\n FeatherIcon,\n ListEnd,\n ListMinus,\n ListPlus,\n PenLine,\n Wand,\n X,\n} from 'lucide-react';\n\nimport { CommandGroup, CommandItem } from './command';\n\nexport type EditorChatState =\n | 'cursorCommand'\n | 'cursorSuggestion'\n | 'selectionCommand'\n | 'selectionSuggestion';\n\nexport const aiChatItems = {\n accept: {\n icon: ,\n label: 'Accept',\n value: 'accept',\n onSelect: ({ editor }) => {\n editor.getTransforms(AIChatPlugin).aiChat.accept();\n focusEditor(editor, getEndPoint(editor, editor.selection!));\n },\n },\n continueWrite: {\n icon: ,\n label: 'Continue writing',\n value: 'continueWrite',\n onSelect: ({ editor }) => {\n const ancestorNode = getAncestorNode(editor);\n const isEmpty = getNodeString(ancestorNode![0]).trim().length === 0;\n\n void editor.getApi(AIChatPlugin).aiChat.submit({\n mode: 'insert',\n prompt: isEmpty\n ? `\n{editor}\n \nStart writing a new paragraph AFTER ONLY ONE SENTENCE`\n : 'Continue writing AFTER ONLY ONE SENTENCE. DONT REPEAT THE TEXT.',\n });\n },\n },\n discard: {\n icon: ,\n label: 'Discard',\n shortcut: 'Escape',\n value: 'discard',\n onSelect: ({ editor }) => {\n editor.getTransforms(AIPlugin).ai.undo();\n editor.getApi(AIChatPlugin).aiChat.hide();\n },\n },\n explain: {\n icon: ,\n label: 'Explain',\n value: 'explain',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: {\n default: 'Explain {editor}',\n selecting: 'Explain',\n },\n });\n },\n },\n fixSpelling: {\n icon: ,\n label: 'Fix spelling & grammar',\n value: 'fixSpelling',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Fix spelling and grammar',\n });\n },\n },\n improveWriting: {\n icon: ,\n label: 'Improve writing',\n value: 'improveWriting',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Improve the writing',\n });\n },\n },\n insertBelow: {\n icon: ,\n label: 'Insert below',\n value: 'insertBelow',\n onSelect: ({ aiEditor, editor }) => {\n void editor.getTransforms(AIChatPlugin).aiChat.insertBelow(aiEditor);\n },\n },\n makeLonger: {\n icon: ,\n label: 'Make longer',\n value: 'makeLonger',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Make longer',\n });\n },\n },\n makeShorter: {\n icon: ,\n label: 'Make shorter',\n value: 'makeShorter',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Make shorter',\n });\n },\n },\n replace: {\n icon: ,\n label: 'Replace selection',\n value: 'replace',\n onSelect: ({ aiEditor, editor }) => {\n void editor.getTransforms(AIChatPlugin).aiChat.replaceSelection(aiEditor);\n },\n },\n simplifyLanguage: {\n icon: ,\n label: 'Simplify language',\n value: 'simplifyLanguage',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Simplify the language',\n });\n },\n },\n summarize: {\n icon: ,\n label: 'Add a summary',\n value: 'summarize',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n mode: 'insert',\n prompt: {\n default: 'Summarize {editor}',\n selecting: 'Summarize',\n },\n });\n },\n },\n tryAgain: {\n icon: ,\n label: 'Try again',\n value: 'tryAgain',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.reload();\n },\n },\n} satisfies Record<\n string,\n {\n icon: React.ReactNode;\n label: string;\n value: string;\n component?: React.ComponentType<{ menuState: EditorChatState }>;\n filterItems?: boolean;\n items?: { label: string; value: string }[];\n shortcut?: string;\n onSelect?: ({\n aiEditor,\n editor,\n }: {\n aiEditor: PlateEditor;\n editor: PlateEditor;\n }) => void;\n }\n>;\n\nconst menuStateItems: Record<\n EditorChatState,\n {\n items: (typeof aiChatItems)[keyof typeof aiChatItems][];\n heading?: string;\n }[]\n> = {\n cursorCommand: [\n {\n items: [\n aiChatItems.continueWrite,\n aiChatItems.summarize,\n aiChatItems.explain,\n ],\n },\n ],\n cursorSuggestion: [\n {\n items: [aiChatItems.accept, aiChatItems.discard, aiChatItems.tryAgain],\n },\n ],\n selectionCommand: [\n {\n items: [\n aiChatItems.improveWriting,\n aiChatItems.makeLonger,\n aiChatItems.makeShorter,\n aiChatItems.fixSpelling,\n aiChatItems.simplifyLanguage,\n ],\n },\n ],\n selectionSuggestion: [\n {\n items: [\n aiChatItems.replace,\n aiChatItems.insertBelow,\n aiChatItems.discard,\n aiChatItems.tryAgain,\n ],\n },\n ],\n};\n\nexport const AIMenuItems = ({\n aiEditorRef,\n setValue,\n}: {\n aiEditorRef: React.MutableRefObject;\n setValue: (value: string) => void;\n}) => {\n const { editor, useOption } = useEditorPlugin(AIChatPlugin);\n const { messages } = useOption('chat');\n const isSelecting = useIsSelecting();\n\n const menuState = useMemo(() => {\n if (messages && messages.length > 0) {\n return isSelecting ? 'selectionSuggestion' : 'cursorSuggestion';\n }\n\n return isSelecting ? 'selectionCommand' : 'cursorCommand';\n }, [isSelecting, messages]);\n\n const menuGroups = useMemo(() => {\n const items = menuStateItems[menuState];\n\n return items;\n }, [menuState]);\n\n useEffect(() => {\n if (menuGroups.length > 0 && menuGroups[0].items.length > 0) {\n setValue(menuGroups[0].items[0].value);\n }\n }, [menuGroups, setValue]);\n\n return (\n <>\n {menuGroups.map((group, index) => (\n \n {group.items.map((menuItem) => (\n {\n menuItem.onSelect?.({\n aiEditor: aiEditorRef.current!,\n editor: editor,\n });\n }}\n >\n {menuItem.icon}\n {menuItem.label} \n \n ))}\n \n ))}\n >\n );\n};\n",
+ "content": "'use client';\n\nimport { useEffect, useMemo } from 'react';\n\nimport { AIChatPlugin, AIPlugin } from '@udecode/plate-ai/react';\nimport {\n getAncestorNode,\n getEndPoint,\n getNodeString,\n} from '@udecode/plate-common';\nimport {\n type PlateEditor,\n focusEditor,\n useEditorPlugin,\n} from '@udecode/plate-common/react';\nimport { useIsSelecting } from '@udecode/plate-selection/react';\nimport {\n Album,\n BadgeHelp,\n Check,\n CornerUpLeft,\n FeatherIcon,\n ListEnd,\n ListMinus,\n ListPlus,\n PenLine,\n Wand,\n X,\n} from 'lucide-react';\n\nimport { CommandGroup, CommandItem } from './command';\n\nexport type EditorChatState =\n | 'cursorCommand'\n | 'cursorSuggestion'\n | 'selectionCommand'\n | 'selectionSuggestion';\n\nexport const aiChatItems = {\n accept: {\n icon: ,\n label: 'Accept',\n value: 'accept',\n onSelect: ({ editor }) => {\n editor.getTransforms(AIChatPlugin).aiChat.accept();\n focusEditor(editor, getEndPoint(editor, editor.selection!));\n },\n },\n continueWrite: {\n icon: ,\n label: 'Continue writing',\n value: 'continueWrite',\n onSelect: ({ editor }) => {\n const ancestorNode = getAncestorNode(editor);\n\n if (!ancestorNode) return;\n\n const isEmpty = getNodeString(ancestorNode[0]).trim().length === 0;\n\n void editor.getApi(AIChatPlugin).aiChat.submit({\n mode: 'insert',\n prompt: isEmpty\n ? `\n{editor}\n \nStart writing a new paragraph AFTER ONLY ONE SENTENCE`\n : 'Continue writing AFTER ONLY ONE SENTENCE. DONT REPEAT THE TEXT.',\n });\n },\n },\n discard: {\n icon: ,\n label: 'Discard',\n shortcut: 'Escape',\n value: 'discard',\n onSelect: ({ editor }) => {\n editor.getTransforms(AIPlugin).ai.undo();\n editor.getApi(AIChatPlugin).aiChat.hide();\n },\n },\n explain: {\n icon: ,\n label: 'Explain',\n value: 'explain',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: {\n default: 'Explain {editor}',\n selecting: 'Explain',\n },\n });\n },\n },\n fixSpelling: {\n icon: ,\n label: 'Fix spelling & grammar',\n value: 'fixSpelling',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Fix spelling and grammar',\n });\n },\n },\n improveWriting: {\n icon: ,\n label: 'Improve writing',\n value: 'improveWriting',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Improve the writing',\n });\n },\n },\n insertBelow: {\n icon: ,\n label: 'Insert below',\n value: 'insertBelow',\n onSelect: ({ aiEditor, editor }) => {\n void editor.getTransforms(AIChatPlugin).aiChat.insertBelow(aiEditor);\n },\n },\n makeLonger: {\n icon: ,\n label: 'Make longer',\n value: 'makeLonger',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Make longer',\n });\n },\n },\n makeShorter: {\n icon: ,\n label: 'Make shorter',\n value: 'makeShorter',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Make shorter',\n });\n },\n },\n replace: {\n icon: ,\n label: 'Replace selection',\n value: 'replace',\n onSelect: ({ aiEditor, editor }) => {\n void editor.getTransforms(AIChatPlugin).aiChat.replaceSelection(aiEditor);\n },\n },\n simplifyLanguage: {\n icon: ,\n label: 'Simplify language',\n value: 'simplifyLanguage',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Simplify the language',\n });\n },\n },\n summarize: {\n icon: ,\n label: 'Add a summary',\n value: 'summarize',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n mode: 'insert',\n prompt: {\n default: 'Summarize {editor}',\n selecting: 'Summarize',\n },\n });\n },\n },\n tryAgain: {\n icon: ,\n label: 'Try again',\n value: 'tryAgain',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.reload();\n },\n },\n} satisfies Record<\n string,\n {\n icon: React.ReactNode;\n label: string;\n value: string;\n component?: React.ComponentType<{ menuState: EditorChatState }>;\n filterItems?: boolean;\n items?: { label: string; value: string }[];\n shortcut?: string;\n onSelect?: ({\n aiEditor,\n editor,\n }: {\n aiEditor: PlateEditor;\n editor: PlateEditor;\n }) => void;\n }\n>;\n\nconst menuStateItems: Record<\n EditorChatState,\n {\n items: (typeof aiChatItems)[keyof typeof aiChatItems][];\n heading?: string;\n }[]\n> = {\n cursorCommand: [\n {\n items: [\n aiChatItems.continueWrite,\n aiChatItems.summarize,\n aiChatItems.explain,\n ],\n },\n ],\n cursorSuggestion: [\n {\n items: [aiChatItems.accept, aiChatItems.discard, aiChatItems.tryAgain],\n },\n ],\n selectionCommand: [\n {\n items: [\n aiChatItems.improveWriting,\n aiChatItems.makeLonger,\n aiChatItems.makeShorter,\n aiChatItems.fixSpelling,\n aiChatItems.simplifyLanguage,\n ],\n },\n ],\n selectionSuggestion: [\n {\n items: [\n aiChatItems.replace,\n aiChatItems.insertBelow,\n aiChatItems.discard,\n aiChatItems.tryAgain,\n ],\n },\n ],\n};\n\nexport const AIMenuItems = ({\n aiEditorRef,\n setValue,\n}: {\n aiEditorRef: React.MutableRefObject;\n setValue: (value: string) => void;\n}) => {\n const { editor, useOption } = useEditorPlugin(AIChatPlugin);\n const { messages } = useOption('chat');\n const isSelecting = useIsSelecting();\n\n const menuState = useMemo(() => {\n if (messages && messages.length > 0) {\n return isSelecting ? 'selectionSuggestion' : 'cursorSuggestion';\n }\n\n return isSelecting ? 'selectionCommand' : 'cursorCommand';\n }, [isSelecting, messages]);\n\n const menuGroups = useMemo(() => {\n const items = menuStateItems[menuState];\n\n return items;\n }, [menuState]);\n\n useEffect(() => {\n if (menuGroups.length > 0 && menuGroups[0].items.length > 0) {\n setValue(menuGroups[0].items[0].value);\n }\n }, [menuGroups, setValue]);\n\n return (\n <>\n {menuGroups.map((group, index) => (\n \n {group.items.map((menuItem) => (\n {\n menuItem.onSelect?.({\n aiEditor: aiEditorRef.current!,\n editor: editor,\n });\n }}\n >\n {menuItem.icon}\n {menuItem.label} \n \n ))}\n \n ))}\n >\n );\n};\n",
"path": "plate-ui/ai-menu-items.tsx",
"target": "components/plate-ui/ai-menu-items.tsx",
"type": "registry:ui"
diff --git a/apps/www/public/r/styles/default/ai-plugins.json b/apps/www/public/r/styles/default/ai-plugins.json
index 8fb9b36881..b9b1aaa0d5 100644
--- a/apps/www/public/r/styles/default/ai-plugins.json
+++ b/apps/www/public/r/styles/default/ai-plugins.json
@@ -7,12 +7,11 @@
"@udecode/plate-heading",
"@udecode/plate-horizontal-rule",
"@udecode/plate-link",
- "@udecode/plate-markdown",
- "@udecode/plate-selection"
+ "@udecode/plate-markdown"
],
"files": [
{
- "content": "'use client';\n\nimport React from 'react';\n\nimport { withProps } from '@udecode/cn';\nimport { AIChatPlugin, AIPlugin } from '@udecode/plate-ai/react';\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport {\n CodeBlockPlugin,\n CodeLinePlugin,\n CodeSyntaxPlugin,\n} from '@udecode/plate-code-block/react';\nimport {\n ParagraphPlugin,\n PlateLeaf,\n createPlateEditor,\n} from '@udecode/plate-common/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { LinkPlugin } from '@udecode/plate-link/react';\nimport { MarkdownPlugin } from '@udecode/plate-markdown';\nimport { BlockSelectionPlugin } from '@udecode/plate-selection/react';\n\nimport { AIMenu } from '@/components/plate-ui/ai-menu';\nimport { BlockquoteElement } from '@/components/plate-ui/blockquote-element';\nimport { CodeBlockElement } from '@/components/plate-ui/code-block-element';\nimport { CodeLeaf } from '@/components/plate-ui/code-leaf';\nimport { CodeLineElement } from '@/components/plate-ui/code-line-element';\nimport { CodeSyntaxLeaf } from '@/components/plate-ui/code-syntax-leaf';\nimport { SelectionOverlayPlugin } from '@/components/plate-ui/cursor-overlay';\nimport { HeadingElement } from '@/components/plate-ui/heading-element';\nimport { HrElement } from '@/components/plate-ui/hr-element';\nimport { LinkElement } from '@/components/plate-ui/link-element';\nimport { ParagraphElement } from '@/components/plate-ui/paragraph-element';\n\nimport { basicNodesPlugins } from './basic-nodes-plugins';\nimport { indentListPlugins } from './indent-list-plugins';\nimport { linkPlugin } from './link-plugin';\n\nconst createAIEditor = () => {\n const editor = createPlateEditor({\n id: 'ai',\n override: {\n components: {\n [BlockquotePlugin.key]: BlockquoteElement,\n [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }),\n [CodeBlockPlugin.key]: CodeBlockElement,\n [CodeLinePlugin.key]: CodeLineElement,\n [CodePlugin.key]: CodeLeaf,\n [CodeSyntaxPlugin.key]: CodeSyntaxLeaf,\n [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }),\n [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }),\n [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }),\n [HorizontalRulePlugin.key]: HrElement,\n [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }),\n [LinkPlugin.key]: LinkElement,\n [ParagraphPlugin.key]: ParagraphElement,\n [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }),\n [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }),\n },\n },\n plugins: [\n ParagraphPlugin,\n ...basicNodesPlugins,\n HorizontalRulePlugin,\n linkPlugin,\n ...indentListPlugins,\n MarkdownPlugin.configure({ options: { indentList: true } }),\n // FIXME\n BlockSelectionPlugin.configure({\n api: {},\n extendEditor: null,\n options: {},\n render: {},\n useHooks: null,\n handlers: {},\n }),\n ],\n value: [{ children: [{ text: '' }], type: 'p' }],\n });\n\n return editor;\n};\n\nconst systemCommon = `\\\nYou are an advanced AI-powered note-taking assistant, designed to enhance productivity and creativity in note management.\nRespond directly to user prompts with clear, concise, and relevant content. Maintain a neutral, helpful tone.\n\nRules:\n- is the entire note the user is working on.\n- is a reminder of how you should reply to INSTRUCTIONS. It does not apply to questions.\n- Anything else is the user prompt.\n- Your response should be tailored to the user's prompt, providing precise assistance to optimize note management.\n- For INSTRUCTIONS: Follow the exactly. Provide ONLY the content to be inserted or replaced. No explanations or comments.\n- For QUESTIONS: Provide a helpful and concise answer. You may include brief explanations if necessary.\n- CRITICAL: Distinguish between INSTRUCTIONS and QUESTIONS. Instructions typically ask you to modify or add content. Questions ask for information or clarification.\n`;\n\nconst systemDefault = `\\\n${systemCommon}\n- is the current block of text the user is working on.\n- Ensure your output can seamlessly fit into the existing structure.\n- CRITICAL: Provide only a single block of text. DO NOT create multiple paragraphs or separate blocks.\n\n{block}\n \n`;\n\nconst systemSelecting = `\\\n${systemCommon}\n- is the block of text containing the user's selection, providing context.\n- Ensure your output can seamlessly fit into the existing structure.\n- is the specific text the user has selected in the block and wants to modify or ask about.\n- Consider the context provided by , but only modify . Your response should be a direct replacement for .\n\n{block}\n \n\n{selection}\n \n`;\n\nconst systemBlockSelecting = `\\\n${systemCommon}\n- represents the full blocks of text the user has selected and wants to modify or ask about.\n- Your response should be a direct replacement for the entire .\n- Maintain the overall structure and formatting of the selected blocks, unless explicitly instructed otherwise.\n- CRITICAL: Provide only the content to replace . Do not add additional blocks or change the block structure unless specifically requested.\n\n{block}\n \n`;\n\nconst userDefault = `\nCRITICAL: DO NOT use block formatting. You can only use inline formatting.\nCRITICAL: DO NOT start new lines or paragraphs.\nNEVER write .\n \n{prompt}`;\n\nconst userSelecting = `\nIf this is a question, provide a helpful and concise answer about .\nIf this is an instruction, provide ONLY the text to replace . No explanations.\nEnsure it fits seamlessly within . If is empty, write ONE random sentence.\nNEVER write or .\n \n{prompt} about `;\n\nconst userBlockSelecting = `\nIf this is a question, provide a helpful and concise answer about .\nIf this is an instruction, provide ONLY the content to replace the entire . No explanations.\nMaintain the overall structure unless instructed otherwise.\nNEVER write or .\n \n{prompt} about `;\n\nexport const PROMPT_TEMPLATES = {\n systemBlockSelecting,\n systemDefault,\n systemSelecting,\n userBlockSelecting,\n userDefault,\n userSelecting,\n};\n\nexport const aiPlugins = [\n SelectionOverlayPlugin,\n MarkdownPlugin.configure({ options: { indentList: true } }),\n AIPlugin,\n AIChatPlugin.configure({\n options: {\n createAIEditor,\n promptTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.userBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.userSelecting\n : PROMPT_TEMPLATES.userDefault;\n },\n scrollContainerSelector: '#scroll_container',\n systemTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.systemBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.systemSelecting\n : PROMPT_TEMPLATES.systemDefault;\n },\n },\n render: { afterEditable: () => },\n }),\n] as const;\n",
+ "content": "'use client';\n\nimport React from 'react';\n\nimport { withProps } from '@udecode/cn';\nimport { AIChatPlugin, AIPlugin } from '@udecode/plate-ai/react';\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport {\n CodeBlockPlugin,\n CodeLinePlugin,\n CodeSyntaxPlugin,\n} from '@udecode/plate-code-block/react';\nimport {\n ParagraphPlugin,\n PlateLeaf,\n createPlateEditor,\n} from '@udecode/plate-common/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { LinkPlugin } from '@udecode/plate-link/react';\nimport { MarkdownPlugin } from '@udecode/plate-markdown';\n\nimport { cursorOverlayPlugin } from '@/components/editor/plugins/cursor-overlay-plugin';\nimport { AIMenu } from '@/components/plate-ui/ai-menu';\nimport { BlockquoteElement } from '@/components/plate-ui/blockquote-element';\nimport { CodeBlockElement } from '@/components/plate-ui/code-block-element';\nimport { CodeLeaf } from '@/components/plate-ui/code-leaf';\nimport { CodeLineElement } from '@/components/plate-ui/code-line-element';\nimport { CodeSyntaxLeaf } from '@/components/plate-ui/code-syntax-leaf';\nimport { HeadingElement } from '@/components/plate-ui/heading-element';\nimport { HrElement } from '@/components/plate-ui/hr-element';\nimport { LinkElement } from '@/components/plate-ui/link-element';\nimport { ParagraphElement } from '@/components/plate-ui/paragraph-element';\n\nimport { basicNodesPlugins } from './basic-nodes-plugins';\nimport { blockSelectionReadOnlyPlugin } from './block-selection-plugins';\nimport { indentListPlugins } from './indent-list-plugins';\nimport { linkPlugin } from './link-plugin';\n\nconst createAIEditor = () => {\n const editor = createPlateEditor({\n id: 'ai',\n override: {\n components: {\n [BlockquotePlugin.key]: BlockquoteElement,\n [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }),\n [CodeBlockPlugin.key]: CodeBlockElement,\n [CodeLinePlugin.key]: CodeLineElement,\n [CodePlugin.key]: CodeLeaf,\n [CodeSyntaxPlugin.key]: CodeSyntaxLeaf,\n [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }),\n [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }),\n [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }),\n [HorizontalRulePlugin.key]: HrElement,\n [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }),\n [LinkPlugin.key]: LinkElement,\n [ParagraphPlugin.key]: ParagraphElement,\n [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }),\n [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }),\n },\n },\n plugins: [\n ...basicNodesPlugins,\n ...indentListPlugins,\n HorizontalRulePlugin,\n linkPlugin,\n MarkdownPlugin.configure({ options: { indentList: true } }),\n blockSelectionReadOnlyPlugin,\n ],\n });\n\n return editor;\n};\n\nconst systemCommon = `\\\nYou are an advanced AI-powered note-taking assistant, designed to enhance productivity and creativity in note management.\nRespond directly to user prompts with clear, concise, and relevant content. Maintain a neutral, helpful tone.\n\nRules:\n- is the entire note the user is working on.\n- is a reminder of how you should reply to INSTRUCTIONS. It does not apply to questions.\n- Anything else is the user prompt.\n- Your response should be tailored to the user's prompt, providing precise assistance to optimize note management.\n- For INSTRUCTIONS: Follow the exactly. Provide ONLY the content to be inserted or replaced. No explanations or comments.\n- For QUESTIONS: Provide a helpful and concise answer. You may include brief explanations if necessary.\n- CRITICAL: Distinguish between INSTRUCTIONS and QUESTIONS. Instructions typically ask you to modify or add content. Questions ask for information or clarification.\n`;\n\nconst systemDefault = `\\\n${systemCommon}\n- is the current block of text the user is working on.\n- Ensure your output can seamlessly fit into the existing structure.\n- CRITICAL: Provide only a single block of text. DO NOT create multiple paragraphs or separate blocks.\n\n{block}\n \n`;\n\nconst systemSelecting = `\\\n${systemCommon}\n- is the block of text containing the user's selection, providing context.\n- Ensure your output can seamlessly fit into the existing structure.\n- is the specific text the user has selected in the block and wants to modify or ask about.\n- Consider the context provided by , but only modify . Your response should be a direct replacement for .\n\n{block}\n \n\n{selection}\n \n`;\n\nconst systemBlockSelecting = `\\\n${systemCommon}\n- represents the full blocks of text the user has selected and wants to modify or ask about.\n- Your response should be a direct replacement for the entire .\n- Maintain the overall structure and formatting of the selected blocks, unless explicitly instructed otherwise.\n- CRITICAL: Provide only the content to replace . Do not add additional blocks or change the block structure unless specifically requested.\n\n{block}\n \n`;\n\nconst userDefault = `\nCRITICAL: DO NOT use block formatting. You can only use inline formatting.\nCRITICAL: DO NOT start new lines or paragraphs.\nNEVER write .\n \n{prompt}`;\n\nconst userSelecting = `\nIf this is a question, provide a helpful and concise answer about .\nIf this is an instruction, provide ONLY the text to replace . No explanations.\nEnsure it fits seamlessly within . If is empty, write ONE random sentence.\nNEVER write or .\n \n{prompt} about `;\n\nconst userBlockSelecting = `\nIf this is a question, provide a helpful and concise answer about .\nIf this is an instruction, provide ONLY the content to replace the entire . No explanations.\nMaintain the overall structure unless instructed otherwise.\nNEVER write or .\n \n{prompt} about `;\n\nexport const PROMPT_TEMPLATES = {\n systemBlockSelecting,\n systemDefault,\n systemSelecting,\n userBlockSelecting,\n userDefault,\n userSelecting,\n};\n\nexport const aiPlugins = [\n cursorOverlayPlugin,\n MarkdownPlugin.configure({ options: { indentList: true } }),\n AIPlugin,\n AIChatPlugin.configure({\n options: {\n createAIEditor,\n promptTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.userBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.userSelecting\n : PROMPT_TEMPLATES.userDefault;\n },\n systemTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.systemBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.systemSelecting\n : PROMPT_TEMPLATES.systemDefault;\n },\n },\n render: { afterEditable: () => },\n }),\n] as const;\n",
"path": "components/editor/plugins/ai-plugins.tsx",
"target": "components/editor/plugins/ai-plugins.tsx",
"type": "registry:component"
@@ -21,6 +20,8 @@
"name": "ai-plugins",
"registryDependencies": [
"basic-nodes-plugins",
+ "block-selection-plugins",
+ "cursor-overlay-plugin",
"indent-list-plugins",
"link-plugin",
"ai-menu",
diff --git a/apps/www/public/r/styles/default/ai-toolbar-button.json b/apps/www/public/r/styles/default/ai-toolbar-button.json
index d988d31ed7..25beec8982 100644
--- a/apps/www/public/r/styles/default/ai-toolbar-button.json
+++ b/apps/www/public/r/styles/default/ai-toolbar-button.json
@@ -21,7 +21,7 @@
},
"files": [
{
- "content": "'use client';\n\nimport React from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport { AIChatPlugin } from '@udecode/plate-ai/react';\nimport { useEditorPlugin } from '@udecode/plate-common/react';\n\nimport { ToolbarButton } from './toolbar';\n\nexport const AIToolbarButton = withRef(\n ({ children, ...rest }, ref) => {\n const { api } = useEditorPlugin(AIChatPlugin);\n\n return (\n {\n api.aiChat.show();\n }}\n >\n {children}\n \n );\n }\n);\n",
+ "content": "'use client';\n\nimport React from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport { AIChatPlugin } from '@udecode/plate-ai/react';\nimport { useEditorPlugin } from '@udecode/plate-common/react';\n\nimport { ToolbarButton } from './toolbar';\n\nexport const AIToolbarButton = withRef(\n ({ children, ...rest }, ref) => {\n const { api } = useEditorPlugin(AIChatPlugin);\n\n return (\n {\n api.aiChat.show();\n }}\n onMouseDown={(e) => {\n e.preventDefault();\n }}\n >\n {children}\n \n );\n }\n);\n",
"path": "plate-ui/ai-toolbar-button.tsx",
"target": "components/plate-ui/ai-toolbar-button.tsx",
"type": "registry:ui"
diff --git a/apps/www/public/r/styles/default/alert-dialog.json b/apps/www/public/r/styles/default/alert-dialog.json
new file mode 100644
index 0000000000..67551e4712
--- /dev/null
+++ b/apps/www/public/r/styles/default/alert-dialog.json
@@ -0,0 +1,24 @@
+{
+ "dependencies": [
+ "@radix-ui/react-alert-dialog"
+ ],
+ "doc": {
+ "description": "A modal dialog that interrupts the user with important content and expects a response.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/alert-dialog"
+ }
+ },
+ "files": [
+ {
+ "content": "'use client';\n\nimport * as React from 'react';\n\nimport * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';\nimport { cn } from '@udecode/cn';\n\nimport { buttonVariants } from './button';\n\nconst AlertDialog = AlertDialogPrimitive.Root;\n\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\n\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\n\nconst AlertDialogOverlay = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n));\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\n\nconst AlertDialogContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n \n));\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;\n\nconst AlertDialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes) => (\n
\n);\nAlertDialogHeader.displayName = 'AlertDialogHeader';\n\nconst AlertDialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes) => (\n
\n);\nAlertDialogFooter.displayName = 'AlertDialogFooter';\n\nconst AlertDialogTitle = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n));\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;\n\nconst AlertDialogDescription = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n));\nAlertDialogDescription.displayName =\n AlertDialogPrimitive.Description.displayName;\n\nconst AlertDialogAction = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n));\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;\n\nconst AlertDialogCancel = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n));\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;\n\nexport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogOverlay,\n AlertDialogPortal,\n AlertDialogTitle,\n AlertDialogTrigger,\n};\n",
+ "path": "plate-ui/alert-dialog.tsx",
+ "target": "components/plate-ui/alert-dialog.tsx",
+ "type": "registry:ui"
+ }
+ ],
+ "name": "alert-dialog",
+ "registryDependencies": [
+ "button"
+ ],
+ "type": "registry:ui"
+}
\ No newline at end of file
diff --git a/apps/www/public/r/styles/default/align-demo.json b/apps/www/public/r/styles/default/align-demo.json
index 27185ce4c2..4624cc932c 100644
--- a/apps/www/public/r/styles/default/align-demo.json
+++ b/apps/www/public/r/styles/default/align-demo.json
@@ -4,13 +4,33 @@
},
"files": [
{
- "content": "'use client';\n\nimport React, { useRef } from 'react';\n\nimport type { ValueId } from '@/config/customizer-plugins';\n\nimport { cn } from '@udecode/cn';\nimport { AutoformatPlugin } from '@udecode/plate-autoformat/react';\nimport { SingleLinePlugin } from '@udecode/plate-break/react';\nimport { CommentsPlugin } from '@udecode/plate-comments/react';\nimport { Plate, usePlateEditor } from '@udecode/plate-common/react';\nimport { ExcalidrawPlugin } from '@udecode/plate-excalidraw/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { ListPlugin, TodoListPlugin } from '@udecode/plate-list/react';\nimport { NormalizeTypesPlugin } from '@udecode/plate-normalizers';\nimport { PlaywrightPlugin } from '@udecode/plate-playwright';\nimport { TablePlugin } from '@udecode/plate-table/react';\n\nimport { CheckPlugin } from '@/components/context/check-plugin';\nimport { settingsStore } from '@/components/context/settings-store';\nimport { getAutoformatOptions } from '@/lib/plate/demo/plugins/autoformatOptions';\nimport { createPlateUI } from '@/plate/create-plate-ui';\nimport { editableProps } from '@/plate/demo/editableProps';\nimport { isEnabled } from '@/plate/demo/is-enabled';\nimport { DragOverCursorPlugin } from '@/plate/demo/plugins/DragOverCursorPlugin';\nimport { usePlaygroundValue } from '@/plate/demo/values/usePlaygroundValue';\nimport { editorPlugins } from '@/components/editor/plugins/editor-plugins';\nimport { CommentsPopover } from '@/components/plate-ui/comments-popover';\nimport { CursorOverlay } from '@/components/plate-ui/cursor-overlay';\nimport { Editor, EditorContainer } from '@/components/plate-ui/editor';\nimport { FixedToolbar } from '@/components/plate-ui/fixed-toolbar';\nimport { FixedToolbarButtons } from '@/components/plate-ui/fixed-toolbar-buttons';\nimport { FixedToolbarButtonsList } from '@/components/plate-ui/fixed-toolbar-buttons-list';\nimport { FloatingToolbar } from '@/components/plate-ui/floating-toolbar';\nimport { FloatingToolbarButtons } from '@/components/plate-ui/floating-toolbar-buttons';\n\nimport { usePlaygroundEnabled } from './usePlaygroundEnabled';\n\nexport const usePlaygroundEditor = (id: any = '') => {\n const enabledPlugins = settingsStore.use.checkedPlugins();\n const overridePlugins = usePlaygroundEnabled(id);\n const autoformatOptions = getAutoformatOptions(id, enabledPlugins);\n\n const value = usePlaygroundValue(id);\n const key = settingsStore.use.version();\n const editorId = id || 'playground-' + key;\n\n return usePlateEditor(\n {\n id: editorId,\n override: {\n components: createPlateUI({\n draggable: isEnabled('dnd', id),\n placeholder: isEnabled('placeholder', id),\n }),\n plugins: overridePlugins,\n },\n plugins: [\n ...editorPlugins,\n\n AutoformatPlugin.configure({\n options: autoformatOptions,\n }),\n TablePlugin.configure({\n options: {\n enableMerging: id === 'tableMerge',\n },\n }),\n ListPlugin,\n TodoListPlugin,\n ExcalidrawPlugin,\n NormalizeTypesPlugin.configure({\n options: {\n rules: [{ path: [0], strictType: HEADING_KEYS.h1 }],\n },\n }),\n SingleLinePlugin,\n\n // Testing\n PlaywrightPlugin.configure({\n enabled: process.env.NODE_ENV !== 'production',\n }),\n ],\n value: value,\n },\n []\n );\n};\n\nexport default function PlaygroundDemo({\n id,\n className,\n}: {\n id?: ValueId;\n className?: string;\n scrollSelector?: string;\n}) {\n const containerRef = useRef(null);\n const enabled = settingsStore.use.checkedComponents();\n\n const editor = usePlaygroundEditor(id);\n\n return (\n \n \n \n \n \n {id === 'list' ? (\n \n ) : (\n \n )}\n \n \n \n\n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n \n\n \n
\n \n \n );\n}\n\nconst DemoIdContext = React.createContext(undefined);\n\nexport function DemoId({\n id,\n children,\n}: {\n children: React.ReactNode;\n id?: string;\n}) {\n return {children} ;\n}\n\nexport function useDemoId() {\n return React.useContext(DemoIdContext);\n}\n",
- "path": "example/playground-demo.tsx",
- "target": "components/playground-demo.tsx",
+ "content": "'use client';\n\nimport React from 'react';\n\nimport { Plate } from '@udecode/plate-common/react';\n\nimport { editorPlugins } from '@/components/editor/plugins/editor-plugins';\nimport { useCreateEditor } from '@/components/editor/use-create-editor';\nimport { Editor, EditorContainer } from '@/components/plate-ui/editor';\n\nimport { DEMO_VALUES } from './values/demo-values';\n\nexport default function Demo({ id }: { id: string }) {\n const editor = useCreateEditor({\n plugins: [...editorPlugins],\n value: DEMO_VALUES[id],\n });\n\n return (\n \n \n \n \n \n );\n}\n",
+ "path": "example/demo.tsx",
+ "target": "components/demo.tsx",
+ "type": "registry:example"
+ },
+ {
+ "content": "import { jsx } from '@udecode/plate-test-utils';\n\njsx;\n\nexport const alignValue: any = (\n \n Alignment \n \n Align text within blocks to create visually appealing and balanced\n layouts.\n \n Center \n \n Create clean and balanced layouts by justifying block text, providing a\n professional and polished look.\n \n \n);\n",
+ "path": "example/values/align-value.tsx",
+ "target": "components/align-value.tsx",
+ "type": "registry:example"
+ },
+ {
+ "content": "'use client';\n\nimport type { Value } from '@udecode/plate-common';\n\nimport { withProps } from '@udecode/cn';\nimport { AIPlugin } from '@udecode/plate-ai/react';\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n SubscriptPlugin,\n SuperscriptPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport {\n CodeBlockPlugin,\n CodeLinePlugin,\n CodeSyntaxPlugin,\n} from '@udecode/plate-code-block/react';\nimport { CommentsPlugin } from '@udecode/plate-comments/react';\nimport {\n type CreatePlateEditorOptions,\n ParagraphPlugin,\n PlateLeaf,\n usePlateEditor,\n} from '@udecode/plate-common/react';\nimport { DatePlugin } from '@udecode/plate-date/react';\nimport { EmojiInputPlugin } from '@udecode/plate-emoji/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { TocPlugin } from '@udecode/plate-heading/react';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { KbdPlugin } from '@udecode/plate-kbd/react';\nimport { ColumnItemPlugin, ColumnPlugin } from '@udecode/plate-layout/react';\nimport { LinkPlugin } from '@udecode/plate-link/react';\nimport {\n AudioPlugin,\n FilePlugin,\n ImagePlugin,\n MediaEmbedPlugin,\n PlaceholderPlugin,\n VideoPlugin,\n} from '@udecode/plate-media/react';\nimport {\n MentionInputPlugin,\n MentionPlugin,\n} from '@udecode/plate-mention/react';\nimport { SlashInputPlugin } from '@udecode/plate-slash-command/react';\nimport {\n TableCellHeaderPlugin,\n TableCellPlugin,\n TablePlugin,\n TableRowPlugin,\n} from '@udecode/plate-table/react';\nimport { TogglePlugin } from '@udecode/plate-toggle/react';\n\nimport { AILeaf } from '@/components/plate-ui/ai-leaf';\nimport { BlockquoteElement } from '@/components/plate-ui/blockquote-element';\nimport { CodeBlockElement } from '@/components/plate-ui/code-block-element';\nimport { CodeLeaf } from '@/components/plate-ui/code-leaf';\nimport { CodeLineElement } from '@/components/plate-ui/code-line-element';\nimport { CodeSyntaxLeaf } from '@/components/plate-ui/code-syntax-leaf';\nimport { ColumnElement } from '@/components/plate-ui/column-element';\nimport { ColumnGroupElement } from '@/components/plate-ui/column-group-element';\nimport { CommentLeaf } from '@/components/plate-ui/comment-leaf';\nimport { DateElement } from '@/components/plate-ui/date-element';\nimport { EmojiInputElement } from '@/components/plate-ui/emoji-input-element';\nimport { HeadingElement } from '@/components/plate-ui/heading-element';\nimport { HighlightLeaf } from '@/components/plate-ui/highlight-leaf';\nimport { HrElement } from '@/components/plate-ui/hr-element';\nimport { ImageElement } from '@/components/plate-ui/image-element';\nimport { KbdLeaf } from '@/components/plate-ui/kbd-leaf';\nimport { LinkElement } from '@/components/plate-ui/link-element';\nimport { MediaAudioElement } from '@/components/plate-ui/media-audio-element';\nimport { MediaEmbedElement } from '@/components/plate-ui/media-embed-element';\nimport { MediaFileElement } from '@/components/plate-ui/media-file-element';\nimport { MediaPlaceholderElement } from '@/components/plate-ui/media-placeholder-element';\nimport { MediaVideoElement } from '@/components/plate-ui/media-video-element';\nimport { MentionElement } from '@/components/plate-ui/mention-element';\nimport { MentionInputElement } from '@/components/plate-ui/mention-input-element';\nimport { ParagraphElement } from '@/components/plate-ui/paragraph-element';\nimport { withPlaceholders } from '@/components/plate-ui/placeholder';\nimport { SlashInputElement } from '@/components/plate-ui/slash-input-element';\nimport {\n TableCellElement,\n TableCellHeaderElement,\n} from '@/components/plate-ui/table-cell-element';\nimport { TableElement } from '@/components/plate-ui/table-element';\nimport { TableRowElement } from '@/components/plate-ui/table-row-element';\nimport { TocElement } from '@/components/plate-ui/toc-element';\nimport { ToggleElement } from '@/components/plate-ui/toggle-element';\nimport { withDraggables } from '@/components/plate-ui/with-draggables';\n\nimport { editorPlugins, viewPlugins } from './plugins/editor-plugins';\n\nexport const viewComponents = {\n [AudioPlugin.key]: MediaAudioElement,\n [BlockquotePlugin.key]: BlockquoteElement,\n [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }),\n [CodeBlockPlugin.key]: CodeBlockElement,\n [CodeLinePlugin.key]: CodeLineElement,\n [CodePlugin.key]: CodeLeaf,\n [CodeSyntaxPlugin.key]: CodeSyntaxLeaf,\n [ColumnItemPlugin.key]: ColumnElement,\n [ColumnPlugin.key]: ColumnGroupElement,\n [CommentsPlugin.key]: CommentLeaf,\n [DatePlugin.key]: DateElement,\n [FilePlugin.key]: MediaFileElement,\n [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }),\n [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }),\n [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }),\n [HEADING_KEYS.h4]: withProps(HeadingElement, { variant: 'h4' }),\n [HEADING_KEYS.h5]: withProps(HeadingElement, { variant: 'h5' }),\n [HEADING_KEYS.h6]: withProps(HeadingElement, { variant: 'h6' }),\n [HighlightPlugin.key]: HighlightLeaf,\n [HorizontalRulePlugin.key]: HrElement,\n [ImagePlugin.key]: ImageElement,\n [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }),\n [KbdPlugin.key]: KbdLeaf,\n [LinkPlugin.key]: LinkElement,\n [MediaEmbedPlugin.key]: MediaEmbedElement,\n [MentionPlugin.key]: MentionElement,\n [ParagraphPlugin.key]: ParagraphElement,\n [PlaceholderPlugin.key]: MediaPlaceholderElement,\n [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }),\n [SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }),\n [SuperscriptPlugin.key]: withProps(PlateLeaf, { as: 'sup' }),\n [TableCellHeaderPlugin.key]: TableCellHeaderElement,\n [TableCellPlugin.key]: TableCellElement,\n [TablePlugin.key]: TableElement,\n [TableRowPlugin.key]: TableRowElement,\n [TocPlugin.key]: TocElement,\n [TogglePlugin.key]: ToggleElement,\n [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }),\n [VideoPlugin.key]: MediaVideoElement,\n};\n\nexport const editorComponents = {\n ...viewComponents,\n [AIPlugin.key]: AILeaf,\n [EmojiInputPlugin.key]: EmojiInputElement,\n [MentionInputPlugin.key]: MentionInputElement,\n [SlashInputPlugin.key]: SlashInputElement,\n};\n\nexport const useCreateEditor = (\n {\n components,\n override,\n readOnly,\n ...options\n }: {\n components?: Record;\n plugins?: any[];\n readOnly?: boolean;\n } & Omit = {},\n deps: any[] = []\n) => {\n return usePlateEditor(\n {\n override: {\n components: {\n ...(readOnly\n ? viewComponents\n : withPlaceholders(withDraggables(editorComponents))),\n ...components,\n },\n ...override,\n },\n plugins: (readOnly ? viewPlugins : editorPlugins) as any,\n ...options,\n },\n deps\n );\n};\n",
+ "path": "components/editor/use-create-editor.ts",
+ "target": "components/use-create-editor.ts",
+ "type": "registry:example"
+ },
+ {
+ "content": "'use client';\n\nimport { CalloutPlugin } from '@udecode/plate-callout/react';\nimport { ParagraphPlugin } from '@udecode/plate-common/react';\nimport { DatePlugin } from '@udecode/plate-date/react';\nimport { DocxPlugin } from '@udecode/plate-docx';\nimport { EmojiPlugin } from '@udecode/plate-emoji/react';\nimport {\n FontBackgroundColorPlugin,\n FontColorPlugin,\n FontSizePlugin,\n} from '@udecode/plate-font/react';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { JuicePlugin } from '@udecode/plate-juice';\nimport { KbdPlugin } from '@udecode/plate-kbd/react';\nimport { ColumnPlugin } from '@udecode/plate-layout/react';\nimport { MarkdownPlugin } from '@udecode/plate-markdown';\nimport {\n EquationPlugin,\n InlineEquationPlugin,\n} from '@udecode/plate-math/react';\nimport { SlashPlugin } from '@udecode/plate-slash-command/react';\nimport { TogglePlugin } from '@udecode/plate-toggle/react';\nimport { TrailingBlockPlugin } from '@udecode/plate-trailing-block';\n\nimport { FixedToolbarPlugin } from '@/components/editor/plugins/fixed-toolbar-plugin';\nimport { FloatingToolbarPlugin } from '@/components/editor/plugins/floating-toolbar-plugin';\n\nimport { aiPlugins } from './ai-plugins';\nimport { alignPlugin } from './align-plugin';\nimport { autoformatPlugin } from './autoformat-plugin';\nimport { basicNodesPlugins } from './basic-nodes-plugins';\nimport { blockMenuPlugins } from './block-menu-plugins';\nimport { commentsPlugin } from './comments-plugin';\nimport { cursorOverlayPlugin } from './cursor-overlay-plugin';\nimport { deletePlugins } from './delete-plugins';\nimport { dndPlugins } from './dnd-plugins';\nimport { exitBreakPlugin } from './exit-break-plugin';\nimport { indentListPlugins } from './indent-list-plugins';\nimport { lineHeightPlugin } from './line-height-plugin';\nimport { linkPlugin } from './link-plugin';\nimport { mediaPlugins } from './media-plugins';\nimport { mentionPlugin } from './mention-plugin';\nimport { resetBlockTypePlugin } from './reset-block-type-plugin';\nimport { softBreakPlugin } from './soft-break-plugin';\nimport { tablePlugin } from './table-plugin';\nimport { tocPlugin } from './toc-plugin';\n\nexport const viewPlugins = [\n ...basicNodesPlugins,\n HorizontalRulePlugin,\n linkPlugin,\n DatePlugin,\n mentionPlugin,\n tablePlugin,\n TogglePlugin,\n tocPlugin,\n ...mediaPlugins,\n InlineEquationPlugin,\n EquationPlugin,\n CalloutPlugin,\n ColumnPlugin,\n\n // Marks\n FontColorPlugin,\n FontBackgroundColorPlugin,\n FontSizePlugin,\n HighlightPlugin,\n KbdPlugin,\n\n // Block Style\n alignPlugin,\n ...indentListPlugins,\n lineHeightPlugin,\n\n // Collaboration\n commentsPlugin,\n] as const;\n\nexport const editorPlugins = [\n // AI\n ...aiPlugins,\n\n // Nodes\n ...viewPlugins,\n\n // Functionality\n SlashPlugin,\n autoformatPlugin,\n cursorOverlayPlugin,\n ...blockMenuPlugins,\n ...dndPlugins,\n EmojiPlugin,\n exitBreakPlugin,\n resetBlockTypePlugin,\n ...deletePlugins,\n softBreakPlugin,\n TrailingBlockPlugin.configure({ options: { type: ParagraphPlugin.key } }),\n\n // Deserialization\n DocxPlugin,\n MarkdownPlugin.configure({ options: { indentList: true } }),\n JuicePlugin,\n\n // UI\n FixedToolbarPlugin,\n FloatingToolbarPlugin,\n];\n",
+ "path": "components/editor/plugins/editor-plugins.tsx",
+ "target": "components/editor-plugins.tsx",
"type": "registry:example"
}
],
"name": "align-demo",
- "registryDependencies": [],
+ "registryDependencies": [
+ "align-plugin"
+ ],
"type": "registry:example"
}
\ No newline at end of file
diff --git a/apps/www/public/r/styles/default/api-ai.json b/apps/www/public/r/styles/default/api-ai.json
index 2296e52502..3b1d961861 100644
--- a/apps/www/public/r/styles/default/api-ai.json
+++ b/apps/www/public/r/styles/default/api-ai.json
@@ -6,20 +6,18 @@
"files": [
{
"content": "import type { NextRequest } from 'next/server';\n\nimport { createOpenAI } from '@ai-sdk/openai';\nimport { convertToCoreMessages, streamText } from 'ai';\nimport { NextResponse } from 'next/server';\n\nexport async function POST(req: NextRequest) {\n const {\n apiKey: key,\n messages,\n model = 'gpt-4o-mini',\n system,\n } = await req.json();\n\n const apiKey = key || process.env.OPENAI_API_KEY;\n\n if (!apiKey) {\n return NextResponse.json(\n { error: 'Missing OpenAI API key.' },\n { status: 401 }\n );\n }\n\n const openai = createOpenAI({ apiKey });\n\n try {\n const result = await streamText({\n maxTokens: 2048,\n messages: convertToCoreMessages(messages),\n model: openai(model),\n system: system,\n });\n\n return result.toDataStreamResponse();\n } catch {\n return NextResponse.json(\n { error: 'Failed to process AI request' },\n { status: 500 }\n );\n }\n}\n",
- "path": "components/api/ai/command/route.ts",
+ "path": "app/api/ai/command/route.ts",
"target": "app/api/ai/command/route.ts",
- "type": "registry:page"
+ "type": "registry:lib"
},
{
"content": "import type { NextRequest } from 'next/server';\n\nimport { createOpenAI } from '@ai-sdk/openai';\nimport { generateText } from 'ai';\nimport { NextResponse } from 'next/server';\n\nexport async function POST(req: NextRequest) {\n const {\n apiKey: key,\n model = 'gpt-4o-mini',\n prompt,\n system,\n } = await req.json();\n\n const apiKey = key || process.env.OPENAI_API_KEY;\n\n if (!apiKey) {\n return NextResponse.json(\n { error: 'Missing OpenAI API key.' },\n { status: 401 }\n );\n }\n\n const openai = createOpenAI({ apiKey });\n\n try {\n const result = await generateText({\n abortSignal: req.signal,\n maxTokens: 50,\n model: openai(model),\n prompt: prompt,\n system,\n temperature: 0.7,\n });\n\n return NextResponse.json(result);\n } catch (error: any) {\n if (error.name === 'AbortError') {\n return NextResponse.json(null, { status: 408 });\n }\n\n return NextResponse.json(\n { error: 'Failed to process AI request' },\n { status: 500 }\n );\n }\n}\n",
- "path": "components/api/ai/copilot/route.ts",
+ "path": "app/api/ai/copilot/route.ts",
"target": "app/api/ai/copilot/route.ts",
- "type": "registry:page"
+ "type": "registry:lib"
}
],
"name": "api-ai",
- "registryDependencies": [
- "use-chat"
- ],
- "type": "registry:component"
+ "registryDependencies": [],
+ "type": "registry:lib"
}
\ No newline at end of file
diff --git a/apps/www/public/r/styles/default/api-uploadthing.json b/apps/www/public/r/styles/default/api-uploadthing.json
new file mode 100644
index 0000000000..a58eea3a4b
--- /dev/null
+++ b/apps/www/public/r/styles/default/api-uploadthing.json
@@ -0,0 +1,16 @@
+{
+ "dependencies": [
+ "uploadthing@7.2.0"
+ ],
+ "files": [
+ {
+ "content": "import type { FileRouter } from 'uploadthing/next';\n\nimport { createRouteHandler, createUploadthing } from 'uploadthing/next';\n\nconst f = createUploadthing();\n\nconst ourFileRouter = {\n editorUploader: f(['image', 'text', 'blob', 'pdf', 'video', 'audio'])\n .middleware(() => {\n return {};\n })\n .onUploadComplete(({ file }) => {\n return { file };\n }),\n} satisfies FileRouter;\n\nexport type OurFileRouter = typeof ourFileRouter;\n\nexport const { GET, POST } = createRouteHandler({\n router: ourFileRouter,\n});\n",
+ "path": "app/api/uploadthing/route.ts",
+ "target": "app/api/uploadthing/route.ts",
+ "type": "registry:lib"
+ }
+ ],
+ "name": "api-uploadthing",
+ "registryDependencies": [],
+ "type": "registry:lib"
+}
\ No newline at end of file
diff --git a/apps/www/public/r/styles/default/autoformat-demo.json b/apps/www/public/r/styles/default/autoformat-demo.json
new file mode 100644
index 0000000000..1ee7314776
--- /dev/null
+++ b/apps/www/public/r/styles/default/autoformat-demo.json
@@ -0,0 +1,37 @@
+{
+ "doc": {
+ "description": "Apply formatting automatically using shortcodes.",
+ "title": "Autoformat"
+ },
+ "files": [
+ {
+ "content": "'use client';\n\nimport React from 'react';\n\nimport { Plate } from '@udecode/plate-common/react';\n\nimport { editorPlugins } from '@/components/editor/plugins/editor-plugins';\nimport { useCreateEditor } from '@/components/editor/use-create-editor';\nimport { Editor, EditorContainer } from '@/components/plate-ui/editor';\n\nimport { DEMO_VALUES } from './values/demo-values';\n\nexport default function Demo({ id }: { id: string }) {\n const editor = useCreateEditor({\n plugins: [...editorPlugins],\n value: DEMO_VALUES[id],\n });\n\n return (\n \n \n \n \n \n );\n}\n",
+ "path": "example/demo.tsx",
+ "target": "components/demo.tsx",
+ "type": "registry:example"
+ },
+ {
+ "content": "import { jsx } from '@udecode/plate-test-utils';\n\njsx;\n\nexport const autoformatValue: any = (\n \n Autoformat \n \n Empower your writing experience by enabling autoformatting features. Add\n Markdown-like shortcuts that automatically apply formatting as you type.\n \n While typing, try these mark rules: \n \n Type ** or __ on either side of\n your text to add **bold* mark.\n \n \n Type * or _ on either side of your\n text to add *italic mark.\n \n\n \n Type ` on either side of your text to add `inline code\n mark.\n \n\n \n Type ~~ on either side of your text to add\n ~~strikethrough~ mark.\n \n \n Note that nothing happens when there is a character before, try on:*bold\n \n \n We even support smart quotes, try typing{' '}\n \"hello\" 'world' .\n \n\n \n At the beginning of any new block or existing block, try these (block\n rules):\n \n\n \n Type * , - or + \n followed by space to create a bulleted list.\n \n \n Type 1. or 1) followed by{' '}\n space \n to create a numbered list.\n \n \n Type [] ,or [x] \n followed by space to create a todo list.\n \n \n Type > followed by space to\n create a block quote.\n \n \n Type ``` to create a code block.\n \n \n Type --- to create a horizontal rule.\n \n\n \n Type # followed by space to create\n an H1 heading.\n \n \n Type ### followed by space to\n create an H3 sub-heading.\n \n \n Type #### followed by space to\n create an H4 sub-heading.\n \n \n Type ##### followed by space to\n create an H5 sub-heading.\n \n \n Type ###### followed by space to\n create an H6 sub-heading.\n \n \n);\n",
+ "path": "example/values/autoformat-value.tsx",
+ "target": "components/autoformat-value.tsx",
+ "type": "registry:example"
+ },
+ {
+ "content": "'use client';\n\nimport type { Value } from '@udecode/plate-common';\n\nimport { withProps } from '@udecode/cn';\nimport { AIPlugin } from '@udecode/plate-ai/react';\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n SubscriptPlugin,\n SuperscriptPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport {\n CodeBlockPlugin,\n CodeLinePlugin,\n CodeSyntaxPlugin,\n} from '@udecode/plate-code-block/react';\nimport { CommentsPlugin } from '@udecode/plate-comments/react';\nimport {\n type CreatePlateEditorOptions,\n ParagraphPlugin,\n PlateLeaf,\n usePlateEditor,\n} from '@udecode/plate-common/react';\nimport { DatePlugin } from '@udecode/plate-date/react';\nimport { EmojiInputPlugin } from '@udecode/plate-emoji/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { TocPlugin } from '@udecode/plate-heading/react';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { KbdPlugin } from '@udecode/plate-kbd/react';\nimport { ColumnItemPlugin, ColumnPlugin } from '@udecode/plate-layout/react';\nimport { LinkPlugin } from '@udecode/plate-link/react';\nimport {\n AudioPlugin,\n FilePlugin,\n ImagePlugin,\n MediaEmbedPlugin,\n PlaceholderPlugin,\n VideoPlugin,\n} from '@udecode/plate-media/react';\nimport {\n MentionInputPlugin,\n MentionPlugin,\n} from '@udecode/plate-mention/react';\nimport { SlashInputPlugin } from '@udecode/plate-slash-command/react';\nimport {\n TableCellHeaderPlugin,\n TableCellPlugin,\n TablePlugin,\n TableRowPlugin,\n} from '@udecode/plate-table/react';\nimport { TogglePlugin } from '@udecode/plate-toggle/react';\n\nimport { AILeaf } from '@/components/plate-ui/ai-leaf';\nimport { BlockquoteElement } from '@/components/plate-ui/blockquote-element';\nimport { CodeBlockElement } from '@/components/plate-ui/code-block-element';\nimport { CodeLeaf } from '@/components/plate-ui/code-leaf';\nimport { CodeLineElement } from '@/components/plate-ui/code-line-element';\nimport { CodeSyntaxLeaf } from '@/components/plate-ui/code-syntax-leaf';\nimport { ColumnElement } from '@/components/plate-ui/column-element';\nimport { ColumnGroupElement } from '@/components/plate-ui/column-group-element';\nimport { CommentLeaf } from '@/components/plate-ui/comment-leaf';\nimport { DateElement } from '@/components/plate-ui/date-element';\nimport { EmojiInputElement } from '@/components/plate-ui/emoji-input-element';\nimport { HeadingElement } from '@/components/plate-ui/heading-element';\nimport { HighlightLeaf } from '@/components/plate-ui/highlight-leaf';\nimport { HrElement } from '@/components/plate-ui/hr-element';\nimport { ImageElement } from '@/components/plate-ui/image-element';\nimport { KbdLeaf } from '@/components/plate-ui/kbd-leaf';\nimport { LinkElement } from '@/components/plate-ui/link-element';\nimport { MediaAudioElement } from '@/components/plate-ui/media-audio-element';\nimport { MediaEmbedElement } from '@/components/plate-ui/media-embed-element';\nimport { MediaFileElement } from '@/components/plate-ui/media-file-element';\nimport { MediaPlaceholderElement } from '@/components/plate-ui/media-placeholder-element';\nimport { MediaVideoElement } from '@/components/plate-ui/media-video-element';\nimport { MentionElement } from '@/components/plate-ui/mention-element';\nimport { MentionInputElement } from '@/components/plate-ui/mention-input-element';\nimport { ParagraphElement } from '@/components/plate-ui/paragraph-element';\nimport { withPlaceholders } from '@/components/plate-ui/placeholder';\nimport { SlashInputElement } from '@/components/plate-ui/slash-input-element';\nimport {\n TableCellElement,\n TableCellHeaderElement,\n} from '@/components/plate-ui/table-cell-element';\nimport { TableElement } from '@/components/plate-ui/table-element';\nimport { TableRowElement } from '@/components/plate-ui/table-row-element';\nimport { TocElement } from '@/components/plate-ui/toc-element';\nimport { ToggleElement } from '@/components/plate-ui/toggle-element';\nimport { withDraggables } from '@/components/plate-ui/with-draggables';\n\nimport { editorPlugins, viewPlugins } from './plugins/editor-plugins';\n\nexport const viewComponents = {\n [AudioPlugin.key]: MediaAudioElement,\n [BlockquotePlugin.key]: BlockquoteElement,\n [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }),\n [CodeBlockPlugin.key]: CodeBlockElement,\n [CodeLinePlugin.key]: CodeLineElement,\n [CodePlugin.key]: CodeLeaf,\n [CodeSyntaxPlugin.key]: CodeSyntaxLeaf,\n [ColumnItemPlugin.key]: ColumnElement,\n [ColumnPlugin.key]: ColumnGroupElement,\n [CommentsPlugin.key]: CommentLeaf,\n [DatePlugin.key]: DateElement,\n [FilePlugin.key]: MediaFileElement,\n [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }),\n [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }),\n [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }),\n [HEADING_KEYS.h4]: withProps(HeadingElement, { variant: 'h4' }),\n [HEADING_KEYS.h5]: withProps(HeadingElement, { variant: 'h5' }),\n [HEADING_KEYS.h6]: withProps(HeadingElement, { variant: 'h6' }),\n [HighlightPlugin.key]: HighlightLeaf,\n [HorizontalRulePlugin.key]: HrElement,\n [ImagePlugin.key]: ImageElement,\n [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }),\n [KbdPlugin.key]: KbdLeaf,\n [LinkPlugin.key]: LinkElement,\n [MediaEmbedPlugin.key]: MediaEmbedElement,\n [MentionPlugin.key]: MentionElement,\n [ParagraphPlugin.key]: ParagraphElement,\n [PlaceholderPlugin.key]: MediaPlaceholderElement,\n [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }),\n [SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }),\n [SuperscriptPlugin.key]: withProps(PlateLeaf, { as: 'sup' }),\n [TableCellHeaderPlugin.key]: TableCellHeaderElement,\n [TableCellPlugin.key]: TableCellElement,\n [TablePlugin.key]: TableElement,\n [TableRowPlugin.key]: TableRowElement,\n [TocPlugin.key]: TocElement,\n [TogglePlugin.key]: ToggleElement,\n [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }),\n [VideoPlugin.key]: MediaVideoElement,\n};\n\nexport const editorComponents = {\n ...viewComponents,\n [AIPlugin.key]: AILeaf,\n [EmojiInputPlugin.key]: EmojiInputElement,\n [MentionInputPlugin.key]: MentionInputElement,\n [SlashInputPlugin.key]: SlashInputElement,\n};\n\nexport const useCreateEditor = (\n {\n components,\n override,\n readOnly,\n ...options\n }: {\n components?: Record;\n plugins?: any[];\n readOnly?: boolean;\n } & Omit = {},\n deps: any[] = []\n) => {\n return usePlateEditor(\n {\n override: {\n components: {\n ...(readOnly\n ? viewComponents\n : withPlaceholders(withDraggables(editorComponents))),\n ...components,\n },\n ...override,\n },\n plugins: (readOnly ? viewPlugins : editorPlugins) as any,\n ...options,\n },\n deps\n );\n};\n",
+ "path": "components/editor/use-create-editor.ts",
+ "target": "components/use-create-editor.ts",
+ "type": "registry:example"
+ },
+ {
+ "content": "'use client';\n\nimport { CalloutPlugin } from '@udecode/plate-callout/react';\nimport { ParagraphPlugin } from '@udecode/plate-common/react';\nimport { DatePlugin } from '@udecode/plate-date/react';\nimport { DocxPlugin } from '@udecode/plate-docx';\nimport { EmojiPlugin } from '@udecode/plate-emoji/react';\nimport {\n FontBackgroundColorPlugin,\n FontColorPlugin,\n FontSizePlugin,\n} from '@udecode/plate-font/react';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { JuicePlugin } from '@udecode/plate-juice';\nimport { KbdPlugin } from '@udecode/plate-kbd/react';\nimport { ColumnPlugin } from '@udecode/plate-layout/react';\nimport { MarkdownPlugin } from '@udecode/plate-markdown';\nimport {\n EquationPlugin,\n InlineEquationPlugin,\n} from '@udecode/plate-math/react';\nimport { SlashPlugin } from '@udecode/plate-slash-command/react';\nimport { TogglePlugin } from '@udecode/plate-toggle/react';\nimport { TrailingBlockPlugin } from '@udecode/plate-trailing-block';\n\nimport { FixedToolbarPlugin } from '@/components/editor/plugins/fixed-toolbar-plugin';\nimport { FloatingToolbarPlugin } from '@/components/editor/plugins/floating-toolbar-plugin';\n\nimport { aiPlugins } from './ai-plugins';\nimport { alignPlugin } from './align-plugin';\nimport { autoformatPlugin } from './autoformat-plugin';\nimport { basicNodesPlugins } from './basic-nodes-plugins';\nimport { blockMenuPlugins } from './block-menu-plugins';\nimport { commentsPlugin } from './comments-plugin';\nimport { cursorOverlayPlugin } from './cursor-overlay-plugin';\nimport { deletePlugins } from './delete-plugins';\nimport { dndPlugins } from './dnd-plugins';\nimport { exitBreakPlugin } from './exit-break-plugin';\nimport { indentListPlugins } from './indent-list-plugins';\nimport { lineHeightPlugin } from './line-height-plugin';\nimport { linkPlugin } from './link-plugin';\nimport { mediaPlugins } from './media-plugins';\nimport { mentionPlugin } from './mention-plugin';\nimport { resetBlockTypePlugin } from './reset-block-type-plugin';\nimport { softBreakPlugin } from './soft-break-plugin';\nimport { tablePlugin } from './table-plugin';\nimport { tocPlugin } from './toc-plugin';\n\nexport const viewPlugins = [\n ...basicNodesPlugins,\n HorizontalRulePlugin,\n linkPlugin,\n DatePlugin,\n mentionPlugin,\n tablePlugin,\n TogglePlugin,\n tocPlugin,\n ...mediaPlugins,\n InlineEquationPlugin,\n EquationPlugin,\n CalloutPlugin,\n ColumnPlugin,\n\n // Marks\n FontColorPlugin,\n FontBackgroundColorPlugin,\n FontSizePlugin,\n HighlightPlugin,\n KbdPlugin,\n\n // Block Style\n alignPlugin,\n ...indentListPlugins,\n lineHeightPlugin,\n\n // Collaboration\n commentsPlugin,\n] as const;\n\nexport const editorPlugins = [\n // AI\n ...aiPlugins,\n\n // Nodes\n ...viewPlugins,\n\n // Functionality\n SlashPlugin,\n autoformatPlugin,\n cursorOverlayPlugin,\n ...blockMenuPlugins,\n ...dndPlugins,\n EmojiPlugin,\n exitBreakPlugin,\n resetBlockTypePlugin,\n ...deletePlugins,\n softBreakPlugin,\n TrailingBlockPlugin.configure({ options: { type: ParagraphPlugin.key } }),\n\n // Deserialization\n DocxPlugin,\n MarkdownPlugin.configure({ options: { indentList: true } }),\n JuicePlugin,\n\n // UI\n FixedToolbarPlugin,\n FloatingToolbarPlugin,\n];\n",
+ "path": "components/editor/plugins/editor-plugins.tsx",
+ "target": "components/editor-plugins.tsx",
+ "type": "registry:example"
+ }
+ ],
+ "name": "autoformat-demo",
+ "registryDependencies": [
+ "autoformat-plugin"
+ ],
+ "type": "registry:example"
+}
\ No newline at end of file
diff --git a/apps/www/public/r/styles/default/autoformat-list-plugin.json b/apps/www/public/r/styles/default/autoformat-list-plugin.json
index 4813d731db..b6c3b0d956 100644
--- a/apps/www/public/r/styles/default/autoformat-list-plugin.json
+++ b/apps/www/public/r/styles/default/autoformat-list-plugin.json
@@ -12,7 +12,7 @@
],
"files": [
{
- "content": "'use client';\n\nimport type {\n AutoformatBlockRule,\n AutoformatRule,\n} from '@udecode/plate-autoformat';\nimport type { SlateEditor } from '@udecode/plate-common';\nimport type { TTodoListItemElement } from '@udecode/plate-list';\n\nimport {\n autoformatArrow,\n autoformatLegal,\n autoformatLegalHtml,\n autoformatMath,\n autoformatPunctuation,\n autoformatSmartQuotes,\n} from '@udecode/plate-autoformat';\nimport { AutoformatPlugin } from '@udecode/plate-autoformat/react';\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n SubscriptPlugin,\n SuperscriptPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport { insertEmptyCodeBlock } from '@udecode/plate-code-block';\nimport {\n CodeBlockPlugin,\n CodeLinePlugin,\n} from '@udecode/plate-code-block/react';\nimport {\n getParentNode,\n insertNodes,\n isBlock,\n isElement,\n isType,\n setNodes,\n} from '@udecode/plate-common';\nimport { ParagraphPlugin } from '@udecode/plate-common/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { toggleList, unwrapList } from '@udecode/plate-list';\nimport {\n BulletedListPlugin,\n ListItemPlugin,\n NumberedListPlugin,\n TodoListPlugin,\n} from '@udecode/plate-list/react';\nimport { TogglePlugin, openNextToggles } from '@udecode/plate-toggle/react';\n\nexport const preFormat: AutoformatBlockRule['preFormat'] = (editor) =>\n unwrapList(editor);\n\nexport const format = (editor: SlateEditor, customFormatting: any) => {\n if (editor.selection) {\n const parentEntry = getParentNode(editor, editor.selection);\n\n if (!parentEntry) return;\n\n const [node] = parentEntry;\n\n if (\n isElement(node) &&\n !isType(editor, node, CodeBlockPlugin.key) &&\n !isType(editor, node, CodeLinePlugin.key)\n ) {\n customFormatting();\n }\n }\n};\n\nexport const formatList = (editor: SlateEditor, elementType: string) => {\n format(editor, () =>\n toggleList(editor, {\n type: elementType,\n })\n );\n};\n\nexport const autoformatMarks: AutoformatRule[] = [\n {\n match: '***',\n mode: 'mark',\n type: [BoldPlugin.key, ItalicPlugin.key],\n },\n {\n match: '__*',\n mode: 'mark',\n type: [UnderlinePlugin.key, ItalicPlugin.key],\n },\n {\n match: '__**',\n mode: 'mark',\n type: [UnderlinePlugin.key, BoldPlugin.key],\n },\n {\n match: '___***',\n mode: 'mark',\n type: [UnderlinePlugin.key, BoldPlugin.key, ItalicPlugin.key],\n },\n {\n match: '**',\n mode: 'mark',\n type: BoldPlugin.key,\n },\n {\n match: '__',\n mode: 'mark',\n type: UnderlinePlugin.key,\n },\n {\n match: '*',\n mode: 'mark',\n type: ItalicPlugin.key,\n },\n {\n match: '_',\n mode: 'mark',\n type: ItalicPlugin.key,\n },\n {\n match: '~~',\n mode: 'mark',\n type: StrikethroughPlugin.key,\n },\n {\n match: '^',\n mode: 'mark',\n type: SuperscriptPlugin.key,\n },\n {\n match: '~',\n mode: 'mark',\n type: SubscriptPlugin.key,\n },\n {\n match: '==',\n mode: 'mark',\n type: HighlightPlugin.key,\n },\n {\n match: '≡',\n mode: 'mark',\n type: HighlightPlugin.key,\n },\n {\n match: '`',\n mode: 'mark',\n type: CodePlugin.key,\n },\n];\n\nexport const autoformatBlocks: AutoformatRule[] = [\n {\n match: '# ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h1,\n },\n {\n match: '## ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h2,\n },\n {\n match: '### ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h3,\n },\n {\n match: '#### ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h4,\n },\n {\n match: '##### ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h5,\n },\n {\n match: '###### ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h6,\n },\n {\n match: '> ',\n mode: 'block',\n preFormat,\n type: BlockquotePlugin.key,\n },\n {\n format: (editor) => {\n insertEmptyCodeBlock(editor, {\n defaultType: ParagraphPlugin.key,\n insertNodesOptions: { select: true },\n });\n },\n match: '```',\n mode: 'block',\n preFormat,\n triggerAtBlockStart: false,\n type: CodeBlockPlugin.key,\n },\n {\n match: '+ ',\n mode: 'block',\n preFormat: openNextToggles,\n type: TogglePlugin.key,\n },\n {\n format: (editor) => {\n setNodes(editor, { type: HorizontalRulePlugin.key });\n insertNodes(editor, {\n children: [{ text: '' }],\n type: ParagraphPlugin.key,\n });\n },\n match: ['---', '—-', '___ '],\n mode: 'block',\n type: HorizontalRulePlugin.key,\n },\n];\n\nexport const autoformatLists: AutoformatRule[] = [\n {\n format: (editor) => formatList(editor, BulletedListPlugin.key),\n match: ['* ', '- '],\n mode: 'block',\n preFormat,\n type: ListItemPlugin.key,\n },\n {\n format: (editor) => formatList(editor, NumberedListPlugin.key),\n match: [String.raw`^\\d+\\.$ `, String.raw`^\\d+\\)$ `],\n matchByRegex: true,\n mode: 'block',\n preFormat,\n type: ListItemPlugin.key,\n },\n {\n match: '[] ',\n mode: 'block',\n type: TodoListPlugin.key,\n },\n {\n format: (editor) =>\n setNodes(\n editor,\n { checked: true, type: TodoListPlugin.key },\n {\n match: (n) => isBlock(editor, n),\n }\n ),\n match: '[x] ',\n mode: 'block',\n type: TodoListPlugin.key,\n },\n];\n\nexport const autoformatPlugin = AutoformatPlugin.configure({\n options: {\n enableUndoOnDelete: true,\n rules: [\n ...autoformatBlocks,\n ...autoformatMarks,\n ...autoformatSmartQuotes,\n ...autoformatPunctuation,\n ...autoformatLegal,\n ...autoformatLegalHtml,\n ...autoformatArrow,\n ...autoformatMath,\n ...autoformatLists,\n ],\n },\n});\n",
+ "content": "'use client';\n\nimport type {\n AutoformatBlockRule,\n AutoformatRule,\n} from '@udecode/plate-autoformat';\nimport type { SlateEditor } from '@udecode/plate-common';\nimport type { TTodoListItemElement } from '@udecode/plate-list';\n\nimport {\n autoformatArrow,\n autoformatLegal,\n autoformatLegalHtml,\n autoformatMath,\n autoformatPunctuation,\n autoformatSmartQuotes,\n} from '@udecode/plate-autoformat';\nimport { AutoformatPlugin } from '@udecode/plate-autoformat/react';\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n SubscriptPlugin,\n SuperscriptPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport { insertEmptyCodeBlock } from '@udecode/plate-code-block';\nimport {\n CodeBlockPlugin,\n CodeLinePlugin,\n} from '@udecode/plate-code-block/react';\nimport {\n getParentNode,\n insertNodes,\n isBlock,\n isElement,\n isType,\n setNodes,\n} from '@udecode/plate-common';\nimport { ParagraphPlugin } from '@udecode/plate-common/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';\nimport { toggleList, unwrapList } from '@udecode/plate-list';\nimport {\n BulletedListPlugin,\n ListItemPlugin,\n NumberedListPlugin,\n TodoListPlugin,\n} from '@udecode/plate-list/react';\nimport { TogglePlugin, openNextToggles } from '@udecode/plate-toggle/react';\n\nexport const preFormat: AutoformatBlockRule['preFormat'] = (editor) =>\n unwrapList(editor);\n\nexport const format = (editor: SlateEditor, customFormatting: any) => {\n if (editor.selection) {\n const parentEntry = getParentNode(editor, editor.selection);\n\n if (!parentEntry) return;\n\n const [node] = parentEntry;\n\n if (\n isElement(node) &&\n !isType(editor, node, CodeBlockPlugin.key) &&\n !isType(editor, node, CodeLinePlugin.key)\n ) {\n customFormatting();\n }\n }\n};\n\nexport const formatList = (editor: SlateEditor, elementType: string) => {\n format(editor, () =>\n toggleList(editor, {\n type: elementType,\n })\n );\n};\n\nexport const autoformatMarks: AutoformatRule[] = [\n {\n match: '***',\n mode: 'mark',\n type: [BoldPlugin.key, ItalicPlugin.key],\n },\n {\n match: '__*',\n mode: 'mark',\n type: [UnderlinePlugin.key, ItalicPlugin.key],\n },\n {\n match: '__**',\n mode: 'mark',\n type: [UnderlinePlugin.key, BoldPlugin.key],\n },\n {\n match: '___***',\n mode: 'mark',\n type: [UnderlinePlugin.key, BoldPlugin.key, ItalicPlugin.key],\n },\n {\n match: '**',\n mode: 'mark',\n type: BoldPlugin.key,\n },\n {\n match: '__',\n mode: 'mark',\n type: UnderlinePlugin.key,\n },\n {\n match: '*',\n mode: 'mark',\n type: ItalicPlugin.key,\n },\n {\n match: '_',\n mode: 'mark',\n type: ItalicPlugin.key,\n },\n {\n match: '~~',\n mode: 'mark',\n type: StrikethroughPlugin.key,\n },\n {\n match: '^',\n mode: 'mark',\n type: SuperscriptPlugin.key,\n },\n {\n match: '~',\n mode: 'mark',\n type: SubscriptPlugin.key,\n },\n {\n match: '==',\n mode: 'mark',\n type: HighlightPlugin.key,\n },\n {\n match: '≡',\n mode: 'mark',\n type: HighlightPlugin.key,\n },\n {\n match: '`',\n mode: 'mark',\n type: CodePlugin.key,\n },\n];\n\nexport const autoformatBlocks: AutoformatRule[] = [\n {\n match: '# ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h1,\n },\n {\n match: '## ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h2,\n },\n {\n match: '### ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h3,\n },\n {\n match: '#### ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h4,\n },\n {\n match: '##### ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h5,\n },\n {\n match: '###### ',\n mode: 'block',\n preFormat,\n type: HEADING_KEYS.h6,\n },\n {\n match: '> ',\n mode: 'block',\n preFormat,\n type: BlockquotePlugin.key,\n },\n {\n format: (editor) => {\n insertEmptyCodeBlock(editor, {\n defaultType: ParagraphPlugin.key,\n insertNodesOptions: { select: true },\n });\n },\n match: '```',\n mode: 'block',\n preFormat,\n triggerAtBlockStart: false,\n type: CodeBlockPlugin.key,\n },\n {\n match: '+ ',\n mode: 'block',\n preFormat: openNextToggles,\n type: TogglePlugin.key,\n },\n {\n format: (editor) => {\n setNodes(editor, { type: HorizontalRulePlugin.key });\n insertNodes(editor, {\n children: [{ text: '' }],\n type: ParagraphPlugin.key,\n });\n },\n match: ['---', '—-', '___ '],\n mode: 'block',\n type: HorizontalRulePlugin.key,\n },\n];\n\nexport const autoformatLists: AutoformatRule[] = [\n {\n format: (editor) => formatList(editor, BulletedListPlugin.key),\n match: ['* ', '- '],\n mode: 'block',\n preFormat,\n type: ListItemPlugin.key,\n },\n {\n format: (editor) => formatList(editor, NumberedListPlugin.key),\n match: [String.raw`^\\d+\\.$ `, String.raw`^\\d+\\)$ `],\n matchByRegex: true,\n mode: 'block',\n preFormat,\n type: ListItemPlugin.key,\n },\n {\n match: '[] ',\n mode: 'block',\n type: TodoListPlugin.key,\n },\n {\n format: (editor) =>\n setNodes(\n editor,\n { checked: true, type: TodoListPlugin.key },\n {\n match: (n) => isBlock(editor, n),\n }\n ),\n match: '[x] ',\n mode: 'block',\n type: TodoListPlugin.key,\n },\n];\n\nexport const autoformatListPlugin = AutoformatPlugin.configure({\n options: {\n enableUndoOnDelete: true,\n rules: [\n ...autoformatBlocks,\n ...autoformatMarks,\n ...autoformatSmartQuotes,\n ...autoformatPunctuation,\n ...autoformatLegal,\n ...autoformatLegalHtml,\n ...autoformatArrow,\n ...autoformatMath,\n ...autoformatLists,\n ],\n },\n});\n",
"path": "components/editor/plugins/autoformat-list-plugin.ts",
"target": "components/editor/plugins/autoformat-list-plugin.ts",
"type": "registry:component"
diff --git a/apps/www/public/r/styles/default/avatar.json b/apps/www/public/r/styles/default/avatar.json
index b36e454462..a4135855c2 100644
--- a/apps/www/public/r/styles/default/avatar.json
+++ b/apps/www/public/r/styles/default/avatar.json
@@ -3,7 +3,10 @@
"@radix-ui/react-avatar"
],
"doc": {
- "description": "An image element with a fallback for representing the user."
+ "description": "An image element with a fallback for representing the user.",
+ "links": {
+ "doc": "https://ui.shadcn.com/docs/components/avatar"
+ }
},
"files": [
{
diff --git a/apps/www/public/r/styles/default/basic-editor-handler-demo.json b/apps/www/public/r/styles/default/basic-editor-handler-demo.json
index af2a8dca49..c040f0b0f3 100644
--- a/apps/www/public/r/styles/default/basic-editor-handler-demo.json
+++ b/apps/www/public/r/styles/default/basic-editor-handler-demo.json
@@ -1,7 +1,7 @@
{
"files": [
{
- "content": "'use client';\n\nimport React, { useState } from 'react';\n\nimport type { Value } from '@udecode/plate-common';\n\nimport {\n type PlateContentProps,\n Plate,\n usePlateEditor,\n} from '@udecode/plate-common/react';\n\nimport {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@/components/ui/accordion';\nimport { Editor, EditorContainer } from '@/components/plate-ui/editor';\n\nconst editableProps: PlateContentProps = {\n autoFocus: false,\n placeholder: 'Type…',\n spellCheck: false,\n};\n\nconst value = [\n {\n children: [\n {\n text: 'This is editable plain text with react and history plugins, just like a textarea!',\n },\n ],\n type: 'p',\n },\n];\n\nexport default function BasicEditorHandlerDemo() {\n const [debugValue, setDebugValue] = useState(value);\n\n const localValue =\n typeof window !== 'undefined' && localStorage.getItem('editorContent');\n\n const editor = usePlateEditor({\n value: localValue ? JSON.parse(localValue) : value,\n });\n\n return (\n {\n localStorage.setItem('editorContent', JSON.stringify(value));\n setDebugValue(value);\n }}\n editor={editor}\n >\n \n \n \n\n \n \n Debug Value \n {JSON.stringify(debugValue)} \n \n \n \n );\n}\n",
+ "content": "'use client';\n\nimport React, { useState } from 'react';\n\nimport type { Value } from '@udecode/plate-common';\n\nimport { Plate, usePlateEditor } from '@udecode/plate-common/react';\n\nimport {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@/components/ui/accordion';\nimport { Editor, EditorContainer } from '@/components/plate-ui/editor';\n\nconst value = [\n {\n children: [\n {\n text: 'This is editable plain text with react and history plugins, just like a textarea!',\n },\n ],\n type: 'p',\n },\n];\n\nexport default function BasicEditorHandlerDemo() {\n const [debugValue, setDebugValue] = useState(value);\n\n const localValue =\n typeof window !== 'undefined' && localStorage.getItem('editorContent');\n\n const editor = usePlateEditor({\n value: localValue ? JSON.parse(localValue) : value,\n });\n\n return (\n {\n localStorage.setItem('editorContent', JSON.stringify(value));\n setDebugValue(value);\n }}\n editor={editor}\n >\n \n \n \n\n \n \n Debug Value \n {JSON.stringify(debugValue)} \n \n \n \n );\n}\n",
"path": "example/basic-editor-handler-demo.tsx",
"target": "components/basic-editor-handler-demo.tsx",
"type": "registry:example"
diff --git a/apps/www/public/r/styles/default/basic-editor-value-demo.json b/apps/www/public/r/styles/default/basic-editor-value-demo.json
index 4e1747ca90..092ddd3690 100644
--- a/apps/www/public/r/styles/default/basic-editor-value-demo.json
+++ b/apps/www/public/r/styles/default/basic-editor-value-demo.json
@@ -1,7 +1,7 @@
{
"files": [
{
- "content": "'use client';\n\nimport React from 'react';\n\nimport {\n type PlateContentProps,\n Plate,\n usePlateEditor,\n} from '@udecode/plate-common/react';\n\nimport { Editor, EditorContainer } from '@/components/plate-ui/editor';\n\nconst editableProps: PlateContentProps = {\n autoFocus: false,\n placeholder: 'Type…',\n spellCheck: false,\n};\n\nconst value = [\n {\n children: [\n {\n text: 'This is editable plain text with react and history plugins, just like a