From 652865c7cf3c2979ed4802edcf7abd38c531e746 Mon Sep 17 00:00:00 2001 From: Saul-Mirone Date: Tue, 11 Feb 2025 08:18:57 +0000 Subject: [PATCH] refactor(editor): remove global types in model (#10082) Closes: [BS-2249](https://linear.app/affine-design/issue/BS-2249/remove-global-types-in-model) ```ts // before matchFlavours(model, ['affine:page']); // after matchFlavours(model, [PageBlockModel]); ``` --- .../src/attachment-service.ts | 3 +- .../src/detail-panel/note-renderer.ts | 15 +++++--- .../src/edgeless-text-block.ts | 10 +++-- .../src/common/render-linked-doc.ts | 17 +++++---- .../embed-linked-doc-block.ts | 3 +- .../affine/block-image/src/image-service.ts | 3 +- .../block-list/src/commands/dedent-list.ts | 3 +- .../block-list/src/commands/indent-list.ts | 5 ++- .../src/commands/list-to-paragraph.ts | 3 +- .../block-list/src/commands/split-list.ts | 3 +- .../affine/block-list/src/commands/utils.ts | 6 +-- .../block-list/src/utils/forward-delete.ts | 3 +- .../block-note/src/commands/block-type.ts | 11 ++++-- .../src/commands/change-note-display-mode.ts | 6 +-- .../src/commands/dedent-block-to-root.ts | 3 +- .../block-note/src/commands/dedent-block.ts | 3 +- .../src/commands/dedent-blocks-to-root.ts | 3 +- .../block-note/src/commands/dedent-blocks.ts | 3 +- .../block-note/src/commands/indent-block.ts | 8 ++-- .../block-note/src/commands/indent-blocks.ts | 15 +++----- .../components/edgeless-note-background.ts | 6 ++- .../affine/block-note/src/note-service.ts | 34 ++++++++++------- blocksuite/affine/block-note/src/utils.ts | 4 +- .../src/commands/dedent-paragraph.ts | 5 ++- .../src/commands/indent-paragraph.ts | 14 +++---- .../src/commands/split-paragraph.ts | 3 +- .../block-paragraph/src/paragraph-keymap.ts | 11 ++++-- .../src/utils/forward-delete.ts | 37 ++++++++++++------- .../src/utils/merge-with-prev.ts | 35 ++++++++++++------ .../affine/block-surface-ref/src/commands.ts | 7 +++- .../components/src/doc-title/doc-title.ts | 13 +++++-- .../src/drop-indicator/file-drop-manager.ts | 6 ++- .../affine/components/src/rich-text/dom.ts | 3 +- .../src/rich-text/format/delete-text.ts | 3 +- .../src/rich-text/keymap/bracket.ts | 5 ++- .../src/rich-text/markdown/divider.ts | 8 +++- .../components/src/rich-text/markdown/list.ts | 8 +++- .../src/rich-text/markdown/markdown-input.ts | 5 ++- .../src/rich-text/markdown/paragraph.ts | 9 +++-- .../src/rich-text/markdown/to-code.ts | 3 +- .../model/src/blocks/code/code-model.ts | 11 +++--- .../model/src/blocks/divider/divider-model.ts | 9 ++++- .../model/src/blocks/list/list-model.ts | 9 +++-- .../blocks/surface-ref/surface-ref-model.ts | 5 ++- .../shared/src/adapters/middlewares/copy.ts | 3 +- .../shared/src/adapters/middlewares/paste.ts | 6 ++- blocksuite/affine/shared/src/consts/index.ts | 21 ++++++++++- .../shared/src/utils/collapsed/paragraph.ts | 6 +-- .../shared/src/utils/dnd/calc-drop-target.ts | 5 ++- .../src/utils/dnd/get-drop-rect-by-point.ts | 3 +- .../shared/src/utils/dom/point-to-block.ts | 9 +++-- .../affine/shared/src/utils/model/checker.ts | 24 +++++++----- .../affine/shared/src/utils/model/doc.ts | 1 - .../src/utils/model/get-content-block.ts | 3 +- .../affine/shared/src/utils/model/getter.ts | 6 +-- .../affine/shared/src/utils/model/list.ts | 8 ++-- .../src/middleware/reorder-list.ts | 3 +- .../affine/widget-drag-handle/src/utils.ts | 10 +++-- .../src/watchers/drag-event-watcher.ts | 22 +++++++---- .../widget-edgeless-auto-connect/src/index.ts | 6 +-- .../src/doc/doc-remote-selection.ts | 28 +++++++++----- .../_common/export-manager/export-manager.ts | 6 ++- .../edgeless/clipboard/clipboard.ts | 3 +- .../root-block/edgeless/edgeless-keyboard.ts | 5 ++- .../edgeless/edgeless-root-block.ts | 4 +- .../root-block/keyboard/keyboard-manager.ts | 5 ++- .../src/root-block/page/page-root-block.ts | 36 +++++++++++------- .../element-toolbar/change-frame-button.ts | 3 +- .../element-toolbar/change-group-button.ts | 8 +++- .../element-toolbar/change-note-button.ts | 6 +-- .../widgets/format-bar/format-bar.ts | 14 +++++-- .../root-block/widgets/linked-doc/config.ts | 2 +- .../page-dragging-area/page-dragging-area.ts | 4 +- .../root-block/widgets/slash-menu/config.ts | 2 +- .../root-block/widgets/slash-menu/index.ts | 7 +--- .../block-std/src/__tests__/test-schema.ts | 14 +++++-- .../store/src/__tests__/block.unit.spec.ts | 14 +++++-- .../store/src/__tests__/test-schema.ts | 6 ++- .../src/__tests__/transformer.unit.spec.ts | 8 ++-- .../store/src/model/block/block-model.ts | 4 +- .../framework/store/src/model/block/zod.ts | 12 ------ .../framework/store/src/model/store/store.ts | 14 ------- .../outline/body/outline-panel-body.ts | 10 ++--- .../fragments/outline/card/outline-preview.ts | 2 +- .../fragments/outline/mobile-outline-panel.ts | 11 ++++-- .../src/fragments/outline/utils/query.ts | 12 +++--- .../presets/ai/_common/chat-actions-handle.ts | 5 ++- .../blocksuite/presets/ai/_common/config.ts | 12 ++++-- .../presets/ai/actions/edgeless-handler.ts | 3 +- .../presets/ai/actions/edgeless-response.ts | 2 +- .../src/blocksuite/presets/ai/ai-panel.ts | 3 +- .../ai/peek-view/chat-block-peek-view.ts | 2 +- .../src/blocksuite/presets/ai/slides/index.ts | 2 +- .../blocksuite/presets/ai/utils/edgeless.ts | 7 +++- .../blocksuite/presets/ai/utils/extract.ts | 5 ++- .../presets/ai/utils/selection-utils.ts | 15 ++++---- .../custom/widgets/edgeless-note-header.tsx | 4 +- 97 files changed, 492 insertions(+), 323 deletions(-) diff --git a/blocksuite/affine/block-attachment/src/attachment-service.ts b/blocksuite/affine/block-attachment/src/attachment-service.ts index a338573c07..10e80ecdbb 100644 --- a/blocksuite/affine/block-attachment/src/attachment-service.ts +++ b/blocksuite/affine/block-attachment/src/attachment-service.ts @@ -1,3 +1,4 @@ +import { SurfaceBlockModel } from '@blocksuite/affine-block-surface'; import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator'; import { AttachmentBlockSchema } from '@blocksuite/affine-model'; import { @@ -23,7 +24,7 @@ export const AttachmentDropOption = FileDropConfigExtension({ const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize; - if (targetModel && !matchFlavours(targetModel, ['affine:surface'])) { + if (targetModel && !matchFlavours(targetModel, [SurfaceBlockModel])) { addSiblingAttachmentBlocks( std.host, attachmentFiles, diff --git a/blocksuite/affine/block-database/src/detail-panel/note-renderer.ts b/blocksuite/affine/block-database/src/detail-panel/note-renderer.ts index b5c269e20e..826c1faae4 100644 --- a/blocksuite/affine/block-database/src/detail-panel/note-renderer.ts +++ b/blocksuite/affine/block-database/src/detail-panel/note-renderer.ts @@ -1,6 +1,9 @@ -import type { - DatabaseBlockModel, - RootBlockModel, +import { + CodeBlockModel, + type DatabaseBlockModel, + ListBlockModel, + ParagraphBlockModel, + type RootBlockModel, } from '@blocksuite/affine-model'; import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts'; import { TelemetryProvider } from '@blocksuite/affine-shared/services'; @@ -73,9 +76,9 @@ export class NoteRenderer .find(child => child.flavour === 'affine:note') ?.children.find(block => matchFlavours(block, [ - 'affine:paragraph', - 'affine:list', - 'affine:code', + ParagraphBlockModel, + ListBlockModel, + CodeBlockModel, ]) ); } diff --git a/blocksuite/affine/block-edgeless-text/src/edgeless-text-block.ts b/blocksuite/affine/block-edgeless-text/src/edgeless-text-block.ts index de049d905d..db615f4b57 100644 --- a/blocksuite/affine/block-edgeless-text/src/edgeless-text-block.ts +++ b/blocksuite/affine/block-edgeless-text/src/edgeless-text-block.ts @@ -1,6 +1,10 @@ import { TextUtils } from '@blocksuite/affine-block-surface'; import { formatBlockCommand } from '@blocksuite/affine-components/rich-text'; -import type { EdgelessTextBlockModel } from '@blocksuite/affine-model'; +import { + type EdgelessTextBlockModel, + ListBlockModel, + ParagraphBlockModel, +} from '@blocksuite/affine-model'; import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { BlockComponent } from '@blocksuite/block-std'; @@ -158,7 +162,7 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent - note.children.filter(child => matchFlavours(child, ['affine:image'])) + note.children.filter(child => matchFlavours(child, [ImageBlockModel])) )[0]; if (target) { @@ -135,9 +138,7 @@ async function renderNoteContent( const cardStyle = card.model.style; const isHorizontal = cardStyle === 'horizontal'; - const allowFlavours: (keyof BlockSuite.BlockModels)[] = isHorizontal - ? [] - : ['affine:image']; + const allowFlavours = isHorizontal ? [] : [ImageBlockModel]; const noteChildren = notes.flatMap(note => note.children.filter(model => { @@ -214,7 +215,7 @@ async function renderNoteContent( } function filterTextModel(model: BlockModel) { - if (matchFlavours(model, ['affine:paragraph', 'affine:list'])) { + if (matchFlavours(model, [ParagraphBlockModel, ListBlockModel])) { return !!model.text?.toString().length; } return false; @@ -223,7 +224,7 @@ function filterTextModel(model: BlockModel) { export function getNotesFromDoc(doc: Store) { const notes = doc.root?.children.filter( child => - matchFlavours(child, ['affine:note']) && + matchFlavours(child, [NoteBlockModel]) && child.displayMode !== NoteDisplayMode.EdgelessOnly ); @@ -304,7 +305,7 @@ export function getDocContentWithMaxLength(doc: Store, maxlength = 500) { export function getTitleFromSelectedModels(selectedModels: DraftModel[]) { const firstBlock = selectedModels[0]; if ( - matchFlavours(firstBlock, ['affine:paragraph']) && + matchFlavours(firstBlock, [ParagraphBlockModel]) && firstBlock.type.startsWith('h') ) { return firstBlock.text.toString(); diff --git a/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-linked-doc-block.ts b/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-linked-doc-block.ts index 23415d4c74..041f2b287f 100644 --- a/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-linked-doc-block.ts +++ b/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-linked-doc-block.ts @@ -1,3 +1,4 @@ +import { SurfaceBlockModel } from '@blocksuite/affine-block-surface'; import { isPeekable, Peekable } from '@blocksuite/affine-components/peek'; import { RefNodeSlotsProvider } from '@blocksuite/affine-components/rich-text'; import type { @@ -373,7 +374,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent { if ( !matchFlavours(model, [ - 'affine:paragraph', - 'affine:list', - 'affine:code', + ParagraphBlockModel, + ListBlockModel, + CodeBlockModel, ]) ) { return; diff --git a/blocksuite/affine/block-note/src/commands/change-note-display-mode.ts b/blocksuite/affine/block-note/src/commands/change-note-display-mode.ts index 427d26d98d..6ecfbf5190 100644 --- a/blocksuite/affine/block-note/src/commands/change-note-display-mode.ts +++ b/blocksuite/affine/block-note/src/commands/change-note-display-mode.ts @@ -1,4 +1,4 @@ -import { NoteDisplayMode } from '@blocksuite/affine-model'; +import { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { Command } from '@blocksuite/block-std'; @@ -10,7 +10,7 @@ export const changeNoteDisplayMode: Command<{ const { std, noteId, mode, stopCapture } = ctx; const noteBlockModel = std.store.getBlock(noteId)?.model; - if (!noteBlockModel || !matchFlavours(noteBlockModel, ['affine:note'])) + if (!noteBlockModel || !matchFlavours(noteBlockModel, [NoteBlockModel])) return; const currentMode = noteBlockModel.displayMode; @@ -27,7 +27,7 @@ export const changeNoteDisplayMode: Command<{ const parent = std.store.getParent(noteBlockModel); if (parent) { const notes = parent.children.filter(child => - matchFlavours(child, ['affine:note']) + matchFlavours(child, [NoteBlockModel]) ); const firstEdgelessOnlyNote = notes.find( note => note.displayMode === NoteDisplayMode.EdgelessOnly diff --git a/blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts b/blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts index a7db93b928..3d17e560e6 100644 --- a/blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts +++ b/blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts @@ -1,3 +1,4 @@ +import { NoteBlockModel } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { Command } from '@blocksuite/block-std'; @@ -20,7 +21,7 @@ export const dedentBlockToRoot: Command<{ let parent = store.getParent(model); let changed = false; - while (parent && !matchFlavours(parent, ['affine:note'])) { + while (parent && !matchFlavours(parent, [NoteBlockModel])) { if (!changed) { if (stopCapture) store.captureSync(); changed = true; diff --git a/blocksuite/affine/block-note/src/commands/dedent-block.ts b/blocksuite/affine/block-note/src/commands/dedent-block.ts index be655ac187..e33dd8fd1e 100644 --- a/blocksuite/affine/block-note/src/commands/dedent-block.ts +++ b/blocksuite/affine/block-note/src/commands/dedent-block.ts @@ -1,3 +1,4 @@ +import { ParagraphBlockModel } from '@blocksuite/affine-model'; import { calculateCollapsedSiblings, matchFlavours, @@ -45,7 +46,7 @@ export const dedentBlock: Command<{ if (stopCapture) store.captureSync(); if ( - matchFlavours(model, ['affine:paragraph']) && + matchFlavours(model, [ParagraphBlockModel]) && model.type.startsWith('h') && model.collapsed ) { diff --git a/blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts b/blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts index 8fd12a94c3..4396c0a249 100644 --- a/blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts +++ b/blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts @@ -1,3 +1,4 @@ +import { NoteBlockModel } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { type Command, TextSelection } from '@blocksuite/block-std'; @@ -30,7 +31,7 @@ export const dedentBlocksToRoot: Command<{ for (let i = blockIds.length - 1; i >= 0; i--) { const model = blockIds[i]; const parent = store.getParent(model); - if (parent && !matchFlavours(parent, ['affine:note'])) { + if (parent && !matchFlavours(parent, [NoteBlockModel])) { std.command.exec(dedentBlockToRoot, { blockId: model, stopCapture: false, diff --git a/blocksuite/affine/block-note/src/commands/dedent-blocks.ts b/blocksuite/affine/block-note/src/commands/dedent-blocks.ts index 98885da71e..9fb04980bb 100644 --- a/blocksuite/affine/block-note/src/commands/dedent-blocks.ts +++ b/blocksuite/affine/block-note/src/commands/dedent-blocks.ts @@ -1,3 +1,4 @@ +import { ParagraphBlockModel } from '@blocksuite/affine-model'; import { calculateCollapsedSiblings, matchFlavours, @@ -57,7 +58,7 @@ export const dedentBlocks: Command<{ const model = store.getBlock(id)?.model; if (!model) return; if ( - matchFlavours(model, ['affine:paragraph']) && + matchFlavours(model, [ParagraphBlockModel]) && model.type.startsWith('h') && model.collapsed ) { diff --git a/blocksuite/affine/block-note/src/commands/indent-block.ts b/blocksuite/affine/block-note/src/commands/indent-block.ts index 75b0e8a6ff..f9d22acfa5 100644 --- a/blocksuite/affine/block-note/src/commands/indent-block.ts +++ b/blocksuite/affine/block-note/src/commands/indent-block.ts @@ -1,4 +1,4 @@ -import type { ListBlockModel } from '@blocksuite/affine-model'; +import { ListBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model'; import { calculateCollapsedSiblings, matchFlavours, @@ -50,7 +50,7 @@ export const indentBlock: Command<{ if (stopCapture) store.captureSync(); if ( - matchFlavours(model, ['affine:paragraph']) && + matchFlavours(model, [ParagraphBlockModel]) && model.type.startsWith('h') && model.collapsed ) { @@ -62,12 +62,12 @@ export const indentBlock: Command<{ // update collapsed state of affine list if ( - matchFlavours(previousSibling, ['affine:list']) && + matchFlavours(previousSibling, [ListBlockModel]) && previousSibling.collapsed ) { store.updateBlock(previousSibling, { collapsed: false, - } as Partial); + }); } return next(); diff --git a/blocksuite/affine/block-note/src/commands/indent-blocks.ts b/blocksuite/affine/block-note/src/commands/indent-blocks.ts index 6e94f3239b..5793f02dce 100644 --- a/blocksuite/affine/block-note/src/commands/indent-blocks.ts +++ b/blocksuite/affine/block-note/src/commands/indent-blocks.ts @@ -1,3 +1,4 @@ +import { ParagraphBlockModel } from '@blocksuite/affine-model'; import { calculateCollapsedSiblings, getNearestHeadingBefore, @@ -58,7 +59,7 @@ export const indentBlocks: Command<{ const model = store.getBlock(id)?.model; if (!model) return; if ( - matchFlavours(model, ['affine:paragraph']) && + matchFlavours(model, [ParagraphBlockModel]) && model.type.startsWith('h') && model.collapsed ) { @@ -83,12 +84,10 @@ export const indentBlocks: Command<{ const nearestHeading = getNearestHeadingBefore(firstModel); if ( nearestHeading && - matchFlavours(nearestHeading, ['affine:paragraph']) && + matchFlavours(nearestHeading, [ParagraphBlockModel]) && nearestHeading.collapsed ) { - store.updateBlock(nearestHeading, { - collapsed: false, - }); + store.updateBlock(nearestHeading, { collapsed: false }); } } @@ -106,12 +105,10 @@ export const indentBlocks: Command<{ const nearestHeading = getNearestHeadingBefore(firstModel); if ( nearestHeading && - matchFlavours(nearestHeading, ['affine:paragraph']) && + matchFlavours(nearestHeading, [ParagraphBlockModel]) && nearestHeading.collapsed ) { - store.updateBlock(nearestHeading, { - collapsed: false, - }); + store.updateBlock(nearestHeading, { collapsed: false }); } } diff --git a/blocksuite/affine/block-note/src/components/edgeless-note-background.ts b/blocksuite/affine/block-note/src/components/edgeless-note-background.ts index 07c0b1e2a8..47d6166753 100644 --- a/blocksuite/affine/block-note/src/components/edgeless-note-background.ts +++ b/blocksuite/affine/block-note/src/components/edgeless-note-background.ts @@ -1,6 +1,8 @@ import { DefaultTheme, + ListBlockModel, NoteBlockModel, + ParagraphBlockModel, StrokeStyle, } from '@blocksuite/affine-model'; import { ThemeProvider } from '@blocksuite/affine-shared/services'; @@ -98,10 +100,10 @@ export class EdgelessNoteBackground extends SignalWatcher( if ( (!nearestModel.text || - !matchFlavours(nearestModel, ['affine:paragraph', 'affine:list'])) && + !matchFlavours(nearestModel, [ParagraphBlockModel, ListBlockModel])) && (!siblingModel || !siblingModel.text || - !matchFlavours(siblingModel, ['affine:paragraph', 'affine:list'])) + !matchFlavours(siblingModel, [ParagraphBlockModel, ListBlockModel])) ) { const [pId] = this.doc.addSiblingBlocks( nearestModel, diff --git a/blocksuite/affine/block-note/src/note-service.ts b/blocksuite/affine/block-note/src/note-service.ts index 9a8e8d63e8..02cde50227 100644 --- a/blocksuite/affine/block-note/src/note-service.ts +++ b/blocksuite/affine/block-note/src/note-service.ts @@ -1,5 +1,11 @@ import { textConversionConfigs } from '@blocksuite/affine-components/rich-text'; -import { NoteBlockSchema } from '@blocksuite/affine-model'; +import { + CodeBlockModel, + ListBlockModel, + NoteBlockModel, + NoteBlockSchema, + ParagraphBlockModel, +} from '@blocksuite/affine-model'; import { getBlockSelectionsCommand, getNextBlockCommand, @@ -149,7 +155,7 @@ export class NoteBlockService extends BlockService { const doc = this._std.store; let parent = doc.getBlock(blockId)?.model ?? null; while (parent) { - if (matchFlavours(parent, [NoteBlockSchema.model.flavour])) { + if (matchFlavours(parent, [NoteBlockModel])) { return parent; } parent = doc.getParent(parent); @@ -189,9 +195,9 @@ export class NoteBlockService extends BlockService { if ( !matchFlavours(nextBlock.model, [ - 'affine:paragraph', - 'affine:list', - 'affine:code', + ParagraphBlockModel, + ListBlockModel, + CodeBlockModel, ]) ) { this._std.command.exec(selectBlock, { @@ -225,9 +231,9 @@ export class NoteBlockService extends BlockService { event.preventDefault(); if ( matchFlavours(nextBlock.model, [ - 'affine:paragraph', - 'affine:list', - 'affine:code', + ParagraphBlockModel, + ListBlockModel, + CodeBlockModel, ]) ) { this._std.command.exec(focusBlockStart, { @@ -279,9 +285,9 @@ export class NoteBlockService extends BlockService { if ( !matchFlavours(prevBlock.model, [ - 'affine:paragraph', - 'affine:list', - 'affine:code', + ParagraphBlockModel, + ListBlockModel, + CodeBlockModel, ]) ) { this._std.command.exec(selectBlock, { @@ -313,9 +319,9 @@ export class NoteBlockService extends BlockService { if ( matchFlavours(prevBlock.model, [ - 'affine:paragraph', - 'affine:list', - 'affine:code', + ParagraphBlockModel, + ListBlockModel, + CodeBlockModel, ]) ) { event.preventDefault(); diff --git a/blocksuite/affine/block-note/src/utils.ts b/blocksuite/affine/block-note/src/utils.ts index 174eeb2c37..ec41758034 100644 --- a/blocksuite/affine/block-note/src/utils.ts +++ b/blocksuite/affine/block-note/src/utils.ts @@ -1,4 +1,4 @@ -import { type NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model'; +import { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model'; import { FeatureFlagService } from '@blocksuite/affine-shared/services'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { BlockStdScope } from '@blocksuite/block-std'; @@ -11,7 +11,7 @@ export function isPageBlock(std: BlockStdScope, note: NoteBlockModel) { std.get(FeatureFlagService).getFlag('enable_page_block') && note.parent?.children.find( child => - matchFlavours(child, ['affine:note']) && + matchFlavours(child, [NoteBlockModel]) && child.displayMode !== NoteDisplayMode.EdgelessOnly ) === note ); diff --git a/blocksuite/affine/block-paragraph/src/commands/dedent-paragraph.ts b/blocksuite/affine/block-paragraph/src/commands/dedent-paragraph.ts index 918e634e2d..72bc407490 100644 --- a/blocksuite/affine/block-paragraph/src/commands/dedent-paragraph.ts +++ b/blocksuite/affine/block-paragraph/src/commands/dedent-paragraph.ts @@ -1,3 +1,4 @@ +import { ParagraphBlockModel } from '@blocksuite/affine-model'; import type { IndentContext } from '@blocksuite/affine-shared/types'; import { calculateCollapsedSiblings, @@ -34,7 +35,7 @@ export const canDedentParagraphCommand: Command< } const model = store.getBlock(blockId)?.model; - if (!model || !matchFlavours(model, ['affine:paragraph'])) { + if (!model || !matchFlavours(model, [ParagraphBlockModel])) { return; } @@ -88,7 +89,7 @@ export const dedentParagraphCommand: Command<{ store.captureSync(); if ( - matchFlavours(model, ['affine:paragraph']) && + matchFlavours(model, [ParagraphBlockModel]) && model.type.startsWith('h') && model.collapsed ) { diff --git a/blocksuite/affine/block-paragraph/src/commands/indent-paragraph.ts b/blocksuite/affine/block-paragraph/src/commands/indent-paragraph.ts index 8b3016e5eb..5051922d1d 100644 --- a/blocksuite/affine/block-paragraph/src/commands/indent-paragraph.ts +++ b/blocksuite/affine/block-paragraph/src/commands/indent-paragraph.ts @@ -1,4 +1,4 @@ -import type { ListBlockModel } from '@blocksuite/affine-model'; +import { ListBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model'; import type { IndentContext } from '@blocksuite/affine-shared/types'; import { calculateCollapsedSiblings, @@ -37,7 +37,7 @@ export const canIndentParagraphCommand: Command< } const model = std.store.getBlock(blockId)?.model; - if (!model || !matchFlavours(model, ['affine:paragraph'])) { + if (!model || !matchFlavours(model, [ParagraphBlockModel])) { return; } @@ -95,7 +95,7 @@ export const indentParagraphCommand: Command<{ const nearestHeading = getNearestHeadingBefore(model); if ( nearestHeading && - matchFlavours(nearestHeading, ['affine:paragraph']) && + matchFlavours(nearestHeading, [ParagraphBlockModel]) && nearestHeading.collapsed ) { store.updateBlock(nearestHeading, { @@ -105,7 +105,7 @@ export const indentParagraphCommand: Command<{ } if ( - matchFlavours(model, ['affine:paragraph']) && + matchFlavours(model, [ParagraphBlockModel]) && model.type.startsWith('h') && model.collapsed ) { @@ -124,7 +124,7 @@ export const indentParagraphCommand: Command<{ const nearestHeading = getNearestHeadingBefore(model); if ( nearestHeading && - matchFlavours(nearestHeading, ['affine:paragraph']) && + matchFlavours(nearestHeading, [ParagraphBlockModel]) && nearestHeading.collapsed ) { store.updateBlock(nearestHeading, { @@ -135,12 +135,12 @@ export const indentParagraphCommand: Command<{ // update collapsed state of affine list if ( - matchFlavours(previousSibling, ['affine:list']) && + matchFlavours(previousSibling, [ListBlockModel]) && previousSibling.collapsed ) { store.updateBlock(previousSibling, { collapsed: false, - } as Partial); + }); } const textSelection = selection.find(TextSelection); diff --git a/blocksuite/affine/block-paragraph/src/commands/split-paragraph.ts b/blocksuite/affine/block-paragraph/src/commands/split-paragraph.ts index 011078d27a..8ae4614444 100644 --- a/blocksuite/affine/block-paragraph/src/commands/split-paragraph.ts +++ b/blocksuite/affine/block-paragraph/src/commands/split-paragraph.ts @@ -2,6 +2,7 @@ import { focusTextModel, getInlineEditorByModel, } from '@blocksuite/affine-components/rich-text'; +import { ParagraphBlockModel } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { type Command, TextSelection } from '@blocksuite/block-std'; @@ -23,7 +24,7 @@ export const splitParagraphCommand: Command< if (!blockId) return; const model = store.getBlock(blockId)?.model; - if (!model || !matchFlavours(model, ['affine:paragraph'])) return; + if (!model || !matchFlavours(model, [ParagraphBlockModel])) return; const inlineEditor = getInlineEditorByModel(host, model); const range = inlineEditor?.getInlineRange(); diff --git a/blocksuite/affine/block-paragraph/src/paragraph-keymap.ts b/blocksuite/affine/block-paragraph/src/paragraph-keymap.ts index fc5805d830..539aa26bba 100644 --- a/blocksuite/affine/block-paragraph/src/paragraph-keymap.ts +++ b/blocksuite/affine/block-paragraph/src/paragraph-keymap.ts @@ -4,7 +4,10 @@ import { markdownInput, textKeymap, } from '@blocksuite/affine-components/rich-text'; -import { ParagraphBlockSchema } from '@blocksuite/affine-model'; +import { + ParagraphBlockModel, + ParagraphBlockSchema, +} from '@blocksuite/affine-model'; import { calculateCollapsedSiblings, matchFlavours, @@ -37,7 +40,7 @@ export const ParagraphKeymapExtension = KeymapExtension( const { store } = std; const model = store.getBlock(text.from.blockId)?.model; - if (!model || !matchFlavours(model, ['affine:paragraph'])) return; + if (!model || !matchFlavours(model, [ParagraphBlockModel])) return; const event = ctx.get('keyboardState').raw; event.preventDefault(); @@ -68,7 +71,7 @@ export const ParagraphKeymapExtension = KeymapExtension( const text = std.selection.find(TextSelection); if (!text) return; const model = store.getBlock(text.from.blockId)?.model; - if (!model || !matchFlavours(model, ['affine:paragraph'])) return; + if (!model || !matchFlavours(model, [ParagraphBlockModel])) return; const inlineEditor = getInlineEditorByModel( std.host, text.from.blockId @@ -95,7 +98,7 @@ export const ParagraphKeymapExtension = KeymapExtension( const text = std.selection.find(TextSelection); if (!text) return; const model = store.getBlock(text.from.blockId)?.model; - if (!model || !matchFlavours(model, ['affine:paragraph'])) return; + if (!model || !matchFlavours(model, [ParagraphBlockModel])) return; const inlineEditor = getInlineEditorByModel( std.host, text.from.blockId diff --git a/blocksuite/affine/block-paragraph/src/utils/forward-delete.ts b/blocksuite/affine/block-paragraph/src/utils/forward-delete.ts index 681274a331..3e2130a0c4 100644 --- a/blocksuite/affine/block-paragraph/src/utils/forward-delete.ts +++ b/blocksuite/affine/block-paragraph/src/utils/forward-delete.ts @@ -1,4 +1,14 @@ -import { EMBED_BLOCK_FLAVOUR_LIST } from '@blocksuite/affine-shared/consts'; +import { + AttachmentBlockModel, + BookmarkBlockModel, + CodeBlockModel, + DatabaseBlockModel, + DividerBlockModel, + ImageBlockModel, + ListBlockModel, + ParagraphBlockModel, +} from '@blocksuite/affine-model'; +import { EMBED_BLOCK_MODEL_LIST } from '@blocksuite/affine-shared/consts'; import { getNextContentBlock, matchFlavours, @@ -15,31 +25,32 @@ export function forwardDelete(std: BlockStdScope) { if (!text) return; const isCollapsed = text.isCollapsed(); const model = store.getBlock(text.from.blockId)?.model; - if (!model || !matchFlavours(model, ['affine:paragraph'])) return; + if (!model || !matchFlavours(model, [ParagraphBlockModel])) return; const isEnd = isCollapsed && text.from.index === model.text.length; if (!isEnd) return; const parent = store.getParent(model); if (!parent) return; const nextSibling = store.getNext(model); - const ignoreForwardDeleteFlavourList: BlockSuite.Flavour[] = [ - 'affine:attachment', - 'affine:bookmark', - 'affine:database', - 'affine:code', - 'affine:image', - 'affine:divider', - ...EMBED_BLOCK_FLAVOUR_LIST, - ]; - if (matchFlavours(nextSibling, ignoreForwardDeleteFlavourList)) { + if ( + matchFlavours(nextSibling, [ + AttachmentBlockModel, + BookmarkBlockModel, + DatabaseBlockModel, + CodeBlockModel, + ImageBlockModel, + DividerBlockModel, + ...EMBED_BLOCK_MODEL_LIST, + ] as const) + ) { std.selection.setGroup('note', [ std.selection.create(BlockSelection, { blockId: nextSibling.id }), ]); return true; } - if (nextSibling?.text) { + if (matchFlavours(nextSibling, [ParagraphBlockModel, ListBlockModel])) { model.text.join(nextSibling.text); if (nextSibling.children) { const parent = store.getParent(nextSibling); diff --git a/blocksuite/affine/block-paragraph/src/utils/merge-with-prev.ts b/blocksuite/affine/block-paragraph/src/utils/merge-with-prev.ts index 4e268ca6b9..a2a12cd8a3 100644 --- a/blocksuite/affine/block-paragraph/src/utils/merge-with-prev.ts +++ b/blocksuite/affine/block-paragraph/src/utils/merge-with-prev.ts @@ -2,8 +2,19 @@ import { asyncSetInlineRange, focusTextModel, } from '@blocksuite/affine-components/rich-text'; -import type { RootBlockModel } from '@blocksuite/affine-model'; -import { EMBED_BLOCK_FLAVOUR_LIST } from '@blocksuite/affine-shared/consts'; +import { + AttachmentBlockModel, + BookmarkBlockModel, + CodeBlockModel, + DatabaseBlockModel, + DividerBlockModel, + EdgelessTextBlockModel, + ImageBlockModel, + ListBlockModel, + ParagraphBlockModel, + type RootBlockModel, +} from '@blocksuite/affine-model'; +import { EMBED_BLOCK_MODEL_LIST } from '@blocksuite/affine-shared/consts'; import type { ExtendedModel } from '@blocksuite/affine-shared/types'; import { focusTitle, @@ -33,7 +44,7 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) { const parent = doc.getParent(model); if (!parent) return false; - if (matchFlavours(parent, ['affine:edgeless-text'])) { + if (matchFlavours(parent, [EdgelessTextBlockModel])) { return true; } @@ -42,7 +53,7 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) { return handleNoPreviousSibling(editorHost, model); } - if (matchFlavours(prevBlock, ['affine:paragraph', 'affine:list'])) { + if (matchFlavours(prevBlock, [ParagraphBlockModel, ListBlockModel])) { const modelIndex = parent.children.indexOf(model); if ( (modelIndex === -1 || modelIndex === parent.children.length - 1) && @@ -64,12 +75,12 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) { if ( matchFlavours(prevBlock, [ - 'affine:attachment', - 'affine:bookmark', - 'affine:code', - 'affine:image', - 'affine:divider', - ...EMBED_BLOCK_FLAVOUR_LIST, + AttachmentBlockModel, + BookmarkBlockModel, + CodeBlockModel, + ImageBlockModel, + DividerBlockModel, + ...EMBED_BLOCK_MODEL_LIST, ]) ) { const selection = editorHost.selection.create(BlockSelection, { @@ -86,7 +97,7 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) { return true; } - if (matchFlavours(parent, ['affine:database'])) { + if (matchFlavours(parent, [DatabaseBlockModel])) { doc.deleteBlock(model); focusTextModel(editorHost.std, prevBlock.id, prevBlock.text?.yText.length); return true; @@ -104,7 +115,7 @@ function handleNoPreviousSibling(editorHost: EditorHost, model: ExtendedModel) { // Probably no title, e.g. in edgeless mode if (!titleEditor) { if ( - matchFlavours(parent, ['affine:edgeless-text']) || + matchFlavours(parent, [EdgelessTextBlockModel]) || model.children.length > 0 ) { doc.deleteBlock(model, { diff --git a/blocksuite/affine/block-surface-ref/src/commands.ts b/blocksuite/affine/block-surface-ref/src/commands.ts index 0a4a7c9114..588b8795f2 100644 --- a/blocksuite/affine/block-surface-ref/src/commands.ts +++ b/blocksuite/affine/block-surface-ref/src/commands.ts @@ -1,5 +1,8 @@ import { getSurfaceBlock } from '@blocksuite/affine-block-surface'; -import type { SurfaceRefProps } from '@blocksuite/affine-model'; +import { + FrameBlockModel, + type SurfaceRefProps, +} from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { Command } from '@blocksuite/block-std'; import type { BlockModel } from '@blocksuite/store'; @@ -38,7 +41,7 @@ export const insertSurfaceRefBlockCommand: Command< if (element?.type === 'group') { surfaceRefProps.refFlavour = 'group'; - } else if (matchFlavours(blockModel, ['affine:frame'])) { + } else if (matchFlavours(blockModel, [FrameBlockModel])) { surfaceRefProps.refFlavour = 'frame'; } else { console.error(`reference not found ${reference}`); diff --git a/blocksuite/affine/components/src/doc-title/doc-title.ts b/blocksuite/affine/components/src/doc-title/doc-title.ts index 7f8a4d7d68..e17faa3f40 100644 --- a/blocksuite/affine/components/src/doc-title/doc-title.ts +++ b/blocksuite/affine/components/src/doc-title/doc-title.ts @@ -1,6 +1,9 @@ import { - type NoteBlockModel, + CodeBlockModel, + ListBlockModel, + NoteBlockModel, NoteDisplayMode, + ParagraphBlockModel, type RootBlockModel, } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; @@ -69,7 +72,7 @@ export class DocTitle extends WithDisposable(ShadowlessElement) { private _getOrCreateFirstPageVisibleNote() { const note = this._rootModel.children.find( (child): child is NoteBlockModel => - matchFlavours(child, ['affine:note']) && + matchFlavours(child, [NoteBlockModel]) && child.displayMode !== NoteDisplayMode.EdgelessOnly ); if (note) return note; @@ -102,7 +105,11 @@ export class DocTitle extends WithDisposable(ShadowlessElement) { const note = this._getOrCreateFirstPageVisibleNote(); const firstText = note?.children.find(block => - matchFlavours(block, ['affine:paragraph', 'affine:list', 'affine:code']) + matchFlavours(block, [ + ParagraphBlockModel, + ListBlockModel, + CodeBlockModel, + ]) ); if (firstText) { if (this._std) focusTextModel(this._std, firstText.id); diff --git a/blocksuite/affine/components/src/drop-indicator/file-drop-manager.ts b/blocksuite/affine/components/src/drop-indicator/file-drop-manager.ts index c29f409cae..c63f33a5d6 100644 --- a/blocksuite/affine/components/src/drop-indicator/file-drop-manager.ts +++ b/blocksuite/affine/components/src/drop-indicator/file-drop-manager.ts @@ -1,3 +1,4 @@ +import { NoteBlockModel } from '@blocksuite/affine-model'; import { calcDropTarget, type DropTarget, @@ -11,6 +12,7 @@ import { type EditorHost, LifeCycleWatcher, } from '@blocksuite/block-std'; +import { SurfaceBlockModel } from '@blocksuite/block-std/gfx'; import { createIdentifier } from '@blocksuite/global/di'; import type { IVec } from '@blocksuite/global/utils'; import { Point, throttle } from '@blocksuite/global/utils'; @@ -57,7 +59,7 @@ export class FileDropExtension extends LifeCycleWatcher { const model = element.model; const parent = this.std.store.getParent(model); - if (!matchFlavours(parent, ['affine:surface' as BlockSuite.Flavour])) { + if (!matchFlavours(parent, [SurfaceBlockModel])) { const point = this.point$.value; target = point && calcDropTarget(point, model, element); } @@ -73,7 +75,7 @@ export class FileDropExtension extends LifeCycleWatcher { if (!rootModel) return null; let lastNote = rootModel.children[rootModel.children.length - 1]; - if (!lastNote || !matchFlavours(lastNote, ['affine:note'])) { + if (!lastNote || !matchFlavours(lastNote, [NoteBlockModel])) { const newNoteId = this.doc.addBlock('affine:note', {}, rootModel.id); const newNote = this.doc.getBlock(newNoteId)?.model; if (!newNote) return null; diff --git a/blocksuite/affine/components/src/rich-text/dom.ts b/blocksuite/affine/components/src/rich-text/dom.ts index fd2f5bcf3e..e73e3ddadf 100644 --- a/blocksuite/affine/components/src/rich-text/dom.ts +++ b/blocksuite/affine/components/src/rich-text/dom.ts @@ -1,3 +1,4 @@ +import { DatabaseBlockModel } from '@blocksuite/affine-model'; import { asyncGetBlockComponent, getCurrentNativeRange, @@ -41,7 +42,7 @@ export function getInlineEditorByModel( typeof model === 'string' ? editorHost.std.store.getBlock(model)?.model : model; - if (!blockModel || matchFlavours(blockModel, ['affine:database'])) { + if (!blockModel || matchFlavours(blockModel, [DatabaseBlockModel])) { // Not support database model since it's may be have multiple inline editor instances. // Support to enter the editing state through the Enter key in the database. return null; diff --git a/blocksuite/affine/components/src/rich-text/format/delete-text.ts b/blocksuite/affine/components/src/rich-text/format/delete-text.ts index 839713f98d..c224e61bb2 100644 --- a/blocksuite/affine/components/src/rich-text/format/delete-text.ts +++ b/blocksuite/affine/components/src/rich-text/format/delete-text.ts @@ -1,3 +1,4 @@ +import { RootBlockModel } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { type Command, TextSelection } from '@blocksuite/block-std'; import type { Text } from '@blocksuite/store'; @@ -24,7 +25,7 @@ export const deleteTextCommand: Command<{ if (!fromElement) return; let fromText: Text | undefined; - if (matchFlavours(fromElement.model, ['affine:page'])) { + if (matchFlavours(fromElement.model, [RootBlockModel])) { fromText = fromElement.model.title; } else { fromText = fromElement.model.text; diff --git a/blocksuite/affine/components/src/rich-text/keymap/bracket.ts b/blocksuite/affine/components/src/rich-text/keymap/bracket.ts index 8c2d412735..61bfc2b707 100644 --- a/blocksuite/affine/components/src/rich-text/keymap/bracket.ts +++ b/blocksuite/affine/components/src/rich-text/keymap/bracket.ts @@ -1,3 +1,4 @@ +import { CodeBlockModel } from '@blocksuite/affine-model'; import { BRACKET_PAIRS } from '@blocksuite/affine-shared/consts'; import { createDefaultDoc, @@ -28,7 +29,7 @@ export const bracketKeymap = ( if (!textSelection) return; const model = doc.getBlock(textSelection.from.blockId)?.model; if (!model) return; - if (!matchFlavours(model, ['affine:code'])) return; + if (!matchFlavours(model, [CodeBlockModel])) return; const inlineEditor = getInlineEditorByModel( std.host, textSelection.from.blockId @@ -55,7 +56,7 @@ export const bracketKeymap = ( const model = doc.getBlock(textSelection.from.blockId)?.model; if (!model) return; - const isCodeBlock = matchFlavours(model, ['affine:code']); + const isCodeBlock = matchFlavours(model, [CodeBlockModel]); // When selection is collapsed, only trigger auto complete in code block if (textSelection.isCollapsed() && !isCodeBlock) return; if (!textSelection.isInSameBlock()) return; diff --git a/blocksuite/affine/components/src/rich-text/markdown/divider.ts b/blocksuite/affine/components/src/rich-text/markdown/divider.ts index 697689c5ba..063d0f57ee 100644 --- a/blocksuite/affine/components/src/rich-text/markdown/divider.ts +++ b/blocksuite/affine/components/src/rich-text/markdown/divider.ts @@ -1,3 +1,7 @@ +import { + DividerBlockModel, + ParagraphBlockModel, +} from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockModel } from '@blocksuite/store'; @@ -12,8 +16,8 @@ export function toDivider( ) { const { store: doc } = std; if ( - matchFlavours(model, ['affine:divider']) || - (matchFlavours(model, ['affine:paragraph']) && model.type === 'quote') + matchFlavours(model, [DividerBlockModel]) || + (matchFlavours(model, [ParagraphBlockModel]) && model.type === 'quote') ) { return; } diff --git a/blocksuite/affine/components/src/rich-text/markdown/list.ts b/blocksuite/affine/components/src/rich-text/markdown/list.ts index 13dc5316a3..f7263de4bc 100644 --- a/blocksuite/affine/components/src/rich-text/markdown/list.ts +++ b/blocksuite/affine/components/src/rich-text/markdown/list.ts @@ -1,4 +1,8 @@ -import type { ListProps, ListType } from '@blocksuite/affine-model'; +import { + type ListProps, + type ListType, + ParagraphBlockModel, +} from '@blocksuite/affine-model'; import { matchFlavours, toNumberedList } from '@blocksuite/affine-shared/utils'; import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockModel } from '@blocksuite/store'; @@ -13,7 +17,7 @@ export function toList( prefix: string, otherProperties?: Partial ) { - if (!matchFlavours(model, ['affine:paragraph'])) { + if (!matchFlavours(model, [ParagraphBlockModel])) { return; } const { store: doc } = std; diff --git a/blocksuite/affine/components/src/rich-text/markdown/markdown-input.ts b/blocksuite/affine/components/src/rich-text/markdown/markdown-input.ts index 3b754eb546..489959ae0d 100644 --- a/blocksuite/affine/components/src/rich-text/markdown/markdown-input.ts +++ b/blocksuite/affine/components/src/rich-text/markdown/markdown-input.ts @@ -1,3 +1,4 @@ +import { CodeBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model'; import { isMarkdownPrefix, matchFlavours, @@ -31,10 +32,10 @@ export function markdownInput( const prefixText = getPrefixText(inline); if (!isMarkdownPrefix(prefixText)) return; - const isParagraph = matchFlavours(model, ['affine:paragraph']); + const isParagraph = matchFlavours(model, [ParagraphBlockModel]); const isHeading = isParagraph && model.type.startsWith('h'); const isParagraphQuoteBlock = isParagraph && model.type === 'quote'; - const isCodeBlock = matchFlavours(model, ['affine:code']); + const isCodeBlock = matchFlavours(model, [CodeBlockModel]); if (isHeading || isParagraphQuoteBlock || isCodeBlock) return; const lineInfo = inline.getLine(range.index); diff --git a/blocksuite/affine/components/src/rich-text/markdown/paragraph.ts b/blocksuite/affine/components/src/rich-text/markdown/paragraph.ts index b728a3fe9d..287bcb4378 100644 --- a/blocksuite/affine/components/src/rich-text/markdown/paragraph.ts +++ b/blocksuite/affine/components/src/rich-text/markdown/paragraph.ts @@ -1,4 +1,7 @@ -import type { ParagraphType } from '@blocksuite/affine-model'; +import { + ParagraphBlockModel, + type ParagraphType, +} from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockModel } from '@blocksuite/store'; @@ -13,7 +16,7 @@ export function toParagraph( prefix: string ) { const { store: doc } = std; - if (!matchFlavours(model, ['affine:paragraph'])) { + if (!matchFlavours(model, [ParagraphBlockModel])) { const parent = doc.getParent(model); if (!parent) return; @@ -33,7 +36,7 @@ export function toParagraph( return id; } - if (matchFlavours(model, ['affine:paragraph']) && model.type !== type) { + if (matchFlavours(model, [ParagraphBlockModel]) && model.type !== type) { beforeConvert(std, model, prefix.length); doc.updateBlock(model, { type }); diff --git a/blocksuite/affine/components/src/rich-text/markdown/to-code.ts b/blocksuite/affine/components/src/rich-text/markdown/to-code.ts index 113eedec54..c147237e67 100644 --- a/blocksuite/affine/components/src/rich-text/markdown/to-code.ts +++ b/blocksuite/affine/components/src/rich-text/markdown/to-code.ts @@ -1,3 +1,4 @@ +import { ParagraphBlockModel } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockModel } from '@blocksuite/store'; @@ -10,7 +11,7 @@ export function toCode( prefixText: string, language: string | null ) { - if (matchFlavours(model, ['affine:paragraph']) && model.type === 'quote') { + if (matchFlavours(model, [ParagraphBlockModel]) && model.type === 'quote') { return; } diff --git a/blocksuite/affine/model/src/blocks/code/code-model.ts b/blocksuite/affine/model/src/blocks/code/code-model.ts index 74fbb8e332..36bd0a6520 100644 --- a/blocksuite/affine/model/src/blocks/code/code-model.ts +++ b/blocksuite/affine/model/src/blocks/code/code-model.ts @@ -1,8 +1,4 @@ -import { - defineBlockSchema, - type SchemaToModel, - type Text, -} from '@blocksuite/store'; +import { BlockModel, defineBlockSchema, type Text } from '@blocksuite/store'; interface CodeBlockProps { text: Text; @@ -31,9 +27,12 @@ export const CodeBlockSchema = defineBlockSchema({ ], children: [], }, + toModel: () => new CodeBlockModel(), }); -export type CodeBlockModel = SchemaToModel; +export class CodeBlockModel extends BlockModel { + override text!: Text; +} declare global { namespace BlockSuite { diff --git a/blocksuite/affine/model/src/blocks/divider/divider-model.ts b/blocksuite/affine/model/src/blocks/divider/divider-model.ts index bc824f9694..06456298b8 100644 --- a/blocksuite/affine/model/src/blocks/divider/divider-model.ts +++ b/blocksuite/affine/model/src/blocks/divider/divider-model.ts @@ -1,4 +1,4 @@ -import { defineBlockSchema, type SchemaToModel } from '@blocksuite/store'; +import { BlockModel, defineBlockSchema } from '@blocksuite/store'; export const DividerBlockSchema = defineBlockSchema({ flavour: 'affine:divider', @@ -7,9 +7,14 @@ export const DividerBlockSchema = defineBlockSchema({ role: 'content', children: [], }, + toModel: () => new DividerBlockModel(), }); -export type DividerBlockModel = SchemaToModel; +type Props = { + text: string; +}; + +export class DividerBlockModel extends BlockModel {} declare global { namespace BlockSuite { diff --git a/blocksuite/affine/model/src/blocks/list/list-model.ts b/blocksuite/affine/model/src/blocks/list/list-model.ts index b694906cad..235ded40a4 100644 --- a/blocksuite/affine/model/src/blocks/list/list-model.ts +++ b/blocksuite/affine/model/src/blocks/list/list-model.ts @@ -1,5 +1,5 @@ -import type { SchemaToModel, Text } from '@blocksuite/store'; -import { defineBlockSchema } from '@blocksuite/store'; +import type { Text } from '@blocksuite/store'; +import { BlockModel, defineBlockSchema } from '@blocksuite/store'; // `toggle` type has been deprecated, do not use it export type ListType = 'bulleted' | 'numbered' | 'todo' | 'toggle'; @@ -35,9 +35,12 @@ export const ListBlockSchema = defineBlockSchema({ 'affine:edgeless-text', ], }, + toModel: () => new ListBlockModel(), }); -export type ListBlockModel = SchemaToModel; +export class ListBlockModel extends BlockModel { + override text!: Text; +} declare global { namespace BlockSuite { diff --git a/blocksuite/affine/model/src/blocks/surface-ref/surface-ref-model.ts b/blocksuite/affine/model/src/blocks/surface-ref/surface-ref-model.ts index 636b161b8e..c2997e87a1 100644 --- a/blocksuite/affine/model/src/blocks/surface-ref/surface-ref-model.ts +++ b/blocksuite/affine/model/src/blocks/surface-ref/surface-ref-model.ts @@ -1,4 +1,4 @@ -import { defineBlockSchema, type SchemaToModel } from '@blocksuite/store'; +import { BlockModel, defineBlockSchema } from '@blocksuite/store'; export type SurfaceRefProps = { reference: string; @@ -18,9 +18,10 @@ export const SurfaceRefBlockSchema = defineBlockSchema({ role: 'content', parent: ['affine:note', 'affine:paragraph', 'affine:list'], }, + toModel: () => new SurfaceRefBlockModel(), }); -export type SurfaceRefBlockModel = SchemaToModel; +export class SurfaceRefBlockModel extends BlockModel {} declare global { namespace BlockSuite { diff --git a/blocksuite/affine/shared/src/adapters/middlewares/copy.ts b/blocksuite/affine/shared/src/adapters/middlewares/copy.ts index 0fef009467..4bd4e35311 100644 --- a/blocksuite/affine/shared/src/adapters/middlewares/copy.ts +++ b/blocksuite/affine/shared/src/adapters/middlewares/copy.ts @@ -1,3 +1,4 @@ +import { RootBlockModel } from '@blocksuite/affine-model'; import { type BlockStdScope, type EditorHost, @@ -19,7 +20,7 @@ const handlePoint = ( model: DraftModel ) => { const { index, length } = point; - if (matchFlavours(model, ['affine:page'])) { + if (matchFlavours(model, [RootBlockModel])) { if (length === 0) return; (snapshot.props.title as Record).delta = model.title.sliceToDelta(index, length + index); diff --git a/blocksuite/affine/shared/src/adapters/middlewares/paste.ts b/blocksuite/affine/shared/src/adapters/middlewares/paste.ts index b355356c33..d10af873a2 100644 --- a/blocksuite/affine/shared/src/adapters/middlewares/paste.ts +++ b/blocksuite/affine/shared/src/adapters/middlewares/paste.ts @@ -1,6 +1,8 @@ import { + CodeBlockModel, type DocMode, DocModes, + ImageBlockModel, type ParagraphBlockModel, type ReferenceInfo, } from '@blocksuite/affine-model'; @@ -291,7 +293,7 @@ class PasteTr { return; } if (!cursorModel.text) { - if (matchFlavours(cursorModel, ['affine:image'])) { + if (matchFlavours(cursorModel, [ImageBlockModel])) { const selection = this.std.selection.create(ImageSelection, { blockId: target.blockId, }); @@ -356,7 +358,7 @@ class PasteTr { if ( this.firstSnapshot !== this.lastSnapshot && this.lastSnapshot.props.text && - !matchFlavours(this.pointState.model, ['affine:code']) + !matchFlavours(this.pointState.model, [CodeBlockModel]) ) { const text = fromJSON(this.lastSnapshot.props.text) as Text; const doc = new Y.Doc(); diff --git a/blocksuite/affine/shared/src/consts/index.ts b/blocksuite/affine/shared/src/consts/index.ts index cccb7dcd2f..ba79dfe727 100644 --- a/blocksuite/affine/shared/src/consts/index.ts +++ b/blocksuite/affine/shared/src/consts/index.ts @@ -1,4 +1,13 @@ -import type { EmbedCardStyle } from '@blocksuite/affine-model'; +import { + type EmbedCardStyle, + EmbedFigmaModel, + EmbedGithubModel, + EmbedHtmlModel, + EmbedLinkedDocModel, + EmbedLoomModel, + EmbedSyncedDocModel, + EmbedYoutubeModel, +} from '@blocksuite/affine-model'; export const BLOCK_CHILDREN_CONTAINER_PADDING_LEFT = 24; export const EDGELESS_BLOCK_CHILD_PADDING = 24; @@ -48,6 +57,16 @@ export const EMBED_BLOCK_FLAVOUR_LIST = [ 'affine:embed-loom', ] as const; +export const EMBED_BLOCK_MODEL_LIST = [ + EmbedGithubModel, + EmbedYoutubeModel, + EmbedFigmaModel, + EmbedLinkedDocModel, + EmbedSyncedDocModel, + EmbedHtmlModel, + EmbedLoomModel, +] as const; + export const DEFAULT_IMAGE_PROXY_ENDPOINT = 'https://affine-worker.toeverything.workers.dev/api/worker/image-proxy'; diff --git a/blocksuite/affine/shared/src/utils/collapsed/paragraph.ts b/blocksuite/affine/shared/src/utils/collapsed/paragraph.ts index 37caec3686..42b4cdf929 100644 --- a/blocksuite/affine/shared/src/utils/collapsed/paragraph.ts +++ b/blocksuite/affine/shared/src/utils/collapsed/paragraph.ts @@ -1,4 +1,4 @@ -import type { ParagraphBlockModel } from '@blocksuite/affine-model'; +import { ParagraphBlockModel } from '@blocksuite/affine-model'; import type { BlockModel } from '@blocksuite/store'; import { matchFlavours } from '../model/checker.js'; @@ -15,7 +15,7 @@ export function calculateCollapsedSiblings( const collapsedEdgeIndex = children.findIndex((child, i) => { if ( i > index && - matchFlavours(child, ['affine:paragraph']) && + matchFlavours(child, [ParagraphBlockModel]) && child.type.startsWith('h') ) { const modelLevel = parseInt(model.type.slice(1)); @@ -46,7 +46,7 @@ export function getNearestHeadingBefore( for (let i = index - 1; i >= 0; i--) { const sibling = parent.children[i]; if ( - matchFlavours(sibling, ['affine:paragraph']) && + matchFlavours(sibling, [ParagraphBlockModel]) && sibling.type.startsWith('h') ) { return sibling; diff --git a/blocksuite/affine/shared/src/utils/dnd/calc-drop-target.ts b/blocksuite/affine/shared/src/utils/dnd/calc-drop-target.ts index 91594b1122..23db1c9fb7 100644 --- a/blocksuite/affine/shared/src/utils/dnd/calc-drop-target.ts +++ b/blocksuite/affine/shared/src/utils/dnd/calc-drop-target.ts @@ -1,3 +1,4 @@ +import { DatabaseBlockModel, ListBlockModel } from '@blocksuite/affine-model'; import type { BlockComponent } from '@blocksuite/block-std'; import { type Point, Rect } from '@blocksuite/global/utils'; import type { BlockModel } from '@blocksuite/store'; @@ -55,7 +56,7 @@ export function calcDropTarget( .every(m => children.includes(m.flavour)); } - if (!shouldAppendToDatabase && !matchFlavours(model, ['affine:database'])) { + if (!shouldAppendToDatabase && !matchFlavours(model, [DatabaseBlockModel])) { const databaseBlockComponent = element.closest('affine-database'); if (databaseBlockComponent) { @@ -149,7 +150,7 @@ export function calcDropTarget( const hasChild = (element as BlockComponent).childBlocks.length; if ( allowSublist && - matchFlavours(model, ['affine:list']) && + matchFlavours(model, [ListBlockModel]) && !hasChild && point.x > domRect.x + BLOCK_CHILDREN_CONTAINER_PADDING_LEFT ) { diff --git a/blocksuite/affine/shared/src/utils/dnd/get-drop-rect-by-point.ts b/blocksuite/affine/shared/src/utils/dnd/get-drop-rect-by-point.ts index 35977ba5da..a3e68a4f2f 100644 --- a/blocksuite/affine/shared/src/utils/dnd/get-drop-rect-by-point.ts +++ b/blocksuite/affine/shared/src/utils/dnd/get-drop-rect-by-point.ts @@ -1,3 +1,4 @@ +import { DatabaseBlockModel } from '@blocksuite/affine-model'; import { BLOCK_ID_ATTR } from '@blocksuite/block-std'; import type { Point } from '@blocksuite/global/utils'; import type { BlockModel } from '@blocksuite/store'; @@ -24,7 +25,7 @@ export function getDropRectByPoint( flag: DropFlags.Normal, }; - const isDatabase = matchFlavours(model, ['affine:database']); + const isDatabase = matchFlavours(model, [DatabaseBlockModel]); if (isDatabase) { const table = getDatabaseBlockTableElement(element); diff --git a/blocksuite/affine/shared/src/utils/dom/point-to-block.ts b/blocksuite/affine/shared/src/utils/dom/point-to-block.ts index 0688a327f3..aa05de567a 100644 --- a/blocksuite/affine/shared/src/utils/dom/point-to-block.ts +++ b/blocksuite/affine/shared/src/utils/dom/point-to-block.ts @@ -1,4 +1,6 @@ +import { NoteBlockModel, RootBlockModel } from '@blocksuite/affine-model'; import { BLOCK_ID_ATTR, type BlockComponent } from '@blocksuite/block-std'; +import { SurfaceBlockModel } from '@blocksuite/block-std/gfx'; import type { Point, Rect } from '@blocksuite/global/utils'; import type { BlockModel } from '@blocksuite/store'; @@ -35,10 +37,9 @@ function hasBlockId(element: Element): element is BlockComponent { */ function isRootOrNoteOrSurface(element: BlockComponent) { return matchFlavours(element.model, [ - 'affine:page', - 'affine:note', - // @ts-expect-error TODO: migrate surface model to @blocksuite/affine-model - 'affine:surface', + RootBlockModel, + NoteBlockModel, + SurfaceBlockModel, ]); } diff --git a/blocksuite/affine/shared/src/utils/model/checker.ts b/blocksuite/affine/shared/src/utils/model/checker.ts index 8145fe9d51..9a2ba890c4 100644 --- a/blocksuite/affine/shared/src/utils/model/checker.ts +++ b/blocksuite/affine/shared/src/utils/model/checker.ts @@ -1,15 +1,19 @@ -import type { BlockModel, DraftModel, Store } from '@blocksuite/store'; -import { minimatch } from 'minimatch'; +import type { BlockModel, Store } from '@blocksuite/store'; -export function matchFlavours( - model: DraftModel | null, - expected: Key -): model is BlockSuite.BlockModels[Key[number]] { +type ConstructorType = { new (): U }; +type ModelList = + T extends Array + ? U extends ConstructorType + ? Array + : never + : never; + +export function matchFlavours< + const Model extends ConstructorType[], + U extends ModelList[number] = ModelList[number], +>(model: unknown, expected: Model): model is U { return ( - !!model && - expected.some(key => - minimatch(model.flavour as keyof BlockSuite.BlockModels, key) - ) + !!model && expected.some(expectedModel => model instanceof expectedModel) ); } diff --git a/blocksuite/affine/shared/src/utils/model/doc.ts b/blocksuite/affine/shared/src/utils/model/doc.ts index e1ad250910..4e1111a9b2 100644 --- a/blocksuite/affine/shared/src/utils/model/doc.ts +++ b/blocksuite/affine/shared/src/utils/model/doc.ts @@ -16,7 +16,6 @@ export function createDefaultDoc( title, }); - // @ts-expect-error FIXME: will be fixed when surface model migrated to affine-model doc.addBlock('affine:surface', {}, rootId); const noteId = doc.addBlock('affine:note', {}, rootId); doc.addBlock('affine:paragraph', {}, noteId); diff --git a/blocksuite/affine/shared/src/utils/model/get-content-block.ts b/blocksuite/affine/shared/src/utils/model/get-content-block.ts index 95f5ed381f..ce83a10f14 100644 --- a/blocksuite/affine/shared/src/utils/model/get-content-block.ts +++ b/blocksuite/affine/shared/src/utils/model/get-content-block.ts @@ -1,3 +1,4 @@ +import { FrameBlockModel } from '@blocksuite/affine-model'; import type { EditorHost } from '@blocksuite/block-std'; import type { BlockModel } from '@blocksuite/store'; @@ -73,7 +74,7 @@ export function getPrevContentBlock( const prev = getPrev(model); if (prev) { - if (prev.role === 'content' && !matchFlavours(prev, ['affine:frame'])) { + if (prev.role === 'content' && !matchFlavours(prev, [FrameBlockModel])) { return prev; } diff --git a/blocksuite/affine/shared/src/utils/model/getter.ts b/blocksuite/affine/shared/src/utils/model/getter.ts index 3479d54bb8..2ba8a5c579 100644 --- a/blocksuite/affine/shared/src/utils/model/getter.ts +++ b/blocksuite/affine/shared/src/utils/model/getter.ts @@ -1,4 +1,4 @@ -import { type NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model'; +import { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model'; import type { BlockComponent, EditorHost } from '@blocksuite/block-std'; import type { BlockModel, Store } from '@blocksuite/store'; @@ -37,7 +37,7 @@ export async function asyncGetBlockComponent( export function findNoteBlockModel(model: BlockModel) { return findAncestorModel(model, m => - matchFlavours(m, ['affine:note']) + matchFlavours(m, [NoteBlockModel]) ) as NoteBlockModel | null; } @@ -48,7 +48,7 @@ export function getLastNoteBlock(doc: Store) { for (let i = children.length - 1; i >= 0; i--) { const child = children[i]; if ( - matchFlavours(child, ['affine:note']) && + matchFlavours(child, [NoteBlockModel]) && child.displayMode !== NoteDisplayMode.EdgelessOnly ) { note = child as NoteBlockModel; diff --git a/blocksuite/affine/shared/src/utils/model/list.ts b/blocksuite/affine/shared/src/utils/model/list.ts index 782b2aa4d3..f45b2c2652 100644 --- a/blocksuite/affine/shared/src/utils/model/list.ts +++ b/blocksuite/affine/shared/src/utils/model/list.ts @@ -1,4 +1,4 @@ -import type { ListBlockModel } from '@blocksuite/affine-model'; +import { ListBlockModel } from '@blocksuite/affine-model'; import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockModel, Store } from '@blocksuite/store'; @@ -23,7 +23,7 @@ export function getNextContinuousNumberedLists( const firstNotNumberedListIndex = parent.children.findIndex( (model, i) => i > modelIndex && - (!matchFlavours(model, ['affine:list']) || model.type !== 'numbered') + (!matchFlavours(model, [ListBlockModel]) || model.type !== 'numbered') ); const newContinuousLists = parent.children.slice( modelIndex + 1, @@ -32,7 +32,7 @@ export function getNextContinuousNumberedLists( if ( !newContinuousLists.every( model => - matchFlavours(model, ['affine:list']) && model.type === 'numbered' + matchFlavours(model, [ListBlockModel]) && model.type === 'numbered' ) ) return []; @@ -56,7 +56,7 @@ export function toNumberedList( // if there is a numbered list before, the order continues from the previous list if ( prevSibling && - matchFlavours(prevSibling, ['affine:list']) && + matchFlavours(prevSibling, [ListBlockModel]) && prevSibling.type === 'numbered' ) { doc.transact(() => { diff --git a/blocksuite/affine/widget-drag-handle/src/middleware/reorder-list.ts b/blocksuite/affine/widget-drag-handle/src/middleware/reorder-list.ts index c7986d6add..abdeb200fb 100644 --- a/blocksuite/affine/widget-drag-handle/src/middleware/reorder-list.ts +++ b/blocksuite/affine/widget-drag-handle/src/middleware/reorder-list.ts @@ -1,4 +1,5 @@ import { correctNumberedListsOrderToPrev } from '@blocksuite/affine-block-list'; +import { ListBlockModel } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { BlockStdScope } from '@blocksuite/block-std'; import type { TransformerMiddleware } from '@blocksuite/store'; @@ -10,7 +11,7 @@ export const reorderList = if (payload.type === 'block') { const model = payload.model; if ( - matchFlavours(model, ['affine:list']) && + matchFlavours(model, [ListBlockModel]) && model.type === 'numbered' ) { const next = std.store.getNext(model); diff --git a/blocksuite/affine/widget-drag-handle/src/utils.ts b/blocksuite/affine/widget-drag-handle/src/utils.ts index 82b7257b50..4a60796682 100644 --- a/blocksuite/affine/widget-drag-handle/src/utils.ts +++ b/blocksuite/affine/widget-drag-handle/src/utils.ts @@ -3,7 +3,11 @@ import { type EdgelessNoteBlockComponent, } from '@blocksuite/affine-block-note'; import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph'; -import type { ParagraphBlockModel } from '@blocksuite/affine-model'; +import { + DatabaseBlockModel, + ListBlockModel, + type ParagraphBlockModel, +} from '@blocksuite/affine-model'; import { DocModeProvider } from '@blocksuite/affine-shared/services'; import { calcDropTarget, @@ -216,7 +220,7 @@ export const getDropResult = ( const model = closestBlock.model; - const isDatabase = matchFlavours(model, ['affine:database']); + const isDatabase = matchFlavours(model, [DatabaseBlockModel]); if (isDatabase) { return dropIndicator; } @@ -232,7 +236,7 @@ export const getDropResult = ( export function getDragHandleLeftPadding(blocks: BlockComponent[]) { const hasToggleList = blocks.some( block => - (matchFlavours(block.model, ['affine:list']) && + (matchFlavours(block.model, [ListBlockModel]) && block.model.children.length > 0) || (block instanceof ParagraphBlockComponent && block.model.type.startsWith('h') && diff --git a/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts b/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts index 2770df1ac8..ca4b502e36 100644 --- a/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts +++ b/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts @@ -2,9 +2,17 @@ import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph'; import { addNoteAtPoint, getSurfaceBlock, + SurfaceBlockModel, } from '@blocksuite/affine-block-surface'; import { DropIndicator } from '@blocksuite/affine-components/drop-indicator'; -import type { EmbedCardStyle, NoteBlockModel } from '@blocksuite/affine-model'; +import { + AttachmentBlockModel, + BookmarkBlockModel, + DatabaseBlockModel, + type EmbedCardStyle, + ListBlockModel, + NoteBlockModel, +} from '@blocksuite/affine-model'; import { BLOCK_CHILDREN_CONTAINER_PADDING_LEFT, EMBED_CARD_HEIGHT, @@ -153,17 +161,17 @@ export class DragEventWatcher { !snapshot || snapshot.content.length === 0 || !dragPayload?.from || - matchFlavours(model, ['affine:database']) + matchFlavours(model, [DatabaseBlockModel]) ) return null; - const isDropOnNoteBlock = matchFlavours(model, ['affine:note']); + const isDropOnNoteBlock = matchFlavours(model, [NoteBlockModel]); const edge = dropPayload.edge; const scale = this.widget.scale.peek(); let result: DropResult; - if (edge === 'right' && matchFlavours(dropBlock.model, ['affine:list'])) { + if (edge === 'right' && matchFlavours(dropBlock.model, [ListBlockModel])) { const domRect = getRectByBlockComponent(dropBlock); const placement = 'in'; const rect = Rect.fromLWTH( @@ -365,7 +373,7 @@ export class DragEventWatcher { result.placement === 'in' ? model : this.std.store.getParent(model); if (!parent) return; - if (matchFlavours(parent, ['affine:surface'])) { + if (matchFlavours(parent, [SurfaceBlockModel])) { return; } @@ -375,7 +383,7 @@ export class DragEventWatcher { : parent.children.indexOf(model) + (result.placement === 'before' ? 0 : 1); - if (matchFlavours(parent, ['affine:note'])) { + if (matchFlavours(parent, [NoteBlockModel])) { const [first] = snapshot.content; if (first.flavour === 'affine:note') { if (parent.id !== first.id) { @@ -677,7 +685,7 @@ export class DragEventWatcher { }) ); - if (matchFlavours(view.model, ['affine:attachment', 'affine:bookmark'])) { + if (matchFlavours(view.model, [AttachmentBlockModel, BookmarkBlockModel])) { cleanups.push(this._makeDraggable(view)); } diff --git a/blocksuite/affine/widget-edgeless-auto-connect/src/index.ts b/blocksuite/affine/widget-edgeless-auto-connect/src/index.ts index 16ee5e529e..c1095a299d 100644 --- a/blocksuite/affine/widget-edgeless-auto-connect/src/index.ts +++ b/blocksuite/affine/widget-edgeless-auto-connect/src/index.ts @@ -14,7 +14,7 @@ import { NoteBlockModel, NoteDisplayMode, type RootBlockModel, - type SurfaceRefBlockModel, + SurfaceRefBlockModel, } from '@blocksuite/affine-model'; import { FeatureFlagService } from '@blocksuite/affine-shared/services'; import { @@ -195,7 +195,7 @@ export class EdgelessAutoConnectWidget extends WidgetComponent { const pageVisibleBlocks = new Map(); const notes = service.doc.root?.children.filter(child => - matchFlavours(child, ['affine:note']) + matchFlavours(child, [NoteBlockModel]) ); const edgelessOnlyNotesSet = new Set(); @@ -209,7 +209,7 @@ export class EdgelessAutoConnectWidget extends WidgetComponent { } note.children.forEach(model => { - if (matchFlavours(model, ['affine:surface-ref'])) { + if (matchFlavours(model, [SurfaceRefBlockModel])) { const reference = this._crud.getElementById(model.reference); if (!isAutoConnectElement(reference)) return; diff --git a/blocksuite/affine/widget-remote-selection/src/doc/doc-remote-selection.ts b/blocksuite/affine/widget-remote-selection/src/doc/doc-remote-selection.ts index 339d5d8eac..d23d61a7da 100644 --- a/blocksuite/affine/widget-remote-selection/src/doc/doc-remote-selection.ts +++ b/blocksuite/affine/widget-remote-selection/src/doc/doc-remote-selection.ts @@ -1,4 +1,13 @@ +import { + AttachmentBlockModel, + BookmarkBlockModel, + CodeBlockModel, + DatabaseBlockModel, + ImageBlockModel, + SurfaceRefBlockModel, +} from '@blocksuite/affine-model'; import { getSelectionRectsCommand } from '@blocksuite/affine-shared/commands'; +import { EMBED_BLOCK_MODEL_LIST } from '@blocksuite/affine-shared/consts'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { BlockSelection, @@ -67,16 +76,15 @@ export class AffineDocRemoteSelectionWidget extends WidgetComponent { private get _config(): DocRemoteSelectionConfig { return { blockSelectionBackgroundTransparent: block => { - return ( - matchFlavours(block, [ - 'affine:code', - 'affine:database', - 'affine:image', - 'affine:attachment', - 'affine:bookmark', - 'affine:surface-ref', - ]) || /affine:embed-*/.test(block.flavour) - ); + return matchFlavours(block, [ + CodeBlockModel, + DatabaseBlockModel, + ImageBlockModel, + AttachmentBlockModel, + BookmarkBlockModel, + SurfaceRefBlockModel, + ...EMBED_BLOCK_MODEL_LIST, + ]); }, }; } diff --git a/blocksuite/blocks/src/_common/export-manager/export-manager.ts b/blocksuite/blocks/src/_common/export-manager/export-manager.ts index c0cd5a5d82..8652eb2989 100644 --- a/blocksuite/blocks/src/_common/export-manager/export-manager.ts +++ b/blocksuite/blocks/src/_common/export-manager/export-manager.ts @@ -3,7 +3,9 @@ import { SurfaceElementModel, } from '@blocksuite/affine-block-surface'; import { + FrameBlockModel, GroupElementModel, + ImageBlockModel, type RootBlockModel, } from '@blocksuite/affine-model'; import { FetchUtils } from '@blocksuite/affine-shared/adapters'; @@ -456,7 +458,7 @@ export class ExportManager { edgeless?.service.gfx.getElementsByBound(bound, { type: 'block' }) ?? []; for (const block of blocks) { - if (matchFlavours(block, ['affine:image'])) { + if (matchFlavours(block, [ImageBlockModel])) { if (!block.sourceId) return; const blob = await block.doc.blobSync.get(block.sourceId); @@ -493,7 +495,7 @@ export class ExportManager { ); } - if (matchFlavours(block, ['affine:frame'])) { + if (matchFlavours(block, [FrameBlockModel])) { // TODO(@L-Sun): use children of frame instead of bound const blocksInsideFrame = getBlocksInFrameBound(this.doc, block, false); const frameBound = Bound.deserialize(block.xywh); diff --git a/blocksuite/blocks/src/root-block/edgeless/clipboard/clipboard.ts b/blocksuite/blocks/src/root-block/edgeless/clipboard/clipboard.ts index cbe1dbf186..fcf7ef438d 100644 --- a/blocksuite/blocks/src/root-block/edgeless/clipboard/clipboard.ts +++ b/blocksuite/blocks/src/root-block/edgeless/clipboard/clipboard.ts @@ -11,6 +11,7 @@ import { BookmarkStyles, DEFAULT_NOTE_HEIGHT, DEFAULT_NOTE_WIDTH, + FrameBlockModel, MAX_IMAGE_WIDTH, ReferenceInfoSchema, } from '@blocksuite/affine-model'; @@ -994,7 +995,7 @@ export class EdgelessClipboardController extends PageClipboard { for (const nodeElement of nodeElements) { await _drawTopLevelBlock(nodeElement); - if (matchFlavours(nodeElement, ['affine:frame'])) { + if (matchFlavours(nodeElement, [FrameBlockModel])) { const blocksInsideFrame: BlockSuite.EdgelessBlockModelType[] = []; this.edgeless.service.frame .getElementsInFrameBound(nodeElement, false) diff --git a/blocksuite/blocks/src/root-block/edgeless/edgeless-keyboard.ts b/blocksuite/blocks/src/root-block/edgeless/edgeless-keyboard.ts index 23047fac69..c977cb447c 100644 --- a/blocksuite/blocks/src/root-block/edgeless/edgeless-keyboard.ts +++ b/blocksuite/blocks/src/root-block/edgeless/edgeless-keyboard.ts @@ -9,6 +9,7 @@ import { GroupElementModel, LayoutType, MindmapElementModel, + NoteBlockModel, NoteDisplayMode, type ShapeElementModel, } from '@blocksuite/affine-model'; @@ -134,7 +135,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager { selection.selectedElements.length === 1 && selection.firstElement instanceof GfxBlockElementModel && matchFlavours(selection.firstElement as GfxBlockElementModel, [ - 'affine:note', + NoteBlockModel, ]) ) { rootComponent.slots.toggleNoteSlicer.emit(); @@ -259,7 +260,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager { block => block.group === null && !( - matchFlavours(block, ['affine:note']) && + matchFlavours(block, [NoteBlockModel]) && block.displayMode === NoteDisplayMode.DocOnly ) ) diff --git a/blocksuite/blocks/src/root-block/edgeless/edgeless-root-block.ts b/blocksuite/blocks/src/root-block/edgeless/edgeless-root-block.ts index 10657b0cb6..30b9ffbc00 100644 --- a/blocksuite/blocks/src/root-block/edgeless/edgeless-root-block.ts +++ b/blocksuite/blocks/src/root-block/edgeless/edgeless-root-block.ts @@ -7,7 +7,7 @@ import { normalizeWheelDeltaY, } from '@blocksuite/affine-block-surface'; import { - type NoteBlockModel, + NoteBlockModel, NoteDisplayMode, type RootBlockModel, type ShapeElementModel, @@ -350,7 +350,7 @@ export class EdgelessRootBlockComponent extends BlockComponent< const primaryMode = std.get(DocModeProvider).getPrimaryMode(this.doc.id); const note = this.model.children.find( (child): child is NoteBlockModel => - matchFlavours(child, ['affine:note']) && + matchFlavours(child, [NoteBlockModel]) && child.displayMode !== NoteDisplayMode.EdgelessOnly ); diff --git a/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts b/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts index 7e58b59ca7..cf8f57c2b9 100644 --- a/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts +++ b/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts @@ -5,6 +5,7 @@ import { promptDocTitle, } from '@blocksuite/affine-block-embed'; import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph'; +import { NoteBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model'; import { draftSelectedModelsCommand, getSelectedModelsCommand, @@ -37,7 +38,7 @@ export class PageKeyboardManager { const model = block.model; if ( - matchFlavours(model, ['affine:paragraph']) && + matchFlavours(model, [ParagraphBlockModel]) && model.type.startsWith('h') && model.collapsed ) { @@ -133,7 +134,7 @@ export class PageKeyboardManager { const selectedModels = ctx.selectedModels?.filter( block => !block.flavour.startsWith('affine:embed-') && - matchFlavours(doc.getParent(block), ['affine:note']) + matchFlavours(doc.getParent(block), [NoteBlockModel]) ); const draftedModels = ctx.draftedModels; diff --git a/blocksuite/blocks/src/root-block/page/page-root-block.ts b/blocksuite/blocks/src/root-block/page/page-root-block.ts index 42bac3f24f..1c747949ea 100644 --- a/blocksuite/blocks/src/root-block/page/page-root-block.ts +++ b/blocksuite/blocks/src/root-block/page/page-root-block.ts @@ -1,6 +1,12 @@ import { focusTextModel } from '@blocksuite/affine-components/rich-text'; -import type { NoteBlockModel, RootBlockModel } from '@blocksuite/affine-model'; -import { NoteDisplayMode } from '@blocksuite/affine-model'; +import { + CodeBlockModel, + ListBlockModel, + NoteBlockModel, + NoteDisplayMode, + ParagraphBlockModel, + type RootBlockModel, +} from '@blocksuite/affine-model'; import { PageViewportService } from '@blocksuite/affine-shared/services'; import type { Viewport } from '@blocksuite/affine-shared/types'; import { @@ -122,7 +128,11 @@ export class PageRootBlockComponent extends BlockComponent< focusFirstParagraph = (): { id: string; created: boolean } => { const defaultNote = this._getDefaultNoteBlock(); const firstText = defaultNote?.children.find(block => - matchFlavours(block, ['affine:paragraph', 'affine:list', 'affine:code']) + matchFlavours(block, [ + ParagraphBlockModel, + ListBlockModel, + CodeBlockModel, + ]) ); if (firstText) { focusTextModel(this.std, firstText.id); @@ -238,9 +248,8 @@ export class PageRootBlockComponent extends BlockComponent< 'Mod-a': () => { const blocks = this.model.children .filter(model => { - if (matchFlavours(model, ['affine:note'])) { - const note = model as NoteBlockModel; - if (note.displayMode === NoteDisplayMode.EdgelessOnly) + if (matchFlavours(model, [NoteBlockModel])) { + if (model.displayMode === NoteDisplayMode.EdgelessOnly) return false; return true; @@ -385,12 +394,11 @@ export class PageRootBlockComponent extends BlockComponent< .slice() .reverse() .find(child => { - const isNote = matchFlavours(child, ['affine:note']); + const isNote = matchFlavours(child, [NoteBlockModel]); if (!isNote) return false; - const note = child as NoteBlockModel; const displayOnDoc = - !!note.displayMode && - note.displayMode !== NoteDisplayMode.EdgelessOnly; + !!child.displayMode && + child.displayMode !== NoteDisplayMode.EdgelessOnly; return displayOnDoc; }); if (!lastNote) { @@ -402,7 +410,9 @@ export class PageRootBlockComponent extends BlockComponent< const last = lastNote.children.at(-1); if ( !last || - !(matchFlavours(last, ['affine:paragraph']) && last.text.length === 0) + !( + matchFlavours(last, [ParagraphBlockModel]) && last.text.length === 0 + ) ) { if (readonly) return; const paragraphId = this.doc.addBlock( @@ -442,7 +452,7 @@ export class PageRootBlockComponent extends BlockComponent< override firstUpdated() { this._initViewportResizeEffect(); const noteModels = this.model.children.filter(model => - matchFlavours(model, ['affine:note']) + matchFlavours(model, [NoteBlockModel]) ); noteModels.forEach(note => { this.disposables.add( @@ -463,7 +473,7 @@ export class PageRootBlockComponent extends BlockComponent< )}`; const children = this.renderChildren(this.model, child => { - const isNote = matchFlavours(child, ['affine:note']); + const isNote = matchFlavours(child, [NoteBlockModel]); const note = child as NoteBlockModel; const displayOnEdgeless = !!note.displayMode && note.displayMode === NoteDisplayMode.EdgelessOnly; diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-frame-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-frame-button.ts index bd594cd1a3..08d724263b 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-frame-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-frame-button.ts @@ -19,6 +19,7 @@ import { DEFAULT_NOTE_HEIGHT, DefaultTheme, type FrameBlockModel, + NoteBlockModel, NoteDisplayMode, resolveColor, } from '@blocksuite/affine-model'; @@ -92,7 +93,7 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) { const rootModel = this.edgeless.doc.root; const notes = rootModel.children.filter( model => - matchFlavours(model, ['affine:note']) && + matchFlavours(model, [NoteBlockModel]) && model.displayMode !== NoteDisplayMode.EdgelessOnly ); const lastNote = notes[notes.length - 1]; diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-group-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-group-button.ts index 58e4340699..cdf7210734 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-group-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-group-button.ts @@ -6,7 +6,11 @@ import { import { toast } from '@blocksuite/affine-components/toast'; import { renderToolbarSeparator } from '@blocksuite/affine-components/toolbar'; import type { GroupElementModel } from '@blocksuite/affine-model'; -import { DEFAULT_NOTE_HEIGHT, NoteDisplayMode } from '@blocksuite/affine-model'; +import { + DEFAULT_NOTE_HEIGHT, + NoteBlockModel, + NoteDisplayMode, +} from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { deserializeXYWH, @@ -27,7 +31,7 @@ export class EdgelessChangeGroupButton extends WithDisposable(LitElement) { const rootModel = this.edgeless.doc.root; const notes = rootModel.children.filter( model => - matchFlavours(model, ['affine:note']) && + matchFlavours(model, [NoteBlockModel]) && model.displayMode !== NoteDisplayMode.EdgelessOnly ); const lastNote = notes[notes.length - 1]; diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts index 6963a1b89f..e42f11bd05 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts @@ -24,7 +24,7 @@ import { import { type ColorScheme, DefaultTheme, - type NoteBlockModel, + NoteBlockModel, NoteDisplayMode, resolveColor, type StrokeStyle, @@ -158,7 +158,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { this._pageBlockEnabled && this.notes.length === 1 && this.notes[0].parent?.children.find(child => - matchFlavours(child, ['affine:note']) + matchFlavours(child, [NoteBlockModel]) ) === this.notes[0] ); } @@ -340,7 +340,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { const isFirstNote = onlyOne && note.parent?.children.find(child => - matchFlavours(child, ['affine:note']) + matchFlavours(child, [NoteBlockModel]) ) === note; const theme = this.edgeless.std.get(ThemeProvider).theme; const buttons = [ diff --git a/blocksuite/blocks/src/root-block/widgets/format-bar/format-bar.ts b/blocksuite/blocks/src/root-block/widgets/format-bar/format-bar.ts index f4aba14528..637e60d764 100644 --- a/blocksuite/blocks/src/root-block/widgets/format-bar/format-bar.ts +++ b/blocksuite/blocks/src/root-block/widgets/format-bar/format-bar.ts @@ -10,6 +10,12 @@ import { getMoreMenuConfig, type MenuItemGroup, } from '@blocksuite/affine-components/toolbar'; +import { + CodeBlockModel, + ImageBlockModel, + ListBlockModel, + ParagraphBlockModel, +} from '@blocksuite/affine-model'; import { getSelectedBlocksCommand, getTextSelectionCommand, @@ -355,10 +361,10 @@ export class AffineFormatBarWidget extends WidgetComponent { const selectedBlock = this._selectedBlocks[0]; if ( !matchFlavours(selectedBlock.model, [ - 'affine:paragraph', - 'affine:list', - 'affine:code', - 'affine:image', + ParagraphBlockModel, + ListBlockModel, + CodeBlockModel, + ImageBlockModel, ]) ) { return false; diff --git a/blocksuite/blocks/src/root-block/widgets/linked-doc/config.ts b/blocksuite/blocks/src/root-block/widgets/linked-doc/config.ts index 3840d48d9e..4f38cd1cfc 100644 --- a/blocksuite/blocks/src/root-block/widgets/linked-doc/config.ts +++ b/blocksuite/blocks/src/root-block/widgets/linked-doc/config.ts @@ -35,7 +35,7 @@ export interface LinkedWidgetConfig { * [[ -> @ */ convertTriggerKey: boolean; - ignoreBlockTypes: (keyof BlockSuite.BlockModels)[]; + ignoreBlockTypes: string[]; ignoreSelector: string; getMenus: ( query: string, diff --git a/blocksuite/blocks/src/root-block/widgets/page-dragging-area/page-dragging-area.ts b/blocksuite/blocks/src/root-block/widgets/page-dragging-area/page-dragging-area.ts index 098dcca856..1577c5e9fc 100644 --- a/blocksuite/blocks/src/root-block/widgets/page-dragging-area/page-dragging-area.ts +++ b/blocksuite/blocks/src/root-block/widgets/page-dragging-area/page-dragging-area.ts @@ -1,4 +1,4 @@ -import type { RootBlockModel } from '@blocksuite/affine-model'; +import { NoteBlockModel, RootBlockModel } from '@blocksuite/affine-model'; import { autoScroll, getScrollContainer, @@ -445,7 +445,7 @@ function isDragArea(e: PointerEventState) { const el = e.raw.target; assertInstanceOf(el, Element); const block = el.closest(`[${BLOCK_ID_ATTR}]`); - return block && matchFlavours(block.model, ['affine:page', 'affine:note']); + return block && matchFlavours(block.model, [RootBlockModel, NoteBlockModel]); } declare global { diff --git a/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts b/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts index b58f9263cb..3d0d3efae8 100644 --- a/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts +++ b/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts @@ -84,7 +84,7 @@ import { export type SlashMenuConfig = { triggerKeys: string[]; - ignoreBlockTypes: BlockSuite.Flavour[]; + ignoreBlockTypes: string[]; items: SlashMenuItem[]; maxHeight: number; tooltipTimeout: number; diff --git a/blocksuite/blocks/src/root-block/widgets/slash-menu/index.ts b/blocksuite/blocks/src/root-block/widgets/slash-menu/index.ts index e083653995..4d9b0c7a6e 100644 --- a/blocksuite/blocks/src/root-block/widgets/slash-menu/index.ts +++ b/blocksuite/blocks/src/root-block/widgets/slash-menu/index.ts @@ -2,10 +2,7 @@ import { type AffineInlineEditor, getInlineEditorByModel, } from '@blocksuite/affine-components/rich-text'; -import { - getCurrentNativeRange, - matchFlavours, -} from '@blocksuite/affine-shared/utils'; +import { getCurrentNativeRange } from '@blocksuite/affine-shared/utils'; import type { UIEventStateContext } from '@blocksuite/block-std'; import { TextSelection, WidgetComponent } from '@blocksuite/block-std'; import { @@ -155,7 +152,7 @@ export class AffineSlashMenuWidget extends WidgetComponent { const model = this.host.doc.getBlock(textSelection.blockId)?.model; if (!model) return; - if (matchFlavours(model, this.config.ignoreBlockTypes)) return; + if (this.config.ignoreBlockTypes.includes(model.flavour)) return; const inlineRange = inlineEditor.getInlineRange(); if (!inlineRange) return; diff --git a/blocksuite/framework/block-std/src/__tests__/test-schema.ts b/blocksuite/framework/block-std/src/__tests__/test-schema.ts index 7285f9ce10..e97e43e9ed 100644 --- a/blocksuite/framework/block-std/src/__tests__/test-schema.ts +++ b/blocksuite/framework/block-std/src/__tests__/test-schema.ts @@ -1,4 +1,4 @@ -import { defineBlockSchema, type SchemaToModel } from '@blocksuite/store'; +import { BlockModel, defineBlockSchema } from '@blocksuite/store'; export const RootBlockSchema = defineBlockSchema({ flavour: 'test:page', @@ -15,7 +15,9 @@ export const RootBlockSchema = defineBlockSchema({ }, }); -export type RootBlockModel = SchemaToModel; +export class RootBlockModel extends BlockModel< + ReturnType<(typeof RootBlockSchema)['model']['props']> +> {} export const NoteBlockSchema = defineBlockSchema({ flavour: 'test:note', @@ -28,7 +30,9 @@ export const NoteBlockSchema = defineBlockSchema({ }, }); -export type NoteBlockModel = SchemaToModel; +export class NoteBlockModel extends BlockModel< + ReturnType<(typeof NoteBlockSchema)['model']['props']> +> {} export const HeadingBlockSchema = defineBlockSchema({ flavour: 'test:heading', @@ -43,7 +47,9 @@ export const HeadingBlockSchema = defineBlockSchema({ }, }); -export type HeadingBlockModel = SchemaToModel; +export class HeadingBlockModel extends BlockModel< + ReturnType<(typeof HeadingBlockSchema)['model']['props']> +> {} declare global { namespace BlockSuite { diff --git a/blocksuite/framework/store/src/__tests__/block.unit.spec.ts b/blocksuite/framework/store/src/__tests__/block.unit.spec.ts index 761c27c507..7c2bc5a4c0 100644 --- a/blocksuite/framework/store/src/__tests__/block.unit.spec.ts +++ b/blocksuite/framework/store/src/__tests__/block.unit.spec.ts @@ -4,9 +4,9 @@ import * as Y from 'yjs'; import { Block, + BlockModel, defineBlockSchema, internalPrimitives, - type SchemaToModel, } from '../model/block/index.js'; import type { YBlock } from '../model/block/types.js'; import { Schema } from '../schema/index.js'; @@ -54,9 +54,15 @@ const flatTableSchema = defineBlockSchema({ isFlatData: true, }, }); -type RootModel = SchemaToModel; -type TableModel = SchemaToModel; -type FlatTableModel = SchemaToModel; +class RootModel extends BlockModel< + ReturnType<(typeof pageSchema)['model']['props']> +> {} +class TableModel extends BlockModel< + ReturnType<(typeof tableSchema)['model']['props']> +> {} +class FlatTableModel extends BlockModel< + ReturnType<(typeof flatTableSchema)['model']['props']> +> {} function createTestOptions() { const idGenerator = createAutoIncrementIdGenerator(); diff --git a/blocksuite/framework/store/src/__tests__/test-schema.ts b/blocksuite/framework/store/src/__tests__/test-schema.ts index 2a132ddad0..4d91ab8672 100644 --- a/blocksuite/framework/store/src/__tests__/test-schema.ts +++ b/blocksuite/framework/store/src/__tests__/test-schema.ts @@ -1,4 +1,4 @@ -import { defineBlockSchema, type SchemaToModel } from '../model/index.js'; +import { BlockModel, defineBlockSchema } from '../model/index.js'; export const RootBlockSchema = defineBlockSchema({ flavour: 'affine:page', @@ -14,7 +14,9 @@ export const RootBlockSchema = defineBlockSchema({ }, }); -export type RootBlockModel = SchemaToModel; +export class RootBlockModel extends BlockModel< + ReturnType<(typeof RootBlockSchema)['model']['props']> +> {} export const NoteBlockSchema = defineBlockSchema({ flavour: 'affine:note', diff --git a/blocksuite/framework/store/src/__tests__/transformer.unit.spec.ts b/blocksuite/framework/store/src/__tests__/transformer.unit.spec.ts index a5a634ab2f..33af32fdcc 100644 --- a/blocksuite/framework/store/src/__tests__/transformer.unit.spec.ts +++ b/blocksuite/framework/store/src/__tests__/transformer.unit.spec.ts @@ -2,8 +2,8 @@ import { expect, test } from 'vitest'; import * as Y from 'yjs'; import { MemoryBlobCRUD } from '../adapter/index.js'; -import type { BlockModel } from '../model/block/block-model.js'; -import { defineBlockSchema, type SchemaToModel } from '../model/block/zod.js'; +import { BlockModel } from '../model/block/block-model.js'; +import { defineBlockSchema } from '../model/block/zod.js'; import { Text } from '../reactive/index.js'; import { Schema } from '../schema/index.js'; import { createAutoIncrementIdGenerator } from '../test/index.js'; @@ -39,7 +39,9 @@ const docSchema = defineBlockSchema({ }, }); -type RootBlockModel = SchemaToModel; +class RootBlockModel extends BlockModel< + ReturnType<(typeof docSchema)['model']['props']> +> {} function createTestOptions() { const idGenerator = createAutoIncrementIdGenerator(); diff --git a/blocksuite/framework/store/src/model/block/block-model.ts b/blocksuite/framework/store/src/model/block/block-model.ts index 16ff97f2da..384fbffc7a 100644 --- a/blocksuite/framework/store/src/model/block/block-model.ts +++ b/blocksuite/framework/store/src/model/block/block-model.ts @@ -20,9 +20,7 @@ type SignaledProps = Props & { * myBlock.foo = 'bar'; * ``` */ -function MagicProps(): { - new (): Props; -} { +function MagicProps(): { new (): Props } { return class {} as never; } diff --git a/blocksuite/framework/store/src/model/block/zod.ts b/blocksuite/framework/store/src/model/block/zod.ts index 67666cd94e..22fbd45597 100644 --- a/blocksuite/framework/store/src/model/block/zod.ts +++ b/blocksuite/framework/store/src/model/block/zod.ts @@ -51,18 +51,6 @@ export type PropsGetter = ( internalPrimitives: InternalPrimitives ) => Props; -export type SchemaToModel< - Schema extends { - model: { - props: PropsGetter; - flavour: string; - }; - }, -> = BlockModel> & - ReturnType & { - flavour: Schema['model']['flavour']; - }; - export function defineBlockSchema< Flavour extends string, Role extends RoleType, diff --git a/blocksuite/framework/store/src/model/store/store.ts b/blocksuite/framework/store/src/model/store/store.ts index cc10aed1ae..71668ff54d 100644 --- a/blocksuite/framework/store/src/model/store/store.ts +++ b/blocksuite/framework/store/src/model/store/store.ts @@ -451,20 +451,6 @@ export class Store { } } - addBlock( - flavour: Key, - blockProps?: BlockSuite.ModelProps, - parent?: BlockModel | string | null, - parentIndex?: number - ): string; - - addBlock( - flavour: never, - blockProps?: Partial>, - parent?: BlockModel | string | null, - parentIndex?: number - ): string; - addBlock( flavour: string, blockProps: Partial> = {}, diff --git a/blocksuite/presets/src/fragments/outline/body/outline-panel-body.ts b/blocksuite/presets/src/fragments/outline/body/outline-panel-body.ts index 9d98c3d7fa..c9e367eb4c 100644 --- a/blocksuite/presets/src/fragments/outline/body/outline-panel-body.ts +++ b/blocksuite/presets/src/fragments/outline/body/outline-panel-body.ts @@ -1,9 +1,9 @@ import { effects } from '@blocksuite/affine-block-note/effects'; import { ShadowlessElement, SurfaceSelection } from '@blocksuite/block-std'; -import type { NoteBlockModel } from '@blocksuite/blocks'; import { changeNoteDisplayMode, matchFlavours, + NoteBlockModel, NoteDisplayMode, } from '@blocksuite/blocks'; import { @@ -155,7 +155,7 @@ export class OutlinePanelBody extends SignalWatcher( const noteIndex = new Map(); children.forEach((block, index) => { - if (matchFlavours(block, ['affine:note'])) { + if (matchFlavours(block, [NoteBlockModel])) { noteIndex.set(block, index); } }); @@ -171,7 +171,7 @@ export class OutlinePanelBody extends SignalWatcher( if (targetIndex === null) return; const removeSelectedNoteFilter = (block: BlockModel) => - !matchFlavours(block, ['affine:note']) || !selected.includes(block); + !matchFlavours(block, [NoteBlockModel]) || !selected.includes(block); const leftPart = children .slice(0, targetIndex) @@ -200,7 +200,7 @@ export class OutlinePanelBody extends SignalWatcher( private _selectNote(e: SelectEvent) { const { selected, id, multiselect } = e.detail; const note = this.doc.getBlock(id)?.model; - if (!note || !matchFlavours(note, ['affine:note'])) return; + if (!note || !matchFlavours(note, [NoteBlockModel])) return; let selectedNotes = this._selectedNotes$.peek(); @@ -231,7 +231,7 @@ export class OutlinePanelBody extends SignalWatcher( .filter(SurfaceSelection) .map(({ blockId }) => doc.getBlock(blockId)?.model) .filter(model => { - return !!model && matchFlavours(model, ['affine:note']); + return !!model && matchFlavours(model, [NoteBlockModel]); }); const preSelected = this._selectedNotes$.peek(); diff --git a/blocksuite/presets/src/fragments/outline/card/outline-preview.ts b/blocksuite/presets/src/fragments/outline/card/outline-preview.ts index 4941336f91..997c8ee987 100644 --- a/blocksuite/presets/src/fragments/outline/card/outline-preview.ts +++ b/blocksuite/presets/src/fragments/outline/card/outline-preview.ts @@ -122,7 +122,7 @@ export class OutlineBlockPreview extends SignalWatcher( const showPreviewIcon = this._context.showIcons$.value; - switch (block.flavour as keyof BlockSuite.BlockModels) { + switch (block.flavour) { case 'affine:page': assertType(block); return block.title.length > 0 diff --git a/blocksuite/presets/src/fragments/outline/mobile-outline-panel.ts b/blocksuite/presets/src/fragments/outline/mobile-outline-panel.ts index 88ebc36631..60b3da27e6 100644 --- a/blocksuite/presets/src/fragments/outline/mobile-outline-panel.ts +++ b/blocksuite/presets/src/fragments/outline/mobile-outline-panel.ts @@ -1,6 +1,11 @@ import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme'; import { PropTypes, requiredProperties } from '@blocksuite/block-std'; -import { matchFlavours, NoteDisplayMode } from '@blocksuite/blocks'; +import { + matchFlavours, + NoteDisplayMode, + ParagraphBlockModel, + RootBlockModel, +} from '@blocksuite/blocks'; import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils'; import type { BlockModel } from '@blocksuite/store'; import { signal } from '@preact/signals-core'; @@ -129,11 +134,11 @@ export class MobileOutlineMenu extends SignalWatcher( renderItem = (item: BlockModel) => { let className = ''; let text = ''; - if (matchFlavours(item, ['affine:page'])) { + if (matchFlavours(item, [RootBlockModel])) { className = 'title'; text = item.title$.value.toString(); } else if ( - matchFlavours(item, ['affine:paragraph']) && + matchFlavours(item, [ParagraphBlockModel]) && ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(item.type$.value) ) { className = item.type$.value; diff --git a/blocksuite/presets/src/fragments/outline/utils/query.ts b/blocksuite/presets/src/fragments/outline/utils/query.ts index 4b7cb36651..69a6a876f5 100644 --- a/blocksuite/presets/src/fragments/outline/utils/query.ts +++ b/blocksuite/presets/src/fragments/outline/utils/query.ts @@ -1,10 +1,10 @@ import { BlocksUtils, matchFlavours, - type NoteBlockModel, + NoteBlockModel, NoteDisplayMode, - type ParagraphBlockModel, - type RootBlockModel, + ParagraphBlockModel, + RootBlockModel, } from '@blocksuite/blocks'; import type { BlockModel, Store } from '@blocksuite/store'; @@ -24,7 +24,7 @@ export function getNotesFromDoc( const notes: NoteBlockModel[] = []; rootModel.children.forEach(block => { - if (!matchFlavours(block, ['affine:note'])) return; + if (!matchFlavours(block, [NoteBlockModel])) return; if (modes.includes(block.displayMode$.value)) { notes.push(block); @@ -35,14 +35,14 @@ export function getNotesFromDoc( } export function isRootBlock(block: BlockModel): block is RootBlockModel { - return BlocksUtils.matchFlavours(block, ['affine:page']); + return BlocksUtils.matchFlavours(block, [RootBlockModel]); } export function isHeadingBlock( block: BlockModel ): block is ParagraphBlockModel { return ( - BlocksUtils.matchFlavours(block, ['affine:paragraph']) && + BlocksUtils.matchFlavours(block, [ParagraphBlockModel]) && headingKeys.has(block.type$.value) ); } diff --git a/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts b/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts index b604f9381d..9fe390bf99 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts @@ -17,6 +17,7 @@ import { getSelectedBlocksCommand, NoteDisplayMode, NotificationProvider, + ParagraphBlockModel, RefNodeSlotsProvider, TelemetryProvider, } from '@blocksuite/affine/blocks'; @@ -166,7 +167,7 @@ function addAIChatBlock( const y = viewportCenter.y - height / 2; const bound = new Bound(x, y, width, height); const aiChatBlockId = doc.addBlock( - 'affine:embed-ai-chat' as keyof BlockSuite.BlockModels, + 'affine:embed-ai-chat', { xywh: bound.serialize(), messages: JSON.stringify(messages), @@ -231,7 +232,7 @@ const REPLACE_SELECTION = { if (currentTextSelection) { const { doc } = host; const block = doc.getBlock(currentTextSelection.blockId); - if (matchFlavours(block?.model ?? null, ['affine:paragraph'])) { + if (matchFlavours(block?.model ?? null, [ParagraphBlockModel])) { block?.model.text?.replace( currentTextSelection.from.index, currentTextSelection.from.length, diff --git a/packages/frontend/core/src/blocksuite/presets/ai/_common/config.ts b/packages/frontend/core/src/blocksuite/presets/ai/_common/config.ts index e285999fe3..be7a63aec1 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/_common/config.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/_common/config.ts @@ -2,8 +2,12 @@ import type { Chain, InitCommandCtx } from '@blocksuite/affine/block-std'; import { type AIItemGroupConfig, type AISubItemConfig, + CodeBlockModel, getSelectedModelsCommand, + ImageBlockModel, + ListBlockModel, matchFlavours, + ParagraphBlockModel, } from '@blocksuite/affine/blocks'; import { actionToHandler } from '../actions/doc-handler'; @@ -103,7 +107,7 @@ const textBlockShowWhen = (chain: Chain) => { if (!selectedModels || selectedModels.length === 0) return false; return selectedModels.some(model => - matchFlavours(model, ['affine:paragraph', 'affine:list']) + matchFlavours(model, [ParagraphBlockModel, ListBlockModel]) ); }; @@ -117,7 +121,7 @@ const codeBlockShowWhen = (chain: Chain) => { if (!selectedModels || selectedModels.length > 1) return false; const model = selectedModels[0]; - return matchFlavours(model, ['affine:code']); + return matchFlavours(model, [CodeBlockModel]); }; const imageBlockShowWhen = (chain: Chain) => { @@ -130,7 +134,7 @@ const imageBlockShowWhen = (chain: Chain) => { if (!selectedModels || selectedModels.length > 1) return false; const model = selectedModels[0]; - return matchFlavours(model, ['affine:image']); + return matchFlavours(model, [ImageBlockModel]); }; const EditAIGroup: AIItemGroupConfig = { @@ -278,7 +282,7 @@ const GenerateWithAIGroup: AIItemGroupConfig = { return selectedModels.every( model => - matchFlavours(model, ['affine:paragraph', 'affine:list']) && + matchFlavours(model, [ParagraphBlockModel, ListBlockModel]) && !model.type.startsWith('h') ); }, diff --git a/packages/frontend/core/src/blocksuite/presets/ai/actions/edgeless-handler.ts b/packages/frontend/core/src/blocksuite/presets/ai/actions/edgeless-handler.ts index 29d3155c11..e8eaae8b2a 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/actions/edgeless-handler.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/actions/edgeless-handler.ts @@ -7,6 +7,7 @@ import type { } from '@blocksuite/affine/blocks'; import { BlocksUtils, + CodeBlockModel, EdgelessTextBlockModel, EmbedSyncedDocModel, ImageBlockModel, @@ -485,7 +486,7 @@ export function noteWithCodeBlockShowWen( return ( selected[0] instanceof NoteBlockModel && selected[0].children.length === 1 && - BlocksUtils.matchFlavours(selected[0].children[0], ['affine:code']) + BlocksUtils.matchFlavours(selected[0].children[0], [CodeBlockModel]) ); } diff --git a/packages/frontend/core/src/blocksuite/presets/ai/actions/edgeless-response.ts b/packages/frontend/core/src/blocksuite/presets/ai/actions/edgeless-response.ts index 30a30c2e1b..1932df7cb1 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/actions/edgeless-response.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/actions/edgeless-response.ts @@ -527,7 +527,7 @@ async function responseToCreateSlides(host: EditorHost, ctx: AIContext) { await job.insertTemplate(content); } - getSurfaceElementFromEditor(host).refresh(); + getSurfaceElementFromEditor(host)?.refresh(); } catch (error) { console.error('Error creating slides:', error); } diff --git a/packages/frontend/core/src/blocksuite/presets/ai/ai-panel.ts b/packages/frontend/core/src/blocksuite/presets/ai/ai-panel.ts index a0d979ade2..332ba9f7bc 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/ai-panel.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/ai-panel.ts @@ -7,6 +7,7 @@ import { ImageBlockModel, isInsideEdgelessEditor, matchFlavours, + NoteBlockModel, NoteDisplayMode, } from '@blocksuite/affine/blocks'; import { assertExists, Bound } from '@blocksuite/affine/global/utils'; @@ -115,7 +116,7 @@ function createNewNote(host: EditorHost): AIItemConfig { // set the viewport to show the new note block and original note block const newNote = doc.getBlock(noteBlockId)?.model; - if (!newNote || !matchFlavours(newNote, ['affine:note'])) return; + if (!newNote || !matchFlavours(newNote, [NoteBlockModel])) return; const newNoteBound = Bound.deserialize(newNote.xywh); const bounds = [bound, newNoteBound]; service.gfx.fitToScreen({ diff --git a/packages/frontend/core/src/blocksuite/presets/ai/peek-view/chat-block-peek-view.ts b/packages/frontend/core/src/blocksuite/presets/ai/peek-view/chat-block-peek-view.ts index 9960d0badb..65352c4e0b 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/peek-view/chat-block-peek-view.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/peek-view/chat-block-peek-view.ts @@ -175,7 +175,7 @@ export class AIChatBlockPeekView extends LitElement { const edgelessService = this._rootService as EdgelessRootService; const bound = calcChildBound(this.parentModel, edgelessService); const aiChatBlockId = edgelessService.crud.addBlock( - 'affine:embed-ai-chat' as keyof BlockSuite.BlockModels, + 'affine:embed-ai-chat', { xywh: bound.serialize(), messages: JSON.stringify(messages), diff --git a/packages/frontend/core/src/blocksuite/presets/ai/slides/index.ts b/packages/frontend/core/src/blocksuite/presets/ai/slides/index.ts index 57a2c937be..747db2ef59 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/slides/index.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/slides/index.ts @@ -51,7 +51,7 @@ export const PPTBuilder = (host: EditorHost) => { ); } await job.insertTemplate(content); - getSurfaceElementFromEditor(host).refresh(); + getSurfaceElementFromEditor(host)?.refresh(); }; return { diff --git a/packages/frontend/core/src/blocksuite/presets/ai/utils/edgeless.ts b/packages/frontend/core/src/blocksuite/presets/ai/utils/edgeless.ts index f3d4e20da3..4f0c3a242a 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/utils/edgeless.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/utils/edgeless.ts @@ -5,7 +5,10 @@ import { type EdgelessRootService, matchFlavours, MindmapElementModel, + NoteBlockModel, + RootBlockModel, type ShapeElementModel, + SurfaceBlockModel, } from '@blocksuite/affine/blocks'; export function mindMapToMarkdown(mindmap: MindmapElementModel) { @@ -62,10 +65,10 @@ export function getEdgelessCopilotWidget( export function findNoteBlockModel(blockElement: BlockComponent) { let curBlock = blockElement; while (curBlock) { - if (matchFlavours(curBlock.model, ['affine:note'])) { + if (matchFlavours(curBlock.model, [NoteBlockModel])) { return curBlock.model; } - if (matchFlavours(curBlock.model, ['affine:page', 'affine:surface'])) { + if (matchFlavours(curBlock.model, [RootBlockModel, SurfaceBlockModel])) { return null; } if (!curBlock.parentComponent) { diff --git a/packages/frontend/core/src/blocksuite/presets/ai/utils/extract.ts b/packages/frontend/core/src/blocksuite/presets/ai/utils/extract.ts index 638a4f42aa..20edefc627 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/utils/extract.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/utils/extract.ts @@ -1,11 +1,12 @@ import type { EditorHost } from '@blocksuite/affine/block-std'; import { BlocksUtils, + DatabaseBlockModel, DocModeProvider, embedSyncedDocMiddleware, getImageSelectionsCommand, getSelectedBlocksCommand, - type ImageBlockModel, + ImageBlockModel, isInsideEdgelessEditor, MarkdownAdapter, type NoteBlockModel, @@ -163,7 +164,7 @@ export async function extractMarkdownFromDoc( const blockModels = getNoteBlockModels(doc); const textModels = blockModels.filter( model => - !BlocksUtils.matchFlavours(model, ['affine:image', 'affine:database']) + !BlocksUtils.matchFlavours(model, [ImageBlockModel, DatabaseBlockModel]) ); const drafts = textModels.map(toDraftModel); const slice = Slice.fromModels(doc, drafts); diff --git a/packages/frontend/core/src/blocksuite/presets/ai/utils/selection-utils.ts b/packages/frontend/core/src/blocksuite/presets/ai/utils/selection-utils.ts index de9aa4508d..a712bf2fd1 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/utils/selection-utils.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/utils/selection-utils.ts @@ -3,6 +3,7 @@ import type { GfxModel } from '@blocksuite/affine/block-std/gfx'; import { BlocksUtils, type CopilotTool, + DatabaseBlockModel, EdgelessRootService, type FrameBlockModel, getBlockSelectionsCommand, @@ -13,7 +14,6 @@ import { ImageBlockModel, type SurfaceBlockComponent, } from '@blocksuite/affine/blocks'; -import { assertExists } from '@blocksuite/affine/global/utils'; import { type BlockModel, type DraftModel, @@ -134,7 +134,7 @@ export async function getTextContentFromBlockModels( // Currently only filter out images and databases const selectedTextModels = models.filter( model => - !BlocksUtils.matchFlavours(model, ['affine:image', 'affine:database']) + !BlocksUtils.matchFlavours(model, [ImageBlockModel, DatabaseBlockModel]) ); const drafts = selectedTextModels.map(toDraftModel); drafts.forEach(draft => traverse(draft, drafts)); @@ -147,13 +147,13 @@ export async function getSelectedTextContent( type: 'markdown' | 'plain-text' = 'markdown' ) { const selectedModels = getSelectedModels(editorHost); - assertExists(selectedModels); + if (!selectedModels) return ''; return getTextContentFromBlockModels(editorHost, selectedModels, type); } export async function selectAboveBlocks(editorHost: EditorHost, num = 10) { let selectedModels = getSelectedModels(editorHost); - assertExists(selectedModels); + if (!selectedModels) return ''; const lastLeafModel = selectedModels[selectedModels.length - 1]; @@ -163,8 +163,7 @@ export async function selectAboveBlocks(editorHost: EditorHost, num = 10) { lastRootModel = noteModel; noteModel = editorHost.doc.getParent(noteModel); } - assertExists(noteModel); - assertExists(lastRootModel); + if (!noteModel || !lastRootModel) return ''; const endIndex = noteModel.children.indexOf(lastRootModel) + 1; const startIndex = Math.max(0, endIndex - num); @@ -212,13 +211,13 @@ export const stopPropagation = (e: Event) => { export function getSurfaceElementFromEditor(editor: EditorHost) { const { doc } = editor; const surfaceModel = doc.getBlockByFlavour('affine:surface')[0]; - assertExists(surfaceModel); + if (!surfaceModel) return null; const surfaceId = surfaceModel.id; const surfaceElement = editor.querySelector( `affine-surface[data-block-id="${surfaceId}"]` ) as SurfaceBlockComponent; - assertExists(surfaceElement); + if (!surfaceElement) return null; return surfaceElement; } diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/widgets/edgeless-note-header.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/widgets/edgeless-note-header.tsx index 4096971f83..d9c19b07ec 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/widgets/edgeless-note-header.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/widgets/edgeless-note-header.tsx @@ -11,7 +11,7 @@ import { track } from '@affine/track'; import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx'; import { matchFlavours, - type NoteBlockModel, + NoteBlockModel, NoteDisplayMode, } from '@blocksuite/affine/blocks'; import { Bound } from '@blocksuite/affine/global/utils'; @@ -176,7 +176,7 @@ export const EdgelessNoteHeader = ({ note }: { note: NoteBlockModel }) => { const isFirstVisibleNote = note.parent?.children.find( child => - matchFlavours(child, ['affine:note']) && + matchFlavours(child, [NoteBlockModel]) && child.displayMode === NoteDisplayMode.DocAndEdgeless ) === note;