import type { Command } from '@blockeditor/block-std'; import { assertExists } from '@blockeditor/global/utils'; import { INLINE_ROOT_ATTR, type InlineRootElement } from '@blockeditor/inline'; import type { BlockElement } from '@blockeditor/lit'; import { FORMAT_NATIVE_SUPPORT_FLAVOURS } from '../../../_common/configs/text-format/consts.js'; import { BLOCK_ID_ATTR } from '../../../_common/consts.js'; import type { AffineTextAttributes } from '../../../_common/inline/presets/affine-inline-specs.js'; import type { Flavour } from '../../../models.js'; // for native range export const formatNativeCommand: Command< 'host', never, { range?: Range; styles: AffineTextAttributes; mode?: 'replace' | 'merge'; } > = (ctx, next) => { const { host, styles, mode = 'merge' } = ctx; assertExists( host, '`host` is required, you need to use `withHost` command before adding this command to the pipeline.' ); let range = ctx.range; if (!range) { const selection = document.getSelection(); if (!selection || selection.rangeCount === 0) return; range = selection.getRangeAt(0); } if (!range) return; const selectedInlineEditors = Array.from( host.querySelectorAll(`[${INLINE_ROOT_ATTR}]`) ) .filter(el => range?.intersectsNode(el)) .filter(el => { const blockElement = el.closest(`[${BLOCK_ID_ATTR}]`); if (blockElement) { return FORMAT_NATIVE_SUPPORT_FLAVOURS.includes( blockElement.model.flavour as Flavour ); } return false; }) .map(el => el.inlineEditor); selectedInlineEditors.forEach(inlineEditor => { const inlineRange = inlineEditor.getInlineRange(); if (!inlineRange) return; inlineEditor.formatText(inlineRange, styles, { mode, }); }); next(); }; declare global { namespace BlockEditor { interface Commands { formatNative: typeof formatNativeCommand; } } }