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]);
```
This commit is contained in:
Saul-Mirone
2025-02-11 08:18:57 +00:00
parent 64bb6c5a71
commit 652865c7cf
97 changed files with 492 additions and 323 deletions

View File

@@ -1,3 +1,4 @@
import { SurfaceBlockModel } from '@blocksuite/affine-block-surface';
import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator'; import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator';
import { AttachmentBlockSchema } from '@blocksuite/affine-model'; import { AttachmentBlockSchema } from '@blocksuite/affine-model';
import { import {
@@ -23,7 +24,7 @@ export const AttachmentDropOption = FileDropConfigExtension({
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize; const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
if (targetModel && !matchFlavours(targetModel, ['affine:surface'])) { if (targetModel && !matchFlavours(targetModel, [SurfaceBlockModel])) {
addSiblingAttachmentBlocks( addSiblingAttachmentBlocks(
std.host, std.host,
attachmentFiles, attachmentFiles,

View File

@@ -1,6 +1,9 @@
import type { import {
DatabaseBlockModel, CodeBlockModel,
RootBlockModel, type DatabaseBlockModel,
ListBlockModel,
ParagraphBlockModel,
type RootBlockModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts'; import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
import { TelemetryProvider } from '@blocksuite/affine-shared/services'; import { TelemetryProvider } from '@blocksuite/affine-shared/services';
@@ -73,9 +76,9 @@ export class NoteRenderer
.find(child => child.flavour === 'affine:note') .find(child => child.flavour === 'affine:note')
?.children.find(block => ?.children.find(block =>
matchFlavours(block, [ matchFlavours(block, [
'affine:paragraph', ParagraphBlockModel,
'affine:list', ListBlockModel,
'affine:code', CodeBlockModel,
]) ])
); );
} }

View File

@@ -1,6 +1,10 @@
import { TextUtils } from '@blocksuite/affine-block-surface'; import { TextUtils } from '@blocksuite/affine-block-surface';
import { formatBlockCommand } from '@blocksuite/affine-components/rich-text'; 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 { ThemeProvider } from '@blocksuite/affine-shared/services';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std'; import type { BlockComponent } from '@blocksuite/block-std';
@@ -158,7 +162,7 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent<EdgelessTextBl
const firstChild = this.model.firstChild(); const firstChild = this.model.firstChild();
if ( if (
!firstChild || !firstChild ||
!matchFlavours(firstChild, ['affine:list', 'affine:paragraph']) !matchFlavours(firstChild, [ListBlockModel, ParagraphBlockModel])
) { ) {
newParagraphId = this.doc.addBlock( newParagraphId = this.doc.addBlock(
'affine:paragraph', 'affine:paragraph',
@@ -171,7 +175,7 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent<EdgelessTextBl
const lastChild = this.model.lastChild(); const lastChild = this.model.lastChild();
if ( if (
!lastChild || !lastChild ||
!matchFlavours(lastChild, ['affine:list', 'affine:paragraph']) !matchFlavours(lastChild, [ListBlockModel, ParagraphBlockModel])
) { ) {
newParagraphId = this.doc.addBlock( newParagraphId = this.doc.addBlock(
'affine:paragraph', 'affine:paragraph',

View File

@@ -1,8 +1,11 @@
import { getSurfaceBlock } from '@blocksuite/affine-block-surface'; import { getSurfaceBlock } from '@blocksuite/affine-block-surface';
import { import {
type DocMode, type DocMode,
type ImageBlockModel, ImageBlockModel,
ListBlockModel,
NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
ParagraphBlockModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts'; import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts';
import { NotificationProvider } from '@blocksuite/affine-shared/services'; import { NotificationProvider } from '@blocksuite/affine-shared/services';
@@ -68,7 +71,7 @@ async function renderPageAsBanner(card: EmbedSyncedDocCard) {
} }
const target = notes.flatMap(note => const target = notes.flatMap(note =>
note.children.filter(child => matchFlavours(child, ['affine:image'])) note.children.filter(child => matchFlavours(child, [ImageBlockModel]))
)[0]; )[0];
if (target) { if (target) {
@@ -135,9 +138,7 @@ async function renderNoteContent(
const cardStyle = card.model.style; const cardStyle = card.model.style;
const isHorizontal = cardStyle === 'horizontal'; const isHorizontal = cardStyle === 'horizontal';
const allowFlavours: (keyof BlockSuite.BlockModels)[] = isHorizontal const allowFlavours = isHorizontal ? [] : [ImageBlockModel];
? []
: ['affine:image'];
const noteChildren = notes.flatMap(note => const noteChildren = notes.flatMap(note =>
note.children.filter(model => { note.children.filter(model => {
@@ -214,7 +215,7 @@ async function renderNoteContent(
} }
function filterTextModel(model: BlockModel) { function filterTextModel(model: BlockModel) {
if (matchFlavours(model, ['affine:paragraph', 'affine:list'])) { if (matchFlavours(model, [ParagraphBlockModel, ListBlockModel])) {
return !!model.text?.toString().length; return !!model.text?.toString().length;
} }
return false; return false;
@@ -223,7 +224,7 @@ function filterTextModel(model: BlockModel) {
export function getNotesFromDoc(doc: Store) { export function getNotesFromDoc(doc: Store) {
const notes = doc.root?.children.filter( const notes = doc.root?.children.filter(
child => child =>
matchFlavours(child, ['affine:note']) && matchFlavours(child, [NoteBlockModel]) &&
child.displayMode !== NoteDisplayMode.EdgelessOnly child.displayMode !== NoteDisplayMode.EdgelessOnly
); );
@@ -304,7 +305,7 @@ export function getDocContentWithMaxLength(doc: Store, maxlength = 500) {
export function getTitleFromSelectedModels(selectedModels: DraftModel[]) { export function getTitleFromSelectedModels(selectedModels: DraftModel[]) {
const firstBlock = selectedModels[0]; const firstBlock = selectedModels[0];
if ( if (
matchFlavours(firstBlock, ['affine:paragraph']) && matchFlavours(firstBlock, [ParagraphBlockModel]) &&
firstBlock.type.startsWith('h') firstBlock.type.startsWith('h')
) { ) {
return firstBlock.text.toString(); return firstBlock.text.toString();

View File

@@ -1,3 +1,4 @@
import { SurfaceBlockModel } from '@blocksuite/affine-block-surface';
import { isPeekable, Peekable } from '@blocksuite/affine-components/peek'; import { isPeekable, Peekable } from '@blocksuite/affine-components/peek';
import { RefNodeSlotsProvider } from '@blocksuite/affine-components/rich-text'; import { RefNodeSlotsProvider } from '@blocksuite/affine-components/rich-text';
import type { import type {
@@ -373,7 +374,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
const isLoading = this._loading; const isLoading = this._loading;
const isError = this.isError; const isError = this.isError;
const isEmpty = this._isDocEmpty() && this.isBannerEmpty; const isEmpty = this._isDocEmpty() && this.isBannerEmpty;
const inCanvas = matchFlavours(this.model.parent, ['affine:surface']); const inCanvas = matchFlavours(this.model.parent, [SurfaceBlockModel]);
const cardClassMap = classMap({ const cardClassMap = classMap({
loading: isLoading, loading: isLoading,

View File

@@ -1,3 +1,4 @@
import { SurfaceBlockModel } from '@blocksuite/affine-block-surface';
import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator'; import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator';
import { ImageBlockSchema, MAX_IMAGE_WIDTH } from '@blocksuite/affine-model'; import { ImageBlockSchema, MAX_IMAGE_WIDTH } from '@blocksuite/affine-model';
import { import {
@@ -25,7 +26,7 @@ export const ImageDropOption = FileDropConfigExtension({
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize; const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
if (targetModel && !matchFlavours(targetModel, ['affine:surface'])) { if (targetModel && !matchFlavours(targetModel, [SurfaceBlockModel])) {
addSiblingImageBlock( addSiblingImageBlock(
std.host, std.host,
imageFiles, imageFiles,

View File

@@ -1,3 +1,4 @@
import { ListBlockModel } from '@blocksuite/affine-model';
import type { IndentContext } from '@blocksuite/affine-shared/types'; import type { IndentContext } from '@blocksuite/affine-shared/types';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import { type Command, TextSelection } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
@@ -53,7 +54,7 @@ export const canDedentListCommand: Command<
* ccc * ccc
*/ */
const model = store.getBlock(blockId)?.model; const model = store.getBlock(blockId)?.model;
if (!model || !matchFlavours(model, ['affine:list'])) { if (!model || !matchFlavours(model, [ListBlockModel])) {
return; return;
} }
/** /**

View File

@@ -1,3 +1,4 @@
import { ListBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model';
import type { IndentContext } from '@blocksuite/affine-shared/types'; import type { IndentContext } from '@blocksuite/affine-shared/types';
import { import {
getNearestHeadingBefore, getNearestHeadingBefore,
@@ -54,7 +55,7 @@ export const canIndentListCommand: Command<
* ccc * ccc
*/ */
const model = store.getBlock(blockId)?.model; const model = store.getBlock(blockId)?.model;
if (!model || !matchFlavours(model, ['affine:list'])) { if (!model || !matchFlavours(model, [ListBlockModel])) {
return; return;
} }
const schema = std.store.schema; const schema = std.store.schema;
@@ -125,7 +126,7 @@ export const indentListCommand: Command<{
const nearestHeading = getNearestHeadingBefore(model); const nearestHeading = getNearestHeadingBefore(model);
if ( if (
nearestHeading && nearestHeading &&
matchFlavours(nearestHeading, ['affine:paragraph']) && matchFlavours(nearestHeading, [ParagraphBlockModel]) &&
nearestHeading.collapsed nearestHeading.collapsed
) { ) {
store.updateBlock(nearestHeading, { store.updateBlock(nearestHeading, {

View File

@@ -1,4 +1,5 @@
import { focusTextModel } from '@blocksuite/affine-components/rich-text'; import { focusTextModel } from '@blocksuite/affine-components/rich-text';
import { ListBlockModel } from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import type { Command } from '@blocksuite/block-std';
@@ -16,7 +17,7 @@ export const listToParagraphCommand: Command<
const doc = std.store; const doc = std.store;
const model = doc.getBlock(id)?.model; const model = doc.getBlock(id)?.model;
if (!model || !matchFlavours(model, ['affine:list'])) return false; if (!model || !matchFlavours(model, [ListBlockModel])) return false;
const parent = doc.getParent(model); const parent = doc.getParent(model);
if (!parent) return false; if (!parent) return false;

View File

@@ -1,4 +1,5 @@
import { focusTextModel } from '@blocksuite/affine-components/rich-text'; import { focusTextModel } from '@blocksuite/affine-components/rich-text';
import { ListBlockModel } from '@blocksuite/affine-model';
import { import {
getNextContinuousNumberedLists, getNextContinuousNumberedLists,
matchFlavours, matchFlavours,
@@ -17,7 +18,7 @@ export const splitListCommand: Command<{
const doc = host.doc; const doc = host.doc;
const model = doc.getBlock(blockId)?.model; const model = doc.getBlock(blockId)?.model;
if (!model || !matchFlavours(model, ['affine:list'])) { if (!model || !matchFlavours(model, [ListBlockModel])) {
console.error(`block ${blockId} is not a list block`); console.error(`block ${blockId} is not a list block`);
return; return;
} }

View File

@@ -1,4 +1,4 @@
import type { ListBlockModel } from '@blocksuite/affine-model'; import { ListBlockModel } from '@blocksuite/affine-model';
import { import {
getNextContinuousNumberedLists, getNextContinuousNumberedLists,
matchFlavours, matchFlavours,
@@ -22,7 +22,7 @@ export function correctNumberedListsOrderToPrev(
if (!model) return; if (!model) return;
if ( if (
!matchFlavours(model, ['affine:list']) || !matchFlavours(model, [ListBlockModel]) ||
model.type$.value !== 'numbered' model.type$.value !== 'numbered'
) { ) {
return; return;
@@ -33,7 +33,7 @@ export function correctNumberedListsOrderToPrev(
const previousSibling = doc.getPrev(model); const previousSibling = doc.getPrev(model);
if ( if (
previousSibling && previousSibling &&
matchFlavours(previousSibling, ['affine:list']) && matchFlavours(previousSibling, [ListBlockModel]) &&
previousSibling.type === 'numbered' previousSibling.type === 'numbered'
) { ) {
if (!previousSibling.order) previousSibling.order = 1; if (!previousSibling.order) previousSibling.order = 1;

View File

@@ -1,3 +1,4 @@
import { ListBlockModel } from '@blocksuite/affine-model';
import { import {
getNextContentBlock, getNextContentBlock,
matchFlavours, matchFlavours,
@@ -25,7 +26,7 @@ export function forwardDelete(std: BlockStdScope): true | undefined {
const isCollapsed = text.isCollapsed(); const isCollapsed = text.isCollapsed();
const doc = std.store; const doc = std.store;
const model = doc.getBlock(text.from.blockId)?.model; const model = doc.getBlock(text.from.blockId)?.model;
if (!model || !matchFlavours(model, ['affine:list'])) return; if (!model || !matchFlavours(model, [ListBlockModel])) return;
const isEnd = isCollapsed && text.from.index === model.text.length; const isEnd = isCollapsed && text.from.index === model.text.length;
if (!isEnd) return; if (!isEnd) return;
// Has children in list // Has children in list

View File

@@ -3,6 +3,11 @@ import {
focusTextModel, focusTextModel,
onModelTextUpdated, onModelTextUpdated,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import {
CodeBlockModel,
ListBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import { import {
getBlockSelectionsCommand, getBlockSelectionsCommand,
getSelectedBlocksCommand, getSelectedBlocksCommand,
@@ -196,9 +201,9 @@ export const updateBlockType: Command<
blockModels.forEach(model => { blockModels.forEach(model => {
if ( if (
!matchFlavours(model, [ !matchFlavours(model, [
'affine:paragraph', ParagraphBlockModel,
'affine:list', ListBlockModel,
'affine:code', CodeBlockModel,
]) ])
) { ) {
return; return;

View File

@@ -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 { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import type { Command } from '@blocksuite/block-std';
@@ -10,7 +10,7 @@ export const changeNoteDisplayMode: Command<{
const { std, noteId, mode, stopCapture } = ctx; const { std, noteId, mode, stopCapture } = ctx;
const noteBlockModel = std.store.getBlock(noteId)?.model; const noteBlockModel = std.store.getBlock(noteId)?.model;
if (!noteBlockModel || !matchFlavours(noteBlockModel, ['affine:note'])) if (!noteBlockModel || !matchFlavours(noteBlockModel, [NoteBlockModel]))
return; return;
const currentMode = noteBlockModel.displayMode; const currentMode = noteBlockModel.displayMode;
@@ -27,7 +27,7 @@ export const changeNoteDisplayMode: Command<{
const parent = std.store.getParent(noteBlockModel); const parent = std.store.getParent(noteBlockModel);
if (parent) { if (parent) {
const notes = parent.children.filter(child => const notes = parent.children.filter(child =>
matchFlavours(child, ['affine:note']) matchFlavours(child, [NoteBlockModel])
); );
const firstEdgelessOnlyNote = notes.find( const firstEdgelessOnlyNote = notes.find(
note => note.displayMode === NoteDisplayMode.EdgelessOnly note => note.displayMode === NoteDisplayMode.EdgelessOnly

View File

@@ -1,3 +1,4 @@
import { NoteBlockModel } from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import type { Command } from '@blocksuite/block-std';
@@ -20,7 +21,7 @@ export const dedentBlockToRoot: Command<{
let parent = store.getParent(model); let parent = store.getParent(model);
let changed = false; let changed = false;
while (parent && !matchFlavours(parent, ['affine:note'])) { while (parent && !matchFlavours(parent, [NoteBlockModel])) {
if (!changed) { if (!changed) {
if (stopCapture) store.captureSync(); if (stopCapture) store.captureSync();
changed = true; changed = true;

View File

@@ -1,3 +1,4 @@
import { ParagraphBlockModel } from '@blocksuite/affine-model';
import { import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
matchFlavours, matchFlavours,
@@ -45,7 +46,7 @@ export const dedentBlock: Command<{
if (stopCapture) store.captureSync(); if (stopCapture) store.captureSync();
if ( if (
matchFlavours(model, ['affine:paragraph']) && matchFlavours(model, [ParagraphBlockModel]) &&
model.type.startsWith('h') && model.type.startsWith('h') &&
model.collapsed model.collapsed
) { ) {

View File

@@ -1,3 +1,4 @@
import { NoteBlockModel } from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import { type Command, TextSelection } from '@blocksuite/block-std'; 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--) { for (let i = blockIds.length - 1; i >= 0; i--) {
const model = blockIds[i]; const model = blockIds[i];
const parent = store.getParent(model); const parent = store.getParent(model);
if (parent && !matchFlavours(parent, ['affine:note'])) { if (parent && !matchFlavours(parent, [NoteBlockModel])) {
std.command.exec(dedentBlockToRoot, { std.command.exec(dedentBlockToRoot, {
blockId: model, blockId: model,
stopCapture: false, stopCapture: false,

View File

@@ -1,3 +1,4 @@
import { ParagraphBlockModel } from '@blocksuite/affine-model';
import { import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
matchFlavours, matchFlavours,
@@ -57,7 +58,7 @@ export const dedentBlocks: Command<{
const model = store.getBlock(id)?.model; const model = store.getBlock(id)?.model;
if (!model) return; if (!model) return;
if ( if (
matchFlavours(model, ['affine:paragraph']) && matchFlavours(model, [ParagraphBlockModel]) &&
model.type.startsWith('h') && model.type.startsWith('h') &&
model.collapsed model.collapsed
) { ) {

View File

@@ -1,4 +1,4 @@
import type { ListBlockModel } from '@blocksuite/affine-model'; import { ListBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model';
import { import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
matchFlavours, matchFlavours,
@@ -50,7 +50,7 @@ export const indentBlock: Command<{
if (stopCapture) store.captureSync(); if (stopCapture) store.captureSync();
if ( if (
matchFlavours(model, ['affine:paragraph']) && matchFlavours(model, [ParagraphBlockModel]) &&
model.type.startsWith('h') && model.type.startsWith('h') &&
model.collapsed model.collapsed
) { ) {
@@ -62,12 +62,12 @@ export const indentBlock: Command<{
// update collapsed state of affine list // update collapsed state of affine list
if ( if (
matchFlavours(previousSibling, ['affine:list']) && matchFlavours(previousSibling, [ListBlockModel]) &&
previousSibling.collapsed previousSibling.collapsed
) { ) {
store.updateBlock(previousSibling, { store.updateBlock(previousSibling, {
collapsed: false, collapsed: false,
} as Partial<ListBlockModel>); });
} }
return next(); return next();

View File

@@ -1,3 +1,4 @@
import { ParagraphBlockModel } from '@blocksuite/affine-model';
import { import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
getNearestHeadingBefore, getNearestHeadingBefore,
@@ -58,7 +59,7 @@ export const indentBlocks: Command<{
const model = store.getBlock(id)?.model; const model = store.getBlock(id)?.model;
if (!model) return; if (!model) return;
if ( if (
matchFlavours(model, ['affine:paragraph']) && matchFlavours(model, [ParagraphBlockModel]) &&
model.type.startsWith('h') && model.type.startsWith('h') &&
model.collapsed model.collapsed
) { ) {
@@ -83,12 +84,10 @@ export const indentBlocks: Command<{
const nearestHeading = getNearestHeadingBefore(firstModel); const nearestHeading = getNearestHeadingBefore(firstModel);
if ( if (
nearestHeading && nearestHeading &&
matchFlavours(nearestHeading, ['affine:paragraph']) && matchFlavours(nearestHeading, [ParagraphBlockModel]) &&
nearestHeading.collapsed nearestHeading.collapsed
) { ) {
store.updateBlock(nearestHeading, { store.updateBlock(nearestHeading, { collapsed: false });
collapsed: false,
});
} }
} }
@@ -106,12 +105,10 @@ export const indentBlocks: Command<{
const nearestHeading = getNearestHeadingBefore(firstModel); const nearestHeading = getNearestHeadingBefore(firstModel);
if ( if (
nearestHeading && nearestHeading &&
matchFlavours(nearestHeading, ['affine:paragraph']) && matchFlavours(nearestHeading, [ParagraphBlockModel]) &&
nearestHeading.collapsed nearestHeading.collapsed
) { ) {
store.updateBlock(nearestHeading, { store.updateBlock(nearestHeading, { collapsed: false });
collapsed: false,
});
} }
} }

View File

@@ -1,6 +1,8 @@
import { import {
DefaultTheme, DefaultTheme,
ListBlockModel,
NoteBlockModel, NoteBlockModel,
ParagraphBlockModel,
StrokeStyle, StrokeStyle,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
@@ -98,10 +100,10 @@ export class EdgelessNoteBackground extends SignalWatcher(
if ( if (
(!nearestModel.text || (!nearestModel.text ||
!matchFlavours(nearestModel, ['affine:paragraph', 'affine:list'])) && !matchFlavours(nearestModel, [ParagraphBlockModel, ListBlockModel])) &&
(!siblingModel || (!siblingModel ||
!siblingModel.text || !siblingModel.text ||
!matchFlavours(siblingModel, ['affine:paragraph', 'affine:list'])) !matchFlavours(siblingModel, [ParagraphBlockModel, ListBlockModel]))
) { ) {
const [pId] = this.doc.addSiblingBlocks( const [pId] = this.doc.addSiblingBlocks(
nearestModel, nearestModel,

View File

@@ -1,5 +1,11 @@
import { textConversionConfigs } from '@blocksuite/affine-components/rich-text'; 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 { import {
getBlockSelectionsCommand, getBlockSelectionsCommand,
getNextBlockCommand, getNextBlockCommand,
@@ -149,7 +155,7 @@ export class NoteBlockService extends BlockService {
const doc = this._std.store; const doc = this._std.store;
let parent = doc.getBlock(blockId)?.model ?? null; let parent = doc.getBlock(blockId)?.model ?? null;
while (parent) { while (parent) {
if (matchFlavours(parent, [NoteBlockSchema.model.flavour])) { if (matchFlavours(parent, [NoteBlockModel])) {
return parent; return parent;
} }
parent = doc.getParent(parent); parent = doc.getParent(parent);
@@ -189,9 +195,9 @@ export class NoteBlockService extends BlockService {
if ( if (
!matchFlavours(nextBlock.model, [ !matchFlavours(nextBlock.model, [
'affine:paragraph', ParagraphBlockModel,
'affine:list', ListBlockModel,
'affine:code', CodeBlockModel,
]) ])
) { ) {
this._std.command.exec(selectBlock, { this._std.command.exec(selectBlock, {
@@ -225,9 +231,9 @@ export class NoteBlockService extends BlockService {
event.preventDefault(); event.preventDefault();
if ( if (
matchFlavours(nextBlock.model, [ matchFlavours(nextBlock.model, [
'affine:paragraph', ParagraphBlockModel,
'affine:list', ListBlockModel,
'affine:code', CodeBlockModel,
]) ])
) { ) {
this._std.command.exec(focusBlockStart, { this._std.command.exec(focusBlockStart, {
@@ -279,9 +285,9 @@ export class NoteBlockService extends BlockService {
if ( if (
!matchFlavours(prevBlock.model, [ !matchFlavours(prevBlock.model, [
'affine:paragraph', ParagraphBlockModel,
'affine:list', ListBlockModel,
'affine:code', CodeBlockModel,
]) ])
) { ) {
this._std.command.exec(selectBlock, { this._std.command.exec(selectBlock, {
@@ -313,9 +319,9 @@ export class NoteBlockService extends BlockService {
if ( if (
matchFlavours(prevBlock.model, [ matchFlavours(prevBlock.model, [
'affine:paragraph', ParagraphBlockModel,
'affine:list', ListBlockModel,
'affine:code', CodeBlockModel,
]) ])
) { ) {
event.preventDefault(); event.preventDefault();

View File

@@ -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 { FeatureFlagService } from '@blocksuite/affine-shared/services';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std'; 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') && std.get(FeatureFlagService).getFlag('enable_page_block') &&
note.parent?.children.find( note.parent?.children.find(
child => child =>
matchFlavours(child, ['affine:note']) && matchFlavours(child, [NoteBlockModel]) &&
child.displayMode !== NoteDisplayMode.EdgelessOnly child.displayMode !== NoteDisplayMode.EdgelessOnly
) === note ) === note
); );

View File

@@ -1,3 +1,4 @@
import { ParagraphBlockModel } from '@blocksuite/affine-model';
import type { IndentContext } from '@blocksuite/affine-shared/types'; import type { IndentContext } from '@blocksuite/affine-shared/types';
import { import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
@@ -34,7 +35,7 @@ export const canDedentParagraphCommand: Command<
} }
const model = store.getBlock(blockId)?.model; const model = store.getBlock(blockId)?.model;
if (!model || !matchFlavours(model, ['affine:paragraph'])) { if (!model || !matchFlavours(model, [ParagraphBlockModel])) {
return; return;
} }
@@ -88,7 +89,7 @@ export const dedentParagraphCommand: Command<{
store.captureSync(); store.captureSync();
if ( if (
matchFlavours(model, ['affine:paragraph']) && matchFlavours(model, [ParagraphBlockModel]) &&
model.type.startsWith('h') && model.type.startsWith('h') &&
model.collapsed model.collapsed
) { ) {

View File

@@ -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 type { IndentContext } from '@blocksuite/affine-shared/types';
import { import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
@@ -37,7 +37,7 @@ export const canIndentParagraphCommand: Command<
} }
const model = std.store.getBlock(blockId)?.model; const model = std.store.getBlock(blockId)?.model;
if (!model || !matchFlavours(model, ['affine:paragraph'])) { if (!model || !matchFlavours(model, [ParagraphBlockModel])) {
return; return;
} }
@@ -95,7 +95,7 @@ export const indentParagraphCommand: Command<{
const nearestHeading = getNearestHeadingBefore(model); const nearestHeading = getNearestHeadingBefore(model);
if ( if (
nearestHeading && nearestHeading &&
matchFlavours(nearestHeading, ['affine:paragraph']) && matchFlavours(nearestHeading, [ParagraphBlockModel]) &&
nearestHeading.collapsed nearestHeading.collapsed
) { ) {
store.updateBlock(nearestHeading, { store.updateBlock(nearestHeading, {
@@ -105,7 +105,7 @@ export const indentParagraphCommand: Command<{
} }
if ( if (
matchFlavours(model, ['affine:paragraph']) && matchFlavours(model, [ParagraphBlockModel]) &&
model.type.startsWith('h') && model.type.startsWith('h') &&
model.collapsed model.collapsed
) { ) {
@@ -124,7 +124,7 @@ export const indentParagraphCommand: Command<{
const nearestHeading = getNearestHeadingBefore(model); const nearestHeading = getNearestHeadingBefore(model);
if ( if (
nearestHeading && nearestHeading &&
matchFlavours(nearestHeading, ['affine:paragraph']) && matchFlavours(nearestHeading, [ParagraphBlockModel]) &&
nearestHeading.collapsed nearestHeading.collapsed
) { ) {
store.updateBlock(nearestHeading, { store.updateBlock(nearestHeading, {
@@ -135,12 +135,12 @@ export const indentParagraphCommand: Command<{
// update collapsed state of affine list // update collapsed state of affine list
if ( if (
matchFlavours(previousSibling, ['affine:list']) && matchFlavours(previousSibling, [ListBlockModel]) &&
previousSibling.collapsed previousSibling.collapsed
) { ) {
store.updateBlock(previousSibling, { store.updateBlock(previousSibling, {
collapsed: false, collapsed: false,
} as Partial<ListBlockModel>); });
} }
const textSelection = selection.find(TextSelection); const textSelection = selection.find(TextSelection);

View File

@@ -2,6 +2,7 @@ import {
focusTextModel, focusTextModel,
getInlineEditorByModel, getInlineEditorByModel,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import { ParagraphBlockModel } from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import { type Command, TextSelection } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
@@ -23,7 +24,7 @@ export const splitParagraphCommand: Command<
if (!blockId) return; if (!blockId) return;
const model = store.getBlock(blockId)?.model; 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 inlineEditor = getInlineEditorByModel(host, model);
const range = inlineEditor?.getInlineRange(); const range = inlineEditor?.getInlineRange();

View File

@@ -4,7 +4,10 @@ import {
markdownInput, markdownInput,
textKeymap, textKeymap,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import { ParagraphBlockSchema } from '@blocksuite/affine-model'; import {
ParagraphBlockModel,
ParagraphBlockSchema,
} from '@blocksuite/affine-model';
import { import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
matchFlavours, matchFlavours,
@@ -37,7 +40,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
const { store } = std; const { store } = std;
const model = store.getBlock(text.from.blockId)?.model; 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; const event = ctx.get('keyboardState').raw;
event.preventDefault(); event.preventDefault();
@@ -68,7 +71,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
const text = std.selection.find(TextSelection); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const model = store.getBlock(text.from.blockId)?.model; const model = store.getBlock(text.from.blockId)?.model;
if (!model || !matchFlavours(model, ['affine:paragraph'])) return; if (!model || !matchFlavours(model, [ParagraphBlockModel])) return;
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(
std.host, std.host,
text.from.blockId text.from.blockId
@@ -95,7 +98,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
const text = std.selection.find(TextSelection); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const model = store.getBlock(text.from.blockId)?.model; const model = store.getBlock(text.from.blockId)?.model;
if (!model || !matchFlavours(model, ['affine:paragraph'])) return; if (!model || !matchFlavours(model, [ParagraphBlockModel])) return;
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(
std.host, std.host,
text.from.blockId text.from.blockId

View File

@@ -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 { import {
getNextContentBlock, getNextContentBlock,
matchFlavours, matchFlavours,
@@ -15,31 +25,32 @@ export function forwardDelete(std: BlockStdScope) {
if (!text) return; if (!text) return;
const isCollapsed = text.isCollapsed(); const isCollapsed = text.isCollapsed();
const model = store.getBlock(text.from.blockId)?.model; 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; const isEnd = isCollapsed && text.from.index === model.text.length;
if (!isEnd) return; if (!isEnd) return;
const parent = store.getParent(model); const parent = store.getParent(model);
if (!parent) return; if (!parent) return;
const nextSibling = store.getNext(model); 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.setGroup('note', [
std.selection.create(BlockSelection, { blockId: nextSibling.id }), std.selection.create(BlockSelection, { blockId: nextSibling.id }),
]); ]);
return true; return true;
} }
if (nextSibling?.text) { if (matchFlavours(nextSibling, [ParagraphBlockModel, ListBlockModel])) {
model.text.join(nextSibling.text); model.text.join(nextSibling.text);
if (nextSibling.children) { if (nextSibling.children) {
const parent = store.getParent(nextSibling); const parent = store.getParent(nextSibling);

View File

@@ -2,8 +2,19 @@ import {
asyncSetInlineRange, asyncSetInlineRange,
focusTextModel, focusTextModel,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import type { RootBlockModel } from '@blocksuite/affine-model'; import {
import { EMBED_BLOCK_FLAVOUR_LIST } from '@blocksuite/affine-shared/consts'; 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 type { ExtendedModel } from '@blocksuite/affine-shared/types';
import { import {
focusTitle, focusTitle,
@@ -33,7 +44,7 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) {
const parent = doc.getParent(model); const parent = doc.getParent(model);
if (!parent) return false; if (!parent) return false;
if (matchFlavours(parent, ['affine:edgeless-text'])) { if (matchFlavours(parent, [EdgelessTextBlockModel])) {
return true; return true;
} }
@@ -42,7 +53,7 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) {
return handleNoPreviousSibling(editorHost, model); return handleNoPreviousSibling(editorHost, model);
} }
if (matchFlavours(prevBlock, ['affine:paragraph', 'affine:list'])) { if (matchFlavours(prevBlock, [ParagraphBlockModel, ListBlockModel])) {
const modelIndex = parent.children.indexOf(model); const modelIndex = parent.children.indexOf(model);
if ( if (
(modelIndex === -1 || modelIndex === parent.children.length - 1) && (modelIndex === -1 || modelIndex === parent.children.length - 1) &&
@@ -64,12 +75,12 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) {
if ( if (
matchFlavours(prevBlock, [ matchFlavours(prevBlock, [
'affine:attachment', AttachmentBlockModel,
'affine:bookmark', BookmarkBlockModel,
'affine:code', CodeBlockModel,
'affine:image', ImageBlockModel,
'affine:divider', DividerBlockModel,
...EMBED_BLOCK_FLAVOUR_LIST, ...EMBED_BLOCK_MODEL_LIST,
]) ])
) { ) {
const selection = editorHost.selection.create(BlockSelection, { const selection = editorHost.selection.create(BlockSelection, {
@@ -86,7 +97,7 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) {
return true; return true;
} }
if (matchFlavours(parent, ['affine:database'])) { if (matchFlavours(parent, [DatabaseBlockModel])) {
doc.deleteBlock(model); doc.deleteBlock(model);
focusTextModel(editorHost.std, prevBlock.id, prevBlock.text?.yText.length); focusTextModel(editorHost.std, prevBlock.id, prevBlock.text?.yText.length);
return true; return true;
@@ -104,7 +115,7 @@ function handleNoPreviousSibling(editorHost: EditorHost, model: ExtendedModel) {
// Probably no title, e.g. in edgeless mode // Probably no title, e.g. in edgeless mode
if (!titleEditor) { if (!titleEditor) {
if ( if (
matchFlavours(parent, ['affine:edgeless-text']) || matchFlavours(parent, [EdgelessTextBlockModel]) ||
model.children.length > 0 model.children.length > 0
) { ) {
doc.deleteBlock(model, { doc.deleteBlock(model, {

View File

@@ -1,5 +1,8 @@
import { getSurfaceBlock } from '@blocksuite/affine-block-surface'; 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 { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import type { Command } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -38,7 +41,7 @@ export const insertSurfaceRefBlockCommand: Command<
if (element?.type === 'group') { if (element?.type === 'group') {
surfaceRefProps.refFlavour = 'group'; surfaceRefProps.refFlavour = 'group';
} else if (matchFlavours(blockModel, ['affine:frame'])) { } else if (matchFlavours(blockModel, [FrameBlockModel])) {
surfaceRefProps.refFlavour = 'frame'; surfaceRefProps.refFlavour = 'frame';
} else { } else {
console.error(`reference not found ${reference}`); console.error(`reference not found ${reference}`);

View File

@@ -1,6 +1,9 @@
import { import {
type NoteBlockModel, CodeBlockModel,
ListBlockModel,
NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
ParagraphBlockModel,
type RootBlockModel, type RootBlockModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
@@ -69,7 +72,7 @@ export class DocTitle extends WithDisposable(ShadowlessElement) {
private _getOrCreateFirstPageVisibleNote() { private _getOrCreateFirstPageVisibleNote() {
const note = this._rootModel.children.find( const note = this._rootModel.children.find(
(child): child is NoteBlockModel => (child): child is NoteBlockModel =>
matchFlavours(child, ['affine:note']) && matchFlavours(child, [NoteBlockModel]) &&
child.displayMode !== NoteDisplayMode.EdgelessOnly child.displayMode !== NoteDisplayMode.EdgelessOnly
); );
if (note) return note; if (note) return note;
@@ -102,7 +105,11 @@ export class DocTitle extends WithDisposable(ShadowlessElement) {
const note = this._getOrCreateFirstPageVisibleNote(); const note = this._getOrCreateFirstPageVisibleNote();
const firstText = note?.children.find(block => const firstText = note?.children.find(block =>
matchFlavours(block, ['affine:paragraph', 'affine:list', 'affine:code']) matchFlavours(block, [
ParagraphBlockModel,
ListBlockModel,
CodeBlockModel,
])
); );
if (firstText) { if (firstText) {
if (this._std) focusTextModel(this._std, firstText.id); if (this._std) focusTextModel(this._std, firstText.id);

View File

@@ -1,3 +1,4 @@
import { NoteBlockModel } from '@blocksuite/affine-model';
import { import {
calcDropTarget, calcDropTarget,
type DropTarget, type DropTarget,
@@ -11,6 +12,7 @@ import {
type EditorHost, type EditorHost,
LifeCycleWatcher, LifeCycleWatcher,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { SurfaceBlockModel } from '@blocksuite/block-std/gfx';
import { createIdentifier } from '@blocksuite/global/di'; import { createIdentifier } from '@blocksuite/global/di';
import type { IVec } from '@blocksuite/global/utils'; import type { IVec } from '@blocksuite/global/utils';
import { Point, throttle } 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 model = element.model;
const parent = this.std.store.getParent(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; const point = this.point$.value;
target = point && calcDropTarget(point, model, element); target = point && calcDropTarget(point, model, element);
} }
@@ -73,7 +75,7 @@ export class FileDropExtension extends LifeCycleWatcher {
if (!rootModel) return null; if (!rootModel) return null;
let lastNote = rootModel.children[rootModel.children.length - 1]; 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 newNoteId = this.doc.addBlock('affine:note', {}, rootModel.id);
const newNote = this.doc.getBlock(newNoteId)?.model; const newNote = this.doc.getBlock(newNoteId)?.model;
if (!newNote) return null; if (!newNote) return null;

View File

@@ -1,3 +1,4 @@
import { DatabaseBlockModel } from '@blocksuite/affine-model';
import { import {
asyncGetBlockComponent, asyncGetBlockComponent,
getCurrentNativeRange, getCurrentNativeRange,
@@ -41,7 +42,7 @@ export function getInlineEditorByModel(
typeof model === 'string' typeof model === 'string'
? editorHost.std.store.getBlock(model)?.model ? editorHost.std.store.getBlock(model)?.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. // 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. // Support to enter the editing state through the Enter key in the database.
return null; return null;

View File

@@ -1,3 +1,4 @@
import { RootBlockModel } from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import { type Command, TextSelection } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
import type { Text } from '@blocksuite/store'; import type { Text } from '@blocksuite/store';
@@ -24,7 +25,7 @@ export const deleteTextCommand: Command<{
if (!fromElement) return; if (!fromElement) return;
let fromText: Text | undefined; let fromText: Text | undefined;
if (matchFlavours(fromElement.model, ['affine:page'])) { if (matchFlavours(fromElement.model, [RootBlockModel])) {
fromText = fromElement.model.title; fromText = fromElement.model.title;
} else { } else {
fromText = fromElement.model.text; fromText = fromElement.model.text;

View File

@@ -1,3 +1,4 @@
import { CodeBlockModel } from '@blocksuite/affine-model';
import { BRACKET_PAIRS } from '@blocksuite/affine-shared/consts'; import { BRACKET_PAIRS } from '@blocksuite/affine-shared/consts';
import { import {
createDefaultDoc, createDefaultDoc,
@@ -28,7 +29,7 @@ export const bracketKeymap = (
if (!textSelection) return; if (!textSelection) return;
const model = doc.getBlock(textSelection.from.blockId)?.model; const model = doc.getBlock(textSelection.from.blockId)?.model;
if (!model) return; if (!model) return;
if (!matchFlavours(model, ['affine:code'])) return; if (!matchFlavours(model, [CodeBlockModel])) return;
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(
std.host, std.host,
textSelection.from.blockId textSelection.from.blockId
@@ -55,7 +56,7 @@ export const bracketKeymap = (
const model = doc.getBlock(textSelection.from.blockId)?.model; const model = doc.getBlock(textSelection.from.blockId)?.model;
if (!model) return; 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 // When selection is collapsed, only trigger auto complete in code block
if (textSelection.isCollapsed() && !isCodeBlock) return; if (textSelection.isCollapsed() && !isCodeBlock) return;
if (!textSelection.isInSameBlock()) return; if (!textSelection.isInSameBlock()) return;

View File

@@ -1,3 +1,7 @@
import {
DividerBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockStdScope } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -12,8 +16,8 @@ export function toDivider(
) { ) {
const { store: doc } = std; const { store: doc } = std;
if ( if (
matchFlavours(model, ['affine:divider']) || matchFlavours(model, [DividerBlockModel]) ||
(matchFlavours(model, ['affine:paragraph']) && model.type === 'quote') (matchFlavours(model, [ParagraphBlockModel]) && model.type === 'quote')
) { ) {
return; return;
} }

View File

@@ -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 { matchFlavours, toNumberedList } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockStdScope } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -13,7 +17,7 @@ export function toList(
prefix: string, prefix: string,
otherProperties?: Partial<ListProps> otherProperties?: Partial<ListProps>
) { ) {
if (!matchFlavours(model, ['affine:paragraph'])) { if (!matchFlavours(model, [ParagraphBlockModel])) {
return; return;
} }
const { store: doc } = std; const { store: doc } = std;

View File

@@ -1,3 +1,4 @@
import { CodeBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model';
import { import {
isMarkdownPrefix, isMarkdownPrefix,
matchFlavours, matchFlavours,
@@ -31,10 +32,10 @@ export function markdownInput(
const prefixText = getPrefixText(inline); const prefixText = getPrefixText(inline);
if (!isMarkdownPrefix(prefixText)) return; if (!isMarkdownPrefix(prefixText)) return;
const isParagraph = matchFlavours(model, ['affine:paragraph']); const isParagraph = matchFlavours(model, [ParagraphBlockModel]);
const isHeading = isParagraph && model.type.startsWith('h'); const isHeading = isParagraph && model.type.startsWith('h');
const isParagraphQuoteBlock = isParagraph && model.type === 'quote'; const isParagraphQuoteBlock = isParagraph && model.type === 'quote';
const isCodeBlock = matchFlavours(model, ['affine:code']); const isCodeBlock = matchFlavours(model, [CodeBlockModel]);
if (isHeading || isParagraphQuoteBlock || isCodeBlock) return; if (isHeading || isParagraphQuoteBlock || isCodeBlock) return;
const lineInfo = inline.getLine(range.index); const lineInfo = inline.getLine(range.index);

View File

@@ -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 { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockStdScope } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -13,7 +16,7 @@ export function toParagraph(
prefix: string prefix: string
) { ) {
const { store: doc } = std; const { store: doc } = std;
if (!matchFlavours(model, ['affine:paragraph'])) { if (!matchFlavours(model, [ParagraphBlockModel])) {
const parent = doc.getParent(model); const parent = doc.getParent(model);
if (!parent) return; if (!parent) return;
@@ -33,7 +36,7 @@ export function toParagraph(
return id; return id;
} }
if (matchFlavours(model, ['affine:paragraph']) && model.type !== type) { if (matchFlavours(model, [ParagraphBlockModel]) && model.type !== type) {
beforeConvert(std, model, prefix.length); beforeConvert(std, model, prefix.length);
doc.updateBlock(model, { type }); doc.updateBlock(model, { type });

View File

@@ -1,3 +1,4 @@
import { ParagraphBlockModel } from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockStdScope } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -10,7 +11,7 @@ export function toCode(
prefixText: string, prefixText: string,
language: string | null language: string | null
) { ) {
if (matchFlavours(model, ['affine:paragraph']) && model.type === 'quote') { if (matchFlavours(model, [ParagraphBlockModel]) && model.type === 'quote') {
return; return;
} }

View File

@@ -1,8 +1,4 @@
import { import { BlockModel, defineBlockSchema, type Text } from '@blocksuite/store';
defineBlockSchema,
type SchemaToModel,
type Text,
} from '@blocksuite/store';
interface CodeBlockProps { interface CodeBlockProps {
text: Text; text: Text;
@@ -31,9 +27,12 @@ export const CodeBlockSchema = defineBlockSchema({
], ],
children: [], children: [],
}, },
toModel: () => new CodeBlockModel(),
}); });
export type CodeBlockModel = SchemaToModel<typeof CodeBlockSchema>; export class CodeBlockModel extends BlockModel<CodeBlockProps> {
override text!: Text;
}
declare global { declare global {
namespace BlockSuite { namespace BlockSuite {

View File

@@ -1,4 +1,4 @@
import { defineBlockSchema, type SchemaToModel } from '@blocksuite/store'; import { BlockModel, defineBlockSchema } from '@blocksuite/store';
export const DividerBlockSchema = defineBlockSchema({ export const DividerBlockSchema = defineBlockSchema({
flavour: 'affine:divider', flavour: 'affine:divider',
@@ -7,9 +7,14 @@ export const DividerBlockSchema = defineBlockSchema({
role: 'content', role: 'content',
children: [], children: [],
}, },
toModel: () => new DividerBlockModel(),
}); });
export type DividerBlockModel = SchemaToModel<typeof DividerBlockSchema>; type Props = {
text: string;
};
export class DividerBlockModel extends BlockModel<Props> {}
declare global { declare global {
namespace BlockSuite { namespace BlockSuite {

View File

@@ -1,5 +1,5 @@
import type { SchemaToModel, Text } from '@blocksuite/store'; import type { Text } from '@blocksuite/store';
import { defineBlockSchema } from '@blocksuite/store'; import { BlockModel, defineBlockSchema } from '@blocksuite/store';
// `toggle` type has been deprecated, do not use it // `toggle` type has been deprecated, do not use it
export type ListType = 'bulleted' | 'numbered' | 'todo' | 'toggle'; export type ListType = 'bulleted' | 'numbered' | 'todo' | 'toggle';
@@ -35,9 +35,12 @@ export const ListBlockSchema = defineBlockSchema({
'affine:edgeless-text', 'affine:edgeless-text',
], ],
}, },
toModel: () => new ListBlockModel(),
}); });
export type ListBlockModel = SchemaToModel<typeof ListBlockSchema>; export class ListBlockModel extends BlockModel<ListProps> {
override text!: Text;
}
declare global { declare global {
namespace BlockSuite { namespace BlockSuite {

View File

@@ -1,4 +1,4 @@
import { defineBlockSchema, type SchemaToModel } from '@blocksuite/store'; import { BlockModel, defineBlockSchema } from '@blocksuite/store';
export type SurfaceRefProps = { export type SurfaceRefProps = {
reference: string; reference: string;
@@ -18,9 +18,10 @@ export const SurfaceRefBlockSchema = defineBlockSchema({
role: 'content', role: 'content',
parent: ['affine:note', 'affine:paragraph', 'affine:list'], parent: ['affine:note', 'affine:paragraph', 'affine:list'],
}, },
toModel: () => new SurfaceRefBlockModel(),
}); });
export type SurfaceRefBlockModel = SchemaToModel<typeof SurfaceRefBlockSchema>; export class SurfaceRefBlockModel extends BlockModel<SurfaceRefProps> {}
declare global { declare global {
namespace BlockSuite { namespace BlockSuite {

View File

@@ -1,3 +1,4 @@
import { RootBlockModel } from '@blocksuite/affine-model';
import { import {
type BlockStdScope, type BlockStdScope,
type EditorHost, type EditorHost,
@@ -19,7 +20,7 @@ const handlePoint = (
model: DraftModel model: DraftModel
) => { ) => {
const { index, length } = point; const { index, length } = point;
if (matchFlavours(model, ['affine:page'])) { if (matchFlavours(model, [RootBlockModel])) {
if (length === 0) return; if (length === 0) return;
(snapshot.props.title as Record<string, unknown>).delta = (snapshot.props.title as Record<string, unknown>).delta =
model.title.sliceToDelta(index, length + index); model.title.sliceToDelta(index, length + index);

View File

@@ -1,6 +1,8 @@
import { import {
CodeBlockModel,
type DocMode, type DocMode,
DocModes, DocModes,
ImageBlockModel,
type ParagraphBlockModel, type ParagraphBlockModel,
type ReferenceInfo, type ReferenceInfo,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
@@ -291,7 +293,7 @@ class PasteTr {
return; return;
} }
if (!cursorModel.text) { if (!cursorModel.text) {
if (matchFlavours(cursorModel, ['affine:image'])) { if (matchFlavours(cursorModel, [ImageBlockModel])) {
const selection = this.std.selection.create(ImageSelection, { const selection = this.std.selection.create(ImageSelection, {
blockId: target.blockId, blockId: target.blockId,
}); });
@@ -356,7 +358,7 @@ class PasteTr {
if ( if (
this.firstSnapshot !== this.lastSnapshot && this.firstSnapshot !== this.lastSnapshot &&
this.lastSnapshot.props.text && 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 text = fromJSON(this.lastSnapshot.props.text) as Text;
const doc = new Y.Doc(); const doc = new Y.Doc();

View File

@@ -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 BLOCK_CHILDREN_CONTAINER_PADDING_LEFT = 24;
export const EDGELESS_BLOCK_CHILD_PADDING = 24; export const EDGELESS_BLOCK_CHILD_PADDING = 24;
@@ -48,6 +57,16 @@ export const EMBED_BLOCK_FLAVOUR_LIST = [
'affine:embed-loom', 'affine:embed-loom',
] as const; ] as const;
export const EMBED_BLOCK_MODEL_LIST = [
EmbedGithubModel,
EmbedYoutubeModel,
EmbedFigmaModel,
EmbedLinkedDocModel,
EmbedSyncedDocModel,
EmbedHtmlModel,
EmbedLoomModel,
] as const;
export const DEFAULT_IMAGE_PROXY_ENDPOINT = export const DEFAULT_IMAGE_PROXY_ENDPOINT =
'https://affine-worker.toeverything.workers.dev/api/worker/image-proxy'; 'https://affine-worker.toeverything.workers.dev/api/worker/image-proxy';

View File

@@ -1,4 +1,4 @@
import type { ParagraphBlockModel } from '@blocksuite/affine-model'; import { ParagraphBlockModel } from '@blocksuite/affine-model';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
import { matchFlavours } from '../model/checker.js'; import { matchFlavours } from '../model/checker.js';
@@ -15,7 +15,7 @@ export function calculateCollapsedSiblings(
const collapsedEdgeIndex = children.findIndex((child, i) => { const collapsedEdgeIndex = children.findIndex((child, i) => {
if ( if (
i > index && i > index &&
matchFlavours(child, ['affine:paragraph']) && matchFlavours(child, [ParagraphBlockModel]) &&
child.type.startsWith('h') child.type.startsWith('h')
) { ) {
const modelLevel = parseInt(model.type.slice(1)); const modelLevel = parseInt(model.type.slice(1));
@@ -46,7 +46,7 @@ export function getNearestHeadingBefore(
for (let i = index - 1; i >= 0; i--) { for (let i = index - 1; i >= 0; i--) {
const sibling = parent.children[i]; const sibling = parent.children[i];
if ( if (
matchFlavours(sibling, ['affine:paragraph']) && matchFlavours(sibling, [ParagraphBlockModel]) &&
sibling.type.startsWith('h') sibling.type.startsWith('h')
) { ) {
return sibling; return sibling;

View File

@@ -1,3 +1,4 @@
import { DatabaseBlockModel, ListBlockModel } from '@blocksuite/affine-model';
import type { BlockComponent } from '@blocksuite/block-std'; import type { BlockComponent } from '@blocksuite/block-std';
import { type Point, Rect } from '@blocksuite/global/utils'; import { type Point, Rect } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -55,7 +56,7 @@ export function calcDropTarget(
.every(m => children.includes(m.flavour)); .every(m => children.includes(m.flavour));
} }
if (!shouldAppendToDatabase && !matchFlavours(model, ['affine:database'])) { if (!shouldAppendToDatabase && !matchFlavours(model, [DatabaseBlockModel])) {
const databaseBlockComponent = const databaseBlockComponent =
element.closest<BlockComponent>('affine-database'); element.closest<BlockComponent>('affine-database');
if (databaseBlockComponent) { if (databaseBlockComponent) {
@@ -149,7 +150,7 @@ export function calcDropTarget(
const hasChild = (element as BlockComponent).childBlocks.length; const hasChild = (element as BlockComponent).childBlocks.length;
if ( if (
allowSublist && allowSublist &&
matchFlavours(model, ['affine:list']) && matchFlavours(model, [ListBlockModel]) &&
!hasChild && !hasChild &&
point.x > domRect.x + BLOCK_CHILDREN_CONTAINER_PADDING_LEFT point.x > domRect.x + BLOCK_CHILDREN_CONTAINER_PADDING_LEFT
) { ) {

View File

@@ -1,3 +1,4 @@
import { DatabaseBlockModel } from '@blocksuite/affine-model';
import { BLOCK_ID_ATTR } from '@blocksuite/block-std'; import { BLOCK_ID_ATTR } from '@blocksuite/block-std';
import type { Point } from '@blocksuite/global/utils'; import type { Point } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -24,7 +25,7 @@ export function getDropRectByPoint(
flag: DropFlags.Normal, flag: DropFlags.Normal,
}; };
const isDatabase = matchFlavours(model, ['affine:database']); const isDatabase = matchFlavours(model, [DatabaseBlockModel]);
if (isDatabase) { if (isDatabase) {
const table = getDatabaseBlockTableElement(element); const table = getDatabaseBlockTableElement(element);

View File

@@ -1,4 +1,6 @@
import { NoteBlockModel, RootBlockModel } from '@blocksuite/affine-model';
import { BLOCK_ID_ATTR, type BlockComponent } from '@blocksuite/block-std'; 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 { Point, Rect } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -35,10 +37,9 @@ function hasBlockId(element: Element): element is BlockComponent {
*/ */
function isRootOrNoteOrSurface(element: BlockComponent) { function isRootOrNoteOrSurface(element: BlockComponent) {
return matchFlavours(element.model, [ return matchFlavours(element.model, [
'affine:page', RootBlockModel,
'affine:note', NoteBlockModel,
// @ts-expect-error TODO: migrate surface model to @blocksuite/affine-model SurfaceBlockModel,
'affine:surface',
]); ]);
} }

View File

@@ -1,15 +1,19 @@
import type { BlockModel, DraftModel, Store } from '@blocksuite/store'; import type { BlockModel, Store } from '@blocksuite/store';
import { minimatch } from 'minimatch';
export function matchFlavours<Key extends (keyof BlockSuite.BlockModels)[]>( type ConstructorType<U> = { new (): U };
model: DraftModel | null, type ModelList<T> =
expected: Key T extends Array<infer U>
): model is BlockSuite.BlockModels[Key[number]] { ? U extends ConstructorType<infer C>
? Array<C>
: never
: never;
export function matchFlavours<
const Model extends ConstructorType<BlockModel>[],
U extends ModelList<Model>[number] = ModelList<Model>[number],
>(model: unknown, expected: Model): model is U {
return ( return (
!!model && !!model && expected.some(expectedModel => model instanceof expectedModel)
expected.some(key =>
minimatch(model.flavour as keyof BlockSuite.BlockModels, key)
)
); );
} }

View File

@@ -16,7 +16,6 @@ export function createDefaultDoc(
title, title,
}); });
// @ts-expect-error FIXME: will be fixed when surface model migrated to affine-model
doc.addBlock('affine:surface', {}, rootId); doc.addBlock('affine:surface', {}, rootId);
const noteId = doc.addBlock('affine:note', {}, rootId); const noteId = doc.addBlock('affine:note', {}, rootId);
doc.addBlock('affine:paragraph', {}, noteId); doc.addBlock('affine:paragraph', {}, noteId);

View File

@@ -1,3 +1,4 @@
import { FrameBlockModel } from '@blocksuite/affine-model';
import type { EditorHost } from '@blocksuite/block-std'; import type { EditorHost } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -73,7 +74,7 @@ export function getPrevContentBlock(
const prev = getPrev(model); const prev = getPrev(model);
if (prev) { if (prev) {
if (prev.role === 'content' && !matchFlavours(prev, ['affine:frame'])) { if (prev.role === 'content' && !matchFlavours(prev, [FrameBlockModel])) {
return prev; return prev;
} }

View File

@@ -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 { BlockComponent, EditorHost } from '@blocksuite/block-std';
import type { BlockModel, Store } from '@blocksuite/store'; import type { BlockModel, Store } from '@blocksuite/store';
@@ -37,7 +37,7 @@ export async function asyncGetBlockComponent(
export function findNoteBlockModel(model: BlockModel) { export function findNoteBlockModel(model: BlockModel) {
return findAncestorModel(model, m => return findAncestorModel(model, m =>
matchFlavours(m, ['affine:note']) matchFlavours(m, [NoteBlockModel])
) as NoteBlockModel | null; ) as NoteBlockModel | null;
} }
@@ -48,7 +48,7 @@ export function getLastNoteBlock(doc: Store) {
for (let i = children.length - 1; i >= 0; i--) { for (let i = children.length - 1; i >= 0; i--) {
const child = children[i]; const child = children[i];
if ( if (
matchFlavours(child, ['affine:note']) && matchFlavours(child, [NoteBlockModel]) &&
child.displayMode !== NoteDisplayMode.EdgelessOnly child.displayMode !== NoteDisplayMode.EdgelessOnly
) { ) {
note = child as NoteBlockModel; note = child as NoteBlockModel;

View File

@@ -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 { BlockStdScope } from '@blocksuite/block-std';
import type { BlockModel, Store } from '@blocksuite/store'; import type { BlockModel, Store } from '@blocksuite/store';
@@ -23,7 +23,7 @@ export function getNextContinuousNumberedLists(
const firstNotNumberedListIndex = parent.children.findIndex( const firstNotNumberedListIndex = parent.children.findIndex(
(model, i) => (model, i) =>
i > modelIndex && i > modelIndex &&
(!matchFlavours(model, ['affine:list']) || model.type !== 'numbered') (!matchFlavours(model, [ListBlockModel]) || model.type !== 'numbered')
); );
const newContinuousLists = parent.children.slice( const newContinuousLists = parent.children.slice(
modelIndex + 1, modelIndex + 1,
@@ -32,7 +32,7 @@ export function getNextContinuousNumberedLists(
if ( if (
!newContinuousLists.every( !newContinuousLists.every(
model => model =>
matchFlavours(model, ['affine:list']) && model.type === 'numbered' matchFlavours(model, [ListBlockModel]) && model.type === 'numbered'
) )
) )
return []; return [];
@@ -56,7 +56,7 @@ export function toNumberedList(
// if there is a numbered list before, the order continues from the previous list // if there is a numbered list before, the order continues from the previous list
if ( if (
prevSibling && prevSibling &&
matchFlavours(prevSibling, ['affine:list']) && matchFlavours(prevSibling, [ListBlockModel]) &&
prevSibling.type === 'numbered' prevSibling.type === 'numbered'
) { ) {
doc.transact(() => { doc.transact(() => {

View File

@@ -1,4 +1,5 @@
import { correctNumberedListsOrderToPrev } from '@blocksuite/affine-block-list'; import { correctNumberedListsOrderToPrev } from '@blocksuite/affine-block-list';
import { ListBlockModel } from '@blocksuite/affine-model';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockStdScope } from '@blocksuite/block-std';
import type { TransformerMiddleware } from '@blocksuite/store'; import type { TransformerMiddleware } from '@blocksuite/store';
@@ -10,7 +11,7 @@ export const reorderList =
if (payload.type === 'block') { if (payload.type === 'block') {
const model = payload.model; const model = payload.model;
if ( if (
matchFlavours(model, ['affine:list']) && matchFlavours(model, [ListBlockModel]) &&
model.type === 'numbered' model.type === 'numbered'
) { ) {
const next = std.store.getNext(model); const next = std.store.getNext(model);

View File

@@ -3,7 +3,11 @@ import {
type EdgelessNoteBlockComponent, type EdgelessNoteBlockComponent,
} from '@blocksuite/affine-block-note'; } from '@blocksuite/affine-block-note';
import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph'; 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 { DocModeProvider } from '@blocksuite/affine-shared/services';
import { import {
calcDropTarget, calcDropTarget,
@@ -216,7 +220,7 @@ export const getDropResult = (
const model = closestBlock.model; const model = closestBlock.model;
const isDatabase = matchFlavours(model, ['affine:database']); const isDatabase = matchFlavours(model, [DatabaseBlockModel]);
if (isDatabase) { if (isDatabase) {
return dropIndicator; return dropIndicator;
} }
@@ -232,7 +236,7 @@ export const getDropResult = (
export function getDragHandleLeftPadding(blocks: BlockComponent[]) { export function getDragHandleLeftPadding(blocks: BlockComponent[]) {
const hasToggleList = blocks.some( const hasToggleList = blocks.some(
block => block =>
(matchFlavours(block.model, ['affine:list']) && (matchFlavours(block.model, [ListBlockModel]) &&
block.model.children.length > 0) || block.model.children.length > 0) ||
(block instanceof ParagraphBlockComponent && (block instanceof ParagraphBlockComponent &&
block.model.type.startsWith('h') && block.model.type.startsWith('h') &&

View File

@@ -2,9 +2,17 @@ import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph';
import { import {
addNoteAtPoint, addNoteAtPoint,
getSurfaceBlock, getSurfaceBlock,
SurfaceBlockModel,
} from '@blocksuite/affine-block-surface'; } from '@blocksuite/affine-block-surface';
import { DropIndicator } from '@blocksuite/affine-components/drop-indicator'; 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 { import {
BLOCK_CHILDREN_CONTAINER_PADDING_LEFT, BLOCK_CHILDREN_CONTAINER_PADDING_LEFT,
EMBED_CARD_HEIGHT, EMBED_CARD_HEIGHT,
@@ -153,17 +161,17 @@ export class DragEventWatcher {
!snapshot || !snapshot ||
snapshot.content.length === 0 || snapshot.content.length === 0 ||
!dragPayload?.from || !dragPayload?.from ||
matchFlavours(model, ['affine:database']) matchFlavours(model, [DatabaseBlockModel])
) )
return null; return null;
const isDropOnNoteBlock = matchFlavours(model, ['affine:note']); const isDropOnNoteBlock = matchFlavours(model, [NoteBlockModel]);
const edge = dropPayload.edge; const edge = dropPayload.edge;
const scale = this.widget.scale.peek(); const scale = this.widget.scale.peek();
let result: DropResult; let result: DropResult;
if (edge === 'right' && matchFlavours(dropBlock.model, ['affine:list'])) { if (edge === 'right' && matchFlavours(dropBlock.model, [ListBlockModel])) {
const domRect = getRectByBlockComponent(dropBlock); const domRect = getRectByBlockComponent(dropBlock);
const placement = 'in'; const placement = 'in';
const rect = Rect.fromLWTH( const rect = Rect.fromLWTH(
@@ -365,7 +373,7 @@ export class DragEventWatcher {
result.placement === 'in' ? model : this.std.store.getParent(model); result.placement === 'in' ? model : this.std.store.getParent(model);
if (!parent) return; if (!parent) return;
if (matchFlavours(parent, ['affine:surface'])) { if (matchFlavours(parent, [SurfaceBlockModel])) {
return; return;
} }
@@ -375,7 +383,7 @@ export class DragEventWatcher {
: parent.children.indexOf(model) + : parent.children.indexOf(model) +
(result.placement === 'before' ? 0 : 1); (result.placement === 'before' ? 0 : 1);
if (matchFlavours(parent, ['affine:note'])) { if (matchFlavours(parent, [NoteBlockModel])) {
const [first] = snapshot.content; const [first] = snapshot.content;
if (first.flavour === 'affine:note') { if (first.flavour === 'affine:note') {
if (parent.id !== first.id) { 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)); cleanups.push(this._makeDraggable(view));
} }

View File

@@ -14,7 +14,7 @@ import {
NoteBlockModel, NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
type RootBlockModel, type RootBlockModel,
type SurfaceRefBlockModel, SurfaceRefBlockModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { FeatureFlagService } from '@blocksuite/affine-shared/services'; import { FeatureFlagService } from '@blocksuite/affine-shared/services';
import { import {
@@ -195,7 +195,7 @@ export class EdgelessAutoConnectWidget extends WidgetComponent<RootBlockModel> {
const pageVisibleBlocks = new Map<AutoConnectElement, number>(); const pageVisibleBlocks = new Map<AutoConnectElement, number>();
const notes = service.doc.root?.children.filter(child => const notes = service.doc.root?.children.filter(child =>
matchFlavours(child, ['affine:note']) matchFlavours(child, [NoteBlockModel])
); );
const edgelessOnlyNotesSet = new Set<NoteBlockModel>(); const edgelessOnlyNotesSet = new Set<NoteBlockModel>();
@@ -209,7 +209,7 @@ export class EdgelessAutoConnectWidget extends WidgetComponent<RootBlockModel> {
} }
note.children.forEach(model => { note.children.forEach(model => {
if (matchFlavours(model, ['affine:surface-ref'])) { if (matchFlavours(model, [SurfaceRefBlockModel])) {
const reference = this._crud.getElementById(model.reference); const reference = this._crud.getElementById(model.reference);
if (!isAutoConnectElement(reference)) return; if (!isAutoConnectElement(reference)) return;

View File

@@ -1,4 +1,13 @@
import {
AttachmentBlockModel,
BookmarkBlockModel,
CodeBlockModel,
DatabaseBlockModel,
ImageBlockModel,
SurfaceRefBlockModel,
} from '@blocksuite/affine-model';
import { getSelectionRectsCommand } from '@blocksuite/affine-shared/commands'; 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 { matchFlavours } from '@blocksuite/affine-shared/utils';
import { import {
BlockSelection, BlockSelection,
@@ -67,16 +76,15 @@ export class AffineDocRemoteSelectionWidget extends WidgetComponent {
private get _config(): DocRemoteSelectionConfig { private get _config(): DocRemoteSelectionConfig {
return { return {
blockSelectionBackgroundTransparent: block => { blockSelectionBackgroundTransparent: block => {
return ( return matchFlavours(block, [
matchFlavours(block, [ CodeBlockModel,
'affine:code', DatabaseBlockModel,
'affine:database', ImageBlockModel,
'affine:image', AttachmentBlockModel,
'affine:attachment', BookmarkBlockModel,
'affine:bookmark', SurfaceRefBlockModel,
'affine:surface-ref', ...EMBED_BLOCK_MODEL_LIST,
]) || /affine:embed-*/.test(block.flavour) ]);
);
}, },
}; };
} }

View File

@@ -3,7 +3,9 @@ import {
SurfaceElementModel, SurfaceElementModel,
} from '@blocksuite/affine-block-surface'; } from '@blocksuite/affine-block-surface';
import { import {
FrameBlockModel,
GroupElementModel, GroupElementModel,
ImageBlockModel,
type RootBlockModel, type RootBlockModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { FetchUtils } from '@blocksuite/affine-shared/adapters'; import { FetchUtils } from '@blocksuite/affine-shared/adapters';
@@ -456,7 +458,7 @@ export class ExportManager {
edgeless?.service.gfx.getElementsByBound(bound, { type: 'block' }) ?? edgeless?.service.gfx.getElementsByBound(bound, { type: 'block' }) ??
[]; [];
for (const block of blocks) { for (const block of blocks) {
if (matchFlavours(block, ['affine:image'])) { if (matchFlavours(block, [ImageBlockModel])) {
if (!block.sourceId) return; if (!block.sourceId) return;
const blob = await block.doc.blobSync.get(block.sourceId); 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 // TODO(@L-Sun): use children of frame instead of bound
const blocksInsideFrame = getBlocksInFrameBound(this.doc, block, false); const blocksInsideFrame = getBlocksInFrameBound(this.doc, block, false);
const frameBound = Bound.deserialize(block.xywh); const frameBound = Bound.deserialize(block.xywh);

View File

@@ -11,6 +11,7 @@ import {
BookmarkStyles, BookmarkStyles,
DEFAULT_NOTE_HEIGHT, DEFAULT_NOTE_HEIGHT,
DEFAULT_NOTE_WIDTH, DEFAULT_NOTE_WIDTH,
FrameBlockModel,
MAX_IMAGE_WIDTH, MAX_IMAGE_WIDTH,
ReferenceInfoSchema, ReferenceInfoSchema,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
@@ -994,7 +995,7 @@ export class EdgelessClipboardController extends PageClipboard {
for (const nodeElement of nodeElements) { for (const nodeElement of nodeElements) {
await _drawTopLevelBlock(nodeElement); await _drawTopLevelBlock(nodeElement);
if (matchFlavours(nodeElement, ['affine:frame'])) { if (matchFlavours(nodeElement, [FrameBlockModel])) {
const blocksInsideFrame: BlockSuite.EdgelessBlockModelType[] = []; const blocksInsideFrame: BlockSuite.EdgelessBlockModelType[] = [];
this.edgeless.service.frame this.edgeless.service.frame
.getElementsInFrameBound(nodeElement, false) .getElementsInFrameBound(nodeElement, false)

View File

@@ -9,6 +9,7 @@ import {
GroupElementModel, GroupElementModel,
LayoutType, LayoutType,
MindmapElementModel, MindmapElementModel,
NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
type ShapeElementModel, type ShapeElementModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
@@ -134,7 +135,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
selection.selectedElements.length === 1 && selection.selectedElements.length === 1 &&
selection.firstElement instanceof GfxBlockElementModel && selection.firstElement instanceof GfxBlockElementModel &&
matchFlavours(selection.firstElement as GfxBlockElementModel, [ matchFlavours(selection.firstElement as GfxBlockElementModel, [
'affine:note', NoteBlockModel,
]) ])
) { ) {
rootComponent.slots.toggleNoteSlicer.emit(); rootComponent.slots.toggleNoteSlicer.emit();
@@ -259,7 +260,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
block => block =>
block.group === null && block.group === null &&
!( !(
matchFlavours(block, ['affine:note']) && matchFlavours(block, [NoteBlockModel]) &&
block.displayMode === NoteDisplayMode.DocOnly block.displayMode === NoteDisplayMode.DocOnly
) )
) )

View File

@@ -7,7 +7,7 @@ import {
normalizeWheelDeltaY, normalizeWheelDeltaY,
} from '@blocksuite/affine-block-surface'; } from '@blocksuite/affine-block-surface';
import { import {
type NoteBlockModel, NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
type RootBlockModel, type RootBlockModel,
type ShapeElementModel, type ShapeElementModel,
@@ -350,7 +350,7 @@ export class EdgelessRootBlockComponent extends BlockComponent<
const primaryMode = std.get(DocModeProvider).getPrimaryMode(this.doc.id); const primaryMode = std.get(DocModeProvider).getPrimaryMode(this.doc.id);
const note = this.model.children.find( const note = this.model.children.find(
(child): child is NoteBlockModel => (child): child is NoteBlockModel =>
matchFlavours(child, ['affine:note']) && matchFlavours(child, [NoteBlockModel]) &&
child.displayMode !== NoteDisplayMode.EdgelessOnly child.displayMode !== NoteDisplayMode.EdgelessOnly
); );

View File

@@ -5,6 +5,7 @@ import {
promptDocTitle, promptDocTitle,
} from '@blocksuite/affine-block-embed'; } from '@blocksuite/affine-block-embed';
import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph'; import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph';
import { NoteBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model';
import { import {
draftSelectedModelsCommand, draftSelectedModelsCommand,
getSelectedModelsCommand, getSelectedModelsCommand,
@@ -37,7 +38,7 @@ export class PageKeyboardManager {
const model = block.model; const model = block.model;
if ( if (
matchFlavours(model, ['affine:paragraph']) && matchFlavours(model, [ParagraphBlockModel]) &&
model.type.startsWith('h') && model.type.startsWith('h') &&
model.collapsed model.collapsed
) { ) {
@@ -133,7 +134,7 @@ export class PageKeyboardManager {
const selectedModels = ctx.selectedModels?.filter( const selectedModels = ctx.selectedModels?.filter(
block => block =>
!block.flavour.startsWith('affine:embed-') && !block.flavour.startsWith('affine:embed-') &&
matchFlavours(doc.getParent(block), ['affine:note']) matchFlavours(doc.getParent(block), [NoteBlockModel])
); );
const draftedModels = ctx.draftedModels; const draftedModels = ctx.draftedModels;

View File

@@ -1,6 +1,12 @@
import { focusTextModel } from '@blocksuite/affine-components/rich-text'; import { focusTextModel } from '@blocksuite/affine-components/rich-text';
import type { NoteBlockModel, RootBlockModel } from '@blocksuite/affine-model'; import {
import { NoteDisplayMode } from '@blocksuite/affine-model'; CodeBlockModel,
ListBlockModel,
NoteBlockModel,
NoteDisplayMode,
ParagraphBlockModel,
type RootBlockModel,
} from '@blocksuite/affine-model';
import { PageViewportService } from '@blocksuite/affine-shared/services'; import { PageViewportService } from '@blocksuite/affine-shared/services';
import type { Viewport } from '@blocksuite/affine-shared/types'; import type { Viewport } from '@blocksuite/affine-shared/types';
import { import {
@@ -122,7 +128,11 @@ export class PageRootBlockComponent extends BlockComponent<
focusFirstParagraph = (): { id: string; created: boolean } => { focusFirstParagraph = (): { id: string; created: boolean } => {
const defaultNote = this._getDefaultNoteBlock(); const defaultNote = this._getDefaultNoteBlock();
const firstText = defaultNote?.children.find(block => const firstText = defaultNote?.children.find(block =>
matchFlavours(block, ['affine:paragraph', 'affine:list', 'affine:code']) matchFlavours(block, [
ParagraphBlockModel,
ListBlockModel,
CodeBlockModel,
])
); );
if (firstText) { if (firstText) {
focusTextModel(this.std, firstText.id); focusTextModel(this.std, firstText.id);
@@ -238,9 +248,8 @@ export class PageRootBlockComponent extends BlockComponent<
'Mod-a': () => { 'Mod-a': () => {
const blocks = this.model.children const blocks = this.model.children
.filter(model => { .filter(model => {
if (matchFlavours(model, ['affine:note'])) { if (matchFlavours(model, [NoteBlockModel])) {
const note = model as NoteBlockModel; if (model.displayMode === NoteDisplayMode.EdgelessOnly)
if (note.displayMode === NoteDisplayMode.EdgelessOnly)
return false; return false;
return true; return true;
@@ -385,12 +394,11 @@ export class PageRootBlockComponent extends BlockComponent<
.slice() .slice()
.reverse() .reverse()
.find(child => { .find(child => {
const isNote = matchFlavours(child, ['affine:note']); const isNote = matchFlavours(child, [NoteBlockModel]);
if (!isNote) return false; if (!isNote) return false;
const note = child as NoteBlockModel;
const displayOnDoc = const displayOnDoc =
!!note.displayMode && !!child.displayMode &&
note.displayMode !== NoteDisplayMode.EdgelessOnly; child.displayMode !== NoteDisplayMode.EdgelessOnly;
return displayOnDoc; return displayOnDoc;
}); });
if (!lastNote) { if (!lastNote) {
@@ -402,7 +410,9 @@ export class PageRootBlockComponent extends BlockComponent<
const last = lastNote.children.at(-1); const last = lastNote.children.at(-1);
if ( if (
!last || !last ||
!(matchFlavours(last, ['affine:paragraph']) && last.text.length === 0) !(
matchFlavours(last, [ParagraphBlockModel]) && last.text.length === 0
)
) { ) {
if (readonly) return; if (readonly) return;
const paragraphId = this.doc.addBlock( const paragraphId = this.doc.addBlock(
@@ -442,7 +452,7 @@ export class PageRootBlockComponent extends BlockComponent<
override firstUpdated() { override firstUpdated() {
this._initViewportResizeEffect(); this._initViewportResizeEffect();
const noteModels = this.model.children.filter(model => const noteModels = this.model.children.filter(model =>
matchFlavours(model, ['affine:note']) matchFlavours(model, [NoteBlockModel])
); );
noteModels.forEach(note => { noteModels.forEach(note => {
this.disposables.add( this.disposables.add(
@@ -463,7 +473,7 @@ export class PageRootBlockComponent extends BlockComponent<
)}`; )}`;
const children = this.renderChildren(this.model, child => { const children = this.renderChildren(this.model, child => {
const isNote = matchFlavours(child, ['affine:note']); const isNote = matchFlavours(child, [NoteBlockModel]);
const note = child as NoteBlockModel; const note = child as NoteBlockModel;
const displayOnEdgeless = const displayOnEdgeless =
!!note.displayMode && note.displayMode === NoteDisplayMode.EdgelessOnly; !!note.displayMode && note.displayMode === NoteDisplayMode.EdgelessOnly;

View File

@@ -19,6 +19,7 @@ import {
DEFAULT_NOTE_HEIGHT, DEFAULT_NOTE_HEIGHT,
DefaultTheme, DefaultTheme,
type FrameBlockModel, type FrameBlockModel,
NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
resolveColor, resolveColor,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
@@ -92,7 +93,7 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) {
const rootModel = this.edgeless.doc.root; const rootModel = this.edgeless.doc.root;
const notes = rootModel.children.filter( const notes = rootModel.children.filter(
model => model =>
matchFlavours(model, ['affine:note']) && matchFlavours(model, [NoteBlockModel]) &&
model.displayMode !== NoteDisplayMode.EdgelessOnly model.displayMode !== NoteDisplayMode.EdgelessOnly
); );
const lastNote = notes[notes.length - 1]; const lastNote = notes[notes.length - 1];

View File

@@ -6,7 +6,11 @@ import {
import { toast } from '@blocksuite/affine-components/toast'; import { toast } from '@blocksuite/affine-components/toast';
import { renderToolbarSeparator } from '@blocksuite/affine-components/toolbar'; import { renderToolbarSeparator } from '@blocksuite/affine-components/toolbar';
import type { GroupElementModel } from '@blocksuite/affine-model'; 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 { matchFlavours } from '@blocksuite/affine-shared/utils';
import { import {
deserializeXYWH, deserializeXYWH,
@@ -27,7 +31,7 @@ export class EdgelessChangeGroupButton extends WithDisposable(LitElement) {
const rootModel = this.edgeless.doc.root; const rootModel = this.edgeless.doc.root;
const notes = rootModel.children.filter( const notes = rootModel.children.filter(
model => model =>
matchFlavours(model, ['affine:note']) && matchFlavours(model, [NoteBlockModel]) &&
model.displayMode !== NoteDisplayMode.EdgelessOnly model.displayMode !== NoteDisplayMode.EdgelessOnly
); );
const lastNote = notes[notes.length - 1]; const lastNote = notes[notes.length - 1];

View File

@@ -24,7 +24,7 @@ import {
import { import {
type ColorScheme, type ColorScheme,
DefaultTheme, DefaultTheme,
type NoteBlockModel, NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
resolveColor, resolveColor,
type StrokeStyle, type StrokeStyle,
@@ -158,7 +158,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
this._pageBlockEnabled && this._pageBlockEnabled &&
this.notes.length === 1 && this.notes.length === 1 &&
this.notes[0].parent?.children.find(child => this.notes[0].parent?.children.find(child =>
matchFlavours(child, ['affine:note']) matchFlavours(child, [NoteBlockModel])
) === this.notes[0] ) === this.notes[0]
); );
} }
@@ -340,7 +340,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
const isFirstNote = const isFirstNote =
onlyOne && onlyOne &&
note.parent?.children.find(child => note.parent?.children.find(child =>
matchFlavours(child, ['affine:note']) matchFlavours(child, [NoteBlockModel])
) === note; ) === note;
const theme = this.edgeless.std.get(ThemeProvider).theme; const theme = this.edgeless.std.get(ThemeProvider).theme;
const buttons = [ const buttons = [

View File

@@ -10,6 +10,12 @@ import {
getMoreMenuConfig, getMoreMenuConfig,
type MenuItemGroup, type MenuItemGroup,
} from '@blocksuite/affine-components/toolbar'; } from '@blocksuite/affine-components/toolbar';
import {
CodeBlockModel,
ImageBlockModel,
ListBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import { import {
getSelectedBlocksCommand, getSelectedBlocksCommand,
getTextSelectionCommand, getTextSelectionCommand,
@@ -355,10 +361,10 @@ export class AffineFormatBarWidget extends WidgetComponent {
const selectedBlock = this._selectedBlocks[0]; const selectedBlock = this._selectedBlocks[0];
if ( if (
!matchFlavours(selectedBlock.model, [ !matchFlavours(selectedBlock.model, [
'affine:paragraph', ParagraphBlockModel,
'affine:list', ListBlockModel,
'affine:code', CodeBlockModel,
'affine:image', ImageBlockModel,
]) ])
) { ) {
return false; return false;

View File

@@ -35,7 +35,7 @@ export interface LinkedWidgetConfig {
* [[ -> @ * [[ -> @
*/ */
convertTriggerKey: boolean; convertTriggerKey: boolean;
ignoreBlockTypes: (keyof BlockSuite.BlockModels)[]; ignoreBlockTypes: string[];
ignoreSelector: string; ignoreSelector: string;
getMenus: ( getMenus: (
query: string, query: string,

View File

@@ -1,4 +1,4 @@
import type { RootBlockModel } from '@blocksuite/affine-model'; import { NoteBlockModel, RootBlockModel } from '@blocksuite/affine-model';
import { import {
autoScroll, autoScroll,
getScrollContainer, getScrollContainer,
@@ -445,7 +445,7 @@ function isDragArea(e: PointerEventState) {
const el = e.raw.target; const el = e.raw.target;
assertInstanceOf(el, Element); assertInstanceOf(el, Element);
const block = el.closest<BlockComponent>(`[${BLOCK_ID_ATTR}]`); const block = el.closest<BlockComponent>(`[${BLOCK_ID_ATTR}]`);
return block && matchFlavours(block.model, ['affine:page', 'affine:note']); return block && matchFlavours(block.model, [RootBlockModel, NoteBlockModel]);
} }
declare global { declare global {

View File

@@ -84,7 +84,7 @@ import {
export type SlashMenuConfig = { export type SlashMenuConfig = {
triggerKeys: string[]; triggerKeys: string[];
ignoreBlockTypes: BlockSuite.Flavour[]; ignoreBlockTypes: string[];
items: SlashMenuItem[]; items: SlashMenuItem[];
maxHeight: number; maxHeight: number;
tooltipTimeout: number; tooltipTimeout: number;

View File

@@ -2,10 +2,7 @@ import {
type AffineInlineEditor, type AffineInlineEditor,
getInlineEditorByModel, getInlineEditorByModel,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import { import { getCurrentNativeRange } from '@blocksuite/affine-shared/utils';
getCurrentNativeRange,
matchFlavours,
} from '@blocksuite/affine-shared/utils';
import type { UIEventStateContext } from '@blocksuite/block-std'; import type { UIEventStateContext } from '@blocksuite/block-std';
import { TextSelection, WidgetComponent } from '@blocksuite/block-std'; import { TextSelection, WidgetComponent } from '@blocksuite/block-std';
import { import {
@@ -155,7 +152,7 @@ export class AffineSlashMenuWidget extends WidgetComponent {
const model = this.host.doc.getBlock(textSelection.blockId)?.model; const model = this.host.doc.getBlock(textSelection.blockId)?.model;
if (!model) return; if (!model) return;
if (matchFlavours(model, this.config.ignoreBlockTypes)) return; if (this.config.ignoreBlockTypes.includes(model.flavour)) return;
const inlineRange = inlineEditor.getInlineRange(); const inlineRange = inlineEditor.getInlineRange();
if (!inlineRange) return; if (!inlineRange) return;

View File

@@ -1,4 +1,4 @@
import { defineBlockSchema, type SchemaToModel } from '@blocksuite/store'; import { BlockModel, defineBlockSchema } from '@blocksuite/store';
export const RootBlockSchema = defineBlockSchema({ export const RootBlockSchema = defineBlockSchema({
flavour: 'test:page', flavour: 'test:page',
@@ -15,7 +15,9 @@ export const RootBlockSchema = defineBlockSchema({
}, },
}); });
export type RootBlockModel = SchemaToModel<typeof RootBlockSchema>; export class RootBlockModel extends BlockModel<
ReturnType<(typeof RootBlockSchema)['model']['props']>
> {}
export const NoteBlockSchema = defineBlockSchema({ export const NoteBlockSchema = defineBlockSchema({
flavour: 'test:note', flavour: 'test:note',
@@ -28,7 +30,9 @@ export const NoteBlockSchema = defineBlockSchema({
}, },
}); });
export type NoteBlockModel = SchemaToModel<typeof NoteBlockSchema>; export class NoteBlockModel extends BlockModel<
ReturnType<(typeof NoteBlockSchema)['model']['props']>
> {}
export const HeadingBlockSchema = defineBlockSchema({ export const HeadingBlockSchema = defineBlockSchema({
flavour: 'test:heading', flavour: 'test:heading',
@@ -43,7 +47,9 @@ export const HeadingBlockSchema = defineBlockSchema({
}, },
}); });
export type HeadingBlockModel = SchemaToModel<typeof HeadingBlockSchema>; export class HeadingBlockModel extends BlockModel<
ReturnType<(typeof HeadingBlockSchema)['model']['props']>
> {}
declare global { declare global {
namespace BlockSuite { namespace BlockSuite {

View File

@@ -4,9 +4,9 @@ import * as Y from 'yjs';
import { import {
Block, Block,
BlockModel,
defineBlockSchema, defineBlockSchema,
internalPrimitives, internalPrimitives,
type SchemaToModel,
} from '../model/block/index.js'; } from '../model/block/index.js';
import type { YBlock } from '../model/block/types.js'; import type { YBlock } from '../model/block/types.js';
import { Schema } from '../schema/index.js'; import { Schema } from '../schema/index.js';
@@ -54,9 +54,15 @@ const flatTableSchema = defineBlockSchema({
isFlatData: true, isFlatData: true,
}, },
}); });
type RootModel = SchemaToModel<typeof pageSchema>; class RootModel extends BlockModel<
type TableModel = SchemaToModel<typeof tableSchema>; ReturnType<(typeof pageSchema)['model']['props']>
type FlatTableModel = SchemaToModel<typeof flatTableSchema>; > {}
class TableModel extends BlockModel<
ReturnType<(typeof tableSchema)['model']['props']>
> {}
class FlatTableModel extends BlockModel<
ReturnType<(typeof flatTableSchema)['model']['props']>
> {}
function createTestOptions() { function createTestOptions() {
const idGenerator = createAutoIncrementIdGenerator(); const idGenerator = createAutoIncrementIdGenerator();

View File

@@ -1,4 +1,4 @@
import { defineBlockSchema, type SchemaToModel } from '../model/index.js'; import { BlockModel, defineBlockSchema } from '../model/index.js';
export const RootBlockSchema = defineBlockSchema({ export const RootBlockSchema = defineBlockSchema({
flavour: 'affine:page', flavour: 'affine:page',
@@ -14,7 +14,9 @@ export const RootBlockSchema = defineBlockSchema({
}, },
}); });
export type RootBlockModel = SchemaToModel<typeof RootBlockSchema>; export class RootBlockModel extends BlockModel<
ReturnType<(typeof RootBlockSchema)['model']['props']>
> {}
export const NoteBlockSchema = defineBlockSchema({ export const NoteBlockSchema = defineBlockSchema({
flavour: 'affine:note', flavour: 'affine:note',

View File

@@ -2,8 +2,8 @@ import { expect, test } from 'vitest';
import * as Y from 'yjs'; import * as Y from 'yjs';
import { MemoryBlobCRUD } from '../adapter/index.js'; import { MemoryBlobCRUD } from '../adapter/index.js';
import type { BlockModel } from '../model/block/block-model.js'; import { BlockModel } from '../model/block/block-model.js';
import { defineBlockSchema, type SchemaToModel } from '../model/block/zod.js'; import { defineBlockSchema } from '../model/block/zod.js';
import { Text } from '../reactive/index.js'; import { Text } from '../reactive/index.js';
import { Schema } from '../schema/index.js'; import { Schema } from '../schema/index.js';
import { createAutoIncrementIdGenerator } from '../test/index.js'; import { createAutoIncrementIdGenerator } from '../test/index.js';
@@ -39,7 +39,9 @@ const docSchema = defineBlockSchema({
}, },
}); });
type RootBlockModel = SchemaToModel<typeof docSchema>; class RootBlockModel extends BlockModel<
ReturnType<(typeof docSchema)['model']['props']>
> {}
function createTestOptions() { function createTestOptions() {
const idGenerator = createAutoIncrementIdGenerator(); const idGenerator = createAutoIncrementIdGenerator();

View File

@@ -20,9 +20,7 @@ type SignaledProps<Props> = Props & {
* myBlock.foo = 'bar'; * myBlock.foo = 'bar';
* ``` * ```
*/ */
function MagicProps(): { function MagicProps(): { new <Props>(): Props } {
new <Props>(): Props;
} {
return class {} as never; return class {} as never;
} }

View File

@@ -51,18 +51,6 @@ export type PropsGetter<Props> = (
internalPrimitives: InternalPrimitives internalPrimitives: InternalPrimitives
) => Props; ) => Props;
export type SchemaToModel<
Schema extends {
model: {
props: PropsGetter<object>;
flavour: string;
};
},
> = BlockModel<ReturnType<Schema['model']['props']>> &
ReturnType<Schema['model']['props']> & {
flavour: Schema['model']['flavour'];
};
export function defineBlockSchema< export function defineBlockSchema<
Flavour extends string, Flavour extends string,
Role extends RoleType, Role extends RoleType,

View File

@@ -451,20 +451,6 @@ export class Store {
} }
} }
addBlock<Key extends BlockSuite.Flavour>(
flavour: Key,
blockProps?: BlockSuite.ModelProps<BlockSuite.BlockModels[Key]>,
parent?: BlockModel | string | null,
parentIndex?: number
): string;
addBlock(
flavour: never,
blockProps?: Partial<BlockProps & Omit<BlockProps, 'flavour'>>,
parent?: BlockModel | string | null,
parentIndex?: number
): string;
addBlock( addBlock(
flavour: string, flavour: string,
blockProps: Partial<BlockProps & Omit<BlockProps, 'flavour'>> = {}, blockProps: Partial<BlockProps & Omit<BlockProps, 'flavour'>> = {},

View File

@@ -1,9 +1,9 @@
import { effects } from '@blocksuite/affine-block-note/effects'; import { effects } from '@blocksuite/affine-block-note/effects';
import { ShadowlessElement, SurfaceSelection } from '@blocksuite/block-std'; import { ShadowlessElement, SurfaceSelection } from '@blocksuite/block-std';
import type { NoteBlockModel } from '@blocksuite/blocks';
import { import {
changeNoteDisplayMode, changeNoteDisplayMode,
matchFlavours, matchFlavours,
NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
} from '@blocksuite/blocks'; } from '@blocksuite/blocks';
import { import {
@@ -155,7 +155,7 @@ export class OutlinePanelBody extends SignalWatcher(
const noteIndex = new Map<NoteBlockModel, number>(); const noteIndex = new Map<NoteBlockModel, number>();
children.forEach((block, index) => { children.forEach((block, index) => {
if (matchFlavours(block, ['affine:note'])) { if (matchFlavours(block, [NoteBlockModel])) {
noteIndex.set(block, index); noteIndex.set(block, index);
} }
}); });
@@ -171,7 +171,7 @@ export class OutlinePanelBody extends SignalWatcher(
if (targetIndex === null) return; if (targetIndex === null) return;
const removeSelectedNoteFilter = (block: BlockModel) => const removeSelectedNoteFilter = (block: BlockModel) =>
!matchFlavours(block, ['affine:note']) || !selected.includes(block); !matchFlavours(block, [NoteBlockModel]) || !selected.includes(block);
const leftPart = children const leftPart = children
.slice(0, targetIndex) .slice(0, targetIndex)
@@ -200,7 +200,7 @@ export class OutlinePanelBody extends SignalWatcher(
private _selectNote(e: SelectEvent) { private _selectNote(e: SelectEvent) {
const { selected, id, multiselect } = e.detail; const { selected, id, multiselect } = e.detail;
const note = this.doc.getBlock(id)?.model; 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(); let selectedNotes = this._selectedNotes$.peek();
@@ -231,7 +231,7 @@ export class OutlinePanelBody extends SignalWatcher(
.filter(SurfaceSelection) .filter(SurfaceSelection)
.map(({ blockId }) => doc.getBlock(blockId)?.model) .map(({ blockId }) => doc.getBlock(blockId)?.model)
.filter(model => { .filter(model => {
return !!model && matchFlavours(model, ['affine:note']); return !!model && matchFlavours(model, [NoteBlockModel]);
}); });
const preSelected = this._selectedNotes$.peek(); const preSelected = this._selectedNotes$.peek();

View File

@@ -122,7 +122,7 @@ export class OutlineBlockPreview extends SignalWatcher(
const showPreviewIcon = this._context.showIcons$.value; const showPreviewIcon = this._context.showIcons$.value;
switch (block.flavour as keyof BlockSuite.BlockModels) { switch (block.flavour) {
case 'affine:page': case 'affine:page':
assertType<RootBlockModel>(block); assertType<RootBlockModel>(block);
return block.title.length > 0 return block.title.length > 0

View File

@@ -1,6 +1,11 @@
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme'; import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { PropTypes, requiredProperties } from '@blocksuite/block-std'; 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 { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
import { signal } from '@preact/signals-core'; import { signal } from '@preact/signals-core';
@@ -129,11 +134,11 @@ export class MobileOutlineMenu extends SignalWatcher(
renderItem = (item: BlockModel) => { renderItem = (item: BlockModel) => {
let className = ''; let className = '';
let text = ''; let text = '';
if (matchFlavours(item, ['affine:page'])) { if (matchFlavours(item, [RootBlockModel])) {
className = 'title'; className = 'title';
text = item.title$.value.toString(); text = item.title$.value.toString();
} else if ( } else if (
matchFlavours(item, ['affine:paragraph']) && matchFlavours(item, [ParagraphBlockModel]) &&
['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(item.type$.value) ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(item.type$.value)
) { ) {
className = item.type$.value; className = item.type$.value;

View File

@@ -1,10 +1,10 @@
import { import {
BlocksUtils, BlocksUtils,
matchFlavours, matchFlavours,
type NoteBlockModel, NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
type ParagraphBlockModel, ParagraphBlockModel,
type RootBlockModel, RootBlockModel,
} from '@blocksuite/blocks'; } from '@blocksuite/blocks';
import type { BlockModel, Store } from '@blocksuite/store'; import type { BlockModel, Store } from '@blocksuite/store';
@@ -24,7 +24,7 @@ export function getNotesFromDoc(
const notes: NoteBlockModel[] = []; const notes: NoteBlockModel[] = [];
rootModel.children.forEach(block => { rootModel.children.forEach(block => {
if (!matchFlavours(block, ['affine:note'])) return; if (!matchFlavours(block, [NoteBlockModel])) return;
if (modes.includes(block.displayMode$.value)) { if (modes.includes(block.displayMode$.value)) {
notes.push(block); notes.push(block);
@@ -35,14 +35,14 @@ export function getNotesFromDoc(
} }
export function isRootBlock(block: BlockModel): block is RootBlockModel { export function isRootBlock(block: BlockModel): block is RootBlockModel {
return BlocksUtils.matchFlavours(block, ['affine:page']); return BlocksUtils.matchFlavours(block, [RootBlockModel]);
} }
export function isHeadingBlock( export function isHeadingBlock(
block: BlockModel block: BlockModel
): block is ParagraphBlockModel { ): block is ParagraphBlockModel {
return ( return (
BlocksUtils.matchFlavours(block, ['affine:paragraph']) && BlocksUtils.matchFlavours(block, [ParagraphBlockModel]) &&
headingKeys.has(block.type$.value) headingKeys.has(block.type$.value)
); );
} }

View File

@@ -17,6 +17,7 @@ import {
getSelectedBlocksCommand, getSelectedBlocksCommand,
NoteDisplayMode, NoteDisplayMode,
NotificationProvider, NotificationProvider,
ParagraphBlockModel,
RefNodeSlotsProvider, RefNodeSlotsProvider,
TelemetryProvider, TelemetryProvider,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
@@ -166,7 +167,7 @@ function addAIChatBlock(
const y = viewportCenter.y - height / 2; const y = viewportCenter.y - height / 2;
const bound = new Bound(x, y, width, height); const bound = new Bound(x, y, width, height);
const aiChatBlockId = doc.addBlock( const aiChatBlockId = doc.addBlock(
'affine:embed-ai-chat' as keyof BlockSuite.BlockModels, 'affine:embed-ai-chat',
{ {
xywh: bound.serialize(), xywh: bound.serialize(),
messages: JSON.stringify(messages), messages: JSON.stringify(messages),
@@ -231,7 +232,7 @@ const REPLACE_SELECTION = {
if (currentTextSelection) { if (currentTextSelection) {
const { doc } = host; const { doc } = host;
const block = doc.getBlock(currentTextSelection.blockId); const block = doc.getBlock(currentTextSelection.blockId);
if (matchFlavours(block?.model ?? null, ['affine:paragraph'])) { if (matchFlavours(block?.model ?? null, [ParagraphBlockModel])) {
block?.model.text?.replace( block?.model.text?.replace(
currentTextSelection.from.index, currentTextSelection.from.index,
currentTextSelection.from.length, currentTextSelection.from.length,

View File

@@ -2,8 +2,12 @@ import type { Chain, InitCommandCtx } from '@blocksuite/affine/block-std';
import { import {
type AIItemGroupConfig, type AIItemGroupConfig,
type AISubItemConfig, type AISubItemConfig,
CodeBlockModel,
getSelectedModelsCommand, getSelectedModelsCommand,
ImageBlockModel,
ListBlockModel,
matchFlavours, matchFlavours,
ParagraphBlockModel,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { actionToHandler } from '../actions/doc-handler'; import { actionToHandler } from '../actions/doc-handler';
@@ -103,7 +107,7 @@ const textBlockShowWhen = (chain: Chain<InitCommandCtx>) => {
if (!selectedModels || selectedModels.length === 0) return false; if (!selectedModels || selectedModels.length === 0) return false;
return selectedModels.some(model => return selectedModels.some(model =>
matchFlavours(model, ['affine:paragraph', 'affine:list']) matchFlavours(model, [ParagraphBlockModel, ListBlockModel])
); );
}; };
@@ -117,7 +121,7 @@ const codeBlockShowWhen = (chain: Chain<InitCommandCtx>) => {
if (!selectedModels || selectedModels.length > 1) return false; if (!selectedModels || selectedModels.length > 1) return false;
const model = selectedModels[0]; const model = selectedModels[0];
return matchFlavours(model, ['affine:code']); return matchFlavours(model, [CodeBlockModel]);
}; };
const imageBlockShowWhen = (chain: Chain<InitCommandCtx>) => { const imageBlockShowWhen = (chain: Chain<InitCommandCtx>) => {
@@ -130,7 +134,7 @@ const imageBlockShowWhen = (chain: Chain<InitCommandCtx>) => {
if (!selectedModels || selectedModels.length > 1) return false; if (!selectedModels || selectedModels.length > 1) return false;
const model = selectedModels[0]; const model = selectedModels[0];
return matchFlavours(model, ['affine:image']); return matchFlavours(model, [ImageBlockModel]);
}; };
const EditAIGroup: AIItemGroupConfig = { const EditAIGroup: AIItemGroupConfig = {
@@ -278,7 +282,7 @@ const GenerateWithAIGroup: AIItemGroupConfig = {
return selectedModels.every( return selectedModels.every(
model => model =>
matchFlavours(model, ['affine:paragraph', 'affine:list']) && matchFlavours(model, [ParagraphBlockModel, ListBlockModel]) &&
!model.type.startsWith('h') !model.type.startsWith('h')
); );
}, },

View File

@@ -7,6 +7,7 @@ import type {
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { import {
BlocksUtils, BlocksUtils,
CodeBlockModel,
EdgelessTextBlockModel, EdgelessTextBlockModel,
EmbedSyncedDocModel, EmbedSyncedDocModel,
ImageBlockModel, ImageBlockModel,
@@ -485,7 +486,7 @@ export function noteWithCodeBlockShowWen(
return ( return (
selected[0] instanceof NoteBlockModel && selected[0] instanceof NoteBlockModel &&
selected[0].children.length === 1 && selected[0].children.length === 1 &&
BlocksUtils.matchFlavours(selected[0].children[0], ['affine:code']) BlocksUtils.matchFlavours(selected[0].children[0], [CodeBlockModel])
); );
} }

View File

@@ -527,7 +527,7 @@ async function responseToCreateSlides(host: EditorHost, ctx: AIContext) {
await job.insertTemplate(content); await job.insertTemplate(content);
} }
getSurfaceElementFromEditor(host).refresh(); getSurfaceElementFromEditor(host)?.refresh();
} catch (error) { } catch (error) {
console.error('Error creating slides:', error); console.error('Error creating slides:', error);
} }

View File

@@ -7,6 +7,7 @@ import {
ImageBlockModel, ImageBlockModel,
isInsideEdgelessEditor, isInsideEdgelessEditor,
matchFlavours, matchFlavours,
NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { assertExists, Bound } from '@blocksuite/affine/global/utils'; 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 // set the viewport to show the new note block and original note block
const newNote = doc.getBlock(noteBlockId)?.model; 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 newNoteBound = Bound.deserialize(newNote.xywh);
const bounds = [bound, newNoteBound]; const bounds = [bound, newNoteBound];
service.gfx.fitToScreen({ service.gfx.fitToScreen({

View File

@@ -175,7 +175,7 @@ export class AIChatBlockPeekView extends LitElement {
const edgelessService = this._rootService as EdgelessRootService; const edgelessService = this._rootService as EdgelessRootService;
const bound = calcChildBound(this.parentModel, edgelessService); const bound = calcChildBound(this.parentModel, edgelessService);
const aiChatBlockId = edgelessService.crud.addBlock( const aiChatBlockId = edgelessService.crud.addBlock(
'affine:embed-ai-chat' as keyof BlockSuite.BlockModels, 'affine:embed-ai-chat',
{ {
xywh: bound.serialize(), xywh: bound.serialize(),
messages: JSON.stringify(messages), messages: JSON.stringify(messages),

View File

@@ -51,7 +51,7 @@ export const PPTBuilder = (host: EditorHost) => {
); );
} }
await job.insertTemplate(content); await job.insertTemplate(content);
getSurfaceElementFromEditor(host).refresh(); getSurfaceElementFromEditor(host)?.refresh();
}; };
return { return {

View File

@@ -5,7 +5,10 @@ import {
type EdgelessRootService, type EdgelessRootService,
matchFlavours, matchFlavours,
MindmapElementModel, MindmapElementModel,
NoteBlockModel,
RootBlockModel,
type ShapeElementModel, type ShapeElementModel,
SurfaceBlockModel,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
export function mindMapToMarkdown(mindmap: MindmapElementModel) { export function mindMapToMarkdown(mindmap: MindmapElementModel) {
@@ -62,10 +65,10 @@ export function getEdgelessCopilotWidget(
export function findNoteBlockModel(blockElement: BlockComponent) { export function findNoteBlockModel(blockElement: BlockComponent) {
let curBlock = blockElement; let curBlock = blockElement;
while (curBlock) { while (curBlock) {
if (matchFlavours(curBlock.model, ['affine:note'])) { if (matchFlavours(curBlock.model, [NoteBlockModel])) {
return curBlock.model; return curBlock.model;
} }
if (matchFlavours(curBlock.model, ['affine:page', 'affine:surface'])) { if (matchFlavours(curBlock.model, [RootBlockModel, SurfaceBlockModel])) {
return null; return null;
} }
if (!curBlock.parentComponent) { if (!curBlock.parentComponent) {

View File

@@ -1,11 +1,12 @@
import type { EditorHost } from '@blocksuite/affine/block-std'; import type { EditorHost } from '@blocksuite/affine/block-std';
import { import {
BlocksUtils, BlocksUtils,
DatabaseBlockModel,
DocModeProvider, DocModeProvider,
embedSyncedDocMiddleware, embedSyncedDocMiddleware,
getImageSelectionsCommand, getImageSelectionsCommand,
getSelectedBlocksCommand, getSelectedBlocksCommand,
type ImageBlockModel, ImageBlockModel,
isInsideEdgelessEditor, isInsideEdgelessEditor,
MarkdownAdapter, MarkdownAdapter,
type NoteBlockModel, type NoteBlockModel,
@@ -163,7 +164,7 @@ export async function extractMarkdownFromDoc(
const blockModels = getNoteBlockModels(doc); const blockModels = getNoteBlockModels(doc);
const textModels = blockModels.filter( const textModels = blockModels.filter(
model => model =>
!BlocksUtils.matchFlavours(model, ['affine:image', 'affine:database']) !BlocksUtils.matchFlavours(model, [ImageBlockModel, DatabaseBlockModel])
); );
const drafts = textModels.map(toDraftModel); const drafts = textModels.map(toDraftModel);
const slice = Slice.fromModels(doc, drafts); const slice = Slice.fromModels(doc, drafts);

View File

@@ -3,6 +3,7 @@ import type { GfxModel } from '@blocksuite/affine/block-std/gfx';
import { import {
BlocksUtils, BlocksUtils,
type CopilotTool, type CopilotTool,
DatabaseBlockModel,
EdgelessRootService, EdgelessRootService,
type FrameBlockModel, type FrameBlockModel,
getBlockSelectionsCommand, getBlockSelectionsCommand,
@@ -13,7 +14,6 @@ import {
ImageBlockModel, ImageBlockModel,
type SurfaceBlockComponent, type SurfaceBlockComponent,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { assertExists } from '@blocksuite/affine/global/utils';
import { import {
type BlockModel, type BlockModel,
type DraftModel, type DraftModel,
@@ -134,7 +134,7 @@ export async function getTextContentFromBlockModels(
// Currently only filter out images and databases // Currently only filter out images and databases
const selectedTextModels = models.filter( const selectedTextModels = models.filter(
model => model =>
!BlocksUtils.matchFlavours(model, ['affine:image', 'affine:database']) !BlocksUtils.matchFlavours(model, [ImageBlockModel, DatabaseBlockModel])
); );
const drafts = selectedTextModels.map(toDraftModel); const drafts = selectedTextModels.map(toDraftModel);
drafts.forEach(draft => traverse(draft, drafts)); drafts.forEach(draft => traverse(draft, drafts));
@@ -147,13 +147,13 @@ export async function getSelectedTextContent(
type: 'markdown' | 'plain-text' = 'markdown' type: 'markdown' | 'plain-text' = 'markdown'
) { ) {
const selectedModels = getSelectedModels(editorHost); const selectedModels = getSelectedModels(editorHost);
assertExists(selectedModels); if (!selectedModels) return '';
return getTextContentFromBlockModels(editorHost, selectedModels, type); return getTextContentFromBlockModels(editorHost, selectedModels, type);
} }
export async function selectAboveBlocks(editorHost: EditorHost, num = 10) { export async function selectAboveBlocks(editorHost: EditorHost, num = 10) {
let selectedModels = getSelectedModels(editorHost); let selectedModels = getSelectedModels(editorHost);
assertExists(selectedModels); if (!selectedModels) return '';
const lastLeafModel = selectedModels[selectedModels.length - 1]; const lastLeafModel = selectedModels[selectedModels.length - 1];
@@ -163,8 +163,7 @@ export async function selectAboveBlocks(editorHost: EditorHost, num = 10) {
lastRootModel = noteModel; lastRootModel = noteModel;
noteModel = editorHost.doc.getParent(noteModel); noteModel = editorHost.doc.getParent(noteModel);
} }
assertExists(noteModel); if (!noteModel || !lastRootModel) return '';
assertExists(lastRootModel);
const endIndex = noteModel.children.indexOf(lastRootModel) + 1; const endIndex = noteModel.children.indexOf(lastRootModel) + 1;
const startIndex = Math.max(0, endIndex - num); const startIndex = Math.max(0, endIndex - num);
@@ -212,13 +211,13 @@ export const stopPropagation = (e: Event) => {
export function getSurfaceElementFromEditor(editor: EditorHost) { export function getSurfaceElementFromEditor(editor: EditorHost) {
const { doc } = editor; const { doc } = editor;
const surfaceModel = doc.getBlockByFlavour('affine:surface')[0]; const surfaceModel = doc.getBlockByFlavour('affine:surface')[0];
assertExists(surfaceModel); if (!surfaceModel) return null;
const surfaceId = surfaceModel.id; const surfaceId = surfaceModel.id;
const surfaceElement = editor.querySelector( const surfaceElement = editor.querySelector(
`affine-surface[data-block-id="${surfaceId}"]` `affine-surface[data-block-id="${surfaceId}"]`
) as SurfaceBlockComponent; ) as SurfaceBlockComponent;
assertExists(surfaceElement); if (!surfaceElement) return null;
return surfaceElement; return surfaceElement;
} }

View File

@@ -11,7 +11,7 @@ import { track } from '@affine/track';
import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx'; import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
import { import {
matchFlavours, matchFlavours,
type NoteBlockModel, NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { Bound } from '@blocksuite/affine/global/utils'; import { Bound } from '@blocksuite/affine/global/utils';
@@ -176,7 +176,7 @@ export const EdgelessNoteHeader = ({ note }: { note: NoteBlockModel }) => {
const isFirstVisibleNote = const isFirstVisibleNote =
note.parent?.children.find( note.parent?.children.find(
child => child =>
matchFlavours(child, ['affine:note']) && matchFlavours(child, [NoteBlockModel]) &&
child.displayMode === NoteDisplayMode.DocAndEdgeless child.displayMode === NoteDisplayMode.DocAndEdgeless
) === note; ) === note;