Skip to content

set x-slate-fragment in PlateView #4431

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .changeset/platejs-core-minor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@platejs/core": minor
---

Added comprehensive copy functionality and view editor support for static rendering.

**New Components:**
- Added `PlateView` component for static editor rendering with copy support
- Added `usePlateViewEditor` hook for creating memoized static editors

**Static Editor Enhancements:**
- Added `withStatic` HOC to enhance editors with static rendering capabilities
- Added `ViewPlugin` that enables copy operations in static editors
- Added `getStaticPlugins` to configure plugins for static rendering
- Added `onCopy` handler that properly serializes content with `x-slate-fragment` format

**New Utilities:**
- Added `getSelectedDomBlocks` to extract selected DOM elements with Slate metadata
- Added `getSelectedDomNode` to get DOM nodes from browser selection
- Added `isSelectOutside` to check if selection is outside editor bounds
- Added `getPlainText` to recursively extract plain text from DOM nodes

This enables seamless copy operations from static Plate editors, allowing content to be pasted into other Slate editors while preserving rich formatting and structure.
7 changes: 7 additions & 0 deletions apps/www/src/app/dev/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function DevLayout(props: { children: React.ReactNode }) {
return (
<>
<main>{props.children}</main>
</>
);
}
41 changes: 41 additions & 0 deletions apps/www/src/app/dev/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import { useEffect, useState } from 'react';

import { usePlateViewEditor } from '@platejs/core/react';

import { BaseEditorKit } from '@/registry/components/editor/editor-base-kit';
import { playgroundValue } from '@/registry/examples/values/playground-value';
import { EditorView } from '@/registry/ui/editor';
import { EditorStatic } from '@/registry/ui/editor-static';

export default function DevPage() {
const [isClient, setIsClient] = useState(false);

useEffect(() => {
setIsClient(true);
}, []);

// const editor = createStaticEditor({ plugins: BaseEditorKit });
const editor = usePlateViewEditor(
{
plugins: BaseEditorKit,
value: playgroundValue,
},
[]
);

if (!isClient) {
return <main>Loading...</main>;
}

return (
<main>
<EditorView editor={editor} />

<h1>123</h1>

<EditorStatic editor={editor} />
</main>
);
}
2 changes: 1 addition & 1 deletion apps/www/src/registry/ui/code-block-node-static.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function CodeBlockElementStatic(
) {
return (
<SlateElement
className="py-1 **:[.hljs-addition]:bg-[#f0fff4] **:[.hljs-addition]:text-[#22863a] **:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#005cc5] **:[.hljs-built\\_in,.hljs-symbol]:text-[#e36209] **:[.hljs-bullet]:text-[#735c0f] **:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] **:[.hljs-deletion]:bg-[#ffeef0] **:[.hljs-deletion]:text-[#b31d28] **:[.hljs-emphasis]:italic **:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\_]:text-[#d73a49] **:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#22863a] **:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#032f62] **:[.hljs-section]:font-bold **:[.hljs-section]:text-[#005cc5] **:[.hljs-strong]:font-bold **:[.hljs-title,.hljs-title.class\\_,.hljs-title.class\\_.inherited\\_\\_,.hljs-title.function\\_]:text-[#6f42c1] dark:**:[.hljs-addition]:bg-[#3c5743] dark:**:[.hljs-addition]:text-[#ceead5] dark:**:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#6596cf] dark:**:[.hljs-built\\_in,.hljs-symbol]:text-[#c3854e] dark:**:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] dark:**:[.hljs-deletion]:bg-[#473235] dark:**:[.hljs-deletion]:text-[#e7c7cb] dark:**:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\_]:text-[#ee6960] dark:**:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#36a84f] dark:**:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#3593ff] dark:**:[.hljs-section]:text-[#61a5f2] dark:**:[.hljs-title,.hljs-title.class\\_,.hljs-title.class\\_.inherited\\_\\_,.hljs-title.function\\_]:text-[#a77bfa]"
className="py-1 **:[.hljs-addition]:bg-[#f0fff4] **:[.hljs-addition]:text-[#22863a] dark:**:[.hljs-addition]:bg-[#3c5743] dark:**:[.hljs-addition]:text-[#ceead5] **:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#005cc5] dark:**:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#6596cf] **:[.hljs-built\\\\_in,.hljs-symbol]:text-[#e36209] dark:**:[.hljs-built\\\\_in,.hljs-symbol]:text-[#c3854e] **:[.hljs-bullet]:text-[#735c0f] **:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] dark:**:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] **:[.hljs-deletion]:bg-[#ffeef0] **:[.hljs-deletion]:text-[#b31d28] dark:**:[.hljs-deletion]:bg-[#473235] dark:**:[.hljs-deletion]:text-[#e7c7cb] **:[.hljs-emphasis]:italic **:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#d73a49] dark:**:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#ee6960] **:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#22863a] dark:**:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#36a84f] **:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#032f62] dark:**:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#3593ff] **:[.hljs-section]:font-bold **:[.hljs-section]:text-[#005cc5] dark:**:[.hljs-section]:text-[#61a5f2] **:[.hljs-strong]:font-bold **:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#6f42c1] dark:**:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#a77bfa]"
{...props}
>
<div className="relative rounded-md bg-muted/50">
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/registry/ui/code-block-node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function CodeBlockElement(props: PlateElementProps<TCodeBlockElement>) {

return (
<PlateElement
className="py-1 **:[.hljs-addition]:bg-[#f0fff4] **:[.hljs-addition]:text-[#22863a] **:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#005cc5] **:[.hljs-built\\_in,.hljs-symbol]:text-[#e36209] **:[.hljs-bullet]:text-[#735c0f] **:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] **:[.hljs-deletion]:bg-[#ffeef0] **:[.hljs-deletion]:text-[#b31d28] **:[.hljs-emphasis]:italic **:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\_]:text-[#d73a49] **:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#22863a] **:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#032f62] **:[.hljs-section]:font-bold **:[.hljs-section]:text-[#005cc5] **:[.hljs-strong]:font-bold **:[.hljs-title,.hljs-title.class\\_,.hljs-title.class\\_.inherited\\_\\_,.hljs-title.function\\_]:text-[#6f42c1] dark:**:[.hljs-addition]:bg-[#3c5743] dark:**:[.hljs-addition]:text-[#ceead5] dark:**:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#6596cf] dark:**:[.hljs-built\\_in,.hljs-symbol]:text-[#c3854e] dark:**:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] dark:**:[.hljs-deletion]:bg-[#473235] dark:**:[.hljs-deletion]:text-[#e7c7cb] dark:**:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\_]:text-[#ee6960] dark:**:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#36a84f] dark:**:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#3593ff] dark:**:[.hljs-section]:text-[#61a5f2] dark:**:[.hljs-title,.hljs-title.class\\_,.hljs-title.class\\_.inherited\\_\\_,.hljs-title.function\\_]:text-[#a77bfa]"
className="py-1 **:[.hljs-addition]:bg-[#f0fff4] **:[.hljs-addition]:text-[#22863a] dark:**:[.hljs-addition]:bg-[#3c5743] dark:**:[.hljs-addition]:text-[#ceead5] **:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#005cc5] dark:**:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#6596cf] **:[.hljs-built\\\\_in,.hljs-symbol]:text-[#e36209] dark:**:[.hljs-built\\\\_in,.hljs-symbol]:text-[#c3854e] **:[.hljs-bullet]:text-[#735c0f] **:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] dark:**:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] **:[.hljs-deletion]:bg-[#ffeef0] **:[.hljs-deletion]:text-[#b31d28] dark:**:[.hljs-deletion]:bg-[#473235] dark:**:[.hljs-deletion]:text-[#e7c7cb] **:[.hljs-emphasis]:italic **:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#d73a49] dark:**:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#ee6960] **:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#22863a] dark:**:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#36a84f] **:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#032f62] dark:**:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#3593ff] **:[.hljs-section]:font-bold **:[.hljs-section]:text-[#005cc5] dark:**:[.hljs-section]:text-[#61a5f2] **:[.hljs-strong]:font-bold **:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#6f42c1] dark:**:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#a77bfa]"
{...props}
>
<div className="relative rounded-md bg-muted/50">
Expand Down
19 changes: 17 additions & 2 deletions apps/www/src/registry/ui/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import * as React from 'react';

import type { VariantProps } from 'class-variance-authority';
import type { PlateContentProps } from 'platejs/react';
import type { PlateContentProps, PlateViewProps } from 'platejs/react';

import { cva } from 'class-variance-authority';
import { PlateContainer, PlateContent } from 'platejs/react';
import { PlateContainer, PlateContent, PlateView } from 'platejs/react';

import { cn } from '@/lib/utils';

Expand Down Expand Up @@ -112,3 +112,18 @@ export const Editor = React.forwardRef<HTMLDivElement, EditorProps>(
);

Editor.displayName = 'Editor';

export function EditorView({
className,
variant,
...props
}: PlateViewProps & VariantProps<typeof editorVariants>) {
return (
<PlateView
{...props}
className={cn(editorVariants({ variant }), className)}
/>
);
}

EditorView.displayName = 'EditorView';
5 changes: 5 additions & 0 deletions docs/components/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ Since Plate UI is not a component library, a changelog is maintained here.

Use the [CLI](https://platejs.org/docs/components/cli) to install the latest version of the components.

## July 2025 #24

### July 2 #24.1
- `editor`: Added `EditorView` component using the new `PlateView` from `@platejs/core/react` for static editor rendering with copy functionality

## June 2025 #23

### June 29 #23.9
Expand Down
10 changes: 4 additions & 6 deletions packages/ai/src/lib/streaming/streamDeserializeMd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ export const streamDeserializeMd = (

let blocks = [];

blocks = editor
.getApi(MarkdownPlugin)
.markdown.deserialize(input, {
...options,
preserveEmptyParagraphs: false,
});
blocks = editor.getApi(MarkdownPlugin).markdown.deserialize(input, {
...options,
preserveEmptyParagraphs: false,
});

const trimmedData = getChunkTrimmed(data);

Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/internal/plugin/resolvePlugins.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { SlateEditor } from '../../lib/editor';

import { createSlatePlugin } from '../../lib/plugin';
import { DebugPlugin } from '../../lib/plugins';
import { createPlateEditor } from '../../react';
import { createSlatePlugin } from '../../lib/plugin/createSlatePlugin';
import { DebugPlugin } from '../../lib/plugins/debug/DebugPlugin';
import { createPlateEditor } from '../../react/editor/withPlate';
import { createPlatePlugin } from '../../react/plugin/createPlatePlugin';
import { getPlugin } from '../../react/plugin/getPlugin';
import { resolvePluginTest } from './resolveCreatePluginTest';
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/lib/static/editor/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* @file Automatically generated by barrelsby.
*/

export * from './withStatic';
Loading