diff --git a/blocksuite/affine/block-bookmark/src/bookmark-spec.ts b/blocksuite/affine/block-bookmark/src/bookmark-spec.ts index ec870ba6aa..0bd93b6ae3 100644 --- a/blocksuite/affine/block-bookmark/src/bookmark-spec.ts +++ b/blocksuite/affine/block-bookmark/src/bookmark-spec.ts @@ -1,17 +1,11 @@ -import { - BlockViewExtension, - CommandExtension, - FlavourExtension, -} from '@blocksuite/block-std'; +import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; import { BookmarkBlockAdapterExtensions } from './adapters/extension.js'; -import { commands } from './commands/index.js'; export const BookmarkBlockSpec: ExtensionType[] = [ FlavourExtension('affine:bookmark'), - CommandExtension(commands), BlockViewExtension('affine:bookmark', model => { return model.parent?.flavour === 'affine:surface' ? literal`affine-edgeless-bookmark` diff --git a/blocksuite/affine/block-bookmark/src/commands/index.ts b/blocksuite/affine/block-bookmark/src/commands/index.ts index db4864967f..ccf842c89b 100644 --- a/blocksuite/affine/block-bookmark/src/commands/index.ts +++ b/blocksuite/affine/block-bookmark/src/commands/index.ts @@ -1,9 +1,2 @@ -import type { BlockCommands } from '@blocksuite/block-std'; - -import { insertBookmarkCommand } from './insert-bookmark.js'; -import { insertLinkByQuickSearchCommand } from './insert-link-by-quick-search.js'; - -export const commands: BlockCommands = { - insertBookmark: insertBookmarkCommand, - insertLinkByQuickSearch: insertLinkByQuickSearchCommand, -}; +export { insertBookmarkCommand } from './insert-bookmark.js'; +export { insertLinkByQuickSearchCommand } from './insert-link-by-quick-search.js'; diff --git a/blocksuite/affine/block-bookmark/src/commands/insert-bookmark.ts b/blocksuite/affine/block-bookmark/src/commands/insert-bookmark.ts index f6d586c168..79afdc99e0 100644 --- a/blocksuite/affine/block-bookmark/src/commands/insert-bookmark.ts +++ b/blocksuite/affine/block-bookmark/src/commands/insert-bookmark.ts @@ -5,11 +5,7 @@ import type { EmbedCardStyle } from '@blocksuite/affine-model'; import { EmbedOptionProvider } from '@blocksuite/affine-shared/services'; import type { Command } from '@blocksuite/block-std'; -export const insertBookmarkCommand: Command< - never, - 'insertedLinkType', - { url: string } -> = (ctx, next) => { +export const insertBookmarkCommand: Command<{ url: string }> = (ctx, next) => { const { url, std } = ctx; const embedOptions = std.get(EmbedOptionProvider).getEmbedBlockOptions(url); diff --git a/blocksuite/affine/block-bookmark/src/commands/insert-link-by-quick-search.ts b/blocksuite/affine/block-bookmark/src/commands/insert-link-by-quick-search.ts index a4539f4da8..8d82d74bed 100644 --- a/blocksuite/affine/block-bookmark/src/commands/insert-link-by-quick-search.ts +++ b/blocksuite/affine/block-bookmark/src/commands/insert-link-by-quick-search.ts @@ -1,10 +1,15 @@ -import type { InsertedLinkType } from '@blocksuite/affine-block-embed'; +import { + type InsertedLinkType, + insertEmbedLinkedDocCommand, +} from '@blocksuite/affine-block-embed'; import { QuickSearchProvider } from '@blocksuite/affine-shared/services'; import type { Command } from '@blocksuite/block-std'; +import { insertBookmarkCommand } from './insert-bookmark'; + export const insertLinkByQuickSearchCommand: Command< - never, - 'insertedLinkType' + {}, + { insertedLinkType: Promise } > = (ctx, next) => { const { std } = ctx; const quickSearchService = std.getOptional(QuickSearchProvider); @@ -20,7 +25,7 @@ export const insertLinkByQuickSearchCommand: Command< // add linked doc if ('docId' in result) { - std.command.exec('insertEmbedLinkedDoc', { + std.command.exec(insertEmbedLinkedDocCommand, { docId: result.docId, params: result.params, }); @@ -31,7 +36,7 @@ export const insertLinkByQuickSearchCommand: Command< // add normal link; if ('externalUrl' in result) { - std.command.exec('insertBookmark', { url: result.externalUrl }); + std.command.exec(insertBookmarkCommand, { url: result.externalUrl }); return { flavour: 'affine:bookmark', }; diff --git a/blocksuite/affine/block-bookmark/src/effects.ts b/blocksuite/affine/block-bookmark/src/effects.ts index 6bcab7a6d0..62dfc50d3e 100644 --- a/blocksuite/affine/block-bookmark/src/effects.ts +++ b/blocksuite/affine/block-bookmark/src/effects.ts @@ -1,7 +1,5 @@ import { BookmarkBlockComponent } from './bookmark-block'; import { BookmarkEdgelessBlockComponent } from './bookmark-edgeless-block'; -import type { insertBookmarkCommand } from './commands/insert-bookmark'; -import type { insertLinkByQuickSearchCommand } from './commands/insert-link-by-quick-search'; import { BookmarkCard } from './components/bookmark-card'; import { EmbedCardCreateModal, @@ -24,12 +22,3 @@ export function effects() { EmbedCardEditCaptionEditModal ); } - -declare global { - namespace BlockSuite { - interface Commands { - insertBookmark: typeof insertBookmarkCommand; - insertLinkByQuickSearch: typeof insertLinkByQuickSearchCommand; - } - } -} diff --git a/blocksuite/affine/block-bookmark/src/index.ts b/blocksuite/affine/block-bookmark/src/index.ts index 1698b867dc..f6f0991e69 100644 --- a/blocksuite/affine/block-bookmark/src/index.ts +++ b/blocksuite/affine/block-bookmark/src/index.ts @@ -1,4 +1,5 @@ export * from './adapters'; export * from './bookmark-block'; export * from './bookmark-spec'; +export * from './commands'; export * from './components'; diff --git a/blocksuite/affine/block-code/src/clipboard/index.ts b/blocksuite/affine/block-code/src/clipboard/index.ts index cc1dbce1cb..e59f9618dd 100644 --- a/blocksuite/affine/block-code/src/clipboard/index.ts +++ b/blocksuite/affine/block-code/src/clipboard/index.ts @@ -1,8 +1,14 @@ +import { deleteTextCommand } from '@blocksuite/affine-components/rich-text'; import { HtmlAdapter, pasteMiddleware, PlainTextAdapter, } from '@blocksuite/affine-shared/adapters'; +import { + getBlockIndexCommand, + getBlockSelectionsCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import { type BlockComponent, Clipboard, @@ -40,13 +46,13 @@ export class CodeClipboardController { this._std.command .chain() .try(cmd => [ - cmd.getTextSelection().inline<'currentSelectionPath'>((ctx, next) => { + cmd.pipe(getTextSelectionCommand).pipe((ctx, next) => { const textSelection = ctx.currentTextSelection; if (!textSelection) return; const end = textSelection.to ?? textSelection.from; next({ currentSelectionPath: end.blockId }); }), - cmd.getBlockSelections().inline<'currentSelectionPath'>((ctx, next) => { + cmd.pipe(getBlockSelectionsCommand).pipe((ctx, next) => { const currentBlockSelections = ctx.currentBlockSelections; if (!currentBlockSelections) return; const blockSelection = currentBlockSelections.at(-1); @@ -54,9 +60,9 @@ export class CodeClipboardController { next({ currentSelectionPath: blockSelection.blockId }); }), ]) - .getBlockIndex() - .try(cmd => [cmd.getTextSelection().deleteText()]) - .inline((ctx, next) => { + .pipe(getBlockIndexCommand) + .try(cmd => [cmd.pipe(getTextSelectionCommand).pipe(deleteTextCommand)]) + .pipe((ctx, next) => { if (!ctx.parentBlock) { return; } diff --git a/blocksuite/affine/block-database/src/commands.ts b/blocksuite/affine/block-database/src/commands.ts index e7572683fd..e6939a77b9 100644 --- a/blocksuite/affine/block-database/src/commands.ts +++ b/blocksuite/affine/block-database/src/commands.ts @@ -1,5 +1,5 @@ import type { DatabaseBlockModel } from '@blocksuite/affine-model'; -import type { BlockCommands, Command } from '@blocksuite/block-std'; +import type { Command } from '@blocksuite/block-std'; import type { BlockModel, Store } from '@blocksuite/store'; import { @@ -8,12 +8,14 @@ import { } from './data-source'; export const insertDatabaseBlockCommand: Command< - 'selectedModels', - 'insertedDatabaseBlockId', { + selectedModels?: BlockModel[]; viewType: string; place?: 'after' | 'before'; removeEmptyLine?: boolean; + }, + { + insertedDatabaseBlockId: string; } > = (ctx, next) => { const { selectedModels, viewType, place, removeEmptyLine, std } = ctx; @@ -65,7 +67,3 @@ export const initDatabaseBlock = ( doc.addBlock('affine:paragraph', {}, parent.id); } }; - -export const commands: BlockCommands = { - insertDatabaseBlock: insertDatabaseBlockCommand, -}; diff --git a/blocksuite/affine/block-database/src/data-source.ts b/blocksuite/affine/block-database/src/data-source.ts index 29d3f9554c..964da1793d 100644 --- a/blocksuite/affine/block-database/src/data-source.ts +++ b/blocksuite/affine/block-database/src/data-source.ts @@ -3,6 +3,7 @@ import type { ColumnUpdater, DatabaseBlockModel, } from '@blocksuite/affine-model'; +import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands'; import { FeatureFlagService } from '@blocksuite/affine-shared/services'; import { insertPositionToIndex, @@ -517,12 +518,9 @@ export const databaseViewInitTemplate = ( datasource.viewManager.viewAdd(viewType); }; export const convertToDatabase = (host: EditorHost, viewType: string) => { - const [_, ctx] = host.std.command - .chain() - .getSelectedModels({ - types: ['block', 'text'], - }) - .run(); + const [_, ctx] = host.std.command.exec(getSelectedModelsCommand, { + types: ['block', 'text'], + }); const { selectedModels } = ctx; const firstModel = selectedModels?.[0]; if (!firstModel) return; diff --git a/blocksuite/affine/block-database/src/database-spec.ts b/blocksuite/affine/block-database/src/database-spec.ts index f2f174f6f0..872ca00f24 100644 --- a/blocksuite/affine/block-database/src/database-spec.ts +++ b/blocksuite/affine/block-database/src/database-spec.ts @@ -1,17 +1,11 @@ -import { - BlockViewExtension, - CommandExtension, - FlavourExtension, -} from '@blocksuite/block-std'; +import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; import { DatabaseBlockAdapterExtensions } from './adapters/extension.js'; -import { commands } from './commands.js'; export const DatabaseBlockSpec: ExtensionType[] = [ FlavourExtension('affine:database'), - CommandExtension(commands), BlockViewExtension('affine:database', literal`affine-database`), DatabaseBlockAdapterExtensions, ].flat(); diff --git a/blocksuite/affine/block-database/src/effects.ts b/blocksuite/affine/block-database/src/effects.ts index c33e34ff8b..33cb8c8fc9 100644 --- a/blocksuite/affine/block-database/src/effects.ts +++ b/blocksuite/affine/block-database/src/effects.ts @@ -1,4 +1,3 @@ -import type { insertDatabaseBlockCommand } from './commands'; import { CenterPeek } from './components/layout'; import { DatabaseTitle } from './components/title'; import type { DatabaseOptionsConfig } from './config'; @@ -44,20 +43,5 @@ declare global { interface BlockConfigs { 'affine:database': Partial; } - - interface CommandContext { - insertedDatabaseBlockId?: string; - } - - interface Commands { - /** - * insert a database block after or before the current block selection - * @param latex the LaTeX content. A input dialog will be shown if not provided - * @param removeEmptyLine remove the current block if it is empty - * @param place where to insert the LaTeX block - * @returns the id of the inserted LaTeX block - */ - insertDatabaseBlock: typeof insertDatabaseBlockCommand; - } } } diff --git a/blocksuite/affine/block-database/src/index.ts b/blocksuite/affine/block-database/src/index.ts index 8857b5ec28..2480d51ce6 100644 --- a/blocksuite/affine/block-database/src/index.ts +++ b/blocksuite/affine/block-database/src/index.ts @@ -1,8 +1,5 @@ -import type * as CommandType from '@blocksuite/affine-shared/commands'; - -declare type _GLOBAL_ = typeof CommandType; - export * from './adapters'; +export * from './commands'; export type { DatabaseOptionsConfig } from './config'; export * from './data-source'; export * from './database-block'; diff --git a/blocksuite/affine/block-database/src/selection.ts b/blocksuite/affine/block-database/src/selection.ts index 5f660668aa..f3dfa2217a 100644 --- a/blocksuite/affine/block-database/src/selection.ts +++ b/blocksuite/affine/block-database/src/selection.ts @@ -65,12 +65,4 @@ export class DatabaseSelection extends BaseSelection { } } -declare global { - namespace BlockSuite { - interface Selection { - database: typeof DatabaseSelection; - } - } -} - export const DatabaseSelectionExtension = SelectionExtension(DatabaseSelection); diff --git a/blocksuite/affine/block-edgeless-text/src/commands/index.ts b/blocksuite/affine/block-edgeless-text/src/commands/index.ts index b8ea759323..db5d5d01aa 100644 --- a/blocksuite/affine/block-edgeless-text/src/commands/index.ts +++ b/blocksuite/affine/block-edgeless-text/src/commands/index.ts @@ -1,7 +1 @@ -import type { BlockCommands } from '@blocksuite/block-std'; - -import { insertEdgelessTextCommand } from './insert-edgeless-text.js'; - -export const commands: BlockCommands = { - insertEdgelessText: insertEdgelessTextCommand, -}; +export { insertEdgelessTextCommand } from './insert-edgeless-text.js'; diff --git a/blocksuite/affine/block-edgeless-text/src/commands/insert-edgeless-text.ts b/blocksuite/affine/block-edgeless-text/src/commands/insert-edgeless-text.ts index 1e14219a10..c6832f56bb 100644 --- a/blocksuite/affine/block-edgeless-text/src/commands/insert-edgeless-text.ts +++ b/blocksuite/affine/block-edgeless-text/src/commands/insert-edgeless-text.ts @@ -14,11 +14,12 @@ import { } from '../edgeless-text-block.js'; export const insertEdgelessTextCommand: Command< - never, - 'textId', { x: number; y: number; + }, + { + textId: string; } > = (ctx, next) => { const { std, x, y } = ctx; diff --git a/blocksuite/affine/block-edgeless-text/src/edgeless-text-block.ts b/blocksuite/affine/block-edgeless-text/src/edgeless-text-block.ts index cddb00e458..de049d905d 100644 --- a/blocksuite/affine/block-edgeless-text/src/edgeless-text-block.ts +++ b/blocksuite/affine/block-edgeless-text/src/edgeless-text-block.ts @@ -1,4 +1,5 @@ import { TextUtils } from '@blocksuite/affine-block-surface'; +import { formatBlockCommand } from '@blocksuite/affine-components/rich-text'; import type { EdgelessTextBlockModel } from '@blocksuite/affine-model'; import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; @@ -98,21 +99,21 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent; - } - - interface Commands { - insertEmbedLinkedDoc: typeof insertEmbedLinkedDocCommand; - } } } diff --git a/blocksuite/affine/block-embed/src/embed-linked-doc-block/commands/index.ts b/blocksuite/affine/block-embed/src/embed-linked-doc-block/commands/index.ts index e7e81fb6ea..d065c4a3e7 100644 --- a/blocksuite/affine/block-embed/src/embed-linked-doc-block/commands/index.ts +++ b/blocksuite/affine/block-embed/src/embed-linked-doc-block/commands/index.ts @@ -1,9 +1 @@ -import type { BlockCommands } from '@blocksuite/block-std'; - -import { insertEmbedLinkedDocCommand } from './insert-embed-linked-doc.js'; - -export const commands: BlockCommands = { - insertEmbedLinkedDoc: insertEmbedLinkedDocCommand, -}; - -export type { InsertedLinkType } from './insert-embed-linked-doc'; +export * from './insert-embed-linked-doc'; diff --git a/blocksuite/affine/block-embed/src/embed-linked-doc-block/commands/insert-embed-linked-doc.ts b/blocksuite/affine/block-embed/src/embed-linked-doc-block/commands/insert-embed-linked-doc.ts index e4d5aaa7a1..8e5cf1a647 100644 --- a/blocksuite/affine/block-embed/src/embed-linked-doc-block/commands/insert-embed-linked-doc.ts +++ b/blocksuite/affine/block-embed/src/embed-linked-doc-block/commands/insert-embed-linked-doc.ts @@ -7,14 +7,10 @@ export type InsertedLinkType = { flavour?: 'affine:bookmark' | 'affine:embed-linked-doc'; } | null; -export const insertEmbedLinkedDocCommand: Command< - never, - 'insertedLinkType', - { - docId: string; - params?: ReferenceParams; - } -> = (ctx, next) => { +export const insertEmbedLinkedDocCommand: Command<{ + docId: string; + params?: ReferenceParams; +}> = (ctx, next) => { const { docId, params, std } = ctx; const flavour = 'affine:embed-linked-doc'; const targetStyle: EmbedCardStyle = 'vertical'; diff --git a/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-edgeless-linked-doc-block.ts b/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-edgeless-linked-doc-block.ts index d564088d9d..96ed8743a6 100644 --- a/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-edgeless-linked-doc-block.ts +++ b/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-edgeless-linked-doc-block.ts @@ -1,4 +1,7 @@ -import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; +import { + EdgelessCRUDIdentifier, + reassociateConnectorsCommand, +} from '@blocksuite/affine-block-surface'; import { EMBED_CARD_HEIGHT, EMBED_CARD_WIDTH, @@ -51,7 +54,7 @@ export class EmbedEdgelessLinkedDocBlockComponent extends toEdgelessEmbedBlock( surface ); - this.std.command.exec('reassociateConnectors', { + this.std.command.exec(reassociateConnectorsCommand, { oldId: id, newId, }); diff --git a/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-linked-doc-spec.ts b/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-linked-doc-spec.ts index 78d17fb51d..128171c90d 100644 --- a/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-linked-doc-spec.ts +++ b/blocksuite/affine/block-embed/src/embed-linked-doc-block/embed-linked-doc-spec.ts @@ -1,12 +1,10 @@ -import { BlockViewExtension, CommandExtension } from '@blocksuite/block-std'; +import { BlockViewExtension } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; import { EmbedLinkedDocBlockAdapterExtensions } from './adapters/extension.js'; -import { commands } from './commands/index.js'; export const EmbedLinkedDocBlockSpec: ExtensionType[] = [ - CommandExtension(commands), BlockViewExtension('affine:embed-linked-doc', model => { return model.parent?.flavour === 'affine:surface' ? literal`affine-embed-edgeless-linked-doc-block` diff --git a/blocksuite/affine/block-embed/src/embed-linked-doc-block/index.ts b/blocksuite/affine/block-embed/src/embed-linked-doc-block/index.ts index 70e4756bd3..1e2b112586 100644 --- a/blocksuite/affine/block-embed/src/embed-linked-doc-block/index.ts +++ b/blocksuite/affine/block-embed/src/embed-linked-doc-block/index.ts @@ -1,4 +1,4 @@ export * from './adapters'; -export type { InsertedLinkType } from './commands'; +export * from './commands'; export * from './embed-linked-doc-block'; export * from './embed-linked-doc-spec'; diff --git a/blocksuite/affine/block-embed/src/embed-synced-doc-block/embed-edgeless-synced-doc-block.ts b/blocksuite/affine/block-embed/src/embed-synced-doc-block/embed-edgeless-synced-doc-block.ts index 6a14ee07c7..a58d5d8b4a 100644 --- a/blocksuite/affine/block-embed/src/embed-synced-doc-block/embed-edgeless-synced-doc-block.ts +++ b/blocksuite/affine/block-embed/src/embed-synced-doc-block/embed-edgeless-synced-doc-block.ts @@ -1,4 +1,7 @@ -import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; +import { + EdgelessCRUDIdentifier, + reassociateConnectorsCommand, +} from '@blocksuite/affine-block-surface'; import type { AliasInfo } from '@blocksuite/affine-model'; import { EMBED_CARD_HEIGHT, @@ -144,7 +147,7 @@ export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock( surface ); - this.std.command.exec('reassociateConnectors', { + this.std.command.exec(reassociateConnectorsCommand, { oldId: id, newId, }); diff --git a/blocksuite/affine/block-image/package.json b/blocksuite/affine/block-image/package.json index 60cee41ed4..14ed87c621 100644 --- a/blocksuite/affine/block-image/package.json +++ b/blocksuite/affine/block-image/package.json @@ -13,6 +13,8 @@ "author": "toeverything", "license": "MIT", "dependencies": { + "@blocksuite/affine-block-note": "workspace:*", + "@blocksuite/affine-block-surface": "workspace:*", "@blocksuite/affine-components": "workspace:*", "@blocksuite/affine-model": "workspace:*", "@blocksuite/affine-shared": "workspace:*", diff --git a/blocksuite/affine/block-image/src/commands/index.ts b/blocksuite/affine/block-image/src/commands/index.ts index ab253b6fb6..80a31acfb7 100644 --- a/blocksuite/affine/block-image/src/commands/index.ts +++ b/blocksuite/affine/block-image/src/commands/index.ts @@ -1,9 +1 @@ -import { getImageSelectionsCommand } from '@blocksuite/affine-shared/commands'; -import type { BlockCommands } from '@blocksuite/block-std'; - -import { insertImagesCommand } from './insert-images.js'; - -export const commands: BlockCommands = { - getImageSelections: getImageSelectionsCommand, - insertImages: insertImagesCommand, -}; +export { insertImagesCommand } from './insert-images.js'; diff --git a/blocksuite/affine/block-image/src/commands/insert-images.ts b/blocksuite/affine/block-image/src/commands/insert-images.ts index 2a1234c033..8b9f8e5caa 100644 --- a/blocksuite/affine/block-image/src/commands/insert-images.ts +++ b/blocksuite/affine/block-image/src/commands/insert-images.ts @@ -1,12 +1,18 @@ import { getImageFilesFromLocal } from '@blocksuite/affine-shared/utils'; import type { Command } from '@blocksuite/block-std'; +import type { BlockModel } from '@blocksuite/store'; import { addSiblingImageBlock } from '../utils.js'; export const insertImagesCommand: Command< - 'selectedModels', - 'insertedImageIds', - { removeEmptyLine?: boolean; place?: 'after' | 'before' } + { + selectedModels?: BlockModel[]; + removeEmptyLine?: boolean; + place?: 'after' | 'before'; + }, + { + insertedImageIds: Promise; + } > = (ctx, next) => { const { selectedModels, place, removeEmptyLine, std } = ctx; if (!selectedModels) return; diff --git a/blocksuite/affine/block-image/src/components/page-image-block.ts b/blocksuite/affine/block-image/src/components/page-image-block.ts index dc7b21b1bc..3a37e22710 100644 --- a/blocksuite/affine/block-image/src/components/page-image-block.ts +++ b/blocksuite/affine/block-image/src/components/page-image-block.ts @@ -1,5 +1,13 @@ +import { focusBlockEnd, focusBlockStart } from '@blocksuite/affine-block-note'; +import { + getNextBlockCommand, + getPrevBlockCommand, +} from '@blocksuite/affine-shared/commands'; import { ImageSelection } from '@blocksuite/affine-shared/selection'; -import type { UIEventStateContext } from '@blocksuite/block-std'; +import type { + BlockComponent, + UIEventStateContext, +} from '@blocksuite/block-std'; import { BlockSelection, ShadowlessElement, @@ -136,15 +144,14 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) { std.command .chain() - .getNextBlock({ path: this.block.blockId }) - .inline((ctx, next) => { + .pipe(getNextBlockCommand, { path: this.block.blockId }) + .pipe<{ focusBlock: BlockComponent }>((ctx, next) => { const { nextBlock } = ctx; if (!nextBlock) return; return next({ focusBlock: nextBlock }); }) - // @ts-expect-error FIXME(command): BS-2216 - .focusBlockStart() + .pipe(focusBlockStart) .run(); return true; }, @@ -162,15 +169,14 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) { std.command .chain() - .getPrevBlock({ path: this.block.blockId }) - .inline((ctx, next) => { + .pipe(getPrevBlockCommand, { path: this.block.blockId }) + .pipe<{ focusBlock: BlockComponent }>((ctx, next) => { const { prevBlock } = ctx; if (!prevBlock) return; return next({ focusBlock: prevBlock }); }) - // @ts-expect-error FIXME(command): BS-2216 - .focusBlockEnd() + .pipe(focusBlockEnd) .run(); return true; }, diff --git a/blocksuite/affine/block-image/src/effects.ts b/blocksuite/affine/block-image/src/effects.ts index f5823d3bfe..0bb9ca9de4 100644 --- a/blocksuite/affine/block-image/src/effects.ts +++ b/blocksuite/affine/block-image/src/effects.ts @@ -1,6 +1,3 @@ -import type { getImageSelectionsCommand } from '@blocksuite/affine-shared/commands'; - -import type { insertImagesCommand } from './commands/insert-images.js'; import { ImageBlockFallbackCard } from './components/image-block-fallback.js'; import { ImageBlockPageComponent } from './components/page-image-block.js'; import { ImageBlockComponent } from './image-block.js'; @@ -16,21 +13,6 @@ export function effects() { declare global { namespace BlockSuite { - interface CommandContext { - insertedImageIds?: Promise; - } - - interface Commands { - getImageSelections: typeof getImageSelectionsCommand; - /** - * open file dialog to insert images before or after the current block selection - * @param removeEmptyLine remove the current block if it is empty - * @param place where to insert the images - * @returns a promise that resolves to the inserted image ids - */ - insertImages: typeof insertImagesCommand; - } - interface BlockServices { 'affine:image': ImageBlockService; } diff --git a/blocksuite/affine/block-image/src/image-spec.ts b/blocksuite/affine/block-image/src/image-spec.ts index a1fbe453bf..9f49445883 100644 --- a/blocksuite/affine/block-image/src/image-spec.ts +++ b/blocksuite/affine/block-image/src/image-spec.ts @@ -1,6 +1,5 @@ import { BlockViewExtension, - CommandExtension, FlavourExtension, WidgetViewMapExtension, } from '@blocksuite/block-std'; @@ -8,13 +7,11 @@ import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; import { ImageBlockAdapterExtensions } from './adapters/extension.js'; -import { commands } from './commands/index.js'; import { ImageBlockService, ImageDropOption } from './image-service.js'; export const ImageBlockSpec: ExtensionType[] = [ FlavourExtension('affine:image'), ImageBlockService, - CommandExtension(commands), BlockViewExtension('affine:image', model => { const parent = model.doc.getParent(model.id); diff --git a/blocksuite/affine/block-image/src/index.ts b/blocksuite/affine/block-image/src/index.ts index eb321e4640..1a353b8a49 100644 --- a/blocksuite/affine/block-image/src/index.ts +++ b/blocksuite/affine/block-image/src/index.ts @@ -3,6 +3,7 @@ import type * as SurfaceEffects from '@blocksuite/affine-block-surface/effects'; declare type _GLOBAL_ = typeof SurfaceEffects; export * from './adapters'; +export * from './commands'; export * from './image-block'; export * from './image-edgeless-block'; export * from './image-service'; diff --git a/blocksuite/affine/block-image/src/utils.ts b/blocksuite/affine/block-image/src/utils.ts index 3be3763013..cdec18c869 100644 --- a/blocksuite/affine/block-image/src/utils.ts +++ b/blocksuite/affine/block-image/src/utils.ts @@ -1,3 +1,4 @@ +import { autoResizeElementsCommand } from '@blocksuite/affine-block-surface'; import { toast } from '@blocksuite/affine-components/toast'; import type { AttachmentBlockProps, @@ -516,7 +517,7 @@ export async function addImages( editing: false, }); if (isMultipleFiles) { - std.command.exec('autoResizeElements'); + std.command.exec(autoResizeElementsCommand); } return blockIds; } diff --git a/blocksuite/affine/block-image/tsconfig.json b/blocksuite/affine/block-image/tsconfig.json index 230fca2705..9c4f45635a 100644 --- a/blocksuite/affine/block-image/tsconfig.json +++ b/blocksuite/affine/block-image/tsconfig.json @@ -7,6 +7,8 @@ }, "include": ["./src"], "references": [ + { "path": "../block-note" }, + { "path": "../block-surface" }, { "path": "../components" }, { "path": "../model" }, { "path": "../shared" }, diff --git a/blocksuite/affine/block-latex/src/commands.ts b/blocksuite/affine/block-latex/src/commands.ts index 82c92c827b..5bd02cb05a 100644 --- a/blocksuite/affine/block-latex/src/commands.ts +++ b/blocksuite/affine/block-latex/src/commands.ts @@ -1,16 +1,19 @@ import type { LatexProps } from '@blocksuite/affine-model'; -import type { BlockCommands, Command } from '@blocksuite/block-std'; +import type { Command } from '@blocksuite/block-std'; import { assertInstanceOf } from '@blocksuite/global/utils'; +import type { BlockModel } from '@blocksuite/store'; import { LatexBlockComponent } from './latex-block.js'; export const insertLatexBlockCommand: Command< - 'selectedModels', - 'insertedLatexBlockId', { latex?: string; place?: 'after' | 'before'; removeEmptyLine?: boolean; + selectedModels?: BlockModel[]; + }, + { + insertedLatexBlockId: Promise; } > = (ctx, next) => { const { selectedModels, latex, place, removeEmptyLine, std } = ctx; @@ -51,7 +54,3 @@ export const insertLatexBlockCommand: Command< }), }); }; - -export const commands: BlockCommands = { - insertLatexBlock: insertLatexBlockCommand, -}; diff --git a/blocksuite/affine/block-latex/src/effects.ts b/blocksuite/affine/block-latex/src/effects.ts index 6c4231e28f..73e99790ba 100644 --- a/blocksuite/affine/block-latex/src/effects.ts +++ b/blocksuite/affine/block-latex/src/effects.ts @@ -1,25 +1,5 @@ -import type { insertLatexBlockCommand } from './commands'; import { LatexBlockComponent } from './latex-block'; export function effects() { customElements.define('affine-latex', LatexBlockComponent); } - -declare global { - namespace BlockSuite { - interface CommandContext { - insertedLatexBlockId?: Promise; - } - - interface Commands { - /** - * insert a LaTeX block after or before the current block selection - * @param latex the LaTeX content. A input dialog will be shown if not provided - * @param place where to insert the LaTeX block - * @param removeEmptyLine remove the current block if it is empty - * @returns the id of the inserted LaTeX block - */ - insertLatexBlock: typeof insertLatexBlockCommand; - } - } -} diff --git a/blocksuite/affine/block-latex/src/index.ts b/blocksuite/affine/block-latex/src/index.ts index db59377ad6..9f7c718519 100644 --- a/blocksuite/affine/block-latex/src/index.ts +++ b/blocksuite/affine/block-latex/src/index.ts @@ -1,13 +1,9 @@ -import type * as NoteType from '@blocksuite/affine-block-note/effects'; -import type * as CommandsType from '@blocksuite/affine-shared/commands'; import type * as RemarkMathType from 'remark-math'; export * from './adapters'; +export * from './commands'; export * from './latex-block'; export * from './latex-spec'; // Global types -declare type _GLOBAl = - | typeof NoteType - | typeof CommandsType - | typeof RemarkMathType; +declare type _GLOBAl = typeof RemarkMathType; diff --git a/blocksuite/affine/block-latex/src/latex-block.ts b/blocksuite/affine/block-latex/src/latex-block.ts index 9a3b77c421..6c988f078d 100644 --- a/blocksuite/affine/block-latex/src/latex-block.ts +++ b/blocksuite/affine/block-latex/src/latex-block.ts @@ -1,3 +1,4 @@ +import { selectBlock } from '@blocksuite/affine-block-note'; import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption'; import { createLitPortal } from '@blocksuite/affine-components/portal'; import type { LatexBlockModel } from '@blocksuite/affine-model'; @@ -96,7 +97,7 @@ export class LatexBlockComponent extends CaptionedBlockComponent = (ctx, next) => { const { std, id, order, stopCapturing = true } = ctx; diff --git a/blocksuite/affine/block-list/src/commands/dedent-list.ts b/blocksuite/affine/block-list/src/commands/dedent-list.ts index dc85196c51..5e50995fed 100644 --- a/blocksuite/affine/block-list/src/commands/dedent-list.ts +++ b/blocksuite/affine/block-list/src/commands/dedent-list.ts @@ -5,9 +5,10 @@ import { type Command, TextSelection } from '@blocksuite/block-std'; import { correctNumberedListsOrderToPrev } from './utils.js'; export const canDedentListCommand: Command< - never, - 'indentContext', - Partial> + Partial>, + { + indentContext: IndentContext; + } > = (ctx, next) => { let { blockId, inlineIndex } = ctx; const { std } = ctx; @@ -91,7 +92,9 @@ export const canDedentListCommand: Command< }); }; -export const dedentListCommand: Command<'indentContext'> = (ctx, next) => { +export const dedentListCommand: Command<{ + indentContext: IndentContext; +}> = (ctx, next) => { const { indentContext: dedentContext, std } = ctx; const { store, selection, range, host } = std; diff --git a/blocksuite/affine/block-list/src/commands/indent-list.ts b/blocksuite/affine/block-list/src/commands/indent-list.ts index ed7d6eaf2f..719d9323eb 100644 --- a/blocksuite/affine/block-list/src/commands/indent-list.ts +++ b/blocksuite/affine/block-list/src/commands/indent-list.ts @@ -8,9 +8,10 @@ import { type Command, TextSelection } from '@blocksuite/block-std'; import { correctNumberedListsOrderToPrev } from './utils.js'; export const canIndentListCommand: Command< - never, - 'indentContext', - Partial> + Partial>, + { + indentContext: IndentContext; + } > = (ctx, next) => { let { blockId, inlineIndex } = ctx; const { std } = ctx; @@ -84,10 +85,9 @@ export const canIndentListCommand: Command< }); }; -export const indentListCommand: Command<'indentContext', never> = ( - ctx, - next -) => { +export const indentListCommand: Command<{ + indentContext: IndentContext; +}> = (ctx, next) => { const { indentContext, std } = ctx; if ( !indentContext || diff --git a/blocksuite/affine/block-list/src/commands/index.ts b/blocksuite/affine/block-list/src/commands/index.ts index ce896f9be5..7845d060fd 100644 --- a/blocksuite/affine/block-list/src/commands/index.ts +++ b/blocksuite/affine/block-list/src/commands/index.ts @@ -1,17 +1,5 @@ -import type { BlockCommands } from '@blocksuite/block-std'; - -import { convertToNumberedListCommand } from './convert-to-numbered-list.js'; -import { canDedentListCommand, dedentListCommand } from './dedent-list.js'; -import { canIndentListCommand, indentListCommand } from './indent-list.js'; -import { listToParagraphCommand } from './list-to-paragraph.js'; -import { splitListCommand } from './split-list.js'; - -export const commands: BlockCommands = { - convertToNumberedList: convertToNumberedListCommand, - listToParagraph: listToParagraphCommand, - splitList: splitListCommand, - canIndentList: canIndentListCommand, - indentList: indentListCommand, - canDedentList: canDedentListCommand, - dedentList: dedentListCommand, -}; +export { convertToNumberedListCommand } from './convert-to-numbered-list.js'; +export { canDedentListCommand, dedentListCommand } from './dedent-list.js'; +export { canIndentListCommand, indentListCommand } from './indent-list.js'; +export { listToParagraphCommand } from './list-to-paragraph.js'; +export { splitListCommand } from './split-list.js'; diff --git a/blocksuite/affine/block-list/src/commands/list-to-paragraph.ts b/blocksuite/affine/block-list/src/commands/list-to-paragraph.ts index 21ce5246ca..8c47d8b9b0 100644 --- a/blocksuite/affine/block-list/src/commands/list-to-paragraph.ts +++ b/blocksuite/affine/block-list/src/commands/list-to-paragraph.ts @@ -3,11 +3,12 @@ import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { Command } from '@blocksuite/block-std'; export const listToParagraphCommand: Command< - never, - 'listConvertedId', { id: string; stopCapturing?: boolean; + }, + { + listConvertedId: string; } > = (ctx, next) => { const { id, stopCapturing = true } = ctx; diff --git a/blocksuite/affine/block-list/src/commands/split-list.ts b/blocksuite/affine/block-list/src/commands/split-list.ts index 2a70b71c85..15c3b04374 100644 --- a/blocksuite/affine/block-list/src/commands/split-list.ts +++ b/blocksuite/affine/block-list/src/commands/split-list.ts @@ -5,16 +5,13 @@ import { } from '@blocksuite/affine-shared/utils'; import type { Command, EditorHost } from '@blocksuite/block-std'; +import { canDedentListCommand, dedentListCommand } from './dedent-list.js'; import { correctNumberedListsOrderToPrev } from './utils.js'; -export const splitListCommand: Command< - never, - never, - { - blockId: string; - inlineIndex: number; - } -> = (ctx, next) => { +export const splitListCommand: Command<{ + blockId: string; + inlineIndex: number; +}> = (ctx, next) => { const { blockId, inlineIndex, std } = ctx; const host = std.host as EditorHost; const doc = host.doc; @@ -100,11 +97,11 @@ export const splitListCommand: Command< if (parent.role === 'content') { host.command .chain() - .canDedentList({ + .pipe(canDedentListCommand, { blockId, inlineIndex: 0, }) - .dedentList() + .pipe(dedentListCommand) .run(); next(); diff --git a/blocksuite/affine/block-list/src/effects.ts b/blocksuite/affine/block-list/src/effects.ts index 791fc336e5..a0a601352b 100644 --- a/blocksuite/affine/block-list/src/effects.ts +++ b/blocksuite/affine/block-list/src/effects.ts @@ -1,16 +1,3 @@ -import type { IndentContext } from '@blocksuite/affine-shared/types'; - -import type { convertToNumberedListCommand } from './commands/convert-to-numbered-list.js'; -import type { - canDedentListCommand, - dedentListCommand, -} from './commands/dedent-list.js'; -import type { - canIndentListCommand, - indentListCommand, -} from './commands/indent-list.js'; -import type { listToParagraphCommand } from './commands/list-to-paragraph.js'; -import type { splitListCommand } from './commands/split-list.js'; import { ListBlockComponent } from './list-block.js'; export function effects() { @@ -18,23 +5,6 @@ export function effects() { } declare global { - namespace BlockSuite { - interface CommandContext { - listConvertedId?: string; - indentContext?: IndentContext; - } - - interface Commands { - convertToNumberedList: typeof convertToNumberedListCommand; - canDedentList: typeof canDedentListCommand; - canIndentList: typeof canIndentListCommand; - dedentList: typeof dedentListCommand; - indentList: typeof indentListCommand; - listToParagraph: typeof listToParagraphCommand; - splitList: typeof splitListCommand; - } - } - interface HTMLElementTagNameMap { 'affine-list': ListBlockComponent; } diff --git a/blocksuite/affine/block-list/src/index.ts b/blocksuite/affine/block-list/src/index.ts index f8f82f1d5f..7f18d6d6a4 100644 --- a/blocksuite/affine/block-list/src/index.ts +++ b/blocksuite/affine/block-list/src/index.ts @@ -1,4 +1,5 @@ export * from './adapters/index.js'; +export * from './commands'; export { correctNumberedListsOrderToPrev } from './commands/utils'; export * from './list-block.js'; export * from './list-spec.js'; diff --git a/blocksuite/affine/block-list/src/list-keymap.ts b/blocksuite/affine/block-list/src/list-keymap.ts index f172cf9b5d..8733b0bfe2 100644 --- a/blocksuite/affine/block-list/src/list-keymap.ts +++ b/blocksuite/affine/block-list/src/list-keymap.ts @@ -3,9 +3,20 @@ import { textKeymap, } from '@blocksuite/affine-components/rich-text'; import { ListBlockSchema } from '@blocksuite/affine-model'; +import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands'; import { KeymapExtension, TextSelection } from '@blocksuite/block-std'; import { IS_MAC } from '@blocksuite/global/env'; +import { + canDedentListCommand, + dedentListCommand, +} from './commands/dedent-list.js'; +import { + canIndentListCommand, + indentListCommand, +} from './commands/indent-list.js'; +import { listToParagraphCommand } from './commands/list-to-paragraph.js'; +import { splitListCommand } from './commands/split-list.js'; import { forwardDelete } from './utils/forward-delete.js'; export const ListKeymapExtension = KeymapExtension( @@ -16,10 +27,13 @@ export const ListKeymapExtension = KeymapExtension( if (!text) return false; ctx.get('keyboardState').raw.preventDefault(); - std.command.exec('splitList', { - blockId: text.from.blockId, - inlineIndex: text.from.index, - }); + std.command + .chain() + .pipe(splitListCommand, { + blockId: text.from.blockId, + inlineIndex: text.from.index, + }) + .run(); return true; }, 'Mod-Enter': ctx => { @@ -27,16 +41,22 @@ export const ListKeymapExtension = KeymapExtension( if (!text) return false; ctx.get('keyboardState').raw.preventDefault(); - std.command.exec('splitList', { - blockId: text.from.blockId, - inlineIndex: text.from.index, - }); + std.command + .chain() + .pipe(splitListCommand, { + blockId: text.from.blockId, + inlineIndex: text.from.index, + }) + .run(); return true; }, Tab: ctx => { - const { selectedModels } = std.command.exec('getSelectedModels', { - types: ['text'], - }); + const [_, { selectedModels }] = std.command + .chain() + .pipe(getSelectedModelsCommand, { + types: ['text'], + }) + .run(); if (selectedModels?.length !== 1) { return false; } @@ -46,18 +66,21 @@ export const ListKeymapExtension = KeymapExtension( ctx.get('keyboardState').raw.preventDefault(); std.command .chain() - .canIndentList({ + .pipe(canIndentListCommand, { blockId: text.from.blockId, inlineIndex: text.from.index, }) - .indentList() + .pipe(indentListCommand) .run(); return true; }, 'Shift-Tab': ctx => { - const { selectedModels } = std.command.exec('getSelectedModels', { - types: ['text'], - }); + const [_, { selectedModels }] = std.command + .chain() + .pipe(getSelectedModelsCommand, { + types: ['text'], + }) + .run(); if (selectedModels?.length !== 1) { return; } @@ -67,11 +90,11 @@ export const ListKeymapExtension = KeymapExtension( ctx.get('keyboardState').raw.preventDefault(); std.command .chain() - .canDedentList({ + .pipe(canDedentListCommand, { blockId: text.from.blockId, inlineIndex: text.from.index, }) - .dedentList() + .pipe(dedentListCommand) .run(); return true; }, @@ -83,7 +106,12 @@ export const ListKeymapExtension = KeymapExtension( if (!isStart) return false; ctx.get('keyboardState').raw.preventDefault(); - std.command.exec('listToParagraph', { id: text.from.blockId }); + std.command + .chain() + .pipe(listToParagraphCommand, { + id: text.from.blockId, + }) + .run(); return true; }, 'Control-d': ctx => { diff --git a/blocksuite/affine/block-list/src/list-spec.ts b/blocksuite/affine/block-list/src/list-spec.ts index 421d49be53..e496e9772c 100644 --- a/blocksuite/affine/block-list/src/list-spec.ts +++ b/blocksuite/affine/block-list/src/list-spec.ts @@ -1,18 +1,12 @@ -import { - BlockViewExtension, - CommandExtension, - FlavourExtension, -} from '@blocksuite/block-std'; +import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; import { ListBlockAdapterExtensions } from './adapters/extension.js'; -import { commands } from './commands/index.js'; import { ListKeymapExtension, ListTextKeymapExtension } from './list-keymap.js'; export const ListBlockSpec: ExtensionType[] = [ FlavourExtension('affine:list'), - CommandExtension(commands), BlockViewExtension('affine:list', literal`affine-list`), ListKeymapExtension, ListTextKeymapExtension, diff --git a/blocksuite/affine/block-note/src/commands/block-type.ts b/blocksuite/affine/block-note/src/commands/block-type.ts index 9ea10e6304..b123807b00 100644 --- a/blocksuite/affine/block-note/src/commands/block-type.ts +++ b/blocksuite/affine/block-note/src/commands/block-type.ts @@ -3,12 +3,18 @@ import { focusTextModel, onModelTextUpdated, } from '@blocksuite/affine-components/rich-text'; +import { + getBlockSelectionsCommand, + getSelectedBlocksCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import { matchFlavours, mergeToCodeModel, transformModel, } from '@blocksuite/affine-shared/utils'; import { + type BlockComponent, BlockSelection, type Command, TextSelection, @@ -21,9 +27,12 @@ type UpdateBlockConfig = { }; export const updateBlockType: Command< - 'selectedBlocks', - 'updatedBlocks', - UpdateBlockConfig + UpdateBlockConfig & { + selectedBlocks?: BlockComponent[]; + }, + { + updatedBlocks: BlockModel[]; + } > = (ctx, next) => { const { std, flavour, props } = ctx; const host = std.host; @@ -35,8 +44,11 @@ export const updateBlockType: Command< if (selectedBlocks == null) { const [result, ctx] = std.command .chain() - .tryAll(chain => [chain.getTextSelection(), chain.getBlockSelections()]) - .getSelectedBlocks({ types: ['text', 'block'] }) + .tryAll(chain => [ + chain.pipe(getTextSelectionCommand), + chain.pipe(getBlockSelectionsCommand), + ]) + .pipe(getSelectedBlocksCommand, { types: ['text', 'block'] }) .run(); if (result) { selectedBlocks = ctx.selectedBlocks; @@ -60,7 +72,10 @@ export const updateBlockType: Command< ); } - const mergeToCode: Command = (_, next) => { + const mergeToCode: Command<{}, { updatedBlocks: BlockModel[] }> = ( + _, + next + ) => { if (flavour !== 'affine:code') return; const id = mergeToCodeModel(blockModels); if (!id) return; @@ -72,7 +87,10 @@ export const updateBlockType: Command< }).catch(console.error); return next({ updatedBlocks: [model] }); }; - const appendDivider: Command = (_, next) => { + const appendDivider: Command<{}, { updatedBlocks: BlockModel[] }> = ( + _, + next + ) => { if (flavour !== 'affine:divider') { return false; } @@ -99,7 +117,7 @@ export const updateBlockType: Command< return next({ updatedBlocks: [newModel] }); }; - const focusText: Command<'updatedBlocks'> = (ctx, next) => { + const focusText: Command<{ updatedBlocks: BlockModel[] }> = (ctx, next) => { const { updatedBlocks } = ctx; if (!updatedBlocks || updatedBlocks.length === 0) { return false; @@ -139,7 +157,7 @@ export const updateBlockType: Command< return next(); }; - const focusBlock: Command<'updatedBlocks'> = (ctx, next) => { + const focusBlock: Command<{ updatedBlocks: BlockModel[] }> = (ctx, next) => { const { updatedBlocks } = ctx; if (!updatedBlocks || updatedBlocks.length === 0) { return false; @@ -165,15 +183,15 @@ export const updateBlockType: Command< const [result, resultCtx] = std.command .chain() - .inline((_, next) => { + .pipe((_, next) => { doc.captureSync(); return next(); }) // update block type - .try<'updatedBlocks'>(chain => [ - chain.inline<'updatedBlocks'>(mergeToCode), - chain.inline<'updatedBlocks'>(appendDivider), - chain.inline<'updatedBlocks'>((_, next) => { + .try<{ updatedBlocks: BlockModel[] }>(chain => [ + chain.pipe(mergeToCode), + chain.pipe(appendDivider), + chain.pipe((_, next) => { const newModels: BlockModel[] = []; blockModels.forEach(model => { if ( @@ -204,15 +222,15 @@ export const updateBlockType: Command< ]) // focus .try(chain => [ - chain.inline((_, next) => { + chain.pipe((_, next) => { if (['affine:code', 'affine:divider'].includes(flavour)) { return next(); } return false; }), - chain.inline(focusText), - chain.inline(focusBlock), - chain.inline((_, next) => next()), + chain.pipe(focusText), + chain.pipe(focusBlock), + chain.pipe((_, next) => next()), ]) .run(); diff --git a/blocksuite/affine/block-note/src/commands/change-note-display-mode.ts b/blocksuite/affine/block-note/src/commands/change-note-display-mode.ts index 9227e523df..427d26d98d 100644 --- a/blocksuite/affine/block-note/src/commands/change-note-display-mode.ts +++ b/blocksuite/affine/block-note/src/commands/change-note-display-mode.ts @@ -2,11 +2,11 @@ import { NoteDisplayMode } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { Command } from '@blocksuite/block-std'; -export const changeNoteDisplayMode: Command< - never, - never, - { noteId: string; mode: NoteDisplayMode; stopCapture?: boolean } -> = (ctx, next) => { +export const changeNoteDisplayMode: Command<{ + noteId: string; + mode: NoteDisplayMode; + stopCapture?: boolean; +}> = (ctx, next) => { const { std, noteId, mode, stopCapture } = ctx; const noteBlockModel = std.store.getBlock(noteId)?.model; diff --git a/blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts b/blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts index c810317112..a7db93b928 100644 --- a/blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts +++ b/blocksuite/affine/block-note/src/commands/dedent-block-to-root.ts @@ -1,14 +1,12 @@ import { matchFlavours } from '@blocksuite/affine-shared/utils'; import type { Command } from '@blocksuite/block-std'; -export const dedentBlockToRoot: Command< - never, - never, - { - blockId?: string; - stopCapture?: boolean; - } -> = (ctx, next) => { +import { dedentBlock } from './dedent-block'; + +export const dedentBlockToRoot: Command<{ + blockId?: string; + stopCapture?: boolean; +}> = (ctx, next) => { let { blockId } = ctx; const { std, stopCapture = true } = ctx; const { store } = std; @@ -27,7 +25,7 @@ export const dedentBlockToRoot: Command< if (stopCapture) store.captureSync(); changed = true; } - std.command.exec('dedentBlock', { blockId: model.id, stopCapture: true }); + std.command.exec(dedentBlock, { blockId: model.id, stopCapture: true }); parent = store.getParent(model); } diff --git a/blocksuite/affine/block-note/src/commands/dedent-block.ts b/blocksuite/affine/block-note/src/commands/dedent-block.ts index a761168e96..be655ac187 100644 --- a/blocksuite/affine/block-note/src/commands/dedent-block.ts +++ b/blocksuite/affine/block-note/src/commands/dedent-block.ts @@ -20,14 +20,10 @@ import type { Command } from '@blocksuite/block-std'; * - ddd * - eee */ -export const dedentBlock: Command< - never, - never, - { - blockId?: string; - stopCapture?: boolean; - } -> = (ctx, next) => { +export const dedentBlock: Command<{ + blockId?: string; + stopCapture?: boolean; +}> = (ctx, next) => { let { blockId } = ctx; const { std, stopCapture = true } = ctx; const { store } = std; diff --git a/blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts b/blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts index ec679ea62a..8fd12a94c3 100644 --- a/blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts +++ b/blocksuite/affine/block-note/src/commands/dedent-blocks-to-root.ts @@ -1,14 +1,12 @@ import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { type Command, TextSelection } from '@blocksuite/block-std'; -export const dedentBlocksToRoot: Command< - never, - never, - { - blockIds?: string[]; - stopCapture?: boolean; - } -> = (ctx, next) => { +import { dedentBlockToRoot } from './dedent-block-to-root'; + +export const dedentBlocksToRoot: Command<{ + blockIds?: string[]; + stopCapture?: boolean; +}> = (ctx, next) => { let { blockIds } = ctx; const { std, stopCapture = true } = ctx; const { store } = std; @@ -33,7 +31,7 @@ export const dedentBlocksToRoot: Command< const model = blockIds[i]; const parent = store.getParent(model); if (parent && !matchFlavours(parent, ['affine:note'])) { - std.command.exec('dedentBlockToRoot', { + std.command.exec(dedentBlockToRoot, { blockId: model, stopCapture: false, }); diff --git a/blocksuite/affine/block-note/src/commands/dedent-blocks.ts b/blocksuite/affine/block-note/src/commands/dedent-blocks.ts index 69b89bc447..98885da71e 100644 --- a/blocksuite/affine/block-note/src/commands/dedent-blocks.ts +++ b/blocksuite/affine/block-note/src/commands/dedent-blocks.ts @@ -4,14 +4,12 @@ import { } from '@blocksuite/affine-shared/utils'; import { type Command, TextSelection } from '@blocksuite/block-std'; -export const dedentBlocks: Command< - never, - never, - { - blockIds?: string[]; - stopCapture?: boolean; - } -> = (ctx, next) => { +import { dedentBlock } from './dedent-block'; + +export const dedentBlocks: Command<{ + blockIds?: string[]; + stopCapture?: boolean; +}> = (ctx, next) => { let { blockIds } = ctx; const { std, stopCapture = true } = ctx; const { store, selection, range, host } = std; @@ -72,7 +70,7 @@ export const dedentBlocks: Command< .slice(firstDedentIndex) .filter(id => !collapsedIds.includes(id)); dedentIds.reverse().forEach(id => { - std.command.exec('dedentBlock', { blockId: id, stopCapture: false }); + std.command.exec(dedentBlock, { blockId: id, stopCapture: false }); }); const textSelection = selection.find(TextSelection); diff --git a/blocksuite/affine/block-note/src/commands/focus-block-end.ts b/blocksuite/affine/block-note/src/commands/focus-block-end.ts index 6a81962979..8a6b1946a2 100644 --- a/blocksuite/affine/block-note/src/commands/focus-block-end.ts +++ b/blocksuite/affine/block-note/src/commands/focus-block-end.ts @@ -1,6 +1,12 @@ -import { type Command, TextSelection } from '@blocksuite/block-std'; +import { + type BlockComponent, + type Command, + TextSelection, +} from '@blocksuite/block-std'; -export const focusBlockEnd: Command<'focusBlock'> = (ctx, next) => { +export const focusBlockEnd: Command<{ + focusBlock?: BlockComponent; +}> = (ctx, next) => { const { focusBlock, std } = ctx; if (!focusBlock || !focusBlock.model.text) return; diff --git a/blocksuite/affine/block-note/src/commands/focus-block-start.ts b/blocksuite/affine/block-note/src/commands/focus-block-start.ts index 904f1de2c8..e0e5609045 100644 --- a/blocksuite/affine/block-note/src/commands/focus-block-start.ts +++ b/blocksuite/affine/block-note/src/commands/focus-block-start.ts @@ -1,6 +1,12 @@ -import { type Command, TextSelection } from '@blocksuite/block-std'; +import { + type BlockComponent, + type Command, + TextSelection, +} from '@blocksuite/block-std'; -export const focusBlockStart: Command<'focusBlock'> = (ctx, next) => { +export const focusBlockStart: Command<{ + focusBlock?: BlockComponent; +}> = (ctx, next) => { const { focusBlock, std } = ctx; if (!focusBlock || !focusBlock.model.text) return; diff --git a/blocksuite/affine/block-note/src/commands/indent-block.ts b/blocksuite/affine/block-note/src/commands/indent-block.ts index 5d9e6c1597..75b0e8a6ff 100644 --- a/blocksuite/affine/block-note/src/commands/indent-block.ts +++ b/blocksuite/affine/block-note/src/commands/indent-block.ts @@ -21,14 +21,10 @@ import type { Command } from '@blocksuite/block-std'; * - ddd * - eee */ -export const indentBlock: Command< - never, - never, - { - blockId?: string; - stopCapture?: boolean; - } -> = (ctx, next) => { +export const indentBlock: Command<{ + blockId?: string; + stopCapture?: boolean; +}> = (ctx, next) => { let { blockId } = ctx; const { std, stopCapture = true } = ctx; const { store } = std; diff --git a/blocksuite/affine/block-note/src/commands/indent-blocks.ts b/blocksuite/affine/block-note/src/commands/indent-blocks.ts index 3f093696cc..6e94f3239b 100644 --- a/blocksuite/affine/block-note/src/commands/indent-blocks.ts +++ b/blocksuite/affine/block-note/src/commands/indent-blocks.ts @@ -5,14 +5,12 @@ import { } from '@blocksuite/affine-shared/utils'; import { type Command, TextSelection } from '@blocksuite/block-std'; -export const indentBlocks: Command< - never, - never, - { - blockIds?: string[]; - stopCapture?: boolean; - } -> = (ctx, next) => { +import { indentBlock } from './indent-block'; + +export const indentBlocks: Command<{ + blockIds?: string[]; + stopCapture?: boolean; +}> = (ctx, next) => { let { blockIds } = ctx; const { std, stopCapture = true } = ctx; const { store, selection, range, host } = std; @@ -95,7 +93,7 @@ export const indentBlocks: Command< } indentIds.forEach(id => { - std.command.exec('indentBlock', { blockId: id, stopCapture: false }); + std.command.exec(indentBlock, { blockId: id, stopCapture: false }); }); { diff --git a/blocksuite/affine/block-note/src/commands/index.ts b/blocksuite/affine/block-note/src/commands/index.ts index ae18065d02..a58bfa6867 100644 --- a/blocksuite/affine/block-note/src/commands/index.ts +++ b/blocksuite/affine/block-note/src/commands/index.ts @@ -1,42 +1,12 @@ -import { - getBlockIndexCommand, - getBlockSelectionsCommand, - getNextBlockCommand, - getPrevBlockCommand, - getSelectedBlocksCommand, -} from '@blocksuite/affine-shared/commands'; -import type { BlockCommands } from '@blocksuite/block-std'; - -import { updateBlockType } from './block-type.js'; -import { changeNoteDisplayMode } from './change-note-display-mode.js'; -import { dedentBlock } from './dedent-block.js'; -import { dedentBlockToRoot } from './dedent-block-to-root.js'; -import { dedentBlocks } from './dedent-blocks.js'; -import { dedentBlocksToRoot } from './dedent-blocks-to-root.js'; -import { focusBlockEnd } from './focus-block-end.js'; -import { focusBlockStart } from './focus-block-start.js'; -import { indentBlock } from './indent-block.js'; -import { indentBlocks } from './indent-blocks.js'; -import { selectBlock } from './select-block.js'; -import { selectBlocksBetween } from './select-blocks-between.js'; - -export const commands: BlockCommands = { - // block - getBlockIndex: getBlockIndexCommand, - getPrevBlock: getPrevBlockCommand, - getNextBlock: getNextBlockCommand, - getSelectedBlocks: getSelectedBlocksCommand, - getBlockSelections: getBlockSelectionsCommand, - selectBlock, - selectBlocksBetween, - focusBlockStart, - focusBlockEnd, - updateBlockType, - indentBlock, - dedentBlock, - indentBlocks, - dedentBlocks, - dedentBlockToRoot, - dedentBlocksToRoot, - changeNoteDisplayMode, -}; +export { updateBlockType } from './block-type.js'; +export { changeNoteDisplayMode } from './change-note-display-mode.js'; +export { dedentBlock } from './dedent-block.js'; +export { dedentBlockToRoot } from './dedent-block-to-root.js'; +export { dedentBlocks } from './dedent-blocks.js'; +export { dedentBlocksToRoot } from './dedent-blocks-to-root.js'; +export { focusBlockEnd } from './focus-block-end.js'; +export { focusBlockStart } from './focus-block-start.js'; +export { indentBlock } from './indent-block.js'; +export { indentBlocks } from './indent-blocks.js'; +export { selectBlock } from './select-block.js'; +export { selectBlocksBetween } from './select-blocks-between.js'; diff --git a/blocksuite/affine/block-note/src/commands/select-block.ts b/blocksuite/affine/block-note/src/commands/select-block.ts index 0fa4d3967f..a0d8793678 100644 --- a/blocksuite/affine/block-note/src/commands/select-block.ts +++ b/blocksuite/affine/block-note/src/commands/select-block.ts @@ -1,6 +1,12 @@ -import { BlockSelection, type Command } from '@blocksuite/block-std'; +import { + type BlockComponent, + BlockSelection, + type Command, +} from '@blocksuite/block-std'; -export const selectBlock: Command<'focusBlock'> = (ctx, next) => { +export const selectBlock: Command<{ + focusBlock?: BlockComponent; +}> = (ctx, next) => { const { focusBlock, std } = ctx; if (!focusBlock) { return; diff --git a/blocksuite/affine/block-note/src/commands/select-blocks-between.ts b/blocksuite/affine/block-note/src/commands/select-blocks-between.ts index 7ae46ded72..ef6aee7e7f 100644 --- a/blocksuite/affine/block-note/src/commands/select-blocks-between.ts +++ b/blocksuite/affine/block-note/src/commands/select-blocks-between.ts @@ -1,10 +1,14 @@ -import { BlockSelection, type Command } from '@blocksuite/block-std'; +import { + type BlockComponent, + BlockSelection, + type Command, +} from '@blocksuite/block-std'; -export const selectBlocksBetween: Command< - 'focusBlock' | 'anchorBlock', - never, - { tail: boolean } -> = (ctx, next) => { +export const selectBlocksBetween: Command<{ + focusBlock?: BlockComponent; + anchorBlock?: BlockComponent; + tail: boolean; +}> = (ctx, next) => { const { focusBlock, anchorBlock, tail } = ctx; if (!focusBlock || !anchorBlock) { return; diff --git a/blocksuite/affine/block-note/src/effects.ts b/blocksuite/affine/block-note/src/effects.ts index 6848e91e22..bd999671d5 100644 --- a/blocksuite/affine/block-note/src/effects.ts +++ b/blocksuite/affine/block-note/src/effects.ts @@ -1,18 +1,3 @@ -import type { BlockComponent } from '@blocksuite/block-std'; -import type { BlockModel } from '@blocksuite/store'; - -import type { updateBlockType } from './commands/block-type'; -import type { changeNoteDisplayMode } from './commands/change-note-display-mode'; -import type { dedentBlock } from './commands/dedent-block'; -import type { dedentBlockToRoot } from './commands/dedent-block-to-root'; -import type { dedentBlocks } from './commands/dedent-blocks'; -import type { dedentBlocksToRoot } from './commands/dedent-blocks-to-root'; -import type { focusBlockEnd } from './commands/focus-block-end'; -import type { focusBlockStart } from './commands/focus-block-start'; -import type { indentBlock } from './commands/indent-block'; -import type { indentBlocks } from './commands/indent-blocks'; -import type { selectBlock } from './commands/select-block'; -import type { selectBlocksBetween } from './commands/select-blocks-between'; import type { NoteConfig } from './config'; import { NoteBlockComponent } from './note-block'; import { @@ -29,25 +14,6 @@ export function effects() { declare global { namespace BlockSuite { - interface Commands { - selectBlock: typeof selectBlock; - selectBlocksBetween: typeof selectBlocksBetween; - focusBlockStart: typeof focusBlockStart; - focusBlockEnd: typeof focusBlockEnd; - indentBlocks: typeof indentBlocks; - dedentBlock: typeof dedentBlock; - dedentBlocksToRoot: typeof dedentBlocksToRoot; - dedentBlocks: typeof dedentBlocks; - indentBlock: typeof indentBlock; - updateBlockType: typeof updateBlockType; - dedentBlockToRoot: typeof dedentBlockToRoot; - changeNoteDisplayMode: typeof changeNoteDisplayMode; - } - interface CommandContext { - focusBlock?: BlockComponent | null; - anchorBlock?: BlockComponent | null; - updatedBlocks?: BlockModel[]; - } interface BlockServices { 'affine:note': NoteBlockService; } diff --git a/blocksuite/affine/block-note/src/note-service.ts b/blocksuite/affine/block-note/src/note-service.ts index 3c9d9913e4..9a8e8d63e8 100644 --- a/blocksuite/affine/block-note/src/note-service.ts +++ b/blocksuite/affine/block-note/src/note-service.ts @@ -1,17 +1,34 @@ import { textConversionConfigs } from '@blocksuite/affine-components/rich-text'; import { NoteBlockSchema } from '@blocksuite/affine-model'; +import { + getBlockSelectionsCommand, + getNextBlockCommand, + getPrevBlockCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { type BlockComponent, BlockSelection, BlockService, type BlockStdScope, + type Chain, TextSelection, type UIEventHandler, type UIEventStateContext, } from '@blocksuite/block-std'; import type { BaseSelection, BlockModel } from '@blocksuite/store'; +import { + dedentBlocks, + dedentBlocksToRoot, + focusBlockEnd, + focusBlockStart, + indentBlocks, + selectBlock, + selectBlocksBetween, + updateBlockType, +} from './commands'; import { moveBlockConfigs } from './move-block'; import { quickActionConfig } from './quick-action'; @@ -76,13 +93,13 @@ export class NoteBlockService extends BlockService { ctx.get('defaultState').event.preventDefault(); const [result] = this._std.command .chain() - .updateBlockType({ + .pipe(updateBlockType, { flavour: item.flavour, props: { type: item.type, }, }) - .inline((ctx, next) => { + .pipe((ctx, next) => { const newModels = ctx.updatedBlocks; if (!newModels) { return; @@ -145,7 +162,7 @@ export class NoteBlockService extends BlockService { const [result] = this._std.command .chain() - .inline((_, next) => { + .pipe((_, next) => { this._reset(); return next(); }) @@ -154,16 +171,16 @@ export class NoteBlockService extends BlockService { // 1. is paragraph, list, code block - follow the default behavior // 2. is not - select the next block (use block selection instead of text selection) cmd - .getTextSelection() - .inline<'currentSelectionPath'>((ctx, next) => { + .pipe(getTextSelectionCommand) + .pipe<{ currentSelectionPath: string }>((ctx, next) => { const currentTextSelection = ctx.currentTextSelection; if (!currentTextSelection) { return; } return next({ currentSelectionPath: currentTextSelection.blockId }); }) - .getNextBlock() - .inline((ctx, next) => { + .pipe(getNextBlockCommand) + .pipe((ctx, next) => { const { nextBlock } = ctx; if (!nextBlock) { @@ -177,13 +194,9 @@ export class NoteBlockService extends BlockService { 'affine:code', ]) ) { - this._std.command - .chain() - .with({ - focusBlock: nextBlock, - }) - .selectBlock() - .run(); + this._std.command.exec(selectBlock, { + focusBlock: nextBlock, + }); } return next({}); @@ -193,8 +206,8 @@ export class NoteBlockService extends BlockService { // 1. is paragraph, list, code block - focus it // 2. is not - select it using block selection cmd - .getBlockSelections() - .inline<'currentSelectionPath'>((ctx, next) => { + .pipe(getBlockSelectionsCommand) + .pipe<{ currentSelectionPath: string }>((ctx, next) => { const currentBlockSelections = ctx.currentBlockSelections; const blockSelection = currentBlockSelections?.at(-1); if (!blockSelection) { @@ -202,8 +215,8 @@ export class NoteBlockService extends BlockService { } return next({ currentSelectionPath: blockSelection.blockId }); }) - .getNextBlock() - .inline<'focusBlock'>((ctx, next) => { + .pipe(getNextBlockCommand) + .pipe<{ focusBlock: BlockComponent }>((ctx, next) => { const { nextBlock } = ctx; if (!nextBlock) { return; @@ -217,18 +230,15 @@ export class NoteBlockService extends BlockService { 'affine:code', ]) ) { - this._std.command - .chain() - .focusBlockStart({ focusBlock: nextBlock }) - .run(); + this._std.command.exec(focusBlockStart, { + focusBlock: nextBlock, + }); return next(); } - this._std.command - .chain() - .with({ focusBlock: nextBlock }) - .selectBlock() - .run(); + this._std.command.exec(selectBlock, { + focusBlock: nextBlock, + }); return next(); }), ]) @@ -242,7 +252,7 @@ export class NoteBlockService extends BlockService { const [result] = this._std.command .chain() - .inline((_, next) => { + .pipe((_, next) => { this._reset(); return next(); }) @@ -251,16 +261,16 @@ export class NoteBlockService extends BlockService { // 1. is paragraph, list, code block - follow the default behavior // 2. is not - select the previous block (use block selection instead of text selection) cmd - .getTextSelection() - .inline<'currentSelectionPath'>((ctx, next) => { + .pipe(getTextSelectionCommand) + .pipe<{ currentSelectionPath: string }>((ctx, next) => { const currentTextSelection = ctx.currentTextSelection; if (!currentTextSelection) { return; } return next({ currentSelectionPath: currentTextSelection.blockId }); }) - .getPrevBlock() - .inline((ctx, next) => { + .pipe(getPrevBlockCommand) + .pipe((ctx, next) => { const { prevBlock } = ctx; if (!prevBlock) { @@ -274,23 +284,19 @@ export class NoteBlockService extends BlockService { 'affine:code', ]) ) { - this._std.command - .chain() - .with({ - focusBlock: prevBlock, - }) - .selectBlock() - .run(); + this._std.command.exec(selectBlock, { + focusBlock: prevBlock, + }); } - return next({}); + return next(); }), // block selection - select the previous block // 1. is paragraph, list, code block - focus it // 2. is not - select it using block selection cmd - .getBlockSelections() - .inline<'currentSelectionPath'>((ctx, next) => { + .pipe(getBlockSelectionsCommand) + .pipe<{ currentSelectionPath: string }>((ctx, next) => { const currentBlockSelections = ctx.currentBlockSelections; const blockSelection = currentBlockSelections?.at(-1); if (!blockSelection) { @@ -298,8 +304,8 @@ export class NoteBlockService extends BlockService { } return next({ currentSelectionPath: blockSelection.blockId }); }) - .getPrevBlock() - .inline<'focusBlock'>((ctx, next) => { + .pipe(getPrevBlockCommand) + .pipe((ctx, next) => { const { prevBlock } = ctx; if (!prevBlock) { return; @@ -313,18 +319,15 @@ export class NoteBlockService extends BlockService { ]) ) { event.preventDefault(); - this._std.command - .chain() - .focusBlockEnd({ focusBlock: prevBlock }) - .run(); + this._std.command.exec(focusBlockEnd, { + focusBlock: prevBlock, + }); return next(); } - this._std.command - .chain() - .with({ focusBlock: prevBlock }) - .selectBlock() - .run(); + this._std.command.exec(selectBlock, { + focusBlock: prevBlock, + }); return next(); }), ]) @@ -333,34 +336,36 @@ export class NoteBlockService extends BlockService { return result; }; - private readonly _onBlockShiftDown = (cmd: BlockSuite.CommandChain) => { + private readonly _onBlockShiftDown = (cmd: Chain) => { return cmd - .getBlockSelections() - .inline<'currentSelectionPath' | 'anchorBlock'>((ctx, next) => { - const blockSelections = ctx.currentBlockSelections; - if (!blockSelections) { - return; - } + .pipe(getBlockSelectionsCommand) + .pipe<{ currentSelectionPath: string; anchorBlock: BlockComponent }>( + (ctx, next) => { + const blockSelections = ctx.currentBlockSelections; + if (!blockSelections) { + return; + } - if (!this._anchorSel) { - this._anchorSel = blockSelections.at(-1) ?? null; - } - if (!this._anchorSel) { - return; - } + if (!this._anchorSel) { + this._anchorSel = blockSelections.at(-1) ?? null; + } + if (!this._anchorSel) { + return; + } - const anchorBlock = ctx.std.view.getBlock(this._anchorSel.blockId); - if (!anchorBlock) { - return; + const anchorBlock = ctx.std.view.getBlock(this._anchorSel.blockId); + if (!anchorBlock) { + return; + } + return next({ + anchorBlock, + currentSelectionPath: + this._focusBlock?.blockId ?? anchorBlock?.blockId, + }); } - return next({ - anchorBlock, - currentSelectionPath: - this._focusBlock?.blockId ?? anchorBlock?.blockId, - }); - }) - .getNextBlock({}) - .inline<'focusBlock'>((ctx, next) => { + ) + .pipe(getNextBlockCommand) + .pipe<{ focusBlock: BlockComponent }>((ctx, next) => { const nextBlock = ctx.nextBlock; if (!nextBlock) { return; @@ -370,35 +375,37 @@ export class NoteBlockService extends BlockService { focusBlock: this._focusBlock, }); }) - .selectBlocksBetween({ tail: true }); + .pipe(selectBlocksBetween, { tail: true }); }; - private readonly _onBlockShiftUp = (cmd: BlockSuite.CommandChain) => { + private readonly _onBlockShiftUp = (cmd: Chain) => { return cmd - .getBlockSelections() - .inline<'currentSelectionPath' | 'anchorBlock'>((ctx, next) => { - const blockSelections = ctx.currentBlockSelections; - if (!blockSelections) { - return; + .pipe(getBlockSelectionsCommand) + .pipe<{ currentSelectionPath: string; anchorBlock: BlockComponent }>( + (ctx, next) => { + const blockSelections = ctx.currentBlockSelections; + if (!blockSelections) { + return; + } + if (!this._anchorSel) { + this._anchorSel = blockSelections.at(0) ?? null; + } + if (!this._anchorSel) { + return; + } + const anchorBlock = ctx.std.view.getBlock(this._anchorSel.blockId); + if (!anchorBlock) { + return; + } + return next({ + anchorBlock, + currentSelectionPath: + this._focusBlock?.blockId ?? anchorBlock?.blockId, + }); } - if (!this._anchorSel) { - this._anchorSel = blockSelections.at(0) ?? null; - } - if (!this._anchorSel) { - return; - } - const anchorBlock = ctx.std.view.getBlock(this._anchorSel.blockId); - if (!anchorBlock) { - return; - } - return next({ - anchorBlock, - currentSelectionPath: - this._focusBlock?.blockId ?? anchorBlock?.blockId, - }); - }) - .getPrevBlock({}) - .inline((ctx, next) => { + ) + .pipe(getPrevBlockCommand) + .pipe((ctx, next) => { const prevBlock = ctx.prevBlock; if (!prevBlock) { return; @@ -408,15 +415,15 @@ export class NoteBlockService extends BlockService { focusBlock: this._focusBlock, }); }) - .selectBlocksBetween({ tail: false }); + .pipe(selectBlocksBetween, { tail: false }); }; private readonly _onEnter = (ctx: UIEventStateContext) => { const event = ctx.get('defaultState').event; const [result] = this._std.command .chain() - .getBlockSelections() - .inline((ctx, next) => { + .pipe(getBlockSelectionsCommand) + .pipe((ctx, next) => { const blockSelection = ctx.currentBlockSelections?.at(-1); if (!blockSelection) { return; @@ -466,8 +473,8 @@ export class NoteBlockService extends BlockService { private readonly _onEsc = () => { const [result] = this._std.command .chain() - .getBlockSelections() - .inline((ctx, next) => { + .pipe(getBlockSelectionsCommand) + .pipe((ctx, next) => { const blockSelection = ctx.currentBlockSelections?.at(-1); if (!blockSelection) { return; @@ -556,7 +563,7 @@ export class NoteBlockService extends BlockService { ...this._bindQuickActionHotKey(), ...this._bindTextConversionHotKey(), Tab: ctx => { - const { success } = this.std.command.exec('indentBlocks'); + const [success] = this.std.command.exec(indentBlocks); if (!success) return; @@ -564,7 +571,7 @@ export class NoteBlockService extends BlockService { return true; }, 'Shift-Tab': ctx => { - const { success } = this.std.command.exec('dedentBlocks'); + const [success] = this.std.command.exec(dedentBlocks); if (!success) return; @@ -572,7 +579,7 @@ export class NoteBlockService extends BlockService { return true; }, 'Mod-Backspace': ctx => { - const { success } = this.std.command.exec('dedentBlocksToRoot'); + const [success] = this.std.command.exec(dedentBlocksToRoot); if (!success) return; diff --git a/blocksuite/affine/block-note/src/note-spec.ts b/blocksuite/affine/block-note/src/note-spec.ts index d429d2358a..a51d9e54ce 100644 --- a/blocksuite/affine/block-note/src/note-spec.ts +++ b/blocksuite/affine/block-note/src/note-spec.ts @@ -1,8 +1,4 @@ -import { - BlockViewExtension, - CommandExtension, - FlavourExtension, -} from '@blocksuite/block-std'; +import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; @@ -10,13 +6,11 @@ import { DocNoteBlockAdapterExtensions, EdgelessNoteBlockAdapterExtensions, } from './adapters/index.js'; -import { commands } from './commands/index.js'; import { NoteBlockService } from './note-service.js'; export const NoteBlockSpec: ExtensionType[] = [ FlavourExtension('affine:note'), NoteBlockService, - CommandExtension(commands), BlockViewExtension('affine:note', literal`affine-note`), DocNoteBlockAdapterExtensions, ].flat(); @@ -24,7 +18,6 @@ export const NoteBlockSpec: ExtensionType[] = [ export const EdgelessNoteBlockSpec: ExtensionType[] = [ FlavourExtension('affine:note'), NoteBlockService, - CommandExtension(commands), BlockViewExtension('affine:note', literal`affine-edgeless-note`), EdgelessNoteBlockAdapterExtensions, ].flat(); diff --git a/blocksuite/affine/block-note/src/quick-action.ts b/blocksuite/affine/block-note/src/quick-action.ts index 1cdc9d7dd6..7bce663b49 100644 --- a/blocksuite/affine/block-note/src/quick-action.ts +++ b/blocksuite/affine/block-note/src/quick-action.ts @@ -4,6 +4,10 @@ import { notifyDocCreated, promptDocTitle, } from '@blocksuite/affine-block-embed'; +import { + draftSelectedModelsCommand, + getSelectedModelsCommand, +} from '@blocksuite/affine-shared/commands'; import type { BlockStdScope } from '@blocksuite/block-std'; export interface QuickActionConfig { @@ -18,23 +22,20 @@ export const quickActionConfig: QuickActionConfig[] = [ id: 'convert-to-linked-doc', hotkey: `Mod-Shift-l`, showWhen: std => { - const [_, ctx] = std.command - .chain() - .getSelectedModels({ - types: ['block'], - }) - .run(); + const [_, ctx] = std.command.exec(getSelectedModelsCommand, { + types: ['block'], + }); const { selectedModels } = ctx; return !!selectedModels && selectedModels.length > 0; }, action: std => { const [_, ctx] = std.command .chain() - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { types: ['block'], mode: 'flat', }) - .draftSelectedModels() + .pipe(draftSelectedModelsCommand) .run(); const { selectedModels, draftedModels } = ctx; if (!selectedModels) return; diff --git a/blocksuite/affine/block-paragraph/src/commands/add-paragraph.ts b/blocksuite/affine/block-paragraph/src/commands/add-paragraph.ts index 48f2bd992f..e0cefd87a6 100644 --- a/blocksuite/affine/block-paragraph/src/commands/add-paragraph.ts +++ b/blocksuite/affine/block-paragraph/src/commands/add-paragraph.ts @@ -5,10 +5,11 @@ import { type Command, TextSelection } from '@blocksuite/block-std'; * Add a paragraph next to the current block. */ export const addParagraphCommand: Command< - never, - 'paragraphConvertedId', { blockId?: string; + }, + { + paragraphConvertedId: string; } > = (ctx, next) => { const { std } = ctx; diff --git a/blocksuite/affine/block-paragraph/src/commands/append-paragraph.ts b/blocksuite/affine/block-paragraph/src/commands/append-paragraph.ts index 029ccfce9f..67ef96e8a2 100644 --- a/blocksuite/affine/block-paragraph/src/commands/append-paragraph.ts +++ b/blocksuite/affine/block-paragraph/src/commands/append-paragraph.ts @@ -6,11 +6,10 @@ import { Text } from '@blocksuite/store'; /** * Append a paragraph block at the end of the whole page. */ -export const appendParagraphCommand: Command< - never, - never, - { text?: string } -> = (ctx, next) => { +export const appendParagraphCommand: Command<{ text?: string }> = ( + ctx, + next +) => { const { std, text = '' } = ctx; const { store } = std; if (!store.root) return; diff --git a/blocksuite/affine/block-paragraph/src/commands/dedent-paragraph.ts b/blocksuite/affine/block-paragraph/src/commands/dedent-paragraph.ts index 1f804c9c5b..918e634e2d 100644 --- a/blocksuite/affine/block-paragraph/src/commands/dedent-paragraph.ts +++ b/blocksuite/affine/block-paragraph/src/commands/dedent-paragraph.ts @@ -6,9 +6,10 @@ import { import { type Command, TextSelection } from '@blocksuite/block-std'; export const canDedentParagraphCommand: Command< - never, - 'indentContext', - Partial> + Partial>, + { + indentContext: IndentContext; + } > = (ctx, next) => { let { blockId, inlineIndex } = ctx; const { std } = ctx; @@ -56,7 +57,9 @@ export const canDedentParagraphCommand: Command< }); }; -export const dedentParagraphCommand: Command<'indentContext'> = (ctx, next) => { +export const dedentParagraphCommand: Command<{ + indentContext: IndentContext; +}> = (ctx, next) => { const { indentContext: dedentContext, std } = ctx; const { store, selection, range, host } = std; diff --git a/blocksuite/affine/block-paragraph/src/commands/indent-paragraph.ts b/blocksuite/affine/block-paragraph/src/commands/indent-paragraph.ts index 592dd421d1..8b3016e5eb 100644 --- a/blocksuite/affine/block-paragraph/src/commands/indent-paragraph.ts +++ b/blocksuite/affine/block-paragraph/src/commands/indent-paragraph.ts @@ -8,9 +8,10 @@ import { import { type Command, TextSelection } from '@blocksuite/block-std'; export const canIndentParagraphCommand: Command< - never, - 'indentContext', - Partial> + Partial>, + { + indentContext: IndentContext; + } > = (cxt, next) => { let { blockId, inlineIndex } = cxt; const { std } = cxt; @@ -60,7 +61,9 @@ export const canIndentParagraphCommand: Command< }); }; -export const indentParagraphCommand: Command<'indentContext'> = (ctx, next) => { +export const indentParagraphCommand: Command<{ + indentContext: IndentContext; +}> = (ctx, next) => { const { indentContext, std } = ctx; const { store, selection, host, range } = std; diff --git a/blocksuite/affine/block-paragraph/src/commands/index.ts b/blocksuite/affine/block-paragraph/src/commands/index.ts index 1b1071ec57..2b81a36699 100644 --- a/blocksuite/affine/block-paragraph/src/commands/index.ts +++ b/blocksuite/affine/block-paragraph/src/commands/index.ts @@ -1,23 +1,11 @@ -import type { BlockCommands } from '@blocksuite/block-std'; - -import { addParagraphCommand } from './add-paragraph.js'; -import { appendParagraphCommand } from './append-paragraph.js'; -import { +export { addParagraphCommand } from './add-paragraph.js'; +export { appendParagraphCommand } from './append-paragraph.js'; +export { canDedentParagraphCommand, dedentParagraphCommand, } from './dedent-paragraph.js'; -import { +export { canIndentParagraphCommand, indentParagraphCommand, } from './indent-paragraph.js'; -import { splitParagraphCommand } from './split-paragraph.js'; - -export const commands: BlockCommands = { - appendParagraph: appendParagraphCommand, - splitParagraph: splitParagraphCommand, - addParagraph: addParagraphCommand, - canIndentParagraph: canIndentParagraphCommand, - canDedentParagraph: canDedentParagraphCommand, - indentParagraph: indentParagraphCommand, - dedentParagraph: dedentParagraphCommand, -}; +export { splitParagraphCommand } from './split-paragraph.js'; diff --git a/blocksuite/affine/block-paragraph/src/commands/split-paragraph.ts b/blocksuite/affine/block-paragraph/src/commands/split-paragraph.ts index 81a78d0fc8..011078d27a 100644 --- a/blocksuite/affine/block-paragraph/src/commands/split-paragraph.ts +++ b/blocksuite/affine/block-paragraph/src/commands/split-paragraph.ts @@ -6,10 +6,11 @@ import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { type Command, TextSelection } from '@blocksuite/block-std'; export const splitParagraphCommand: Command< - never, - 'paragraphConvertedId', { blockId?: string; + }, + { + paragraphConvertedId: string; } > = (ctx, next) => { const { std } = ctx; diff --git a/blocksuite/affine/block-paragraph/src/effects.ts b/blocksuite/affine/block-paragraph/src/effects.ts index 015b73892d..4f633f167b 100644 --- a/blocksuite/affine/block-paragraph/src/effects.ts +++ b/blocksuite/affine/block-paragraph/src/effects.ts @@ -1,16 +1,3 @@ -import type { IndentContext } from '@blocksuite/affine-shared/types'; - -import type { addParagraphCommand } from './commands/add-paragraph.js'; -import type { appendParagraphCommand } from './commands/append-paragraph.js'; -import type { - canDedentParagraphCommand, - dedentParagraphCommand, -} from './commands/dedent-paragraph.js'; -import type { - canIndentParagraphCommand, - indentParagraphCommand, -} from './commands/indent-paragraph.js'; -import type { splitParagraphCommand } from './commands/split-paragraph.js'; import { effects as ParagraphHeadingIconEffects } from './heading-icon.js'; import { ParagraphBlockComponent } from './paragraph-block.js'; import type { ParagraphBlockService } from './paragraph-service.js'; @@ -25,19 +12,6 @@ declare global { interface BlockServices { 'affine:paragraph': ParagraphBlockService; } - interface Commands { - addParagraph: typeof addParagraphCommand; - appendParagraph: typeof appendParagraphCommand; - canIndentParagraph: typeof canIndentParagraphCommand; - canDedentParagraph: typeof canDedentParagraphCommand; - dedentParagraph: typeof dedentParagraphCommand; - indentParagraph: typeof indentParagraphCommand; - splitParagraph: typeof splitParagraphCommand; - } - interface CommandContext { - paragraphConvertedId?: string; - indentContext?: IndentContext; - } } interface HTMLElementTagNameMap { 'affine-paragraph': ParagraphBlockComponent; diff --git a/blocksuite/affine/block-paragraph/src/index.ts b/blocksuite/affine/block-paragraph/src/index.ts index fe6ce306d2..de229b245b 100644 --- a/blocksuite/affine/block-paragraph/src/index.ts +++ b/blocksuite/affine/block-paragraph/src/index.ts @@ -1,4 +1,5 @@ export * from './adapters/index.js'; +export * from './commands'; export * from './paragraph-block.js'; export * from './paragraph-service.js'; export * from './paragraph-spec.js'; diff --git a/blocksuite/affine/block-paragraph/src/paragraph-keymap.ts b/blocksuite/affine/block-paragraph/src/paragraph-keymap.ts index ec5586993f..fc5805d830 100644 --- a/blocksuite/affine/block-paragraph/src/paragraph-keymap.ts +++ b/blocksuite/affine/block-paragraph/src/paragraph-keymap.ts @@ -12,6 +12,16 @@ import { import { KeymapExtension, TextSelection } from '@blocksuite/block-std'; import { IS_MAC } from '@blocksuite/global/env'; +import { addParagraphCommand } from './commands/add-paragraph.js'; +import { + canDedentParagraphCommand, + dedentParagraphCommand, +} from './commands/dedent-paragraph.js'; +import { + canIndentParagraphCommand, + indentParagraphCommand, +} from './commands/indent-paragraph.js'; +import { splitParagraphCommand } from './commands/split-paragraph.js'; import { forwardDelete } from './utils/forward-delete.js'; import { mergeWithPrev } from './utils/merge-with-prev.js'; @@ -46,7 +56,11 @@ export const ParagraphKeymapExtension = KeymapExtension( return true; } - std.command.chain().canDedentParagraph().dedentParagraph().run(); + std.command + .chain() + .pipe(canDedentParagraphCommand) + .pipe(dedentParagraphCommand) + .run(); return true; }, 'Mod-Enter': ctx => { @@ -73,7 +87,7 @@ export const ParagraphKeymapExtension = KeymapExtension( return true; } - std.command.exec('addParagraph'); + std.command.chain().pipe(addParagraphCommand).run(); return true; }, Enter: ctx => { @@ -113,7 +127,7 @@ export const ParagraphKeymapExtension = KeymapExtension( raw.preventDefault(); store.captureSync(); model.text.delete(range.index - 1, 1); - std.command.exec('addParagraph'); + std.command.chain().pipe(addParagraphCommand).run(); return true; } return true; @@ -146,11 +160,11 @@ export const ParagraphKeymapExtension = KeymapExtension( } if (isEnd) { - std.command.exec('addParagraph'); + std.command.chain().pipe(addParagraphCommand).run(); return true; } - std.command.exec('splitParagraph'); + std.command.chain().pipe(splitParagraphCommand).run(); return true; }, Delete: ctx => { @@ -189,8 +203,8 @@ export const ParagraphKeymapExtension = KeymapExtension( Tab: ctx => { const [success] = std.command .chain() - .canIndentParagraph() - .indentParagraph() + .pipe(canIndentParagraphCommand) + .pipe(indentParagraphCommand) .run(); if (!success) { return; @@ -201,8 +215,8 @@ export const ParagraphKeymapExtension = KeymapExtension( 'Shift-Tab': ctx => { const [success] = std.command .chain() - .canDedentParagraph() - .dedentParagraph() + .pipe(canDedentParagraphCommand) + .pipe(dedentParagraphCommand) .run(); if (!success) { return; diff --git a/blocksuite/affine/block-paragraph/src/paragraph-spec.ts b/blocksuite/affine/block-paragraph/src/paragraph-spec.ts index 8fcc215a4f..a22df6f6bf 100644 --- a/blocksuite/affine/block-paragraph/src/paragraph-spec.ts +++ b/blocksuite/affine/block-paragraph/src/paragraph-spec.ts @@ -1,13 +1,8 @@ -import { - BlockViewExtension, - CommandExtension, - FlavourExtension, -} from '@blocksuite/block-std'; +import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; import { ParagraphBlockAdapterExtensions } from './adapters/extension.js'; -import { commands } from './commands/index.js'; import { ParagraphKeymapExtension, ParagraphTextKeymapExtension, @@ -17,7 +12,6 @@ import { ParagraphBlockService } from './paragraph-service.js'; export const ParagraphBlockSpec: ExtensionType[] = [ FlavourExtension('affine:paragraph'), ParagraphBlockService, - CommandExtension(commands), BlockViewExtension('affine:paragraph', literal`affine-paragraph`), ParagraphTextKeymapExtension, ParagraphKeymapExtension, diff --git a/blocksuite/affine/block-surface-ref/src/commands.ts b/blocksuite/affine/block-surface-ref/src/commands.ts index f4257fbf2d..0a4a7c9114 100644 --- a/blocksuite/affine/block-surface-ref/src/commands.ts +++ b/blocksuite/affine/block-surface-ref/src/commands.ts @@ -1,15 +1,18 @@ import { getSurfaceBlock } from '@blocksuite/affine-block-surface'; import type { SurfaceRefProps } from '@blocksuite/affine-model'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; -import type { BlockCommands, Command } from '@blocksuite/block-std'; +import type { Command } from '@blocksuite/block-std'; +import type { BlockModel } from '@blocksuite/store'; export const insertSurfaceRefBlockCommand: Command< - 'selectedModels', - 'insertedSurfaceRefBlockId', { reference: string; place: 'after' | 'before'; removeEmptyLine?: boolean; + selectedModels?: BlockModel[]; + }, + { + insertedSurfaceRefBlockId: string; } > = (ctx, next) => { const { selectedModels, reference, place, removeEmptyLine, std } = ctx; @@ -57,7 +60,3 @@ export const insertSurfaceRefBlockCommand: Command< insertedSurfaceRefBlockId: result[0], }); }; - -export const commands: BlockCommands = { - insertSurfaceRefBlock: insertSurfaceRefBlockCommand, -}; diff --git a/blocksuite/affine/block-surface-ref/src/effects.ts b/blocksuite/affine/block-surface-ref/src/effects.ts index 8465ad8af1..2982e4c54e 100644 --- a/blocksuite/affine/block-surface-ref/src/effects.ts +++ b/blocksuite/affine/block-surface-ref/src/effects.ts @@ -1,4 +1,3 @@ -import type { insertSurfaceRefBlockCommand } from './commands.js'; import { SurfaceRefGenericBlockPortal } from './portal/generic-block.js'; import { SurfaceRefNotePortal } from './portal/note.js'; import { SurfaceRefBlockComponent } from './surface-ref-block.js'; @@ -16,22 +15,3 @@ export function effects() { ); customElements.define('surface-ref-note-portal', SurfaceRefNotePortal); } - -declare global { - namespace BlockSuite { - interface CommandContext { - insertedSurfaceRefBlockId?: string; - } - - interface Commands { - /** - * insert a SurfaceRef block after or before the current block selection - * @param reference the reference block id. The block should be group or frame - * @param place where to insert the LaTeX block - * @param removeEmptyLine remove the current block if it is empty - * @returns the id of the inserted SurfaceRef block - */ - insertSurfaceRefBlock: typeof insertSurfaceRefBlockCommand; - } - } -} diff --git a/blocksuite/affine/block-surface-ref/src/index.ts b/blocksuite/affine/block-surface-ref/src/index.ts index e6edd2c6a7..33e9135377 100644 --- a/blocksuite/affine/block-surface-ref/src/index.ts +++ b/blocksuite/affine/block-surface-ref/src/index.ts @@ -1,5 +1,4 @@ -import '@blocksuite/affine-shared/commands'; - +export * from './commands.js'; export * from './surface-ref-block.js'; export * from './surface-ref-block-edgeless.js'; export { diff --git a/blocksuite/affine/block-surface-ref/src/surface-ref-spec.ts b/blocksuite/affine/block-surface-ref/src/surface-ref-spec.ts index 95a0443dd4..b6cd7c180d 100644 --- a/blocksuite/affine/block-surface-ref/src/surface-ref-spec.ts +++ b/blocksuite/affine/block-surface-ref/src/surface-ref-spec.ts @@ -1,17 +1,13 @@ import { BlockViewExtension, - CommandExtension, FlavourExtension, WidgetViewMapExtension, } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; -import { commands } from './commands.js'; - export const PageSurfaceRefBlockSpec: ExtensionType[] = [ FlavourExtension('affine:surface-ref'), - CommandExtension(commands), BlockViewExtension('affine:surface-ref', literal`affine-surface-ref`), WidgetViewMapExtension('affine:surface-ref', { surfaceToolbar: literal`affine-surface-ref-toolbar`, diff --git a/blocksuite/affine/block-surface/src/commands/auto-align.ts b/blocksuite/affine/block-surface/src/commands/auto-align.ts index 370176de8b..80a5fdf55e 100644 --- a/blocksuite/affine/block-surface/src/commands/auto-align.ts +++ b/blocksuite/affine/block-surface/src/commands/auto-align.ts @@ -24,10 +24,7 @@ import { updateXYWH } from '../utils/update-xywh.js'; /** * Automatically arrange elements according to fixed row and column rules */ -export const autoArrangeElementsCommand: Command = ( - ctx, - next -) => { +export const autoArrangeElementsCommand: Command = (ctx, next) => { const { updateBlock } = ctx.std.store; const gfx = ctx.std.get(GfxControllerIdentifier); @@ -42,10 +39,7 @@ export const autoArrangeElementsCommand: Command = ( /** * Adjust the height of the selected element to a fixed value and arrange the elements */ -export const autoResizeElementsCommand: Command = ( - ctx, - next -) => { +export const autoResizeElementsCommand: Command = (ctx, next) => { const { updateBlock } = ctx.std.store; const gfx = ctx.std.get(GfxControllerIdentifier); diff --git a/blocksuite/affine/block-surface/src/commands/index.ts b/blocksuite/affine/block-surface/src/commands/index.ts index c1439dd56e..8f65e72aed 100644 --- a/blocksuite/affine/block-surface/src/commands/index.ts +++ b/blocksuite/affine/block-surface/src/commands/index.ts @@ -1,13 +1,5 @@ -import type { BlockCommands } from '@blocksuite/block-std'; - -import { +export { autoArrangeElementsCommand, autoResizeElementsCommand, } from './auto-align.js'; -import { reassociateConnectorsCommand } from './reassociate-connectors.js'; - -export const commands: BlockCommands = { - reassociateConnectors: reassociateConnectorsCommand, - autoArrangeElements: autoArrangeElementsCommand, - autoResizeElements: autoResizeElementsCommand, -}; +export { reassociateConnectorsCommand } from './reassociate-connectors.js'; diff --git a/blocksuite/affine/block-surface/src/commands/reassociate-connectors.ts b/blocksuite/affine/block-surface/src/commands/reassociate-connectors.ts index c244c970bb..dca0dda390 100644 --- a/blocksuite/affine/block-surface/src/commands/reassociate-connectors.ts +++ b/blocksuite/affine/block-surface/src/commands/reassociate-connectors.ts @@ -6,11 +6,10 @@ import type { Command } from '@blocksuite/block-std'; * @param oldId - the old block id * @param newId - the new block id */ -export const reassociateConnectorsCommand: Command< - never, - never, - { oldId: string; newId: string } -> = (ctx, next) => { +export const reassociateConnectorsCommand: Command<{ + oldId: string; + newId: string; +}> = (ctx, next) => { const { oldId, newId } = ctx; const service = ctx.std.getService('affine:surface'); if (!oldId || !newId || !service) { diff --git a/blocksuite/affine/block-surface/src/effects.ts b/blocksuite/affine/block-surface/src/effects.ts index 796c0dcacd..a9ee0b6e35 100644 --- a/blocksuite/affine/block-surface/src/effects.ts +++ b/blocksuite/affine/block-surface/src/effects.ts @@ -1,8 +1,3 @@ -import type { - autoArrangeElementsCommand, - autoResizeElementsCommand, -} from './commands/auto-align.js'; -import type { reassociateConnectorsCommand } from './commands/reassociate-connectors.js'; import { SurfaceBlockComponent } from './surface-block.js'; import { SurfaceBlockVoidComponent } from './surface-block-void.js'; import type { SurfaceBlockModel } from './surface-model.js'; @@ -21,10 +16,5 @@ declare global { interface BlockModels { 'affine:surface': SurfaceBlockModel; } - interface Commands { - reassociateConnectors: typeof reassociateConnectorsCommand; - autoArrangeElements: typeof autoArrangeElementsCommand; - autoResizeElements: typeof autoResizeElementsCommand; - } } } diff --git a/blocksuite/affine/block-surface/src/index.ts b/blocksuite/affine/block-surface/src/index.ts index 488dd11f16..2d05675938 100644 --- a/blocksuite/affine/block-surface/src/index.ts +++ b/blocksuite/affine/block-surface/src/index.ts @@ -116,3 +116,5 @@ export const MindmapUtils = { hideNodeConnector, containsNode, }; + +export * from './commands'; diff --git a/blocksuite/affine/block-surface/src/surface-spec.ts b/blocksuite/affine/block-surface/src/surface-spec.ts index acd639eda0..c4062e8173 100644 --- a/blocksuite/affine/block-surface/src/surface-spec.ts +++ b/blocksuite/affine/block-surface/src/surface-spec.ts @@ -1,8 +1,4 @@ -import { - BlockViewExtension, - CommandExtension, - FlavourExtension, -} from '@blocksuite/block-std'; +import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; @@ -10,7 +6,6 @@ import { EdgelessSurfaceBlockAdapterExtensions, SurfaceBlockAdapterExtensions, } from './adapters/extension'; -import { commands } from './commands'; import { EdgelessCRUDExtension, EdgelessLegacySlotExtension, @@ -21,7 +16,6 @@ import { MindMapView } from './view/mindmap'; const CommonSurfaceBlockSpec: ExtensionType[] = [ FlavourExtension('affine:surface'), SurfaceBlockService, - CommandExtension(commands), MindMapView, EdgelessCRUDExtension, EdgelessLegacySlotExtension, diff --git a/blocksuite/affine/block-table/src/commands.ts b/blocksuite/affine/block-table/src/commands.ts index aa7c7cc3de..659c1b3f83 100644 --- a/blocksuite/affine/block-table/src/commands.ts +++ b/blocksuite/affine/block-table/src/commands.ts @@ -1,15 +1,15 @@ -import '@blocksuite/affine-shared/commands'; - import { TableModelFlavour } from '@blocksuite/affine-model'; import { generateFractionalIndexingKeyBetween } from '@blocksuite/affine-shared/utils'; -import type { BlockCommands, Command } from '@blocksuite/block-std'; -import { nanoid, Text } from '@blocksuite/store'; +import type { Command } from '@blocksuite/block-std'; +import { type BlockModel, nanoid, Text } from '@blocksuite/store'; export const insertTableBlockCommand: Command< - 'selectedModels', - 'insertedTableBlockId', { place?: 'after' | 'before'; removeEmptyLine?: boolean; + selectedModels?: BlockModel[]; + }, + { + insertedTableBlockId: string; } > = (ctx, next) => { const { selectedModels, place, removeEmptyLine, std } = ctx; @@ -61,7 +61,3 @@ export const insertTableBlockCommand: Command< next({ insertedTableBlockId: blockId }); }; - -export const tableCommands: BlockCommands = { - insertTableBlock: insertTableBlockCommand, -}; diff --git a/blocksuite/affine/block-table/src/effects.ts b/blocksuite/affine/block-table/src/effects.ts index f3eb0bfe14..60b6268250 100644 --- a/blocksuite/affine/block-table/src/effects.ts +++ b/blocksuite/affine/block-table/src/effects.ts @@ -1,5 +1,4 @@ import { AddButton } from './add-button'; -import type { insertTableBlockCommand } from './commands'; import { SelectionLayer } from './selection-layer'; import { TableBlockComponent } from './table-block'; import { TableCell } from './table-cell'; @@ -10,15 +9,3 @@ export function effects() { customElements.define('affine-table-add-button', AddButton); customElements.define('affine-table-selection-layer', SelectionLayer); } - -declare global { - namespace BlockSuite { - interface CommandContext { - insertedTableBlockId?: string; - } - - interface Commands { - insertTableBlock: typeof insertTableBlockCommand; - } - } -} diff --git a/blocksuite/affine/block-table/src/selection-schema.ts b/blocksuite/affine/block-table/src/selection-schema.ts index 134a414546..387c4d2fbf 100644 --- a/blocksuite/affine/block-table/src/selection-schema.ts +++ b/blocksuite/affine/block-table/src/selection-schema.ts @@ -107,12 +107,4 @@ export class TableSelection extends BaseSelection { } } -declare global { - namespace BlockSuite { - interface Selection { - table: typeof TableSelection; - } - } -} - export const TableSelectionExtension = SelectionExtension(TableSelection); diff --git a/blocksuite/affine/block-table/src/table-spec.ts b/blocksuite/affine/block-table/src/table-spec.ts index 1b37fd50a5..b2d1e39231 100644 --- a/blocksuite/affine/block-table/src/table-spec.ts +++ b/blocksuite/affine/block-table/src/table-spec.ts @@ -1,18 +1,12 @@ import { TableModelFlavour } from '@blocksuite/affine-model'; -import { - BlockViewExtension, - CommandExtension, - FlavourExtension, -} from '@blocksuite/block-std'; +import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; import { TableBlockAdapterExtensions } from './adapters/extension.js'; -import { tableCommands } from './commands.js'; export const TableBlockSpec: ExtensionType[] = [ FlavourExtension(TableModelFlavour), - CommandExtension(tableCommands), BlockViewExtension(TableModelFlavour, literal`affine-table`), TableBlockAdapterExtensions, ].flat(); diff --git a/blocksuite/affine/components/src/peek/commands.ts b/blocksuite/affine/components/src/peek/commands.ts index e175adbbbc..31f8433844 100644 --- a/blocksuite/affine/components/src/peek/commands.ts +++ b/blocksuite/affine/components/src/peek/commands.ts @@ -1,4 +1,8 @@ -/// +import { + getBlockSelectionsCommand, + getSelectedBlocksCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import type { BlockComponent, Command, @@ -10,15 +14,22 @@ import { isPeekable, peek } from './peekable.js'; const getSelectedPeekableBlocks = (cmd: InitCommandCtx) => { const [result, ctx] = cmd.std.command .chain() - .tryAll(chain => [chain.getTextSelection(), chain.getBlockSelections()]) - .getSelectedBlocks({ types: ['text', 'block'] }) + .tryAll(chain => [ + chain.pipe(getTextSelectionCommand), + chain.pipe(getBlockSelectionsCommand), + ]) + .pipe(getSelectedBlocksCommand, { types: ['text', 'block'] }) .run(); return ((result ? ctx.selectedBlocks : []) || []).filter(isPeekable); }; export const getSelectedPeekableBlocksCommand: Command< - 'selectedBlocks', - 'selectedPeekableBlocks' + { + selectedBlocks: BlockComponent[]; + }, + { + selectedPeekableBlocks: BlockComponent[]; + } > = (ctx, next) => { const selectedPeekableBlocks = getSelectedPeekableBlocks(ctx); if (selectedPeekableBlocks.length > 0) { @@ -26,10 +37,9 @@ export const getSelectedPeekableBlocksCommand: Command< } }; -export const peekSelectedBlockCommand: Command<'selectedBlocks'> = ( - ctx, - next -) => { +export const peekSelectedBlockCommand: Command<{ + selectedBlocks: BlockComponent[]; +}> = (ctx, next) => { const peekableBlocks = getSelectedPeekableBlocks(ctx); // if there are multiple blocks, peek the first one const block = peekableBlocks.at(0); @@ -39,17 +49,3 @@ export const peekSelectedBlockCommand: Command<'selectedBlocks'> = ( next(); } }; - -declare global { - namespace BlockSuite { - interface CommandContext { - selectedPeekableBlocks?: BlockComponent[]; - } - - interface Commands { - peekSelectedBlock: typeof peekSelectedBlockCommand; - getSelectedPeekableBlocks: typeof getSelectedPeekableBlocksCommand; - // todo: add command for peek an inline element? - } - } -} diff --git a/blocksuite/affine/components/src/rich-text/effects.ts b/blocksuite/affine/components/src/rich-text/effects.ts index 0f2a7aeefb..59c8b7e307 100644 --- a/blocksuite/affine/components/src/rich-text/effects.ts +++ b/blocksuite/affine/components/src/rich-text/effects.ts @@ -1,21 +1,3 @@ -import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; - -import type { deleteTextCommand } from './format/delete-text.js'; -import type { formatBlockCommand } from './format/format-block.js'; -import type { formatNativeCommand } from './format/format-native.js'; -import type { formatTextCommand } from './format/format-text.js'; -import type { insertInlineLatex } from './format/insert-inline-latex.js'; -import type { - getTextStyle, - isTextStyleActive, - toggleBold, - toggleCode, - toggleItalic, - toggleLink, - toggleStrike, - toggleTextStyleCommand, - toggleUnderline, -} from './format/text-style.js'; import { AffineFootnoteNode, AffineLink, @@ -64,25 +46,4 @@ declare global { 'latex-editor-menu': LatexEditorMenu; 'link-popup': LinkPopup; } - namespace BlockSuite { - interface CommandContext { - textStyle?: AffineTextAttributes; - } - interface Commands { - deleteText: typeof deleteTextCommand; - formatBlock: typeof formatBlockCommand; - formatNative: typeof formatNativeCommand; - formatText: typeof formatTextCommand; - toggleBold: typeof toggleBold; - toggleItalic: typeof toggleItalic; - toggleUnderline: typeof toggleUnderline; - toggleStrike: typeof toggleStrike; - toggleCode: typeof toggleCode; - toggleLink: typeof toggleLink; - toggleTextStyle: typeof toggleTextStyleCommand; - getTextStyle: typeof getTextStyle; - isTextStyleActive: typeof isTextStyleActive; - insertInlineLatex: typeof insertInlineLatex; - } - } } diff --git a/blocksuite/affine/components/src/rich-text/format/config.ts b/blocksuite/affine/components/src/rich-text/format/config.ts index c3fe5de3f4..7277540ebc 100644 --- a/blocksuite/affine/components/src/rich-text/format/config.ts +++ b/blocksuite/affine/components/src/rich-text/format/config.ts @@ -9,6 +9,15 @@ import { StrikethroughIcon, UnderlineIcon, } from '../../icons/index.js'; +import { + isTextStyleActive, + toggleBold, + toggleCode, + toggleItalic, + toggleLink, + toggleStrike, + toggleUnderline, +} from './text-style.js'; export interface TextFormatConfig { id: string; @@ -28,12 +37,12 @@ export const textFormatConfigs: TextFormatConfig[] = [ activeWhen: host => { const [result] = host.std.command .chain() - .isTextStyleActive({ key: 'bold' }) + .pipe(isTextStyleActive, { key: 'bold' }) .run(); return result; }, action: host => { - host.std.command.chain().toggleBold().run(); + host.std.command.chain().pipe(toggleBold).run(); }, }, { @@ -44,12 +53,12 @@ export const textFormatConfigs: TextFormatConfig[] = [ activeWhen: host => { const [result] = host.std.command .chain() - .isTextStyleActive({ key: 'italic' }) + .pipe(isTextStyleActive, { key: 'italic' }) .run(); return result; }, action: host => { - host.std.command.chain().toggleItalic().run(); + host.std.command.chain().pipe(toggleItalic).run(); }, }, { @@ -60,12 +69,12 @@ export const textFormatConfigs: TextFormatConfig[] = [ activeWhen: host => { const [result] = host.std.command .chain() - .isTextStyleActive({ key: 'underline' }) + .pipe(isTextStyleActive, { key: 'underline' }) .run(); return result; }, action: host => { - host.std.command.chain().toggleUnderline().run(); + host.std.command.chain().pipe(toggleUnderline).run(); }, }, { @@ -76,12 +85,12 @@ export const textFormatConfigs: TextFormatConfig[] = [ activeWhen: host => { const [result] = host.std.command .chain() - .isTextStyleActive({ key: 'strike' }) + .pipe(isTextStyleActive, { key: 'strike' }) .run(); return result; }, action: host => { - host.std.command.chain().toggleStrike().run(); + host.std.command.chain().pipe(toggleStrike).run(); }, }, { @@ -92,12 +101,12 @@ export const textFormatConfigs: TextFormatConfig[] = [ activeWhen: host => { const [result] = host.std.command .chain() - .isTextStyleActive({ key: 'code' }) + .pipe(isTextStyleActive, { key: 'code' }) .run(); return result; }, action: host => { - host.std.command.chain().toggleCode().run(); + host.std.command.chain().pipe(toggleCode).run(); }, }, { @@ -108,12 +117,12 @@ export const textFormatConfigs: TextFormatConfig[] = [ activeWhen: host => { const [result] = host.std.command .chain() - .isTextStyleActive({ key: 'link' }) + .pipe(isTextStyleActive, { key: 'link' }) .run(); return result; }, action: host => { - host.std.command.chain().toggleLink().run(); + host.std.command.chain().pipe(toggleLink).run(); }, }, ]; diff --git a/blocksuite/affine/components/src/rich-text/format/delete-text.ts b/blocksuite/affine/components/src/rich-text/format/delete-text.ts index dceef6a262..839713f98d 100644 --- a/blocksuite/affine/components/src/rich-text/format/delete-text.ts +++ b/blocksuite/affine/components/src/rich-text/format/delete-text.ts @@ -2,13 +2,10 @@ import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { type Command, TextSelection } from '@blocksuite/block-std'; import type { Text } from '@blocksuite/store'; -export const deleteTextCommand: Command< - 'currentTextSelection', - never, - { - textSelection?: TextSelection; - } -> = (ctx, next) => { +export const deleteTextCommand: Command<{ + currentTextSelection?: TextSelection; + textSelection?: TextSelection; +}> = (ctx, next) => { const textSelection = ctx.textSelection ?? ctx.currentTextSelection; if (!textSelection) return; diff --git a/blocksuite/affine/components/src/rich-text/format/format-block.ts b/blocksuite/affine/components/src/rich-text/format/format-block.ts index be155240d3..468ea77a42 100644 --- a/blocksuite/affine/components/src/rich-text/format/format-block.ts +++ b/blocksuite/affine/components/src/rich-text/format/format-block.ts @@ -1,3 +1,4 @@ +import { getSelectedBlocksCommand } from '@blocksuite/affine-shared/commands'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import type { BlockSelection, Command } from '@blocksuite/block-std'; import { assertExists } from '@blocksuite/global/utils'; @@ -6,15 +7,12 @@ import { INLINE_ROOT_ATTR, type InlineRootElement } from '@blocksuite/inline'; import { FORMAT_BLOCK_SUPPORT_FLAVOURS } from './consts.js'; // for block selection -export const formatBlockCommand: Command< - 'currentBlockSelections', - never, - { - blockSelections?: BlockSelection[]; - styles: AffineTextAttributes; - mode?: 'replace' | 'merge'; - } -> = (ctx, next) => { +export const formatBlockCommand: Command<{ + currentBlockSelections?: BlockSelection[]; + blockSelections?: BlockSelection[]; + styles: AffineTextAttributes; + mode?: 'replace' | 'merge'; +}> = (ctx, next) => { const blockSelections = ctx.blockSelections ?? ctx.currentBlockSelections; assertExists( blockSelections, @@ -28,7 +26,7 @@ export const formatBlockCommand: Command< const success = ctx.std.command .chain() - .getSelectedBlocks({ + .pipe(getSelectedBlocksCommand, { blockSelections, filter: el => FORMAT_BLOCK_SUPPORT_FLAVOURS.includes( @@ -36,7 +34,7 @@ export const formatBlockCommand: Command< ), types: ['block'], }) - .inline((ctx, next) => { + .pipe((ctx, next) => { const { selectedBlocks } = ctx; assertExists(selectedBlocks); diff --git a/blocksuite/affine/components/src/rich-text/format/format-native.ts b/blocksuite/affine/components/src/rich-text/format/format-native.ts index a4fa57bd48..9afe4d6fe1 100644 --- a/blocksuite/affine/components/src/rich-text/format/format-native.ts +++ b/blocksuite/affine/components/src/rich-text/format/format-native.ts @@ -9,15 +9,11 @@ import { INLINE_ROOT_ATTR, type InlineRootElement } from '@blocksuite/inline'; import { FORMAT_NATIVE_SUPPORT_FLAVOURS } from './consts.js'; // for native range -export const formatNativeCommand: Command< - never, - never, - { - range?: Range; - styles: AffineTextAttributes; - mode?: 'replace' | 'merge'; - } -> = (ctx, next) => { +export const formatNativeCommand: Command<{ + range?: Range; + styles: AffineTextAttributes; + mode?: 'replace' | 'merge'; +}> = (ctx, next) => { const { styles, mode = 'merge' } = ctx; let range = ctx.range; diff --git a/blocksuite/affine/components/src/rich-text/format/format-text.ts b/blocksuite/affine/components/src/rich-text/format/format-text.ts index e0c81f5519..1f37cd45e2 100644 --- a/blocksuite/affine/components/src/rich-text/format/format-text.ts +++ b/blocksuite/affine/components/src/rich-text/format/format-text.ts @@ -1,3 +1,4 @@ +import { getSelectedBlocksCommand } from '@blocksuite/affine-shared/commands'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import type { Command, TextSelection } from '@blocksuite/block-std'; import { INLINE_ROOT_ATTR, type InlineRootElement } from '@blocksuite/inline'; @@ -6,15 +7,12 @@ import { FORMAT_TEXT_SUPPORT_FLAVOURS } from './consts.js'; import { clearMarksOnDiscontinuousInput } from './utils.js'; // for text selection -export const formatTextCommand: Command< - 'currentTextSelection', - never, - { - textSelection?: TextSelection; - styles: AffineTextAttributes; - mode?: 'replace' | 'merge'; - } -> = (ctx, next) => { +export const formatTextCommand: Command<{ + currentTextSelection?: TextSelection; + textSelection?: TextSelection; + styles: AffineTextAttributes; + mode?: 'replace' | 'merge'; +}> = (ctx, next) => { const { styles, mode = 'merge' } = ctx; const textSelection = ctx.textSelection ?? ctx.currentTextSelection; @@ -22,7 +20,7 @@ export const formatTextCommand: Command< const success = ctx.std.command .chain() - .getSelectedBlocks({ + .pipe(getSelectedBlocksCommand, { textSelection, filter: el => FORMAT_TEXT_SUPPORT_FLAVOURS.includes( @@ -30,7 +28,7 @@ export const formatTextCommand: Command< ), types: ['text'], }) - .inline((ctx, next) => { + .pipe((ctx, next) => { const { selectedBlocks } = ctx; if (!selectedBlocks) return; diff --git a/blocksuite/affine/components/src/rich-text/format/index.ts b/blocksuite/affine/components/src/rich-text/format/index.ts index 3b3163e0e1..f2bcbeeb34 100644 --- a/blocksuite/affine/components/src/rich-text/format/index.ts +++ b/blocksuite/affine/components/src/rich-text/format/index.ts @@ -1,19 +1,16 @@ -import { getTextSelectionCommand } from '@blocksuite/affine-shared/commands'; -import type { BlockCommands } from '@blocksuite/block-std'; - -import { deleteTextCommand } from './delete-text.js'; export type { TextFormatConfig } from './config.js'; export { textFormatConfigs } from './config.js'; -import { formatBlockCommand } from './format-block.js'; export { FORMAT_BLOCK_SUPPORT_FLAVOURS, FORMAT_NATIVE_SUPPORT_FLAVOURS, FORMAT_TEXT_SUPPORT_FLAVOURS, } from './consts.js'; -import { formatNativeCommand } from './format-native.js'; -import { formatTextCommand } from './format-text.js'; -import { insertInlineLatex } from './insert-inline-latex.js'; -import { +export { deleteTextCommand } from './delete-text.js'; +export { formatBlockCommand } from './format-block.js'; +export { formatNativeCommand } from './format-native.js'; +export { formatTextCommand } from './format-text.js'; +export { insertInlineLatex } from './insert-inline-latex.js'; +export { getTextStyle, isTextStyleActive, toggleBold, @@ -29,21 +26,3 @@ export { insertContent, isFormatSupported, } from './utils.js'; - -export const textCommands: BlockCommands = { - deleteText: deleteTextCommand, - formatBlock: formatBlockCommand, - formatNative: formatNativeCommand, - formatText: formatTextCommand, - toggleBold: toggleBold, - toggleItalic: toggleItalic, - toggleUnderline: toggleUnderline, - toggleStrike: toggleStrike, - toggleCode: toggleCode, - toggleLink: toggleLink, - toggleTextStyle: toggleTextStyleCommand, - isTextStyleActive: isTextStyleActive, - getTextStyle: getTextStyle, - getTextSelection: getTextSelectionCommand, - insertInlineLatex: insertInlineLatex, -}; diff --git a/blocksuite/affine/components/src/rich-text/format/insert-inline-latex.ts b/blocksuite/affine/components/src/rich-text/format/insert-inline-latex.ts index fd3d3c21fc..ca0c15d2cc 100644 --- a/blocksuite/affine/components/src/rich-text/format/insert-inline-latex.ts +++ b/blocksuite/affine/components/src/rich-text/format/insert-inline-latex.ts @@ -1,12 +1,9 @@ import type { Command, TextSelection } from '@blocksuite/block-std'; -export const insertInlineLatex: Command< - 'currentTextSelection', - never, - { - textSelection?: TextSelection; - } -> = (ctx, next) => { +export const insertInlineLatex: Command<{ + currentTextSelection?: TextSelection; + textSelection?: TextSelection; +}> = (ctx, next) => { const textSelection = ctx.textSelection ?? ctx.currentTextSelection; if (!textSelection || !textSelection.isCollapsed()) return; diff --git a/blocksuite/affine/components/src/rich-text/format/text-style.ts b/blocksuite/affine/components/src/rich-text/format/text-style.ts index cc625a4109..64feb32fe7 100644 --- a/blocksuite/affine/components/src/rich-text/format/text-style.ts +++ b/blocksuite/affine/components/src/rich-text/format/text-style.ts @@ -1,22 +1,25 @@ +import { + getBlockSelectionsCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import type { Command } from '@blocksuite/block-std'; import { INLINE_ROOT_ATTR, type InlineRootElement } from '@blocksuite/inline'; import { toggleLinkPopup } from '../inline/index.js'; +import { formatBlockCommand } from './format-block.js'; +import { formatNativeCommand } from './format-native.js'; +import { formatTextCommand } from './format-text.js'; import { getCombinedTextStyle } from './utils.js'; -export const toggleTextStyleCommand: Command< - never, - never, - { - key: Extract< - keyof AffineTextAttributes, - 'bold' | 'italic' | 'underline' | 'strike' | 'code' - >; - } -> = (ctx, next) => { +export const toggleTextStyleCommand: Command<{ + key: Extract< + keyof AffineTextAttributes, + 'bold' | 'italic' | 'underline' | 'strike' | 'code' + >; +}> = (ctx, next) => { const { std, key } = ctx; - const [active] = std.command.chain().isTextStyleActive({ key }).run(); + const [active] = std.command.chain().pipe(isTextStyleActive, { key }).run(); const payload: { styles: AffineTextAttributes; @@ -30,9 +33,9 @@ export const toggleTextStyleCommand: Command< const [result] = std.command .chain() .try(chain => [ - chain.getTextSelection().formatText(payload), - chain.getBlockSelections().formatBlock(payload), - chain.formatNative(payload), + chain.pipe(getTextSelectionCommand).pipe(formatTextCommand, payload), + chain.pipe(getBlockSelectionsCommand).pipe(formatBlockCommand, payload), + chain.pipe(formatNativeCommand, payload), ]) .run(); @@ -50,7 +53,10 @@ const toggleTextStyleCommandWrapper = ( > ): Command => { return (ctx, next) => { - const { success } = ctx.std.command.exec('toggleTextStyle', { key }); + const [success] = ctx.std.command + .chain() + .pipe(toggleTextStyleCommand, { key }) + .run(); if (success) next(); return false; }; @@ -95,7 +101,10 @@ export const toggleLink: Command = (_ctx, next) => { return next(); }; -export const getTextStyle: Command = (ctx, next) => { +export const getTextStyle: Command<{}, { textStyle: AffineTextAttributes }> = ( + ctx, + next +) => { const [result, innerCtx] = getCombinedTextStyle( ctx.std.command.chain() ).run(); @@ -106,14 +115,13 @@ export const getTextStyle: Command = (ctx, next) => { return next({ textStyle: innerCtx.textStyle }); }; -export const isTextStyleActive: Command< - never, - never, - { key: keyof AffineTextAttributes } -> = (ctx, next) => { +export const isTextStyleActive: Command<{ key: keyof AffineTextAttributes }> = ( + ctx, + next +) => { const key = ctx.key; const [result] = getCombinedTextStyle(ctx.std.command.chain()) - .inline((ctx, next) => { + .pipe((ctx, next) => { const { textStyle } = ctx; if (textStyle && key in textStyle) { diff --git a/blocksuite/affine/components/src/rich-text/format/utils.ts b/blocksuite/affine/components/src/rich-text/format/utils.ts index 13db010753..657199ffee 100644 --- a/blocksuite/affine/components/src/rich-text/format/utils.ts +++ b/blocksuite/affine/components/src/rich-text/format/utils.ts @@ -1,9 +1,13 @@ +import { + getBlockSelectionsCommand, + getSelectedBlocksCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import { BLOCK_ID_ATTR, type BlockComponent, type Chain, - type CommandKeyToData, type EditorHost, type InitCommandCtx, } from '@blocksuite/block-std'; @@ -71,24 +75,22 @@ function getSelectedInlineEditors( }); } -function handleCurrentSelection< - InlineOut extends BlockSuite.CommandDataName = never, ->( +function handleCurrentSelection( chain: Chain, handler: ( type: 'text' | 'block' | 'native', inlineEditors: InlineEditor[] - ) => CommandKeyToData | boolean | void -) { - return chain.try(chain => [ + ) => { textStyle: AffineTextAttributes } | boolean | void +): Chain { + return chain.try(chain => [ // text selection, corresponding to `formatText` command chain - .getTextSelection() - .getSelectedBlocks({ + .pipe(getTextSelectionCommand) + .pipe(getSelectedBlocksCommand, { types: ['text'], filter: el => FORMAT_TEXT_SUPPORT_FLAVOURS.includes(el.model.flavour), }) - .inline((ctx, next) => { + .pipe((ctx, next) => { const { selectedBlocks } = ctx; assertExists(selectedBlocks); @@ -110,12 +112,12 @@ function handleCurrentSelection< }), // block selection, corresponding to `formatBlock` command chain - .getBlockSelections() - .getSelectedBlocks({ + .pipe(getBlockSelectionsCommand) + .pipe(getSelectedBlocksCommand, { types: ['block'], filter: el => FORMAT_BLOCK_SUPPORT_FLAVOURS.includes(el.model.flavour), }) - .inline((ctx, next) => { + .pipe((ctx, next) => { const { selectedBlocks } = ctx; assertExists(selectedBlocks); @@ -135,7 +137,7 @@ function handleCurrentSelection< return next(result); }), // native selection, corresponding to `formatNative` command - chain.inline((ctx, next) => { + chain.pipe((ctx, next) => { const selectedInlineEditors = Array.from( ctx.std.host.querySelectorAll(`[${INLINE_ROOT_ATTR}]`) ) @@ -166,7 +168,7 @@ function handleCurrentSelection< } export function getCombinedTextStyle(chain: Chain) { - return handleCurrentSelection<'textStyle'>(chain, (type, inlineEditors) => { + return handleCurrentSelection(chain, (type, inlineEditors) => { if (type === 'text') { return { textStyle: getCombinedFormatFromInlineEditors( diff --git a/blocksuite/affine/components/src/rich-text/index.ts b/blocksuite/affine/components/src/rich-text/index.ts index cc079c2cfe..c764dc9ad6 100644 --- a/blocksuite/affine/components/src/rich-text/index.ts +++ b/blocksuite/affine/components/src/rich-text/index.ts @@ -13,17 +13,7 @@ export { } from './dom'; export * from './effects'; export * from './extension'; -export { - clearMarksOnDiscontinuousInput, - FORMAT_BLOCK_SUPPORT_FLAVOURS, - FORMAT_NATIVE_SUPPORT_FLAVOURS, - FORMAT_TEXT_SUPPORT_FLAVOURS, - insertContent, - isFormatSupported, - textCommands, - type TextFormatConfig, - textFormatConfigs, -} from './format'; +export * from './format'; export * from './inline'; export { textKeymap } from './keymap'; export { insertLinkedNode } from './linked-node'; diff --git a/blocksuite/affine/shared/src/commands/block-crud/get-block-index.ts b/blocksuite/affine/shared/src/commands/block-crud/get-block-index.ts index 280b29250c..896fceee92 100644 --- a/blocksuite/affine/shared/src/commands/block-crud/get-block-index.ts +++ b/blocksuite/affine/shared/src/commands/block-crud/get-block-index.ts @@ -1,18 +1,22 @@ import type { BlockComponent, Command } from '@blocksuite/block-std'; -import { assertExists } from '@blocksuite/global/utils'; export const getBlockIndexCommand: Command< - 'currentSelectionPath', - 'blockIndex' | 'parentBlock', { + currentSelectionPath?: string; path?: string; + }, + { + blockIndex?: number; + parentBlock?: BlockComponent; } > = (ctx, next) => { const path = ctx.path ?? ctx.currentSelectionPath; - assertExists( - path, - '`path` is required, you need to pass it in args or ctx before adding this command to the pipeline.' - ); + if (!path) { + console.error( + '`path` is required, you need to pass it in args or ctx before adding this command to the pipeline.' + ); + return; + } const parentModel = ctx.std.store.getParent(path); if (!parentModel) return; @@ -29,16 +33,3 @@ export const getBlockIndexCommand: Command< parentBlock: parent as BlockComponent, }); }; - -declare global { - namespace BlockSuite { - interface CommandContext { - blockIndex?: number; - parentBlock?: BlockComponent; - } - - interface Commands { - getBlockIndex: typeof getBlockIndexCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/block-crud/get-next-block.ts b/blocksuite/affine/shared/src/commands/block-crud/get-next-block.ts index 94d5c42959..e71bd38ad8 100644 --- a/blocksuite/affine/shared/src/commands/block-crud/get-next-block.ts +++ b/blocksuite/affine/shared/src/commands/block-crud/get-next-block.ts @@ -1,5 +1,4 @@ import type { BlockComponent, Command } from '@blocksuite/block-std'; -import { assertExists } from '@blocksuite/global/utils'; import { getNextContentBlock } from '../../utils/index.js'; @@ -13,17 +12,21 @@ function getNextBlock(std: BlockSuite.Std, path: string) { } export const getNextBlockCommand: Command< - 'currentSelectionPath', - 'nextBlock', { + currentSelectionPath?: string; path?: string; + }, + { + nextBlock?: BlockComponent; } > = (ctx, next) => { const path = ctx.path ?? ctx.currentSelectionPath; - assertExists( - path, - '`path` is required, you need to pass it in args or ctx before adding this command to the pipeline.' - ); + if (!path) { + console.error( + '`path` is required, you need to pass it in args or ctx before adding this command to the pipeline.' + ); + return; + } const nextBlock = getNextBlock(ctx.std, path); @@ -31,15 +34,3 @@ export const getNextBlockCommand: Command< next({ nextBlock }); } }; - -declare global { - namespace BlockSuite { - interface CommandContext { - nextBlock?: BlockComponent; - } - - interface Commands { - getNextBlock: typeof getNextBlockCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/block-crud/get-prev-block.ts b/blocksuite/affine/shared/src/commands/block-crud/get-prev-block.ts index b9e1cf6c4f..fcc14b5223 100644 --- a/blocksuite/affine/shared/src/commands/block-crud/get-prev-block.ts +++ b/blocksuite/affine/shared/src/commands/block-crud/get-prev-block.ts @@ -1,5 +1,4 @@ import type { BlockComponent, Command } from '@blocksuite/block-std'; -import { assertExists } from '@blocksuite/global/utils'; import { getPrevContentBlock } from '../../utils/index.js'; @@ -14,17 +13,21 @@ function getPrevBlock(std: BlockSuite.Std, path: string) { } export const getPrevBlockCommand: Command< - 'currentSelectionPath', - 'prevBlock', { + currentSelectionPath?: string; path?: string; + }, + { + prevBlock?: BlockComponent; } > = (ctx, next) => { const path = ctx.path ?? ctx.currentSelectionPath; - assertExists( - path, - '`path` is required, you need to pass it in args or ctx before adding this command to the pipeline.' - ); + if (!path) { + console.error( + '`path` is required, you need to pass it in args or ctx before adding this command to the pipeline.' + ); + return; + } const prevBlock = getPrevBlock(ctx.std, path); @@ -32,15 +35,3 @@ export const getPrevBlockCommand: Command< next({ prevBlock }); } }; - -declare global { - namespace BlockSuite { - interface CommandContext { - prevBlock?: BlockComponent; - } - - interface Commands { - getPrevBlock: typeof getPrevBlockCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts b/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts index 0dbb56a062..f64a052e40 100644 --- a/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts +++ b/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts @@ -9,9 +9,10 @@ import type { RoleType } from '@blocksuite/store'; import type { ImageSelection } from '../../selection/index.js'; export const getSelectedBlocksCommand: Command< - 'currentTextSelection' | 'currentBlockSelections' | 'currentImageSelections', - 'selectedBlocks', { + currentTextSelection?: TextSelection; + currentBlockSelections?: BlockSelection[]; + currentImageSelections?: ImageSelection[]; textSelection?: TextSelection; blockSelections?: BlockSelection[]; imageSelections?: ImageSelection[]; @@ -19,6 +20,9 @@ export const getSelectedBlocksCommand: Command< types?: Array<'image' | 'text' | 'block'>; roles?: RoleType[]; mode?: 'all' | 'flat' | 'highest'; + }, + { + selectedBlocks: BlockComponent[]; } > = (ctx, next) => { const { @@ -146,15 +150,3 @@ export const getSelectedBlocksCommand: Command< selectedBlocks: result, }); }; - -declare global { - namespace BlockSuite { - interface CommandContext { - selectedBlocks?: BlockComponent[]; - } - - interface Commands { - getSelectedBlocks: typeof getSelectedBlocksCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/index.ts b/blocksuite/affine/shared/src/commands/index.ts index 5ba866847e..9b43680d15 100644 --- a/blocksuite/affine/shared/src/commands/index.ts +++ b/blocksuite/affine/shared/src/commands/index.ts @@ -21,13 +21,3 @@ export { getTextSelectionCommand, type SelectionRect, } from './selection/index.js'; - -declare global { - namespace BlockSuite { - // if we use `with` or `inline` to add command data either then use a command we - // need to update this interface - interface CommandContext { - currentSelectionPath?: string; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/model-crud/clear-and-select-first-model.ts b/blocksuite/affine/shared/src/commands/model-crud/clear-and-select-first-model.ts index 9b1bfda0bd..39bce3dadf 100644 --- a/blocksuite/affine/shared/src/commands/model-crud/clear-and-select-first-model.ts +++ b/blocksuite/affine/shared/src/commands/model-crud/clear-and-select-first-model.ts @@ -1,9 +1,9 @@ import { type Command, TextSelection } from '@blocksuite/block-std'; +import type { BlockModel } from '@blocksuite/store'; -export const clearAndSelectFirstModelCommand: Command<'selectedModels'> = ( - ctx, - next -) => { +export const clearAndSelectFirstModelCommand: Command<{ + selectedModels?: BlockModel[]; +}> = (ctx, next) => { const models = ctx.selectedModels; if (!models) { @@ -31,11 +31,3 @@ export const clearAndSelectFirstModelCommand: Command<'selectedModels'> = ( return next(); }; - -declare global { - namespace BlockSuite { - interface Commands { - clearAndSelectFirstModel: typeof clearAndSelectFirstModelCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/model-crud/copy-selected-models.ts b/blocksuite/affine/shared/src/commands/model-crud/copy-selected-models.ts index 5be64ef32a..6d2663e210 100644 --- a/blocksuite/affine/shared/src/commands/model-crud/copy-selected-models.ts +++ b/blocksuite/affine/shared/src/commands/model-crud/copy-selected-models.ts @@ -1,10 +1,10 @@ import type { Command } from '@blocksuite/block-std'; -import { Slice } from '@blocksuite/store'; +import { type BlockModel, type DraftModel, Slice } from '@blocksuite/store'; -export const copySelectedModelsCommand: Command<'draftedModels' | 'onCopy'> = ( - ctx, - next -) => { +export const copySelectedModelsCommand: Command<{ + draftedModels?: Promise>[]>; + onCopy?: () => void; +}> = (ctx, next) => { const models = ctx.draftedModels; if (!models) { console.error( @@ -23,14 +23,3 @@ export const copySelectedModelsCommand: Command<'draftedModels' | 'onCopy'> = ( .catch(console.error); return next(); }; - -declare global { - namespace BlockSuite { - interface CommandContext { - onCopy?: () => void; - } - interface Commands { - copySelectedModels: typeof copySelectedModelsCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/model-crud/delete-selected-models.ts b/blocksuite/affine/shared/src/commands/model-crud/delete-selected-models.ts index 4b95265dde..6e38e23a29 100644 --- a/blocksuite/affine/shared/src/commands/model-crud/delete-selected-models.ts +++ b/blocksuite/affine/shared/src/commands/model-crud/delete-selected-models.ts @@ -1,9 +1,9 @@ import type { Command } from '@blocksuite/block-std'; +import type { BlockModel } from '@blocksuite/store'; -export const deleteSelectedModelsCommand: Command<'selectedModels'> = ( - ctx, - next -) => { +export const deleteSelectedModelsCommand: Command<{ + selectedModels?: BlockModel[]; +}> = (ctx, next) => { const models = ctx.selectedModels; if (!models) { @@ -19,11 +19,3 @@ export const deleteSelectedModelsCommand: Command<'selectedModels'> = ( return next(); }; - -declare global { - namespace BlockSuite { - interface Commands { - deleteSelectedModels: typeof deleteSelectedModelsCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/model-crud/draft-selected-models.ts b/blocksuite/affine/shared/src/commands/model-crud/draft-selected-models.ts index fcf603bcb6..3117681359 100644 --- a/blocksuite/affine/shared/src/commands/model-crud/draft-selected-models.ts +++ b/blocksuite/affine/shared/src/commands/model-crud/draft-selected-models.ts @@ -6,8 +6,12 @@ import { } from '@blocksuite/store'; export const draftSelectedModelsCommand: Command< - 'selectedModels', - 'draftedModels' + { + selectedModels?: BlockModel[]; + }, + { + draftedModels: Promise>[]>; + } > = (ctx, next) => { const models = ctx.selectedModels; if (!models) { @@ -44,15 +48,3 @@ export const draftSelectedModelsCommand: Command< return next({ draftedModels: draftedModelsPromise }); }; - -declare global { - namespace BlockSuite { - interface CommandContext { - draftedModels?: Promise>[]>; - } - - interface Commands { - draftSelectedModels: typeof draftSelectedModelsCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/model-crud/duplicate-selected-model.ts b/blocksuite/affine/shared/src/commands/model-crud/duplicate-selected-model.ts index 61c91a7456..8d900fe001 100644 --- a/blocksuite/affine/shared/src/commands/model-crud/duplicate-selected-model.ts +++ b/blocksuite/affine/shared/src/commands/model-crud/duplicate-selected-model.ts @@ -1,9 +1,10 @@ import type { Command } from '@blocksuite/block-std'; -import { Slice } from '@blocksuite/store'; +import { type BlockModel, type DraftModel, Slice } from '@blocksuite/store'; -export const duplicateSelectedModelsCommand: Command< - 'draftedModels' | 'selectedModels' -> = (ctx, next) => { +export const duplicateSelectedModelsCommand: Command<{ + draftedModels?: Promise>[]>; + selectedModels?: BlockModel[]; +}> = (ctx, next) => { const { std, draftedModels, selectedModels } = ctx; if (!draftedModels || !selectedModels) return; @@ -28,11 +29,3 @@ export const duplicateSelectedModelsCommand: Command< return next(); }; - -declare global { - namespace BlockSuite { - interface Commands { - duplicateSelectedModels: typeof duplicateSelectedModelsCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/model-crud/get-selected-models.ts b/blocksuite/affine/shared/src/commands/model-crud/get-selected-models.ts index 5f72cad3f0..d9a1244a66 100644 --- a/blocksuite/affine/shared/src/commands/model-crud/get-selected-models.ts +++ b/blocksuite/affine/shared/src/commands/model-crud/get-selected-models.ts @@ -1,6 +1,13 @@ import type { Command } from '@blocksuite/block-std'; import type { BlockModel } from '@blocksuite/store'; +import { getSelectedBlocksCommand } from '../block-crud/get-selected-blocks'; +import { + getBlockSelectionsCommand, + getImageSelectionsCommand, + getTextSelectionCommand, +} from '../selection'; + /** * Retrieves the selected models based on the provided selection types and mode. * @@ -29,11 +36,12 @@ import type { BlockModel } from '@blocksuite/store'; * @returns An object containing the selected models as an array of BlockModel instances. */ export const getSelectedModelsCommand: Command< - never, - 'selectedModels', { types?: Array<'image' | 'text' | 'block'>; mode?: 'all' | 'flat' | 'highest'; + }, + { + selectedModels: BlockModel[]; } > = (ctx, next) => { const types = ctx.types ?? ['block', 'text', 'image']; @@ -42,15 +50,12 @@ export const getSelectedModelsCommand: Command< ctx.std.command .chain() .tryAll(chain => [ - chain.getTextSelection(), - chain.getBlockSelections(), - chain.getImageSelections(), + chain.pipe(getTextSelectionCommand), + chain.pipe(getBlockSelectionsCommand), + chain.pipe(getImageSelectionsCommand), ]) - .getSelectedBlocks({ - types, - mode, - }) - .inline(ctx => { + .pipe(getSelectedBlocksCommand, { types, mode }) + .pipe(ctx => { const { selectedBlocks = [] } = ctx; selectedModels.push(...selectedBlocks.map(el => el.model)); }) @@ -58,15 +63,3 @@ export const getSelectedModelsCommand: Command< next({ selectedModels }); }; - -declare global { - namespace BlockSuite { - interface CommandContext { - selectedModels?: BlockModel[]; - } - - interface Commands { - getSelectedModels: typeof getSelectedModelsCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/model-crud/retain-first-model.ts b/blocksuite/affine/shared/src/commands/model-crud/retain-first-model.ts index 12571d88b0..0c886b6e94 100644 --- a/blocksuite/affine/shared/src/commands/model-crud/retain-first-model.ts +++ b/blocksuite/affine/shared/src/commands/model-crud/retain-first-model.ts @@ -1,9 +1,9 @@ import type { Command } from '@blocksuite/block-std'; +import type { BlockModel } from '@blocksuite/store'; -export const retainFirstModelCommand: Command<'selectedModels'> = ( - ctx, - next -) => { +export const retainFirstModelCommand: Command<{ + selectedModels?: BlockModel[]; +}> = (ctx, next) => { if (!ctx.selectedModels) { console.error( '`selectedModels` is required, you need to use `getSelectedModels` command before adding this command to the pipeline.' @@ -17,11 +17,3 @@ export const retainFirstModelCommand: Command<'selectedModels'> = ( return next(); }; - -declare global { - namespace BlockSuite { - interface Commands { - retainFirstModel: typeof retainFirstModelCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/selection/get-block-selections.ts b/blocksuite/affine/shared/src/commands/selection/get-block-selections.ts index 0a94bfce39..cd0241d271 100644 --- a/blocksuite/affine/shared/src/commands/selection/get-block-selections.ts +++ b/blocksuite/affine/shared/src/commands/selection/get-block-selections.ts @@ -1,23 +1,10 @@ -import { BlockSelection, type Command } from '@blocksuite/block-std'; +import { BlockSelection } from '@blocksuite/block-std'; -export const getBlockSelectionsCommand: Command< - never, - 'currentBlockSelections' -> = (ctx, next) => { +import type { GetSelectionCommand } from './types'; + +export const getBlockSelectionsCommand: GetSelectionCommand = (ctx, next) => { const currentBlockSelections = ctx.std.selection.filter(BlockSelection); if (currentBlockSelections.length === 0) return; next({ currentBlockSelections }); }; - -declare global { - namespace BlockSuite { - interface CommandContext { - currentBlockSelections?: BlockSelection[]; - } - - interface Commands { - getBlockSelections: typeof getBlockSelectionsCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/selection/get-image-selections.ts b/blocksuite/affine/shared/src/commands/selection/get-image-selections.ts index 39f1a83114..a55a2176a6 100644 --- a/blocksuite/affine/shared/src/commands/selection/get-image-selections.ts +++ b/blocksuite/affine/shared/src/commands/selection/get-image-selections.ts @@ -1,25 +1,9 @@ -import type { Command } from '@blocksuite/block-std'; - import { ImageSelection } from '../../selection/index.js'; +import type { GetSelectionCommand } from './types.js'; -export const getImageSelectionsCommand: Command< - never, - 'currentImageSelections' -> = (ctx, next) => { +export const getImageSelectionsCommand: GetSelectionCommand = (ctx, next) => { const currentImageSelections = ctx.std.selection.filter(ImageSelection); if (currentImageSelections.length === 0) return; next({ currentImageSelections }); }; - -declare global { - namespace BlockSuite { - interface CommandContext { - currentImageSelections?: ImageSelection[]; - } - - interface Commands { - getImageSelections: typeof getImageSelectionsCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/selection/get-selection-rects.ts b/blocksuite/affine/shared/src/commands/selection/get-selection-rects.ts index a3e5822f91..fedb02075f 100644 --- a/blocksuite/affine/shared/src/commands/selection/get-selection-rects.ts +++ b/blocksuite/affine/shared/src/commands/selection/get-selection-rects.ts @@ -19,11 +19,14 @@ export interface SelectionRect { } export const getSelectionRectsCommand: Command< - 'currentTextSelection' | 'currentBlockSelections', - 'selectionRects', { + currentTextSelection?: TextSelection; + currentBlockSelections?: BlockSelection[]; textSelection?: TextSelection; blockSelections?: BlockSelection[]; + }, + { + selectionRects: SelectionRect[]; } > = (ctx, next) => { let textSelection; @@ -86,26 +89,6 @@ export const getSelectionRectsCommand: Command< return; }; -declare global { - namespace BlockSuite { - interface CommandContext { - selectionRects?: SelectionRect[]; - } - - interface Commands { - /** - * Get the selection rects of the current selection or given selections. - * - * @chain may be `getTextSelection`, `getBlockSelections`, or nothing. - * @param textSelection The provided text selection. - * @param blockSelections The provided block selections. If `textSelection` is provided, this will be ignored. - * @returns The selection rects. - */ - getSelectionRects: typeof getSelectionRectsCommand; - } - } -} - function covers(rect1: SelectionRect, rect2: SelectionRect): boolean { return ( rect1.left <= rect2.left && diff --git a/blocksuite/affine/shared/src/commands/selection/get-text-selection.ts b/blocksuite/affine/shared/src/commands/selection/get-text-selection.ts index 1c86f53271..886f0fbd78 100644 --- a/blocksuite/affine/shared/src/commands/selection/get-text-selection.ts +++ b/blocksuite/affine/shared/src/commands/selection/get-text-selection.ts @@ -1,23 +1,10 @@ -import { type Command, TextSelection } from '@blocksuite/block-std'; +import { TextSelection } from '@blocksuite/block-std'; -export const getTextSelectionCommand: Command = ( - ctx, - next -) => { +import type { GetSelectionCommand } from './types'; + +export const getTextSelectionCommand: GetSelectionCommand = (ctx, next) => { const currentTextSelection = ctx.std.selection.find(TextSelection); if (!currentTextSelection) return; next({ currentTextSelection }); }; - -declare global { - namespace BlockSuite { - interface CommandContext { - currentTextSelection?: TextSelection; - } - - interface Commands { - getTextSelection: typeof getTextSelectionCommand; - } - } -} diff --git a/blocksuite/affine/shared/src/commands/selection/types.ts b/blocksuite/affine/shared/src/commands/selection/types.ts new file mode 100644 index 0000000000..20e0f0204b --- /dev/null +++ b/blocksuite/affine/shared/src/commands/selection/types.ts @@ -0,0 +1,16 @@ +import type { + BlockSelection, + Command, + TextSelection, +} from '@blocksuite/block-std'; + +import type { ImageSelection } from '../../selection/image'; + +export type GetSelectionCommand = Command< + {}, + { + currentTextSelection?: TextSelection; + currentBlockSelections?: BlockSelection[]; + currentImageSelections?: ImageSelection[]; + } +>; diff --git a/blocksuite/affine/shared/src/selection/hightlight.ts b/blocksuite/affine/shared/src/selection/hightlight.ts index 6af6fb016d..1fa3304ec7 100644 --- a/blocksuite/affine/shared/src/selection/hightlight.ts +++ b/blocksuite/affine/shared/src/selection/hightlight.ts @@ -50,13 +50,5 @@ export class HighlightSelection extends BaseSelection { } } -declare global { - namespace BlockSuite { - interface Selection { - highlight: typeof HighlightSelection; - } - } -} - export const HighlightSelectionExtension = SelectionExtension(HighlightSelection); diff --git a/blocksuite/affine/shared/src/selection/image.ts b/blocksuite/affine/shared/src/selection/image.ts index f2b15a9a9c..38bcacf853 100644 --- a/blocksuite/affine/shared/src/selection/image.ts +++ b/blocksuite/affine/shared/src/selection/image.ts @@ -30,12 +30,4 @@ export class ImageSelection extends BaseSelection { } } -declare global { - namespace BlockSuite { - interface Selection { - image: typeof ImageSelection; - } - } -} - export const ImageSelectionExtension = SelectionExtension(ImageSelection); diff --git a/blocksuite/affine/widget-remote-selection/src/doc/doc-remote-selection.ts b/blocksuite/affine/widget-remote-selection/src/doc/doc-remote-selection.ts index 0df138b957..339d5d8eac 100644 --- a/blocksuite/affine/widget-remote-selection/src/doc/doc-remote-selection.ts +++ b/blocksuite/affine/widget-remote-selection/src/doc/doc-remote-selection.ts @@ -1,3 +1,4 @@ +import { getSelectionRectsCommand } from '@blocksuite/affine-shared/commands'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { BlockSelection, @@ -212,10 +213,13 @@ export class AffineDocRemoteSelectionWidget extends WidgetComponent { if (!textSelection && !blockSelections.length) return []; - const { selectionRects } = this.std.command.exec('getSelectionRects', { - textSelection, - blockSelections, - }); + const [_, { selectionRects }] = this.std.command.exec( + getSelectionRectsCommand, + { + textSelection, + blockSelections, + } + ); if (!selectionRects) return []; diff --git a/blocksuite/blocks/src/effects.ts b/blocksuite/blocks/src/effects.ts index 2f2f29688d..62e2f60aab 100644 --- a/blocksuite/blocks/src/effects.ts +++ b/blocksuite/blocks/src/effects.ts @@ -35,11 +35,9 @@ import { effects as widgetDragHandleEffects } from '@blocksuite/affine-widget-dr import { effects as widgetFrameTitleEffects } from '@blocksuite/affine-widget-frame-title/effects'; import { effects as widgetRemoteSelectionEffects } from '@blocksuite/affine-widget-remote-selection/effects'; import { effects as widgetScrollAnchoringEffects } from '@blocksuite/affine-widget-scroll-anchoring/effects'; -import type { BlockComponent } from '@blocksuite/block-std'; import { effects as stdEffects } from '@blocksuite/block-std/effects'; import { effects as dataViewEffects } from '@blocksuite/data-view/effects'; import { effects as inlineEffects } from '@blocksuite/inline/effects'; -import type { BlockModel } from '@blocksuite/store'; import { registerSpecs } from './_specs/register-specs.js'; import { EdgelessAutoCompletePanel } from './root-block/edgeless/components/auto-complete/auto-complete-panel.js'; @@ -403,11 +401,6 @@ export function effects() { declare global { namespace BlockSuite { - interface CommandContext { - focusBlock?: BlockComponent | null; - anchorBlock?: BlockComponent | null; - updatedBlocks?: BlockModel[]; - } interface BlockConfigs { 'affine:page': RootBlockConfig; } diff --git a/blocksuite/blocks/src/index.ts b/blocksuite/blocks/src/index.ts index 2414fb9aca..1c5ae2511c 100644 --- a/blocksuite/blocks/src/index.ts +++ b/blocksuite/blocks/src/index.ts @@ -122,6 +122,7 @@ export { PlainTextAdapterFactoryExtension, PlainTextAdapterFactoryIdentifier, } from '@blocksuite/affine-shared/adapters'; +export * from '@blocksuite/affine-shared/commands'; export { HighlightSelection } from '@blocksuite/affine-shared/selection'; export * from '@blocksuite/affine-shared/services'; export { scrollbarStyle } from '@blocksuite/affine-shared/styles'; diff --git a/blocksuite/blocks/src/root-block/clipboard/index.ts b/blocksuite/blocks/src/root-block/clipboard/index.ts index 7255edd112..3c54ed3422 100644 --- a/blocksuite/blocks/src/root-block/clipboard/index.ts +++ b/blocksuite/blocks/src/root-block/clipboard/index.ts @@ -1,3 +1,4 @@ +import { deleteTextCommand } from '@blocksuite/affine-components/rich-text'; import { AttachmentAdapter, copyMiddleware, @@ -7,6 +8,18 @@ import { NotionTextAdapter, pasteMiddleware, } from '@blocksuite/affine-shared/adapters'; +import { + clearAndSelectFirstModelCommand, + copySelectedModelsCommand, + deleteSelectedModelsCommand, + draftSelectedModelsCommand, + getBlockIndexCommand, + getBlockSelectionsCommand, + getImageSelectionsCommand, + getSelectedModelsCommand, + getTextSelectionCommand, + retainFirstModelCommand, +} from '@blocksuite/affine-shared/commands'; import type { BlockComponent, UIEventHandler } from '@blocksuite/block-std'; import { DisposableGroup } from '@blocksuite/global/utils'; import type { BlockSnapshot, Store } from '@blocksuite/store'; @@ -23,9 +36,9 @@ export class PageClipboard { return this._std.command .chain() .with({ onCopy }) - .getSelectedModels() - .draftSelectedModels() - .copySelectedModels(); + .pipe(getSelectedModelsCommand) + .pipe(draftSelectedModelsCommand) + .pipe(copySelectedModelsCommand); }; protected _disposables = new DisposableGroup(); @@ -126,9 +139,9 @@ export class PageClipboard { this._copySelected(() => { this._std.command .chain() - .try(cmd => [ - cmd.getTextSelection().deleteText(), - cmd.getSelectedModels().deleteSelectedModels(), + .try<{}>(cmd => [ + cmd.pipe(getTextSelectionCommand).pipe(deleteTextCommand), + cmd.pipe(getSelectedModelsCommand).pipe(deleteSelectedModelsCommand), ]) .run(); }).run(); @@ -142,22 +155,22 @@ export class PageClipboard { this._std.command .chain() .try(cmd => [ - cmd.getTextSelection(), + cmd.pipe(getTextSelectionCommand), cmd - .getSelectedModels() - .clearAndSelectFirstModel() - .retainFirstModel() - .deleteSelectedModels(), + .pipe(getSelectedModelsCommand) + .pipe(clearAndSelectFirstModelCommand) + .pipe(retainFirstModelCommand) + .pipe(deleteSelectedModelsCommand), ]) - .try(cmd => [ - cmd.getTextSelection().inline<'currentSelectionPath'>((ctx, next) => { + .try<{ currentSelectionPath: string }>(cmd => [ + cmd.pipe(getTextSelectionCommand).pipe((ctx, next) => { const textSelection = ctx.currentTextSelection; if (!textSelection) { return; } next({ currentSelectionPath: textSelection.from.blockId }); }), - cmd.getBlockSelections().inline<'currentSelectionPath'>((ctx, next) => { + cmd.pipe(getBlockSelectionsCommand).pipe((ctx, next) => { const currentBlockSelections = ctx.currentBlockSelections; if (!currentBlockSelections) { return; @@ -168,7 +181,7 @@ export class PageClipboard { } next({ currentSelectionPath: blockSelection.blockId }); }), - cmd.getImageSelections().inline<'currentSelectionPath'>((ctx, next) => { + cmd.pipe(getImageSelectionsCommand).pipe((ctx, next) => { const currentImageSelections = ctx.currentImageSelections; if (!currentImageSelections) { return; @@ -180,8 +193,8 @@ export class PageClipboard { next({ currentSelectionPath: imageSelection.blockId }); }), ]) - .getBlockIndex() - .inline((ctx, next) => { + .pipe(getBlockIndexCommand) + .pipe((ctx, next) => { if (!ctx.parentBlock) { return; } diff --git a/blocksuite/blocks/src/root-block/commands/index.ts b/blocksuite/blocks/src/root-block/commands/index.ts deleted file mode 100644 index a611e929ff..0000000000 --- a/blocksuite/blocks/src/root-block/commands/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - getSelectedPeekableBlocksCommand, - peekSelectedBlockCommand, -} from '@blocksuite/affine-components/peek'; -import { textCommands } from '@blocksuite/affine-components/rich-text'; -import { - clearAndSelectFirstModelCommand, - copySelectedModelsCommand, - deleteSelectedModelsCommand, - draftSelectedModelsCommand, - duplicateSelectedModelsCommand, - getSelectedModelsCommand, - getSelectionRectsCommand, - retainFirstModelCommand, -} from '@blocksuite/affine-shared/commands'; -import type { BlockCommands } from '@blocksuite/block-std'; - -export const commands: BlockCommands = { - // models - clearAndSelectFirstModel: clearAndSelectFirstModelCommand, - copySelectedModels: copySelectedModelsCommand, - deleteSelectedModels: deleteSelectedModelsCommand, - draftSelectedModels: draftSelectedModelsCommand, - duplicateSelectedModels: duplicateSelectedModelsCommand, - getSelectedModels: getSelectedModelsCommand, - retainFirstModel: retainFirstModelCommand, - // text - ...textCommands, - // peekable - peekSelectedBlock: peekSelectedBlockCommand, - getSelectedPeekableBlocks: getSelectedPeekableBlocksCommand, - // rect - getSelectionRects: getSelectionRectsCommand, -}; diff --git a/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/auto-complete-panel.ts b/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/auto-complete-panel.ts index 64fabef494..68d1f263b5 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/auto-complete-panel.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/auto-complete-panel.ts @@ -1,3 +1,4 @@ +import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text'; import { CanvasElementType, EdgelessCRUDIdentifier, @@ -254,10 +255,13 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { .get(FeatureFlagService) .getFlag('enable_edgeless_text'); if (textFlag) { - const { textId } = this.edgeless.std.command.exec('insertEdgelessText', { - x: bound.x, - y: bound.y, - }); + const [_, { textId }] = this.edgeless.std.command.exec( + insertEdgelessTextCommand, + { + x: bound.x, + y: bound.y, + } + ); if (!textId) return; const textElement = this.crud.getElementById(textId); diff --git a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/link/link-dense-menu.ts b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/link/link-dense-menu.ts index a0247192c5..a55a75838c 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/link/link-dense-menu.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/link/link-dense-menu.ts @@ -1,3 +1,4 @@ +import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark'; import { menu } from '@blocksuite/affine-components/context-menu'; import { LinkIcon } from '@blocksuite/affine-components/icons'; import { TelemetryProvider } from '@blocksuite/affine-shared/services'; @@ -9,8 +10,8 @@ export const buildLinkDenseMenu: DenseMenuBuilder = edgeless => name: 'Link', prefix: LinkIcon, select: () => { - const { insertedLinkType } = edgeless.std.command.exec( - 'insertLinkByQuickSearch' + const [_, { insertedLinkType }] = edgeless.std.command.exec( + insertLinkByQuickSearchCommand ); insertedLinkType diff --git a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/link/link-tool-button.ts b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/link/link-tool-button.ts index a857e567af..aba3320655 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/link/link-tool-button.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/link/link-tool-button.ts @@ -1,3 +1,4 @@ +import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark'; import { LinkIcon } from '@blocksuite/affine-components/icons'; import { TelemetryProvider } from '@blocksuite/affine-shared/services'; import { css, html, LitElement } from 'lit'; @@ -17,8 +18,8 @@ export class EdgelessLinkToolButton extends QuickToolMixin(LitElement) { override type = 'default' as const; private _onClick() { - const { insertedLinkType } = this.edgeless.std.command.exec( - 'insertLinkByQuickSearch' + const [_, { insertedLinkType }] = this.edgeless.std.command.exec( + insertLinkByQuickSearchCommand ); insertedLinkType ?.then(type => { diff --git a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/basket-elements.ts b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/basket-elements.ts index 2cd1de6d43..64aec3d3fb 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/basket-elements.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/basket-elements.ts @@ -1,3 +1,4 @@ +import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text'; import { CanvasElementType } from '@blocksuite/affine-block-surface'; import { type MindmapStyle, TextElementModel } from '@blocksuite/affine-model'; import { @@ -111,10 +112,13 @@ export const textRender: DraggableTool['render'] = ( .getFlag('enable_edgeless_text'); let id: string; if (flag) { - const { textId } = edgeless.std.command.exec('insertEdgelessText', { - x: bound.x, - y: vCenter - h / 2, - }); + const [_, { textId }] = edgeless.std.command.exec( + insertEdgelessTextCommand, + { + x: bound.x, + y: vCenter - h / 2, + } + ); id = textId!; } else { id = service.crud.addElement(CanvasElementType.TEXT, { diff --git a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/note/note-menu.ts b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/note/note-menu.ts index 7b426cd6d6..10a5ebce9a 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/note/note-menu.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/note/note-menu.ts @@ -1,4 +1,5 @@ import { addAttachments } from '@blocksuite/affine-block-attachment'; +import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark'; import { addImages, LoadedImageIcon } from '@blocksuite/affine-block-image'; import { AttachmentIcon, LinkIcon } from '@blocksuite/affine-components/icons'; import { MAX_IMAGE_WIDTH } from '@blocksuite/affine-model'; @@ -65,8 +66,8 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) { } private _onHandleLinkButtonClick() { - const { insertedLinkType } = this.edgeless.service.std.command.exec( - 'insertLinkByQuickSearch' + const [_, { insertedLinkType }] = this.edgeless.service.std.command.exec( + insertLinkByQuickSearchCommand ); insertedLinkType diff --git a/blocksuite/blocks/src/root-block/edgeless/edgeless-keyboard.ts b/blocksuite/blocks/src/root-block/edgeless/edgeless-keyboard.ts index 2e76b78ae2..649ce7c863 100644 --- a/blocksuite/blocks/src/root-block/edgeless/edgeless-keyboard.ts +++ b/blocksuite/blocks/src/root-block/edgeless/edgeless-keyboard.ts @@ -1,3 +1,4 @@ +import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark'; import { EdgelessTextBlockComponent } from '@blocksuite/affine-block-edgeless-text'; import { toast } from '@blocksuite/affine-components/toast'; import { @@ -182,8 +183,8 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager { ) { return; } - const { insertedLinkType } = std.command.exec( - 'insertLinkByQuickSearch' + const [_, { insertedLinkType }] = std.command.exec( + insertLinkByQuickSearchCommand ); insertedLinkType diff --git a/blocksuite/blocks/src/root-block/edgeless/edgeless-root-spec.ts b/blocksuite/blocks/src/root-block/edgeless/edgeless-root-spec.ts index 6dba1bc287..bd200fc1b7 100644 --- a/blocksuite/blocks/src/root-block/edgeless/edgeless-root-spec.ts +++ b/blocksuite/blocks/src/root-block/edgeless/edgeless-root-spec.ts @@ -16,7 +16,6 @@ import { AFFINE_SCROLL_ANCHORING_WIDGET } from '@blocksuite/affine-widget-scroll import { BlockServiceWatcher, BlockViewExtension, - CommandExtension, FlavourExtension, WidgetViewMapExtension, } from '@blocksuite/block-std'; @@ -26,7 +25,6 @@ import { literal, unsafeStatic } from 'lit/static-html.js'; import { ExportManagerExtension } from '../../_common/export-manager/export-manager.js'; import { RootBlockAdapterExtensions } from '../adapters/extension.js'; -import { commands } from '../commands/index.js'; import { AFFINE_EDGELESS_AUTO_CONNECT_WIDGET } from '../widgets/edgeless-auto-connect/edgeless-auto-connect.js'; import { AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET } from '../widgets/edgeless-zoom-toolbar/index.js'; import { EDGELESS_ELEMENT_TOOLBAR_WIDGET } from '../widgets/element-toolbar/index.js'; @@ -93,7 +91,6 @@ const EdgelessCommonExtension: ExtensionType[] = [ DocModeService, ThemeService, EmbedOptionService, - CommandExtension(commands), ExportManagerExtension, ToolController, DNDAPIExtension, diff --git a/blocksuite/blocks/src/root-block/edgeless/gfx-tool/default-tool.ts b/blocksuite/blocks/src/root-block/edgeless/gfx-tool/default-tool.ts index e3abd898b1..edf99099bc 100644 --- a/blocksuite/blocks/src/root-block/edgeless/gfx-tool/default-tool.ts +++ b/blocksuite/blocks/src/root-block/edgeless/gfx-tool/default-tool.ts @@ -1,3 +1,4 @@ +import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text'; import { ConnectorUtils, OverlayIdentifier, @@ -792,7 +793,7 @@ export class DefaultTool extends BaseTool { if (textFlag) { const [x, y] = this.gfx.viewport.toModelCoord(e.x, e.y); - this.std.command.exec('insertEdgelessText', { x, y }); + this.std.command.exec(insertEdgelessTextCommand, { x, y }); } else { addText(this._edgeless, e); } diff --git a/blocksuite/blocks/src/root-block/edgeless/gfx-tool/text-tool.ts b/blocksuite/blocks/src/root-block/edgeless/gfx-tool/text-tool.ts index 9e36b191b3..644b365b3e 100644 --- a/blocksuite/blocks/src/root-block/edgeless/gfx-tool/text-tool.ts +++ b/blocksuite/blocks/src/root-block/edgeless/gfx-tool/text-tool.ts @@ -1,3 +1,4 @@ +import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text'; import type { TextElementModel } from '@blocksuite/affine-model'; import { FeatureFlagService, @@ -47,7 +48,7 @@ export class TextTool extends BaseTool { if (textFlag) { const [x, y] = this.gfx.viewport.toModelCoord(e.x, e.y); - this.gfx.std.command.exec('insertEdgelessText', { x, y }); + this.gfx.std.command.exec(insertEdgelessTextCommand, { x, y }); this.gfx.tool.setTool('default'); } else { addText(this.gfx, e); diff --git a/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts b/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts index 2cf5368274..7e58b59ca7 100644 --- a/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts +++ b/blocksuite/blocks/src/root-block/keyboard/keyboard-manager.ts @@ -5,6 +5,10 @@ import { promptDocTitle, } from '@blocksuite/affine-block-embed'; import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph'; +import { + draftSelectedModelsCommand, + getSelectedModelsCommand, +} from '@blocksuite/affine-shared/commands'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { type BlockComponent, @@ -120,11 +124,11 @@ export class PageKeyboardManager { const rootComponent = this.rootComponent; const [_, ctx] = this.rootComponent.std.command .chain() - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { types: ['block'], mode: 'highest', }) - .draftSelectedModels() + .pipe(draftSelectedModelsCommand) .run(); const selectedModels = ctx.selectedModels?.filter( block => diff --git a/blocksuite/blocks/src/root-block/page/page-root-spec.ts b/blocksuite/blocks/src/root-block/page/page-root-spec.ts index 2484756549..4c71974af6 100644 --- a/blocksuite/blocks/src/root-block/page/page-root-spec.ts +++ b/blocksuite/blocks/src/root-block/page/page-root-spec.ts @@ -11,7 +11,6 @@ import { AFFINE_DOC_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-re import { AFFINE_SCROLL_ANCHORING_WIDGET } from '@blocksuite/affine-widget-scroll-anchoring'; import { BlockViewExtension, - CommandExtension, FlavourExtension, WidgetViewMapExtension, } from '@blocksuite/block-std'; @@ -20,7 +19,6 @@ import { literal, unsafeStatic } from 'lit/static-html.js'; import { ExportManagerExtension } from '../../_common/export-manager/export-manager.js'; import { RootBlockAdapterExtensions } from '../adapters/extension.js'; -import { commands } from '../commands/index.js'; import { AFFINE_EMBED_CARD_TOOLBAR_WIDGET } from '../widgets/embed-card-toolbar/embed-card-toolbar.js'; import { AFFINE_FORMAT_BAR_WIDGET } from '../widgets/format-bar/format-bar.js'; import { AFFINE_INNER_MODAL_WIDGET } from '../widgets/inner-modal/inner-modal.js'; @@ -75,7 +73,6 @@ const PageCommonExtension: ExtensionType[] = [ export const PageRootBlockSpec: ExtensionType[] = [ ...PageCommonExtension, BlockViewExtension('affine:page', literal`affine-page-root`), - CommandExtension(commands), WidgetViewMapExtension('affine:page', pageRootWidgetViewMap), ExportManagerExtension, DNDAPIExtension, diff --git a/blocksuite/blocks/src/root-block/root-service.ts b/blocksuite/blocks/src/root-block/root-service.ts index fbcac2ba81..4f0501134b 100644 --- a/blocksuite/blocks/src/root-block/root-service.ts +++ b/blocksuite/blocks/src/root-block/root-service.ts @@ -1,4 +1,10 @@ import { RootBlockSchema } from '@blocksuite/affine-model'; +import { + getBlockSelectionsCommand, + getImageSelectionsCommand, + getSelectedBlocksCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import type { BlockComponent } from '@blocksuite/block-std'; import { BlockService } from '@blocksuite/block-std'; @@ -23,12 +29,12 @@ export abstract class RootService extends BlockService { this.std.command .chain() .tryAll(chain => [ - chain.getTextSelection(), - chain.getImageSelections(), - chain.getBlockSelections(), + chain.pipe(getTextSelectionCommand), + chain.pipe(getImageSelectionsCommand), + chain.pipe(getBlockSelectionsCommand), ]) - .getSelectedBlocks() - .inline(({ selectedBlocks }) => { + .pipe(getSelectedBlocksCommand) + .pipe(({ selectedBlocks }) => { if (!selectedBlocks) return; result = selectedBlocks; }) diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/align-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/align-button.ts index 674a68f775..068b25944a 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/align-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/align-button.ts @@ -1,4 +1,6 @@ import { + autoArrangeElementsCommand, + autoResizeElementsCommand, EdgelessCRUDIdentifier, updateXYWH, } from '@blocksuite/affine-block-surface'; @@ -134,10 +136,10 @@ export class EdgelessAlignButton extends WithDisposable(LitElement) { this._alignDistributeVertically(); break; case Alignment.AutoArrange: - this.edgeless.std.command.exec('autoArrangeElements'); + this.edgeless.std.command.exec(autoArrangeElementsCommand); break; case Alignment.AutoResize: - this.edgeless.std.command.exec('autoResizeElements'); + this.edgeless.std.command.exec(autoResizeElementsCommand); break; } } diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-embed-card-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-embed-card-button.ts index 7f280c27b8..25d6e71e52 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-embed-card-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-embed-card-button.ts @@ -10,7 +10,10 @@ import { getDocContentWithMaxLength, getEmbedCardIcons, } from '@blocksuite/affine-block-embed'; -import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; +import { + EdgelessCRUDIdentifier, + reassociateConnectorsCommand, +} from '@blocksuite/affine-block-surface'; import { CaptionIcon, CopyIcon, @@ -164,7 +167,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { this.edgeless.surface.model ); - this.std.command.exec('reassociateConnectors', { + this.std.command.exec(reassociateConnectorsCommand, { oldId: id, newId, }); @@ -220,7 +223,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { ); if (!newId) return; - this.std.command.exec('reassociateConnectors', { + this.std.command.exec(reassociateConnectorsCommand, { oldId: id, newId, }); diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts index 91127d4290..6708abface 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts @@ -1,3 +1,4 @@ +import { changeNoteDisplayMode } from '@blocksuite/affine-block-note'; import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import type { EdgelessColorPickerButton, @@ -195,7 +196,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { } private _setDisplayMode(note: NoteBlockModel, newMode: NoteDisplayMode) { - this.edgeless.std.command.exec('changeNoteDisplayMode', { + this.edgeless.std.command.exec(changeNoteDisplayMode, { noteId: note.id, mode: newMode, stopCapture: true, diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/context.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/context.ts index 1f6855ce31..acafba5ef0 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/context.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/context.ts @@ -1,5 +1,6 @@ import type { SurfaceBlockComponent } from '@blocksuite/affine-block-surface'; import { MenuContext } from '@blocksuite/affine-components/toolbar'; +import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands'; import { GfxPrimitiveElementModel, type GfxSelectionManager, @@ -47,10 +48,9 @@ export class ElementToolbarMoreMenuContext extends MenuContext { } get selectedBlockModels() { - const [result, { selectedModels }] = this.std.command - .chain() - .getSelectedModels() - .run(); + const [result, { selectedModels }] = this.std.command.exec( + getSelectedModelsCommand + ); if (!result) return []; diff --git a/blocksuite/blocks/src/root-block/widgets/format-bar/components/highlight/highlight-button.ts b/blocksuite/blocks/src/root-block/widgets/format-bar/components/highlight/highlight-button.ts index 7ff3aa40db..d1ddabe6ce 100644 --- a/blocksuite/blocks/src/root-block/widgets/format-bar/components/highlight/highlight-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/format-bar/components/highlight/highlight-button.ts @@ -5,6 +5,15 @@ import { TextBackgroundDuotoneIcon, TextForegroundDuotoneIcon, } from '@blocksuite/affine-components/icons'; +import { + formatBlockCommand, + formatNativeCommand, + formatTextCommand, +} from '@blocksuite/affine-components/rich-text'; +import { + getBlockSelectionsCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import type { EditorHost } from '@blocksuite/block-std'; import { assertExists } from '@blocksuite/global/utils'; @@ -41,9 +50,9 @@ const updateHighlight = ( host.std.command .chain() .try(chain => [ - chain.getTextSelection().formatText(payload), - chain.getBlockSelections().formatBlock(payload), - chain.formatNative(payload), + chain.pipe(getTextSelectionCommand).pipe(formatTextCommand, payload), + chain.pipe(getBlockSelectionsCommand).pipe(formatBlockCommand, payload), + chain.pipe(formatNativeCommand, payload), ]) .run(); }; diff --git a/blocksuite/blocks/src/root-block/widgets/format-bar/config.ts b/blocksuite/blocks/src/root-block/widgets/format-bar/config.ts index 08c7d73a74..106cea52e3 100644 --- a/blocksuite/blocks/src/root-block/widgets/format-bar/config.ts +++ b/blocksuite/blocks/src/root-block/widgets/format-bar/config.ts @@ -33,13 +33,33 @@ import { TextIcon, UnderlineIcon, } from '@blocksuite/affine-components/icons'; +import { + deleteTextCommand, + toggleBold, + toggleCode, + toggleItalic, + toggleLink, + toggleStrike, + toggleUnderline, +} from '@blocksuite/affine-components/rich-text'; import { toast } from '@blocksuite/affine-components/toast'; import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar'; import { renderGroups } from '@blocksuite/affine-components/toolbar'; +import { + copySelectedModelsCommand, + deleteSelectedModelsCommand, + draftSelectedModelsCommand, + getBlockIndexCommand, + getBlockSelectionsCommand, + getImageSelectionsCommand, + getSelectedBlocksCommand, + getSelectedModelsCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import { TelemetryProvider } from '@blocksuite/affine-shared/services'; import type { + BlockComponent, Chain, - CommandKeyToData, InitCommandCtx, } from '@blocksuite/block-std'; import { tableViewMeta } from '@blocksuite/data-view/view-presets'; @@ -109,32 +129,32 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) { .addDivider() .addTextStyleToggle({ key: 'bold', - action: chain => chain.toggleBold().run(), + action: chain => chain.pipe(toggleBold).run(), icon: BoldIcon, }) .addTextStyleToggle({ key: 'italic', - action: chain => chain.toggleItalic().run(), + action: chain => chain.pipe(toggleItalic).run(), icon: ItalicIcon, }) .addTextStyleToggle({ key: 'underline', - action: chain => chain.toggleUnderline().run(), + action: chain => chain.pipe(toggleUnderline).run(), icon: UnderlineIcon, }) .addTextStyleToggle({ key: 'strike', - action: chain => chain.toggleStrike().run(), + action: chain => chain.pipe(toggleStrike).run(), icon: StrikethroughIcon, }) .addTextStyleToggle({ key: 'code', - action: chain => chain.toggleCode().run(), + action: chain => chain.pipe(toggleCode).run(), icon: CodeIcon, }) .addTextStyleToggle({ key: 'link', - action: chain => chain.toggleLink().run(), + action: chain => chain.pipe(toggleLink).run(), icon: LinkIcon, }) .addDivider() @@ -151,7 +171,7 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) { showWhen: chain => { const middleware = (count = 0) => { return ( - ctx: CommandKeyToData<'selectedBlocks'>, + ctx: { selectedBlocks: BlockComponent[] }, next: () => void ) => { const { selectedBlocks } = ctx; @@ -166,24 +186,24 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) { }; }; let [result] = chain - .getTextSelection() - .getSelectedBlocks({ + .pipe(getTextSelectionCommand) + .pipe(getSelectedBlocksCommand, { types: ['text'], }) - .inline(middleware(1)) + .pipe(middleware(1)) .run(); if (result) return true; [result] = chain .tryAll(chain => [ - chain.getBlockSelections(), - chain.getImageSelections(), + chain.pipe(getBlockSelectionsCommand), + chain.pipe(getImageSelectionsCommand), ]) - .getSelectedBlocks({ + .pipe(getSelectedBlocksCommand, { types: ['block', 'image'], }) - .inline(middleware(0)) + .pipe(middleware(0)) .run(); return result; @@ -197,11 +217,11 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) { isActive: () => false, action: (chain, formatBar) => { const [_, ctx] = chain - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { types: ['block', 'text'], mode: 'flat', }) - .draftSelectedModels() + .pipe(draftSelectedModelsCommand) .run(); const { draftedModels, selectedModels, std } = ctx; if (!selectedModels?.length || !draftedModels) return; @@ -238,7 +258,7 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) { }, showWhen: chain => { const [_, ctx] = chain - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { types: ['block', 'text'], mode: 'highest', }) @@ -332,14 +352,14 @@ export const BUILT_IN_GROUPS: MenuItemGroup[] = [ action: c => { c.std.command .chain() - .getSelectedModels() + .pipe(getSelectedModelsCommand) .with({ onCopy: () => { toast(c.host, 'Copied to clipboard'); }, }) - .draftSelectedModels() - .copySelectedModels() + .pipe(draftSelectedModelsCommand) + .pipe(copySelectedModelsCommand) .run(); }, }, @@ -352,35 +372,27 @@ export const BUILT_IN_GROUPS: MenuItemGroup[] = [ c.doc.captureSync(); c.std.command .chain() - .try(cmd => [ - cmd - .getTextSelection() - .inline<'currentSelectionPath'>((ctx, next) => { - const textSelection = ctx.currentTextSelection; - assertExists(textSelection); - const end = textSelection.to ?? textSelection.from; - next({ currentSelectionPath: end.blockId }); - }), - cmd - .getBlockSelections() - .inline<'currentSelectionPath'>((ctx, next) => { - const currentBlockSelections = ctx.currentBlockSelections; - assertExists(currentBlockSelections); - const blockSelection = currentBlockSelections.at(-1); - if (!blockSelection) { - return; - } - next({ currentSelectionPath: blockSelection.blockId }); - }), + .try<{ currentSelectionPath: string }>(cmd => [ + cmd.pipe(getTextSelectionCommand).pipe((ctx, next) => { + const textSelection = ctx.currentTextSelection; + assertExists(textSelection); + const end = textSelection.to ?? textSelection.from; + next({ currentSelectionPath: end.blockId }); + }), + cmd.pipe(getBlockSelectionsCommand).pipe((ctx, next) => { + const currentBlockSelections = ctx.currentBlockSelections; + assertExists(currentBlockSelections); + const blockSelection = currentBlockSelections.at(-1); + if (!blockSelection) { + return; + } + next({ currentSelectionPath: blockSelection.blockId }); + }), ]) - .getBlockIndex() - .getSelectedModels() - .draftSelectedModels() - .inline((ctx, next) => { - if (!ctx.draftedModels) { - return next(); - } - + .pipe(getBlockIndexCommand) + .pipe(getSelectedModelsCommand) + .pipe(draftSelectedModelsCommand) + .pipe((ctx, next) => { ctx.draftedModels .then(models => { const slice = Slice.fromModels(ctx.std.store, models); @@ -412,8 +424,8 @@ export const BUILT_IN_GROUPS: MenuItemGroup[] = [ // remove text const [result] = c.std.command .chain() - .getTextSelection() - .deleteText() + .pipe(getTextSelectionCommand) + .pipe(deleteTextCommand) .run(); if (result) { @@ -424,11 +436,11 @@ export const BUILT_IN_GROUPS: MenuItemGroup[] = [ c.std.command .chain() .tryAll(chain => [ - chain.getBlockSelections(), - chain.getImageSelections(), + chain.pipe(getBlockSelectionsCommand), + chain.pipe(getImageSelectionsCommand), ]) - .getSelectedModels() - .deleteSelectedModels() + .pipe(getSelectedModelsCommand) + .pipe(deleteSelectedModelsCommand) .run(); c.toolbar.reset(); diff --git a/blocksuite/blocks/src/root-block/widgets/format-bar/context.ts b/blocksuite/blocks/src/root-block/widgets/format-bar/context.ts index 3d421278a6..f58ceded45 100644 --- a/blocksuite/blocks/src/root-block/widgets/format-bar/context.ts +++ b/blocksuite/blocks/src/root-block/widgets/format-bar/context.ts @@ -1,4 +1,10 @@ import { MenuContext } from '@blocksuite/affine-components/toolbar'; +import { + getBlockSelectionsCommand, + getImageSelectionsCommand, + getSelectedModelsCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import type { AffineFormatBarWidget } from './format-bar.js'; @@ -15,11 +21,11 @@ export class FormatBarContext extends MenuContext { const [success, result] = this.std.command .chain() .tryAll(chain => [ - chain.getTextSelection(), - chain.getBlockSelections(), - chain.getImageSelections(), + chain.pipe(getTextSelectionCommand), + chain.pipe(getBlockSelectionsCommand), + chain.pipe(getImageSelectionsCommand), ]) - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { mode: 'highest', }) .run(); diff --git a/blocksuite/blocks/src/root-block/widgets/format-bar/format-bar.ts b/blocksuite/blocks/src/root-block/widgets/format-bar/format-bar.ts index a972487ba9..f4aba14528 100644 --- a/blocksuite/blocks/src/root-block/widgets/format-bar/format-bar.ts +++ b/blocksuite/blocks/src/root-block/widgets/format-bar/format-bar.ts @@ -1,6 +1,8 @@ +import { updateBlockType } from '@blocksuite/affine-block-note'; import { HoverController } from '@blocksuite/affine-components/hover'; import { isFormatSupported, + isTextStyleActive, type RichText, } from '@blocksuite/affine-components/rich-text'; import { @@ -8,6 +10,10 @@ import { getMoreMenuConfig, type MenuItemGroup, } from '@blocksuite/affine-components/toolbar'; +import { + getSelectedBlocksCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { @@ -167,11 +173,11 @@ export class AffineFormatBarWidget extends WidgetComponent { if (!rootComponent.std.range) return; this.host.std.command .chain() - .getTextSelection() - .getSelectedBlocks({ + .pipe(getTextSelectionCommand) + .pipe(getSelectedBlocksCommand, { types: ['text'], }) - .inline(ctx => { + .pipe(ctx => { const { selectedBlocks } = ctx; if (!selectedBlocks) return; this._selectedBlocks = selectedBlocks; @@ -422,7 +428,7 @@ export class AffineFormatBarWidget extends WidgetComponent { name: config.name ?? camelCaseToWords(type ?? flavour), action: chain => { chain - .updateBlockType({ + .pipe(updateBlockType, { flavour, props: type != null ? { type } : undefined, }) @@ -479,7 +485,7 @@ export class AffineFormatBarWidget extends WidgetComponent { name: camelCaseToWords(key), icon: config.icon, isActive: chain => { - const [result] = chain.isTextStyleActive({ key }).run(); + const [result] = chain.pipe(isTextStyleActive, { key }).run(); return result; }, action: config.action, diff --git a/blocksuite/blocks/src/root-block/widgets/keyboard-toolbar/config.ts b/blocksuite/blocks/src/root-block/widgets/keyboard-toolbar/config.ts index 08dfc2e344..1423f9d4b7 100644 --- a/blocksuite/blocks/src/root-block/widgets/keyboard-toolbar/config.ts +++ b/blocksuite/blocks/src/root-block/widgets/keyboard-toolbar/config.ts @@ -1,12 +1,49 @@ import { addSiblingAttachmentBlocks } from '@blocksuite/affine-block-attachment'; import { toggleEmbedCardCreateModal } from '@blocksuite/affine-block-bookmark'; -import { getSurfaceBlock } from '@blocksuite/affine-block-surface'; +import { insertDatabaseBlockCommand } from '@blocksuite/affine-block-database'; +import { insertImagesCommand } from '@blocksuite/affine-block-image'; +import { insertLatexBlockCommand } from '@blocksuite/affine-block-latex'; import { + canDedentListCommand, + canIndentListCommand, + dedentListCommand, + indentListCommand, +} from '@blocksuite/affine-block-list'; +import { updateBlockType } from '@blocksuite/affine-block-note'; +import { + canDedentParagraphCommand, + canIndentParagraphCommand, + dedentParagraphCommand, + indentParagraphCommand, +} from '@blocksuite/affine-block-paragraph'; +import { getSurfaceBlock } from '@blocksuite/affine-block-surface'; +import { insertSurfaceRefBlockCommand } from '@blocksuite/affine-block-surface-ref'; +import { + formatBlockCommand, + formatNativeCommand, + formatTextCommand, getInlineEditorByModel, + getTextStyle, insertContent, + insertInlineLatex, + toggleBold, + toggleCode, + toggleItalic, + toggleLink, + toggleStrike, + toggleUnderline, } from '@blocksuite/affine-components/rich-text'; import { toast } from '@blocksuite/affine-components/toast'; import type { FrameBlockModel } from '@blocksuite/affine-model'; +import { + copySelectedModelsCommand, + deleteSelectedModelsCommand, + draftSelectedModelsCommand, + duplicateSelectedModelsCommand, + getBlockSelectionsCommand, + getSelectedModelsCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import { @@ -157,7 +194,7 @@ const textToolActionItems: KeyboardToolbarActionItem[] = [ showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:paragraph'), action: ({ std }) => { - std.command.exec('updateBlockType', { + std.command.exec(updateBlockType, { flavour: 'affine:paragraph', props: { type: 'text' }, }); @@ -169,7 +206,7 @@ const textToolActionItems: KeyboardToolbarActionItem[] = [ showWhen: ({ std }: KeyboardToolbarContext) => std.store.schema.flavourSchemaMap.has('affine:paragraph'), action: ({ std }: KeyboardToolbarContext) => { - std.command.exec('updateBlockType', { + std.command.exec(updateBlockType, { flavour: 'affine:paragraph', props: { type: `h${i}` }, }); @@ -180,7 +217,7 @@ const textToolActionItems: KeyboardToolbarActionItem[] = [ showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:code'), icon: CodeBlockIcon(), action: ({ std }) => { - std.command.exec('updateBlockType', { + std.command.exec(updateBlockType, { flavour: 'affine:code', }); }, @@ -191,7 +228,7 @@ const textToolActionItems: KeyboardToolbarActionItem[] = [ std.store.schema.flavourSchemaMap.has('affine:paragraph'), icon: QuoteIcon(), action: ({ std }) => { - std.command.exec('updateBlockType', { + std.command.exec(updateBlockType, { flavour: 'affine:paragraph', props: { type: 'quote' }, }); @@ -203,7 +240,7 @@ const textToolActionItems: KeyboardToolbarActionItem[] = [ showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:divider'), action: ({ std }) => { - std.command.exec('updateBlockType', { + std.command.exec(updateBlockType, { flavour: 'affine:divider', props: { type: 'divider' }, }); @@ -215,7 +252,11 @@ const textToolActionItems: KeyboardToolbarActionItem[] = [ showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:paragraph'), action: ({ std }) => { - std.command.chain().getTextSelection().insertInlineLatex().run(); + std.command + .chain() + .pipe(getTextSelectionCommand) + .pipe(insertInlineLatex) + .run(); }, }, ]; @@ -226,7 +267,7 @@ const listToolActionItems: KeyboardToolbarActionItem[] = [ icon: BulletedListIcon(), showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:list'), action: ({ std }) => { - std.command.exec('updateBlockType', { + std.command.exec(updateBlockType, { flavour: 'affine:list', props: { type: 'bulleted', @@ -239,7 +280,7 @@ const listToolActionItems: KeyboardToolbarActionItem[] = [ icon: NumberedListIcon(), showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:list'), action: ({ std }) => { - std.command.exec('updateBlockType', { + std.command.exec(updateBlockType, { flavour: 'affine:list', props: { type: 'numbered', @@ -252,7 +293,7 @@ const listToolActionItems: KeyboardToolbarActionItem[] = [ icon: CheckBoxCheckLinearIcon(), showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:list'), action: ({ std }) => { - std.command.exec('updateBlockType', { + std.command.exec(updateBlockType, { flavour: 'affine:list', props: { type: 'todo', @@ -273,8 +314,8 @@ const pageToolGroup: KeyboardToolPanelGroup = { action: ({ std }) => { std.command .chain() - .getSelectedModels() - .inline(({ selectedModels }) => { + .pipe(getSelectedModelsCommand) + .pipe(({ selectedModels }) => { const newDoc = createDefaultDoc(std.store.workspace); if (!selectedModels?.length) return; insertContent(std.host, selectedModels[0], REFERENCE_NODE, { @@ -313,8 +354,8 @@ const pageToolGroup: KeyboardToolPanelGroup = { std.command .chain() - .getSelectedModels() - .inline(ctx => { + .pipe(getSelectedModelsCommand) + .pipe(ctx => { const { selectedModels } = ctx; if (!selectedModels?.length) return; @@ -348,8 +389,8 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = { action: ({ std }) => { std.command .chain() - .getSelectedModels() - .insertImages({ removeEmptyLine: true }) + .pipe(getSelectedModelsCommand) + .pipe(insertImagesCommand, { removeEmptyLine: true }) .run(); }, }, @@ -359,7 +400,9 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = { showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:bookmark'), action: async ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -384,7 +427,9 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = { showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:attachment'), action: async ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -409,7 +454,9 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = { showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:embed-youtube'), action: async ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -434,7 +481,9 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = { showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:embed-github'), action: async ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -459,7 +508,9 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = { showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:embed-figma'), action: async ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -485,7 +536,9 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = { showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:embed-loom'), action: async ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -512,8 +565,8 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = { action: ({ std }) => { std.command .chain() - .getSelectedModels() - .insertLatexBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertLatexBlockCommand, { place: 'after', removeEmptyLine: true, }) @@ -538,8 +591,8 @@ const documentGroupFrameToolGroup: DynamicKeyboardToolPanelGroup = ({ action: ({ std }) => { std.command .chain() - .getSelectedModels() - .insertSurfaceRefBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertSurfaceRefBlockCommand, { reference: frameModel.id, place: 'after', removeEmptyLine: true, @@ -560,8 +613,8 @@ const documentGroupFrameToolGroup: DynamicKeyboardToolPanelGroup = ({ action: ({ std }) => { std.command .chain() - .getSelectedModels() - .insertSurfaceRefBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertSurfaceRefBlockCommand, { reference: group.id, place: 'after', removeEmptyLine: true, @@ -587,7 +640,9 @@ const dateToolGroup: KeyboardToolPanelGroup = { name: 'Today', icon: TodayIcon(), action: ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -598,7 +653,9 @@ const dateToolGroup: KeyboardToolPanelGroup = { name: 'Tomorrow', icon: TomorrowIcon(), action: ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -611,7 +668,9 @@ const dateToolGroup: KeyboardToolPanelGroup = { name: 'Yesterday', icon: YesterdayIcon(), action: ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -624,7 +683,9 @@ const dateToolGroup: KeyboardToolPanelGroup = { name: 'Now', icon: NowIcon(), action: ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -645,8 +706,8 @@ const databaseToolGroup: KeyboardToolPanelGroup = { action: ({ std }) => { std.command .chain() - .getSelectedModels() - .insertDatabaseBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertDatabaseBlockCommand, { viewType: viewPresets.tableViewMeta.type, place: 'after', removeEmptyLine: true, @@ -662,8 +723,8 @@ const databaseToolGroup: KeyboardToolPanelGroup = { action: ({ std }) => { std.command .chain() - .getSelectedModels() - .insertDatabaseBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertDatabaseBlockCommand, { viewType: viewPresets.kanbanViewMeta.type, place: 'after', removeEmptyLine: true, @@ -706,73 +767,73 @@ const textStyleToolItems: KeyboardToolbarItem[] = [ name: 'Bold', icon: BoldIcon(), background: ({ std }) => { - const { textStyle } = std.command.exec('getTextStyle'); + const [_, { textStyle }] = std.command.exec(getTextStyle); return textStyle?.bold ? '#00000012' : ''; }, action: ({ std }) => { - std.command.exec('toggleBold'); + std.command.exec(toggleBold); }, }, { name: 'Italic', icon: ItalicIcon(), background: ({ std }) => { - const { textStyle } = std.command.exec('getTextStyle'); + const [_, { textStyle }] = std.command.exec(getTextStyle); return textStyle?.italic ? '#00000012' : ''; }, action: ({ std }) => { - std.command.exec('toggleItalic'); + std.command.exec(toggleItalic); }, }, { name: 'UnderLine', icon: UnderLineIcon(), background: ({ std }) => { - const { textStyle } = std.command.exec('getTextStyle'); + const [_, { textStyle }] = std.command.exec(getTextStyle); return textStyle?.underline ? '#00000012' : ''; }, action: ({ std }) => { - std.command.exec('toggleUnderline'); + std.command.exec(toggleUnderline); }, }, { name: 'StrikeThrough', icon: StrikeThroughIcon(), background: ({ std }) => { - const { textStyle } = std.command.exec('getTextStyle'); + const [_, { textStyle }] = std.command.exec(getTextStyle); return textStyle?.strike ? '#00000012' : ''; }, action: ({ std }) => { - std.command.exec('toggleStrike'); + std.command.exec(toggleStrike); }, }, { name: 'Code', icon: CodeIcon(), background: ({ std }) => { - const { textStyle } = std.command.exec('getTextStyle'); + const [_, { textStyle }] = std.command.exec(getTextStyle); return textStyle?.code ? '#00000012' : ''; }, action: ({ std }) => { - std.command.exec('toggleCode'); + std.command.exec(toggleCode); }, }, { name: 'Link', icon: LinkIcon(), background: ({ std }) => { - const { textStyle } = std.command.exec('getTextStyle'); + const [_, { textStyle }] = std.command.exec(getTextStyle); return textStyle?.link ? '#00000012' : ''; }, action: ({ std }) => { - std.command.exec('toggleLink'); + std.command.exec(toggleLink); }, }, ]; const highlightToolPanel: KeyboardToolPanelConfig = { icon: ({ std }) => { - const { textStyle } = std.command.exec('getTextStyle'); + const [_, { textStyle }] = std.command.exec(getTextStyle); if (textStyle?.color) { return HighLightDuotoneIcon(textStyle.color); } else { @@ -810,9 +871,13 @@ const highlightToolPanel: KeyboardToolPanelConfig = { std.command .chain() .try(chain => [ - chain.getTextSelection().formatText(payload), - chain.getBlockSelections().formatBlock(payload), - chain.formatNative(payload), + chain + .pipe(getTextSelectionCommand) + .pipe(formatTextCommand, payload), + chain + .pipe(getBlockSelectionsCommand) + .pipe(formatBlockCommand, payload), + chain.pipe(formatNativeCommand, payload), ]) .run(); }, @@ -851,9 +916,13 @@ const highlightToolPanel: KeyboardToolPanelConfig = { std.command .chain() .try(chain => [ - chain.getTextSelection().formatText(payload), - chain.getBlockSelections().formatBlock(payload), - chain.formatNative(payload), + chain + .pipe(getTextSelectionCommand) + .pipe(formatTextCommand, payload), + chain + .pipe(getBlockSelectionsCommand) + .pipe(formatBlockCommand, payload), + chain.pipe(formatNativeCommand, payload), ]) .run(); }, @@ -872,15 +941,20 @@ const textSubToolbarConfig: KeyboardSubToolbarConfig = { name: 'InlineTex', icon: TeXIcon(), action: ({ std }) => { - std.command.chain().getTextSelection().insertInlineLatex().run(); + std.command + .chain() + .pipe(getTextSelectionCommand) + .pipe(insertInlineLatex) + .run(); }, }, highlightToolPanel, ], autoShow: ({ std }) => { return computed(() => { - const selection = - std.command.exec('getTextSelection').currentTextSelection; + const [_, { currentTextSelection: selection }] = std.command.exec( + getTextSelectionCommand + ); return selection ? !selection.isCollapsed() : false; }); }, @@ -900,8 +974,8 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = { action: ({ std }) => { std.command .chain() - .getSelectedModels() - .insertImages({ removeEmptyLine: true }) + .pipe(getSelectedModelsCommand) + .pipe(insertImagesCommand, { removeEmptyLine: true }) .run(); }, }, @@ -911,7 +985,9 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = { showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:attachment'), action: async ({ std }) => { - const { selectedModels } = std.command.exec('getSelectedModels'); + const [_, { selectedModels }] = std.command.exec( + getSelectedModelsCommand + ); const model = selectedModels?.[0]; if (!model) return; @@ -950,7 +1026,10 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = { disableWhen: ({ std }) => { const [success] = std.command .chain() - .tryAll(chain => [chain.canIndentParagraph(), chain.canIndentList()]) + .tryAll(chain => [ + chain.pipe(canIndentParagraphCommand), + chain.pipe(canIndentListCommand), + ]) .run(); return !success; }, @@ -958,8 +1037,8 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = { std.command .chain() .tryAll(chain => [ - chain.canIndentParagraph().indentParagraph(), - chain.canIndentList().indentList(), + chain.pipe(canIndentParagraphCommand).pipe(indentParagraphCommand), + chain.pipe(canIndentListCommand).pipe(indentListCommand), ]) .run(); }, @@ -972,7 +1051,10 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = { disableWhen: ({ std }) => { const [success] = std.command .chain() - .tryAll(chain => [chain.canDedentParagraph(), chain.canDedentList()]) + .tryAll(chain => [ + chain.pipe(canDedentParagraphCommand), + chain.pipe(canDedentListCommand), + ]) .run(); return !success; }, @@ -980,8 +1062,8 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = { std.command .chain() .tryAll(chain => [ - chain.canDedentParagraph().dedentParagraph(), - chain.canDedentList().dedentList(), + chain.pipe(canDedentParagraphCommand).pipe(dedentParagraphCommand), + chain.pipe(canDedentListCommand).pipe(dedentListCommand), ]) .run(); }, @@ -992,14 +1074,14 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = { action: ({ std }) => { std.command .chain() - .getSelectedModels() + .pipe(getSelectedModelsCommand) .with({ onCopy: () => { toast(std.host, 'Copied to clipboard'); }, }) - .draftSelectedModels() - .copySelectedModels() + .pipe(draftSelectedModelsCommand) + .pipe(copySelectedModelsCommand) .run(); }, }, @@ -1009,9 +1091,9 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = { action: ({ std }) => { std.command .chain() - .getSelectedModels() - .draftSelectedModels() - .duplicateSelectedModels() + .pipe(getSelectedModelsCommand) + .pipe(draftSelectedModelsCommand) + .pipe(duplicateSelectedModelsCommand) .run(); }, }, @@ -1019,7 +1101,11 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = { name: 'Delete', icon: DeleteIcon(), action: ({ std }) => { - std.command.chain().getSelectedModels().deleteSelectedModels().run(); + std.command + .chain() + .pipe(getSelectedModelsCommand) + .pipe(deleteSelectedModelsCommand) + .run(); }, }, ], diff --git a/blocksuite/blocks/src/root-block/widgets/keyboard-toolbar/keyboard-toolbar.ts b/blocksuite/blocks/src/root-block/widgets/keyboard-toolbar/keyboard-toolbar.ts index dda5337e21..7cb91a6597 100644 --- a/blocksuite/blocks/src/root-block/widgets/keyboard-toolbar/keyboard-toolbar.ts +++ b/blocksuite/blocks/src/root-block/widgets/keyboard-toolbar/keyboard-toolbar.ts @@ -2,6 +2,7 @@ import { VirtualKeyboardController, type VirtualKeyboardControllerConfig, } from '@blocksuite/affine-components/virtual-keyboard'; +import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands'; import { PropTypes, requiredProperties, @@ -94,8 +95,8 @@ export class AffineKeyboardToolbar extends SignalWatcher( const { std } = this.rootComponent; std.command .chain() - .getSelectedModels() - .inline(({ selectedModels }) => { + .pipe(getSelectedModelsCommand) + .pipe(({ selectedModels }) => { if (!selectedModels?.length) return; const block = std.view.getBlock(selectedModels[0].id); diff --git a/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts b/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts index c23dc33e40..c03ebf99b6 100644 --- a/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts +++ b/blocksuite/blocks/src/root-block/widgets/slash-menu/config.ts @@ -1,13 +1,18 @@ import { addSiblingAttachmentBlocks } from '@blocksuite/affine-block-attachment'; import { toggleEmbedCardCreateModal } from '@blocksuite/affine-block-bookmark'; import type { DataViewBlockComponent } from '@blocksuite/affine-block-data-view'; +import { insertDatabaseBlockCommand } from '@blocksuite/affine-block-database'; import { FigmaIcon, GithubIcon, LoomIcon, YoutubeIcon, } from '@blocksuite/affine-block-embed'; +import { insertImagesCommand } from '@blocksuite/affine-block-image'; +import { insertLatexBlockCommand } from '@blocksuite/affine-block-latex'; import { getSurfaceBlock } from '@blocksuite/affine-block-surface'; +import { insertSurfaceRefBlockCommand } from '@blocksuite/affine-block-surface-ref'; +import { insertTableBlockCommand } from '@blocksuite/affine-block-table'; import { ArrowDownBigIcon, ArrowUpBigIcon, @@ -30,6 +35,7 @@ import { import { getInlineEditorByModel, insertContent, + insertInlineLatex, textConversionConfigs, textFormatConfigs, } from '@blocksuite/affine-components/rich-text'; @@ -38,6 +44,10 @@ import type { FrameBlockModel, ParagraphBlockModel, } from '@blocksuite/affine-model'; +import { + getSelectedModelsCommand, + getTextSelectionCommand, +} from '@blocksuite/affine-shared/commands'; import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts'; import { FeatureFlagService, @@ -165,8 +175,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = { action: ({ rootComponent }) => { rootComponent.std.command .chain() - .getTextSelection() - .insertInlineLatex() + .pipe(getTextSelectionCommand) + .pipe(insertInlineLatex) .run(); }, }, @@ -255,8 +265,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = { action: ({ rootComponent }) => { rootComponent.std.command .chain() - .getSelectedModels() - .insertTableBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertTableBlockCommand, { place: 'after', removeEmptyLine: true, }) @@ -273,8 +283,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = { action: async ({ rootComponent }) => { const [success, ctx] = rootComponent.std.command .chain() - .getSelectedModels() - .insertImages({ removeEmptyLine: true }) + .pipe(getSelectedModelsCommand) + .pipe(insertImagesCommand, { removeEmptyLine: true }) .run(); if (success) await ctx.insertedImageIds; @@ -429,8 +439,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = { action: ({ rootComponent }) => { rootComponent.std.command .chain() - .getSelectedModels() - .insertLatexBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertLatexBlockCommand, { place: 'after', removeEmptyLine: true, }) @@ -460,8 +470,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = { action: ({ rootComponent }) => { rootComponent.std.command .chain() - .getSelectedModels() - .insertSurfaceRefBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertSurfaceRefBlockCommand, { reference: frameModel.id, place: 'after', removeEmptyLine: true, @@ -477,8 +487,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = { action: () => { rootComponent.std.command .chain() - .getSelectedModels() - .insertSurfaceRefBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertSurfaceRefBlockCommand, { reference: group.id, place: 'after', removeEmptyLine: true, @@ -568,13 +578,13 @@ export const defaultSlashMenuConfig: SlashMenuConfig = { action: ({ rootComponent }) => { rootComponent.std.command .chain() - .getSelectedModels() - .insertDatabaseBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertDatabaseBlockCommand, { viewType: viewPresets.tableViewMeta.type, place: 'after', removeEmptyLine: true, }) - .inline(({ insertedDatabaseBlockId }) => { + .pipe(({ insertedDatabaseBlockId }) => { if (insertedDatabaseBlockId) { const telemetry = rootComponent.std.getOptional(TelemetryProvider); @@ -631,13 +641,13 @@ export const defaultSlashMenuConfig: SlashMenuConfig = { action: ({ rootComponent }) => { rootComponent.std.command .chain() - .getSelectedModels() - .insertDatabaseBlock({ + .pipe(getSelectedModelsCommand) + .pipe(insertDatabaseBlockCommand, { viewType: viewPresets.kanbanViewMeta.type, place: 'after', removeEmptyLine: true, }) - .inline(({ insertedDatabaseBlockId }) => { + .pipe(({ insertedDatabaseBlockId }) => { if (insertedDatabaseBlockId) { const telemetry = rootComponent.std.getOptional(TelemetryProvider); diff --git a/blocksuite/blocks/src/root-block/widgets/slash-menu/utils.ts b/blocksuite/blocks/src/root-block/widgets/slash-menu/utils.ts index 3170cc27b0..76d1791d5e 100644 --- a/blocksuite/blocks/src/root-block/widgets/slash-menu/utils.ts +++ b/blocksuite/blocks/src/root-block/widgets/slash-menu/utils.ts @@ -1,6 +1,8 @@ -import type { - TextConversionConfig, - TextFormatConfig, +import { updateBlockType } from '@blocksuite/affine-block-note'; +import { + formatBlockCommand, + type TextConversionConfig, + type TextFormatConfig, } from '@blocksuite/affine-components/rich-text'; import { isInsideBlockByFlavour } from '@blocksuite/affine-shared/utils'; import { BlockSelection } from '@blocksuite/block-std'; @@ -102,13 +104,10 @@ export function createConversionItem( tooltip: slashMenuToolTips[name], showWhen: ({ model }) => model.doc.schema.flavourSchemaMap.has(flavour), action: ({ rootComponent }) => { - rootComponent.std.command - .chain() - .updateBlockType({ - flavour, - props: { type }, - }) - .run(); + rootComponent.std.command.exec(updateBlockType, { + flavour, + props: { type }, + }); }, }; } @@ -125,17 +124,14 @@ export function createTextFormatItem( const { std, host } = rootComponent; if (model.text?.length !== 0) { - std.command - .chain() - .formatBlock({ - blockSelections: [ - std.selection.create(BlockSelection, { - blockId: model.id, - }), - ], - styles: { [id]: true }, - }) - .run(); + std.command.exec(formatBlockCommand, { + blockSelections: [ + std.selection.create(BlockSelection, { + blockId: model.id, + }), + ], + styles: { [id]: true }, + }); } else { // like format bar when the line is empty action(host); diff --git a/blocksuite/framework/block-std/src/__tests__/command.unit.spec.ts b/blocksuite/framework/block-std/src/__tests__/command.unit.spec.ts index bbb32a6c58..b296c1c02b 100644 --- a/blocksuite/framework/block-std/src/__tests__/command.unit.spec.ts +++ b/blocksuite/framework/block-std/src/__tests__/command.unit.spec.ts @@ -4,33 +4,15 @@ import type { Command } from '../command/index.js'; import { CommandManager } from '../command/index.js'; type Command1 = Command< - never, - 'commandData1', { command1Option?: string; + }, + { + commandData1: string; } >; -type Command2 = Command<'commandData1', 'commandData2'>; - -type Command3 = Command<'commandData1' | 'commandData2', 'commandData3'>; - -declare global { - namespace BlockSuite { - interface CommandContext { - commandData1?: string; - commandData2?: string; - commandData3?: string; - } - - interface Commands { - command1: Command1; - command2: Command2; - command3: Command3; - command4: Command; - } - } -} +type Command2 = Command<{ commandData1: string }, { commandData2: string }>; describe('CommandManager', () => { let std: BlockSuite.Std; @@ -43,13 +25,15 @@ describe('CommandManager', () => { }); test('can add and execute a command', () => { - const command1: Command = vi.fn((_ctx, next) => next()); - const command2: Command = vi.fn((_ctx, _next) => {}); - commandManager.add('command1', command1); - commandManager.add('command2', command2); + const command1: Command1 = vi.fn((_ctx, next) => next()); + const command2: Command2 = vi.fn((_ctx, _next) => {}); - const [success1] = commandManager.chain().command1().run(); - const [success2] = commandManager.chain().command2().run(); + const [success1] = commandManager.chain().pipe(command1, {}).run(); + const [success2] = commandManager + .chain() + .pipe(command1, {}) + .pipe(command2) + .run(); expect(command1).toHaveBeenCalled(); expect(command2).toHaveBeenCalled(); @@ -62,15 +46,11 @@ describe('CommandManager', () => { const command2: Command = vi.fn((_ctx, next) => next()); const command3: Command = vi.fn((_ctx, next) => next()); - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - const [success] = commandManager .chain() - .command1() - .command2() - .command3() + .pipe(command1) + .pipe(command2) + .pipe(command3) .run(); expect(command1).toHaveBeenCalled(); @@ -84,15 +64,11 @@ describe('CommandManager', () => { const command2: Command = vi.fn((_ctx, _next) => {}); const command3: Command = vi.fn((_ctx, next) => next()); - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - const [success] = commandManager .chain() - .command1() - .command2() - .command3() + .pipe(command1) + .pipe(command2) + .pipe(command3) .run(); expect(command1).toHaveBeenCalled(); @@ -110,15 +86,11 @@ describe('CommandManager', () => { }); const command3: Command = vi.fn((_ctx, next) => next()); - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - const [success] = commandManager .chain() - .command1() - .command2() - .command3() + .pipe(command1) + .pipe(command2) + .pipe(command3) .run(); expect(command1).toHaveBeenCalled(); @@ -129,13 +101,11 @@ describe('CommandManager', () => { }); test('can pass data to command when calling a command', () => { - const command1: Command = vi.fn((_ctx, next) => next()); - - commandManager.add('command1', command1); + const command1: Command1 = vi.fn((_ctx, next) => next()); const [success] = commandManager .chain() - .command1({ command1Option: 'test' }) + .pipe(command1, { command1Option: 'test' }) .run(); expect(command1).toHaveBeenCalledWith( @@ -146,14 +116,14 @@ describe('CommandManager', () => { }); test('can add data to the command chain with `with` method', () => { - const command1: Command = vi.fn((_ctx, next) => next()); - - commandManager.add('command1', command1); + const command1: Command<{ commandData1: string }> = vi.fn((_ctx, next) => + next() + ); const [success, ctx] = commandManager .chain() .with({ commandData1: 'test' }) - .command1() + .pipe(command1) .run(); expect(command1).toHaveBeenCalledWith( @@ -165,18 +135,19 @@ describe('CommandManager', () => { }); test('passes and updates context across commands', () => { - const command1: Command<'std', 'commandData1'> = vi.fn((_ctx, next) => - next({ commandData1: '123' }) + const command1: Command<{}, { commandData1: string }> = vi.fn( + (_ctx, next) => next({ commandData1: '123' }) ); - const command2: Command<'commandData1'> = vi.fn((ctx, next) => { + const command2: Command<{ commandData1: string }> = vi.fn((ctx, next) => { expect(ctx.commandData1).toBe('123'); next({ commandData1: '456' }); }); - commandManager.add('command1', command1); - commandManager.add('command2', command2); - - const [success, ctx] = commandManager.chain().command1().command2().run(); + const [success, ctx] = commandManager + .chain() + .pipe(command1) + .pipe(command2) + .run(); expect(command1).toHaveBeenCalled(); expect(command2).toHaveBeenCalled(); @@ -184,57 +155,15 @@ describe('CommandManager', () => { expect(ctx.commandData1).toBe('456'); }); - test('can execute an inline command', () => { - const inlineCommand: Command = vi.fn((_ctx, next) => next()); - - const success = commandManager.chain().inline(inlineCommand).run(); - - expect(inlineCommand).toHaveBeenCalled(); - expect(success).toBeTruthy(); - }); - - test('can execute a single command with `exec`', () => { - const command1: Command1 = vi.fn((_ctx, next) => - next({ commandData1: (_ctx.command1Option ?? '') + '123' }) - ); - const command2: Command2 = vi.fn((_ctx, next) => - next({ commandData2: 'cmd2' }) - ); - const command3: Command3 = vi.fn((_ctx, next) => next()); - - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - - const result1 = commandManager.exec('command1'); - const result2 = commandManager.exec('command1', { - command1Option: 'test', - }); - const result3 = commandManager.exec('command2'); - const result4 = commandManager.exec('command3'); - - expect(command1).toHaveBeenCalled(); - expect(command2).toHaveBeenCalled(); - expect(command3).toHaveBeenCalled(); - expect(result1).toEqual({ commandData1: '123', success: true }); - expect(result2).toEqual({ commandData1: 'test123', success: true }); - expect(result3).toEqual({ commandData2: 'cmd2', success: true }); - expect(result4).toEqual({ success: true }); - }); - test('should not continue with the rest of the chain if all commands in `try` fail', () => { - const command1: Command = vi.fn((_ctx, _next) => {}); + const command1: Command = vi.fn((_ctx, _next) => {}); const command2: Command = vi.fn((_ctx, _next) => {}); const command3: Command = vi.fn((_ctx, next) => next()); - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - const [success] = commandManager .chain() - .try(cmd => [cmd.command1(), cmd.command2()]) - .command3() + .try(chain => [chain.pipe(command1), chain.pipe(command2)]) + .pipe(command3) .run(); expect(command1).toHaveBeenCalled(); @@ -244,20 +173,16 @@ describe('CommandManager', () => { }); test('should not re-execute previous commands in the chain before `try`', () => { - const command1: Command1 = vi.fn((_ctx, next) => - next({ commandData1: '123' }) + const command1: Command<{}, { commandData1: string }> = vi.fn( + (_ctx, next) => next({ commandData1: '123' }) ); const command2: Command = vi.fn((_ctx, _next) => {}); const command3: Command = vi.fn((_ctx, next) => next()); - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - const [success, ctx] = commandManager .chain() - .command1() - .try(cmd => [cmd.command2(), cmd.command3()]) + .pipe(command1) + .try(chain => [chain.pipe(command2), chain.pipe(command3)]) .run(); expect(command1).toHaveBeenCalledTimes(1); @@ -268,22 +193,22 @@ describe('CommandManager', () => { }); test('should continue with the rest of the chain if one command in `try` succeeds', () => { - const command1: Command1 = vi.fn((_ctx, _next) => {}); - const command2: Command2 = vi.fn((_ctx, next) => - next({ commandData2: '123' }) + const command1: Command< + {}, + { commandData1?: string; commandData2?: string } + > = vi.fn((_ctx, _next) => {}); + const command2: Command< + {}, + { commandData1?: string; commandData2?: string } + > = vi.fn((_ctx, next) => next({ commandData2: '123' })); + const command3: Command<{}, { commandData3: string }> = vi.fn( + (_ctx, next) => next({ commandData3: '456' }) ); - const command3: Command3 = vi.fn((_ctx, next) => - next({ commandData3: '456' }) - ); - - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); const [success, ctx] = commandManager .chain() - .try(cmd => [cmd.command1(), cmd.command2()]) - .command3() + .try(chain => [chain.pipe(command1), chain.pipe(command2)]) + .pipe(command3) .run(); expect(command1).toHaveBeenCalled(); @@ -296,19 +221,18 @@ describe('CommandManager', () => { }); test('should not execute any further commands in `try` after one succeeds', () => { - const command1: Command1 = vi.fn((_ctx, next) => - next({ commandData1: '123' }) - ); - const command2: Command2 = vi.fn((_ctx, next) => - next({ commandData2: '456' }) - ); - - commandManager.add('command1', command1); - commandManager.add('command2', command2); + const command1: Command< + {}, + { commandData1?: string; commandData2?: string } + > = vi.fn((_ctx, next) => next({ commandData1: '123' })); + const command2: Command< + {}, + { commandData1?: string; commandData2?: string } + > = vi.fn((_ctx, next) => next({ commandData2: '456' })); const [success, ctx] = commandManager .chain() - .try(cmd => [cmd.command1(), cmd.command2()]) + .try(chain => [chain.pipe(command1), chain.pipe(command2)]) .run(); expect(command1).toHaveBeenCalled(); @@ -322,31 +246,23 @@ describe('CommandManager', () => { const command1: Command = vi.fn((_ctx, next) => next({ commandData1: 'fromCommand1', commandData2: 'fromCommand1' }) ); - const command2: Command<'commandData1' | 'commandData2'> = vi.fn( - (ctx, next) => { - expect(ctx.commandData1).toBe('fromCommand1'); - expect(ctx.commandData2).toBe('fromCommand1'); - // override commandData2 - next({ commandData2: 'fromCommand2' }); - } - ); - const command3: Command<'commandData1' | 'commandData2'> = vi.fn( - (ctx, next) => { - expect(ctx.commandData1).toBe('fromCommand1'); - expect(ctx.commandData2).toBe('fromCommand2'); - next(); - } - ); - - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); + const command2: Command = vi.fn((ctx, next) => { + expect(ctx.commandData1).toBe('fromCommand1'); + expect(ctx.commandData2).toBe('fromCommand1'); + // override commandData2 + next({ commandData2: 'fromCommand2' }); + }); + const command3: Command = vi.fn((ctx, next) => { + expect(ctx.commandData1).toBe('fromCommand1'); + expect(ctx.commandData2).toBe('fromCommand2'); + next(); + }); const [success] = commandManager .chain() - .command1() - .try(cmd => [cmd.command2()]) - .command3() + .pipe(command1) + .try(chain => [chain.pipe(command2)]) + .pipe(command3) .run(); expect(command1).toHaveBeenCalled(); @@ -360,14 +276,10 @@ describe('CommandManager', () => { const command2: Command = vi.fn((_ctx, next) => next()); const command3: Command = vi.fn((_ctx, next) => next()); - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - const [success] = commandManager .chain() - .tryAll(cmd => [cmd.command1(), cmd.command2()]) - .command3() + .tryAll(chain => [chain.pipe(command1), chain.pipe(command2)]) + .pipe(command3) .run(); expect(command1).toHaveBeenCalled(); @@ -377,23 +289,26 @@ describe('CommandManager', () => { }); test('should execute all commands in `tryAll` even if one has already succeeded', () => { - const command1: Command1 = vi.fn((_ctx, next) => - next({ commandData1: '123' }) - ); - const command2: Command2 = vi.fn((_ctx, next) => - next({ commandData2: '456' }) - ); - const command3: Command3 = vi.fn((_ctx, next) => - next({ commandData3: '789' }) - ); - - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); + const command1: Command< + {}, + { commandData1?: string; commandData2?: string; commandData3?: string } + > = vi.fn((_ctx, next) => next({ commandData1: '123' })); + const command2: Command< + {}, + { commandData1?: string; commandData2?: string; commandData3?: string } + > = vi.fn((_ctx, next) => next({ commandData2: '456' })); + const command3: Command< + {}, + { commandData1?: string; commandData2?: string; commandData3?: string } + > = vi.fn((_ctx, next) => next({ commandData3: '789' })); const [success, ctx] = commandManager .chain() - .tryAll(cmd => [cmd.command1(), cmd.command2(), cmd.command3()]) + .tryAll(chain => [ + chain.pipe(command1), + chain.pipe(command2), + chain.pipe(command3), + ]) .run(); expect(command1).toHaveBeenCalled(); @@ -406,20 +321,16 @@ describe('CommandManager', () => { }); test('should not continue with the rest of the chain if all commands in `tryAll` fail', () => { - const command1: Command1 = vi.fn((_ctx, _next) => {}); - const command2: Command2 = vi.fn((_ctx, _next) => {}); - const command3: Command3 = vi.fn((_ctx, next) => - next({ commandData3: '123' }) + const command1: Command = vi.fn((_ctx, _next) => {}); + const command2: Command = vi.fn((_ctx, _next) => {}); + const command3: Command<{}, { commandData3: string }> = vi.fn( + (_ctx, next) => next({ commandData3: '123' }) ); - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - const [success, ctx] = commandManager .chain() - .tryAll(cmd => [cmd.command1(), cmd.command2()]) - .command3() + .tryAll(chain => [chain.pipe(command1), chain.pipe(command2)]) + .pipe(command3) .run(); expect(command1).toHaveBeenCalled(); @@ -430,43 +341,44 @@ describe('CommandManager', () => { }); test('should pass context correctly in `tryAll` when at least one command succeeds', () => { - const command1: Command = vi.fn((_ctx, next) => - next({ commandData1: 'fromCommand1' }) + const command1: Command<{}, { commandData1: string }> = vi.fn( + (_ctx, next) => next({ commandData1: 'fromCommand1' }) ); - const command2: Command<'commandData1'> = vi.fn((ctx, next) => { + const command2: Command< + { commandData1: string; commandData2?: string }, + { commandData2: string; commandData3: string } + > = vi.fn((ctx, next) => { expect(ctx.commandData1).toBe('fromCommand1'); // override commandData1 next({ commandData1: 'fromCommand2', commandData2: 'fromCommand2' }); }); - const command3: Command<'commandData1' | 'commandData2'> = vi.fn( - (ctx, next) => { - expect(ctx.commandData1).toBe('fromCommand2'); - expect(ctx.commandData2).toBe('fromCommand2'); - next({ - // override commandData2 - commandData2: 'fromCommand3', - commandData3: 'fromCommand3', - }); - } - ); - const command4: Command<'commandData1' | 'commandData2' | 'commandData3'> = - vi.fn((ctx, next) => { - expect(ctx.commandData1).toBe('fromCommand2'); - expect(ctx.commandData2).toBe('fromCommand3'); - expect(ctx.commandData3).toBe('fromCommand3'); - next(); + const command3: Command< + { commandData1: string; commandData2?: string }, + { commandData2: string; commandData3: string } + > = vi.fn((ctx, next) => { + expect(ctx.commandData1).toBe('fromCommand2'); + expect(ctx.commandData2).toBe('fromCommand2'); + next({ + // override commandData2 + commandData2: 'fromCommand3', + commandData3: 'fromCommand3', }); - - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - commandManager.add('command4', command4); + }); + const command4: Command< + { commandData1: string; commandData2: string; commandData3: string }, + {} + > = vi.fn((ctx, next) => { + expect(ctx.commandData1).toBe('fromCommand2'); + expect(ctx.commandData2).toBe('fromCommand3'); + expect(ctx.commandData3).toBe('fromCommand3'); + next(); + }); const [success, ctx] = commandManager .chain() - .command1() - .tryAll(cmd => [cmd.command2(), cmd.command3()]) - .command4() + .pipe(command1) + .tryAll(chain => [chain.pipe(command2), chain.pipe(command3)]) + .pipe(command4) .run(); expect(command1).toHaveBeenCalled(); @@ -485,16 +397,11 @@ describe('CommandManager', () => { const command3: Command = vi.fn((_ctx, _next) => {}); const command4: Command = vi.fn((_ctx, next) => next()); - commandManager.add('command1', command1); - commandManager.add('command2', command2); - commandManager.add('command3', command3); - commandManager.add('command4', command4); - const [success] = commandManager .chain() - .command1() - .tryAll(cmd => [cmd.command2(), cmd.command3()]) - .command4() + .pipe(command1) + .tryAll(chain => [chain.pipe(command2), chain.pipe(command3)]) + .pipe(command4) .run(); expect(command1).toHaveBeenCalledTimes(1); diff --git a/blocksuite/framework/block-std/src/command/manager.ts b/blocksuite/framework/block-std/src/command/manager.ts index a5078d8ea2..5f026220ed 100644 --- a/blocksuite/framework/block-std/src/command/manager.ts +++ b/blocksuite/framework/block-std/src/command/manager.ts @@ -1,16 +1,6 @@ -import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; - import { LifeCycleWatcher } from '../extension/index.js'; -import { CommandIdentifier } from '../identifier.js'; import { cmdSymbol } from './consts.js'; -import type { - Chain, - Command, - ExecCommandResult, - IfAllKeysOptional, - InDataOfCommand, - InitCommandCtx, -} from './types.js'; +import type { Chain, Command, InitCommandCtx } from './types.js'; /** * Command manager to manage all commands @@ -128,12 +118,7 @@ import type { export class CommandManager extends LifeCycleWatcher { static override readonly key = 'commandManager'; - private readonly _commands = new Map(); - - private readonly _createChain = ( - methods: Record, - _cmds: Command[] - ): Chain => { + private readonly _createChain = (_cmds: Command[]): Chain => { const getCommandCtx = this._getCommandCtx; const createChain = this._createChain; const chain = this.chain; @@ -145,7 +130,7 @@ export class CommandManager extends LifeCycleWatcher { let success = false; try { const cmds = this[cmdSymbol]; - ctx = runCmds(ctx as BlockSuite.CommandContext, [ + ctx = runCmds(ctx, [ ...cmds, (_, next) => { success = true; @@ -160,38 +145,35 @@ export class CommandManager extends LifeCycleWatcher { }, with: function (this: Chain, value) { const cmds = this[cmdSymbol]; - return createChain(methods, [ - ...cmds, - (_, next) => next(value), - ]) as never; + return createChain([...cmds, (_, next) => next(value)]) as never; }, - inline: function (this: Chain, command) { + pipe: function (this: Chain, command: Command, input?: object) { const cmds = this[cmdSymbol]; - return createChain(methods, [...cmds, command]) as never; + return createChain([ + ...cmds, + (ctx, next) => command({ ...ctx, ...input }, next), + ]); }, try: function (this: Chain, fn) { const cmds = this[cmdSymbol]; - return createChain(methods, [ + return createChain([ ...cmds, (beforeCtx, next) => { let ctx = beforeCtx; - const chains = fn(chain()); - chains.some(chain => { - // inject ctx in the beginning - chain[cmdSymbol] = [ + const commands = fn(chain()); + + commands.some(innerChain => { + innerChain[cmdSymbol] = [ (_, next) => { next(ctx); }, - ...chain[cmdSymbol], + ...innerChain[cmdSymbol], ]; - const [success] = chain - .inline((branchCtx, next) => { - ctx = { ...ctx, ...branchCtx }; - next(); - }) - .run(); + const [success, branchCtx] = innerChain.run(); + ctx = { ...ctx, ...branchCtx }; + if (success) { next(ctx); return true; @@ -203,40 +185,38 @@ export class CommandManager extends LifeCycleWatcher { }, tryAll: function (this: Chain, fn) { const cmds = this[cmdSymbol]; - return createChain(methods, [ + return createChain([ ...cmds, (beforeCtx, next) => { let ctx = beforeCtx; - const chains = fn(chain()); let allFail = true; - chains.forEach(chain => { - // inject ctx in the beginning - chain[cmdSymbol] = [ + + const commands = fn(chain()); + + commands.forEach(innerChain => { + innerChain[cmdSymbol] = [ (_, next) => { next(ctx); }, - ...chain[cmdSymbol], + ...innerChain[cmdSymbol], ]; - const [success] = chain - .inline((branchCtx, next) => { - ctx = { ...ctx, ...branchCtx }; - next(); - }) - .run(); + const [success, branchCtx] = innerChain.run(); + ctx = { ...ctx, ...branchCtx }; + if (success) { allFail = false; } }); + if (!allFail) { next(ctx); } }, ]) as never; }, - ...methods, - } as Chain; + }; }; private readonly _getCommandCtx = (): InitCommandCtx => { @@ -258,120 +238,18 @@ export class CommandManager extends LifeCycleWatcher { * data is the final context after running the chain */ chain = (): Chain => { - const methods = {} as Record< - string, - (data: Record) => Chain - >; - const createChain = this._createChain; - for (const [name, command] of this._commands.entries()) { - methods[name] = function ( - this: { [cmdSymbol]: Command[] }, - data: Record - ) { - const cmds = this[cmdSymbol]; - return createChain(methods, [ - ...cmds, - (ctx, next) => command({ ...ctx, ...data }, next), - ]); - }; - } - - return createChain(methods, []) as never; + return this._createChain([]); }; - /** - * Register a command to the command manager - * @param name - * @param command - * Make sure to also add the command to the global interface `BlockSuite.Commands` - * ```ts - * const myCommand: Command = (ctx, next) => { - * // do something - * } - * - * declare global { - * namespace BlockSuite { - * interface Commands { - * 'myCommand': typeof myCommand - * } - * } - * } - * ``` - */ - add( - name: N, - command: BlockSuite.Commands[N] - ): CommandManager; - - add(name: string, command: Command) { - this._commands.set(name, command); - return this; - } - - override created() { - const add = this.add.bind(this); - this.std.provider.getAll(CommandIdentifier).forEach((command, key) => { - add(key as keyof BlockSuite.Commands, command); - }); - } - - /** - * Execute a registered command by name - * @param command - * @param payloads - * ```ts - * const { success, ...data } = commandManager.exec('myCommand', { data: 'data' }); - * ``` - * @returns { success, ...data } - success is a boolean to indicate if the command is successful, - * data is the final context after running the command - */ - exec( - command: K, - ...payloads: IfAllKeysOptional< - Omit, keyof InitCommandCtx>, - [ - inData: void | Omit< - InDataOfCommand, - keyof InitCommandCtx - >, - ], - [ - inData: Omit< - InDataOfCommand, - keyof InitCommandCtx - >, - ] - > - ): ExecCommandResult & { success: boolean } { - const cmdFunc = this._commands.get(command); - - if (!cmdFunc) { - throw new BlockSuiteError( - ErrorCode.CommandError, - `The command "${command}" not found` - ); - } - - const inData = payloads[0]; - const ctx = { - ...this._getCommandCtx(), - ...inData, - }; - - let execResult = { - success: false, - } as ExecCommandResult & { success: boolean }; - - cmdFunc(ctx, result => { - // @ts-expect-error FIXME: ts error - execResult = { ...result, success: true }; - }); - - return execResult; - } + exec = ( + command: Command, + input?: Input + ) => { + return this.chain().pipe(command, input).run(); + }; } -function runCmds(ctx: BlockSuite.CommandContext, [cmd, ...rest]: Command[]) { +function runCmds(ctx: InitCommandCtx, [cmd, ...rest]: Command[]) { let _ctx = ctx; if (cmd) { cmd(ctx, data => { diff --git a/blocksuite/framework/block-std/src/command/types.ts b/blocksuite/framework/block-std/src/command/types.ts index ddb4c1f2b7..11967e05eb 100644 --- a/blocksuite/framework/block-std/src/command/types.ts +++ b/blocksuite/framework/block-std/src/command/types.ts @@ -4,77 +4,39 @@ // type TestA = MakeOptionalIfEmpty; // void | {} // type TestB = MakeOptionalIfEmpty; // void | { prop?: string } // type TestC = MakeOptionalIfEmpty; // { prop: string } +import type { BlockStdScope } from '../scope/block-std-scope.js'; import type { cmdSymbol } from './consts.js'; -export type IfAllKeysOptional = - Partial extends T ? (T extends Partial ? Yes : No) : No; -type MakeOptionalIfEmpty = IfAllKeysOptional; - export interface InitCommandCtx { - std: BlockSuite.Std; + std: BlockStdScope; } -export type CommandKeyToData = Pick< - BlockSuite.CommandContext, - K ->; -export type Command< - In extends BlockSuite.CommandDataName = never, - Out extends BlockSuite.CommandDataName = never, - InData extends object = {}, -> = ( - ctx: CommandKeyToData & InitCommandCtx & InData, - next: (ctx?: CommandKeyToData) => void -) => void; -type Omit1 = [keyof Omit] extends [never] - ? void - : Omit; -export type InDataOfCommand = - C extends Command ? CommandKeyToData & R : never; -type OutDataOfCommand = - C extends Command ? CommandKeyToData : never; - -type CommonMethods = { - inline: ( - command: Command, InlineOut> - ) => Chain>; - try: ( - fn: (chain: Chain) => Chain>[] - ) => Chain>; - tryAll: ( - fn: (chain: Chain) => Chain>[] - ) => Chain>; - run(): [ - result: boolean, - ctx: CommandKeyToData>, - ]; - with>(value: T): Chain; -}; - -type Cmds = { +export type Cmds = { [cmdSymbol]: Command[]; }; -export type Chain = CommonMethods & { - [K in keyof BlockSuite.Commands]: ( - data: MakeOptionalIfEmpty< - Omit1, In> - > - ) => Chain>; -} & Cmds; +export type Command = ( + input: Input & InitCommandCtx, + next: (output?: Output) => void +) => void; -export type ExecCommandResult = - OutDataOfCommand; - -declare global { - namespace BlockSuite { - interface CommandContext extends InitCommandCtx {} - - interface Commands {} - - type CommandName = keyof Commands; - type CommandDataName = keyof CommandContext; - - type CommandChain = Chain; - } -} +export type Chain = { + [cmdSymbol]: Command[]; + with: (input: Out) => Chain; + pipe: { + ( + command: Command + ): Chain; + ( + command: Command, + input?: In + ): Chain; + }; + try: ( + commands: (chain: Chain) => Chain[] + ) => Chain; + tryAll: ( + commands: (chain: Chain) => Chain[] + ) => Chain; + run: () => [false, Partial & InitCommandCtx] | [true, CommandCtx]; +}; diff --git a/blocksuite/framework/block-std/src/extension/command.ts b/blocksuite/framework/block-std/src/extension/command.ts deleted file mode 100644 index d92d57227f..0000000000 --- a/blocksuite/framework/block-std/src/extension/command.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { ExtensionType } from '@blocksuite/store'; - -import { CommandIdentifier } from '../identifier.js'; -import type { BlockCommands } from '../spec/index.js'; - -/** - * Create a command extension. - * - * @param commands A map of command names to command implementations. - * - * @example - * ```ts - * import { CommandExtension } from '@blocksuite/block-std'; - * - * const MyCommandExtension = CommandExtension({ - * 'my-command': MyCommand - * }); - * ``` - */ -export function CommandExtension(commands: BlockCommands): ExtensionType { - return { - setup: di => { - Object.entries(commands).forEach(([name, command]) => { - di.addImpl(CommandIdentifier(name), () => command); - }); - }, - }; -} diff --git a/blocksuite/framework/block-std/src/extension/index.ts b/blocksuite/framework/block-std/src/extension/index.ts index a75e97f1f2..41d8534147 100644 --- a/blocksuite/framework/block-std/src/extension/index.ts +++ b/blocksuite/framework/block-std/src/extension/index.ts @@ -1,5 +1,4 @@ export * from './block-view.js'; -export * from './command.js'; export * from './config.js'; export * from './dnd/index.js'; export * from './flavour.js'; diff --git a/blocksuite/framework/block-std/src/spec/type.ts b/blocksuite/framework/block-std/src/spec/type.ts index 3bf2b70efb..6aa0a157fc 100644 --- a/blocksuite/framework/block-std/src/spec/type.ts +++ b/blocksuite/framework/block-std/src/spec/type.ts @@ -1,6 +1,5 @@ import type { BlockModel } from '@blocksuite/store'; import type { StaticValue } from 'lit/static-html.js'; -export type BlockCommands = Partial; export type BlockViewType = StaticValue | ((model: BlockModel) => StaticValue); export type WidgetViewMapType = Record; diff --git a/blocksuite/presets/src/fragments/comment/comment-manager.ts b/blocksuite/presets/src/fragments/comment/comment-manager.ts index 907ce10d14..1e97dbdbc4 100644 --- a/blocksuite/presets/src/fragments/comment/comment-manager.ts +++ b/blocksuite/presets/src/fragments/comment/comment-manager.ts @@ -1,3 +1,4 @@ +import { getSelectedBlocksCommand } from '@blocksuite/affine-shared/commands'; import type { EditorHost, TextSelection } from '@blocksuite/block-std'; import * as Y from 'yjs'; @@ -100,7 +101,7 @@ export class CommentManager { } | null { const [_, ctx] = this._command .chain() - .getSelectedBlocks({ + .pipe(getSelectedBlocksCommand, { currentTextSelection: selection, types: ['text'], }) diff --git a/blocksuite/presets/src/fragments/outline/body/outline-panel-body.ts b/blocksuite/presets/src/fragments/outline/body/outline-panel-body.ts index 43d739b02f..9d98c3d7fa 100644 --- a/blocksuite/presets/src/fragments/outline/body/outline-panel-body.ts +++ b/blocksuite/presets/src/fragments/outline/body/outline-panel-body.ts @@ -1,7 +1,11 @@ import { effects } from '@blocksuite/affine-block-note/effects'; import { ShadowlessElement, SurfaceSelection } from '@blocksuite/block-std'; import type { NoteBlockModel } from '@blocksuite/blocks'; -import { matchFlavours, NoteDisplayMode } from '@blocksuite/blocks'; +import { + changeNoteDisplayMode, + matchFlavours, + NoteDisplayMode, +} from '@blocksuite/blocks'; import { Bound, noop, @@ -128,7 +132,7 @@ export class OutlinePanelBody extends SignalWatcher( // when display mode change to page only, we should de-select the note if it is selected in edgeless mode private _handleDisplayModeChange(e: DisplayModeChangeEvent) { const { note, newMode } = e.detail; - this.editor.std.command.exec('changeNoteDisplayMode', { + this.editor.std.command.exec(changeNoteDisplayMode, { noteId: note.id, mode: newMode, stopCapture: true, diff --git a/blocksuite/tests-legacy/utils/actions/block.ts b/blocksuite/tests-legacy/utils/actions/block.ts index cb1e24243e..8e1620eccd 100644 --- a/blocksuite/tests-legacy/utils/actions/block.ts +++ b/blocksuite/tests-legacy/utils/actions/block.ts @@ -9,14 +9,12 @@ export async function updateBlockType( ) { await page.evaluate( ([flavour, type]) => { - (window.host.std.command.chain() as any) - .updateBlockType({ - flavour, - props: { - type, - }, - }) - .run(); + window.host.std.command.exec(window.$blocksuite.blocks.updateBlockType, { + flavour, + props: { + type, + }, + }); }, [flavour, type] as [BlockSuite.Flavour, string?] ); diff --git a/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts b/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts index 2bfc1210fa..fd3840aaef 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts @@ -14,6 +14,7 @@ import { BlocksUtils, DocModeProvider, EditPropsStore, + getSelectedBlocksCommand, NoteDisplayMode, NotificationProvider, RefNodeSlotsProvider, @@ -216,13 +217,10 @@ const REPLACE_SELECTION = { ) => { const currentTextSelection = currentSelections.text; const currentBlockSelections = currentSelections.blocks; - const [_, data] = host.command - .chain() - .getSelectedBlocks({ - currentTextSelection, - currentBlockSelections, - }) - .run(); + const [_, data] = host.command.exec(getSelectedBlocksCommand, { + currentTextSelection, + currentBlockSelections, + }); if (!data.selectedBlocks) return false; reportResponse('result:replace'); @@ -264,14 +262,11 @@ const INSERT_BELOW = { const currentTextSelection = currentSelections.text; const currentBlockSelections = currentSelections.blocks; const currentImageSelections = currentSelections.images; - const [_, data] = host.command - .chain() - .getSelectedBlocks({ - currentTextSelection, - currentBlockSelections, - currentImageSelections, - }) - .run(); + const [_, data] = host.command.exec(getSelectedBlocksCommand, { + currentTextSelection, + currentBlockSelections, + currentImageSelections, + }); if (!data.selectedBlocks) return false; reportResponse('result:insert'); await insertBelow( diff --git a/packages/frontend/core/src/blocksuite/presets/ai/_common/config.ts b/packages/frontend/core/src/blocksuite/presets/ai/_common/config.ts index 8f14f76bfb..e285999fe3 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/_common/config.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/_common/config.ts @@ -2,6 +2,7 @@ import type { Chain, InitCommandCtx } from '@blocksuite/affine/block-std'; import { type AIItemGroupConfig, type AISubItemConfig, + getSelectedModelsCommand, matchFlavours, } from '@blocksuite/affine/blocks'; @@ -94,7 +95,7 @@ const blockActionTrackerOptions: BlockSuitePresets.TrackerOptions = { const textBlockShowWhen = (chain: Chain) => { const [_, ctx] = chain - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { types: ['block', 'text'], }) .run(); @@ -108,7 +109,7 @@ const textBlockShowWhen = (chain: Chain) => { const codeBlockShowWhen = (chain: Chain) => { const [_, ctx] = chain - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { types: ['block', 'text'], }) .run(); @@ -121,7 +122,7 @@ const codeBlockShowWhen = (chain: Chain) => { const imageBlockShowWhen = (chain: Chain) => { const [_, ctx] = chain - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { types: ['block'], }) .run(); @@ -268,7 +269,7 @@ const GenerateWithAIGroup: AIItemGroupConfig = { handler: actionToHandler('createHeadings', AIPenIconWithAnimation), showWhen: chain => { const [_, ctx] = chain - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { types: ['block', 'text'], }) .run(); diff --git a/packages/frontend/core/src/blocksuite/presets/ai/utils/editor-actions.ts b/packages/frontend/core/src/blocksuite/presets/ai/utils/editor-actions.ts index 4d2e9d73bd..13de9406f3 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/utils/editor-actions.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/utils/editor-actions.ts @@ -6,7 +6,10 @@ import { type TextSelection, } from '@blocksuite/affine/block-std'; import type { AffineAIPanelWidget } from '@blocksuite/affine/blocks'; -import { isInsideEdgelessEditor } from '@blocksuite/affine/blocks'; +import { + deleteTextCommand, + isInsideEdgelessEditor, +} from '@blocksuite/affine/blocks'; import { type BlockModel, Slice } from '@blocksuite/affine/store'; import { @@ -107,7 +110,7 @@ export const replace = async ( ); if (textSelection) { - host.std.command.exec('deleteText', { textSelection }); + host.std.command.exec(deleteTextCommand, { textSelection }); const { snapshot, transformer } = await markdownToSnapshot(content, host); await transformer.snapshotToSlice( snapshot, diff --git a/packages/frontend/core/src/blocksuite/presets/ai/utils/extract.ts b/packages/frontend/core/src/blocksuite/presets/ai/utils/extract.ts index 30339052b6..638a4f42aa 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/utils/extract.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/utils/extract.ts @@ -3,6 +3,8 @@ import { BlocksUtils, DocModeProvider, embedSyncedDocMiddleware, + getImageSelectionsCommand, + getSelectedBlocksCommand, type ImageBlockModel, isInsideEdgelessEditor, MarkdownAdapter, @@ -70,8 +72,8 @@ async function extractPageSelected( } else if (!hasText && hasImages && images.length === 1) { host.command .chain() - .tryAll(chain => [chain.getImageSelections()]) - .getSelectedBlocks({ + .tryAll(chain => [chain.pipe(getImageSelectionsCommand)]) + .pipe(getSelectedBlocksCommand, { types: ['image'], }) .run(); diff --git a/packages/frontend/core/src/blocksuite/presets/ai/utils/selection-utils.ts b/packages/frontend/core/src/blocksuite/presets/ai/utils/selection-utils.ts index 14c2a1bc01..de9aa4508d 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/utils/selection-utils.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/utils/selection-utils.ts @@ -5,6 +5,11 @@ import { type CopilotTool, EdgelessRootService, type FrameBlockModel, + getBlockSelectionsCommand, + getImageSelectionsCommand, + getSelectedBlocksCommand, + getSelectedModelsCommand, + getTextSelectionCommand, ImageBlockModel, type SurfaceBlockComponent, } from '@blocksuite/affine/blocks'; @@ -95,12 +100,9 @@ export async function selectedToPng(editor: EditorHost) { } export function getSelectedModels(editorHost: EditorHost) { - const chain = editorHost.std.command.chain(); - const [_, ctx] = chain - .getSelectedModels({ - types: ['block', 'text'], - }) - .run(); + const [_, ctx] = editorHost.std.command.exec(getSelectedModelsCommand, { + types: ['block', 'text'], + }); const { selectedModels } = ctx; return selectedModels; } @@ -246,11 +248,11 @@ export const getSelections = ( const [_, data] = host.command .chain() .tryAll(chain => [ - chain.getTextSelection(), - chain.getBlockSelections(), - chain.getImageSelections(), + chain.pipe(getTextSelectionCommand), + chain.pipe(getBlockSelectionsCommand), + chain.pipe(getImageSelectionsCommand), ]) - .getSelectedBlocks({ types: ['text', 'block', 'image'], mode }) + .pipe(getSelectedBlocksCommand, { types: ['text', 'block', 'image'], mode }) .run(); return data; @@ -260,11 +262,11 @@ export const getSelectedImagesAsBlobs = async (host: EditorHost) => { const [_, data] = host.command .chain() .tryAll(chain => [ - chain.getTextSelection(), - chain.getBlockSelections(), - chain.getImageSelections(), + chain.pipe(getTextSelectionCommand), + chain.pipe(getBlockSelectionsCommand), + chain.pipe(getImageSelectionsCommand), ]) - .getSelectedBlocks({ + .pipe(getSelectedBlocksCommand, { types: ['block', 'image'], }) .run(); diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx index a35f4adabb..4d34aeeea1 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx @@ -1,5 +1,10 @@ import { FeatureFlagService } from '@affine/core/modules/feature-flag'; -import { type DocMode, getLastNoteBlock } from '@blocksuite/affine/blocks'; +import { + appendParagraphCommand, + type DocMode, + focusBlockEnd, + getLastNoteBlock, +} from '@blocksuite/affine/blocks'; import { Slot } from '@blocksuite/affine/global/utils'; import type { AffineEditorContainer, @@ -148,14 +153,15 @@ export const BlocksuiteEditorContainer = forwardRef< lastBlock.flavour === 'affine:paragraph' && lastBlock.text?.length === 0 ) { - std.command.exec('focusBlockEnd' as never, { - focusBlock: std.view.getBlock(lastBlock.id) as never, + const focusBlock = std.view.getBlock(lastBlock.id) ?? undefined; + std.command.exec(focusBlockEnd, { + focusBlock, }); return; } } - std.command.exec('appendParagraph' as never, {}); + std.command.exec(appendParagraphCommand); }, [affineEditorContainerProxy, page, shared]); return ( diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx index 76a86d48cb..893b3342c4 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx @@ -53,6 +53,7 @@ import { EdgelessRootBlockComponent, EmbedLinkedDocBlockComponent, GenerateDocUrlExtension, + insertLinkByQuickSearchCommand, MobileSpecsPatches, NativeClipboardExtension, NoteConfigExtension, @@ -460,34 +461,28 @@ export function patchQuickSearchService(framework: FrameworkProvider) { (item.name === 'Linked Doc' || item.name === 'Link') ) { item.action = async ({ rootComponent }) => { - // @ts-expect-error fixme - const { success, insertedLinkType } = - // @ts-expect-error fixme - rootComponent.std.command.exec('insertLinkByQuickSearch'); + const [success, { insertedLinkType }] = + rootComponent.std.command.exec(insertLinkByQuickSearchCommand); if (!success) return; insertedLinkType - ?.then( - (type: { - flavour?: 'affine:embed-linked-doc' | 'affine:bookmark'; - }) => { - const flavour = type?.flavour; - if (!flavour) return; + ?.then(type => { + const flavour = type?.flavour; + if (!flavour) return; - if (flavour === 'affine:bookmark') { - track.doc.editor.slashMenu.bookmark(); - return; - } - - if (flavour === 'affine:embed-linked-doc') { - track.doc.editor.slashMenu.linkDoc({ - control: 'linkDoc', - }); - return; - } + if (flavour === 'affine:bookmark') { + track.doc.editor.slashMenu.bookmark(); + return; } - ) + + if (flavour === 'affine:embed-linked-doc') { + track.doc.editor.slashMenu.linkDoc({ + control: 'linkDoc', + }); + return; + } + }) .catch(console.error); }; } diff --git a/packages/frontend/core/src/components/hooks/affine/use-share-url.ts b/packages/frontend/core/src/components/hooks/affine/use-share-url.ts index 0e4abca878..0ec4b405d1 100644 --- a/packages/frontend/core/src/components/hooks/affine/use-share-url.ts +++ b/packages/frontend/core/src/components/hooks/affine/use-share-url.ts @@ -6,7 +6,14 @@ import { useI18n } from '@affine/i18n'; import { track } from '@affine/track'; import { type EditorHost } from '@blocksuite/affine/block-std'; import { GfxBlockElementModel } from '@blocksuite/affine/block-std/gfx'; -import type { DocMode, EdgelessRootService } from '@blocksuite/affine/blocks'; +import { + type DocMode, + type EdgelessRootService, + getBlockSelectionsCommand, + getImageSelectionsCommand, + getSelectedModelsCommand, + getTextSelectionCommand, +} from '@blocksuite/affine/blocks'; import { useService } from '@toeverything/infra'; import { useCallback } from 'react'; @@ -97,11 +104,11 @@ export const getSelectedNodes = ( const [success, ctx] = std.command .chain() .tryAll(chain => [ - chain.getTextSelection(), - chain.getBlockSelections(), - chain.getImageSelections(), + chain.pipe(getTextSelectionCommand), + chain.pipe(getBlockSelectionsCommand), + chain.pipe(getImageSelectionsCommand), ]) - .getSelectedModels({ + .pipe(getSelectedModelsCommand, { mode: 'highest', }) .run(); diff --git a/tools/utils/src/workspace.gen.ts b/tools/utils/src/workspace.gen.ts index 9ff0a411d6..b25e01d88a 100644 --- a/tools/utils/src/workspace.gen.ts +++ b/tools/utils/src/workspace.gen.ts @@ -145,6 +145,8 @@ export const PackageList = [ location: 'blocksuite/affine/block-image', name: '@blocksuite/affine-block-image', workspaceDependencies: [ + 'blocksuite/affine/block-note', + 'blocksuite/affine/block-surface', 'blocksuite/affine/components', 'blocksuite/affine/model', 'blocksuite/affine/shared', diff --git a/yarn.lock b/yarn.lock index 9d85a555c7..4e4f222475 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3483,6 +3483,8 @@ __metadata: version: 0.0.0-use.local resolution: "@blocksuite/affine-block-image@workspace:blocksuite/affine/block-image" dependencies: + "@blocksuite/affine-block-note": "workspace:*" + "@blocksuite/affine-block-surface": "workspace:*" "@blocksuite/affine-components": "workspace:*" "@blocksuite/affine-model": "workspace:*" "@blocksuite/affine-shared": "workspace:*"