import { assertExists, isEqual } from '@blockeditor/global/utils'; import { KEYBOARD_ALLOW_DEFAULT, KEYBOARD_PREVENT_DEFAULT, } from '@blockeditor/inline'; import type { BlockElement } from '@blockeditor/lit'; import { asyncSetInlineRange, matchFlavours, } from '../../../../_common/utils/index.js'; import { getStandardLanguage } from '../../../../code-block/utils/code-languages.js'; import { FALLBACK_LANG } from '../../../../code-block/utils/consts.js'; import type { ParagraphBlockModel } from '../../../../paragraph-block/index.js'; import type { AffineInlineEditor } from '../../../inline/presets/affine-inline-specs.js'; import { convertToDivider, convertToList, convertToParagraph, } from './utils.js'; export function tryConvertBlock( element: BlockElement, inline: AffineInlineEditor, prefixText: string, range: { index: number; length: number } ) { const { model } = element; if ( !prefixText.match( /^(\d+\.|-|\*|\[ ?\]|\[x\]|(#){1,6}|(-){3}|(\*){3}|>|```([a-zA-Z0-9]*))$/ ) ) { return KEYBOARD_ALLOW_DEFAULT; } const [, offset] = inline.getLine(range.index); if (offset > prefixText.length) { return KEYBOARD_ALLOW_DEFAULT; } const isParagraph = matchFlavours(model, ['affine:paragraph']); const isHeading = isParagraph && model.type.startsWith('h'); const isParagraphQuoteBlock = isParagraph && isEqual(model.type, 'quote'); const isCodeBlock = matchFlavours(model, ['affine:code']); if (isHeading || isParagraphQuoteBlock || isCodeBlock) { return KEYBOARD_ALLOW_DEFAULT; } // try to add code block const codeMatch = prefixText.match(/^```([a-zA-Z0-9]*)$/g); if (codeMatch) { if ( model.flavour === 'affine:paragraph' && (model as ParagraphBlockModel).type === 'quote' ) { return KEYBOARD_ALLOW_DEFAULT; } const page = model.page; page.captureSync(); const parent = page.getParent(model); assertExists(parent); const index = parent.children.indexOf(model); const codeId = page.addBlock( 'affine:code', { language: getStandardLanguage(codeMatch[0].slice(3))?.id ?? FALLBACK_LANG, }, parent, index ); if (model.text && model.text.length > prefixText.length) { const text = model.text.clone(); page.addBlock('affine:paragraph', { text }, parent, index + 1); text.delete(0, prefixText.length); } page.deleteBlock(model, { bringChildrenTo: parent, }); const codeBlock = page.getBlockById(codeId); assertExists(codeBlock); asyncSetInlineRange(element.host, codeBlock, { index: 0, length: 0 }).catch( console.error ); return KEYBOARD_PREVENT_DEFAULT; } let isConverted = false; switch (prefixText.trim()) { case '[]': case '[ ]': isConverted = convertToList(element, 'todo', prefixText, { checked: false, }); break; case '[x]': isConverted = convertToList(element, 'todo', prefixText, { checked: true, }); break; case '-': case '*': isConverted = convertToList(element, 'bulleted', prefixText); break; case '***': case '---': isConverted = convertToDivider(element, prefixText); break; case '#': isConverted = convertToParagraph(element, 'h1', prefixText); break; case '##': isConverted = convertToParagraph(element, 'h2', prefixText); break; case '###': isConverted = convertToParagraph(element, 'h3', prefixText); break; case '####': isConverted = convertToParagraph(element, 'h4', prefixText); break; case '#####': isConverted = convertToParagraph(element, 'h5', prefixText); break; case '######': isConverted = convertToParagraph(element, 'h6', prefixText); break; case '>': isConverted = convertToParagraph(element, 'quote', prefixText); break; default: isConverted = convertToList(element, 'numbered', prefixText); } return isConverted ? KEYBOARD_PREVENT_DEFAULT : KEYBOARD_ALLOW_DEFAULT; }