mirror of
https://github.com/docusealco/docuseal
synced 2026-11-04 06:42:32 -05:00
fix editor undo
This commit is contained in:
1 parent
25504e8cb1
commit
73339d8cd9
7 files changed
+212
-64
No files matched your search
@@ -272,7 +272,7 @@ export default {
|
||||
FieldSubmitter,
|
||||
IconX
|
||||
},
|
||||
inject: ['t', 'isMobile', 'nativePlatform'],
|
||||
inject: ['t', 'nativePlatform'],
|
||||
props: {
|
||||
template: {
|
||||
type: Object,
|
||||
|
||||
@@ -1007,6 +1007,9 @@ export default {
|
||||
return {
|
||||
template: this.template,
|
||||
save: this.save,
|
||||
pushUndo: this.pushUndo,
|
||||
undo: this.undo,
|
||||
redo: this.redo,
|
||||
t: this.t,
|
||||
assignDropAreaSize: this.assignDropAreaSize,
|
||||
currencies: this.currencies,
|
||||
@@ -1640,9 +1643,11 @@ export default {
|
||||
this.selectedSubmitter = this.template.submitters[0]
|
||||
},
|
||||
mounted () {
|
||||
this.undoStack = [JSON.stringify(this.template)]
|
||||
this.undoStack = []
|
||||
this.redoStack = []
|
||||
|
||||
this.undoStack.push(this.buildUndoData())
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.resizeObserver = new ResizeObserver(this.onResize)
|
||||
this.resizeObserver.observe(this.$el.getRootNode().querySelector('div[data-v-app]'))
|
||||
@@ -2287,6 +2292,8 @@ export default {
|
||||
|
||||
const dynamicDocumentRefs = this.documentRefs.filter((ref) => ref.isDynamic)
|
||||
|
||||
this.pushUndo()
|
||||
|
||||
dynamicDocumentRefs.forEach((ref) => ref.update())
|
||||
|
||||
this.rebuildVariablesSchema({ disable: false })
|
||||
@@ -2414,35 +2421,76 @@ export default {
|
||||
this.drawOption = null
|
||||
},
|
||||
undo () {
|
||||
this.selectedAreasRef.value = []
|
||||
|
||||
if (this.undoStack.length > 1) {
|
||||
this.undoStack.pop()
|
||||
const stringData = this.undoStack[this.undoStack.length - 1]
|
||||
const currentStringData = JSON.stringify(this.template)
|
||||
const undoData = this.undoStack[this.undoStack.length - 1]
|
||||
const currentUndoData = this.buildUndoData()
|
||||
|
||||
if (stringData && stringData !== currentStringData) {
|
||||
this.redoStack.push(currentStringData)
|
||||
if (!this.isSameUndoData(undoData, currentUndoData)) {
|
||||
this.redoStack.push(currentUndoData)
|
||||
|
||||
Object.assign(this.template, JSON.parse(stringData))
|
||||
|
||||
this.save()
|
||||
this.applyUndoData(undoData, currentUndoData)
|
||||
}
|
||||
}
|
||||
},
|
||||
redo () {
|
||||
const stringData = this.redoStack.pop()
|
||||
this.lastRedoData = stringData
|
||||
const currentStringData = JSON.stringify(this.template)
|
||||
this.selectedAreasRef.value = []
|
||||
|
||||
if (stringData && stringData !== currentStringData) {
|
||||
if (this.undoStack[this.undoStack.length - 1] !== currentStringData) {
|
||||
this.undoStack.push(currentStringData)
|
||||
const undoData = this.redoStack.pop()
|
||||
const currentUndoData = this.buildUndoData()
|
||||
|
||||
if (undoData && !this.isSameUndoData(undoData, currentUndoData)) {
|
||||
if (!this.isSameUndoData(this.undoStack[this.undoStack.length - 1], currentUndoData)) {
|
||||
this.undoStack.push(currentUndoData)
|
||||
}
|
||||
|
||||
Object.assign(this.template, JSON.parse(stringData))
|
||||
this.undoStack.push(undoData)
|
||||
|
||||
this.save()
|
||||
this.applyUndoData(undoData, currentUndoData)
|
||||
}
|
||||
},
|
||||
buildUndoData () {
|
||||
const template = JSON.stringify(toRaw(this.template))
|
||||
const lastUndoData = this.undoStack[this.undoStack.length - 1]
|
||||
|
||||
return { template: template === lastUndoData?.template ? lastUndoData.template : template, sectionDocs: this.buildSectionDocs() }
|
||||
},
|
||||
buildSectionDocs () {
|
||||
return new Map(this.documentRefs.flatMap((ref) => ref.isDynamic ? ref.getSectionDocs() : []))
|
||||
},
|
||||
isSameUndoData (undoData, currentUndoData) {
|
||||
return undoData.template === currentUndoData.template && this.isSameSectionDocs(undoData, currentUndoData)
|
||||
},
|
||||
isSameSectionDocs (undoData, currentUndoData) {
|
||||
return [...currentUndoData.sectionDocs].every(([sectionKey, doc]) => undoData.sectionDocs.get(sectionKey)?.eq(doc))
|
||||
},
|
||||
applyUndoData (undoData, currentUndoData) {
|
||||
const isTemplateChanged = undoData.template !== currentUndoData.template
|
||||
|
||||
if (isTemplateChanged) {
|
||||
Object.assign(this.template, JSON.parse(undoData.template))
|
||||
}
|
||||
|
||||
this.documentRefs.forEach((ref) => {
|
||||
if (ref.isDynamic) ref.restoreSectionDocs(undoData.sectionDocs)
|
||||
})
|
||||
|
||||
this.buildSectionDocs().forEach((doc, sectionKey) => undoData.sectionDocs.set(sectionKey, doc))
|
||||
|
||||
if (isTemplateChanged) {
|
||||
this.save()
|
||||
|
||||
this.$nextTick(this.syncUndoTemplate)
|
||||
}
|
||||
},
|
||||
syncUndoTemplate () {
|
||||
const lastUndoData = this.undoStack[this.undoStack.length - 1]
|
||||
const template = JSON.stringify(toRaw(this.template))
|
||||
|
||||
if (template !== lastUndoData.template) lastUndoData.template = template
|
||||
},
|
||||
onResize () {
|
||||
const breakpointLg = 1024
|
||||
const breakpointMd = 768
|
||||
@@ -2609,15 +2657,11 @@ export default {
|
||||
event.stopImmediatePropagation()
|
||||
event.preventDefault()
|
||||
|
||||
this.selectedAreasRef.value = []
|
||||
|
||||
this.redo()
|
||||
} else if ((event.ctrlKey || event.metaKey) && event.key === 'z') {
|
||||
event.stopImmediatePropagation()
|
||||
event.preventDefault()
|
||||
|
||||
this.selectedAreasRef.value = []
|
||||
|
||||
this.undo()
|
||||
} else if ((event.ctrlKey || event.metaKey) && event.key === 'c' && document.activeElement === document.body) {
|
||||
if (this.selectedAreasRef.value.length > 1) {
|
||||
@@ -2992,16 +3036,29 @@ export default {
|
||||
return false
|
||||
}
|
||||
},
|
||||
pushUndo () {
|
||||
const stringData = JSON.stringify(this.template)
|
||||
pushUndo ({ amend = false } = {}) {
|
||||
const undoData = this.buildUndoData()
|
||||
const lastUndoData = this.undoStack[this.undoStack.length - 1]
|
||||
const isTemplateChanged = undoData.template !== lastUndoData.template
|
||||
const isSectionDocsChanged = !this.isSameSectionDocs(lastUndoData, undoData)
|
||||
|
||||
if (this.undoStack[this.undoStack.length - 1] !== stringData) {
|
||||
this.undoStack.push(stringData)
|
||||
if (this.isUndoBatch || (amend && (!isSectionDocsChanged || (!this.redoStack.length && !isTemplateChanged)))) {
|
||||
this.undoStack[this.undoStack.length - 1] = undoData
|
||||
} else if (isTemplateChanged || isSectionDocsChanged) {
|
||||
this.undoStack.push(undoData)
|
||||
|
||||
if (this.lastRedoData !== stringData) {
|
||||
this.redoStack = []
|
||||
}
|
||||
this.redoStack = []
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
this.isUndoBatch = true
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.isUndoBatch = false
|
||||
|
||||
this.syncUndoTemplate()
|
||||
})
|
||||
},
|
||||
setDefaultAreaSize (area, type) {
|
||||
const documentRef = this.documentRefs.find((e) => e.document.uuid === area.attachment_uuid)
|
||||
@@ -3971,7 +4028,7 @@ export default {
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$el.closest('template-builder')) {
|
||||
this.$el.closest('template-builder').dataset.template = JSON.stringify(this.template)
|
||||
this.$el.closest('template-builder').dataset.template = JSON.stringify(toRaw(this.template))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4004,10 +4061,12 @@ export default {
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$el.closest('template-builder')) {
|
||||
this.$el.closest('template-builder').dataset.dynamicDocuments = JSON.stringify(this.dynamicDocuments)
|
||||
this.$el.closest('template-builder').dataset.dynamicDocuments = JSON.stringify(toRaw(this.dynamicDocuments))
|
||||
}
|
||||
})
|
||||
|
||||
this.pushUndo({ amend: true })
|
||||
|
||||
this.save()
|
||||
},
|
||||
rebuildVariablesSchema ({ disable = true } = {}) {
|
||||
|
||||
@@ -191,6 +191,8 @@ export default {
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.updatedSectionEditors = new Map()
|
||||
|
||||
this.loadListsStyle()
|
||||
},
|
||||
mounted () {
|
||||
@@ -242,6 +244,14 @@ export default {
|
||||
|
||||
this.sectionRefs.forEach((ref) => ref.reloadContent())
|
||||
},
|
||||
getSectionDocs () {
|
||||
return this.sectionRefs.map((ref) => [ref.sectionKey, ref.editor.state.doc])
|
||||
},
|
||||
restoreSectionDocs (sectionDocs) {
|
||||
this.sectionRefs.forEach((ref) => {
|
||||
if (sectionDocs.has(ref.sectionKey)) ref.restoreDoc(sectionDocs.get(ref.sectionKey))
|
||||
})
|
||||
},
|
||||
onBeforeUnload (event) {
|
||||
if (this.saveTimer) {
|
||||
event.preventDefault()
|
||||
@@ -267,11 +277,9 @@ export default {
|
||||
onSectionUpdate (section, { editor }) {
|
||||
clearTimeout(this.saveTimer)
|
||||
|
||||
this.saveTimer = setTimeout(async () => {
|
||||
await this.updateSectionAndSave(section, editor)
|
||||
this.updatedSectionEditors.set(section.id, editor)
|
||||
|
||||
delete this.saveTimer
|
||||
}, 1000)
|
||||
this.saveTimer = setTimeout(() => this.updateSectionsAndSave(), 1000)
|
||||
},
|
||||
loadListsStyle () {
|
||||
this.listsStyle = this.bodyDom.querySelector('body > style[data-lists]')?.textContent || null
|
||||
@@ -291,12 +299,16 @@ export default {
|
||||
updateVariablesSchema () {
|
||||
this.document.variables_schema = buildVariablesSchema(this.bodyDom.body)
|
||||
},
|
||||
updateSectionAndSave (section, editor) {
|
||||
const target = this.bodyDom.getElementById(section.id)
|
||||
updateSectionsAndSave () {
|
||||
this.updatedSectionEditors.forEach((editor, sectionId) => {
|
||||
const target = this.bodyDom.getElementById(sectionId)
|
||||
|
||||
if (target) {
|
||||
target.innerHTML = this.getHtmlForSave(editor)
|
||||
}
|
||||
if (target) {
|
||||
target.innerHTML = this.getHtmlForSave(editor)
|
||||
}
|
||||
})
|
||||
|
||||
this.updatedSectionEditors.clear()
|
||||
|
||||
this.document.body = this.bodyDom.body.innerHTML
|
||||
|
||||
@@ -316,6 +328,8 @@ export default {
|
||||
|
||||
delete this.saveTimer
|
||||
|
||||
this.updatedSectionEditors.clear()
|
||||
|
||||
this.sectionRefs.forEach(({ section, editor }) => {
|
||||
const target = this.bodyDom.getElementById(section.id)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import HardBreak from '@tiptap/extension-hard-break'
|
||||
import History from '@tiptap/extension-history'
|
||||
import Gapcursor from '@tiptap/extension-gapcursor'
|
||||
import Dropcursor from '@tiptap/extension-dropcursor'
|
||||
import { createApp, reactive } from 'vue'
|
||||
@@ -1475,7 +1474,7 @@ const VariableHighlight = Extension.create({
|
||||
}
|
||||
})
|
||||
|
||||
export function buildEditor ({ dynamicAreaProps, getAttachmentsIndex, renderHtmlForSaveRef, onFieldDrop, onFieldDestroy, editorOptions }) {
|
||||
export function buildEditor ({ dynamicAreaProps, getAttachmentsIndex, renderHtmlForSaveRef, onFieldDrop, onFieldDestroy, onUndo, onRedo, editorOptions }) {
|
||||
const FieldNode = Node.create({
|
||||
name: 'fieldNode',
|
||||
inline: true,
|
||||
@@ -1598,6 +1597,51 @@ export function buildEditor ({ dynamicAreaProps, getAttachmentsIndex, renderHtml
|
||||
}
|
||||
})
|
||||
|
||||
const undo = () => {
|
||||
onUndo()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const redo = () => {
|
||||
onRedo()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const BuilderHistory = Extension.create({
|
||||
name: 'builderHistory',
|
||||
addKeyboardShortcuts () {
|
||||
return {
|
||||
'Mod-z': undo,
|
||||
'Shift-Mod-z': redo,
|
||||
'Mod-y': redo,
|
||||
'Mod-я': undo,
|
||||
'Shift-Mod-я': redo
|
||||
}
|
||||
},
|
||||
addProseMirrorPlugins () {
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey('builderHistory'),
|
||||
props: {
|
||||
handleDOMEvents: {
|
||||
beforeinput (_view, event) {
|
||||
const command = { historyUndo: undo, historyRedo: redo }[event.inputType]
|
||||
|
||||
if (!command) return false
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
return command()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const RESIZE_HANDLE_PX = 4
|
||||
const MIN_SIZE_PX = 16
|
||||
|
||||
@@ -1818,7 +1862,7 @@ export function buildEditor ({ dynamicAreaProps, getAttachmentsIndex, renderHtml
|
||||
Document,
|
||||
Text,
|
||||
HardBreak,
|
||||
History,
|
||||
BuilderHistory,
|
||||
Gapcursor,
|
||||
Dropcursor,
|
||||
CustomParagraph,
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
:area="selectedArea"
|
||||
:field="selectedField"
|
||||
:editable="editable"
|
||||
:is-mobile="isMobile"
|
||||
:template="template"
|
||||
:selected-areas-ref="selectedAreasRef"
|
||||
:get-field-type-index="getFieldTypeIndex"
|
||||
@@ -116,7 +117,7 @@
|
||||
<script>
|
||||
import { shallowRef } from 'vue'
|
||||
import { DOMSerializer, Fragment } from '@tiptap/pm/model'
|
||||
import { TextSelection } from '@tiptap/pm/state'
|
||||
import { Selection, TextSelection } from '@tiptap/pm/state'
|
||||
import { findTable, CellSelection } from '@tiptap/pm/tables'
|
||||
import { v4 } from 'uuid'
|
||||
import { IconPlus, IconX } from '@tabler/icons-vue'
|
||||
@@ -138,7 +139,7 @@ export default {
|
||||
IconPlus,
|
||||
IconX
|
||||
},
|
||||
inject: ['template', 'save', 'baseFetch', 't', 'fieldsDragFieldRef', 'customDragFieldRef', 'selectedAreasRef', 'getFieldTypeIndex', 'fieldTypes', 'withPhone', 'withPayment', 'withVerification', 'withKba', 'backgroundColor'],
|
||||
inject: ['template', 'save', 'pushUndo', 'undo', 'redo', 'baseFetch', 't', 'fieldsDragFieldRef', 'customDragFieldRef', 'selectedAreasRef', 'getFieldTypeIndex', 'fieldTypes', 'withPhone', 'withPayment', 'withVerification', 'withKba', 'backgroundColor', 'isMobile'],
|
||||
props: {
|
||||
section: {
|
||||
type: Object,
|
||||
@@ -260,6 +261,9 @@ export default {
|
||||
editor () {
|
||||
return this.editorRef.value
|
||||
},
|
||||
sectionKey () {
|
||||
return `${this.attachmentUuid}:${this.section.id}`
|
||||
},
|
||||
sectionWidthPx () {
|
||||
const pt = parseFloat(this.section.style.width)
|
||||
|
||||
@@ -310,6 +314,8 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.lastUpdateTime = 0
|
||||
|
||||
this.editorRef.value = buildEditor({
|
||||
dynamicAreaProps: {
|
||||
template: this.template,
|
||||
@@ -325,6 +331,8 @@ export default {
|
||||
getAttachmentsIndex: () => this.attachmentsIndex,
|
||||
onFieldDrop: this.onFieldDrop,
|
||||
onFieldDestroy: this.onFieldDestroy,
|
||||
onUndo: this.undo,
|
||||
onRedo: this.redo,
|
||||
renderHtmlForSaveRef: this.renderHtmlForSaveRef,
|
||||
editorOptions: {
|
||||
element: this.$refs.editorElement,
|
||||
@@ -333,6 +341,12 @@ export default {
|
||||
onUpdate: (event) => {
|
||||
this.$emit('update', event)
|
||||
|
||||
if (event.transaction.getMeta('addToHistory') !== false) {
|
||||
this.pushUndo({ amend: event.transaction.time - this.lastUpdateTime < 500 })
|
||||
|
||||
this.lastUpdateTime = event.transaction.time
|
||||
}
|
||||
|
||||
if (this.blockMenuCoords) {
|
||||
this.$nextTick(() => this.setBlockMenuCoords(this.editor))
|
||||
}
|
||||
@@ -352,6 +366,8 @@ export default {
|
||||
this.editor.view.dom.addEventListener('mousemove', this.onEditorMouseMove)
|
||||
this.editor.view.dom.addEventListener('mouseleave', this.onEditorMouseLeave)
|
||||
this.editor.view.dom.addEventListener('keydown', this.onEditorKeyDown)
|
||||
|
||||
this.$nextTick(() => this.pushUndo({ amend: true }))
|
||||
},
|
||||
beforeUnmount () {
|
||||
if (this.editor) {
|
||||
@@ -367,6 +383,19 @@ export default {
|
||||
reloadContent () {
|
||||
this.editor.commands.setContent(this.section.innerHTML, { emitUpdate: false, parseOptions: this.editor.options.parseOptions })
|
||||
},
|
||||
restoreDoc (doc) {
|
||||
const { state, view } = this.editor
|
||||
const { content } = doc.type.schema === state.schema ? doc : state.schema.nodeFromJSON(doc.toJSON())
|
||||
const diffEnd = content.findDiffEnd(state.doc.content)
|
||||
|
||||
if (diffEnd) {
|
||||
const tr = state.tr.replaceWith(0, state.doc.content.size, content).setMeta('addToHistory', false)
|
||||
|
||||
view.dispatch(tr.setSelection(Selection.near(tr.doc.resolve(diffEnd.a))).scrollIntoView())
|
||||
|
||||
this.lastUpdateTime = 0
|
||||
}
|
||||
},
|
||||
findAreaNodePos (areaUuid) {
|
||||
const el = this.editor.view.dom.querySelector(`[data-area-uuid="${areaUuid}"]`)
|
||||
|
||||
@@ -711,6 +740,8 @@ export default {
|
||||
if (event.key === 'Escape') {
|
||||
this.editor.chain().setNodeSelection(0).blur().run()
|
||||
this.deselectArea()
|
||||
} else if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'z') {
|
||||
event.stopPropagation()
|
||||
}
|
||||
},
|
||||
onEditorMouseMove (event) {
|
||||
@@ -826,25 +857,23 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
const newField = {
|
||||
const newField = payload.parentField || {
|
||||
...field,
|
||||
uuid: v4(),
|
||||
submitter_uuid: this.selectedSubmitter.uuid,
|
||||
areas: [area]
|
||||
areas: []
|
||||
}
|
||||
|
||||
newField.areas.push(area)
|
||||
|
||||
if (['radio', 'multiple'].includes(field.type) && field.options?.length) {
|
||||
const oldOptionUuid = area.option_uuid
|
||||
const optionsMap = {}
|
||||
const oldOptionIndex = field.options.findIndex((opt) => opt.uuid === area.option_uuid)
|
||||
|
||||
newField.options = field.options.map((opt) => {
|
||||
const newUuid = v4()
|
||||
optionsMap[opt.uuid] = newUuid
|
||||
if (!payload.parentField) {
|
||||
newField.options = field.options.map((opt) => ({ ...opt, uuid: v4() }))
|
||||
}
|
||||
|
||||
return { ...opt, uuid: newUuid }
|
||||
})
|
||||
|
||||
area.option_uuid = optionsMap[oldOptionUuid] || newField.options[0]?.uuid
|
||||
area.option_uuid = (newField.options[oldOptionIndex] || newField.options[0]).uuid
|
||||
}
|
||||
|
||||
return { field: newField, area }
|
||||
@@ -882,10 +911,13 @@ export default {
|
||||
|
||||
let lastArea = null
|
||||
|
||||
const parentFieldsIndex = {}
|
||||
|
||||
fieldNodes.forEach((fieldNode) => {
|
||||
const fieldValue = fieldNode.dataset.field
|
||||
const areaValue = fieldNode.dataset.area
|
||||
const templateId = fieldNode.dataset.templateId
|
||||
const sourceFieldUuid = fieldNode.getAttribute('uuid')
|
||||
|
||||
if (!fieldValue || !areaValue) {
|
||||
return
|
||||
@@ -894,11 +926,16 @@ export default {
|
||||
const { field, area } = this.buildCopiedField({
|
||||
field: JSON.parse(fieldValue),
|
||||
area: JSON.parse(areaValue),
|
||||
templateId: Number(templateId)
|
||||
templateId: Number(templateId),
|
||||
parentField: parentFieldsIndex[sourceFieldUuid]
|
||||
})
|
||||
|
||||
this.insertFieldInTemplate(field, insertIndex)
|
||||
insertIndex += 1
|
||||
if (!parentFieldsIndex[sourceFieldUuid]) {
|
||||
parentFieldsIndex[sourceFieldUuid] = field
|
||||
|
||||
this.insertFieldInTemplate(field, insertIndex)
|
||||
insertIndex += 1
|
||||
}
|
||||
|
||||
fieldNode.setAttribute('uuid', field.uuid)
|
||||
fieldNode.setAttribute('area-uuid', area.uuid)
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"@tiptap/extension-dropcursor": "^3.19.0",
|
||||
"@tiptap/extension-gapcursor": "^3.19.0",
|
||||
"@tiptap/extension-hard-break": "^3.19.0",
|
||||
"@tiptap/extension-history": "^3.19.0",
|
||||
"@tiptap/extension-italic": "^3.19.0",
|
||||
"@tiptap/extension-link": "^3.19.0",
|
||||
"@tiptap/extension-paragraph": "^3.19.0",
|
||||
|
||||
@@ -1796,11 +1796,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-3.19.0.tgz#7120524cec9ed4b957963693cb4c57cbecbaecf8"
|
||||
integrity sha512-lAmQraYhPS5hafvCl74xDB5+bLuNwBKIEsVoim35I0sDJj5nTrfhaZgMJ91VamMvT+6FF5f1dvBlxBxAWa8jew==
|
||||
|
||||
"@tiptap/extension-history@^3.19.0":
|
||||
version "3.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-3.19.0.tgz#dc3a52f4eb16b7ac077c28284684b558c03adf00"
|
||||
integrity sha512-hhN5nL7Pqcd9iomzeUUMKnmSQieKNlJ2FUgf2dHUqqvn4pWvcHA6P6UwdDNhuKFivSJNNMqtajkOvO4bYq1KPw==
|
||||
|
||||
"@tiptap/extension-italic@^3.19.0":
|
||||
version "3.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-3.19.0.tgz#af2a9c095ec846e379041f3e17e1dd101a5a4bf8"
|
||||
|
||||
Reference in new issue
Block a user