Skip to content

Implement split block API#141

Open
gohabereg wants to merge 5 commits into
mainfrom
feature/blocks-split
Open

Implement split block API#141
gohabereg wants to merge 5 commits into
mainfrom
feature/blocks-split

Conversation

@gohabereg
Copy link
Copy Markdown
Member

Split moved to the core and enhanced. Accessible via Blocks API

Now split follows this algorithm:

  1. If a Tool that is being split has canSplit: true option:
    a. Core would split the inputs in creation order and insert a new block of the same type with inputs after the split. That means if a Tool provides canSplit = true, it should be able to handle partial data
    b. If inputs are contained in the array, array indexes will be renumbered so new block receives array starting from 0

  2. If a Tool doesn't have canSplit or it equals false
    a. New block of the defaultTool type inserted
    b. Content from the inputs after the split is concatenated into a string. Fragments also concatenated into a one array with ranges shifted
    c. Default Tool must support import option in the conversionConfig
    d. Content is converted using the conversion config and inserted into the new block

@gohabereg gohabereg requested a review from Copilot May 9, 2026 21:45
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

Coverage report for ./packages/dom-adapters

St.
Category Percentage Covered / Total
🟢 Statements 96.43% 27/28
🟢 Branches 86.96% 20/23
🟢 Functions 100% 5/5
🟢 Lines 96.43% 27/28

Test suite run success

11 tests passing in 2 suites.

Report generated by 🧪jest coverage report action from 0d7d9d8

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

Coverage report for ./packages/core

St.
Category Percentage Covered / Total
🟢 Statements 100% 171/171
🟢 Branches
96.97% (-3.03% 🔻)
64/66
🟢 Functions 100% 33/33
🟢 Lines 100% 160/160
Show new covered files 🐣
St.
File Statements Branches Functions Lines
🟢 api/BlocksAPI.ts 100% 100% 100% 100%
Show files with reduced coverage 🔻
St.
File Statements Branches Functions Lines
🟢
... / BlockManager.ts
100%
94.44% (-5.56% 🔻)
100% 100%

Test suite run success

103 tests passing in 7 suites.

Report generated by 🧪jest coverage report action from 0d7d9d8

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

Coverage report for ./packages/model

St.
Category Percentage Covered / Total
🟢 Statements
99.71% (-0.08% 🔻)
1043/1046
🟢 Branches
98.43% (-0.55% 🔻)
314/319
🟢 Functions
98.39% (+0.94% 🔼)
244/248
🟢 Lines
99.7% (-0.09% 🔻)
1001/1004
Show new covered files 🐣
St.
File Statements Branches Functions Lines
🟢 utils/textUtils.ts 100% 100% 100% 100%
Show files with reduced coverage 🔻
St.
File Statements Branches Functions Lines
🟢
... / index.ts
99.4% (-0.6% 🔻)
95.83% (-4.17% 🔻)
97.37% (+5.7% 🔼)
99.38% (-0.62% 🔻)

Test suite run success

562 tests passing in 26 suites.

Report generated by 🧪jest coverage report action from 0d7d9d8

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

Coverage report for ./packages/ot-server

St.
Category Percentage Covered / Total
🟡 Statements 70.97% 22/31
🔴 Branches 20% 1/5
🟡 Functions 75% 6/8
🟡 Lines 68.97% 20/29

Test suite run success

4 tests passing in 1 suite.

Report generated by 🧪jest coverage report action from 0d7d9d8

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

⏭️ No files to mutate for ./packages/core

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

Coverage report for ./packages/collaboration-manager

St.
Category Percentage Covered / Total
🟢 Statements 92.51% 358/387
🟢 Branches 85.51% 118/138
🟢 Functions 98.15% 53/54
🟢 Lines 92.41% 353/382

Test suite run success

117 tests passing in 7 suites.

Report generated by 🧪jest coverage report action from 0d7d9d8

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

⏭️ No files to mutate for ./packages/dom-adapters

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

⏭️ No files to mutate for ./packages/model

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR moves block splitting into the core layer and exposes it via the SDK/Core Blocks API, supporting tool-specific splitting (canSplit) and fallback splitting into the default tool via conversionConfig.import. It also extends the document model with helpers for extracting/merging text+fragments and renumbering array-indexed keypaths to support split behavior.

Changes:

  • Add BlocksAPI.split() and implement split logic in BlocksManager.splitBlock() (core), with DOM adapter wiring to call the API.
  • Introduce model utilities for extracting all block text inputs, slicing fragments, merging text nodes, and renumbering dot-notation array keypaths.
  • Extend Block Tool options (conversionConfig, canSplit) and add importTextContent() helper for default-tool import during non-splittable splits.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/sdk/src/tools/facades/BlockToolFacade.ts Adds importTextContent() for building tool data from plain text via conversion config.
packages/sdk/src/entities/BlockTool.ts Extends Block tool options with conversionConfig and canSplit.
packages/sdk/src/api/BlocksAPI.ts Adds split() to the public SDK Blocks API interface.
packages/model/src/utils/textUtils.ts Adds fragment slicing and text-node merge helpers for split/convert flows.
packages/model/src/utils/textUtils.spec.ts Unit tests for sliceFragments() and mergeTextNodes().
packages/model/src/utils/keypath.ts Adds renumberKeys() to remap array-indexed dot keys starting at 0.
packages/model/src/utils/keypath.spec.ts Unit tests for renumberKeys().
packages/model/src/utils/index.ts Re-exports keypath namespace and textUtils from model utils.
packages/model/src/entities/EditorDocument/index.ts Exposes resolveBlockIndex() publicly and adds getBlockTextContent().
packages/model/src/entities/EditorDocument/EditorDocument.spec.ts Tests for resolveBlockIndex() and getBlockTextContent().
packages/model/src/entities/BlockNode/index.ts Adds getTextContent() to collect all TextNode inputs recursively.
packages/model/src/entities/BlockNode/BlockNode.spec.ts Tests for BlockNode.getTextContent().
packages/model/src/EditorJSModel.ts Exposes resolveBlockIndex() and getBlockTextContent() wrappers.
packages/model/src/EditorJSModel.spec.ts Updates API surface test to include new model methods.
packages/dom-adapters/src/tokens.ts Adds IoC token for injecting EditorAPI.
packages/dom-adapters/src/index.ts Binds EditorAPI into the adapter IoC container.
packages/dom-adapters/src/BlockToolAdapter/index.ts Replaces direct split implementation with api.blocks.split() call.
packages/core/src/tools/internal/block-tools/paragraph/index.ts Adds paragraph conversionConfig import/export mapping for default conversion.
packages/core/src/components/BlockManager.ts Implements splitBlock() in core using model text extraction, conversion import, and key renumbering.
packages/core/src/components/BlockManager.spec.ts Unit tests for BlocksManager.splitBlock() (mocked model/tools).
packages/core/src/api/BlocksAPI.ts Implements BlocksAPI.split() delegating into BlocksManager.splitBlock().
packages/core/src/api/BlocksAPI.integration.spec.ts Integration tests for split behavior across canSplit true/false and array-key scenarios.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +165 to +186
public importTextContent(value: string, fragments: InlineFragment[]): BlockToolData {
const conversionConfig = this.options[BlockToolOptionKey.ConversionConfig];
const importFnOrProp = conversionConfig?.import;

if (importFnOrProp === undefined) {
throw new Error(`Tool ${this.name} does not have import configuration for text content`);
}

/**
* @todo pass fragments to the import function?
*/
if (typeof importFnOrProp === 'function') {
return importFnOrProp(value);
}

return {
[importFnOrProp]: {
value,
fragments,
[NODE_TYPE_HIDDEN_PROP]: BlockChildType.Text as BlockChildType.Text,
},
};
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Fix it using keypath.set, add a tests covering that case

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 5b33dcc. importTextContent() now uses keypath.set to build the result, so a dot-notation import key like items.0.text produces the correct nested structure { items: [{ text: {...} }] } instead of a flat { 'items.0.text': {...} }. Added four new tests covering: simple string key, dot-notation key, function import, and missing config.

Comment thread packages/core/src/components/BlockManager.ts
Comment thread packages/core/src/components/BlockManager.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants