diff --git a/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-first-block.unit.spec.ts b/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-first-block.unit.spec.ts
new file mode 100644
index 0000000000..45abde1ed3
--- /dev/null
+++ b/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-first-block.unit.spec.ts
@@ -0,0 +1,230 @@
+/**
+ * @vitest-environment happy-dom
+ */
+import { describe, expect, it } from 'vitest';
+
+import { getFirstBlockCommand } from '../../../commands/block-crud/get-first-content-block';
+import { affine } from '../../helpers/affine-template';
+
+describe('commands/block-crud', () => {
+ describe('getFirstBlockCommand', () => {
+ it('should return null when root is not exists', () => {
+ const host = affine``;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ role: 'content',
+ root: undefined,
+ });
+
+ expect(firstBlock).toBeNull();
+ });
+
+ it('should return first block with content role when found', () => {
+ const host = affine`
+
+
+ First Paragraph
+ Second Paragraph
+
+
+ First Paragraph
+ Second Paragraph
+
+
+ `;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ role: 'hub',
+ root: undefined,
+ });
+
+ expect(firstBlock?.id).toBe('note-1');
+ });
+
+ it('should return first block with any role in the array when found', () => {
+ const host = affine`
+
+
+ First Paragraph
+ Second Paragraph
+
+
+ First Paragraph
+ Second Paragraph
+
+
+ `;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ role: ['hub', 'content'],
+ root: undefined,
+ });
+
+ expect(firstBlock?.id).toBe('note-1');
+ });
+
+ it('should return first block with specified flavour when found', () => {
+ const host = affine`
+
+
+ Paragraph
+ List Item
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ flavour: 'affine:list',
+ root: note,
+ });
+
+ expect(firstBlock?.id).toBe('list-1');
+ });
+
+ it('should return first block with any flavour in the array when found', () => {
+ const host = affine`
+
+
+ Paragraph
+ List Item
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ flavour: ['affine:list', 'affine:code'],
+ root: note,
+ });
+
+ expect(firstBlock?.id).toBe('list-1');
+ });
+
+ it('should return first block matching both role and flavour when both specified', () => {
+ const host = affine`
+
+
+ Content Paragraph
+ Content List
+ hub Paragraph
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ role: 'content',
+ flavour: 'affine:list',
+ root: note,
+ });
+
+ expect(firstBlock?.id).toBe('list-1');
+ });
+
+ it('should return first block with default roles when role not specified', () => {
+ const host = affine`
+
+
+ hub Paragraph
+ Content Paragraph
+ Hub Paragraph
+
+
+ `;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ root: undefined,
+ });
+
+ expect(firstBlock?.id).toBe('note-1');
+ });
+
+ it('should return first block with specified role when found', () => {
+ const host = affine`
+
+
+ Content Paragraph
+ hub Paragraph
+ Database
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ role: 'hub',
+ root: note,
+ });
+
+ expect(firstBlock?.id).toBe('database-1');
+ });
+
+ it('should return null when no blocks with specified role are found in children', () => {
+ const host = affine`
+
+
+ Content Paragraph
+ Another Content Paragraph
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ role: 'hub',
+ root: note,
+ });
+
+ expect(firstBlock).toBeNull();
+ });
+
+ it('should return null when no blocks with specified flavour are found in children', () => {
+ const host = affine`
+
+
+ Paragraph
+ Another Paragraph
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ flavour: 'affine:list',
+ root: note,
+ });
+
+ expect(firstBlock).toBeNull();
+ });
+
+ it('should return first block with specified role within specified root subtree', () => {
+ const host = affine`
+
+
+ 1-1 Content
+ 1-2 hub
+
+
+ 2-1 hub
+ 2-2 Content
+
+
+ `;
+
+ const note = host.doc.getBlock('note-2')?.model;
+
+ const [_, { firstBlock }] = host.command.exec(getFirstBlockCommand, {
+ role: 'content',
+ root: note,
+ });
+
+ expect(firstBlock?.id).toBe('paragraph-2-1');
+ });
+ });
+});
diff --git a/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-first-content-block.unit.spec.ts b/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-first-content-block.unit.spec.ts
deleted file mode 100644
index 34652502b0..0000000000
--- a/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-first-content-block.unit.spec.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * @vitest-environment happy-dom
- */
-import { describe, expect, it } from 'vitest';
-
-import { getFirstContentBlockCommand } from '../../../commands/block-crud/get-first-content-block';
-import { affine } from '../../helpers/affine-template';
-
-describe('commands/block-crud', () => {
- describe('getFirstContentBlockCommand', () => {
- it('should return null when root is not provided and no note block exists', () => {
- const host = affine``;
-
- const [_, { firstBlock }] = host.command.exec(
- getFirstContentBlockCommand,
- {
- root: undefined,
- std: {
- host,
- } as any,
- }
- );
-
- expect(firstBlock).toBeNull();
- });
-
- it('should return first content block when found', () => {
- const host = affine`
-
-
- First Paragraph
- Second Paragraph
-
-
- `;
-
- const [_, { firstBlock }] = host.command.exec(
- getFirstContentBlockCommand,
- {
- root: undefined,
- }
- );
-
- expect(firstBlock?.id).toBe('paragraph-1');
- });
-
- it('should return null when no content blocks are found in children', () => {
- const host = affine`
-
-
-
-
- `;
-
- const [_, { firstBlock }] = host.command.exec(
- getFirstContentBlockCommand,
- {}
- );
-
- expect(firstBlock).toBeNull();
- });
-
- it('should return first content block within specified root subtree', () => {
- const host = affine`
-
-
- 1-1 Paragraph
- 1-2 Paragraph
-
-
- 2-1 Paragraph
- 2-2 Paragraph
-
-
- `;
-
- const noteBlock = host.doc.getBlock('note-2')?.model;
-
- const [_, { firstBlock }] = host.command.exec(
- getFirstContentBlockCommand,
- {
- root: noteBlock,
- }
- );
-
- expect(firstBlock?.id).toBe('paragraph-2-1');
- });
- });
-});
diff --git a/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-last-block.unit.spec.ts b/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-last-block.unit.spec.ts
new file mode 100644
index 0000000000..702d7a079e
--- /dev/null
+++ b/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-last-block.unit.spec.ts
@@ -0,0 +1,230 @@
+/**
+ * @vitest-environment happy-dom
+ */
+import { describe, expect, it } from 'vitest';
+
+import { getLastBlockCommand } from '../../../commands/block-crud/get-last-content-block';
+import { affine } from '../../helpers/affine-template';
+
+describe('commands/block-crud', () => {
+ describe('getLastBlockCommand', () => {
+ it('should return null when root is not exists', () => {
+ const host = affine``;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ role: 'content',
+ root: undefined,
+ });
+
+ expect(lastBlock).toBeNull();
+ });
+
+ it('should return last block with content role when found', () => {
+ const host = affine`
+
+
+ First Paragraph
+ Second Paragraph
+
+
+ First Paragraph
+ Second Paragraph
+
+
+ `;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ role: 'hub',
+ root: undefined,
+ });
+
+ expect(lastBlock?.id).toBe('note-2');
+ });
+
+ it('should return last block with any role in the array when found', () => {
+ const host = affine`
+
+
+ First Paragraph
+ Second Paragraph
+
+
+ First Paragraph
+ Second Paragraph
+
+
+ `;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ role: ['hub', 'content'],
+ root: undefined,
+ });
+
+ expect(lastBlock?.id).toBe('note-2');
+ });
+
+ it('should return last block with specified flavour when found', () => {
+ const host = affine`
+
+
+ Paragraph
+ List Item
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ flavour: 'affine:list',
+ root: note,
+ });
+
+ expect(lastBlock?.id).toBe('list-1');
+ });
+
+ it('should return last block with any flavour in the array when found', () => {
+ const host = affine`
+
+
+ Paragraph
+ List Item
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ flavour: ['affine:list', 'affine:code'],
+ root: note,
+ });
+
+ expect(lastBlock?.id).toBe('list-1');
+ });
+
+ it('should return last block matching both role and flavour when both specified', () => {
+ const host = affine`
+
+
+ Content Paragraph
+ Content List
+ hub Paragraph
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ role: 'content',
+ flavour: 'affine:list',
+ root: note,
+ });
+
+ expect(lastBlock?.id).toBe('list-1');
+ });
+
+ it('should return last block with default roles when role not specified', () => {
+ const host = affine`
+
+
+ hub Paragraph
+ Content Paragraph
+ Hub Paragraph
+
+
+ `;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ root: undefined,
+ });
+
+ expect(lastBlock?.id).toBe('note-1');
+ });
+
+ it('should return last block with specified role when found', () => {
+ const host = affine`
+
+
+ Content Paragraph
+ hub Paragraph
+ Database
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ role: 'hub',
+ root: note,
+ });
+
+ expect(lastBlock?.id).toBe('database-1');
+ });
+
+ it('should return null when no blocks with specified role are found in children', () => {
+ const host = affine`
+
+
+ Content Paragraph
+ Another Content Paragraph
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ role: 'hub',
+ root: note,
+ });
+
+ expect(lastBlock).toBeNull();
+ });
+
+ it('should return null when no blocks with specified flavour are found in children', () => {
+ const host = affine`
+
+
+ Paragraph
+ Another Paragraph
+
+
+ `;
+
+ const note = host.doc.getBlock('note-1')?.model;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ flavour: 'affine:list',
+ root: note,
+ });
+
+ expect(lastBlock).toBeNull();
+ });
+
+ it('should return last block with specified role within specified root subtree', () => {
+ const host = affine`
+
+
+ 1-1 Content
+ 1-2 hub
+
+
+ 2-1 hub
+ 2-2 Content
+
+
+ `;
+
+ const note = host.doc.getBlock('note-2')?.model;
+
+ const [_, { lastBlock }] = host.command.exec(getLastBlockCommand, {
+ role: 'content',
+ root: note,
+ });
+
+ expect(lastBlock?.id).toBe('paragraph-2-2');
+ });
+ });
+});
diff --git a/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-last-content-block.unit.spec.ts b/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-last-content-block.unit.spec.ts
deleted file mode 100644
index 8b1487b8c2..0000000000
--- a/blocksuite/affine/shared/src/__tests__/commands/block-crud/get-last-content-block.unit.spec.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * @vitest-environment happy-dom
- */
-import { describe, expect, it } from 'vitest';
-
-import { getLastContentBlockCommand } from '../../../commands/block-crud/get-last-content-block';
-import { affine } from '../../helpers/affine-template';
-
-describe('commands/block-crud', () => {
- describe('getLastContentBlockCommand', () => {
- it('should return null when root is not provided and no note block exists', () => {
- const host = affine``;
-
- const [_, { lastBlock }] = host.command.exec(getLastContentBlockCommand, {
- root: undefined,
- std: {
- host,
- } as any,
- });
-
- expect(lastBlock).toBeNull();
- });
-
- it('should return last content block when found', () => {
- const host = affine`
-
-
- First Paragraph
- Second Paragraph
-
-
- `;
-
- const [_, { lastBlock }] = host.command.exec(getLastContentBlockCommand, {
- root: undefined,
- });
-
- expect(lastBlock?.id).toBe('paragraph-2');
- });
-
- it('should return null when no content blocks are found in children', () => {
- const host = affine`
-
-
-
-
- `;
-
- const [_, { lastBlock }] = host.command.exec(
- getLastContentBlockCommand,
- {}
- );
-
- expect(lastBlock).toBeNull();
- });
-
- it('should return last content block within specified root subtree', () => {
- const host = affine`
-
-
- 1-1 Paragraph
- 1-2 Paragraph
-
-
- 2-1 Paragraph
- 2-2 Paragraph
-
-
- `;
-
- const noteBlock = host.doc.getBlock('note-2')?.model;
-
- const [_, { lastBlock }] = host.command.exec(getLastContentBlockCommand, {
- root: noteBlock,
- });
-
- expect(lastBlock?.id).toBe('paragraph-2-2');
- });
- });
-});
diff --git a/blocksuite/affine/shared/src/__tests__/helpers/affine-template.ts b/blocksuite/affine/shared/src/__tests__/helpers/affine-template.ts
index 93615dd4fe..eca3305cb0 100644
--- a/blocksuite/affine/shared/src/__tests__/helpers/affine-template.ts
+++ b/blocksuite/affine/shared/src/__tests__/helpers/affine-template.ts
@@ -1,4 +1,5 @@
import {
+ DatabaseBlockSchemaExtension,
ImageBlockSchemaExtension,
ListBlockSchemaExtension,
NoteBlockSchemaExtension,
@@ -18,6 +19,7 @@ const extensions = [
ParagraphBlockSchemaExtension,
ListBlockSchemaExtension,
ImageBlockSchemaExtension,
+ DatabaseBlockSchemaExtension,
];
// Mapping from tag names to flavours
@@ -27,6 +29,7 @@ const tagToFlavour: Record = {
'affine-paragraph': 'affine:paragraph',
'affine-list': 'affine:list',
'affine-image': 'affine:image',
+ 'affine-database': 'affine:database',
};
/**
diff --git a/blocksuite/affine/shared/src/__tests__/helpers/create-test-doc.ts b/blocksuite/affine/shared/src/__tests__/helpers/create-test-doc.ts
deleted file mode 100644
index a5c5a9c4cc..0000000000
--- a/blocksuite/affine/shared/src/__tests__/helpers/create-test-doc.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import {
- BlockSchemaExtension,
- defineBlockSchema,
- type Store,
- Text,
-} from '@blocksuite/store';
-import { TestWorkspace } from '@blocksuite/store/test';
-import { type Element as HappyDOMElement, Window } from 'happy-dom';
-
-// Define schema
-const PageBlockSchema = defineBlockSchema({
- flavour: 'affine:page',
- props: () => ({}),
- metadata: {
- version: 1,
- role: 'root',
- children: ['affine:note'],
- },
-});
-
-const NoteBlockSchema = defineBlockSchema({
- flavour: 'affine:note',
- props: () => ({}),
- metadata: {
- version: 1,
- role: 'hub',
- parent: ['affine:page'],
- children: ['affine:paragraph'],
- },
-});
-
-const ParagraphBlockSchema = defineBlockSchema({
- flavour: 'affine:paragraph',
- props: internal => ({
- text: internal.Text(),
- }),
- metadata: {
- version: 1,
- role: 'content',
- parent: ['affine:note'],
- },
-});
-
-// Create schema extensions
-const PageBlockSchemaExtension = BlockSchemaExtension(PageBlockSchema);
-const NoteBlockSchemaExtension = BlockSchemaExtension(NoteBlockSchema);
-const ParagraphBlockSchemaExtension =
- BlockSchemaExtension(ParagraphBlockSchema);
-
-// Extensions array
-const extensions = [
- PageBlockSchemaExtension,
- NoteBlockSchemaExtension,
- ParagraphBlockSchemaExtension,
-];
-
-/**
- * Parse HTML string and create document block structure
- * @param node Current DOM node
- * @param doc Document object
- * @param parentId Parent block ID
- * @returns Created block ID
- */
-function processNode(
- node: HappyDOMElement,
- doc: Store,
- parentId?: string
-): string | undefined {
- // Skip text nodes and comments
- if (node.nodeType !== 1) {
- return undefined;
- }
-
- const tagName = node.tagName.toLowerCase();
- let blockId: string | undefined = undefined;
-
- // Create appropriate block based on tag name
- if (tagName === 'affine-page') {
- blockId = doc.addBlock('affine:page', {}, parentId);
- } else if (tagName === 'affine-note') {
- blockId = doc.addBlock('affine:note', {}, parentId);
- } else if (tagName === 'affine-paragraph') {
- // Get paragraph text content
- const textContent = node.textContent || '';
- // Get attributes
- const props: Record = { text: new Text(textContent) };
-
- // Process custom attributes
- for (const attr of Array.from(node.attributes)) {
- if (attr.name === 'type') {
- props.type = attr.value;
- } else if (attr.name === 'checked' && attr.value === 'true') {
- props.checked = true;
- }
- }
-
- blockId = doc.addBlock('affine:paragraph', props, parentId);
- } else {
- console.warn(`Unknown tag name: ${tagName}`);
- return undefined;
- }
-
- // Process child nodes
- for (const childNode of Array.from(node.children) as HappyDOMElement[]) {
- processNode(childNode, doc, blockId);
- }
-
- return blockId;
-}
-
-/**
- * Create document from HTML string
- * @param template HTML template string
- * @returns Created document object
- */
-export function createDocFromHTML(template: string) {
- const workspace = new TestWorkspace({});
- workspace.meta.initialize();
-
- const doc = workspace.createDoc({ id: 'test-doc', extensions });
-
- doc.load(() => {
- const window = new Window();
- const document = window.document;
- const container = document.createElement('div');
- container.innerHTML = template;
-
- // Process each child node of the root
- for (const childNode of Array.from(container.children)) {
- processNode(childNode, doc);
- }
- });
-
- return doc;
-}
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
index 4cb139831d..4d586f0c41 100644
--- 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
@@ -1,35 +1,57 @@
import type { Command } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store';
-import { getFirstNoteBlock } from '../../utils';
+type Role = 'content' | 'hub';
/**
- * Get the first content block in the document
+ * Get the first block with specified roles and flavours in the document
*
* @param ctx - Command context
* @param ctx.root - The root note block model
+ * @param ctx.role - The roles to match, can be string or string array. If not provided, default to all supported roles.
+ * @param ctx.flavour - The flavours to match, can be string or string array. If not provided, match any flavour.
* @param next - Next handler function
- * @returns The first content block model or null
+ * @returns The first block model matched or null
*/
-export const getFirstContentBlockCommand: Command<
+export const getFirstBlockCommand: Command<
{
root?: BlockModel;
+ role?: Role | Role[];
+ flavour?: string | string[];
},
{
firstBlock: BlockModel | null;
}
> = (ctx, next) => {
- const doc = ctx.std.host.doc;
- const noteBlock = ctx.root ?? getFirstNoteBlock(doc);
- if (!noteBlock) {
+ const root = ctx.root || ctx.std.host.doc.root;
+ if (!root) {
next({
firstBlock: null,
});
return;
}
- for (const child of noteBlock.children) {
- if (child.role === 'content') {
+ const defaultRoles = ['content', 'hub'];
+
+ const rolesToMatch = ctx.role
+ ? Array.isArray(ctx.role)
+ ? ctx.role
+ : [ctx.role]
+ : defaultRoles;
+
+ const flavoursToMatch = ctx.flavour
+ ? Array.isArray(ctx.flavour)
+ ? ctx.flavour
+ : [ctx.flavour]
+ : null;
+
+ for (const child of root.children) {
+ const roleMatches = rolesToMatch.includes(child.role);
+
+ const flavourMatches =
+ !flavoursToMatch || flavoursToMatch.includes(child.flavour);
+
+ if (roleMatches && flavourMatches) {
next({
firstBlock: child,
});
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
index 9612a6201a..ae6eb89a64 100644
--- 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
@@ -1,35 +1,59 @@
import type { Command } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store';
-import { getLastNoteBlock } from '../../utils';
+type Role = 'content' | 'hub';
/**
- * Get the last content block in the document
+ * Get the last block with specified roles and flavours in the document
*
* @param ctx - Command context
* @param ctx.root - The root note block model
+ * @param ctx.role - The roles to match, can be string or string array. If not provided, default to all supported roles.
+ * @param ctx.flavour - The flavours to match, can be string or string array. If not provided, match any flavour.
* @param next - Next handler function
- * @returns The last content block model or null
+ * @returns The last block model matched or null
*/
-export const getLastContentBlockCommand: Command<
+export const getLastBlockCommand: Command<
{
root?: BlockModel;
+ role?: Role | Role[];
+ flavour?: string | string[];
},
{
lastBlock: BlockModel | null;
}
> = (ctx, next) => {
- const noteBlock = ctx.root ?? getLastNoteBlock(ctx.std.host.doc);
- if (!noteBlock) {
+ const root = ctx.root || ctx.std.host.doc.root;
+ if (!root) {
next({
lastBlock: null,
});
return;
}
- const children = noteBlock.children;
+ const defaultRoles = ['content', 'hub'];
+
+ const rolesToMatch = ctx.role
+ ? Array.isArray(ctx.role)
+ ? ctx.role
+ : [ctx.role]
+ : defaultRoles;
+
+ const flavoursToMatch = ctx.flavour
+ ? Array.isArray(ctx.flavour)
+ ? ctx.flavour
+ : [ctx.flavour]
+ : null;
+
+ const children = root.children;
for (let i = children.length - 1; i >= 0; i--) {
- if (children[i].role === 'content') {
+ const roleMatches = rolesToMatch.includes(children[i].role);
+
+ const flavourMatches =
+ !flavoursToMatch || flavoursToMatch.includes(children[i].flavour);
+
+ // Both role and flavour must match
+ if (roleMatches && flavourMatches) {
next({
lastBlock: children[i],
});
diff --git a/blocksuite/affine/shared/src/commands/block-crud/index.ts b/blocksuite/affine/shared/src/commands/block-crud/index.ts
index 8e1d26d0f2..10177ef2d5 100644
--- a/blocksuite/affine/shared/src/commands/block-crud/index.ts
+++ b/blocksuite/affine/shared/src/commands/block-crud/index.ts
@@ -1,6 +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 { getFirstBlockCommand } from './get-first-content-block.js';
+export { getLastBlockCommand } 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/commands/index.ts b/blocksuite/affine/shared/src/commands/index.ts
index b2d8eb6f5d..00cd284aba 100644
--- a/blocksuite/affine/shared/src/commands/index.ts
+++ b/blocksuite/affine/shared/src/commands/index.ts
@@ -1,7 +1,7 @@
export {
getBlockIndexCommand,
- getFirstContentBlockCommand,
- getLastContentBlockCommand,
+ getFirstBlockCommand,
+ getLastBlockCommand,
getNextBlockCommand,
getPrevBlockCommand,
getSelectedBlocksCommand,