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

Add `BlockPathMap` for incremental block indexing with depth-aware key-path lookups. This replaces the internal flat `blockIndexMap` rebuild with O(1) lookups and O(affected siblings) incremental updates on structural operations. Text edits have zero cost. The public `blockIndexMap` on `EditorSnapshot` is preserved for backward compatibility, derived from the new `blockPathMap`.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ node_modules
.vercel
.env*.local
.eslintcache
.bundle-stats/
4 changes: 4 additions & 0 deletions packages/editor/src/editor/create-slate-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {PortableTextBlock} from '@portabletext/schema'
import {InternalBlockPathMap} from '../internal-utils/block-path-map'
import {buildIndexMaps} from '../internal-utils/build-index-maps'
import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
import {debug} from '../internal-utils/debug'
Expand Down Expand Up @@ -35,6 +36,7 @@ export function createSlateEditor(config: SlateEditorConfig): SlateEditor {
editor.decoratedRanges = []
editor.decoratorState = {}
editor.blockIndexMap = new Map<string, number>()
editor.blockPathMap = new InternalBlockPathMap()
editor.history = {undos: [], redos: []}
editor.lastSelection = null
editor.lastSlateSelection = null
Expand Down Expand Up @@ -70,6 +72,8 @@ export function createSlateEditor(config: SlateEditorConfig): SlateEditor {
},
)

instance.blockPathMap.rebuild(instance.children as Array<PortableTextBlock>)

const slateEditor: SlateEditor = {
instance,
initialValue: [placeholderBlock],
Expand Down
2 changes: 2 additions & 0 deletions packages/editor/src/editor/editor-selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ export function getEditorSnapshot({

return {
blockIndexMap: slateEditorInstance.blockIndexMap,
blockPathMap: slateEditorInstance.blockPathMap,
context: {
containers: new Set<string>(),
converters: [...editorActorSnapshot.context.converters],
keyGenerator: editorActorSnapshot.context.keyGenerator,
readOnly: editorActorSnapshot.matches({'edit mode': 'read only'}),
Expand Down
7 changes: 7 additions & 0 deletions packages/editor/src/editor/editor-snapshot.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {PortableTextBlock} from '@portabletext/schema'
import type {Converter} from '../converters/converter.types'
import type {BlockPathMap} from '../internal-utils/block-path-map'
import {slateRangeToSelection} from '../internal-utils/slate-utils'
import type {EditorSelection} from '../types/editor'
import type {PortableTextSlateEditor} from '../types/slate-editor'
Expand All @@ -9,6 +10,7 @@ import type {EditorSchema} from './editor-schema'
* @public
*/
export type EditorContext = {
containers: Set<string>
converters: Array<Converter>
keyGenerator: () => string
readOnly: boolean
Expand All @@ -23,6 +25,7 @@ export type EditorContext = {
export type EditorSnapshot = {
context: EditorContext
blockIndexMap: Map<string, number>
blockPathMap: BlockPathMap
/**
* @beta
* Subject to change
Expand All @@ -31,12 +34,14 @@ export type EditorSnapshot = {
}

export function createEditorSnapshot({
containers,
converters,
editor,
keyGenerator,
readOnly,
schema,
}: {
containers?: Set<string>
converters: Array<Converter>
editor: PortableTextSlateEditor
keyGenerator: () => string
Expand All @@ -52,6 +57,7 @@ export function createEditorSnapshot({
: null

const context = {
containers: containers ?? new Set<string>(),
converters,
keyGenerator,
readOnly,
Expand All @@ -62,6 +68,7 @@ export function createEditorSnapshot({

return {
blockIndexMap: editor.blockIndexMap,
blockPathMap: editor.blockPathMap,
context,
decoratorState: editor.decoratorState,
} satisfies EditorSnapshot
Expand Down
1 change: 1 addition & 0 deletions packages/editor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export {
} from '@portabletext/schema'
export {useEditorSelector, type EditorSelector} from './editor/editor-selector'
export type {EditorContext, EditorSnapshot} from './editor/editor-snapshot'
export type {BlockPathMap} from './internal-utils/block-path-map'
export {usePortableTextEditor} from './editor/usePortableTextEditor'
export {usePortableTextEditorSelection} from './editor/usePortableTextEditorSelection'
export {defaultKeyGenerator as keyGenerator} from './utils/key-generator'
Expand Down
Loading
Loading