perf: 文本框富文本编辑优化

This commit is contained in:
pipipi-pikachu 2023-08-12 12:05:04 +08:00
parent 4f9db4781a
commit 6c1c8237b0
7 changed files with 54 additions and 18 deletions

View File

@ -47,6 +47,15 @@
font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
}
sup {
vertical-align: super;
font-size: smaller;
}
sub {
vertical-align: sub;
font-size: smaller;
}
blockquote {
overflow: hidden;
padding-right: 1.2em;

View File

@ -35,6 +35,7 @@ export const HOTKEY_DOC = [
{ label: '剪切', value: 'Ctrl + X' },
{ label: '复制', value: 'Ctrl + C' },
{ label: '粘贴', value: 'Ctrl + V' },
{ label: '粘贴为纯文本', value: 'Ctrl + Shift + V' },
{ label: '快速复制粘贴', value: 'Ctrl + D' },
{ label: '全选', value: 'Ctrl + A' },
{ label: '撤销', value: 'Ctrl + Z' },
@ -108,7 +109,10 @@ export const HOTKEY_DOC = [
{ label: '加粗', value: 'Ctrl + B' },
{ label: '斜体', value: 'Ctrl + I' },
{ label: '下划线', value: 'Ctrl + U' },
{ label: '删除线', value: 'Ctrl + D' },
{ label: '行内代码', value: 'Ctrl + E' },
{ label: '上角标', value: 'Ctrl + ;' },
{ label: '下角标', value: `Ctrl + '` },
{ label: '选中段落', value: `ESC` },
],
},
]

View File

@ -2,7 +2,6 @@ import type { NodeType, Schema } from 'prosemirror-model'
import {
inputRules,
wrappingInputRule,
textblockTypeInputRule,
smartQuotes,
emDash,
ellipsis,
@ -22,7 +21,18 @@ const orderedListRule = (nodeType: NodeType) => (
const bulletListRule = (nodeType: NodeType) => wrappingInputRule(/^\s*([-+*])\s$/, nodeType)
const codeBlockRule = (nodeType: NodeType) => textblockTypeInputRule(/^```$/, nodeType)
const codeRule = () => {
const inputRegex = /(?:^|\s)((?:`)((?:[^`]+))(?:`))$/
return new InputRule(inputRegex, (state, match, start, end) => {
const { schema } = state
const tr = state.tr.insertText(`${match[2]} `, start, end)
const mark = schema.marks.code.create()
return tr.addMark(start, start + match[2].length, mark)
})
}
const linkRule = () => {
const urlRegEx = /(?:https?:\/\/)?[\w-]+(?:\.[\w-]+)+\.?(?:\d+)?(?:\/\S*)?$/
@ -46,7 +56,7 @@ export const buildInputRules = (schema: Schema) => {
rules.push(blockQuoteRule(schema.nodes.blockquote))
rules.push(orderedListRule(schema.nodes.ordered_list))
rules.push(bulletListRule(schema.nodes.bullet_list))
rules.push(codeBlockRule(schema.nodes.code_block))
rules.push(codeRule())
rules.push(linkRule())
return inputRules({ rules })

View File

@ -25,14 +25,17 @@ export const buildKeymap = (schema: Schema) => {
bind('Alt-ArrowUp', joinUp)
bind('Alt-ArrowDown', joinDown)
bind('Ctrl-z', undo)
bind('Ctrl-y', redo)
bind('Mod-z', undo)
bind('Mod-y', redo)
bind('Backspace', undoInputRule)
bind('Escape', selectParentNode)
bind('Ctrl-b', toggleMark(schema.marks.strong))
bind('Ctrl-i', toggleMark(schema.marks.em))
bind('Ctrl-u', toggleMark(schema.marks.underline))
bind('Ctrl-d', toggleMark(schema.marks.strikethrough))
bind('Mod-b', toggleMark(schema.marks.strong))
bind('Mod-i', toggleMark(schema.marks.em))
bind('Mod-u', toggleMark(schema.marks.underline))
bind('Mod-d', toggleMark(schema.marks.strikethrough))
bind('Mod-e', toggleMark(schema.marks.code))
bind('Mod-;', toggleMark(schema.marks.superscript))
bind(`Mod-'`, toggleMark(schema.marks.subscript))
bind('Enter', chainCommands(
splitListItem(schema.nodes.list_item),
newlineInCode,

View File

@ -157,10 +157,14 @@ const link: MarkSpec = {
toDOM: node => ['a', node.attrs, 0],
}
const { em, strong, code } = marks
export default {
...marks,
em,
strong,
fontsize,
fontname,
code,
forecolor,
backcolor,
subscript,

View File

@ -137,12 +137,16 @@ const paragraph: NodeSpec = {
},
}
// https://github.com/pipipi-pikachu/PPTist/issues/134
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { hard_break, ...otherNodes } = nodes
const {
doc,
blockquote,
text,
} = nodes
export default {
...otherNodes,
doc,
text,
blockquote,
'ordered_list': orderedList,
'bullet_list': bulletList,
'list_item': listItem,

View File

@ -13,9 +13,9 @@ import { debounce } from 'lodash'
import { storeToRefs } from 'pinia'
import { useMainStore } from '@/store'
import type { EditorView } from 'prosemirror-view'
import { toggleMark, wrapIn } from 'prosemirror-commands'
import { toggleMark, wrapIn, lift } from 'prosemirror-commands'
import { initProsemirrorEditor, createDocument } from '@/utils/prosemirror'
import { findNodesWithSameMark, getTextAttrs, autoSelectAll, addMark, markActive, getFontsize } from '@/utils/prosemirror/utils'
import { isActiveOfParentNodeType, findNodesWithSameMark, getTextAttrs, autoSelectAll, addMark, markActive, getFontsize } from '@/utils/prosemirror/utils'
import emitter, { EmitterEvents, type RichTextAction, type RichTextCommand } from '@/utils/emitter'
import { alignmentCommand } from '@/utils/prosemirror/commands/setTextAlign'
import { indentCommand, textIndentCommand } from '@/utils/prosemirror/commands/setTextIndent'
@ -164,7 +164,9 @@ const execCommand = ({ target, action }: RichTextCommand) => {
toggleMark(editorView.state.schema.marks.superscript)(editorView.state, editorView.dispatch)
}
else if (item.command === 'blockquote') {
wrapIn(editorView.state.schema.nodes.blockquote)(editorView.state, editorView.dispatch)
const isBlockquote = isActiveOfParentNodeType('blockquote', editorView.state)
if (isBlockquote) lift(editorView.state, editorView.dispatch)
else wrapIn(editorView.state.schema.nodes.blockquote)(editorView.state, editorView.dispatch)
}
else if (item.command === 'code') {
toggleMark(editorView.state.schema.marks.code)(editorView.state, editorView.dispatch)