Skip to content
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
11 changes: 11 additions & 0 deletions .changeset/vendor-slate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@portabletext/editor': minor
---

feat: internalize Slate

The Slate framework (`slate`, `slate-dom`, and `slate-react`) is now vendored directly into the package source. This removes the external Slate dependencies entirely.

Why: Slate's public API constrains how we can evolve the editor's internal data model and operation handling. By owning the code, we can make targeted changes to normalization, node identity, and rendering without waiting for upstream changes or working around limitations.

This change comes with no public API changes and the editor's external behavior is unchanged as well.
10 changes: 9 additions & 1 deletion packages/editor/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import {globalIgnores} from 'eslint/config'
import tseslint from 'typescript-eslint'

export default tseslint.config([
globalIgnores(['coverage', 'dist', 'lib', '**/__tests__/**']),
globalIgnores([
'coverage',
'dist',
'lib',
'**/__tests__/**',
'src/slate/**',
'src/slate-dom/**',
'src/slate-react/**',
]),
reactHooks.configs.flat.recommended,
{
files: ['src/**/*.{cjs,mjs,js,jsx,ts,tsx}'],
Expand Down
8 changes: 7 additions & 1 deletion packages/editor/package.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export default defineConfig({
noImplicitSideEffects: 'error',
},
babel: {reactCompiler: true},
reactCompilerOptions: {target: '19'},
reactCompilerOptions: {
target: '19',
sources: (filename: string) =>
!filename.includes('/src/slate/') &&
!filename.includes('/src/slate-dom/') &&
!filename.includes('/src/slate-react/'),
},
dts: 'rolldown',
})
7 changes: 4 additions & 3 deletions packages/editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"test:watch": "vitest"
},
"dependencies": {
"@juggle/resize-observer": "^3.4.0",
"@portabletext/block-tools": "workspace:^",
"@portabletext/keyboard-shortcuts": "workspace:^",
"@portabletext/markdown": "workspace:^",
Expand All @@ -94,9 +95,8 @@
"@sanity/types": "^5.9.0",
"@xstate/react": "^6.0.0",
"debug": "^4.4.3",
"slate": "^0.120.0",
"slate-dom": "^0.119.0",
"slate-react": "^0.120.0",
"is-hotkey": "^0.2.0",
"scroll-into-view-if-needed": "^3.1.0",
"xstate": "^5.25.0"
},
"devDependencies": {
Expand All @@ -106,6 +106,7 @@
"@sanity/pkg-utils": "^10.2.1",
"@sanity/tsconfig": "^2.1.0",
"@types/debug": "^4.1.12",
"@types/is-hotkey": "^0.1.10",
"@types/node": "^20",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
Expand Down
14 changes: 7 additions & 7 deletions packages/editor/src/editor/Editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ import {
type KeyboardEvent,
type TextareaHTMLAttributes,
} from 'react'
import {Editor, Transforms, type Text} from 'slate'
import {debug} from '../internal-utils/debug'
import {getEventPosition} from '../internal-utils/event-position'
import {normalizeSelection} from '../internal-utils/selection'
import {slateRangeToSelection} from '../internal-utils/slate-utils'
import {toSlateRange} from '../internal-utils/to-slate-range'
import {Editor, Transforms, type Text} from '../slate'
import {
ReactEditor,
Editable as SlateEditable,
useSlate,
type RenderElementProps,
type RenderLeafProps,
} from 'slate-react'
import {debug} from '../internal-utils/debug'
import {getEventPosition} from '../internal-utils/event-position'
import {normalizeSelection} from '../internal-utils/selection'
import {slateRangeToSelection} from '../internal-utils/slate-utils'
import {toSlateRange} from '../internal-utils/to-slate-range'
} from '../slate-react'
import type {
EditorSelection,
OnCopyFn,
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/editor/create-editable-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {
type PortableTextChild,
type PortableTextObject,
} from '@portabletext/schema'
import {Editor, Range, Text, Transforms} from 'slate'
import {ReactEditor} from 'slate-react'
import {
isListItemActive,
isStyleActive,
Expand All @@ -18,6 +16,8 @@ import {getFocusBlock} from '../selectors/selector.get-focus-block'
import {getFocusSpan} from '../selectors/selector.get-focus-span'
import {getSelectedValue} from '../selectors/selector.get-selected-value'
import {isActiveAnnotation} from '../selectors/selector.is-active-annotation'
import {Editor, Range, Text, Transforms} from '../slate'
import {ReactEditor} from '../slate-react'
import type {
EditableAPI,
EditableAPIDeleteOptions,
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/editor/create-slate-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {createEditor, type Descendant} from 'slate'
import {withReact} from 'slate-react'
import {buildIndexMaps} from '../internal-utils/build-index-maps'
import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
import {debug} from '../internal-utils/debug'
import {createEditor, type Descendant} from '../slate'
import {plugins} from '../slate-plugins/slate-plugins'
import {withReact} from '../slate-react'
import type {PortableTextSlateEditor} from '../types/slate-editor'
import type {EditorActor} from './editor-machine'
import type {RelayActor} from './relay-machine'
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/editor/editor-dom.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Editor} from 'slate'
import {DOMEditor} from 'slate-dom'
import type {BehaviorEvent} from '../behaviors/behavior.types.event'
import {toSlateRange} from '../internal-utils/to-slate-range'
import {getSelectionEndBlock, getSelectionStartBlock} from '../selectors'
import {Editor} from '../slate'
import {DOMEditor} from '../slate-dom'
import type {PickFromUnion} from '../type-utils'
import type {PortableTextSlateEditor} from '../types/slate-editor'
import type {EditorSnapshot} from './editor-snapshot'
Expand Down
6 changes: 3 additions & 3 deletions packages/editor/src/editor/editor-machine.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type {Patch} from '@portabletext/patches'
import type {PortableTextBlock} from '@portabletext/schema'
import {Transforms} from 'slate'
import {EDITOR_TO_PENDING_SELECTION} from 'slate-dom'
import {ReactEditor} from 'slate-react'
import {
assertEvent,
assign,
Expand All @@ -22,6 +19,9 @@ import type {Converter} from '../converters/converter.types'
import {debug} from '../internal-utils/debug'
import type {EventPosition} from '../internal-utils/event-position'
import {sortByPriority} from '../priority/priority.sort'
import {Transforms} from '../slate'
import {EDITOR_TO_PENDING_SELECTION} from '../slate-dom'
import {ReactEditor} from '../slate-react'
import type {NamespaceEvent, OmitFromUnion} from '../type-utils'
import type {
EditorSelection,
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/editor/editor-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type React from 'react'
import {useEffect, useState} from 'react'
import {Slate} from 'slate-react'
import type {EditorConfig} from '../editor'
import {stopActor} from '../internal-utils/stop-actor'
import {Slate} from '../slate-react'
import {createInternalEditor} from './create-editor'
import {EditorActorContext} from './editor-actor-context'
import {EditorContext} from './editor-context'
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/editor/mutation-machine.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type {Patch} from '@portabletext/patches'
import type {PortableTextBlock} from '@portabletext/schema'
import {Editor} from 'slate'
import type {ActorRefFrom} from 'xstate'
import {
and,
Expand All @@ -15,6 +14,7 @@ import {
type AnyEventObject,
} from 'xstate'
import {debug} from '../internal-utils/debug'
import {Editor} from '../slate'
import type {PortableTextSlateEditor} from '../types/slate-editor'
import type {EditorSchema} from './editor-schema'
import type {PatchEvent} from './relay-machine'
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/editor/perform-hotkey.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {KeyboardEvent} from 'react'
import type {Editor} from 'slate'
import {isHotkey} from '../internal-utils/is-hotkey'
import type {Editor} from '../slate'
import type {HotkeyOptions} from '../types/options'
import type {EditorActor} from './editor-machine'
import type {PortableTextEditor} from './PortableTextEditor'
Expand Down
16 changes: 8 additions & 8 deletions packages/editor/src/editor/range-decorations-machine.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
import {
Element,
Path,
Range,
type BaseRange,
type NodeEntry,
type Operation,
} from 'slate'
import {
and,
assign,
Expand All @@ -18,6 +10,14 @@ import {isDeepEqual} from '../internal-utils/equality'
import {moveRangeByOperation} from '../internal-utils/move-range-by-operation'
import {slateRangeToSelection} from '../internal-utils/slate-utils'
import {toSlateRange} from '../internal-utils/to-slate-range'
import {
Element,
Path,
Range,
type BaseRange,
type NodeEntry,
type Operation,
} from '../slate'
import type {RangeDecoration} from '../types/editor'
import type {PortableTextSlateEditor} from '../types/slate-editor'
import {isEmptyTextBlock} from '../utils'
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/editor/render.block-object.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type {PortableTextObject} from '@portabletext/schema'
import {useContext, useRef, type ReactElement} from 'react'
import type {Element as SlateElement} from 'slate'
import type {RenderElementProps} from 'slate-react'
import type {DropPosition} from '../behaviors/behavior.core.drop-position'
import type {Element as SlateElement} from '../slate'
import type {RenderElementProps} from '../slate-react'
import type {
BlockRenderProps,
PortableTextMemberSchemaTypes,
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/editor/render.element.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {isTextBlock} from '@portabletext/schema'
import {useSelector} from '@xstate/react'
import {useContext, type ReactElement} from 'react'
import type {Element as SlateElement} from 'slate'
import {useSlateStatic, type RenderElementProps} from 'slate-react'
import type {DropPosition} from '../behaviors/behavior.core.drop-position'
import type {Element as SlateElement} from '../slate'
import {useSlateStatic, type RenderElementProps} from '../slate-react'
import type {
RenderBlockFunction,
RenderChildFunction,
Expand Down
6 changes: 3 additions & 3 deletions packages/editor/src/editor/render.inline-object.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {useContext, useRef, type ReactElement} from 'react'
import type {Element as SlateElement} from 'slate'
import {DOMEditor} from 'slate-dom'
import {useSlateStatic, type RenderElementProps} from 'slate-react'
import {getPointBlock} from '../internal-utils/slate-utils'
import type {Element as SlateElement} from '../slate'
import {DOMEditor} from '../slate-dom'
import {useSlateStatic, type RenderElementProps} from '../slate-react'
import type {
BlockChildRenderProps,
PortableTextMemberSchemaTypes,
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/editor/render.leaf.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useSelector} from '@xstate/react'
import {useContext, type CSSProperties} from 'react'
import type {Text} from 'slate'
import type {RenderLeafProps} from 'slate-react'
import type {Text} from '../slate'
import type {RenderLeafProps} from '../slate-react'
import type {
RangeDecoration,
RenderAnnotationFunction,
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/editor/render.span.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useSelector} from '@xstate/react'
import {useContext, useMemo, useRef, type ReactElement} from 'react'
import {useSlateStatic, type RenderLeafProps} from 'slate-react'
import {useSlateStatic, type RenderLeafProps} from '../slate-react'
import type {
BlockAnnotationRenderProps,
BlockChildRenderProps,
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/editor/render.text-block.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type {PortableTextTextBlock} from '@portabletext/schema'
import {useContext, useRef, type ReactElement} from 'react'
import type {Element as SlateElement} from 'slate'
import {useSlateSelector, type RenderElementProps} from 'slate-react'
import type {DropPosition} from '../behaviors/behavior.core.drop-position'
import type {Element as SlateElement} from '../slate'
import {useSlateSelector, type RenderElementProps} from '../slate-react'
import type {
BlockListItemRenderProps,
BlockRenderProps,
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/editor/render.text.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {Editable} from 'slate-react'
import type {Editable} from '../slate-react'

export type RenderTextProps = Parameters<
NonNullable<React.ComponentProps<typeof Editable>['renderText']>
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/editor/selection-state-context.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {useSelector} from '@xstate/react'
import {createContext, useContext} from 'react'
import {useSlateStatic} from 'slate-react'
import {getFocusChild} from '../selectors'
import {getSelectedChildren} from '../selectors/selector.get-selected-children'
import {getSelectionEndPoint} from '../selectors/selector.get-selection-end-point'
import {getSelectionStartPoint} from '../selectors/selector.get-selection-start-point'
import {isSelectionCollapsed} from '../selectors/selector.is-selection-collapsed'
import {useSlateStatic} from '../slate-react'
import {getBlockKeyFromSelectionPoint} from '../utils/util.selection-point'
import {serializePath} from '../utils/util.serialize-path'
import {EditorActorContext} from './editor-actor-context'
Expand Down
16 changes: 8 additions & 8 deletions packages/editor/src/editor/sync-machine.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import type {Patch} from '@portabletext/patches'
import {isSpan, type PortableTextBlock} from '@portabletext/schema'
import {
deleteText,
Editor,
Text,
Transforms,
type Descendant,
type Node,
} from 'slate'
import type {ActorRefFrom} from 'xstate'
import {
and,
Expand All @@ -29,6 +21,14 @@ import {
} from '../internal-utils/equality'
import {validateValue} from '../internal-utils/validateValue'
import {toSlateBlock, VOID_CHILD_KEY} from '../internal-utils/values'
import {
deleteText,
Editor,
Text,
Transforms,
type Descendant,
type Node,
} from '../slate'
import {withRemoteChanges} from '../slate-plugins/slate-plugin.remote-changes'
import {pluginWithoutHistory} from '../slate-plugins/slate-plugin.without-history'
import {withoutPatching} from '../slate-plugins/slate-plugin.without-patching'
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/editor/undo-step.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Path, type Operation} from 'slate'
import {Path, type Operation} from '../slate'
import type {PortableTextSlateEditor} from '../types/slate-editor'

type UndoStep = {
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/editor/validate-selection-machine.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Editor, Transforms} from 'slate'
import {ReactEditor} from 'slate-react'
import {setup} from 'xstate'
import {debug} from '../internal-utils/debug'
import {Editor, Transforms} from '../slate'
import {ReactEditor} from '../slate-react'
import type {PortableTextSlateEditor} from '../types/slate-editor'

const validateSelectionSetup = setup({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {InsertTextOperation, Range} from 'slate'
import {describe, expect, it} from 'vitest'
import type {InsertTextOperation, Range} from '../../slate'
import {moveRangeByOperation} from '../move-range-by-operation'

describe('moveRangeByOperation', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {PortableTextBlock} from '@portabletext/schema'
import {Element, Path, type Node, type Operation} from 'slate'
import type {EditorSchema} from '../editor/editor-schema'
import type {EditorContext} from '../editor/editor-snapshot'
import {Element, Path, type Node, type Operation} from '../slate'
import type {OmitFromUnion} from '../type-utils'
import {
getBlock,
Expand Down
9 changes: 8 additions & 1 deletion packages/editor/src/internal-utils/applyPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@ import {
makeDiff,
parsePatch,
} from '@sanity/diff-match-patch'
import {Editor, Element, Node, Text, Transforms, type Descendant} from 'slate'
import type {EditorContext} from '../editor/editor-snapshot'
import {
Editor,
Element,
Node,
Text,
Transforms,
type Descendant,
} from '../slate'
import type {Path} from '../types/paths'
import type {PortableTextSlateEditor} from '../types/slate-editor'
import {isKeyedSegment} from '../utils/util.is-keyed-segment'
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/internal-utils/equality.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type PortableTextObject,
type Schema,
} from '@portabletext/schema'
import type {Descendant} from 'slate'
import type {Descendant} from '../slate'

export function isEqualValues(
context: {schema: Schema},
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/internal-utils/event-position.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Editor, type BaseRange, type Node} from 'slate'
import {DOMEditor, isDOMNode} from 'slate-dom'
import type {EditorActor} from '../editor/editor-machine'
import type {EditorSchema} from '../editor/editor-schema'
import {Editor, type BaseRange, type Node} from '../slate'
import {DOMEditor, isDOMNode} from '../slate-dom'
import type {EditorSelection} from '../types/editor'
import type {PortableTextSlateEditor} from '../types/slate-editor'
import {getBlockEndPoint} from '../utils/util.get-block-end-point'
Expand Down
Loading