refactor(editor): remove dependency of command global types (#9903)

Closes: [BS-2216](https://linear.app/affine-design/issue/BS-2216/remove-global-types-in-command)
This commit is contained in:
Saul-Mirone
2025-01-27 12:28:46 +00:00
parent 4b549e0484
commit 17bf75e843
170 changed files with 1461 additions and 2124 deletions

View File

@@ -1,4 +1,8 @@
/// <reference types="@blocksuite/affine-shared/commands" />
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?
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
},
},
];

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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;

View File

@@ -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<never, 'textStyle'> = (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<never, 'textStyle'> = (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) {

View File

@@ -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<InitCommandCtx>,
handler: (
type: 'text' | 'block' | 'native',
inlineEditors: InlineEditor<AffineTextAttributes>[]
) => CommandKeyToData<InlineOut> | boolean | void
) {
return chain.try<InlineOut>(chain => [
) => { textStyle: AffineTextAttributes } | boolean | void
): Chain<InitCommandCtx & { textStyle: AffineTextAttributes }> {
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<InlineOut>((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<InlineOut>((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<InlineOut>((ctx, next) => {
chain.pipe((ctx, next) => {
const selectedInlineEditors = Array.from<InlineRootElement>(
ctx.std.host.querySelectorAll(`[${INLINE_ROOT_ATTR}]`)
)
@@ -166,7 +168,7 @@ function handleCurrentSelection<
}
export function getCombinedTextStyle(chain: Chain<InitCommandCtx>) {
return handleCurrentSelection<'textStyle'>(chain, (type, inlineEditors) => {
return handleCurrentSelection(chain, (type, inlineEditors) => {
if (type === 'text') {
return {
textStyle: getCombinedFormatFromInlineEditors(

View File

@@ -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';