restructure commands

This commit is contained in:
Philipp Kühn
2020-11-18 16:43:27 +01:00
parent 03f009d5f0
commit 3cd6c55279
37 changed files with 214 additions and 231 deletions

View File

@@ -70,12 +70,12 @@ editor
Both calls would return `true` if its possible to apply the commands, and `false` in case its not.
### Try commands
If you want to run a list of commands, but want only the first successful command to be applied, you can do this with the `.try()` method. This method runs one command after the other and stops at the first which returns `true`.
If you want to run a list of commands, but want only the first successful command to be applied, you can do this with the `.first()` method. This method runs one command after the other and stops at the first which returns `true`.
For example, the backspace key tries to undo an input rule first. If that was successful, it stops there. If no input rule has been applied and thus cant be reverted, it runs the next command and deletes the selection, if there is one. Here is the simplified example:
```js
editor.try(({ commands }) => [
editor.first(({ commands }) => [
() => commands.undoInputRule(),
() => commands.deleteSelection(),
// …
@@ -85,7 +85,7 @@ editor.try(({ commands }) => [
Inside of commands you can do the same thing like that:
```js
commands.try([
commands.first([
() => commands.undoInputRule(),
() => commands.deleteSelection(),
// …

View File

@@ -3,7 +3,10 @@ import { Command } from '../types'
import getMarkType from '../utils/getMarkType'
import getMarkAttributes from '../utils/getMarkAttributes'
export default (typeOrName: string | MarkType, attributes?: {}): Command => ({ tr, state, dispatch }) => {
/**
* Add a mark with new attributes.
*/
export const addMark = (typeOrName: string | MarkType, attributes?: {}): Command => ({ tr, state, dispatch }) => {
const { selection } = tr
const { from, to, empty } = selection
const type = getMarkType(typeOrName, state.schema)

View File

@@ -1,6 +1,9 @@
import { Command } from '../types'
export default (): Command => ({ view }) => {
/**
* Removes focus from the editor.
*/
export const blur = (): Command => ({ view }) => {
const element = view.dom as HTMLElement
element.blur()

View File

@@ -1,5 +1,8 @@
import { Command } from '../types'
export default (emitUpdate: Boolean = false): Command => ({ commands }) => {
/**
* Clear the whole document.
*/
export const clearContent = (emitUpdate: Boolean = false): Command => ({ commands }) => {
return commands.setContent('', emitUpdate)
}

View File

@@ -1,7 +1,10 @@
import { liftTarget } from 'prosemirror-transform'
import { Command } from '../types'
export default (): Command => ({ state, tr, dispatch }) => {
/**
* Normalize nodes to a simple paragraph.
*/
export const clearNodes = (): Command => ({ state, tr, dispatch }) => {
const { selection } = tr
const { from, to } = selection

View File

@@ -1,5 +1,8 @@
import { Command } from '../types'
export default (fn: (props: Parameters<Command>[0]) => boolean): Command => props => {
/**
* Define a command inline.
*/
export const command = (fn: (props: Parameters<Command>[0]) => boolean): Command => props => {
return fn(props)
}

View File

@@ -1,6 +1,9 @@
import { deleteSelection } from 'prosemirror-commands'
import { deleteSelection as originalDeleteSelection } from 'prosemirror-commands'
import { Command } from '../types'
export default (): Command => ({ state, dispatch }) => {
return deleteSelection(state, dispatch)
/**
* Delete the selection, if there is one.
*/
export const deleteSelection = (): Command => ({ state, dispatch }) => {
return originalDeleteSelection(state, dispatch)
}

View File

@@ -4,7 +4,10 @@ import { Command } from '../types'
import getMarkType from '../utils/getMarkType'
import getMarkRange from '../utils/getMarkRange'
export default (typeOrName: string | MarkType): Command => ({ tr, state, dispatch }) => {
/**
* Extends the text selection to the current mark.
*/
export const extendMarkRange = (typeOrName: string | MarkType): Command => ({ tr, state, dispatch }) => {
const type = getMarkType(typeOrName, state.schema)
const { doc, selection } = tr
const { $from, empty } = selection

View File

@@ -1,6 +1,9 @@
import { Command } from '../types'
export default (commands: Command[] | ((props: Parameters<Command>[0]) => Command[])): Command => props => {
/**
* Runs one command after the other and stops at the first which returns true.
*/
export const first = (commands: Command[] | ((props: Parameters<Command>[0]) => Command[])): Command => props => {
const items = typeof commands === 'function'
? commands(props)
: commands

View File

@@ -35,7 +35,10 @@ function resolveSelection(editor: Editor, position: FocusPosition = null): Resol
}
}
export default (position: FocusPosition = null): Command => ({
/**
* Focus the editor at the given position.
*/
export const focus = (position: FocusPosition = null): Command => ({
editor,
view,
tr,

View File

@@ -17,7 +17,10 @@ function selectionToInsertionEnd(tr: Transaction, startLen: number, bias: number
tr.setSelection(Selection.near(tr.doc.resolve(end as unknown as number), bias))
}
export default (value: string): Command => ({ tr, state, dispatch }) => {
/**
* Insert a string of HTML at the current position.
*/
export const insertHTML = (value: string): Command => ({ tr, state, dispatch }) => {
const { selection } = tr
const element = elementFromString(value)
const slice = DOMParser.fromSchema(state.schema).parseSlice(element)

View File

@@ -1,6 +1,9 @@
import { Command } from '../types'
export default (value: string): Command => ({ tr, dispatch }) => {
/**
* Insert a string of text at the current position.
*/
export const insertText = (value: string): Command => ({ tr, dispatch }) => {
if (dispatch) {
tr.insertText(value)
}

View File

@@ -1,10 +1,13 @@
import { lift } from 'prosemirror-commands'
import { lift as originalLift } from 'prosemirror-commands'
import { NodeType } from 'prosemirror-model'
import { Command } from '../types'
import nodeIsActive from '../utils/nodeIsActive'
import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => {
/**
* Removes an existing wrap.
*/
export const lift = (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
const isActive = nodeIsActive(state, type, attributes)
@@ -12,5 +15,5 @@ export default (typeOrName: string | NodeType, attributes = {}): Command => ({ s
return false
}
return lift(state, dispatch)
return originalLift(state, dispatch)
}

View File

@@ -1,10 +1,13 @@
import { liftListItem } from 'prosemirror-schema-list'
import { liftListItem as originalLiftListItem } from 'prosemirror-schema-list'
import { NodeType } from 'prosemirror-model'
import { Command } from '../types'
import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType): Command => ({ state, dispatch }) => {
/**
* Lift the list item into a wrapping list.
*/
export const liftListItem = (typeOrName: string | NodeType): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
return liftListItem(type)(state, dispatch)
return originalLiftListItem(type)(state, dispatch)
}

View File

@@ -3,7 +3,10 @@ import { Command } from '../types'
import getMarkType from '../utils/getMarkType'
import getMarkRange from '../utils/getMarkRange'
export default (typeOrName: string | MarkType): Command => ({ tr, state, dispatch }) => {
/**
* Remove all marks in the current selection.
*/
export const removeMark = (typeOrName: string | MarkType): Command => ({ tr, state, dispatch }) => {
const { selection } = tr
const type = getMarkType(typeOrName, state.schema)
let { from, to } = selection

View File

@@ -1,6 +1,9 @@
import { Command } from '../types'
export default (): Command => ({ tr, state, dispatch }) => {
/**
* Remove all marks in the current selection.
*/
export const removeMarks = (): Command => ({ tr, state, dispatch }) => {
const { selection } = tr
const { from, to, empty } = selection

View File

@@ -1,18 +0,0 @@
import { NodeType } from 'prosemirror-model'
import getNodeType from '../utils/getNodeType'
import deleteProps from '../utils/deleteProps'
import { Command } from '../types'
export default (typeOrName: string | NodeType, attributes: string[]): Command => ({ tr, state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
const { selection } = tr
const { from, to } = selection
state.doc.nodesBetween(from, to, (node, pos) => {
if (node.type === type && dispatch) {
tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes))
}
})
return true
}

View File

@@ -3,7 +3,10 @@ import getNodeType from '../utils/getNodeType'
import deleteProps from '../utils/deleteProps'
import { Command } from '../types'
export default (typeOrName: string | NodeType, attributes: string | string[]): Command => ({ tr, state, dispatch }) => {
/**
* Resets node attributes to the default value.
*/
export const resetNodeAttributes = (typeOrName: string | NodeType, attributes: string | string[]): Command => ({ tr, state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
const { selection } = tr
const { from, to } = selection

View File

@@ -1,6 +1,9 @@
import { Command } from '../types'
export default (): Command => ({ tr, dispatch }) => {
/**
* Scroll the selection into view.
*/
export const scrollIntoView = (): Command => ({ tr, dispatch }) => {
if (dispatch) {
tr.scrollIntoView()
}

View File

@@ -1,6 +1,9 @@
import { selectAll } from 'prosemirror-commands'
import { selectAll as originalSelectAll } from 'prosemirror-commands'
import { Command } from '../types'
export default (): Command => ({ state, dispatch }) => {
return selectAll(state, dispatch)
/**
* Select the whole document.
*/
export const selectAll = (): Command => ({ state, dispatch }) => {
return originalSelectAll(state, dispatch)
}

View File

@@ -1,6 +1,9 @@
import { selectParentNode } from 'prosemirror-commands'
import { selectParentNode as originalSelectParentNode } from 'prosemirror-commands'
import { Command } from '../types'
export default (): Command => ({ state, dispatch }) => {
return selectParentNode(state, dispatch)
/**
* Select the parent node.
*/
export const selectParentNode = (): Command => ({ state, dispatch }) => {
return originalSelectParentNode(state, dispatch)
}

View File

@@ -1,10 +1,13 @@
import { NodeType } from 'prosemirror-model'
import { setBlockType } from 'prosemirror-commands'
import { setBlockType as originalSetBlockType } from 'prosemirror-commands'
import { Command } from '../types'
import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attrs = {}): Command => ({ state, dispatch }) => {
/**
* Replace a given range with a node.
*/
export const setBlockType = (typeOrName: string | NodeType, attrs = {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
return setBlockType(type, attrs)(state, dispatch)
return originalSetBlockType(type, attrs)(state, dispatch)
}

View File

@@ -1,7 +1,10 @@
import { TextSelection } from 'prosemirror-state'
import { Command } from '../types'
export default (content: string, emitUpdate: Boolean = false, parseOptions = {}): Command => ({ tr, editor, dispatch }) => {
/**
* Replace the whole document with new content.
*/
export const setContent = (content: string, emitUpdate: Boolean = false, parseOptions = {}): Command => ({ tr, editor, dispatch }) => {
const { createDocument } = editor
const { doc } = tr
const document = createDocument(content, parseOptions)

View File

@@ -3,7 +3,10 @@ import { NodeType } from 'prosemirror-model'
import { Command } from '../types'
import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType): Command => ({ state, dispatch }) => {
/**
* Sink the list item down into an inner list.
*/
export const sinkListItem = (typeOrName: string | NodeType): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
return originalSinkListItem(type)(state, dispatch)

View File

@@ -28,7 +28,10 @@ function keepMarks(state: EditorState) {
}
}
export default (options: Partial<SplitBlockOptions> = {}): Command => ({ tr, state, dispatch }) => {
/**
* Forks a new node from an existing node.
*/
export const splitBlock = (options: Partial<SplitBlockOptions> = {}): Command => ({ tr, state, dispatch }) => {
const defaultOptions: SplitBlockOptions = {
withAttributes: false,
withMarks: true,

View File

@@ -1,10 +1,13 @@
import { splitListItem } from 'prosemirror-schema-list'
import { splitListItem as originalSplitListItem } from 'prosemirror-schema-list'
import { NodeType } from 'prosemirror-model'
import { Command } from '../types'
import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType): Command => ({ state, dispatch }) => {
/**
* Splits one list item into two list items.
*/
export const splitListItem = (typeOrName: string | NodeType): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
return splitListItem(type)(state, dispatch)
return originalSplitListItem(type)(state, dispatch)
}

View File

@@ -3,7 +3,10 @@ import { Command } from '../types'
import nodeIsActive from '../utils/nodeIsActive'
import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, toggleTypeOrName: string | NodeType, attrs = {}): Command => ({ state, commands }) => {
/**
* Toggle a node with another node.
*/
export const toggleBlockType = (typeOrName: string | NodeType, toggleTypeOrName: string | NodeType, attrs = {}): Command => ({ state, commands }) => {
const type = getNodeType(typeOrName, state.schema)
const toggleType = getNodeType(toggleTypeOrName, state.schema)
const isActive = nodeIsActive(state, type, attrs)

View File

@@ -4,7 +4,10 @@ import { Command } from '../types'
import getNodeType from '../utils/getNodeType'
import isList from '../utils/isList'
export default (listTypeOrName: string | NodeType, itemTypeOrName: string | NodeType): Command => ({
/**
* Toggle between different list types.
*/
export const toggleList = (listTypeOrName: string | NodeType, itemTypeOrName: string | NodeType): Command => ({
editor, tr, state, dispatch, chain, commands, can,
}) => {
const { extensions } = editor.options

View File

@@ -1,10 +1,13 @@
import { toggleMark } from 'prosemirror-commands'
import { toggleMark as originalToggleMark } from 'prosemirror-commands'
import { MarkType } from 'prosemirror-model'
import { Command } from '../types'
import getMarkType from '../utils/getMarkType'
import markIsActive from '../utils/markIsActive'
export default (typeOrName: string | MarkType, attributes?: {}): Command => ({ state, dispatch, commands }) => {
/**
* Toggle a mark on and off.
*/
export const toggleMark = (typeOrName: string | MarkType, attributes?: {}): Command => ({ state, dispatch, commands }) => {
const type = getMarkType(typeOrName, state.schema)
const hasMarkWithDifferentAttributes = attributes
@@ -15,5 +18,5 @@ export default (typeOrName: string | MarkType, attributes?: {}): Command => ({ s
return commands.addMark(type, attributes)
}
return toggleMark(type, attributes)(state, dispatch)
return originalToggleMark(type, attributes)(state, dispatch)
}

View File

@@ -4,7 +4,10 @@ import { Command } from '../types'
import nodeIsActive from '../utils/nodeIsActive'
import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => {
/**
* Wraps nodes in another node, or removes an existing wrap.
*/
export const toggleWrap = (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
const isActive = nodeIsActive(state, type, attributes)

View File

@@ -2,7 +2,10 @@ import { NodeType } from 'prosemirror-model'
import getNodeType from '../utils/getNodeType'
import { Command } from '../types'
export default (typeOrName: string | NodeType, attributes: {}): Command => ({ tr, state, dispatch }) => {
/**
* Update attributes of a node.
*/
export const updateNodeAttributes = (typeOrName: string | NodeType, attributes: {}): Command => ({ tr, state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
const { selection } = tr
const { from, to } = selection

View File

@@ -1,10 +1,13 @@
import { wrapIn } from 'prosemirror-commands'
import { wrapIn as originalWrapIn } from 'prosemirror-commands'
import { NodeType } from 'prosemirror-model'
import { Command } from '../types'
import nodeIsActive from '../utils/nodeIsActive'
import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => {
/**
* Wraps nodes in another node.
*/
export const wrapIn = (typeOrName: string | NodeType, attributes = {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
const isActive = nodeIsActive(state, type, attributes)
@@ -12,5 +15,5 @@ export default (typeOrName: string | NodeType, attributes = {}): Command => ({ s
return false
}
return wrapIn(type, attributes)(state, dispatch)
return originalWrapIn(type, attributes)(state, dispatch)
}

View File

@@ -1,10 +1,13 @@
import { wrapInList } from 'prosemirror-schema-list'
import { wrapInList as originalWrapInList } from 'prosemirror-schema-list'
import { NodeType } from 'prosemirror-model'
import { Command } from '../types'
import getNodeType from '../utils/getNodeType'
export default (typeOrName: string | NodeType, attrs?: {}): Command => ({ state, dispatch }) => {
/**
* Wrap a node in a list.
*/
export const wrapInList = (typeOrName: string | NodeType, attrs?: {}): Command => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema)
return wrapInList(type, attrs)(state, dispatch)
return originalWrapInList(type, attrs)(state, dispatch)
}

View File

@@ -1,163 +1,70 @@
import { Extension } from '../Extension'
import addMark from '../commands/addMark'
import blur from '../commands/blur'
import clearContent from '../commands/clearContent'
import command from '../commands/command'
import clearNodes from '../commands/clearNodes'
import deleteSelection from '../commands/deleteSelection'
import extendMarkRange from '../commands/extendMarkRange'
import focus from '../commands/focus'
import insertHTML from '../commands/insertHTML'
import insertText from '../commands/insertText'
import lift from '../commands/lift'
import liftListItem from '../commands/liftListItem'
import removeMark from '../commands/removeMark'
import removeMarks from '../commands/removeMarks'
import resetNodeAttributes from '../commands/resetNodeAttributes'
import scrollIntoView from '../commands/scrollIntoView'
import selectAll from '../commands/selectAll'
import selectParentNode from '../commands/selectParentNode'
import setBlockType from '../commands/setBlockType'
import setContent from '../commands/setContent'
import sinkListItem from '../commands/sinkListItem'
import splitBlock from '../commands/splitBlock'
import splitListItem from '../commands/splitListItem'
import toggleBlockType from '../commands/toggleBlockType'
import toggleList from '../commands/toggleList'
import toggleMark from '../commands/toggleMark'
import toggleWrap from '../commands/toggleWrap'
import tryCommand from '../commands/try'
import updateNodeAttributes from '../commands/updateNodeAttributes'
import wrapIn from '../commands/wrapIn'
import wrapInList from '../commands/wrapInList'
import * as addMark from '../commands/addMark'
import * as blur from '../commands/blur'
import * as clearContent from '../commands/clearContent'
import * as command from '../commands/command'
import * as clearNodes from '../commands/clearNodes'
import * as deleteSelection from '../commands/deleteSelection'
import * as extendMarkRange from '../commands/extendMarkRange'
import * as focus from '../commands/focus'
import * as insertHTML from '../commands/insertHTML'
import * as insertText from '../commands/insertText'
import * as lift from '../commands/lift'
import * as liftListItem from '../commands/liftListItem'
import * as removeMark from '../commands/removeMark'
import * as removeMarks from '../commands/removeMarks'
import * as resetNodeAttributes from '../commands/resetNodeAttributes'
import * as scrollIntoView from '../commands/scrollIntoView'
import * as selectAll from '../commands/selectAll'
import * as selectParentNode from '../commands/selectParentNode'
import * as setBlockType from '../commands/setBlockType'
import * as setContent from '../commands/setContent'
import * as sinkListItem from '../commands/sinkListItem'
import * as splitBlock from '../commands/splitBlock'
import * as splitListItem from '../commands/splitListItem'
import * as toggleBlockType from '../commands/toggleBlockType'
import * as toggleList from '../commands/toggleList'
import * as toggleMark from '../commands/toggleMark'
import * as toggleWrap from '../commands/toggleWrap'
import * as first from '../commands/first'
import * as updateNodeAttributes from '../commands/updateNodeAttributes'
import * as wrapIn from '../commands/wrapIn'
import * as wrapInList from '../commands/wrapInList'
export const Commands = Extension.create({
addCommands() {
return {
/**
* Add a mark with new attributes.
*/
addMark,
/**
* Removes focus from the editor.
*/
blur,
/**
* Clear the whole document.
*/
clearContent,
/**
* Normalize nodes to a simple paragraph.
*/
clearNodes,
/**
* Define a command inline.
*/
command,
/**
* Delete the selection, if there is one.
*/
deleteSelection,
/**
* Extends the text selection to the current mark.
*/
extendMarkRange,
/**
* Focus the editor at the given position.
*/
focus,
/**
* Insert a string of HTML at the current position.
*/
insertHTML,
/**
* Insert a string of text at the current position.
*/
insertText,
/**
* Removes an existing wrap.
*/
lift,
/**
* Lift the list item into a wrapping list.
*/
liftListItem,
/**
* Remove all marks in the current selection.
*/
removeMark,
/**
* Remove all marks in the current selection.
*/
removeMarks,
/**
* Resets node attributes to the default value.
*/
resetNodeAttributes,
/**
* Scroll the selection into view.
*/
scrollIntoView,
/**
* Select the whole document.
*/
selectAll,
/**
* Select the parent node.
*/
selectParentNode,
/**
* Replace a given range with a node.
*/
setBlockType,
/**
* Replace the whole document with new content.
*/
setContent,
/**
* Sink the list item down into an inner list.
*/
sinkListItem,
/**
* Forks a new node from an existing node.
*/
splitBlock,
/**
* Splits one list item into two list items.
*/
splitListItem,
/**
* Toggle a node with another node.
*/
toggleBlockType,
/**
* Toggle between different list types.
*/
toggleList,
/**
* Toggle a mark on and off.
*/
toggleMark,
/**
* Wraps nodes in another node, or removes an existing wrap.
*/
toggleWrap,
/**
* Runs one command after the other and stops at the first which returns true.
*/
try: tryCommand,
/**
* Update attributes of a node.
*/
updateNodeAttributes,
/**
* Wraps nodes in another node.
*/
wrapIn,
/**
* Wrap a node in a list.
*/
wrapInList,
...addMark,
...blur,
...clearContent,
...clearNodes,
...command,
...deleteSelection,
...extendMarkRange,
...first,
...focus,
...insertHTML,
...insertText,
...lift,
...liftListItem,
...removeMark,
...removeMarks,
...resetNodeAttributes,
...scrollIntoView,
...selectAll,
...selectParentNode,
...setBlockType,
...setContent,
...sinkListItem,
...splitBlock,
...splitListItem,
...toggleBlockType,
...toggleList,
...toggleMark,
...toggleWrap,
...updateNodeAttributes,
...wrapIn,
...wrapInList,
}
},
})

View File

@@ -14,21 +14,21 @@ import { Extension } from '../Extension'
export const Keymap = Extension.create({
addKeyboardShortcuts() {
const handleBackspace = () => this.editor.commands.try(({ state, dispatch }) => [
const handleBackspace = () => this.editor.commands.first(({ state, dispatch }) => [
() => undoInputRule(state, dispatch),
() => deleteSelection(state, dispatch),
() => joinBackward(state, dispatch),
() => selectNodeBackward(state, dispatch),
])
const handleDelete = () => this.editor.commands.try(({ state, dispatch }) => [
const handleDelete = () => this.editor.commands.first(({ state, dispatch }) => [
() => deleteSelection(state, dispatch),
() => joinForward(state, dispatch),
() => selectNodeForward(state, dispatch),
])
return {
Enter: () => this.editor.commands.try(({ commands, state, dispatch }) => [
Enter: () => this.editor.commands.first(({ commands, state, dispatch }) => [
() => newlineInCode(state, dispatch),
() => createParagraphNear(state, dispatch),
() => liftEmptyBlock(state, dispatch),

View File

@@ -26,7 +26,7 @@ const HardBreak = Node.create({
* Add a hard break
*/
setHardBreak: (): Command => ({ commands, state, dispatch }) => {
return commands.try([
return commands.first([
() => exitCode(state, dispatch),
() => {
if (dispatch) {

View File

@@ -1,4 +1,5 @@
import { Command, Extension } from '@tiptap/core'
import { chain } from 'cypress/types/lodash'
type TextAlignOptions = {
types: string[],