@witchcraft/editor 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -0
  2. package/dist/module.d.mts +1 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +2 -2
  5. package/dist/runtime/components/EditorDemoApp.vue +31 -1
  6. package/dist/runtime/components/EditorDemoControls.d.vue.ts +3 -0
  7. package/dist/runtime/components/EditorDemoControls.vue +55 -12
  8. package/dist/runtime/components/EditorDemoControls.vue.d.ts +3 -0
  9. package/dist/runtime/pm/features/Base/plugins/debugSelectionPlugin.d.ts +1 -5
  10. package/dist/runtime/pm/features/Base/plugins/debugSelectionPlugin.js +43 -17
  11. package/dist/runtime/pm/features/CodeBlock/components/CodeBlockView.vue +0 -1
  12. package/dist/runtime/pm/features/Collaboration/Collaboration.d.ts +14 -63
  13. package/dist/runtime/pm/features/Collaboration/Collaboration.js +4 -164
  14. package/dist/runtime/pm/features/Collaboration/createCollaborationPlugins.d.ts +16 -0
  15. package/dist/runtime/pm/features/Collaboration/createCollaborationPlugins.js +85 -0
  16. package/dist/runtime/pm/features/DocumentApi/DocumentApi.d.ts +16 -3
  17. package/dist/runtime/pm/features/DocumentApi/DocumentApi.js +19 -2
  18. package/dist/runtime/pm/features/DocumentApi/composables/useEditorContent.js +8 -2
  19. package/dist/runtime/pm/features/DocumentApi/composables/useTestDocumentApi.d.ts +4 -1
  20. package/dist/runtime/pm/features/DocumentApi/composables/useTestDocumentApi.js +39 -8
  21. package/dist/runtime/pm/features/DocumentApi/types.d.ts +26 -48
  22. package/dist/runtime/pm/features/Link/components/BubbleMenuExternalLink.vue +0 -2
  23. package/dist/runtime/pm/features/Link/components/BubbleMenuInternalLink.vue +0 -1
  24. package/package.json +38 -37
  25. package/src/module.ts +3 -3
  26. package/src/runtime/components/EditorDemoApp.vue +32 -1
  27. package/src/runtime/components/EditorDemoControls.vue +57 -12
  28. package/src/runtime/pm/features/Base/plugins/debugSelectionPlugin.ts +53 -28
  29. package/src/runtime/pm/features/CodeBlock/components/CodeBlockView.vue +0 -1
  30. package/src/runtime/pm/features/Collaboration/Collaboration.ts +19 -286
  31. package/src/runtime/pm/features/Collaboration/createCollaborationPlugins.ts +132 -0
  32. package/src/runtime/pm/features/DocumentApi/DocumentApi.ts +35 -5
  33. package/src/runtime/pm/features/DocumentApi/composables/useEditorContent.ts +8 -2
  34. package/src/runtime/pm/features/DocumentApi/composables/useTestDocumentApi.ts +56 -8
  35. package/src/runtime/pm/features/DocumentApi/types.ts +30 -52
  36. package/src/runtime/pm/features/Link/components/BubbleMenuExternalLink.vue +0 -2
  37. package/src/runtime/pm/features/Link/components/BubbleMenuInternalLink.vue +0 -1
  38. package/src/runtime/pm/utils/generator/createPsuedoSentence.ts +1 -1
  39. package/dist/runtime/demo/App.d.vue.ts +0 -3
  40. package/dist/runtime/demo/App.vue +0 -100
  41. package/dist/runtime/demo/App.vue.d.ts +0 -3
  42. package/src/runtime/demo/App.vue +0 -113
@@ -10,7 +10,9 @@
10
10
  <EditorDemoControls
11
11
  :code-blocks-theme-list="codeBlocksThemeList"
12
12
  v-model:code-blocks-theme="codeBlocksTheme"
13
+ v-model:use-two-editors="useTwoEditors"
13
14
  />
15
+
14
16
  <Editor
15
17
  class="
16
18
  max-w-[700px]
@@ -34,6 +36,30 @@
34
36
  menus
35
37
  }"
36
38
  />
39
+ <Editor
40
+ v-if="useTwoEditors"
41
+ class="
42
+ max-w-[700px]
43
+ flex-1
44
+ flex
45
+ border
46
+ border-neutral-300
47
+ dark:border-neutral-700
48
+ rounded-sm
49
+ min-h-0
50
+ "
51
+ v-bind="{
52
+ codeBlocksThemeIsDark,
53
+ cssVariables: {
54
+ pmCodeBlockBgColor: codeBlocksThemeBgColor
55
+ },
56
+ docId,
57
+ documentApi,
58
+ linkOptions,
59
+ editorOptions,
60
+ menus
61
+ }"
62
+ />
37
63
  </WRoot>
38
64
  </template>
39
65
 
@@ -41,6 +67,7 @@
41
67
  // all imports must be explicit so this also works without nuxt
42
68
  import type { EditorOptions } from "@tiptap/core"
43
69
  import WRoot from "@witchcraft/ui/components/LibRoot"
70
+ import { useRoute } from "nuxt/app"
44
71
  import { reactive, ref, shallowRef } from "vue"
45
72
 
46
73
  import Editor from "./Editor.vue"
@@ -110,9 +137,13 @@ const menus = shallowRef<Record<string, MenuRenderInfo>>({
110
137
  }
111
138
  })
112
139
 
140
+ const useYjs = useRoute().query.useYjs as string
141
+ const useTwoEditors = ref(false)
142
+
113
143
  const { documentApi } = useTestDocumentApi(
114
144
  editorOptions as any,
115
- testDocuments
145
+ testDocuments,
146
+ { useCollab: useYjs === "true" }
116
147
  )
117
148
  const docId = ref("root")
118
149
  </script>
@@ -1,33 +1,74 @@
1
1
  <template>
2
2
  <div
3
3
  class="
4
+ max-w-[700px]
4
5
  w-full
5
6
  flex
6
- items-stretch
7
+ flex-col
8
+ items-center
7
9
  justify-center
8
10
  gap-2
9
- [&>*]:rounded-sm
11
+ "
12
+ >
13
+ <div
14
+ class="
15
+ flex
16
+ gap-4
17
+ items-center
18
+ justify-center
19
+ border
20
+ border-neutral-300
21
+ dark:border-neutral-700
22
+ rounded-md
23
+ p-2
24
+ "
25
+ >
26
+ <!-- external is to force a reload, otherwise the editors won't work because of how they're setup for the demo (document api is created here and would need to be recreated with the route changes) -->
27
+ <NuxtLink
28
+ v-if="useYjs !== undefined"
29
+ :to="{ path: '/', query: { } }"
30
+ :external="true"
31
+ >Go to Non-Yjs Example</NuxtLink>
32
+ <NuxtLink
33
+ v-else
34
+ :to="{ path: '/', query: { useYjs: 'true' } }"
35
+ :external="true"
36
+ >Go to Yjs Example </NuxtLink>
37
+ <WCheckbox v-model="useTwoEditors">
38
+ Use Two Editors (same document)
39
+ </WCheckbox>
40
+ </div>
41
+ <div
42
+ class="
43
+ flex
44
+ justify-center
45
+ items-center
46
+ gap-2
47
+ [&>*]:rounded-md
10
48
  [&>*]:p-2
11
49
  [&>*]:border
12
50
  [&>*]:border-neutral-300
13
51
  [&>*]:dark:border-neutral-700
52
+
14
53
  "
15
- >
16
- <div class="flex items-center gap-2">
17
- <span>
18
- Global Theme:
19
- </span>
20
- <WDarkModeSwitcher/>
54
+ >
55
+ <div class="flex items-center gap-2">
56
+ <span>
57
+ Global Theme:
58
+ </span>
59
+ <WDarkModeSwitcher/>
60
+ </div>
61
+ <CodeBlockThemePicker
62
+ :code-blocks-theme-list="codeBlocksThemeList"
63
+ v-model:code-blocks-theme="codeBlocksTheme"
64
+ />
21
65
  </div>
22
- <CodeBlockThemePicker
23
- :code-blocks-theme-list="codeBlocksThemeList"
24
- v-model:code-blocks-theme="codeBlocksTheme"
25
- />
26
66
  </div>
27
67
  </template>
28
68
 
29
69
  <script setup lang="ts">
30
70
  import WDarkModeSwitcher from "@witchcraft/ui/components/LibDarkModeSwitcher"
71
+ import { useRoute } from "nuxt/app"
31
72
 
32
73
  import CodeBlockThemePicker from "./CodeBlockThemePicker.vue"
33
74
 
@@ -35,4 +76,8 @@ defineProps<{
35
76
  codeBlocksThemeList: string[]
36
77
  }>()
37
78
  const codeBlocksTheme = defineModel<string>("codeBlocksTheme", { required: true })
79
+
80
+ const useTwoEditors = defineModel<boolean>("useTwoEditors", { required: true })
81
+
82
+ const useYjs = useRoute().query.useYjs as string
38
83
  </script>
@@ -1,56 +1,81 @@
1
1
  /* eslint-disable no-console */
2
2
  import type { Editor } from "@tiptap/core"
3
- import { Plugin, PluginKey, type Transaction } from "@tiptap/pm/state"
3
+ import { Plugin, PluginKey } from "@tiptap/pm/state"
4
+ import { Decoration, DecorationSet } from "@tiptap/pm/view"
4
5
 
5
6
  import { isEmbeddedBlock } from "../../EmbeddedDocument/utils/isEmbeddedBlock.js"
6
7
 
7
- const ROOT_SELECTION_REGEX = /([0-9 -]*)(\[|$)/
8
+ const ROOT_SELECTION_REGEX = /(DEBUG: [0-9 -]*)(\[|$)/
8
9
  const SUB_SELECTION_REGEX = /(\[[0-9 -]*\])/
9
10
 
10
11
  export const debugSelectionPluginKey = new PluginKey("debugSelection")
11
- /**
12
- * Sets the window title to the current selection for debugging in development mode (by checking import.meta.dev).
13
- *
14
- * For embedded editors, adds the selection as `[from - to]`.
15
- */
12
+
13
+ /** Renders a floating tooltip directly above the selection point in dev mode only. */
16
14
  export const debugSelectionPlugin = (editor: Editor, log: boolean = false): Plugin => {
17
15
  let initialized = false
16
+ let currentDisplayString = ""
17
+
18
18
  return new Plugin({
19
19
  key: debugSelectionPluginKey,
20
20
  state: {
21
- init(): void { /**/ },
22
- apply(tr: Transaction): void {
23
- if (!import.meta.dev) { return }
21
+ init() { return DecorationSet.empty },
22
+ apply(tr, oldSet) {
23
+ if (!import.meta.dev) return oldSet.map(tr.mapping, tr.doc)
24
24
  const sel = `${tr.selection.from} - ${tr.selection.to}`
25
+
25
26
  if (isEmbeddedBlock(editor.view)) {
26
- if (log) {
27
- console.log(`embedded selection: ${tr.selection.from} - ${tr.selection.to}`)
28
- }
29
- const hasEmbeddedSelection = document.title.match(SUB_SELECTION_REGEX)
27
+ if (log) console.log(`embedded selection: ${sel}`)
28
+ const hasEmbeddedSelection = currentDisplayString.match(SUB_SELECTION_REGEX)
30
29
  if (hasEmbeddedSelection) {
31
- document.title = document.title
32
- .replace(
33
- SUB_SELECTION_REGEX,
34
- `[${sel}]`
35
- )
30
+ currentDisplayString = currentDisplayString.replace(
31
+ SUB_SELECTION_REGEX,
32
+ `[${sel}]`
33
+ )
36
34
  } else {
37
- document.title = `${document.title} [${sel}]`
35
+ // Ensure prefix is present even if starting as an embedded block
36
+ const prefix = currentDisplayString.startsWith("DEBUG: ") ? "" : "DEBUG: "
37
+ currentDisplayString = `${prefix}${currentDisplayString} [${sel}]`
38
38
  }
39
39
  } else {
40
- if (log) {
41
- console.log(`root selection: ${tr.selection.from} - ${tr.selection.to}`)
42
- }
43
-
40
+ if (log) console.log(`root selection: ${sel}`)
44
41
  if (!initialized) {
45
42
  initialized = true
46
- document.title = sel
43
+ currentDisplayString = `DEBUG: ${sel}`
47
44
  } else {
48
- document.title = document.title.replace(
45
+ currentDisplayString = currentDisplayString.replace(
49
46
  ROOT_SELECTION_REGEX,
50
- `${sel} $2`
47
+ `DEBUG: ${sel} $2`
51
48
  )
52
49
  }
53
- }
50
+ } const widget = document.createElement("div")
51
+ Object.assign(widget.style, {
52
+ position: "absolute",
53
+ top: "110%",
54
+ right: "0",
55
+ zIndex: "100",
56
+ padding: "2px 6px",
57
+ fontSize: "10px",
58
+ fontFamily: "monospace",
59
+ color: "white",
60
+ backgroundColor: "rgba(0, 0, 0, 0.7)",
61
+ borderRadius: "2px",
62
+ pointerEvents: "none",
63
+ whiteSpace: "nowrap",
64
+ lineHeight: "1"
65
+ })
66
+ widget.textContent = currentDisplayString
67
+
68
+ const deco = Decoration.widget(tr.selection.to, widget, {
69
+ side: -1, // Ensure it stays to the left of the cursor
70
+ key: "debug-selection-tooltip"
71
+ })
72
+
73
+ return DecorationSet.create(tr.doc, [deco])
74
+ }
75
+ },
76
+ props: {
77
+ decorations(state) {
78
+ return debugSelectionPluginKey.getState(state)
54
79
  }
55
80
  }
56
81
  })
@@ -23,7 +23,6 @@
23
23
  ref="codeBlockLangPickerEl"
24
24
  >
25
25
  <!-- and here we only style everything but the suggestions so the input matches the code block background -->
26
- <!-- @vue-expect-error -->
27
26
  <WSimpleInput
28
27
  :border="false"
29
28
  wrapper-class="lang-picker-input flex-nowrap z-10"
@@ -1,295 +1,28 @@
1
- import { type Editor, Extension } from "@tiptap/core"
2
- import { Plugin, PluginKey } from "@tiptap/pm/state"
3
- import type { EditorView } from "@tiptap/pm/view"
4
- import {
5
- redo,
6
- undo,
7
- type UndoPluginState,
8
- ySyncPlugin,
9
- yUndoPlugin,
10
- yUndoPluginKey,
11
- yXmlFragmentToProseMirrorRootNode } from "y-prosemirror"
12
- import * as Y from "yjs"
13
-
14
- const ySyncFilterPluginKey = new PluginKey("ySyncFilter")
15
- // eslint-disable-next-line @typescript-eslint/naming-convention
16
- type YSyncOpts = Parameters<typeof ySyncPlugin>[1]
17
- // eslint-disable-next-line @typescript-eslint/naming-convention
18
- type YUndoOpts = Parameters<typeof yUndoPlugin>[0]
19
-
20
- declare module "@tiptap/core" {
21
- // eslint-disable-next-line @typescript-eslint/naming-convention
22
- interface Commands<ReturnType> {
23
- collaboration: {
24
- /** Sets the fragment to use for the collaboration extension. */
25
- setFragment: (
26
- fragment: Y.XmlFragment | undefined,
27
- options?: { register?: boolean, unregister?: boolean }
28
- ) => ReturnType
29
- /** Enables or disables collaboration. */
30
- enableCollaboration: (enable: boolean) => ReturnType
31
- /**
32
- * Undo recent changes
33
- *
34
- * @example editor.commands.undo()
35
- */
36
- undo: () => ReturnType
37
- /**
38
- * Reapply reverted changes
39
- *
40
- * @example editor.commands.redo()
41
- */
42
- redo: () => ReturnType
43
- }
44
- }
45
- }
46
-
47
- export interface CollaborationStorage {
48
- /**
49
- * Whether collaboration is currently disabled.
50
- * Disabling collaboration will prevent any changes from being synced with other users.
51
- */
52
- isDisabled: boolean
53
- }
54
-
55
- export interface CollaborationOptions {
56
-
57
- /**
58
- * A raw Y.js fragment, can be used instead of `document` and `field`.
59
- *
60
- * @example new Y.Doc().getXmlFragment('body')
61
- */
62
- fragment?: Y.XmlFragment | null
63
-
64
- /**
65
- * Fired when the content from Yjs is initially rendered to Tiptap.
66
- */
67
- onFirstRender?: () => void
68
-
69
- /**
70
- * Options for the Yjs sync plugin.
71
- */
72
- ySyncOptions?: YSyncOpts
73
-
74
- /**
75
- * Options for the Yjs undo plugin.
76
- */
77
- yUndoOptions?: YUndoOpts
78
- /** @internal */
79
- // eslint-disable-next-line @typescript-eslint/naming-convention
80
- _destroySyncPlugin?: () => void
81
- }
82
- type CollabInstance = {
83
- editor: Editor
84
- options: CollaborationOptions
85
- storage: CollaborationStorage
86
- }
87
- type ExtendedUndoManager = UndoPluginState["undoManager"] & {
88
- restore?: () => void
89
- }
90
-
91
- function createUndoPlugin(self: CollabInstance): Plugin { // Quick fix until there is an official implementation (thanks to @hamflx).
92
- // See https://github.com/yjs/y-prosemirror/issues/114 and https://github.com/yjs/y-prosemirror/issues/102
93
- const yUndoPluginInstance = yUndoPlugin(self.options.yUndoOptions)
94
- const originalUndoPluginView = yUndoPluginInstance.spec.view
95
-
96
- yUndoPluginInstance.spec.view = (view: EditorView) => {
97
- const { undoManager } = yUndoPluginKey.getState(view.state) as UndoPluginState & { undoManager: ExtendedUndoManager }
98
-
99
- if (undoManager.restore) {
100
- undoManager.restore()
101
- undoManager.restore = () => {
102
- // noop
103
- }
104
- }
105
-
106
- const viewRet = originalUndoPluginView ? originalUndoPluginView(view) : undefined
107
-
108
- return {
109
- destroy: () => {
110
- const hasUndoManSelf = undoManager.trackedOrigins.has(undoManager)
111
-
112
- const observers = undoManager._observers
113
-
114
- undoManager.restore = () => {
115
- if (hasUndoManSelf) {
116
- undoManager.trackedOrigins.add(undoManager)
117
- }
118
-
119
- undoManager.doc.on("afterTransaction", undoManager.afterTransactionHandler)
120
-
121
- undoManager._observers = observers
122
- }
123
-
124
- if (viewRet?.destroy) {
125
- viewRet.destroy()
126
- }
127
- }
128
- }
129
- }
130
- return yUndoPluginInstance
131
- }
132
- function createSyncPlugin(
133
- self: CollabInstance
134
- ): { plugin: Plugin, destroy: () => void } {
135
- const fragment = self.options.fragment
136
- if (!fragment) {
137
- throw new Error("Collaboration requires a fragment.")
138
- }
139
- const plugin = ySyncPlugin(fragment, {
140
- ...self.options.ySyncOptions,
141
- onFirstRender: self.options.onFirstRender
142
- })
143
-
144
- let off: (() => void) | undefined
145
- function destroy(): void {
146
- off?.()
147
- // delete self.editor.state[ySyncPluginKey.key]
148
- }
149
- if (self.editor.options.enableContentCheck) {
150
- const onBeforeTransaction = (): false | undefined => {
151
- try {
152
- yXmlFragmentToProseMirrorRootNode(fragment, self.editor.schema).check()
153
- } catch (error) {
154
- self.editor.emit("contentError", {
155
- error: error as Error,
156
- editor: self.editor,
157
- disableCollaboration: () => {
158
- fragment.doc?.destroy()
159
- self.storage.isDisabled = true
160
- }
161
- })
162
- // If the content is invalid, return false to prevent the transaction from being applied
163
- return false
164
- }
165
- return undefined
166
- }
167
- fragment.doc?.on("beforeTransaction", onBeforeTransaction)
168
- off = () => {
169
- fragment.doc?.off("beforeTransaction", onBeforeTransaction)
170
- }
171
- }
172
- return { plugin, destroy }
173
- }
174
-
175
- function createSyncFilterPlugin(
176
- self: CollabInstance
177
- ): Plugin | undefined {
178
- if (!self.editor.options.enableContentCheck) return
179
- const fragment = self.options.fragment
180
- if (!fragment) {
181
- throw new Error("Collaboration requires a fragment.")
182
- }
183
- return new Plugin({
184
- key: ySyncFilterPluginKey,
185
- filterTransaction: () => {
186
- // When collaboration is disabled, prevent any sync transactions from being applied
187
- if (self.storage.isDisabled) {
188
- // Destroy the Yjs document to prevent any further sync transactions
189
- fragment.doc?.destroy()
190
-
191
- return true
192
- }
193
-
194
- return true
195
- }
196
- })
197
- }
198
-
199
- function createPlugins(self: CollabInstance): Plugin[] {
200
- const { plugin: syncPlugin, destroy: destroySyncPlugin } = createSyncPlugin(self)
201
- self.options._destroySyncPlugin = destroySyncPlugin
202
- const filterPlugin = createSyncFilterPlugin(self)
203
- const plugins = [
204
- syncPlugin,
205
- createUndoPlugin(self)
206
- ]
207
- if (filterPlugin) {
208
- plugins.push(filterPlugin)
209
- }
210
- return plugins
211
- }
1
+ import BaseCollaboration, { type CollaborationOptions } from "@tiptap/extension-collaboration"
212
2
 
213
3
  /**
214
- * This extension allows you to collaborate with others in real-time.
4
+ * Extension of the base collaboration extension without prosemirror plugins or storage.
5
+ *
6
+ * We can't use tiptap's collaboration extension (or any extension that creates the sync plugin) because it expects to be configured with the document's ydoc per editor.
7
+ *
8
+ * This doesn't mesh well with how the document api works (see {@link DocumentApi}).
9
+ *
10
+ * Instead we let the extension register anything it wants (shortcuts, commands, etc) except the plugins and storage. This way it can just be added to the list of editor extensions normally.
11
+ *
12
+ * Then there is a seperate function to actually create the plugins:
13
+ *
14
+ * {@link createCollaborationPlugins} creates the plugins **per doc** and can be used from your document api without an editor instance. See {@link useTestDocumentApi} for an example.
215
15
  *
216
- * @see https://tiptap.dev/api/extensions/collaboration
16
+ * It's also where the `enableContentCheck` option should be set if you need it. It's IGNORED if passed to the extension.
217
17
  */
218
- // eslint-disable-next-line @typescript-eslint/naming-convention
219
- export const Collaboration = Extension.create<CollaborationOptions, CollaborationStorage>({
220
- name: "collaboration",
221
-
222
- priority: 1000,
223
-
224
- addOptions() {
225
- return {
226
- document: null,
227
- field: "default",
228
- fragment: new Y.Doc().getXmlFragment("prosemirror")
229
- }
230
- },
231
18
 
19
+ // eslint-disable-next-line @typescript-eslint/naming-convention
20
+ export const Collaboration = BaseCollaboration.extend<Omit<CollaborationOptions, "document" | "field" | "fragment">, Record<string, never>>({
232
21
  addStorage() {
233
- return {
234
- isDisabled: false
235
- }
236
- },
237
-
238
- onCreate() {
239
- if (this.editor.extensionManager.extensions.find(extension => extension.name === "history")) {
240
- // eslint-disable-next-line no-console
241
- console.warn(
242
- "[tiptap warn]: \"@tiptap/extension-collaboration\" comes with its own history support and is not compatible with \"@tiptap/extension-history\"."
243
- )
244
- }
22
+ return {}
245
23
  },
246
-
247
- addCommands() {
248
- // const self = this
249
- return {
250
- enableCollaboration: (enable: boolean) => () => {
251
- this.storage.isDisabled = !enable
252
- return true
253
- },
254
- undo: () => ({ tr, state, dispatch }) => {
255
- tr.setMeta("preventDispatch", true)
256
-
257
- const undoManager = yUndoPluginKey.getState(state)!.undoManager as ExtendedUndoManager
258
-
259
- if (undoManager.undoStack.length === 0) {
260
- return false
261
- }
262
-
263
- if (!dispatch) {
264
- return true
265
- }
266
-
267
- return undo(state)
268
- },
269
- redo: () => ({ tr, state, dispatch }) => {
270
- tr.setMeta("preventDispatch", true)
271
-
272
- const undoManager = yUndoPluginKey.getState(state)!.undoManager as ExtendedUndoManager
273
-
274
- if (undoManager.redoStack.length === 0) {
275
- return false
276
- }
277
-
278
- if (!dispatch) {
279
- return true
280
- }
281
-
282
- return redo(state)
283
- }
284
-
285
- }
286
- },
287
- onDestroy() {
288
- this.options._destroySyncPlugin?.()
289
- },
290
-
291
- addProseMirrorPlugins(): Plugin[] {
292
- const self = this
293
- return createPlugins(self)
24
+ addProseMirrorPlugins() {
25
+ return []
294
26
  }
295
27
  })
28
+