diff --git a/src/assets/styles/prosemirror.scss b/src/assets/styles/prosemirror.scss index 8c347349..88d6ba3d 100644 --- a/src/assets/styles/prosemirror.scss +++ b/src/assets/styles/prosemirror.scss @@ -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; diff --git a/src/configs/hotkey.ts b/src/configs/hotkey.ts index d610561a..c71b4794 100644 --- a/src/configs/hotkey.ts +++ b/src/configs/hotkey.ts @@ -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` }, ], }, ] \ No newline at end of file diff --git a/src/utils/prosemirror/plugins/inputrules.ts b/src/utils/prosemirror/plugins/inputrules.ts index 0c6d347d..6ec8c57a 100644 --- a/src/utils/prosemirror/plugins/inputrules.ts +++ b/src/utils/prosemirror/plugins/inputrules.ts @@ -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 }) diff --git a/src/utils/prosemirror/plugins/keymap.ts b/src/utils/prosemirror/plugins/keymap.ts index 234a92df..b4370453 100644 --- a/src/utils/prosemirror/plugins/keymap.ts +++ b/src/utils/prosemirror/plugins/keymap.ts @@ -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, diff --git a/src/utils/prosemirror/schema/marks.ts b/src/utils/prosemirror/schema/marks.ts index 7b9b105f..a469b7d2 100644 --- a/src/utils/prosemirror/schema/marks.ts +++ b/src/utils/prosemirror/schema/marks.ts @@ -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, diff --git a/src/utils/prosemirror/schema/nodes.ts b/src/utils/prosemirror/schema/nodes.ts index 082d8afa..00d2608a 100644 --- a/src/utils/prosemirror/schema/nodes.ts +++ b/src/utils/prosemirror/schema/nodes.ts @@ -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, diff --git a/src/views/components/element/ProsemirrorEditor.vue b/src/views/components/element/ProsemirrorEditor.vue index 2ac75883..52622f41 100644 --- a/src/views/components/element/ProsemirrorEditor.vue +++ b/src/views/components/element/ProsemirrorEditor.vue @@ -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)