mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-24 04:27:27 +08:00
fix(editor): support markdown transform when using IME (#12778)
Fix #12284 Close [BS-3517](https://linear.app/affine-design/issue/BS-3517/微软新注音输入法无法使用markdown语法) This PR refactor the markdown transform during inputting, including: - Transfrom markdown syntax input in `inlineEditor.slots.inputting`, where we can detect the space character inputed by IME like Microsoft Bopomofo, but `keydown` event can't. - Remove `markdown-input.ts` which was used in `KeymapExtension` of paragraph, and refactor with `InlineMarkdownExtension` - Adjust existing `InlineMarkdownExtension` since the space is included in text. - Add two `InlineMarkdownExtension` for paragraph and list to impl Heading1-6, number, bullet, to-do list conversion. Other changes: - Improve type hint for parameter of `store.addBlock` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Added markdown shortcuts for creating code blocks and dividers in the rich text editor. - Introduced enhanced paragraph markdown support for headings and blockquotes with inline markdown patterns. - Integrated new list markdown extension supporting numbered, bulleted, and todo lists with checked states. - **Improvements** - Updated markdown formatting patterns to require trailing spaces for links, LaTeX, and inline styles, improving detection accuracy. - Markdown transformations now respond to input events instead of keydown for smoother editing experience. - Added focus management after markdown transformations to maintain seamless editing flow. - **Bug Fixes** - Removed unnecessary prevention of default behavior on space and shift-space key presses in list and paragraph editors. - **Refactor** - Enhanced event handling and typing for editor input events, improving reliability and maintainability. - Refined internal prefix text extraction logic for markdown processing. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { textKeymap } from '@blocksuite/affine-inline-preset';
|
||||
import { ListBlockSchema } from '@blocksuite/affine-model';
|
||||
import { markdownInput } from '@blocksuite/affine-rich-text';
|
||||
import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands';
|
||||
import { IS_MAC } from '@blocksuite/global/env';
|
||||
import { KeymapExtension, TextSelection } from '@blocksuite/std';
|
||||
@@ -125,20 +124,6 @@ export const ListKeymapExtension = KeymapExtension(
|
||||
ctx.get('keyboardState').raw.preventDefault();
|
||||
return true;
|
||||
},
|
||||
Space: ctx => {
|
||||
if (!markdownInput(std)) {
|
||||
return;
|
||||
}
|
||||
ctx.get('keyboardState').raw.preventDefault();
|
||||
return true;
|
||||
},
|
||||
'Shift-Space': ctx => {
|
||||
if (!markdownInput(std)) {
|
||||
return;
|
||||
}
|
||||
ctx.get('keyboardState').raw.preventDefault();
|
||||
return true;
|
||||
},
|
||||
};
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
type ListBlockModel,
|
||||
ListBlockSchema,
|
||||
type ListType,
|
||||
ParagraphBlockModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { focusTextModel } from '@blocksuite/affine-rich-text';
|
||||
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
|
||||
import { matchModels, toNumberedList } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockComponent } from '@blocksuite/std';
|
||||
import { InlineMarkdownExtension } from '@blocksuite/std/inline';
|
||||
|
||||
export const ListMarkdownExtension =
|
||||
InlineMarkdownExtension<AffineTextAttributes>({
|
||||
name: 'list',
|
||||
// group 2: number
|
||||
// group 3: bullet
|
||||
// group 4: bullet
|
||||
// group 5: todo
|
||||
// group 6: todo checked
|
||||
pattern: /^((\d+\.)|(-)|(\*)|(\[ ?\])|(\[x\]))\s$/,
|
||||
action: ({ inlineEditor, pattern, inlineRange, prefixText }) => {
|
||||
if (inlineEditor.yTextString.slice(0, inlineRange.index).includes('\n')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = prefixText.match(pattern);
|
||||
if (!match) return;
|
||||
|
||||
let type: ListType;
|
||||
|
||||
if (match[2]) {
|
||||
type = 'numbered';
|
||||
} else if (match[3] || match[4]) {
|
||||
type = 'bulleted';
|
||||
} else if (match[5] || match[6]) {
|
||||
type = 'todo';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const checked = match[6] !== undefined;
|
||||
|
||||
if (!inlineEditor.rootElement) return;
|
||||
const blockComponent =
|
||||
inlineEditor.rootElement.closest<BlockComponent>('[data-block-id]');
|
||||
if (!blockComponent) return;
|
||||
|
||||
const { model, std, store } = blockComponent;
|
||||
if (!matchModels(model, [ParagraphBlockModel])) return;
|
||||
|
||||
if (type !== 'numbered') {
|
||||
const parent = store.getParent(model);
|
||||
if (!parent) return;
|
||||
const index = parent.children.indexOf(model);
|
||||
|
||||
store.captureSync();
|
||||
inlineEditor.deleteText({
|
||||
index: 0,
|
||||
length: inlineRange.index,
|
||||
});
|
||||
const id = store.addBlock<ListBlockModel>(
|
||||
ListBlockSchema.model.flavour,
|
||||
{
|
||||
type: type,
|
||||
text: model.text?.clone(),
|
||||
children: model.children,
|
||||
...(type === 'todo' ? { checked } : {}),
|
||||
},
|
||||
parent,
|
||||
index
|
||||
);
|
||||
store.deleteBlock(model, { deleteChildren: false });
|
||||
focusTextModel(std, id);
|
||||
} else {
|
||||
let order = parseInt(match[2]);
|
||||
if (!Number.isInteger(order)) order = 1;
|
||||
|
||||
store.captureSync();
|
||||
inlineEditor.deleteText({
|
||||
index: 0,
|
||||
length: inlineRange.index,
|
||||
});
|
||||
|
||||
const id = toNumberedList(std, model, order);
|
||||
if (!id) return;
|
||||
|
||||
focusTextModel(std, id);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { effects } from './effects.js';
|
||||
import { ListKeymapExtension, ListTextKeymapExtension } from './list-keymap.js';
|
||||
import { ListMarkdownExtension } from './markdown.js';
|
||||
|
||||
export class ListViewExtension extends ViewExtensionProvider {
|
||||
override name = 'affine-list-block';
|
||||
@@ -23,6 +24,7 @@ export class ListViewExtension extends ViewExtensionProvider {
|
||||
BlockViewExtension('affine:list', literal`affine-list`),
|
||||
ListKeymapExtension,
|
||||
ListTextKeymapExtension,
|
||||
ListMarkdownExtension,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user