refactor(editor): align rich text util apis (#11039)

This commit is contained in:
Saul-Mirone
2025-03-20 10:40:11 +00:00
parent 77e659d0b0
commit 66ea3038af
18 changed files with 57 additions and 73 deletions

View File

@@ -29,7 +29,7 @@ const linkedDocSlashMenuConfig: SlashMenuConfig = {
model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'), model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'),
action: ({ std, model }) => { action: ({ std, model }) => {
const newDoc = createDefaultDoc(std.host.doc.workspace); const newDoc = createDefaultDoc(std.host.doc.workspace);
insertContent(std.host, model, REFERENCE_NODE, { insertContent(std, model, REFERENCE_NODE, {
reference: { reference: {
type: 'LinkedPage', type: 'LinkedPage',
pageId: newDoc.id, pageId: newDoc.id,
@@ -70,9 +70,9 @@ const linkedDocSlashMenuConfig: SlashMenuConfig = {
// @ts-expect-error same as above // @ts-expect-error same as above
const triggerKey = linkedDocWidget.config.triggerKeys[0]; const triggerKey = linkedDocWidget.config.triggerKeys[0];
insertContent(std.host, model, triggerKey); insertContent(std, model, triggerKey);
const inlineEditor = getInlineEditorByModel(std.host, model); const inlineEditor = getInlineEditorByModel(std, model);
if (inlineEditor) { if (inlineEditor) {
// Wait for range to be updated // Wait for range to be updated
const subscription = inlineEditor.slots.inlineRangeSync.subscribe( const subscription = inlineEditor.slots.inlineRangeSync.subscribe(

View File

@@ -86,7 +86,7 @@ export const updateBlockType: Command<
if (!id) return; if (!id) return;
const model = doc.getModelById(id); const model = doc.getModelById(id);
if (!model) return; if (!model) return;
asyncSetInlineRange(host, model, { asyncSetInlineRange(std, model, {
index: model.text?.length ?? 0, index: model.text?.length ?? 0,
length: 0, length: 0,
}).catch(console.error); }).catch(console.error);
@@ -132,7 +132,7 @@ export const updateBlockType: Command<
const lastNewModel = updatedBlocks[updatedBlocks.length - 1]; const lastNewModel = updatedBlocks[updatedBlocks.length - 1];
const allTextUpdated = updatedBlocks.map(model => const allTextUpdated = updatedBlocks.map(model =>
onModelTextUpdated(host, model) onModelTextUpdated(std, model)
); );
const selectionManager = host.selection; const selectionManager = host.selection;
const textSelection = selectionManager.find(TextSelection); const textSelection = selectionManager.find(TextSelection);

View File

@@ -119,7 +119,7 @@ class NoteKeymap {
} }
const [codeModel] = newModels; const [codeModel] = newModels;
asyncGetBlockComponent(ctx.std.host, codeModel.id) asyncGetBlockComponent(ctx.std, codeModel.id)
.then(codeElement => { .then(codeElement => {
if (!codeElement) { if (!codeElement) {
return; return;

View File

@@ -15,7 +15,7 @@ export const splitParagraphCommand: Command<
} }
> = (ctx, next) => { > = (ctx, next) => {
const { std } = ctx; const { std } = ctx;
const { store, host, selection } = std; const { store, selection } = std;
let blockId = ctx.blockId; let blockId = ctx.blockId;
if (!blockId) { if (!blockId) {
const text = selection.find(TextSelection); const text = selection.find(TextSelection);
@@ -26,7 +26,7 @@ export const splitParagraphCommand: Command<
const model = store.getBlock(blockId)?.model; const model = store.getBlock(blockId)?.model;
if (!model || !matchModels(model, [ParagraphBlockModel])) return; if (!model || !matchModels(model, [ParagraphBlockModel])) return;
const inlineEditor = getInlineEditorByModel(host, model); const inlineEditor = getInlineEditorByModel(std, model);
const range = inlineEditor?.getInlineRange(); const range = inlineEditor?.getInlineRange();
if (!range) return; if (!range) return;

View File

@@ -83,10 +83,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
matchModels(model.parent, [CalloutBlockModel]) matchModels(model.parent, [CalloutBlockModel])
) )
return; return;
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(std, text.from.blockId);
std.host,
text.from.blockId
);
const inlineRange = inlineEditor?.getInlineRange(); const inlineRange = inlineEditor?.getInlineRange();
if (!inlineRange || !inlineEditor) return; if (!inlineRange || !inlineEditor) return;
const raw = ctx.get('keyboardState').raw; const raw = ctx.get('keyboardState').raw;
@@ -115,10 +112,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
matchModels(model.parent, [CalloutBlockModel]) matchModels(model.parent, [CalloutBlockModel])
) )
return; return;
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(std, text.from.blockId);
std.host,
text.from.blockId
);
const inlineRange = inlineEditor?.getInlineRange(); const inlineRange = inlineEditor?.getInlineRange();
if (!inlineRange || !inlineEditor) return; if (!inlineRange || !inlineEditor) return;

View File

@@ -77,7 +77,7 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) {
doc.deleteBlock(model, { doc.deleteBlock(model, {
bringChildrenTo: parent, bringChildrenTo: parent,
}); });
asyncSetInlineRange(editorHost, prevBlock, { asyncSetInlineRange(editorHost.std, prevBlock, {
index: lengthBeforeJoin, index: lengthBeforeJoin,
length: 0, length: 0,
}).catch(console.error); }).catch(console.error);

View File

@@ -318,7 +318,7 @@ const pageToolGroup: KeyboardToolPanelGroup = {
.pipe(({ selectedModels }) => { .pipe(({ selectedModels }) => {
const newDoc = createDefaultDoc(std.store.workspace); const newDoc = createDefaultDoc(std.store.workspace);
if (!selectedModels?.length) return; if (!selectedModels?.length) return;
insertContent(std.host, selectedModels[0], REFERENCE_NODE, { insertContent(std, selectedModels[0], REFERENCE_NODE, {
reference: { reference: {
type: 'LinkedPage', type: 'LinkedPage',
pageId: newDoc.id, pageId: newDoc.id,
@@ -360,9 +360,9 @@ const pageToolGroup: KeyboardToolPanelGroup = {
if (!selectedModels?.length) return; if (!selectedModels?.length) return;
const currentModel = selectedModels[0]; const currentModel = selectedModels[0];
insertContent(std.host, currentModel, triggerKey); insertContent(std, currentModel, triggerKey);
const inlineEditor = getInlineEditorByModel(std.host, currentModel); const inlineEditor = getInlineEditorByModel(std, currentModel);
// Wait for range to be updated // Wait for range to be updated
if (inlineEditor) { if (inlineEditor) {
const subscription = inlineEditor.slots.inlineRangeSync.subscribe( const subscription = inlineEditor.slots.inlineRangeSync.subscribe(
@@ -702,7 +702,7 @@ const dateToolGroup: KeyboardToolPanelGroup = {
const model = selectedModels?.[0]; const model = selectedModels?.[0];
if (!model) return; if (!model) return;
insertContent(std.host, model, formatDate(new Date())); insertContent(std, model, formatDate(new Date()));
}, },
}, },
{ {
@@ -717,7 +717,7 @@ const dateToolGroup: KeyboardToolPanelGroup = {
const tomorrow = new Date(); const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setDate(tomorrow.getDate() + 1);
insertContent(std.host, model, formatDate(tomorrow)); insertContent(std, model, formatDate(tomorrow));
}, },
}, },
{ {
@@ -732,7 +732,7 @@ const dateToolGroup: KeyboardToolPanelGroup = {
const yesterday = new Date(); const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1); yesterday.setDate(yesterday.getDate() - 1);
insertContent(std.host, model, formatDate(yesterday)); insertContent(std, model, formatDate(yesterday));
}, },
}, },
{ {
@@ -745,7 +745,7 @@ const dateToolGroup: KeyboardToolPanelGroup = {
const model = selectedModels?.[0]; const model = selectedModels?.[0];
if (!model) return; if (!model) return;
insertContent(std.host, model, formatTime(new Date())); insertContent(std, model, formatTime(new Date()));
}, },
}, },
], ],

View File

@@ -38,7 +38,7 @@ export class LinkedDocPopover extends SignalWatcher(
this.context.close(); this.context.close();
// clear input query // clear input query
cleanSpecifiedTail( cleanSpecifiedTail(
this.context.std.host, this.context.std,
this.context.inlineEditor, this.context.inlineEditor,
this.context.triggerKey + (this._query || '') this.context.triggerKey + (this._query || '')
); );

View File

@@ -134,7 +134,7 @@ export class AffineMobileLinkedDocMenu extends SignalWatcher(
() => { () => {
this.context.close(); this.context.close();
cleanSpecifiedTail( cleanSpecifiedTail(
this.context.std.host, this.context.std,
this.context.inlineEditor, this.context.inlineEditor,
this.context.triggerKey + (this._query ?? '') this.context.triggerKey + (this._query ?? '')
); );

View File

@@ -4,29 +4,24 @@ import {
getCurrentNativeRange, getCurrentNativeRange,
matchModels, matchModels,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { import { type BlockStdScope, TextSelection } from '@blocksuite/block-std';
type BlockStdScope,
type EditorHost,
TextSelection,
} from '@blocksuite/block-std';
import type { InlineEditor, InlineRange } from '@blocksuite/block-std/inline'; import type { InlineEditor, InlineRange } from '@blocksuite/block-std/inline';
import { BlockModel } from '@blocksuite/store'; import { BlockModel } from '@blocksuite/store';
import type { AffineInlineEditor } from './inline/index.js';
import type { RichText } from './rich-text.js'; import type { RichText } from './rich-text.js';
/** /**
* In most cases, you not need RichText, you can use {@link getInlineEditorByModel} instead. * In most cases, you not need RichText, you can use {@link getInlineEditorByModel} instead.
*/ */
export function getRichTextByModel(editorHost: EditorHost, id: string) { export function getRichTextByModel(std: BlockStdScope, id: string) {
const blockComponent = editorHost.view.getBlock(id); const blockComponent = std.view.getBlock(id);
const richText = blockComponent?.querySelector<RichText>('rich-text'); const richText = blockComponent?.querySelector<RichText>('rich-text');
if (!richText) return null; if (!richText) return null;
return richText; return richText;
} }
export async function asyncGetRichText(editorHost: EditorHost, id: string) { export async function asyncGetRichText(std: BlockStdScope, id: string) {
const blockComponent = await asyncGetBlockComponent(editorHost, id); const blockComponent = await asyncGetBlockComponent(std, id);
if (!blockComponent) return null; if (!blockComponent) return null;
await blockComponent.updateComplete; await blockComponent.updateComplete;
const richText = blockComponent?.querySelector<RichText>('rich-text'); const richText = blockComponent?.querySelector<RichText>('rich-text');
@@ -35,29 +30,27 @@ export async function asyncGetRichText(editorHost: EditorHost, id: string) {
} }
export function getInlineEditorByModel( export function getInlineEditorByModel(
editorHost: EditorHost, std: BlockStdScope,
model: BlockModel | string model: BlockModel | string
) { ) {
const blockModel = const blockModel =
typeof model === 'string' typeof model === 'string' ? std.store.getBlock(model)?.model : model;
? editorHost.std.store.getBlock(model)?.model
: model;
if (!blockModel || matchModels(blockModel, [DatabaseBlockModel])) { if (!blockModel || matchModels(blockModel, [DatabaseBlockModel])) {
// Not support database model since it's may be have multiple inline editor instances. // Not support database model since it's may be have multiple inline editor instances.
// Support to enter the editing state through the Enter key in the database. // Support to enter the editing state through the Enter key in the database.
return null; return null;
} }
const richText = getRichTextByModel(editorHost, blockModel.id); const richText = getRichTextByModel(std, blockModel.id);
if (!richText) return null; if (!richText) return null;
return richText.inlineEditor; return richText.inlineEditor;
} }
export async function asyncSetInlineRange( export async function asyncSetInlineRange(
editorHost: EditorHost, std: BlockStdScope,
model: BlockModel, model: BlockModel,
inlineRange: InlineRange inlineRange: InlineRange
) { ) {
const richText = await asyncGetRichText(editorHost, model.id); const richText = await asyncGetRichText(std, model.id);
if (!richText) { if (!richText) {
return; return;
} }
@@ -94,11 +87,11 @@ export function selectTextModel(
} }
export async function onModelTextUpdated( export async function onModelTextUpdated(
editorHost: EditorHost, std: BlockStdScope,
model: BlockModel, model: BlockModel,
callback?: (text: RichText) => void callback?: (text: RichText) => void
) { ) {
const richText = await asyncGetRichText(editorHost, model.id); const richText = await asyncGetRichText(std, model.id);
if (!richText) { if (!richText) {
console.error('RichText is not ready yet.'); console.error('RichText is not ready yet.');
return; return;
@@ -121,8 +114,8 @@ export async function onModelTextUpdated(
* Remove specified text from the current range. * Remove specified text from the current range.
*/ */
export function cleanSpecifiedTail( export function cleanSpecifiedTail(
editorHost: EditorHost, std: BlockStdScope,
inlineEditorOrModel: AffineInlineEditor | BlockModel, inlineEditorOrModel: InlineEditor | BlockModel,
str: string str: string
) { ) {
if (!str) { if (!str) {
@@ -131,7 +124,7 @@ export function cleanSpecifiedTail(
} }
const inlineEditor = const inlineEditor =
inlineEditorOrModel instanceof BlockModel inlineEditorOrModel instanceof BlockModel
? getInlineEditorByModel(editorHost, inlineEditorOrModel) ? getInlineEditorByModel(std, inlineEditorOrModel)
: inlineEditorOrModel; : inlineEditorOrModel;
if (!inlineEditor) { if (!inlineEditor) {
return; return;

View File

@@ -7,8 +7,8 @@ import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
import { import {
BLOCK_ID_ATTR, BLOCK_ID_ATTR,
type BlockComponent, type BlockComponent,
type BlockStdScope,
type Chain, type Chain,
type EditorHost,
type InitCommandCtx, type InitCommandCtx,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { import {
@@ -233,7 +233,7 @@ export function clearMarksOnDiscontinuousInput(
} }
export function insertContent( export function insertContent(
editorHost: EditorHost, std: BlockStdScope,
model: BlockModel, model: BlockModel,
text: string, text: string,
attributes?: AffineTextAttributes attributes?: AffineTextAttributes
@@ -242,7 +242,7 @@ export function insertContent(
console.error("Can't insert text! Text not found"); console.error("Can't insert text! Text not found");
return; return;
} }
const inlineEditor = getInlineEditorByModel(editorHost, model); const inlineEditor = getInlineEditorByModel(std, model);
if (!inlineEditor) { if (!inlineEditor) {
console.error("Can't insert text! Inline editor not found"); console.error("Can't insert text! Inline editor not found");
return; return;

View File

@@ -18,14 +18,14 @@ export const textCommonKeymap = (
ArrowUp: () => { ArrowUp: () => {
const text = std.selection.find(TextSelection); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const inline = getInlineEditorByModel(std.host, text.from.blockId); const inline = getInlineEditorByModel(std, text.from.blockId);
if (!inline) return; if (!inline) return;
return !inline.isFirstLine(inline.getInlineRange()); return !inline.isFirstLine(inline.getInlineRange());
}, },
ArrowDown: () => { ArrowDown: () => {
const text = std.selection.find(TextSelection); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const inline = getInlineEditorByModel(std.host, text.from.blockId); const inline = getInlineEditorByModel(std, text.from.blockId);
if (!inline) return; if (!inline) return;
return !inline.isLastLine(inline.getInlineRange()); return !inline.isLastLine(inline.getInlineRange());
}, },

View File

@@ -28,7 +28,7 @@ export const bracketKeymap = (
if (!model) return; if (!model) return;
if (!matchModels(model, [CodeBlockModel])) return; if (!matchModels(model, [CodeBlockModel])) return;
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(
std.host, std,
textSelection.from.blockId textSelection.from.blockId
); );
if (!inlineEditor) return; if (!inlineEditor) return;
@@ -61,7 +61,7 @@ export const bracketKeymap = (
ctx.get('keyboardState').raw.preventDefault(); ctx.get('keyboardState').raw.preventDefault();
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(
std.host, std,
textSelection.from.blockId textSelection.from.blockId
); );
if (!inlineEditor) return; if (!inlineEditor) return;
@@ -107,7 +107,7 @@ export const bracketKeymap = (
ctx.get('keyboardState').raw.preventDefault(); ctx.get('keyboardState').raw.preventDefault();
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(
std.host, std,
textSelection.from.blockId textSelection.from.blockId
); );
if (!inlineEditor) return; if (!inlineEditor) return;

View File

@@ -29,7 +29,7 @@ export function markdownInput(
if (!id) return; if (!id) return;
const model = std.store.getBlock(id)?.model; const model = std.store.getBlock(id)?.model;
if (!model) return; if (!model) return;
const inline = getInlineEditorByModel(std.host, model); const inline = getInlineEditorByModel(std, model);
if (!inline) return; if (!inline) return;
const range = inline.getInlineRange(); const range = inline.getInlineRange();
if (!range) return; if (!range) return;

View File

@@ -1,5 +1,5 @@
import { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model'; import { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
import type { BlockComponent, EditorHost } from '@blocksuite/block-std'; import type { BlockComponent, BlockStdScope } from '@blocksuite/block-std';
import type { BlockModel, Store } from '@blocksuite/store'; import type { BlockModel, Store } from '@blocksuite/store';
import { matchModels } from './checker.js'; import { matchModels } from './checker.js';
@@ -23,16 +23,16 @@ export function findAncestorModel(
* *
*/ */
export async function asyncGetBlockComponent( export async function asyncGetBlockComponent(
editorHost: EditorHost, std: BlockStdScope,
id: string id: string
): Promise<BlockComponent | null> { ): Promise<BlockComponent | null> {
const rootBlockId = editorHost.doc.root?.id; const rootBlockId = std.store.root?.id;
if (!rootBlockId) return null; if (!rootBlockId) return null;
const rootComponent = editorHost.view.getBlock(rootBlockId); const rootComponent = std.view.getBlock(rootBlockId);
if (!rootComponent) return null; if (!rootComponent) return null;
await rootComponent.updateComplete; await rootComponent.updateComplete;
return editorHost.view.getBlock(id); return std.view.getBlock(id);
} }
export function findNoteBlockModel(model: BlockModel) { export function findNoteBlockModel(model: BlockModel) {

View File

@@ -37,7 +37,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: formatDate(now), description: formatDate(now),
group: '6_Date@0', group: '6_Date@0',
action: ({ std, model }) => { action: ({ std, model }) => {
insertContent(std.host, model, formatDate(now)); insertContent(std, model, formatDate(now));
}, },
}, },
{ {
@@ -49,7 +49,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
action: ({ std, model }) => { action: ({ std, model }) => {
const tomorrow = new Date(); const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setDate(tomorrow.getDate() + 1);
insertContent(std.host, model, formatDate(tomorrow)); insertContent(std, model, formatDate(tomorrow));
}, },
}, },
{ {
@@ -61,7 +61,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
action: ({ std, model }) => { action: ({ std, model }) => {
const yesterday = new Date(); const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1); yesterday.setDate(yesterday.getDate() - 1);
insertContent(std.host, model, formatDate(yesterday)); insertContent(std, model, formatDate(yesterday));
}, },
}, },
{ {
@@ -71,7 +71,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: formatTime(now), description: formatTime(now),
group: '6_Date@3', group: '6_Date@3',
action: ({ std, model }) => { action: ({ std, model }) => {
insertContent(std.host, model, formatTime(now)); insertContent(std, model, formatTime(now));
}, },
}, },
{ {

View File

@@ -56,7 +56,7 @@ export class SlashMenu extends WithDisposable(LitElement) {
// We must to do clean the slash string before we do the action // We must to do clean the slash string before we do the action
// Otherwise, the action may change the model and cause the slash string to be changed // Otherwise, the action may change the model and cause the slash string to be changed
cleanSpecifiedTail( cleanSpecifiedTail(
this.host, this.context.std,
this.context.model, this.context.model,
AFFINE_SLASH_MENU_TRIGGER_KEY + (this._query || '') AFFINE_SLASH_MENU_TRIGGER_KEY + (this._query || '')
); );
@@ -496,7 +496,7 @@ export class InnerSlashMenu extends WithDisposable(LitElement) {
}); });
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(
this.context.std.host, this.context.std,
this.context.model this.context.model
); );

View File

@@ -39,10 +39,7 @@ const showSlashMenu = debounce(
disposables.dispose() disposables.dispose()
); );
const inlineEditor = getInlineEditorByModel( const inlineEditor = getInlineEditorByModel(context.std, context.model);
context.std.host,
context.model
);
if (!inlineEditor) return; if (!inlineEditor) return;
const slashMenu = new SlashMenu(inlineEditor, abortController); const slashMenu = new SlashMenu(inlineEditor, abortController);
disposables.add(() => slashMenu.remove()); disposables.add(() => slashMenu.remove());
@@ -84,7 +81,7 @@ export class AffineSlashMenuWidget extends WidgetComponent {
const model = this.host.doc.getBlock(textSelection.blockId)?.model; const model = this.host.doc.getBlock(textSelection.blockId)?.model;
if (!model) return; if (!model) return;
return getInlineEditorByModel(this.host, model); return getInlineEditorByModel(this.std, model);
}; };
private readonly _handleInput = ( private readonly _handleInput = (