From aa15b106d93c68c0af53a4f0bd002847324175d9 Mon Sep 17 00:00:00 2001 From: yoyoyohamapi <8338436+yoyoyohamapi@users.noreply.github.com> Date: Fri, 14 Mar 2025 02:35:20 +0000 Subject: [PATCH] feat(editor): content block getter command (#10720) ### TL;DR Added new commands to retrieve the first and last content blocks in a document. ### What changed? - Created `getFirstContentBlockCommand` to find the first content block in a document - Created `getLastContentBlockCommand` to find the last content block in a document - Added `getFirstNoteBlock` utility function to find the first note block in a document --- .../block-crud/get-first-content-block.ts | 43 +++++++++++++++++++ .../block-crud/get-last-content-block.ts | 43 +++++++++++++++++++ .../shared/src/commands/block-crud/index.ts | 2 + .../affine/shared/src/utils/model/getter.ts | 16 +++++++ 4 files changed, 104 insertions(+) create mode 100644 blocksuite/affine/shared/src/commands/block-crud/get-first-content-block.ts create mode 100644 blocksuite/affine/shared/src/commands/block-crud/get-last-content-block.ts diff --git a/blocksuite/affine/shared/src/commands/block-crud/get-first-content-block.ts b/blocksuite/affine/shared/src/commands/block-crud/get-first-content-block.ts new file mode 100644 index 0000000000..4cb139831d --- /dev/null +++ b/blocksuite/affine/shared/src/commands/block-crud/get-first-content-block.ts @@ -0,0 +1,43 @@ +import type { Command } from '@blocksuite/block-std'; +import type { BlockModel } from '@blocksuite/store'; + +import { getFirstNoteBlock } from '../../utils'; + +/** + * Get the first content block in the document + * + * @param ctx - Command context + * @param ctx.root - The root note block model + * @param next - Next handler function + * @returns The first content block model or null + */ +export const getFirstContentBlockCommand: Command< + { + root?: BlockModel; + }, + { + firstBlock: BlockModel | null; + } +> = (ctx, next) => { + const doc = ctx.std.host.doc; + const noteBlock = ctx.root ?? getFirstNoteBlock(doc); + if (!noteBlock) { + next({ + firstBlock: null, + }); + return; + } + + for (const child of noteBlock.children) { + if (child.role === 'content') { + next({ + firstBlock: child, + }); + return; + } + } + + next({ + firstBlock: null, + }); +}; diff --git a/blocksuite/affine/shared/src/commands/block-crud/get-last-content-block.ts b/blocksuite/affine/shared/src/commands/block-crud/get-last-content-block.ts new file mode 100644 index 0000000000..9612a6201a --- /dev/null +++ b/blocksuite/affine/shared/src/commands/block-crud/get-last-content-block.ts @@ -0,0 +1,43 @@ +import type { Command } from '@blocksuite/block-std'; +import type { BlockModel } from '@blocksuite/store'; + +import { getLastNoteBlock } from '../../utils'; + +/** + * Get the last content block in the document + * + * @param ctx - Command context + * @param ctx.root - The root note block model + * @param next - Next handler function + * @returns The last content block model or null + */ +export const getLastContentBlockCommand: Command< + { + root?: BlockModel; + }, + { + lastBlock: BlockModel | null; + } +> = (ctx, next) => { + const noteBlock = ctx.root ?? getLastNoteBlock(ctx.std.host.doc); + if (!noteBlock) { + next({ + lastBlock: null, + }); + return; + } + + const children = noteBlock.children; + for (let i = children.length - 1; i >= 0; i--) { + if (children[i].role === 'content') { + next({ + lastBlock: children[i], + }); + return; + } + } + + next({ + lastBlock: null, + }); +}; diff --git a/blocksuite/affine/shared/src/commands/block-crud/index.ts b/blocksuite/affine/shared/src/commands/block-crud/index.ts index 73c7b2a5cd..8e1d26d0f2 100644 --- a/blocksuite/affine/shared/src/commands/block-crud/index.ts +++ b/blocksuite/affine/shared/src/commands/block-crud/index.ts @@ -1,4 +1,6 @@ export { getBlockIndexCommand } from './get-block-index.js'; +export { getFirstContentBlockCommand } from './get-first-content-block.js'; +export { getLastContentBlockCommand } from './get-last-content-block.js'; export { getNextBlockCommand } from './get-next-block.js'; export { getPrevBlockCommand } from './get-prev-block.js'; export { getSelectedBlocksCommand } from './get-selected-blocks.js'; diff --git a/blocksuite/affine/shared/src/utils/model/getter.ts b/blocksuite/affine/shared/src/utils/model/getter.ts index 0900a3eec9..21bb33257d 100644 --- a/blocksuite/affine/shared/src/utils/model/getter.ts +++ b/blocksuite/affine/shared/src/utils/model/getter.ts @@ -57,3 +57,19 @@ export function getLastNoteBlock(doc: Store) { } return note; } + +export function getFirstNoteBlock(doc: Store) { + let note: NoteBlockModel | null = null; + if (!doc.root) return null; + const { children } = doc.root; + for (const child of children) { + if ( + matchModels(child, [NoteBlockModel]) && + child.displayMode !== NoteDisplayMode.EdgelessOnly + ) { + note = child as NoteBlockModel; + break; + } + } + return note; +}